@jsm-mit/sultana-agent-tools-package 0.6.0 → 0.8.0

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 (41) hide show
  1. package/README.md +71 -0
  2. package/dist/booksy/fetch-page.d.ts +25 -0
  3. package/dist/booksy/fetch-page.d.ts.map +1 -0
  4. package/dist/booksy/fetch-page.js +93 -0
  5. package/dist/booksy/import-tools.d.ts +22 -0
  6. package/dist/booksy/import-tools.d.ts.map +1 -0
  7. package/dist/booksy/import-tools.js +413 -0
  8. package/dist/booksy/nuxt-payload.d.ts +15 -0
  9. package/dist/booksy/nuxt-payload.d.ts.map +1 -0
  10. package/dist/booksy/nuxt-payload.js +96 -0
  11. package/dist/booksy/parse-salon.d.ts +61 -0
  12. package/dist/booksy/parse-salon.d.ts.map +1 -0
  13. package/dist/booksy/parse-salon.js +212 -0
  14. package/dist/booksy/to-sultana.d.ts +62 -0
  15. package/dist/booksy/to-sultana.d.ts.map +1 -0
  16. package/dist/booksy/to-sultana.js +77 -0
  17. package/dist/booksy/tools.d.ts +57 -0
  18. package/dist/booksy/tools.d.ts.map +1 -0
  19. package/dist/booksy/tools.js +227 -0
  20. package/dist/customer/customer-core-port.d.ts +3 -3
  21. package/dist/customer/customer-core-port.d.ts.map +1 -1
  22. package/dist/customer/ic-customer-core-port.d.ts +2 -3
  23. package/dist/customer/ic-customer-core-port.d.ts.map +1 -1
  24. package/dist/customer/ic-customer-core-port.js +4 -9
  25. package/dist/customer/persona.d.ts.map +1 -1
  26. package/dist/customer/persona.js +4 -1
  27. package/dist/customer/texts.d.ts +2 -0
  28. package/dist/customer/texts.d.ts.map +1 -1
  29. package/dist/customer/texts.js +6 -2
  30. package/dist/customer/tools.d.ts.map +1 -1
  31. package/dist/customer/tools.js +22 -4
  32. package/dist/ic-salon-core-port.d.ts +4 -0
  33. package/dist/ic-salon-core-port.d.ts.map +1 -1
  34. package/dist/ic-salon-core-port.js +19 -1
  35. package/dist/index.d.ts +11 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +6 -1
  38. package/dist/persona.d.ts +5 -0
  39. package/dist/persona.d.ts.map +1 -1
  40. package/dist/persona.js +17 -0
  41. package/package.json +2 -1
@@ -0,0 +1,227 @@
1
+ import { ArgumentError, readOptionalString, readOptionalStringArray, readString } from "../tools/args.js";
2
+ import { err, ok } from "../types.js";
3
+ import { BooksyReadError, fetchBooksyPage, fetchPhoto, normalizeBooksyUrl } from "./fetch-page.js";
4
+ import { parseBooksySalon } from "./parse-salon.js";
5
+ import { toSultanaDraft } from "./to-sultana.js";
6
+ export const URL_FIELD = {
7
+ type: "string",
8
+ description: "Adres profilu salonu na Booksy, np. https://booksy.com/pl-pl/12345_nazwa-salonu_fryzjer_3_warszawa.",
9
+ };
10
+ const PHOTO_KINDS = ["logo", "cover", "salon", "inspiration", "service", "staff"];
11
+ const DEFAULT_PHOTO_KINDS = ["logo", "cover", "salon"];
12
+ // A busy profile has about a hundred photos — a service portfolio alone runs to 70 or more.
13
+ const MAX_PHOTOS_PER_CALL = 150;
14
+ /**
15
+ * Read-only tools over a PUBLIC Booksy profile: they fetch one page and hand back its content in
16
+ * the shape Sultana's write tools take. Nothing here writes to a salon, so nothing here asks for
17
+ * a confirmation. Reviews and customer names are never read.
18
+ */
19
+ export function createBooksyReaderTools(options = {}) {
20
+ const reader = options.reader ?? new BooksyProfileReader(options.fetchFn);
21
+ const tools = [readSalonTool(reader), readServicesTool(reader)];
22
+ if (options.photoSink)
23
+ tools.push(downloadPhotosTool(reader, options.photoSink, options.fetchFn));
24
+ return tools;
25
+ }
26
+ /** Reads and recasts a whole profile — for a host or a script that wants the draft without a model. */
27
+ export async function readBooksySalonDraft(url, fetchFn) {
28
+ return new BooksyProfileReader(fetchFn).read(url);
29
+ }
30
+ /** Long enough for a preview, the owner's "tak" and the import to read one fetch; short enough
31
+ * that an owner who has just edited her Booksy profile is not shown yesterday's. */
32
+ const PROFILE_TTL_MS = 10 * 60_000;
33
+ /** A reader may live as long as its host — it must not grow with every salon ever named. */
34
+ const MAX_REMEMBERED_PROFILES = 50;
35
+ /**
36
+ * Every Booksy tool reads the same page — fetch it once. Profiles are public, so one reader can
37
+ * serve a whole host; a profile is remembered for ten minutes, the last fifty of them.
38
+ */
39
+ export class BooksyProfileReader {
40
+ fetchFn;
41
+ now;
42
+ drafts = new Map();
43
+ constructor(fetchFn, now = Date.now) {
44
+ this.fetchFn = fetchFn;
45
+ this.now = now;
46
+ }
47
+ read(rawUrl) {
48
+ const url = normalizeBooksyUrl(rawUrl);
49
+ const remembered = this.drafts.get(url);
50
+ if (remembered && this.now() - remembered.readAt <= PROFILE_TTL_MS)
51
+ return remembered.draft;
52
+ const draft = fetchBooksyPage(url, this.fetchFn).then((html) => toSultanaDraft(parseBooksySalon(html), url));
53
+ const entry = { readAt: this.now(), draft };
54
+ // A failed read must not be remembered — the next call tries again.
55
+ draft.catch(() => {
56
+ if (this.drafts.get(url) === entry)
57
+ this.drafts.delete(url);
58
+ });
59
+ // Re-inserting moves the profile to the end, so the first key is always the oldest.
60
+ this.drafts.delete(url);
61
+ this.drafts.set(url, entry);
62
+ while (this.drafts.size > MAX_REMEMBERED_PROFILES)
63
+ this.drafts.delete(this.drafts.keys().next().value);
64
+ return draft;
65
+ }
66
+ }
67
+ function readSalonTool(reader) {
68
+ return {
69
+ name: "read_booksy_salon",
70
+ progress: "Czytam profil salonu na Booksy…",
71
+ description: "Czyta publiczny profil salonu na Booksy i zwraca: nazwę, opis, adres, współrzędne, godziny otwarcia (dzień 0 = poniedziałek), zespół oraz liczbę usług i zdjęć. Niczego nie zapisuje. Usługi zwraca read_booksy_services.",
72
+ parameters: { type: "object", properties: { url: URL_FIELD }, required: ["url"], additionalProperties: false },
73
+ execute: (args) => guarded("Nie udało się odczytać profilu Booksy.", async () => {
74
+ const draft = await reader.read(readString(args, "url"));
75
+ const categories = new Set(draft.services.map((service) => service.booksyCategory));
76
+ return ok(`Salon „${draft.salon.name}” (${draft.salon.city || "miasto nieznane"}): ${draft.services.length} usług w ${categories.size} kategoriach, zespół ${draft.workers.length} os., ${draft.photos.length} zdjęć.${degradedSuffix(draft)}`, {
77
+ source: draft.source,
78
+ salon: draft.salon,
79
+ weeklyHours: draft.weeklyHours,
80
+ workers: draft.workers.map((worker) => worker.name),
81
+ serviceCount: draft.services.length,
82
+ serviceCategories: [...categories],
83
+ photoCounts: countPhotos(draft.photos),
84
+ });
85
+ }),
86
+ };
87
+ }
88
+ function readServicesTool(reader) {
89
+ return {
90
+ name: "read_booksy_services",
91
+ progress: "Czytam usługi salonu na Booksy…",
92
+ description: "Czyta usługi z publicznego profilu Booksy w postaci gotowej dla add_service: name, pricePln (pełne złote), durationMinutes (wielokrotność 5). Każdy wariant usługi Booksy to osobna usługa. serviceTypeIds są puste — dobierz je przez find_service_type, podpowiedzią jest booksyCategory. Pole notes opisuje, co zmieniono albo czego brakuje — powiedz o tym właścicielce. Niczego nie zapisuje.",
93
+ parameters: {
94
+ type: "object",
95
+ properties: {
96
+ url: URL_FIELD,
97
+ category: { type: "string", description: "Opcjonalnie: tylko usługi z tej kategorii Booksy (fragment nazwy)." },
98
+ },
99
+ required: ["url"],
100
+ additionalProperties: false,
101
+ },
102
+ execute: (args) => guarded("Nie udało się odczytać usług z Booksy.", async () => {
103
+ const draft = await reader.read(readString(args, "url"));
104
+ const category = readOptionalString(args, "category")?.toLowerCase();
105
+ const services = category
106
+ ? draft.services.filter((service) => service.booksyCategory.toLowerCase().includes(category))
107
+ : draft.services;
108
+ if (services.length === 0) {
109
+ return ok(category ? `Brak usług w kategorii „${category}”.` : "Profil Booksy nie ma żadnej usługi.", []);
110
+ }
111
+ const withNotes = services.filter((service) => service.notes.length > 0).length;
112
+ const notesPart = withNotes > 0 ? ` ${withNotes} z nich ma uwagi (pole notes).` : "";
113
+ return ok(`Usługi z Booksy: ${services.length}.${notesPart}${degradedSuffix(draft)}`, services);
114
+ }),
115
+ };
116
+ }
117
+ function downloadPhotosTool(reader, sink, fetchFn) {
118
+ return {
119
+ name: "download_booksy_photos",
120
+ progress: "Pobieram zdjęcia z Booksy…",
121
+ description: `Pobiera zdjęcia z publicznego profilu Booksy i odkłada je po stronie Sultana. Rodzaje: ${PHOTO_KINDS.join(", ")} („service” to portfolio usług — zdjęcia prac przypisane do usługi, „inspiration” to galeria prac). Domyślnie: ${DEFAULT_PHOTO_KINDS.join(", ")}. Najwyżej ${MAX_PHOTOS_PER_CALL} zdjęć na wywołanie. Zwraca listę zapisanych plików. Niczego nie zapisuje w salonie.`,
122
+ parameters: {
123
+ type: "object",
124
+ properties: {
125
+ url: URL_FIELD,
126
+ kinds: { type: "array", items: { type: "string", enum: PHOTO_KINDS }, description: "Które rodzaje zdjęć pobrać." },
127
+ },
128
+ required: ["url"],
129
+ additionalProperties: false,
130
+ },
131
+ execute: (args) => guarded("Nie udało się pobrać zdjęć z Booksy.", async () => {
132
+ const draft = await reader.read(readString(args, "url"));
133
+ const kinds = readKinds(args);
134
+ const wanted = draft.photos.filter((photo) => kinds.includes(photo.kind));
135
+ if (wanted.length === 0)
136
+ return ok("Profil Booksy nie ma zdjęć tych rodzajów.", []);
137
+ const saved = [];
138
+ const failed = [];
139
+ const counters = new Map();
140
+ for (const photo of wanted.slice(0, MAX_PHOTOS_PER_CALL)) {
141
+ const index = (counters.get(photo.kind) ?? 0) + 1;
142
+ counters.set(photo.kind, index);
143
+ // One broken photo must not cost the owner the rest of the gallery.
144
+ try {
145
+ const file = await fetchPhoto(photo.url, fetchFn);
146
+ const location = await sink.save({
147
+ salonKey: draft.source.booksyId ?? "unknown",
148
+ kind: photo.kind,
149
+ fileName: `${photo.kind}-${String(index).padStart(2, "0")}${captionSlug(photo)}.${extensionOf(file.contentType)}`,
150
+ contentType: file.contentType,
151
+ bytes: file.bytes,
152
+ sourceUrl: photo.url,
153
+ caption: photo.caption,
154
+ });
155
+ saved.push({ kind: photo.kind, location, caption: photo.caption });
156
+ }
157
+ catch {
158
+ failed.push(photo.url);
159
+ }
160
+ }
161
+ const skipped = wanted.length - Math.min(wanted.length, MAX_PHOTOS_PER_CALL);
162
+ const parts = [`Pobrano ${saved.length} z ${wanted.length} zdjęć.`];
163
+ if (failed.length > 0)
164
+ parts.push(`Nie udało się pobrać: ${failed.length}.`);
165
+ if (skipped > 0)
166
+ parts.push(`Pominięto ${skipped} ponad limit ${MAX_PHOTOS_PER_CALL}.`);
167
+ if (saved.length === 0)
168
+ return err("unavailable", parts.join(" "));
169
+ return ok(parts.join(" "), { saved, failed });
170
+ }),
171
+ };
172
+ }
173
+ function readKinds(args) {
174
+ const raw = readOptionalStringArray(args, "kinds");
175
+ if (!raw || raw.length === 0)
176
+ return DEFAULT_PHOTO_KINDS;
177
+ const unknown = raw.filter((kind) => !PHOTO_KINDS.includes(kind));
178
+ if (unknown.length > 0)
179
+ throw new ArgumentError(`Nieznany rodzaj zdjęć: ${unknown.join(", ")}. Dozwolone: ${PHOTO_KINDS.join(", ")}.`);
180
+ return raw;
181
+ }
182
+ function countPhotos(photos) {
183
+ const counts = {};
184
+ for (const photo of photos)
185
+ counts[photo.kind] = (counts[photo.kind] ?? 0) + 1;
186
+ return counts;
187
+ }
188
+ export function degradedSuffix(draft) {
189
+ return draft.source.degraded
190
+ ? " UWAGA: odczyt zastępczy — bez czasów trwania, kategorii i galerii; Booksy mogło zmienić stronę."
191
+ : "";
192
+ }
193
+ /** "-makijaz-okolicznosciowy" for a service or staff photo; empty for the rest. */
194
+ function captionSlug(photo) {
195
+ if ((photo.kind !== "service" && photo.kind !== "staff") || !photo.caption)
196
+ return "";
197
+ const slug = photo.caption
198
+ .toLowerCase()
199
+ .replace(/ł/g, "l")
200
+ .normalize("NFD")
201
+ .replace(/[\u0300-\u036f]/g, "")
202
+ .replace(/[^a-z0-9]+/g, "-")
203
+ .replace(/^-+|-+$/g, "")
204
+ .slice(0, 40);
205
+ return slug === "" ? "" : `-${slug}`;
206
+ }
207
+ function extensionOf(contentType) {
208
+ const subtype = contentType.split(";")[0].trim().split("/")[1] ?? "";
209
+ if (subtype === "jpeg" || subtype === "jpg")
210
+ return "jpeg";
211
+ if (subtype === "png" || subtype === "webp" || subtype === "gif")
212
+ return subtype;
213
+ return "bin";
214
+ }
215
+ /** `execute` never throws: every failure becomes a result the agent can read. */
216
+ export async function guarded(fallbackSummary, run) {
217
+ try {
218
+ return await run();
219
+ }
220
+ catch (error) {
221
+ if (error instanceof ArgumentError)
222
+ return err("invalid_arguments", error.message);
223
+ if (error instanceof BooksyReadError)
224
+ return err(error.code, error.summary);
225
+ return err("unavailable", fallbackSummary);
226
+ }
227
+ }
@@ -72,13 +72,13 @@ export interface BookingInput {
72
72
  discountCode?: string;
73
73
  }
74
74
  export interface CustomerCorePort {
75
- /** Where the customer searches around; `null` when the app sent no position. */
75
+ /** The position the app sent; `null` when it sent none. A search may name another place. */
76
76
  readonly location: Coordinates | null;
77
77
  findServiceTypes(query: string): Promise<ServiceTypeView[]>;
78
78
  /** "As soon as possible" around `location`. */
79
- searchAsap(serviceTypeId: string, radiusKm: number, now: Date): Promise<SlotSearchView>;
79
+ searchAsap(location: Coordinates, serviceTypeId: string, radiusKm: number, now: Date): Promise<SlotSearchView>;
80
80
  /** The given start moments around `location`. */
81
- searchAtMoments(serviceTypeId: string, radiusKm: number, moments: number[]): Promise<SlotSearchView>;
81
+ searchAtMoments(location: Coordinates, serviceTypeId: string, radiusKm: number, moments: number[]): Promise<SlotSearchView>;
82
82
  getSalons(salonIds: string[]): Promise<SalonView[]>;
83
83
  listSalonServices(salonId: string): Promise<CustomerServiceView[]>;
84
84
  listSalonPromos(salonId: string): Promise<CustomerPromoView[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"customer-core-port.d.ts","sourceRoot":"","sources":["../../src/customer/customer-core-port.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"customer-core-port.d.ts","sourceRoot":"","sources":["../../src/customer/customer-core-port.ts"],"names":[],"mappings":"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"}
@@ -31,8 +31,8 @@ export declare class IcCustomerCorePort implements CustomerCorePort {
31
31
  private readonly appointments;
32
32
  constructor(options: IcCustomerCorePortOptions);
33
33
  findServiceTypes(query: string): Promise<ServiceTypeView[]>;
34
- searchAsap(serviceTypeId: string, radiusKm: number, now: Date): Promise<SlotSearchView>;
35
- searchAtMoments(serviceTypeId: string, radiusKm: number, moments: number[]): Promise<SlotSearchView>;
34
+ searchAsap(location: Coordinates, serviceTypeId: string, radiusKm: number, now: Date): Promise<SlotSearchView>;
35
+ searchAtMoments(location: Coordinates, serviceTypeId: string, radiusKm: number, moments: number[]): Promise<SlotSearchView>;
36
36
  getSalons(salonIds: string[]): Promise<SalonView[]>;
37
37
  listSalonServices(salonId: string): Promise<CustomerServiceView[]>;
38
38
  listSalonPromos(salonId: string): Promise<CustomerPromoView[]>;
@@ -40,6 +40,5 @@ export declare class IcCustomerCorePort implements CustomerCorePort {
40
40
  book(input: BookingInput): Promise<string>;
41
41
  listMyAppointments(): Promise<AppointmentView[]>;
42
42
  cancel(appointmentId: string): Promise<void>;
43
- private requireLocation;
44
43
  }
45
44
  //# sourceMappingURL=ic-customer-core-port.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ic-customer-core-port.d.ts","sourceRoot":"","sources":["../../src/customer/ic-customer-core-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAepD,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EAER,eAAe,EACf,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAEnB,SAAS,EACT,cAAc,EACjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,yBAAyB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;0FACsF;IACtF,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAaD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,gBAAgB;IACvD,SAAgB,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;IAE7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;gBAErC,OAAO,EAAE,yBAAyB;IAajC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAU3D,UAAU,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAWvF,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAWpG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQnD,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAKlE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAa9D,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAUjF,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAW1C,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAehD,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzD,OAAO,CAAC,eAAe;CAI1B"}
1
+ {"version":3,"file":"ic-customer-core-port.d.ts","sourceRoot":"","sources":["../../src/customer/ic-customer-core-port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAepD,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,KAAK,EAER,eAAe,EACf,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EAEnB,SAAS,EACT,cAAc,EACjB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,yBAAyB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB;0FACsF;IACtF,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAaD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,gBAAgB;IACvD,SAAgB,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAC;IAE7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;gBAErC,OAAO,EAAE,yBAAyB;IAajC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAU3D,UAAU,CAAC,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC;IAW9G,eAAe,CAAC,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAW3H,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAQnD,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAKlE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAa9D,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAUjF,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAW1C,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAehD,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAI5D"}
@@ -37,18 +37,18 @@ export class IcCustomerCorePort {
37
37
  category: entry.category,
38
38
  }));
39
39
  }
40
- async searchAsap(serviceTypeId, radiusKm, now) {
40
+ async searchAsap(location, serviceTypeId, radiusKm, now) {
41
41
  const state = await searchAsapAsyncUnsafe(this.search, {
42
- location: this.requireLocation(),
42
+ location,
43
43
  serviceTypeId,
44
44
  radiusKm,
45
45
  now,
46
46
  });
47
47
  return { phase: state.phase, salons: state.results.map(toSalonSlots), failedProbes: state.failedProbes };
48
48
  }
49
- async searchAtMoments(serviceTypeId, radiusKm, moments) {
49
+ async searchAtMoments(location, serviceTypeId, radiusKm, moments) {
50
50
  const state = await searchAtMomentsAsyncUnsafe(this.search, {
51
- location: this.requireLocation(),
51
+ location,
52
52
  serviceTypeId,
53
53
  radiusKm,
54
54
  moments,
@@ -104,11 +104,6 @@ export class IcCustomerCorePort {
104
104
  async cancel(appointmentId) {
105
105
  await this.appointments.cancelAppointmentAsyncUnsafe(appointmentId);
106
106
  }
107
- requireLocation() {
108
- if (!this.location)
109
- throw new Error("IcCustomerCorePort: no location — the tools check it before they search");
110
- return this.location;
111
- }
112
107
  }
113
108
  function toService(service) {
114
109
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["../../src/customer/persona.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5D;AA2BD,yEAAyE;AACzE,eAAO,MAAM,yBAAyB,QAAgC,CAAC"}
1
+ {"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["../../src/customer/persona.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5D;AA8BD,yEAAyE;AACzE,eAAO,MAAM,yBAAyB,QAAgC,CAAC"}
@@ -24,7 +24,10 @@ const PERSONA = `Jesteś asystentem klienta w aplikacji Sultana: pomagasz znale
24
24
  rezerwujesz i odwołujesz wizyty w imieniu klienta. Rozmawiasz po polsku, krótko i konkretnie.
25
25
 
26
26
  Zasady pracy:
27
- - Szukasz zawsze wokół pozycji klienta, którą podała aplikacja. Nie pytaj o adres.
27
+ - Szukasz wokół pozycji klienta, którą podała aplikacja — sam nie pytaj o adres. Gdy klient wskazuje
28
+ inne miejsce („koło Kolejowej w Warszawie”), zamień je na współrzędne przez geocode_place i podaj
29
+ lat i lng w search_slots. Powiedz klientowi, wokół jakiego miejsca szukałeś. Bez geocode_place
30
+ poproś o włączenie lokalizacji w aplikacji.
28
31
  - Typ usługi ustal przez find_service_type (np. „strzyżenie męskie”). Nigdy nie wymyślaj id.
29
32
  Gdy pasuje kilka typów, dopytaj klienta, o który chodzi.
30
33
  - Terminy znajdź przez search_slots: bez daty = jak najszybciej, z date i time = okolice tej godziny.
@@ -20,6 +20,8 @@ export interface CustomerTexts {
20
20
  salonFallbackName: string;
21
21
  visitFallbackName: string;
22
22
  noLocation: string;
23
+ latAndLngTogether: string;
24
+ badCoordinates: string;
23
25
  bookingOff(problem: string): string;
24
26
  badDay(field: string): string;
25
27
  badTime(field: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"texts.d.ts","sourceRoot":"","sources":["../../src/customer/texts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"texts.d.ts","sourceRoot":"","sources":["../../src/customer/texts.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE;QACN,eAAe,EAAE,MAAM,CAAC;QACxB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC;QACb,gBAAgB,EAAE,MAAM,CAAC;QACzB,MAAM,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAE1B,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAE/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1D,YAAY,EAAE,MAAM,CAAC;IAErB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACtF,WAAW,EAAE,MAAM,CAAC;IAEpB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAExC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAClD,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACjF,eAAe,EAAE,MAAM,CAAC;IAExB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5C,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACnI,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAChD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IAEnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IAEnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACvF,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;CACxB;AAwID,gGAAgG;AAChG,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAG/D;AAED,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAE5D"}
@@ -10,7 +10,9 @@ const PL = {
10
10
  },
11
11
  salonFallbackName: "Salon",
12
12
  visitFallbackName: "wizyta",
13
- noLocation: "Aplikacja nie podała pozycji klienta, więc nie wiem, gdzie szukać. Poproś klienta, żeby włączył lokalizację w aplikacji i otworzył czat ponownie.",
13
+ noLocation: "Aplikacja nie podała pozycji klienta, więc nie wiem, gdzie szukać. Zapytaj klienta, w okolicy jakiego miejsca szukać (ulica, dzielnica, miasto), i podaj współrzędne tego miejsca w lat i lng. Klient może też włączyć lokalizację w aplikacji i otworzyć czat ponownie.",
14
+ latAndLngTogether: "Podaj lat i lng razem albo pomiń oba (szukanie wokół pozycji z aplikacji).",
15
+ badCoordinates: "lat musi być między -90 a 90, a lng między -180 a 180.",
14
16
  bookingOff: (problem) => `Rezerwacje są chwilowo wyłączone: ${problem}.`,
15
17
  badDay: (field) => `Pole "${field}" musi być datą w formacie RRRR-MM-DD.`,
16
18
  badTime: (field) => `Pole "${field}" musi być godziną w formacie GG:MM.`,
@@ -63,7 +65,9 @@ const EN = {
63
65
  },
64
66
  salonFallbackName: "Salon",
65
67
  visitFallbackName: "visit",
66
- noLocation: "The app sent no position of the customer, so I do not know where to search. Ask the customer to turn location on in the app and open the chat again.",
68
+ noLocation: "The app sent no position of the customer, so I do not know where to search. Ask the customer which place to search around (street, district, city) and give that place's coordinates in lat and lng. The customer can also turn location on in the app and open the chat again.",
69
+ latAndLngTogether: "Give lat and lng together, or leave both out (search around the app's position).",
70
+ badCoordinates: "lat must be between -90 and 90, and lng between -180 and 180.",
67
71
  bookingOff: (problem) => `Booking is switched off for the moment: ${problem}.`,
68
72
  badDay: (field) => `Field "${field}" must be a date in the YYYY-MM-DD format.`,
69
73
  badTime: (field) => `Field "${field}" must be a time in the HH:MM format.`,
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/customer/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,OAAO,EAAW,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAmB,gBAAgB,EAAkD,MAAM,yBAAyB,CAAC;AAejI,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,kCAAkC;IAClC,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,qFAAqF;IACrF,KAAK,CAAC,EAAE,aAAa,CAAC;CACzB;AAkDD,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAkFxE;AAUD,wBAAgB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAmCrE;AAkCD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CA4CzE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,CAqF1H;AA+CD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAqC/E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,CA6C5H"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/customer/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D,OAAO,EAAW,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AACvE,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAgC,gBAAgB,EAAkD,MAAM,yBAAyB,CAAC;AAe9I,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,gBAAgB,CAAC;IACvB,kCAAkC;IAClC,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,qFAAqF;IACrF,KAAK,CAAC,EAAE,aAAa,CAAC;CACzB;AAiED,wBAAgB,eAAe,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAsFxE;AAUD,wBAAgB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAmCrE;AAkCD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CA4CzE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,CAqF1H;AA+CD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAqC/E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,GAAG,SAAS,GAAG,SAAS,CA6C5H"}
@@ -44,6 +44,21 @@ function readTimeMinutes(texts, args, key) {
44
44
  throw new ArgumentError(texts.badTime(key));
45
45
  return minutes;
46
46
  }
47
+ /**
48
+ * Where to search: a place the customer named (the model geocodes it and passes `lat` / `lng`)
49
+ * wins over the position the app sent. `null` = neither is known.
50
+ */
51
+ function readSearchLocation(texts, args, fallback) {
52
+ const lat = readOptionalNumber(args, "lat");
53
+ const lng = readOptionalNumber(args, "lng");
54
+ if (lat === undefined && lng === undefined)
55
+ return fallback;
56
+ if (lat === undefined || lng === undefined)
57
+ throw new ArgumentError(texts.latAndLngTogether);
58
+ if (Math.abs(lat) > 90 || Math.abs(lng) > 180)
59
+ throw new ArgumentError(texts.badCoordinates);
60
+ return { lat, lng };
61
+ }
47
62
  function serviceLine(formats) {
48
63
  return (service) => ({ serviceId: service.id, name: service.name, price: formats.price(service.pricePln), duration: formats.duration(service.durationMinutes) });
49
64
  }
@@ -53,7 +68,7 @@ export function searchSlotsTool(context) {
53
68
  return {
54
69
  name: "search_slots",
55
70
  progress: texts.progress.searchSlots,
56
- description: "Szuka salonów w okolicy klienta, które mają wolny termin na usługę danego typu. Bez date i time szuka „jak najszybciej” (najbliższa godzina, a gdy nic nie ma — do 24 godzin naprzód). Z date i time sprawdza okolice tej godziny (pół godziny wcześniej do godziny później). Zwraca salony z usługami (serviceId, cena, czas) i wolnymi terminami. Odległość, cenę i czas trwania dostajesz jako gotowy tekst — przepisz go bez zmian. serviceTypeId weź z find_service_type.",
71
+ description: "Szuka salonów w okolicy klienta, które mają wolny termin na usługę danego typu. Szuka wokół pozycji z aplikacji; gdy klient wskazuje inne miejsce („koło Kolejowej w Warszawie”), podaj lat i lng tego miejsca — odległości liczą się wtedy od niego. Bez date i time szuka „jak najszybciej” (najbliższa godzina, a gdy nic nie ma — do 24 godzin naprzód). Z date i time sprawdza okolice tej godziny (pół godziny wcześniej do godziny później). Zwraca salony z usługami (serviceId, cena, czas) i wolnymi terminami. Odległość, cenę i czas trwania dostajesz jako gotowy tekst — przepisz go bez zmian. serviceTypeId weź z find_service_type.",
57
72
  parameters: {
58
73
  type: "object",
59
74
  properties: {
@@ -61,6 +76,8 @@ export function searchSlotsTool(context) {
61
76
  date: { type: "string", description: "Dzień RRRR-MM-DD. Pomiń razem z time, żeby szukać jak najszybciej." },
62
77
  time: { type: "string", description: "Godzina GG:MM. Wymagana, gdy podajesz date." },
63
78
  radiusKm: { type: "number", description: `Promień szukania w km, domyślnie ${DEFAULT_RADIUS_KM}, najwyżej ${MAX_RADIUS_KM}.` },
79
+ lat: { type: "number", description: "Szerokość geograficzna miejsca wskazanego przez klienta. Tylko razem z lng; pomiń oba, żeby szukać wokół pozycji z aplikacji." },
80
+ lng: { type: "number", description: "Długość geograficzna miejsca wskazanego przez klienta. Tylko razem z lat." },
64
81
  },
65
82
  required: ["serviceTypeId"],
66
83
  additionalProperties: false,
@@ -70,7 +87,8 @@ export function searchSlotsTool(context) {
70
87
  const refusal = zoneRefusal(context);
71
88
  if (refusal)
72
89
  return refusal;
73
- if (!port.location)
90
+ const location = readSearchLocation(texts, args, port.location);
91
+ if (!location)
74
92
  return err("invalid_arguments", texts.noLocation);
75
93
  const formats = formatsOf(context);
76
94
  const serviceTypeId = readString(args, "serviceTypeId");
@@ -84,7 +102,7 @@ export function searchSlotsTool(context) {
84
102
  let found;
85
103
  let lead;
86
104
  if (dayText === undefined) {
87
- found = await port.searchAsap(serviceTypeId, radiusKm, now);
105
+ found = await port.searchAsap(location, serviceTypeId, radiusKm, now);
88
106
  lead = found.phase === "extended" ? texts.leadExtended : texts.leadSoon;
89
107
  }
90
108
  else {
@@ -95,7 +113,7 @@ export function searchSlotsTool(context) {
95
113
  .filter((moment) => moment > now.getTime() && onSlotGrid(new Date(moment).getMinutes()));
96
114
  if (moments.length === 0)
97
115
  return err("invalid_arguments", texts.momentPassed);
98
- found = await port.searchAtMoments(serviceTypeId, radiusKm, moments);
116
+ found = await port.searchAtMoments(location, serviceTypeId, radiusKm, moments);
99
117
  lead = texts.leadAround(day, timeOfMinutes(at));
100
118
  }
101
119
  if (found.salons.length === 0) {
@@ -30,6 +30,7 @@ export declare class IcSalonCorePort implements SalonCorePort {
30
30
  private readonly availability;
31
31
  private readonly assets;
32
32
  private readonly promo;
33
+ private catalog;
33
34
  private readonly discountCodes;
34
35
  constructor(options: IcSalonCorePortOptions);
35
36
  listServices(): Promise<ServiceView[]>;
@@ -38,6 +39,9 @@ export declare class IcSalonCorePort implements SalonCorePort {
38
39
  removeService(serviceId: string): Promise<void>;
39
40
  listWorkers(): Promise<WorkerView[]>;
40
41
  findServiceTypes(query: string): Promise<ServiceTypeView[]>;
42
+ /** The catalogue changes when a superadmin edits it — rarely — while one Booksy import searches
43
+ * it dozens of times. One canister query a minute serves them all. */
44
+ private serviceTypesCatalog;
41
45
  getWeeklyHours(workerId: string, day: number): Promise<TimeRange[]>;
42
46
  setWeeklyHours(workerId: string, day: number, ranges: TimeRange[]): Promise<void>;
43
47
  getDailyBusy(workerId: string, date: string): Promise<TimeRange[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"ic-salon-core-port.d.ts","sourceRoot":"","sources":["../src/ic-salon-core-port.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ic-salon-core-port.d.ts","sourceRoot":"","sources":["../src/ic-salon-core-port.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { Principal } from "@icp-sdk/core/principal";
2
2
  import { AssetsActor, AvailabilityActor, DiscountCodesActor, filterServiceTypes, HrActor, PromoActor, promoMusicOf, SalonServicesActor, ServiceTypesActor, } from "@jsm-mit/sultana-core-motoko-package";
3
+ const SERVICE_TYPES_CATALOG_TTL_MS = 60_000;
3
4
  /**
4
5
  * The port over the published canister wrapper. Conversions live here and nowhere else: the
5
6
  * canister speaks `bigint` and `Principal`, the tools speak numbers and strings.
@@ -14,6 +15,7 @@ export class IcSalonCorePort {
14
15
  availability;
15
16
  assets;
16
17
  promo;
18
+ catalog = null;
17
19
  discountCodes;
18
20
  constructor(options) {
19
21
  this.salonId = options.salonId;
@@ -62,13 +64,29 @@ export class IcSalonCorePort {
62
64
  }));
63
65
  }
64
66
  async findServiceTypes(query) {
65
- const catalog = await this.serviceTypes.getServiceTypesCatalogAsyncUnsafe(this.language);
67
+ const catalog = await this.serviceTypesCatalog();
66
68
  return filterServiceTypes(catalog, query).map((entry) => ({
67
69
  id: entry.id,
68
70
  label: entry.text,
69
71
  category: entry.category,
70
72
  }));
71
73
  }
74
+ /** The catalogue changes when a superadmin edits it — rarely — while one Booksy import searches
75
+ * it dozens of times. One canister query a minute serves them all. */
76
+ serviceTypesCatalog() {
77
+ const now = Date.now();
78
+ if (!this.catalog || now - this.catalog.readAt > SERVICE_TYPES_CATALOG_TTL_MS) {
79
+ const reply = this.serviceTypes.getServiceTypesCatalogAsyncUnsafe(this.language);
80
+ const entry = { readAt: now, reply };
81
+ // A failed read is not kept: the next search asks the canister again.
82
+ reply.catch(() => {
83
+ if (this.catalog === entry)
84
+ this.catalog = null;
85
+ });
86
+ this.catalog = entry;
87
+ }
88
+ return this.catalog.reply;
89
+ }
72
90
  async getWeeklyHours(workerId, day) {
73
91
  return this.availability.getDayFromWeeklyTemplateAsyncUnsafe(BigInt(day), this.salonId, Principal.fromText(workerId));
74
92
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,15 @@
1
1
  export { ConfirmationLedger, DEFAULT_CONFIRMATION_TTL_MS } from "./confirmations.js";
2
2
  export type { ConfirmationLedgerOptions, ConfirmationRefusal, RedeemResult, TurnConfirmations, } from "./confirmations.js";
3
+ export { BooksyReadError } from "./booksy/fetch-page.js";
4
+ export type { FetchFn } from "./booksy/fetch-page.js";
5
+ export type { BooksyImage, BooksyImageKind, BooksyOpenHours, BooksyPriceType, BooksySalon, BooksyService, BooksyServiceCategory, BooksyStaffer, BooksyVariant, } from "./booksy/parse-salon.js";
6
+ export { parseBooksySalon } from "./booksy/parse-salon.js";
7
+ export { toSultanaDraft } from "./booksy/to-sultana.js";
8
+ export type { SultanaDraftDay, SultanaPhotoDraft, SultanaSalonDraft, SultanaSalonDraftProfile, SultanaServiceDraft, SultanaWorkerDraft, } from "./booksy/to-sultana.js";
9
+ export { createBooksyImportTools, createBooksyTools } from "./booksy/import-tools.js";
10
+ export type { CreateBooksyImportToolsOptions, CreateBooksyToolsOptions } from "./booksy/import-tools.js";
11
+ export { BooksyProfileReader, createBooksyReaderTools, readBooksySalonDraft } from "./booksy/tools.js";
12
+ export type { BooksyPhotoFile, BooksyPhotoSink, CreateBooksyReaderToolsOptions } from "./booksy/tools.js";
3
13
  export { createSalonReadTools, createSalonReadToolsFromPort, createSalonTools, createSalonToolsFromPort, } from "./create-salon-tools.js";
4
14
  export type { CreateSalonReadToolsOptions, CreateSalonToolsFromPortOptions, CreateSalonToolsOptions, } from "./create-salon-tools.js";
5
15
  export { createCustomerReadTools, createCustomerReadToolsFromPort, createCustomerTools, createCustomerToolsFromPort, DEFAULT_SALON_TIME_ZONE, } from "./customer/create-customer-tools.js";
@@ -15,7 +25,7 @@ export type { CustomerTexts } from "./customer/texts.js";
15
25
  export { hostTimeZoneProblem } from "./customer/time.js";
16
26
  export { IcSalonCorePort } from "./ic-salon-core-port.js";
17
27
  export type { IcSalonCorePortOptions } from "./ic-salon-core-port.js";
18
- export { SALON_AGENT_PERSONA_PL, SALON_READ_PERSONA_PL } from "./persona.js";
28
+ export { SALON_AGENT_PERSONA_PL, SALON_BOOKSY_PERSONA_PL, SALON_READ_PERSONA_PL } from "./persona.js";
19
29
  export type { DiscountCodeInput, DiscountCodeView, MediaView, PromoView, PromoWriteInput, SalonAccess, SalonCorePort, SalonPromoPort, ServiceTypeView, ServiceView, ServiceWriteInput, SalonSchedulePort, TimeRange, WorkerView, } from "./salon-core-port.js";
20
30
  export { err, needsConfirmation, ok } from "./types.js";
21
31
  export type { AgentTool, ToolErrorCode, ToolParametersSchema, ToolResult } from "./types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACrF,YAAY,EACR,yBAAyB,EACzB,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACH,oBAAoB,EACpB,4BAA4B,EAC5B,gBAAgB,EAChB,wBAAwB,GAC3B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACR,2BAA2B,EAC3B,+BAA+B,EAC/B,uBAAuB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACH,uBAAuB,EACvB,+BAA+B,EAC/B,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,GAC1B,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EACR,8BAA8B,EAC9B,kCAAkC,EAClC,0BAA0B,GAC7B,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EACR,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,cAAc,GACjB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAChF,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxE,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC7E,YAAY,EACR,iBAAiB,EACjB,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,EACd,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,EACT,UAAU,GACb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AACxD,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,4 +1,9 @@
1
1
  export { ConfirmationLedger, DEFAULT_CONFIRMATION_TTL_MS } from "./confirmations.js";
2
+ export { BooksyReadError } from "./booksy/fetch-page.js";
3
+ export { parseBooksySalon } from "./booksy/parse-salon.js";
4
+ export { toSultanaDraft } from "./booksy/to-sultana.js";
5
+ export { createBooksyImportTools, createBooksyTools } from "./booksy/import-tools.js";
6
+ export { BooksyProfileReader, createBooksyReaderTools, readBooksySalonDraft } from "./booksy/tools.js";
2
7
  export { createSalonReadTools, createSalonReadToolsFromPort, createSalonTools, createSalonToolsFromPort, } from "./create-salon-tools.js";
3
8
  export { createCustomerReadTools, createCustomerReadToolsFromPort, createCustomerTools, createCustomerToolsFromPort, DEFAULT_SALON_TIME_ZONE, } from "./customer/create-customer-tools.js";
4
9
  export { IcCustomerCorePort } from "./customer/ic-customer-core-port.js";
@@ -7,5 +12,5 @@ export { CUSTOMER_AGENT_PERSONA_PL, customerAgentPersona } from "./customer/pers
7
12
  export { customerLanguageOf, customerTexts } from "./customer/texts.js";
8
13
  export { hostTimeZoneProblem } from "./customer/time.js";
9
14
  export { IcSalonCorePort } from "./ic-salon-core-port.js";
10
- export { SALON_AGENT_PERSONA_PL, SALON_READ_PERSONA_PL } from "./persona.js";
15
+ export { SALON_AGENT_PERSONA_PL, SALON_BOOKSY_PERSONA_PL, SALON_READ_PERSONA_PL } from "./persona.js";
11
16
  export { err, needsConfirmation, ok } from "./types.js";
package/dist/persona.d.ts CHANGED
@@ -18,4 +18,9 @@ export declare const SALON_AGENT_PERSONA_PL = "Jeste\u015B asystentem w\u0142a\u
18
18
  * promising a change or reporting a hidden service as missing.
19
19
  */
20
20
  export declare const SALON_READ_PERSONA_PL = "Jeste\u015B asystentem w\u0142a\u015Bciciela salonu w aplikacji Sultana.\nRozmawiasz po polsku, kr\u00F3tko i konkretnie. Odpowiadasz na pytania o salon za pomoc\u0105 narz\u0119dzi.\n\nZasady pracy:\n- Zanim odpowiesz o us\u0142ugach, promocjach albo typach us\u0142ug, ZAWSZE wywo\u0142aj narz\u0119dzie\n (list_services, list_promos, find_service_type). Nigdy nie zgaduj.\n- Widzisz tylko AKTYWNE us\u0142ugi i AKTYWNE promocje \u2014 to, co widz\u0105 klientki.\n Powiedz o tym, gdy ma to znaczenie, np. gdy w\u0142a\u015Bciciel pyta o us\u0142ug\u0119, kt\u00F3rej nie ma na li\u015Bcie:\n mo\u017Ce by\u0107 wy\u0142\u0105czona.\n- Nie mo\u017Cesz jeszcze niczego zmienia\u0107. Gdy w\u0142a\u015Bciciel prosi o dodanie, zmian\u0119 albo usuni\u0119cie,\n powiedz, \u017Ce edycja przez czat b\u0119dzie dost\u0119pna wkr\u00F3tce, i wska\u017C stron\u0119 w panelu:\n \u201EUs\u0142ugi\u201D dla us\u0142ug, \u201EPromocje\u201D dla promocji.\n- Ceny podawaj w pe\u0142nych z\u0142otych, czas trwania w minutach.\n- Je\u015Bli narz\u0119dzie zwr\u00F3ci status \"error\", przeczytaj summary i powiedz w\u0142a\u015Bcicielowi,\n co posz\u0142o nie tak. Nie powtarzaj tego samego wywo\u0142ania w k\u00F3\u0142ko.\n- Nie obiecuj zmian, kt\u00F3rych nie mo\u017Cesz wykona\u0107.";
21
+ /**
22
+ * The rules for the Booksy set (`createBooksyTools`) — appended to `SALON_AGENT_PERSONA_PL` by a
23
+ * host that hands those tools to the agent. Separate, because a host may run without them.
24
+ */
25
+ export declare const SALON_BOOKSY_PERSONA_PL = "Kopiowanie salonu z Booksy:\n- Gdy w\u0142a\u015Bcicielka chce przenie\u015B\u0107 co\u015B z Booksy, popro\u015B o adres jej profilu\n (https://booksy.com/pl-pl/<numer>_<nazwa>\u2026), je\u015Bli go nie poda\u0142a.\n- read_booksy_salon i read_booksy_services tylko czytaj\u0105 profil \u2014 niczego nie zapisuj\u0105.\n- Us\u0142ugi kopiuj WY\u0141\u0104CZNIE przez import_booksy_services (wszystkie naraz, jedno potwierdzenie).\n Nie przepisuj ich pojedynczo przez add_service.\n- Podgl\u0105d importu poka\u017C w\u0142a\u015Bcicielce w ca\u0142o\u015Bci, razem z uwagami pod list\u0105. Poprawki\n (\u201Epomi\u0144 X\u201D, \u201EX to inny typ\u201D) podaj w overrides i poka\u017C nowy podgl\u0105d.\n- Gdy import wypisze us\u0142ugi, kt\u00F3rym nie dobra\u0142 typu, dobierz typy przez find_service_type,\n podaj je w overrides i poka\u017C nowy podgl\u0105d.\n- Kopiowania pracownik\u00F3w jeszcze nie ma: wywo\u0142aj import_booksy_workers i przeka\u017C, co odpowie.\n- Nazwy, adresu, opisu i godzin otwarcia salonu nie da si\u0119 jeszcze zmieni\u0107 przez czat \u2014 powiedz to\n wprost. Nie wymy\u015Blaj innego powodu.";
21
26
  //# sourceMappingURL=persona.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["../src/persona.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,i2EAyBoB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,6wCAee,CAAC"}
1
+ {"version":3,"file":"persona.d.ts","sourceRoot":"","sources":["../src/persona.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,i2EAyBoB,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,6wCAee,CAAC;AAElD;;;GAGG;AACH,eAAO,MAAM,uBAAuB,2nCAYE,CAAC"}