@bike4mind/cli 0.1.4 → 0.1.5-feat-automate-cli-publishing.16920

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.
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-43ZKBRTE.js";
8
- import "./chunk-FNZZAMET.js";
9
- import "./chunk-ZT7Z4QVD.js";
7
+ } from "./chunk-I4OCSXED.js";
8
+ import "./chunk-KR3FKYJK.js";
9
+ import "./chunk-5BPMK3EG.js";
10
10
  import {
11
11
  BFLImageService,
12
12
  BaseStorage,
@@ -15,10 +15,10 @@ import {
15
15
  OpenAIBackend,
16
16
  OpenAIImageService,
17
17
  XAIImageService
18
- } from "./chunk-EVPT4ARN.js";
18
+ } from "./chunk-UPNK5QCR.js";
19
19
  import {
20
20
  Logger
21
- } from "./chunk-CC67R4RB.js";
21
+ } from "./chunk-AMDXHL6S.js";
22
22
  import "./chunk-BPFEGDC7.js";
23
23
  import {
24
24
  AiEvents,
@@ -72,7 +72,7 @@ import {
72
72
  XAI_IMAGE_MODELS,
73
73
  b4mLLMTools,
74
74
  getMcpProviderMetadata
75
- } from "./chunk-VVV7WLJ6.js";
75
+ } from "./chunk-PG4BD2XZ.js";
76
76
  import {
77
77
  __require
78
78
  } from "./chunk-PDX44BCA.js";
@@ -3636,8 +3636,8 @@ async function processAndStoreImages(images, context) {
3636
3636
  const buffer = await downloadImage(image);
3637
3637
  const fileType = await fileTypeFromBuffer2(buffer);
3638
3638
  const filename = `${uuidv45()}.${fileType?.ext}`;
3639
- const path10 = await context.imageGenerateStorage.upload(buffer, filename, {});
3640
- return path10;
3639
+ const path11 = await context.imageGenerateStorage.upload(buffer, filename, {});
3640
+ return path11;
3641
3641
  }));
3642
3642
  }
3643
3643
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -4112,15 +4112,126 @@ var mermaidChartTool = {
4112
4112
  };
4113
4113
 
4114
4114
  // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/currentDateTime/index.js
4115
+ var parseDate = (dateStr) => {
4116
+ const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/);
4117
+ if (!match) {
4118
+ throw new Error(`Invalid date format: ${dateStr}. Please use YYYY-MM-DD format.`);
4119
+ }
4120
+ const [, year, month, day] = match;
4121
+ return new Date(parseInt(year), parseInt(month) - 1, parseInt(day));
4122
+ };
4123
+ var getDayOfYear = (date) => {
4124
+ const start = new Date(date.getFullYear(), 0, 0);
4125
+ const diff = date.getTime() - start.getTime();
4126
+ const oneDay = 1e3 * 60 * 60 * 24;
4127
+ return Math.floor(diff / oneDay);
4128
+ };
4129
+ var getWeekNumber = (date) => {
4130
+ const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
4131
+ const dayNum = d.getUTCDay() || 7;
4132
+ d.setUTCDate(d.getUTCDate() + 4 - dayNum);
4133
+ const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
4134
+ return Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
4135
+ };
4136
+ var getJulianDayNumber = (date) => {
4137
+ const year = date.getFullYear();
4138
+ const month = date.getMonth() + 1;
4139
+ const day = date.getDate();
4140
+ const a = Math.floor((14 - month) / 12);
4141
+ const y = year + 4800 - a;
4142
+ const m = month + 12 * a - 3;
4143
+ return day + Math.floor((153 * m + 2) / 5) + 365 * y + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
4144
+ };
4145
+ var daysBetween = (date1, date2) => {
4146
+ const oneDay = 1e3 * 60 * 60 * 24;
4147
+ const diffTime = date2.getTime() - date1.getTime();
4148
+ return Math.round(diffTime / oneDay);
4149
+ };
4150
+ var getDayOfWeekName = (date) => {
4151
+ const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
4152
+ return days[date.getDay()];
4153
+ };
4154
+ var isLeapYear = (year) => {
4155
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
4156
+ };
4115
4157
  var getCurrentDateTime = async (parameters = {}) => {
4116
- const { timezone: timezone2 = "UTC", format = "human", includeTimezone = true, includeWeekday = true } = parameters;
4158
+ const { timezone: timezone2 = "UTC", format = "human", includeTimezone = true, includeWeekday = true, calculate_until, calculate_since, historical_date } = parameters;
4117
4159
  try {
4118
4160
  const now = /* @__PURE__ */ new Date();
4119
4161
  try {
4120
4162
  Intl.DateTimeFormat(void 0, { timeZone: timezone2 });
4121
- } catch (error) {
4163
+ } catch {
4122
4164
  throw new Error(`Invalid timezone: ${timezone2}. Please use IANA timezone format (e.g., "America/New_York", "Europe/London")`);
4123
4165
  }
4166
+ if (historical_date) {
4167
+ const histDate = parseDate(historical_date);
4168
+ const dayName = getDayOfWeekName(histDate);
4169
+ const formattedDate = histDate.toLocaleDateString("en-US", {
4170
+ year: "numeric",
4171
+ month: "long",
4172
+ day: "numeric"
4173
+ });
4174
+ const julianDay = getJulianDayNumber(histDate);
4175
+ const dayOfYear = getDayOfYear(histDate);
4176
+ const weekNum = getWeekNumber(histDate);
4177
+ const leap = isLeapYear(histDate.getFullYear());
4178
+ return `${formattedDate} was a ${dayName}.
4179
+ Day of year: ${dayOfYear}/${leap ? 366 : 365}
4180
+ Week number: ${weekNum}
4181
+ Julian Day Number: ${julianDay}`;
4182
+ }
4183
+ if (calculate_until) {
4184
+ const targetDate = parseDate(calculate_until);
4185
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
4186
+ const days = daysBetween(today, targetDate);
4187
+ if (days < 0) {
4188
+ return `${calculate_until} was ${Math.abs(days)} days ago.`;
4189
+ } else if (days === 0) {
4190
+ return `${calculate_until} is today!`;
4191
+ } else {
4192
+ const weeks = Math.floor(days / 7);
4193
+ const remainingDays = days % 7;
4194
+ let result = `${days} days until ${calculate_until}`;
4195
+ if (weeks > 0) {
4196
+ result += ` (${weeks} week${weeks > 1 ? "s" : ""}`;
4197
+ if (remainingDays > 0) {
4198
+ result += ` and ${remainingDays} day${remainingDays > 1 ? "s" : ""}`;
4199
+ }
4200
+ result += ")";
4201
+ }
4202
+ return result;
4203
+ }
4204
+ }
4205
+ if (calculate_since) {
4206
+ const startDate = parseDate(calculate_since);
4207
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
4208
+ const days = daysBetween(startDate, today);
4209
+ if (days < 0) {
4210
+ return `${calculate_since} is ${Math.abs(days)} days in the future.`;
4211
+ } else if (days === 0) {
4212
+ return `${calculate_since} is today!`;
4213
+ } else {
4214
+ const years = Math.floor(days / 365.25);
4215
+ const remainingDays = Math.round(days % 365.25);
4216
+ const weeks = Math.floor(remainingDays / 7);
4217
+ const daysLeft = remainingDays % 7;
4218
+ let result = `${days.toLocaleString()} days since ${calculate_since}`;
4219
+ if (years > 0) {
4220
+ result += ` (${years} year${years > 1 ? "s" : ""}`;
4221
+ if (remainingDays > 0) {
4222
+ result += `, ${remainingDays} day${remainingDays > 1 ? "s" : ""}`;
4223
+ }
4224
+ result += ")";
4225
+ } else if (weeks > 0) {
4226
+ result += ` (${weeks} week${weeks > 1 ? "s" : ""}`;
4227
+ if (daysLeft > 0) {
4228
+ result += ` and ${daysLeft} day${daysLeft > 1 ? "s" : ""}`;
4229
+ }
4230
+ result += ")";
4231
+ }
4232
+ return result;
4233
+ }
4234
+ }
4124
4235
  const formatOptions = {
4125
4236
  timeZone: timezone2,
4126
4237
  year: "numeric",
@@ -4132,7 +4243,23 @@ var getCurrentDateTime = async (parameters = {}) => {
4132
4243
  ...includeTimezone && { timeZoneName: "short" }
4133
4244
  };
4134
4245
  switch (format) {
4135
- case "iso":
4246
+ case "unix":
4247
+ return `Unix timestamp: ${Math.floor(now.getTime() / 1e3)}`;
4248
+ case "day_of_year": {
4249
+ const dayOfYear = getDayOfYear(now);
4250
+ const totalDays = isLeapYear(now.getFullYear()) ? 366 : 365;
4251
+ const daysRemaining = totalDays - dayOfYear;
4252
+ return `Day ${dayOfYear} of ${totalDays} (${daysRemaining} days remaining in ${now.getFullYear()})`;
4253
+ }
4254
+ case "week_number": {
4255
+ const weekNum = getWeekNumber(now);
4256
+ return `Week ${weekNum} of ${now.getFullYear()} (ISO week number)`;
4257
+ }
4258
+ case "julian_day": {
4259
+ const julianDay = getJulianDayNumber(now);
4260
+ return `Julian Day Number: ${julianDay}`;
4261
+ }
4262
+ case "iso": {
4136
4263
  const offset = now.getTimezoneOffset() * 6e4;
4137
4264
  const localISOTime = new Date(now.getTime() - offset).toISOString();
4138
4265
  const formatter = new Intl.DateTimeFormat("en", {
@@ -4143,10 +4270,12 @@ var getCurrentDateTime = async (parameters = {}) => {
4143
4270
  const timeZonePart = parts.find((part) => part.type === "timeZoneName");
4144
4271
  const timezoneOffset = timeZonePart?.value || "Z";
4145
4272
  return `${localISOTime.slice(0, -1)}${timezoneOffset === "GMT" ? "Z" : timezoneOffset}`;
4146
- case "human":
4273
+ }
4274
+ case "human": {
4147
4275
  const humanFormat = new Intl.DateTimeFormat("en-US", formatOptions);
4148
4276
  return humanFormat.format(now);
4149
- case "detailed":
4277
+ }
4278
+ case "detailed": {
4150
4279
  const detailedFormat = new Intl.DateTimeFormat("en-US", {
4151
4280
  timeZone: timezone2,
4152
4281
  weekday: "long",
@@ -4158,9 +4287,15 @@ var getCurrentDateTime = async (parameters = {}) => {
4158
4287
  timeZoneName: "long"
4159
4288
  });
4160
4289
  const formatted = detailedFormat.format(now);
4161
- return `It's currently ${formatted}`;
4290
+ const dayOfYear = getDayOfYear(now);
4291
+ const weekNum = getWeekNumber(now);
4292
+ const julianDay = getJulianDayNumber(now);
4293
+ const totalDays = isLeapYear(now.getFullYear()) ? 366 : 365;
4294
+ return `It's currently ${formatted}
4295
+ Day ${dayOfYear} of ${totalDays} | Week ${weekNum} | Julian Day ${julianDay}`;
4296
+ }
4162
4297
  default:
4163
- throw new Error(`Unsupported format: ${format}. Supported formats are: iso, human, detailed`);
4298
+ throw new Error(`Unsupported format: ${format}. Supported formats are: iso, human, detailed, unix, day_of_year, week_number, julian_day`);
4164
4299
  }
4165
4300
  } catch (error) {
4166
4301
  if (error instanceof Error) {
@@ -4186,7 +4321,7 @@ var currentDateTimeTool = {
4186
4321
  },
4187
4322
  toolSchema: {
4188
4323
  name: "current_datetime",
4189
- description: "Get the current date and time in the specified timezone. Supports multiple output formats and IANA timezone identifiers.",
4324
+ description: "Get the current date and time with advanced features. Supports multiple output formats, date calculations (days until/since), historical day lookups, and IANA timezone identifiers.",
4190
4325
  parameters: {
4191
4326
  type: "object",
4192
4327
  properties: {
@@ -4196,8 +4331,8 @@ var currentDateTimeTool = {
4196
4331
  },
4197
4332
  format: {
4198
4333
  type: "string",
4199
- description: "Output format for the date/time",
4200
- enum: ["iso", "human", "detailed"]
4334
+ description: "Output format: iso (ISO 8601), human (readable), detailed (with extra info), unix (Unix timestamp), day_of_year (1-366), week_number (ISO week), julian_day (astronomical)",
4335
+ enum: ["iso", "human", "detailed", "unix", "day_of_year", "week_number", "julian_day"]
4201
4336
  },
4202
4337
  includeTimezone: {
4203
4338
  type: "boolean",
@@ -4206,6 +4341,18 @@ var currentDateTimeTool = {
4206
4341
  includeWeekday: {
4207
4342
  type: "boolean",
4208
4343
  description: "Whether to include the day of the week in the output. Defaults to true."
4344
+ },
4345
+ calculate_until: {
4346
+ type: "string",
4347
+ description: 'Calculate days until a future date. Use YYYY-MM-DD format. Example: "2025-12-25" for Christmas countdown.'
4348
+ },
4349
+ calculate_since: {
4350
+ type: "string",
4351
+ description: 'Calculate days since a past date. Use YYYY-MM-DD format. Example: "1990-01-15" for "how long since my birthday".'
4352
+ },
4353
+ historical_date: {
4354
+ type: "string",
4355
+ description: 'Get information about any historical date including day of week, day of year, week number, and Julian Day. Use YYYY-MM-DD format. Example: "1776-07-04".'
4209
4356
  }
4210
4357
  },
4211
4358
  additionalProperties: false,
@@ -4674,8 +4821,8 @@ async function processAndStoreImage(imageUrl, context) {
4674
4821
  const buffer = await downloadImage2(imageUrl);
4675
4822
  const fileType = await fileTypeFromBuffer3(buffer);
4676
4823
  const filename = `${uuidv46()}.${fileType?.ext}`;
4677
- const path10 = await context.imageGenerateStorage.upload(buffer, filename, {});
4678
- return path10;
4824
+ const path11 = await context.imageGenerateStorage.upload(buffer, filename, {});
4825
+ return path11;
4679
4826
  }
4680
4827
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
4681
4828
  await context.onFinish?.("edit_image", storedImagePath);
@@ -5136,121 +5283,1376 @@ function sanitizeJsonString(jsonStr) {
5136
5283
  default:
5137
5284
  result += char;
5138
5285
  }
5139
- } else {
5140
- result += char;
5286
+ } else {
5287
+ result += char;
5288
+ }
5289
+ i++;
5290
+ }
5291
+ return result;
5292
+ }
5293
+ function parseTransformationResult(llmResponse) {
5294
+ if (!llmResponse || !llmResponse.trim()) {
5295
+ throw new Error("LLM returned an empty response. This may be due to content filtering, rate limiting, or a timeout. Please try again.");
5296
+ }
5297
+ const jsonMatch = llmResponse.match(/```json\s*\n([\s\S]*?)\n```/) || llmResponse.match(/```\s*\n([\s\S]*?)\n```/);
5298
+ let jsonStr = jsonMatch ? jsonMatch[1] : llmResponse;
5299
+ jsonStr = jsonStr.trim();
5300
+ if (!jsonStr) {
5301
+ throw new Error("Could not extract valid JSON from LLM response. The response may have been truncated or malformed.");
5302
+ }
5303
+ jsonStr = sanitizeJsonString(jsonStr);
5304
+ try {
5305
+ const result = JSON.parse(jsonStr);
5306
+ if (!result.title || !result.content) {
5307
+ throw new Error("Missing required fields: title and content");
5308
+ }
5309
+ return {
5310
+ title: result.title,
5311
+ content: result.content,
5312
+ summary: result.summary || "",
5313
+ suggestedTags: result.suggestedTags || []
5314
+ };
5315
+ } catch (error) {
5316
+ console.error("Failed to parse transformation result:", { error, response: llmResponse });
5317
+ throw new Error("Failed to parse transformation result from LLM");
5318
+ }
5319
+ }
5320
+ var blogDraftTool = {
5321
+ name: "blog_draft",
5322
+ implementation: (context) => ({
5323
+ toolFn: async (value) => {
5324
+ const params = value;
5325
+ const { logger: logger2, llm } = context;
5326
+ const outputFormat = params.outputFormat || "blog";
5327
+ logger2.info("Drafting blog content", {
5328
+ userId: context.userId,
5329
+ outputFormat,
5330
+ hasVoiceGuide: !!params.voiceGuide,
5331
+ hasAdditionalInstructions: !!params.additionalInstructions
5332
+ });
5333
+ try {
5334
+ const systemPrompt = buildTransformationPrompt({
5335
+ voiceGuide: params.voiceGuide,
5336
+ outputFormat,
5337
+ additionalInstructions: params.additionalInstructions
5338
+ });
5339
+ const modelToUse = context.model || "claude-sonnet-4-5-20250929";
5340
+ logger2.info("Using model for blog draft:", { model: modelToUse });
5341
+ let llmResponse = "";
5342
+ await llm.complete(modelToUse, [
5343
+ { role: "system", content: systemPrompt },
5344
+ {
5345
+ role: "user",
5346
+ content: `Transform this content:
5347
+
5348
+ ${params.sourceContent}`
5349
+ }
5350
+ ], {
5351
+ temperature: 0.7,
5352
+ stream: false
5353
+ }, async (texts) => {
5354
+ llmResponse = texts.filter((t) => t !== null && t !== void 0).join("");
5355
+ });
5356
+ logger2.info("Blog draft complete", { responseLength: llmResponse.length });
5357
+ const result = parseTransformationResult(llmResponse);
5358
+ logger2.info("Blog draft created successfully", {
5359
+ title: result.title,
5360
+ contentLength: result.content.length,
5361
+ tagsCount: result.suggestedTags.length
5362
+ });
5363
+ return `\u2728 Blog draft created successfully!
5364
+
5365
+ \`\`\`json
5366
+ ${JSON.stringify(result, null, 2)}
5367
+ \`\`\`
5368
+
5369
+ \u{1F4CB} The preview card will appear below for you to review and edit before publishing.
5370
+ `;
5371
+ } catch (error) {
5372
+ logger2.error("Blog draft creation failed:", error);
5373
+ throw error;
5374
+ }
5375
+ },
5376
+ toolSchema: {
5377
+ name: "blog_draft",
5378
+ description: '\u{1F3AF} PRIMARY TOOL FOR BLOGGING: Use this tool when user wants to publish conversation/content to their blog. Transforms raw content into structured blog post format. ALWAYS use this for phrases like "publish to my blog", "blog this conversation", "create a blog post". Returns JSON that frontend automatically renders as preview card. This is the FIRST STEP - user will review/edit before final publish. DO NOT skip this step.',
5379
+ parameters: {
5380
+ type: "object",
5381
+ properties: {
5382
+ sourceContent: {
5383
+ type: "string",
5384
+ description: "The content to transform into a blog draft. This can be conversation history, article text, notes, etc. Include all relevant context."
5385
+ },
5386
+ voiceGuide: {
5387
+ type: "string",
5388
+ description: "Optional voice/style guide content (markdown text) to apply. This should contain writing style instructions and patterns."
5389
+ },
5390
+ outputFormat: {
5391
+ type: "string",
5392
+ enum: ["blog", "linkedin", "twitter", "newsletter"],
5393
+ description: 'Output format to generate. Currently only "blog" is fully supported. Others are coming soon.'
5394
+ },
5395
+ additionalInstructions: {
5396
+ type: "string",
5397
+ description: 'Optional additional instructions for the draft. Examples: "Make it more technical", "Add code examples", "Focus on business impact"'
5398
+ }
5399
+ },
5400
+ required: ["sourceContent", "outputFormat"]
5401
+ }
5402
+ }
5403
+ })
5404
+ };
5405
+
5406
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/wikipediaOnThisDay/index.js
5407
+ var parseMonthDay = (dateStr) => {
5408
+ if (!dateStr) {
5409
+ const now = /* @__PURE__ */ new Date();
5410
+ return {
5411
+ month: String(now.getMonth() + 1).padStart(2, "0"),
5412
+ day: String(now.getDate()).padStart(2, "0")
5413
+ };
5414
+ }
5415
+ const mmddMatch = dateStr.match(/^(\d{1,2})-(\d{1,2})$/);
5416
+ if (mmddMatch) {
5417
+ return {
5418
+ month: mmddMatch[1].padStart(2, "0"),
5419
+ day: mmddMatch[2].padStart(2, "0")
5420
+ };
5421
+ }
5422
+ const fullMatch = dateStr.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
5423
+ if (fullMatch) {
5424
+ return {
5425
+ month: fullMatch[2].padStart(2, "0"),
5426
+ day: fullMatch[3].padStart(2, "0")
5427
+ };
5428
+ }
5429
+ throw new Error(`Invalid date format: ${dateStr}. Use MM-DD or YYYY-MM-DD format.`);
5430
+ };
5431
+ var formatEvent = (event) => {
5432
+ return `${event.year}: ${event.text}`;
5433
+ };
5434
+ var filterEvents = (events, yearFilter, decadeFilter) => {
5435
+ if (yearFilter !== void 0) {
5436
+ return events.filter((e) => e.year === yearFilter);
5437
+ }
5438
+ if (decadeFilter !== void 0) {
5439
+ const decadeStart = decadeFilter;
5440
+ const decadeEnd = decadeFilter + 9;
5441
+ return events.filter((e) => e.year >= decadeStart && e.year <= decadeEnd);
5442
+ }
5443
+ return events;
5444
+ };
5445
+ var getWikipediaOnThisDay = async (parameters = {}) => {
5446
+ const { date, type = "selected", limit = 10, year_filter, decade_filter } = parameters;
5447
+ try {
5448
+ const { month, day } = parseMonthDay(date);
5449
+ const url = `https://en.wikipedia.org/api/rest_v1/feed/onthisday/${type}/${month}/${day}`;
5450
+ const response = await fetch(url, {
5451
+ headers: {
5452
+ Accept: "application/json",
5453
+ "User-Agent": "Bike4Mind/1.0 (https://bike4mind.com; contact@bike4mind.com)"
5454
+ }
5455
+ });
5456
+ if (!response.ok) {
5457
+ if (response.status === 404) {
5458
+ throw new Error(`No data available for ${month}-${day}. The date may be invalid.`);
5459
+ }
5460
+ throw new Error(`Wikipedia API error: ${response.status} ${response.statusText}`);
5461
+ }
5462
+ const data = await response.json();
5463
+ const dateLabel = `${parseInt(month)}/${parseInt(day)}`;
5464
+ const results = [];
5465
+ if (type === "all") {
5466
+ if (data.selected?.length) {
5467
+ results.push("**Notable Events:**");
5468
+ const events = filterEvents(data.selected, year_filter, decade_filter).slice(0, Math.ceil(limit / 3));
5469
+ events.forEach((e) => results.push(`- ${formatEvent(e)}`));
5470
+ }
5471
+ if (data.births?.length) {
5472
+ results.push("\n**Notable Births:**");
5473
+ const births = filterEvents(data.births, year_filter, decade_filter).slice(0, Math.ceil(limit / 3));
5474
+ births.forEach((e) => results.push(`- ${formatEvent(e)}`));
5475
+ }
5476
+ if (data.deaths?.length) {
5477
+ results.push("\n**Notable Deaths:**");
5478
+ const deaths = filterEvents(data.deaths, year_filter, decade_filter).slice(0, Math.ceil(limit / 3));
5479
+ deaths.forEach((e) => results.push(`- ${formatEvent(e)}`));
5480
+ }
5481
+ } else if (type === "holidays") {
5482
+ if (data.holidays?.length) {
5483
+ results.push(`**Holidays and Observances on ${dateLabel}:**`);
5484
+ data.holidays.slice(0, limit).forEach((h) => {
5485
+ results.push(`- ${h.text}`);
5486
+ });
5487
+ } else {
5488
+ results.push(`No holidays found for ${dateLabel}.`);
5489
+ }
5490
+ } else {
5491
+ const typeLabels = {
5492
+ selected: "Selected Historical Events",
5493
+ births: "Notable Births",
5494
+ deaths: "Notable Deaths",
5495
+ events: "Historical Events"
5496
+ };
5497
+ const eventData = data[type];
5498
+ if (eventData?.length) {
5499
+ const filtered = filterEvents(eventData, year_filter, decade_filter);
5500
+ const limited = filtered.slice(0, limit);
5501
+ if (limited.length === 0) {
5502
+ const filterDesc = year_filter ? `in ${year_filter}` : decade_filter ? `in the ${decade_filter}s` : "";
5503
+ results.push(`No ${typeLabels[type].toLowerCase()} found for ${dateLabel} ${filterDesc}.`);
5504
+ } else {
5505
+ results.push(`**${typeLabels[type]} on ${dateLabel}:**`);
5506
+ limited.forEach((e) => results.push(`- ${formatEvent(e)}`));
5507
+ if (filtered.length > limit) {
5508
+ results.push(`
5509
+ _...and ${filtered.length - limit} more_`);
5510
+ }
5511
+ }
5512
+ } else {
5513
+ results.push(`No ${typeLabels[type]?.toLowerCase() || "data"} found for ${dateLabel}.`);
5514
+ }
5515
+ }
5516
+ return results.join("\n");
5517
+ } catch (error) {
5518
+ if (error instanceof Error) {
5519
+ throw error;
5520
+ }
5521
+ throw new Error(`Failed to fetch Wikipedia data: ${String(error)}`);
5522
+ }
5523
+ };
5524
+ var wikipediaOnThisDayTool = {
5525
+ name: "wikipedia_on_this_day",
5526
+ implementation: (context) => ({
5527
+ toolFn: async (value) => {
5528
+ const params = value;
5529
+ context.logger.log("\u{1F4C5} WikipediaOnThisDay: Starting execution", params);
5530
+ try {
5531
+ const result = await getWikipediaOnThisDay(params);
5532
+ context.logger.log("\u2705 WikipediaOnThisDay: Execution completed");
5533
+ return result;
5534
+ } catch (error) {
5535
+ context.logger.error("\u274C WikipediaOnThisDay: Execution failed", error);
5536
+ throw error;
5537
+ }
5538
+ },
5539
+ toolSchema: {
5540
+ name: "wikipedia_on_this_day",
5541
+ description: 'Get historical events, births, deaths, and holidays that occurred on a specific date from Wikipedia. Great for "On This Day" features, historical context, and anniversary lookups.',
5542
+ parameters: {
5543
+ type: "object",
5544
+ properties: {
5545
+ date: {
5546
+ type: "string",
5547
+ description: 'The date to look up. Use MM-DD format (e.g., "12-25" for Christmas) or YYYY-MM-DD format. Defaults to today if not specified.'
5548
+ },
5549
+ type: {
5550
+ type: "string",
5551
+ description: 'Type of historical data to retrieve: "selected" (curated notable events), "births" (famous people born), "deaths" (famous people who died), "events" (all historical events), "holidays" (holidays and observances), or "all" (mix of everything).',
5552
+ enum: ["all", "selected", "births", "deaths", "events", "holidays"]
5553
+ },
5554
+ limit: {
5555
+ type: "number",
5556
+ description: "Maximum number of results to return. Defaults to 10."
5557
+ },
5558
+ year_filter: {
5559
+ type: "number",
5560
+ description: "Filter to only show events from a specific year. Example: 1969 to see only events from 1969."
5561
+ },
5562
+ decade_filter: {
5563
+ type: "number",
5564
+ description: "Filter to only show events from a specific decade. Example: 1960 to see events from 1960-1969."
5565
+ }
5566
+ },
5567
+ additionalProperties: false,
5568
+ required: []
5569
+ }
5570
+ }
5571
+ })
5572
+ };
5573
+
5574
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/moonPhase/index.js
5575
+ var PHASE_NAMES = [
5576
+ "New Moon",
5577
+ "Waxing Crescent",
5578
+ "First Quarter",
5579
+ "Waxing Gibbous",
5580
+ "Full Moon",
5581
+ "Waning Gibbous",
5582
+ "Last Quarter",
5583
+ "Waning Crescent"
5584
+ ];
5585
+ var PHASE_EMOJIS = ["\u{1F311}", "\u{1F312}", "\u{1F313}", "\u{1F314}", "\u{1F315}", "\u{1F316}", "\u{1F317}", "\u{1F318}"];
5586
+ var FULL_MOON_NAMES = {
5587
+ 1: "Wolf Moon",
5588
+ 2: "Snow Moon",
5589
+ 3: "Worm Moon",
5590
+ 4: "Pink Moon",
5591
+ 5: "Flower Moon",
5592
+ 6: "Strawberry Moon",
5593
+ 7: "Buck Moon",
5594
+ 8: "Sturgeon Moon",
5595
+ 9: "Harvest Moon",
5596
+ // or Corn Moon - Harvest Moon is the full moon closest to autumn equinox
5597
+ 10: "Hunter's Moon",
5598
+ 11: "Beaver Moon",
5599
+ 12: "Cold Moon"
5600
+ };
5601
+ var getJulianDate = (date) => {
5602
+ const year = date.getFullYear();
5603
+ const month = date.getMonth() + 1;
5604
+ const day = date.getDate() + date.getHours() / 24 + date.getMinutes() / 1440 + date.getSeconds() / 86400;
5605
+ let y = year;
5606
+ let m = month;
5607
+ if (m <= 2) {
5608
+ y -= 1;
5609
+ m += 12;
5610
+ }
5611
+ const a = Math.floor(y / 100);
5612
+ const b = 2 - a + Math.floor(a / 4);
5613
+ return Math.floor(365.25 * (y + 4716)) + Math.floor(30.6001 * (m + 1)) + day + b - 1524.5;
5614
+ };
5615
+ var getMoonAge = (date) => {
5616
+ const SYNODIC_MONTH = 29.53059;
5617
+ const knownNewMoon = new Date(Date.UTC(2e3, 0, 6, 18, 14, 0));
5618
+ const knownNewMoonJD = getJulianDate(knownNewMoon);
5619
+ const currentJD = getJulianDate(date);
5620
+ const daysSinceKnownNewMoon = currentJD - knownNewMoonJD;
5621
+ let moonAge = daysSinceKnownNewMoon % SYNODIC_MONTH;
5622
+ if (moonAge < 0)
5623
+ moonAge += SYNODIC_MONTH;
5624
+ return moonAge;
5625
+ };
5626
+ var getPhaseIndex = (moonAge) => {
5627
+ const SYNODIC_MONTH = 29.53059;
5628
+ const phaseLength = SYNODIC_MONTH / 8;
5629
+ return Math.floor((moonAge + phaseLength / 2) % SYNODIC_MONTH / phaseLength);
5630
+ };
5631
+ var getIllumination = (moonAge) => {
5632
+ const SYNODIC_MONTH = 29.53059;
5633
+ const phase = moonAge / SYNODIC_MONTH * 2 * Math.PI;
5634
+ const illumination = (1 - Math.cos(phase)) / 2 * 100;
5635
+ return Math.round(illumination * 10) / 10;
5636
+ };
5637
+ var daysUntilPhase = (moonAge, targetPhaseDay) => {
5638
+ const SYNODIC_MONTH = 29.53059;
5639
+ let days = targetPhaseDay - moonAge;
5640
+ if (days < 0)
5641
+ days += SYNODIC_MONTH;
5642
+ return Math.round(days * 10) / 10;
5643
+ };
5644
+ var getNextPhaseDate = (date, targetPhaseDay) => {
5645
+ const moonAge = getMoonAge(date);
5646
+ const daysToAdd = daysUntilPhase(moonAge, targetPhaseDay);
5647
+ const nextDate = new Date(date.getTime() + daysToAdd * 24 * 60 * 60 * 1e3);
5648
+ return nextDate;
5649
+ };
5650
+ var parseDate2 = (dateStr) => {
5651
+ if (!dateStr) {
5652
+ return /* @__PURE__ */ new Date();
5653
+ }
5654
+ const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/);
5655
+ if (!match) {
5656
+ throw new Error(`Invalid date format: ${dateStr}. Please use YYYY-MM-DD format.`);
5657
+ }
5658
+ const [, year, month, day] = match;
5659
+ return new Date(parseInt(year), parseInt(month) - 1, parseInt(day), 12, 0, 0);
5660
+ };
5661
+ var getMoonPhase = async (parameters = {}) => {
5662
+ const { date: dateStr, timezone: timezone2 = "UTC" } = parameters;
5663
+ try {
5664
+ try {
5665
+ Intl.DateTimeFormat(void 0, { timeZone: timezone2 });
5666
+ } catch {
5667
+ throw new Error(`Invalid timezone: ${timezone2}. Please use IANA timezone format (e.g., "America/New_York", "Europe/London")`);
5668
+ }
5669
+ const date = parseDate2(dateStr);
5670
+ const moonAge = getMoonAge(date);
5671
+ const phaseIndex = getPhaseIndex(moonAge);
5672
+ const phaseName = PHASE_NAMES[phaseIndex];
5673
+ const phaseEmoji = PHASE_EMOJIS[phaseIndex];
5674
+ const illumination = getIllumination(moonAge);
5675
+ const SYNODIC_MONTH = 29.53059;
5676
+ const daysToNewMoon = daysUntilPhase(moonAge, 0);
5677
+ const daysToFullMoon = daysUntilPhase(moonAge, SYNODIC_MONTH / 2);
5678
+ const daysToFirstQuarter = daysUntilPhase(moonAge, SYNODIC_MONTH / 4);
5679
+ const daysToLastQuarter = daysUntilPhase(moonAge, 3 * SYNODIC_MONTH / 4);
5680
+ const month = date.getMonth() + 1;
5681
+ const traditionalName = FULL_MOON_NAMES[month];
5682
+ const dateFormatter = new Intl.DateTimeFormat("en-US", {
5683
+ timeZone: timezone2,
5684
+ weekday: "long",
5685
+ year: "numeric",
5686
+ month: "long",
5687
+ day: "numeric"
5688
+ });
5689
+ const results = [];
5690
+ results.push(`${phaseEmoji} **${phaseName}**`);
5691
+ results.push("");
5692
+ results.push(`**Date:** ${dateFormatter.format(date)}`);
5693
+ results.push(`**Moon Age:** ${moonAge.toFixed(1)} days into lunar cycle`);
5694
+ results.push(`**Illumination:** ${illumination}%`);
5695
+ if (phaseIndex === 4) {
5696
+ results.push(`**Traditional Name:** ${traditionalName}`);
5697
+ }
5698
+ results.push("");
5699
+ results.push("**Upcoming Phases:**");
5700
+ if (daysToNewMoon > 0.5) {
5701
+ const nextNewMoon = getNextPhaseDate(date, 0);
5702
+ results.push(`- \u{1F311} New Moon: in ${daysToNewMoon.toFixed(1)} days (${nextNewMoon.toLocaleDateString("en-US", { month: "short", day: "numeric" })})`);
5703
+ }
5704
+ if (daysToFirstQuarter > 0.5 && daysToFirstQuarter < SYNODIC_MONTH - 1) {
5705
+ const nextFirst = getNextPhaseDate(date, SYNODIC_MONTH / 4);
5706
+ results.push(`- \u{1F313} First Quarter: in ${daysToFirstQuarter.toFixed(1)} days (${nextFirst.toLocaleDateString("en-US", { month: "short", day: "numeric" })})`);
5707
+ }
5708
+ if (daysToFullMoon > 0.5) {
5709
+ const nextFull = getNextPhaseDate(date, SYNODIC_MONTH / 2);
5710
+ const nextMonth = nextFull.getMonth() + 1;
5711
+ results.push(`- \u{1F315} Full Moon (${FULL_MOON_NAMES[nextMonth]}): in ${daysToFullMoon.toFixed(1)} days (${nextFull.toLocaleDateString("en-US", { month: "short", day: "numeric" })})`);
5712
+ }
5713
+ if (daysToLastQuarter > 0.5 && daysToLastQuarter < SYNODIC_MONTH - 1) {
5714
+ const nextLast = getNextPhaseDate(date, 3 * SYNODIC_MONTH / 4);
5715
+ results.push(`- \u{1F317} Last Quarter: in ${daysToLastQuarter.toFixed(1)} days (${nextLast.toLocaleDateString("en-US", { month: "short", day: "numeric" })})`);
5716
+ }
5717
+ results.push("");
5718
+ results.push(`_Lunar cycle: ${SYNODIC_MONTH.toFixed(2)} days (synodic month)_`);
5719
+ return results.join("\n");
5720
+ } catch (error) {
5721
+ if (error instanceof Error) {
5722
+ throw error;
5723
+ }
5724
+ throw new Error(`Failed to calculate moon phase: ${String(error)}`);
5725
+ }
5726
+ };
5727
+ var moonPhaseTool = {
5728
+ name: "moon_phase",
5729
+ implementation: (context) => ({
5730
+ toolFn: async (value) => {
5731
+ const params = value;
5732
+ context.logger.log("\u{1F319} MoonPhase: Starting execution", params);
5733
+ try {
5734
+ const result = await getMoonPhase(params);
5735
+ context.logger.log("\u2705 MoonPhase: Execution completed");
5736
+ return result;
5737
+ } catch (error) {
5738
+ context.logger.error("\u274C MoonPhase: Execution failed", error);
5739
+ throw error;
5740
+ }
5741
+ },
5742
+ toolSchema: {
5743
+ name: "moon_phase",
5744
+ description: "Get the current moon phase with illumination percentage, moon age, traditional moon names, and upcoming lunar phase predictions. Uses astronomical calculations for accuracy.",
5745
+ parameters: {
5746
+ type: "object",
5747
+ properties: {
5748
+ date: {
5749
+ type: "string",
5750
+ description: "The date to check moon phase for. Use YYYY-MM-DD format. Defaults to today if not specified."
5751
+ },
5752
+ timezone: {
5753
+ type: "string",
5754
+ description: 'IANA timezone identifier (e.g., "America/New_York"). Used for date display. Defaults to UTC.'
5755
+ }
5756
+ },
5757
+ additionalProperties: false,
5758
+ required: []
5759
+ }
5760
+ }
5761
+ })
5762
+ };
5763
+
5764
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/sunriseSunset/index.js
5765
+ var toRadians = (degrees) => degrees * (Math.PI / 180);
5766
+ var toDegrees = (radians) => radians * (180 / Math.PI);
5767
+ var getJulianDay = (date) => {
5768
+ const year = date.getFullYear();
5769
+ const month = date.getMonth() + 1;
5770
+ const day = date.getDate();
5771
+ const a = Math.floor((14 - month) / 12);
5772
+ const y = year + 4800 - a;
5773
+ const m = month + 12 * a - 3;
5774
+ return day + Math.floor((153 * m + 2) / 5) + 365 * y + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
5775
+ };
5776
+ var getJulianCentury = (julianDay) => {
5777
+ return (julianDay - 2451545) / 36525;
5778
+ };
5779
+ var getSunMeanLongitude = (julianCentury) => {
5780
+ let L0 = 280.46646 + julianCentury * (36000.76983 + 3032e-7 * julianCentury);
5781
+ while (L0 > 360)
5782
+ L0 -= 360;
5783
+ while (L0 < 0)
5784
+ L0 += 360;
5785
+ return L0;
5786
+ };
5787
+ var getSunMeanAnomaly = (julianCentury) => {
5788
+ return 357.52911 + julianCentury * (35999.05029 - 1537e-7 * julianCentury);
5789
+ };
5790
+ var getEccentricity = (julianCentury) => {
5791
+ return 0.016708634 - julianCentury * (42037e-9 + 1267e-10 * julianCentury);
5792
+ };
5793
+ var getSunEquationOfCenter = (julianCentury, meanAnomaly) => {
5794
+ const M = toRadians(meanAnomaly);
5795
+ return Math.sin(M) * (1.914602 - julianCentury * (4817e-6 + 14e-6 * julianCentury)) + Math.sin(2 * M) * (0.019993 - 101e-6 * julianCentury) + Math.sin(3 * M) * 289e-6;
5796
+ };
5797
+ var getSunTrueLongitude = (meanLongitude, equationOfCenter) => {
5798
+ return meanLongitude + equationOfCenter;
5799
+ };
5800
+ var getSunApparentLongitude = (trueLongitude, julianCentury) => {
5801
+ const omega = 125.04 - 1934.136 * julianCentury;
5802
+ return trueLongitude - 569e-5 - 478e-5 * Math.sin(toRadians(omega));
5803
+ };
5804
+ var getMeanObliquity = (julianCentury) => {
5805
+ const seconds = 21.448 - julianCentury * (46.815 + julianCentury * (59e-5 - julianCentury * 1813e-6));
5806
+ return 23 + (26 + seconds / 60) / 60;
5807
+ };
5808
+ var getObliquityCorrection = (julianCentury, meanObliquity) => {
5809
+ const omega = 125.04 - 1934.136 * julianCentury;
5810
+ return meanObliquity + 256e-5 * Math.cos(toRadians(omega));
5811
+ };
5812
+ var getSunDeclination = (obliquityCorrection, apparentLongitude) => {
5813
+ return toDegrees(Math.asin(Math.sin(toRadians(obliquityCorrection)) * Math.sin(toRadians(apparentLongitude))));
5814
+ };
5815
+ var getEquationOfTime = (julianCentury, meanLongitude, eccentricity, meanAnomaly, obliquityCorrection) => {
5816
+ const y = Math.tan(toRadians(obliquityCorrection / 2)) ** 2;
5817
+ const L0 = toRadians(meanLongitude);
5818
+ const e = eccentricity;
5819
+ const M = toRadians(meanAnomaly);
5820
+ const EoT = y * Math.sin(2 * L0) - 2 * e * Math.sin(M) + 4 * e * y * Math.sin(M) * Math.cos(2 * L0) - 0.5 * y * y * Math.sin(4 * L0) - 1.25 * e * e * Math.sin(2 * M);
5821
+ return toDegrees(EoT) * 4;
5822
+ };
5823
+ var getHourAngle = (latitude, declination, zenith) => {
5824
+ const latRad = toRadians(latitude);
5825
+ const decRad = toRadians(declination);
5826
+ const zenithRad = toRadians(zenith);
5827
+ const cosHA = Math.cos(zenithRad) / (Math.cos(latRad) * Math.cos(decRad)) - Math.tan(latRad) * Math.tan(decRad);
5828
+ if (cosHA > 1 || cosHA < -1) {
5829
+ return null;
5830
+ }
5831
+ return toDegrees(Math.acos(cosHA));
5832
+ };
5833
+ var getSolarNoon = (longitude, equationOfTime) => {
5834
+ return 720 - 4 * longitude - equationOfTime;
5835
+ };
5836
+ var minutesToTime = (minutes, timezone2) => {
5837
+ const date = /* @__PURE__ */ new Date();
5838
+ date.setUTCHours(0, 0, 0, 0);
5839
+ date.setUTCMinutes(Math.round(minutes));
5840
+ return date.toLocaleTimeString("en-US", {
5841
+ timeZone: timezone2,
5842
+ hour: "numeric",
5843
+ minute: "2-digit"
5844
+ });
5845
+ };
5846
+ var formatDuration = (minutes) => {
5847
+ const hours = Math.floor(minutes / 60);
5848
+ const mins = Math.round(minutes % 60);
5849
+ return `${hours}h ${mins}m`;
5850
+ };
5851
+ var parseDate3 = (dateStr) => {
5852
+ if (!dateStr) {
5853
+ return /* @__PURE__ */ new Date();
5854
+ }
5855
+ const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/);
5856
+ if (!match) {
5857
+ throw new Error(`Invalid date format: ${dateStr}. Please use YYYY-MM-DD format.`);
5858
+ }
5859
+ const [, year, month, day] = match;
5860
+ return new Date(parseInt(year), parseInt(month) - 1, parseInt(day), 12, 0, 0);
5861
+ };
5862
+ var ZENITH = {
5863
+ OFFICIAL: 90.833,
5864
+ // Sunrise/sunset (accounting for refraction)
5865
+ CIVIL: 96,
5866
+ // Civil twilight (6 degrees below horizon)
5867
+ NAUTICAL: 102,
5868
+ // Nautical twilight (12 degrees below horizon)
5869
+ ASTRONOMICAL: 108,
5870
+ // Astronomical twilight (18 degrees below horizon)
5871
+ GOLDEN_HOUR: 84
5872
+ // Golden hour (approximate)
5873
+ };
5874
+ var getSunriseSunset = async (parameters) => {
5875
+ const { latitude, longitude, date: dateStr, timezone: timezone2 = "UTC" } = parameters;
5876
+ if (latitude === void 0 || latitude === null) {
5877
+ throw new Error("Latitude is required. Please provide a value between -90 and 90.");
5878
+ }
5879
+ if (longitude === void 0 || longitude === null) {
5880
+ throw new Error("Longitude is required. Please provide a value between -180 and 180.");
5881
+ }
5882
+ if (latitude < -90 || latitude > 90) {
5883
+ throw new Error(`Invalid latitude: ${latitude}. Must be between -90 and 90.`);
5884
+ }
5885
+ if (longitude < -180 || longitude > 180) {
5886
+ throw new Error(`Invalid longitude: ${longitude}. Must be between -180 and 180.`);
5887
+ }
5888
+ try {
5889
+ Intl.DateTimeFormat(void 0, { timeZone: timezone2 });
5890
+ } catch {
5891
+ throw new Error(`Invalid timezone: ${timezone2}. Please use IANA timezone format (e.g., "America/New_York", "Europe/London")`);
5892
+ }
5893
+ const date = parseDate3(dateStr);
5894
+ const julianDay = getJulianDay(date);
5895
+ const julianCentury = getJulianCentury(julianDay);
5896
+ const meanLongitude = getSunMeanLongitude(julianCentury);
5897
+ const meanAnomaly = getSunMeanAnomaly(julianCentury);
5898
+ const eccentricity = getEccentricity(julianCentury);
5899
+ const equationOfCenter = getSunEquationOfCenter(julianCentury, meanAnomaly);
5900
+ const trueLongitude = getSunTrueLongitude(meanLongitude, equationOfCenter);
5901
+ const apparentLongitude = getSunApparentLongitude(trueLongitude, julianCentury);
5902
+ const meanObliquity = getMeanObliquity(julianCentury);
5903
+ const obliquityCorrection = getObliquityCorrection(julianCentury, meanObliquity);
5904
+ const declination = getSunDeclination(obliquityCorrection, apparentLongitude);
5905
+ const equationOfTime = getEquationOfTime(julianCentury, meanLongitude, eccentricity, meanAnomaly, obliquityCorrection);
5906
+ const solarNoon = getSolarNoon(longitude, equationOfTime);
5907
+ const calculateTime = (zenith, isSunrise) => {
5908
+ const hourAngle = getHourAngle(latitude, declination, zenith);
5909
+ if (hourAngle === null)
5910
+ return null;
5911
+ const minutes = solarNoon + (isSunrise ? -hourAngle : hourAngle) * 4;
5912
+ return minutesToTime(minutes, timezone2);
5913
+ };
5914
+ const sunrise = calculateTime(ZENITH.OFFICIAL, true);
5915
+ const sunset = calculateTime(ZENITH.OFFICIAL, false);
5916
+ const civilDawn = calculateTime(ZENITH.CIVIL, true);
5917
+ const civilDusk = calculateTime(ZENITH.CIVIL, false);
5918
+ const nauticalDawn = calculateTime(ZENITH.NAUTICAL, true);
5919
+ const nauticalDusk = calculateTime(ZENITH.NAUTICAL, false);
5920
+ const astroDawn = calculateTime(ZENITH.ASTRONOMICAL, true);
5921
+ const astroDusk = calculateTime(ZENITH.ASTRONOMICAL, false);
5922
+ const goldenHourMorningEnd = calculateTime(ZENITH.GOLDEN_HOUR, true);
5923
+ const goldenHourEveningStart = calculateTime(ZENITH.GOLDEN_HOUR, false);
5924
+ const sunriseHA = getHourAngle(latitude, declination, ZENITH.OFFICIAL);
5925
+ const dayLength = sunriseHA !== null ? sunriseHA * 8 : null;
5926
+ const yesterdayDate = new Date(date);
5927
+ yesterdayDate.setDate(yesterdayDate.getDate() - 1);
5928
+ const yesterdayJD = getJulianDay(yesterdayDate);
5929
+ const yesterdayJC = getJulianCentury(yesterdayJD);
5930
+ const yesterdayML = getSunMeanLongitude(yesterdayJC);
5931
+ const yesterdayMA = getSunMeanAnomaly(yesterdayJC);
5932
+ const yesterdayEOC = getSunEquationOfCenter(yesterdayJC, yesterdayMA);
5933
+ const yesterdayTL = getSunTrueLongitude(yesterdayML, yesterdayEOC);
5934
+ const yesterdayAL = getSunApparentLongitude(yesterdayTL, yesterdayJC);
5935
+ const yesterdayMO = getMeanObliquity(yesterdayJC);
5936
+ const yesterdayOC = getObliquityCorrection(yesterdayJC, yesterdayMO);
5937
+ const yesterdayDec = getSunDeclination(yesterdayOC, yesterdayAL);
5938
+ const yesterdayHA = getHourAngle(latitude, yesterdayDec, ZENITH.OFFICIAL);
5939
+ const yesterdayDayLength = yesterdayHA !== null ? yesterdayHA * 8 : null;
5940
+ let dayLengthChange = null;
5941
+ let changingDirection = null;
5942
+ if (dayLength !== null && yesterdayDayLength !== null) {
5943
+ dayLengthChange = dayLength - yesterdayDayLength;
5944
+ changingDirection = dayLengthChange > 0 ? "longer" : "shorter";
5945
+ }
5946
+ const dateFormatter = new Intl.DateTimeFormat("en-US", {
5947
+ timeZone: timezone2,
5948
+ weekday: "long",
5949
+ year: "numeric",
5950
+ month: "long",
5951
+ day: "numeric"
5952
+ });
5953
+ const results = [];
5954
+ results.push(`**Sun Times for ${dateFormatter.format(date)}**`);
5955
+ results.push(`\u{1F4CD} Location: ${Math.abs(latitude).toFixed(4)}\xB0${latitude >= 0 ? "N" : "S"}, ${Math.abs(longitude).toFixed(4)}\xB0${longitude >= 0 ? "E" : "W"}`);
5956
+ results.push("");
5957
+ if (!sunrise || !sunset) {
5958
+ if (declination > 0 && latitude > 66.5) {
5959
+ results.push("\u2600\uFE0F **Midnight Sun** - The sun does not set at this latitude today.");
5960
+ } else if (declination < 0 && latitude < -66.5) {
5961
+ results.push("\u2600\uFE0F **Midnight Sun** - The sun does not set at this latitude today.");
5962
+ } else if (declination < 0 && latitude > 66.5) {
5963
+ results.push("\u{1F311} **Polar Night** - The sun does not rise at this latitude today.");
5964
+ } else if (declination > 0 && latitude < -66.5) {
5965
+ results.push("\u{1F311} **Polar Night** - The sun does not rise at this latitude today.");
5966
+ }
5967
+ return results.join("\n");
5968
+ }
5969
+ results.push("**Main Events:**");
5970
+ results.push(`- \u{1F305} Sunrise: ${sunrise}`);
5971
+ results.push(`- \u2600\uFE0F Solar Noon: ${minutesToTime(solarNoon, timezone2)}`);
5972
+ results.push(`- \u{1F307} Sunset: ${sunset}`);
5973
+ if (dayLength !== null) {
5974
+ results.push(`- \u23F1\uFE0F Day Length: ${formatDuration(dayLength)}`);
5975
+ if (dayLengthChange !== null && Math.abs(dayLengthChange) >= 0.1) {
5976
+ const changeSeconds = Math.round(Math.abs(dayLengthChange) * 60);
5977
+ results.push(` _(Days are getting ${changingDirection} by ~${Math.floor(changeSeconds / 60)}m ${changeSeconds % 60}s)_`);
5978
+ }
5979
+ }
5980
+ results.push("");
5981
+ results.push("**Twilight Periods:**");
5982
+ if (astroDawn)
5983
+ results.push(`- \u{1F30C} Astronomical Dawn: ${astroDawn}`);
5984
+ if (nauticalDawn)
5985
+ results.push(`- \u{1F30A} Nautical Dawn: ${nauticalDawn}`);
5986
+ if (civilDawn)
5987
+ results.push(`- \u{1F3D9}\uFE0F Civil Dawn: ${civilDawn}`);
5988
+ if (civilDusk)
5989
+ results.push(`- \u{1F3D9}\uFE0F Civil Dusk: ${civilDusk}`);
5990
+ if (nauticalDusk)
5991
+ results.push(`- \u{1F30A} Nautical Dusk: ${nauticalDusk}`);
5992
+ if (astroDusk)
5993
+ results.push(`- \u{1F30C} Astronomical Dusk: ${astroDusk}`);
5994
+ results.push("");
5995
+ results.push("**Golden Hour (Photography):**");
5996
+ if (sunrise && goldenHourMorningEnd) {
5997
+ results.push(`- \u{1F4F8} Morning: ${sunrise} - ${goldenHourMorningEnd}`);
5998
+ }
5999
+ if (goldenHourEveningStart && sunset) {
6000
+ results.push(`- \u{1F4F8} Evening: ${goldenHourEveningStart} - ${sunset}`);
6001
+ }
6002
+ return results.join("\n");
6003
+ };
6004
+ var sunriseSunsetTool = {
6005
+ name: "sunrise_sunset",
6006
+ implementation: (context) => ({
6007
+ toolFn: async (value) => {
6008
+ const params = value;
6009
+ context.logger.log("\u{1F305} SunriseSunset: Starting execution", params);
6010
+ try {
6011
+ const result = await getSunriseSunset(params);
6012
+ context.logger.log("\u2705 SunriseSunset: Execution completed");
6013
+ return result;
6014
+ } catch (error) {
6015
+ context.logger.error("\u274C SunriseSunset: Execution failed", error);
6016
+ throw error;
6017
+ }
6018
+ },
6019
+ toolSchema: {
6020
+ name: "sunrise_sunset",
6021
+ description: "Calculate sunrise, sunset, twilight times, golden hour, and day length for any location. Uses NOAA solar calculations for accuracy. Also shows how day length is changing (getting longer or shorter).",
6022
+ parameters: {
6023
+ type: "object",
6024
+ properties: {
6025
+ latitude: {
6026
+ type: "number",
6027
+ description: "Latitude in decimal degrees (-90 to 90). Positive for North, negative for South. Example: 40.7128 for New York City."
6028
+ },
6029
+ longitude: {
6030
+ type: "number",
6031
+ description: "Longitude in decimal degrees (-180 to 180). Positive for East, negative for West. Example: -74.0060 for New York City."
6032
+ },
6033
+ date: {
6034
+ type: "string",
6035
+ description: "The date to calculate for. Use YYYY-MM-DD format. Defaults to today if not specified."
6036
+ },
6037
+ timezone: {
6038
+ type: "string",
6039
+ description: 'IANA timezone identifier for time display (e.g., "America/New_York"). Defaults to UTC.'
6040
+ }
6041
+ },
6042
+ additionalProperties: false,
6043
+ required: ["latitude", "longitude"]
6044
+ }
6045
+ }
6046
+ })
6047
+ };
6048
+
6049
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/issTracker/index.js
6050
+ var getLocationDescription = (lat, lon) => {
6051
+ if (lat > 66.5)
6052
+ return "Arctic region";
6053
+ if (lat < -66.5)
6054
+ return "Antarctic region";
6055
+ const ewDir = lon >= 0 ? "Eastern" : "Western";
6056
+ if (lon >= -80 && lon <= 0 && lat >= 0 && lat <= 60 || lon >= -80 && lon <= 0 && lat >= -60 && lat < 0) {
6057
+ return "Atlantic Ocean";
6058
+ }
6059
+ if (lon >= 100 && lon <= 180 || lon >= -180 && lon <= -100) {
6060
+ if (lat >= -60 && lat <= 60)
6061
+ return "Pacific Ocean";
6062
+ }
6063
+ if (lon >= 40 && lon <= 100 && lat >= -40 && lat <= 30) {
6064
+ return "Indian Ocean";
6065
+ }
6066
+ if (lat >= 25 && lat <= 50 && lon >= -130 && lon <= -60)
6067
+ return "North America";
6068
+ if (lat >= -55 && lat < 15 && lon >= -80 && lon <= -35)
6069
+ return "South America";
6070
+ if (lat >= 35 && lat <= 72 && lon >= -10 && lon <= 40)
6071
+ return "Europe";
6072
+ if (lat >= -35 && lat <= 37 && lon >= -20 && lon <= 55)
6073
+ return "Africa";
6074
+ if (lat >= 10 && lat <= 55 && lon >= 60 && lon <= 145)
6075
+ return "Asia";
6076
+ if (lat >= -50 && lat <= -10 && lon >= 110 && lon <= 180)
6077
+ return "Australia/Oceania";
6078
+ return `${ewDir} Hemisphere`;
6079
+ };
6080
+ var calculateDistance = (lat1, lon1, lat2, lon2) => {
6081
+ const R = 6371;
6082
+ const dLat = (lat2 - lat1) * (Math.PI / 180);
6083
+ const dLon = (lon2 - lon1) * (Math.PI / 180);
6084
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * (Math.PI / 180)) * Math.cos(lat2 * (Math.PI / 180)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
6085
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
6086
+ return R * c;
6087
+ };
6088
+ var getISSData = async (parameters) => {
6089
+ const { operation, latitude, longitude } = parameters;
6090
+ const results = [];
6091
+ try {
6092
+ if (operation === "current_position") {
6093
+ const response = await fetch("http://api.open-notify.org/iss-now.json");
6094
+ if (!response.ok) {
6095
+ throw new Error(`ISS API error: ${response.status} ${response.statusText}`);
6096
+ }
6097
+ const data = await response.json();
6098
+ if (data.message !== "success") {
6099
+ throw new Error("Failed to get ISS position from API");
6100
+ }
6101
+ const issLat = parseFloat(data.iss_position.latitude);
6102
+ const issLon = parseFloat(data.iss_position.longitude);
6103
+ const timestamp = new Date(data.timestamp * 1e3);
6104
+ results.push("\u{1F6F0}\uFE0F **International Space Station - Current Position**");
6105
+ results.push("");
6106
+ results.push(`**Coordinates:**`);
6107
+ results.push(`- Latitude: ${issLat.toFixed(4)}\xB0 ${issLat >= 0 ? "N" : "S"}`);
6108
+ results.push(`- Longitude: ${issLon.toFixed(4)}\xB0 ${issLon >= 0 ? "E" : "W"}`);
6109
+ results.push("");
6110
+ results.push(`**Location:** Currently over ${getLocationDescription(issLat, issLon)}`);
6111
+ results.push(`**Timestamp:** ${timestamp.toUTCString()}`);
6112
+ results.push("");
6113
+ results.push("**ISS Facts:**");
6114
+ results.push("- Altitude: ~420 km (260 miles) above Earth");
6115
+ results.push("- Speed: ~28,000 km/h (17,500 mph)");
6116
+ results.push("- Orbits Earth every ~90 minutes");
6117
+ results.push("- Visible from Earth at dawn/dusk when sunlit");
6118
+ if (latitude !== void 0 && longitude !== void 0) {
6119
+ const distance = calculateDistance(latitude, longitude, issLat, issLon);
6120
+ results.push("");
6121
+ results.push("**Distance from Your Location:**");
6122
+ results.push(`- Ground distance: ${Math.round(distance).toLocaleString()} km (${Math.round(distance * 0.621371).toLocaleString()} miles)`);
6123
+ if (distance < 2e3) {
6124
+ results.push("- \u2B50 The ISS may be visible from your location if conditions are right!");
6125
+ }
6126
+ }
6127
+ results.push("");
6128
+ results.push("_Track the ISS in real-time: https://spotthestation.nasa.gov/_");
6129
+ } else if (operation === "crew") {
6130
+ const response = await fetch("http://api.open-notify.org/astros.json");
6131
+ if (!response.ok) {
6132
+ throw new Error(`Astronauts API error: ${response.status} ${response.statusText}`);
6133
+ }
6134
+ const data = await response.json();
6135
+ if (data.message !== "success") {
6136
+ throw new Error("Failed to get astronaut data from API");
6137
+ }
6138
+ const craftGroups = {};
6139
+ for (const person of data.people) {
6140
+ if (!craftGroups[person.craft]) {
6141
+ craftGroups[person.craft] = [];
6142
+ }
6143
+ craftGroups[person.craft].push(person.name);
6144
+ }
6145
+ results.push("\u{1F468}\u200D\u{1F680} **People Currently in Space**");
6146
+ results.push("");
6147
+ results.push(`**Total:** ${data.number} astronauts/cosmonauts`);
6148
+ results.push("");
6149
+ for (const [craft, crew] of Object.entries(craftGroups)) {
6150
+ const emoji = craft === "ISS" ? "\u{1F6F0}\uFE0F" : craft.includes("Shenzhou") ? "\u{1F1E8}\u{1F1F3}" : "\u{1F680}";
6151
+ results.push(`**${emoji} ${craft}** (${crew.length} crew):`);
6152
+ crew.forEach((name) => results.push(`- ${name}`));
6153
+ results.push("");
6154
+ }
6155
+ results.push("_Data from NASA Open Notify API_");
6156
+ } else {
6157
+ throw new Error(`Unknown operation: ${operation}. Supported operations: current_position, crew`);
6158
+ }
6159
+ return results.join("\n");
6160
+ } catch (error) {
6161
+ if (error instanceof Error) {
6162
+ throw error;
6163
+ }
6164
+ throw new Error(`Failed to fetch ISS data: ${String(error)}`);
6165
+ }
6166
+ };
6167
+ var issTrackerTool = {
6168
+ name: "iss_tracker",
6169
+ implementation: (context) => ({
6170
+ toolFn: async (value) => {
6171
+ const params = value;
6172
+ context.logger.log("\u{1F6F0}\uFE0F ISSTracker: Starting execution", params);
6173
+ try {
6174
+ const result = await getISSData(params);
6175
+ context.logger.log("\u2705 ISSTracker: Execution completed");
6176
+ return result;
6177
+ } catch (error) {
6178
+ context.logger.error("\u274C ISSTracker: Execution failed", error);
6179
+ throw error;
6180
+ }
6181
+ },
6182
+ toolSchema: {
6183
+ name: "iss_tracker",
6184
+ description: "Track the International Space Station in real-time. Get current position, or see who is currently in space (on ISS and other spacecraft).",
6185
+ parameters: {
6186
+ type: "object",
6187
+ properties: {
6188
+ operation: {
6189
+ type: "string",
6190
+ description: 'What information to retrieve: "current_position" for ISS location, "crew" for people currently in space.',
6191
+ enum: ["current_position", "crew"]
6192
+ },
6193
+ latitude: {
6194
+ type: "number",
6195
+ description: "Your latitude (optional). If provided with longitude, will show distance from your location to ISS. Example: 40.7128 for NYC."
6196
+ },
6197
+ longitude: {
6198
+ type: "number",
6199
+ description: "Your longitude (optional). If provided with latitude, will show distance from your location to ISS. Example: -74.0060 for NYC."
6200
+ }
6201
+ },
6202
+ additionalProperties: false,
6203
+ required: ["operation"]
6204
+ }
6205
+ }
6206
+ })
6207
+ };
6208
+
6209
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/planetVisibility/index.js
6210
+ var PLANETS = [
6211
+ {
6212
+ name: "Mercury",
6213
+ symbol: "\u263F",
6214
+ a: 0.38709927,
6215
+ e: 0.20563593,
6216
+ i: 7.00497902,
6217
+ L: 252.2503235,
6218
+ longPeri: 77.45779628,
6219
+ longNode: 48.33076593,
6220
+ aRate: 37e-8,
6221
+ eRate: 1906e-8,
6222
+ iRate: -594749e-8,
6223
+ LRate: 149472.67411175,
6224
+ longPeriRate: 0.16047689,
6225
+ longNodeRate: -0.12534081,
6226
+ maxMagnitude: -1.9,
6227
+ color: "gray"
6228
+ },
6229
+ {
6230
+ name: "Venus",
6231
+ symbol: "\u2640",
6232
+ a: 0.72333566,
6233
+ e: 677672e-8,
6234
+ i: 3.39467605,
6235
+ L: 181.9790995,
6236
+ longPeri: 131.60246718,
6237
+ longNode: 76.67984255,
6238
+ aRate: 39e-7,
6239
+ eRate: -4107e-8,
6240
+ iRate: -7889e-7,
6241
+ LRate: 58517.81538729,
6242
+ longPeriRate: 268329e-8,
6243
+ longNodeRate: -0.27769418,
6244
+ maxMagnitude: -4.6,
6245
+ color: "white"
6246
+ },
6247
+ {
6248
+ name: "Mars",
6249
+ symbol: "\u2642",
6250
+ a: 1.52371034,
6251
+ e: 0.0933941,
6252
+ i: 1.84969142,
6253
+ L: -4.55343205,
6254
+ longPeri: -23.94362959,
6255
+ longNode: 49.55953891,
6256
+ aRate: 1847e-8,
6257
+ eRate: 7882e-8,
6258
+ iRate: -813131e-8,
6259
+ LRate: 19140.30268499,
6260
+ longPeriRate: 0.44441088,
6261
+ longNodeRate: -0.29257343,
6262
+ maxMagnitude: -2.9,
6263
+ color: "red"
6264
+ },
6265
+ {
6266
+ name: "Jupiter",
6267
+ symbol: "\u2643",
6268
+ a: 5.202887,
6269
+ e: 0.04838624,
6270
+ i: 1.30439695,
6271
+ L: 34.39644051,
6272
+ longPeri: 14.72847983,
6273
+ longNode: 100.47390909,
6274
+ aRate: -11607e-8,
6275
+ eRate: -13253e-8,
6276
+ iRate: -183714e-8,
6277
+ LRate: 3034.74612775,
6278
+ longPeriRate: 0.21252668,
6279
+ longNodeRate: 0.20469106,
6280
+ maxMagnitude: -2.9,
6281
+ color: "orange"
6282
+ },
6283
+ {
6284
+ name: "Saturn",
6285
+ symbol: "\u2644",
6286
+ a: 9.53667594,
6287
+ e: 0.05386179,
6288
+ i: 2.48599187,
6289
+ L: 49.95424423,
6290
+ longPeri: 92.59887831,
6291
+ longNode: 113.66242448,
6292
+ aRate: -12506e-7,
6293
+ eRate: -50991e-8,
6294
+ iRate: 193609e-8,
6295
+ LRate: 1222.49362201,
6296
+ longPeriRate: -0.41897216,
6297
+ longNodeRate: -0.28867794,
6298
+ maxMagnitude: -0.5,
6299
+ color: "gold"
6300
+ }
6301
+ ];
6302
+ var EARTH = {
6303
+ a: 1.00000261,
6304
+ e: 0.01671123,
6305
+ i: -1531e-8,
6306
+ L: 100.46457166,
6307
+ longPeri: 102.93768193,
6308
+ longNode: 0,
6309
+ aRate: 562e-8,
6310
+ eRate: -4392e-8,
6311
+ iRate: -0.01294668,
6312
+ LRate: 35999.37244981,
6313
+ longPeriRate: 0.32327364,
6314
+ longNodeRate: 0
6315
+ };
6316
+ var toRadians2 = (deg) => deg * (Math.PI / 180);
6317
+ var toDegrees2 = (rad) => rad * (180 / Math.PI);
6318
+ var normalizeAngle = (deg) => (deg % 360 + 360) % 360;
6319
+ var getJulianDay2 = (date) => {
6320
+ const year = date.getFullYear();
6321
+ const month = date.getMonth() + 1;
6322
+ const day = date.getDate() + date.getHours() / 24;
6323
+ const a = Math.floor((14 - month) / 12);
6324
+ const y = year + 4800 - a;
6325
+ const m = month + 12 * a - 3;
6326
+ return day + Math.floor((153 * m + 2) / 5) + 365 * y + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400) - 32045;
6327
+ };
6328
+ var getJulianCenturies = (jd) => (jd - 2451545) / 36525;
6329
+ var getHeliocentricCoords = (planet, T) => {
6330
+ const a = planet.a + planet.aRate * T;
6331
+ const e = planet.e + planet.eRate * T;
6332
+ const i = toRadians2(planet.i + planet.iRate * T);
6333
+ const L = normalizeAngle(planet.L + planet.LRate * T);
6334
+ const longPeri = normalizeAngle(planet.longPeri + planet.longPeriRate * T);
6335
+ const longNode = toRadians2(planet.longNode + planet.longNodeRate * T);
6336
+ const M = toRadians2(normalizeAngle(L - longPeri));
6337
+ let E = M;
6338
+ for (let iter = 0; iter < 10; iter++) {
6339
+ E = M + e * Math.sin(E);
6340
+ }
6341
+ const xv = a * (Math.cos(E) - e);
6342
+ const yv = a * Math.sqrt(1 - e * e) * Math.sin(E);
6343
+ const r = Math.sqrt(xv * xv + yv * yv);
6344
+ const v = Math.atan2(yv, xv);
6345
+ const omega = toRadians2(longPeri) - longNode;
6346
+ const cosOmega = Math.cos(omega + v);
6347
+ const sinOmega = Math.sin(omega + v);
6348
+ const cosI = Math.cos(i);
6349
+ const sinI = Math.sin(i);
6350
+ const cosNode = Math.cos(longNode);
6351
+ const sinNode = Math.sin(longNode);
6352
+ const x = r * (cosNode * cosOmega - sinNode * sinOmega * cosI);
6353
+ const y = r * (sinNode * cosOmega + cosNode * sinOmega * cosI);
6354
+ const z143 = r * sinOmega * sinI;
6355
+ return { x, y, z: z143, r };
6356
+ };
6357
+ var getGeocentricEcliptic = (planet, earth) => {
6358
+ const dx = planet.x - earth.x;
6359
+ const dy = planet.y - earth.y;
6360
+ const dz = planet.z - earth.z;
6361
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
6362
+ const lon = normalizeAngle(toDegrees2(Math.atan2(dy, dx)));
6363
+ const lat = toDegrees2(Math.asin(dz / dist));
6364
+ return { lon, lat, dist };
6365
+ };
6366
+ var eclipticToEquatorial = (lon, lat, obliquity) => {
6367
+ const lonRad = toRadians2(lon);
6368
+ const latRad = toRadians2(lat);
6369
+ const oblRad = toRadians2(obliquity);
6370
+ const sinDec = Math.sin(latRad) * Math.cos(oblRad) + Math.cos(latRad) * Math.sin(oblRad) * Math.sin(lonRad);
6371
+ const dec = toDegrees2(Math.asin(sinDec));
6372
+ const y = Math.sin(lonRad) * Math.cos(oblRad) - Math.tan(latRad) * Math.sin(oblRad);
6373
+ const x = Math.cos(lonRad);
6374
+ const ra = normalizeAngle(toDegrees2(Math.atan2(y, x)));
6375
+ return { ra, dec };
6376
+ };
6377
+ var getLocalSiderealTime = (jd, longitude) => {
6378
+ const T = (jd - 2451545) / 36525;
6379
+ let GST = 280.46061837 + 360.98564736629 * (jd - 2451545) + 387933e-9 * T * T;
6380
+ GST = normalizeAngle(GST);
6381
+ return normalizeAngle(GST + longitude);
6382
+ };
6383
+ var getAltAz = (ra, dec, lat, lst) => {
6384
+ const ha = toRadians2(normalizeAngle(lst - ra));
6385
+ const decRad = toRadians2(dec);
6386
+ const latRad = toRadians2(lat);
6387
+ const sinAlt = Math.sin(decRad) * Math.sin(latRad) + Math.cos(decRad) * Math.cos(latRad) * Math.cos(ha);
6388
+ const alt = toDegrees2(Math.asin(sinAlt));
6389
+ const cosA = (Math.sin(decRad) - Math.sin(latRad) * sinAlt) / (Math.cos(latRad) * Math.cos(toRadians2(alt)));
6390
+ let az = toDegrees2(Math.acos(Math.max(-1, Math.min(1, cosA))));
6391
+ if (Math.sin(ha) > 0)
6392
+ az = 360 - az;
6393
+ return { alt, az };
6394
+ };
6395
+ var getRiseSetTimes = (ra, dec, lat, lon, jd) => {
6396
+ const latRad = toRadians2(lat);
6397
+ const decRad = toRadians2(dec);
6398
+ const h0 = toRadians2(-0.5667);
6399
+ const cosH = (Math.sin(h0) - Math.sin(latRad) * Math.sin(decRad)) / (Math.cos(latRad) * Math.cos(decRad));
6400
+ const lst0 = getLocalSiderealTime(Math.floor(jd) + 0.5, lon);
6401
+ let transitTime = (ra - lst0) / 15;
6402
+ if (transitTime < 0)
6403
+ transitTime += 24;
6404
+ if (transitTime > 24)
6405
+ transitTime -= 24;
6406
+ if (cosH > 1) {
6407
+ return { rises: false, sets: false, transitTime };
6408
+ } else if (cosH < -1) {
6409
+ return { rises: true, sets: true, transitTime };
6410
+ }
6411
+ const H = toDegrees2(Math.acos(cosH)) / 15;
6412
+ let riseTime = transitTime - H;
6413
+ let setTime = transitTime + H;
6414
+ if (riseTime < 0)
6415
+ riseTime += 24;
6416
+ if (setTime > 24)
6417
+ setTime -= 24;
6418
+ return { rises: true, sets: true, riseTime, setTime, transitTime };
6419
+ };
6420
+ var getElongation = (planetLon, sunLon) => {
6421
+ let elong = planetLon - sunLon;
6422
+ if (elong > 180)
6423
+ elong -= 360;
6424
+ if (elong < -180)
6425
+ elong += 360;
6426
+ return elong;
6427
+ };
6428
+ var formatTime = (hours, timezone2) => {
6429
+ const h = Math.floor(hours);
6430
+ const m = Math.round((hours - h) * 60);
6431
+ const date = /* @__PURE__ */ new Date();
6432
+ date.setUTCHours(h, m, 0, 0);
6433
+ return date.toLocaleTimeString("en-US", { timeZone: timezone2, hour: "numeric", minute: "2-digit" });
6434
+ };
6435
+ var getDirection = (az) => {
6436
+ const directions = [
6437
+ "N",
6438
+ "NNE",
6439
+ "NE",
6440
+ "ENE",
6441
+ "E",
6442
+ "ESE",
6443
+ "SE",
6444
+ "SSE",
6445
+ "S",
6446
+ "SSW",
6447
+ "SW",
6448
+ "WSW",
6449
+ "W",
6450
+ "WNW",
6451
+ "NW",
6452
+ "NNW"
6453
+ ];
6454
+ const index = Math.round(az / 22.5) % 16;
6455
+ return directions[index];
6456
+ };
6457
+ var getVisibilityDescription = (elongation, altitude, isSuperior) => {
6458
+ const absElong = Math.abs(elongation);
6459
+ if (altitude < 0) {
6460
+ return { quality: "Not visible", description: "Below horizon" };
6461
+ }
6462
+ if (altitude < 10) {
6463
+ return { quality: "Poor", description: "Very low in sky, atmospheric interference" };
6464
+ }
6465
+ if (isSuperior) {
6466
+ if (absElong > 120) {
6467
+ return { quality: "Excellent", description: "Near opposition, visible most of the night" };
6468
+ } else if (absElong > 90) {
6469
+ return { quality: "Good", description: "Well placed for evening/morning viewing" };
6470
+ } else if (absElong > 45) {
6471
+ return { quality: "Fair", description: "Visible but not optimally placed" };
6472
+ } else {
6473
+ return { quality: "Poor", description: "Too close to Sun" };
6474
+ }
6475
+ } else {
6476
+ if (absElong > 40) {
6477
+ return { quality: "Excellent", description: "Near greatest elongation" };
6478
+ } else if (absElong > 20) {
6479
+ return { quality: "Good", description: "Well separated from Sun" };
6480
+ } else if (absElong > 10) {
6481
+ return { quality: "Fair", description: "Low in twilight" };
6482
+ } else {
6483
+ return { quality: "Poor", description: "Too close to Sun" };
6484
+ }
6485
+ }
6486
+ };
6487
+ var getPlanetVisibility = async (parameters) => {
6488
+ const { latitude, longitude, date: dateStr, timezone: timezone2 = "UTC" } = parameters;
6489
+ if (latitude === void 0 || latitude === null) {
6490
+ throw new Error("Latitude is required. Please provide a value between -90 and 90.");
6491
+ }
6492
+ if (longitude === void 0 || longitude === null) {
6493
+ throw new Error("Longitude is required. Please provide a value between -180 and 180.");
6494
+ }
6495
+ if (latitude < -90 || latitude > 90) {
6496
+ throw new Error(`Invalid latitude: ${latitude}. Must be between -90 and 90.`);
6497
+ }
6498
+ if (longitude < -180 || longitude > 180) {
6499
+ throw new Error(`Invalid longitude: ${longitude}. Must be between -180 and 180.`);
6500
+ }
6501
+ try {
6502
+ Intl.DateTimeFormat(void 0, { timeZone: timezone2 });
6503
+ } catch {
6504
+ throw new Error(`Invalid timezone: ${timezone2}. Please use IANA timezone format.`);
6505
+ }
6506
+ let date;
6507
+ if (dateStr) {
6508
+ const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/);
6509
+ if (!match)
6510
+ throw new Error(`Invalid date format: ${dateStr}. Use YYYY-MM-DD.`);
6511
+ date = new Date(parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), 12, 0, 0);
6512
+ } else {
6513
+ date = /* @__PURE__ */ new Date();
6514
+ }
6515
+ const jd = getJulianDay2(date);
6516
+ const T = getJulianCenturies(jd);
6517
+ const obliquity = 23.439 - 36e-8 * (jd - 2451545);
6518
+ const earthHelio = getHeliocentricCoords(EARTH, T);
6519
+ const sunLon = normalizeAngle(toDegrees2(Math.atan2(-earthHelio.y, -earthHelio.x)));
6520
+ const results = [];
6521
+ const dateFormatter = new Intl.DateTimeFormat("en-US", {
6522
+ timeZone: timezone2,
6523
+ weekday: "long",
6524
+ year: "numeric",
6525
+ month: "long",
6526
+ day: "numeric"
6527
+ });
6528
+ results.push(`\u{1FA90} **Planet Visibility for ${dateFormatter.format(date)}**`);
6529
+ results.push(`\u{1F4CD} ${Math.abs(latitude).toFixed(2)}\xB0${latitude >= 0 ? "N" : "S"}, ${Math.abs(longitude).toFixed(2)}\xB0${longitude >= 0 ? "E" : "W"}`);
6530
+ results.push("");
6531
+ const planetData = [];
6532
+ for (const planet of PLANETS) {
6533
+ const helio = getHeliocentricCoords(planet, T);
6534
+ const geo = getGeocentricEcliptic(helio, earthHelio);
6535
+ const equatorial = eclipticToEquatorial(geo.lon, geo.lat, obliquity);
6536
+ const lst = getLocalSiderealTime(jd, longitude);
6537
+ const altAz = getAltAz(equatorial.ra, equatorial.dec, latitude, lst);
6538
+ const elongation = getElongation(geo.lon, sunLon);
6539
+ const isSuperior = planet.a > 1;
6540
+ const visibility = getVisibilityDescription(elongation, altAz.alt, isSuperior);
6541
+ const riseSet = getRiseSetTimes(equatorial.ra, equatorial.dec, latitude, longitude, jd);
6542
+ planetData.push({
6543
+ name: planet.name,
6544
+ symbol: planet.symbol,
6545
+ color: planet.color,
6546
+ altitude: altAz.alt,
6547
+ azimuth: altAz.az,
6548
+ elongation,
6549
+ visibility,
6550
+ riseSet,
6551
+ isSuperior
6552
+ });
6553
+ }
6554
+ const visiblePlanets = planetData.filter((p) => p.visibility.quality !== "Not visible" && p.visibility.quality !== "Poor");
6555
+ const notVisiblePlanets = planetData.filter((p) => p.visibility.quality === "Not visible" || p.visibility.quality === "Poor");
6556
+ if (visiblePlanets.length > 0) {
6557
+ results.push("**Currently Visible Planets:**");
6558
+ results.push("");
6559
+ for (const p of visiblePlanets.sort((a, b) => b.altitude - a.altitude)) {
6560
+ const emoji = p.name === "Jupiter" ? "\u{1F7E0}" : p.name === "Saturn" ? "\u{1F7E1}" : p.name === "Mars" ? "\u{1F534}" : p.name === "Venus" ? "\u26AA" : "\u26AB";
6561
+ results.push(`${emoji} **${p.name}** ${p.symbol}`);
6562
+ results.push(` Visibility: ${p.visibility.quality} - ${p.visibility.description}`);
6563
+ if (p.altitude > 0) {
6564
+ results.push(` Current position: ${p.altitude.toFixed(1)}\xB0 altitude, ${getDirection(p.azimuth)} (${p.azimuth.toFixed(0)}\xB0)`);
6565
+ }
6566
+ if (p.riseSet.riseTime !== void 0) {
6567
+ results.push(` Rises: ${formatTime(p.riseSet.riseTime, timezone2)} | Transit: ${formatTime(p.riseSet.transitTime, timezone2)} | Sets: ${formatTime(p.riseSet.setTime, timezone2)}`);
6568
+ }
6569
+ if (p.isSuperior) {
6570
+ if (Math.abs(p.elongation) > 150) {
6571
+ results.push(` \u2B50 Near opposition! Best time to observe - visible all night`);
6572
+ } else if (p.elongation > 90) {
6573
+ results.push(` Evening sky - visible after sunset`);
6574
+ } else if (p.elongation < -90) {
6575
+ results.push(` Morning sky - visible before sunrise`);
6576
+ }
6577
+ } else {
6578
+ if (p.elongation > 0) {
6579
+ results.push(` Evening star - look west after sunset`);
6580
+ } else {
6581
+ results.push(` Morning star - look east before sunrise`);
6582
+ }
6583
+ }
6584
+ results.push("");
6585
+ }
6586
+ } else {
6587
+ results.push("_No planets are well-positioned for viewing tonight._");
6588
+ results.push("");
6589
+ }
6590
+ const conjunctions = [];
6591
+ for (let i = 0; i < planetData.length; i++) {
6592
+ for (let j = i + 1; j < planetData.length; j++) {
6593
+ const p1 = planetData[i];
6594
+ const p2 = planetData[j];
6595
+ const separation = Math.sqrt(Math.pow(p1.altitude - p2.altitude, 2) + Math.pow((p1.azimuth - p2.azimuth) * Math.cos(toRadians2(p1.altitude)), 2));
6596
+ if (separation < 10 && p1.altitude > 0 && p2.altitude > 0) {
6597
+ conjunctions.push(`${p1.name} and ${p2.name} are close together (${separation.toFixed(1)}\xB0 apart)`);
6598
+ }
5141
6599
  }
5142
- i++;
5143
- }
5144
- return result;
5145
- }
5146
- function parseTransformationResult(llmResponse) {
5147
- if (!llmResponse || !llmResponse.trim()) {
5148
- throw new Error("LLM returned an empty response. This may be due to content filtering, rate limiting, or a timeout. Please try again.");
5149
6600
  }
5150
- const jsonMatch = llmResponse.match(/```json\s*\n([\s\S]*?)\n```/) || llmResponse.match(/```\s*\n([\s\S]*?)\n```/);
5151
- let jsonStr = jsonMatch ? jsonMatch[1] : llmResponse;
5152
- jsonStr = jsonStr.trim();
5153
- if (!jsonStr) {
5154
- throw new Error("Could not extract valid JSON from LLM response. The response may have been truncated or malformed.");
6601
+ if (conjunctions.length > 0) {
6602
+ results.push("**Planetary Conjunctions:**");
6603
+ conjunctions.forEach((c) => results.push(`- ${c}`));
6604
+ results.push("");
5155
6605
  }
5156
- jsonStr = sanitizeJsonString(jsonStr);
5157
- try {
5158
- const result = JSON.parse(jsonStr);
5159
- if (!result.title || !result.content) {
5160
- throw new Error("Missing required fields: title and content");
6606
+ if (notVisiblePlanets.length > 0) {
6607
+ results.push("**Not Currently Visible:**");
6608
+ for (const p of notVisiblePlanets) {
6609
+ results.push(`- ${p.symbol} ${p.name}: ${p.visibility.description}`);
5161
6610
  }
5162
- return {
5163
- title: result.title,
5164
- content: result.content,
5165
- summary: result.summary || "",
5166
- suggestedTags: result.suggestedTags || []
5167
- };
5168
- } catch (error) {
5169
- console.error("Failed to parse transformation result:", { error, response: llmResponse });
5170
- throw new Error("Failed to parse transformation result from LLM");
5171
6611
  }
5172
- }
5173
- var blogDraftTool = {
5174
- name: "blog_draft",
6612
+ results.push("");
6613
+ results.push("_Naked-eye planets: Mercury, Venus, Mars, Jupiter, Saturn_");
6614
+ return results.join("\n");
6615
+ };
6616
+ var planetVisibilityTool = {
6617
+ name: "planet_visibility",
5175
6618
  implementation: (context) => ({
5176
6619
  toolFn: async (value) => {
5177
6620
  const params = value;
5178
- const { logger, llm } = context;
5179
- const outputFormat = params.outputFormat || "blog";
5180
- logger.info("Drafting blog content", {
5181
- userId: context.userId,
5182
- outputFormat,
5183
- hasVoiceGuide: !!params.voiceGuide,
5184
- hasAdditionalInstructions: !!params.additionalInstructions
5185
- });
6621
+ context.logger.log("\u{1FA90} PlanetVisibility: Starting execution", params);
5186
6622
  try {
5187
- const systemPrompt = buildTransformationPrompt({
5188
- voiceGuide: params.voiceGuide,
5189
- outputFormat,
5190
- additionalInstructions: params.additionalInstructions
5191
- });
5192
- const modelToUse = context.model || "claude-sonnet-4-5-20250929";
5193
- logger.info("Using model for blog draft:", { model: modelToUse });
5194
- let llmResponse = "";
5195
- await llm.complete(modelToUse, [
5196
- { role: "system", content: systemPrompt },
5197
- {
5198
- role: "user",
5199
- content: `Transform this content:
5200
-
5201
- ${params.sourceContent}`
5202
- }
5203
- ], {
5204
- temperature: 0.7,
5205
- stream: false
5206
- }, async (texts) => {
5207
- llmResponse = texts.filter((t) => t !== null && t !== void 0).join("");
5208
- });
5209
- logger.info("Blog draft complete", { responseLength: llmResponse.length });
5210
- const result = parseTransformationResult(llmResponse);
5211
- logger.info("Blog draft created successfully", {
5212
- title: result.title,
5213
- contentLength: result.content.length,
5214
- tagsCount: result.suggestedTags.length
5215
- });
5216
- return `\u2728 Blog draft created successfully!
5217
-
5218
- \`\`\`json
5219
- ${JSON.stringify(result, null, 2)}
5220
- \`\`\`
5221
-
5222
- \u{1F4CB} The preview card will appear below for you to review and edit before publishing.
5223
- `;
6623
+ const result = await getPlanetVisibility(params);
6624
+ context.logger.log("\u2705 PlanetVisibility: Execution completed");
6625
+ return result;
5224
6626
  } catch (error) {
5225
- logger.error("Blog draft creation failed:", error);
6627
+ context.logger.error("\u274C PlanetVisibility: Execution failed", error);
5226
6628
  throw error;
5227
6629
  }
5228
6630
  },
5229
6631
  toolSchema: {
5230
- name: "blog_draft",
5231
- description: '\u{1F3AF} PRIMARY TOOL FOR BLOGGING: Use this tool when user wants to publish conversation/content to their blog. Transforms raw content into structured blog post format. ALWAYS use this for phrases like "publish to my blog", "blog this conversation", "create a blog post". Returns JSON that frontend automatically renders as preview card. This is the FIRST STEP - user will review/edit before final publish. DO NOT skip this step.',
6632
+ name: "planet_visibility",
6633
+ description: "Get visibility information for the 5 naked-eye planets (Mercury, Venus, Mars, Jupiter, Saturn). Shows which planets are visible tonight, their positions, rise/set times, and any planetary conjunctions.",
5232
6634
  parameters: {
5233
6635
  type: "object",
5234
6636
  properties: {
5235
- sourceContent: {
5236
- type: "string",
5237
- description: "The content to transform into a blog draft. This can be conversation history, article text, notes, etc. Include all relevant context."
6637
+ latitude: {
6638
+ type: "number",
6639
+ description: "Latitude in decimal degrees (-90 to 90). Example: 40.7128 for New York City."
5238
6640
  },
5239
- voiceGuide: {
5240
- type: "string",
5241
- description: "Optional voice/style guide content (markdown text) to apply. This should contain writing style instructions and patterns."
6641
+ longitude: {
6642
+ type: "number",
6643
+ description: "Longitude in decimal degrees (-180 to 180). Example: -74.0060 for New York City."
5242
6644
  },
5243
- outputFormat: {
6645
+ date: {
5244
6646
  type: "string",
5245
- enum: ["blog", "linkedin", "twitter", "newsletter"],
5246
- description: 'Output format to generate. Currently only "blog" is fully supported. Others are coming soon.'
6647
+ description: "The date to check. Use YYYY-MM-DD format. Defaults to today if not specified."
5247
6648
  },
5248
- additionalInstructions: {
6649
+ timezone: {
5249
6650
  type: "string",
5250
- description: 'Optional additional instructions for the draft. Examples: "Make it more technical", "Add code examples", "Focus on business impact"'
6651
+ description: 'IANA timezone identifier for time display (e.g., "America/New_York"). Defaults to UTC.'
5251
6652
  }
5252
6653
  },
5253
- required: ["sourceContent", "outputFormat"]
6654
+ additionalProperties: false,
6655
+ required: ["latitude", "longitude"]
5254
6656
  }
5255
6657
  }
5256
6658
  })
@@ -5849,17 +7251,23 @@ var tools = {
5849
7251
  blog_publish: blogPublishTool,
5850
7252
  blog_edit: blogEditTool,
5851
7253
  blog_draft: blogDraftTool,
7254
+ // Time Machine & Night Sky tools
7255
+ wikipedia_on_this_day: wikipediaOnThisDayTool,
7256
+ moon_phase: moonPhaseTool,
7257
+ sunrise_sunset: sunriseSunsetTool,
7258
+ iss_tracker: issTrackerTool,
7259
+ planet_visibility: planetVisibilityTool,
5852
7260
  file_read: fileReadTool,
5853
7261
  create_file: createFileTool,
5854
7262
  glob_files: globFilesTool,
5855
7263
  grep_search: grepSearchTool,
5856
7264
  delete_file: deleteFileTool
5857
7265
  };
5858
- var generateTools = (userId, user, logger, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
7266
+ var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
5859
7267
  const context = {
5860
7268
  userId,
5861
7269
  user,
5862
- logger,
7270
+ logger: logger2,
5863
7271
  db,
5864
7272
  storage,
5865
7273
  imageGenerateStorage,
@@ -5874,8 +7282,19 @@ var generateTools = (userId, user, logger, { db }, storage, imageGenerateStorage
5874
7282
  [key]: tool.implementation(context, config[key])
5875
7283
  }), {});
5876
7284
  };
7285
+ var ATLASSIAN_RECONNECT_MESSAGE = "\u26A0\uFE0F Your Atlassian connection has expired.\n\nPlease reconnect your Atlassian account in Settings > Connected Apps to continue using Confluence and Jira tools.";
5877
7286
  var generateMcpTools = async (mcpData) => {
5878
- const tools2 = await mcpData.getTools();
7287
+ let tools2;
7288
+ try {
7289
+ tools2 = await mcpData.getTools();
7290
+ } catch (error) {
7291
+ const errorName = error instanceof Error ? error.name : "";
7292
+ if (errorName === "AtlassianReconnectRequiredError") {
7293
+ console.warn(`Atlassian reconnection required during getTools for ${mcpData.serverName}`);
7294
+ return [];
7295
+ }
7296
+ throw error;
7297
+ }
5879
7298
  const toolList = Array.isArray(tools2) ? tools2 : tools2?.tools || [];
5880
7299
  if (!Array.isArray(tools2)) {
5881
7300
  console.warn(`MCP server ${mcpData.serverName} returned unexpected tools payload:`, JSON.stringify(tools2));
@@ -5916,13 +7335,12 @@ var generateMcpTools = async (mcpData) => {
5916
7335
  } catch (error) {
5917
7336
  const serverName2 = mcpData.serverName?.toLowerCase();
5918
7337
  if (serverName2 === "atlassian") {
7338
+ const errorName = error instanceof Error ? error.name : "";
5919
7339
  const errorMessage = error instanceof Error ? error.message : String(error);
5920
- const isTokenError = errorMessage.includes("401") || errorMessage.includes("403") || errorMessage.includes("unauthorized") || errorMessage.includes("expired");
7340
+ const isTokenError = errorName === "AtlassianReconnectRequiredError" || errorMessage.includes("401") || errorMessage.includes("403") || errorMessage.includes("unauthorized") || errorMessage.includes("expired");
5921
7341
  if (isTokenError) {
5922
7342
  console.warn(`Atlassian token may be expired for tool ${name}, error:`, errorMessage);
5923
- return `\u26A0\uFE0F Atlassian authentication error: ${errorMessage}
5924
-
5925
- The access token may have expired. Please disconnect and reconnect your Atlassian account in Settings > Connected Apps.`;
7343
+ return ATLASSIAN_RECONNECT_MESSAGE;
5926
7344
  }
5927
7345
  }
5928
7346
  throw error;
@@ -6102,7 +7520,7 @@ async function generateFileDeletePreview(args) {
6102
7520
  if (!existsSync4(args.path)) {
6103
7521
  return `[File does not exist: ${args.path}]`;
6104
7522
  }
6105
- const stats = await import("fs/promises").then((fs8) => fs8.stat(args.path));
7523
+ const stats = await import("fs/promises").then((fs9) => fs9.stat(args.path));
6106
7524
  return `[File will be deleted]
6107
7525
 
6108
7526
  Path: ${args.path}
@@ -6118,21 +7536,21 @@ var NoOpStorage = class extends BaseStorage {
6118
7536
  async upload(input, destination, options) {
6119
7537
  return `/tmp/${destination}`;
6120
7538
  }
6121
- async download(path10) {
7539
+ async download(path11) {
6122
7540
  throw new Error("Download not supported in CLI");
6123
7541
  }
6124
- async delete(path10) {
7542
+ async delete(path11) {
6125
7543
  }
6126
- async getSignedUrl(path10) {
6127
- return `/tmp/${path10}`;
7544
+ async getSignedUrl(path11) {
7545
+ return `/tmp/${path11}`;
6128
7546
  }
6129
- getPublicUrl(path10) {
6130
- return `/tmp/${path10}`;
7547
+ getPublicUrl(path11) {
7548
+ return `/tmp/${path11}`;
6131
7549
  }
6132
- async getPreview(path10) {
6133
- return `/tmp/${path10}`;
7550
+ async getPreview(path11) {
7551
+ return `/tmp/${path11}`;
6134
7552
  }
6135
- async getMetadata(path10) {
7553
+ async getMetadata(path11) {
6136
7554
  return { size: 0, contentType: "application/octet-stream" };
6137
7555
  }
6138
7556
  };
@@ -6205,7 +7623,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
6205
7623
  };
6206
7624
  }
6207
7625
  function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, toolApiKeys) {
6208
- const logger = new CliLogger();
7626
+ const logger2 = new CliLogger();
6209
7627
  const storage = new NoOpStorage();
6210
7628
  const user = {
6211
7629
  _id: userId,
@@ -6276,7 +7694,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
6276
7694
  const toolsMap = generateTools(
6277
7695
  userId,
6278
7696
  user,
6279
- logger,
7697
+ logger2,
6280
7698
  dbAdapters,
6281
7699
  storage,
6282
7700
  storage,
@@ -6457,10 +7875,265 @@ function getEnvironmentName(configApiConfig) {
6457
7875
  return "Bike4Mind";
6458
7876
  }
6459
7877
 
7878
+ // src/utils/Logger.ts
7879
+ import fs8 from "fs/promises";
7880
+ import path9 from "path";
7881
+ import os from "os";
7882
+ var Logger2 = class _Logger {
7883
+ constructor() {
7884
+ this.logFilePath = null;
7885
+ this.sessionId = null;
7886
+ this.fileLoggingEnabled = true;
7887
+ this.consoleVerbose = false;
7888
+ }
7889
+ static {
7890
+ this.instance = null;
7891
+ }
7892
+ static getInstance() {
7893
+ if (!_Logger.instance) {
7894
+ _Logger.instance = new _Logger();
7895
+ }
7896
+ return _Logger.instance;
7897
+ }
7898
+ /**
7899
+ * Initialize the logger with a session ID
7900
+ */
7901
+ async initialize(sessionId) {
7902
+ this.sessionId = sessionId;
7903
+ const debugDir = path9.join(os.homedir(), ".bike4mind", "debug");
7904
+ await fs8.mkdir(debugDir, { recursive: true });
7905
+ this.logFilePath = path9.join(debugDir, `${sessionId}.txt`);
7906
+ await this.writeToFile("INFO", "=== CLI SESSION START ===");
7907
+ }
7908
+ /**
7909
+ * Set whether verbose console logging is enabled
7910
+ */
7911
+ setVerbose(enabled) {
7912
+ this.consoleVerbose = enabled;
7913
+ }
7914
+ /**
7915
+ * Set whether file logging is enabled
7916
+ */
7917
+ setFileLoggingEnabled(enabled) {
7918
+ this.fileLoggingEnabled = enabled;
7919
+ }
7920
+ /**
7921
+ * DEBUG level - verbose-only console, always file
7922
+ */
7923
+ debug(message) {
7924
+ this.writeToFile("DEBUG", message).catch(() => {
7925
+ });
7926
+ if (this.consoleVerbose) {
7927
+ console.log(message);
7928
+ }
7929
+ }
7930
+ /**
7931
+ * INFO level - always shown to user
7932
+ */
7933
+ info(message) {
7934
+ this.writeToFile("INFO", message).catch(() => {
7935
+ });
7936
+ console.log(message);
7937
+ }
7938
+ /**
7939
+ * WARN level - always shown to user
7940
+ */
7941
+ warn(message) {
7942
+ this.writeToFile("WARN", message).catch(() => {
7943
+ });
7944
+ console.warn(message);
7945
+ }
7946
+ /**
7947
+ * ERROR level - always shown to user
7948
+ */
7949
+ error(message, err) {
7950
+ this.writeToFile("ERROR", message).catch(() => {
7951
+ });
7952
+ if (err) {
7953
+ this.logErrorDetailsToFile(err).catch(() => {
7954
+ });
7955
+ }
7956
+ console.error(message);
7957
+ }
7958
+ /**
7959
+ * Write log entry to file
7960
+ */
7961
+ async writeToFile(level, message) {
7962
+ if (!this.fileLoggingEnabled || !this.logFilePath) {
7963
+ return;
7964
+ }
7965
+ try {
7966
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").substring(0, 19);
7967
+ const logEntry = `[${timestamp}] [${level}] ${message}
7968
+ `;
7969
+ await fs8.appendFile(this.logFilePath, logEntry, "utf-8");
7970
+ } catch (error) {
7971
+ console.error("File logging failed:", error);
7972
+ }
7973
+ }
7974
+ /**
7975
+ * Log error details to file
7976
+ */
7977
+ async logErrorDetailsToFile(err) {
7978
+ if (!this.fileLoggingEnabled || !this.logFilePath) {
7979
+ return;
7980
+ }
7981
+ try {
7982
+ if (err && typeof err === "object" && "response" in err && err.response) {
7983
+ const response = err.response;
7984
+ const config = err && typeof err === "object" && "config" in err ? err.config : void 0;
7985
+ await this.writeToFile("ERROR", ` Status: ${response.status} ${response.statusText || ""}`);
7986
+ await this.writeToFile("ERROR", ` URL: ${config?.url || "unknown"}`);
7987
+ await this.writeToFile("ERROR", ` Headers: ${this.safeStringify(response.headers)}`);
7988
+ if (response.data) {
7989
+ const errorText = this.extractErrorMessage(response.data);
7990
+ if (errorText.trim().startsWith("<!DOCTYPE") || errorText.trim().startsWith("<html")) {
7991
+ await this.writeToFile("ERROR", ` Response Type: HTML Error Page`);
7992
+ const parsedError = this.parseHtmlError(errorText);
7993
+ if (parsedError) {
7994
+ await this.writeToFile("ERROR", ` Error Message: ${parsedError}`);
7995
+ }
7996
+ await this.writeToFile("ERROR", ` Raw HTML: ${this.truncate(errorText, 1e3)}`);
7997
+ } else {
7998
+ const preview = this.truncate(errorText, 500);
7999
+ await this.writeToFile("ERROR", ` Response: ${preview}`);
8000
+ }
8001
+ } else {
8002
+ await this.writeToFile("ERROR", ` Response: (no data)`);
8003
+ }
8004
+ }
8005
+ if (err && typeof err === "object" && "stack" in err && typeof err.stack === "string") {
8006
+ const stackLines = err.stack.split("\n").slice(0, 5).join("\n ");
8007
+ await this.writeToFile("ERROR", ` Stack: ${stackLines}`);
8008
+ } else if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
8009
+ await this.writeToFile("ERROR", ` Message: ${err.message}`);
8010
+ }
8011
+ } catch (error) {
8012
+ }
8013
+ }
8014
+ /**
8015
+ * Safely stringify object, handling circular references
8016
+ */
8017
+ safeStringify(obj) {
8018
+ try {
8019
+ return JSON.stringify(obj);
8020
+ } catch (error) {
8021
+ if (error instanceof Error && error.message.includes("circular")) {
8022
+ try {
8023
+ const seen = /* @__PURE__ */ new WeakSet();
8024
+ return JSON.stringify(obj, (key, value) => {
8025
+ if (typeof value === "object" && value !== null) {
8026
+ if (seen.has(value)) {
8027
+ return "[Circular]";
8028
+ }
8029
+ seen.add(value);
8030
+ }
8031
+ return value;
8032
+ });
8033
+ } catch {
8034
+ return "[Unable to stringify]";
8035
+ }
8036
+ }
8037
+ return "[Stringify error]";
8038
+ }
8039
+ }
8040
+ /**
8041
+ * Extract readable text from error response data
8042
+ */
8043
+ extractErrorMessage(data) {
8044
+ if (Buffer.isBuffer(data)) {
8045
+ return data.toString("utf-8");
8046
+ }
8047
+ if (typeof data === "string") {
8048
+ return data;
8049
+ }
8050
+ if (data && typeof data === "object" && "_readableState" in data && data._readableState && typeof data._readableState === "object" && "buffer" in data._readableState && Array.isArray(data._readableState.buffer) && data._readableState.buffer.length > 0) {
8051
+ const chunks = [];
8052
+ for (const chunk of data._readableState.buffer) {
8053
+ if (chunk && typeof chunk === "object" && "type" in chunk && chunk.type === "Buffer" && "data" in chunk && Array.isArray(chunk.data)) {
8054
+ chunks.push(Buffer.from(chunk.data));
8055
+ } else if (Buffer.isBuffer(chunk)) {
8056
+ chunks.push(chunk);
8057
+ } else if (chunk && typeof chunk === "object" && "data" in chunk) {
8058
+ if (Buffer.isBuffer(chunk.data)) {
8059
+ chunks.push(chunk.data);
8060
+ } else if (Array.isArray(chunk.data)) {
8061
+ chunks.push(Buffer.from(chunk.data));
8062
+ }
8063
+ }
8064
+ }
8065
+ if (chunks.length > 0) {
8066
+ return Buffer.concat(chunks).toString("utf-8");
8067
+ }
8068
+ }
8069
+ return this.safeStringify(data);
8070
+ }
8071
+ /**
8072
+ * Parse HTML error page to extract error message
8073
+ */
8074
+ parseHtmlError(html) {
8075
+ const titleMatch = html.match(/<title>(.*?)<\/title>/i);
8076
+ const h1Match = html.match(/<h1>(.*?)<\/h1>/i);
8077
+ const bodyMatch = html.match(/<body[^>]*>(.*?)<\/body>/is);
8078
+ if (titleMatch && titleMatch[1] !== "Error") {
8079
+ return titleMatch[1].trim();
8080
+ }
8081
+ if (h1Match) {
8082
+ return h1Match[1].trim();
8083
+ }
8084
+ if (bodyMatch) {
8085
+ const text = bodyMatch[1].replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
8086
+ return text.substring(0, 200);
8087
+ }
8088
+ return null;
8089
+ }
8090
+ /**
8091
+ * Truncate string to max length with ellipsis
8092
+ */
8093
+ truncate(str, maxLength) {
8094
+ if (str.length <= maxLength) {
8095
+ return str;
8096
+ }
8097
+ return str.substring(0, maxLength) + "... [truncated]";
8098
+ }
8099
+ /**
8100
+ * Format bytes to human-readable size
8101
+ */
8102
+ formatBytes(bytes) {
8103
+ if (bytes === 0) return "0 B";
8104
+ const k = 1024;
8105
+ const sizes = ["B", "KB", "MB", "GB"];
8106
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
8107
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
8108
+ }
8109
+ /**
8110
+ * Clean up old debug logs (older than 30 days)
8111
+ */
8112
+ async cleanupOldLogs() {
8113
+ if (!this.fileLoggingEnabled) return;
8114
+ try {
8115
+ const debugDir = path9.join(os.homedir(), ".bike4mind", "debug");
8116
+ const files = await fs8.readdir(debugDir);
8117
+ const now = Date.now();
8118
+ const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1e3;
8119
+ for (const file of files) {
8120
+ const filePath = path9.join(debugDir, file);
8121
+ const stats = await fs8.stat(filePath);
8122
+ if (stats.mtime.getTime() < thirtyDaysAgo) {
8123
+ await fs8.unlink(filePath);
8124
+ }
8125
+ }
8126
+ } catch (error) {
8127
+ console.error("Failed to cleanup old logs:", error);
8128
+ }
8129
+ }
8130
+ };
8131
+ var logger = Logger2.getInstance();
8132
+
6460
8133
  // ../../b4m-core/packages/mcp/dist/src/client.js
6461
8134
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
6462
8135
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
6463
- import path9 from "path";
8136
+ import path10 from "path";
6464
8137
  import { existsSync as existsSync5, readdirSync } from "fs";
6465
8138
  var MCPClient = class {
6466
8139
  // Note: This class handles MCP server communication with repository filtering
@@ -6486,16 +8159,16 @@ var MCPClient = class {
6486
8159
  const root = process.env.INIT_CWD || process.cwd();
6487
8160
  const candidatePaths = [
6488
8161
  // When running from SST Lambda with node_modules structure (copyFiles)
6489
- path9.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
8162
+ path10.join(root, `node_modules/@bike4mind/mcp/dist/src/${this.serverName}/index.js`),
6490
8163
  // When running from SST Lambda deployed environment (/var/task)
6491
- path9.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8164
+ path10.join(root, `b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
6492
8165
  // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
6493
- path9.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8166
+ path10.join(root, `../../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
6494
8167
  // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
6495
- path9.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8168
+ path10.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
6496
8169
  // Original paths (backward compatibility)
6497
- path9.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
6498
- path9.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
8170
+ path10.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
8171
+ path10.join(root, "core", "mcp", "servers", this.serverName, "dist", "index.js")
6499
8172
  ];
6500
8173
  const serverScriptPath = candidatePaths.find((p) => existsSync5(p));
6501
8174
  if (!serverScriptPath) {
@@ -6595,18 +8268,18 @@ var McpManager = class {
6595
8268
  async initialize() {
6596
8269
  const enabledServers = this.config.mcpServers.filter((s) => s.enabled);
6597
8270
  if (enabledServers.length === 0) {
6598
- console.log("\u{1F4E1} No MCP servers enabled");
8271
+ logger.info("\u{1F4E1} No MCP servers enabled");
6599
8272
  return;
6600
8273
  }
6601
- console.log(`\u{1F4E1} Initializing ${enabledServers.length} MCP server(s)...`);
8274
+ logger.info(`\u{1F4E1} Initializing ${enabledServers.length} MCP server(s)...`);
6602
8275
  const results = await Promise.allSettled(enabledServers.map((serverConfig) => this.connectServer(serverConfig)));
6603
8276
  const successful = results.filter((r) => r.status === "fulfilled").length;
6604
8277
  const failed = results.filter((r) => r.status === "rejected").length;
6605
8278
  if (successful > 0) {
6606
- console.log(`\u2705 Connected to ${successful} MCP server(s)`);
8279
+ logger.info(`\u2705 Connected to ${successful} MCP server(s)`);
6607
8280
  }
6608
8281
  if (failed > 0) {
6609
- console.warn(`\u26A0\uFE0F Failed to connect to ${failed} MCP server(s)`);
8282
+ logger.warn(`\u26A0\uFE0F Failed to connect to ${failed} MCP server(s)`);
6610
8283
  }
6611
8284
  }
6612
8285
  /**
@@ -6614,7 +8287,7 @@ var McpManager = class {
6614
8287
  */
6615
8288
  async connectServer(serverConfig) {
6616
8289
  try {
6617
- console.log(`\u{1F504} Connecting to ${serverConfig.name}...`);
8290
+ logger.debug(`\u{1F504} Connecting to ${serverConfig.name}...`);
6618
8291
  const envVariables = Object.entries(serverConfig.env).map(([key, value]) => ({
6619
8292
  key,
6620
8293
  value
@@ -6635,9 +8308,9 @@ var McpManager = class {
6635
8308
  client,
6636
8309
  tools: tools2
6637
8310
  });
6638
- console.log(`\u2705 Connected to ${serverConfig.name} (${tools2.length} tools)`);
8311
+ logger.info(`\u2705 Connected to ${serverConfig.name} (${tools2.length} tools)`);
6639
8312
  } catch (error) {
6640
- console.error(`\u274C Failed to connect to ${serverConfig.name}:`, error);
8313
+ logger.error(`\u274C Failed to connect to ${serverConfig.name}:`, error);
6641
8314
  throw error;
6642
8315
  }
6643
8316
  }
@@ -6667,13 +8340,13 @@ var McpManager = class {
6667
8340
  if (this.servers.size === 0) {
6668
8341
  return;
6669
8342
  }
6670
- console.log(`\u{1F50C} Disconnecting from ${this.servers.size} MCP server(s)...`);
8343
+ logger.debug(`\u{1F50C} Disconnecting from ${this.servers.size} MCP server(s)...`);
6671
8344
  const disconnectPromises = Array.from(this.servers.values()).map(async (server) => {
6672
8345
  try {
6673
8346
  await server.client.disconnect();
6674
- console.log(`\u2705 Disconnected from ${server.name}`);
8347
+ logger.debug(`\u2705 Disconnected from ${server.name}`);
6675
8348
  } catch (error) {
6676
- console.warn(`\u26A0\uFE0F Error disconnecting from ${server.name}:`, error);
8349
+ logger.error(`\u26A0\uFE0F Error disconnecting from ${server.name}:`, error);
6677
8350
  }
6678
8351
  });
6679
8352
  await Promise.allSettled(disconnectPromises);
@@ -6695,6 +8368,7 @@ var McpManager = class {
6695
8368
 
6696
8369
  // src/llm/ServerLlmBackend.ts
6697
8370
  import { createParser } from "eventsource-parser";
8371
+ import { isAxiosError } from "axios";
6698
8372
  function stripThinkingBlocks(text) {
6699
8373
  return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
6700
8374
  }
@@ -6709,12 +8383,12 @@ var ServerLlmBackend = class {
6709
8383
  * Parses SSE stream and invokes callback for each event
6710
8384
  */
6711
8385
  async complete(model, messages, options, callback) {
6712
- console.log("[ServerLlmBackend] Starting complete() with model:", model);
8386
+ logger.debug(`[ServerLlmBackend] Starting complete() with model: ${model}`);
6713
8387
  return new Promise(async (resolve, reject) => {
6714
8388
  try {
6715
- console.log("[ServerLlmBackend] Making streaming request...");
8389
+ logger.debug("[ServerLlmBackend] Making streaming request...");
6716
8390
  const response = await this.makeStreamingRequest(model, messages, options);
6717
- console.log("[ServerLlmBackend] Got response, setting up SSE parser");
8391
+ logger.debug("[ServerLlmBackend] Got response, setting up SSE parser");
6718
8392
  let eventCount = 0;
6719
8393
  let accumulatedText = "";
6720
8394
  let lastUsageInfo = {};
@@ -6723,28 +8397,28 @@ var ServerLlmBackend = class {
6723
8397
  const parser = createParser({
6724
8398
  onEvent: (event) => {
6725
8399
  eventCount++;
6726
- console.log(`[ServerLlmBackend] SSE event #${eventCount}, data:`, event.data?.substring(0, 100));
8400
+ logger.debug(`[ServerLlmBackend] SSE event #${eventCount}, data: ${event.data?.substring(0, 100)}`);
6727
8401
  const data = event.data;
6728
8402
  if (data === "[DONE]") {
6729
8403
  receivedDone = true;
6730
- console.log("[ServerLlmBackend] Received [DONE], accumulated text length:", accumulatedText.length);
8404
+ logger.debug(`[ServerLlmBackend] Received [DONE], accumulated text length: ${accumulatedText.length}`);
6731
8405
  const cleanedText = stripThinkingBlocks(accumulatedText);
6732
8406
  if (toolsUsed.length > 0) {
6733
8407
  const info = {
6734
8408
  toolsUsed,
6735
8409
  ...lastUsageInfo
6736
8410
  };
6737
- console.log("[ServerLlmBackend] Calling callback with tools, will wait for completion");
8411
+ logger.debug("[ServerLlmBackend] Calling callback with tools, will wait for completion");
6738
8412
  callback([cleanedText], info).catch((err) => {
6739
- console.error("[ServerLlmBackend] Callback error:", err);
8413
+ logger.error("[ServerLlmBackend] Callback error:", err);
6740
8414
  reject(err);
6741
8415
  }).then(() => {
6742
- console.log("[ServerLlmBackend] Callback completed, resolving");
8416
+ logger.debug("[ServerLlmBackend] Callback completed, resolving");
6743
8417
  resolve();
6744
8418
  });
6745
8419
  } else if (cleanedText) {
6746
8420
  callback([cleanedText], lastUsageInfo).catch((err) => {
6747
- console.error("[ServerLlmBackend] Callback error:", err);
8421
+ logger.error("[ServerLlmBackend] Callback error:", err);
6748
8422
  reject(err);
6749
8423
  }).then(() => resolve());
6750
8424
  } else {
@@ -6754,9 +8428,9 @@ var ServerLlmBackend = class {
6754
8428
  }
6755
8429
  try {
6756
8430
  const parsed = JSON.parse(data);
6757
- console.log("[ServerLlmBackend] Parsed event type:", parsed.type);
8431
+ logger.debug(`[ServerLlmBackend] Parsed event type: ${parsed.type}`);
6758
8432
  if (parsed.type === "error") {
6759
- console.error("[ServerLlmBackend] Error event:", parsed.message);
8433
+ logger.error("[ServerLlmBackend] Error event:", parsed);
6760
8434
  reject(new Error(parsed.message || "Server error"));
6761
8435
  return;
6762
8436
  }
@@ -6769,9 +8443,20 @@ var ServerLlmBackend = class {
6769
8443
  outputTokens: parsed.usage.outputTokens
6770
8444
  };
6771
8445
  }
6772
- console.log("[ServerLlmBackend] Accumulated text length:", accumulatedText.length);
8446
+ logger.debug(`[ServerLlmBackend] Accumulated text length: ${accumulatedText.length}`);
6773
8447
  } else if (parsed.type === "tool_use") {
6774
- console.log("[ServerLlmBackend] Tool use event, tools:", parsed.tools?.length);
8448
+ logger.debug(`[ServerLlmBackend] Tool use event, tools: ${parsed.tools?.length}`);
8449
+ if (parsed.tools && parsed.tools.length > 0) {
8450
+ for (const tool of parsed.tools) {
8451
+ logger.debug(`TOOL REQUEST: ${tool.name}`);
8452
+ try {
8453
+ const paramsStr = JSON.stringify(tool.input);
8454
+ logger.debug(` Params: ${paramsStr}`);
8455
+ } catch {
8456
+ logger.debug(` Params: [Unable to stringify]`);
8457
+ }
8458
+ }
8459
+ }
6775
8460
  const textChunk = parsed.text || "";
6776
8461
  if (textChunk) {
6777
8462
  accumulatedText += textChunk;
@@ -6787,7 +8472,7 @@ var ServerLlmBackend = class {
6787
8472
  }
6788
8473
  }
6789
8474
  } catch (parseError) {
6790
- console.error("[ServerLlmBackend] Failed to parse SSE event:", parseError);
8475
+ logger.error("[ServerLlmBackend] Failed to parse SSE event:", parseError);
6791
8476
  }
6792
8477
  }
6793
8478
  });
@@ -6796,16 +8481,72 @@ var ServerLlmBackend = class {
6796
8481
  });
6797
8482
  response.data.on("end", () => {
6798
8483
  if (!receivedDone) {
6799
- console.log("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
8484
+ logger.debug("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
6800
8485
  resolve();
6801
8486
  } else {
6802
- console.log("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
8487
+ logger.debug("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
6803
8488
  }
6804
8489
  });
6805
8490
  response.data.on("error", (error) => {
6806
8491
  reject(error);
6807
8492
  });
6808
8493
  } catch (error) {
8494
+ logger.error("LLM completion failed", error);
8495
+ if (isAxiosError(error)) {
8496
+ logger.debug(
8497
+ `[ServerLlmBackend] Axios error details: ${JSON.stringify({
8498
+ status: error.response?.status,
8499
+ statusText: error.response?.statusText,
8500
+ url: error.config?.url,
8501
+ method: error.config?.method
8502
+ })}`
8503
+ );
8504
+ if (error.response?.status === 403 && error.response.data) {
8505
+ let errorDetails = "";
8506
+ try {
8507
+ let responseText = "";
8508
+ const stream = error.response.data;
8509
+ if (Buffer.isBuffer(stream)) {
8510
+ responseText = stream.toString("utf-8");
8511
+ } else if (stream?._readableState?.buffer?.length > 0) {
8512
+ const chunks = [];
8513
+ for (const chunk of stream._readableState.buffer) {
8514
+ if (chunk?.data) {
8515
+ chunks.push(Buffer.from(chunk.data));
8516
+ }
8517
+ }
8518
+ responseText = Buffer.concat(chunks).toString("utf-8");
8519
+ } else if (typeof stream === "string") {
8520
+ responseText = stream;
8521
+ }
8522
+ logger.debug(`[ServerLlmBackend] Response preview: ${responseText.substring(0, 200)}`);
8523
+ if (responseText.includes("<!DOCTYPE") || responseText.includes("<html")) {
8524
+ const titleMatch = responseText.match(/<title>(.*?)<\/title>/i);
8525
+ const h1Match = responseText.match(/<h1>(.*?)<\/h1>/i);
8526
+ if (titleMatch && titleMatch[1] !== "Error") {
8527
+ errorDetails = titleMatch[1].trim();
8528
+ } else if (h1Match) {
8529
+ errorDetails = h1Match[1].trim();
8530
+ }
8531
+ } else if (responseText) {
8532
+ errorDetails = responseText.substring(0, 100).trim();
8533
+ }
8534
+ } catch (extractError) {
8535
+ logger.error("[ServerLlmBackend] Error extracting response:", extractError);
8536
+ }
8537
+ const errorMsg = errorDetails ? `403 Forbidden: ${errorDetails}` : "403 Forbidden - Request blocked by server. Check debug logs at ~/.bike4mind/debug/";
8538
+ reject(new Error(errorMsg));
8539
+ return;
8540
+ }
8541
+ if (error.response) {
8542
+ reject(
8543
+ new Error(
8544
+ `Request failed with status ${error.response.status}: ${error.response.statusText || "Unknown error"}`
8545
+ )
8546
+ );
8547
+ return;
8548
+ }
8549
+ }
6809
8550
  if (error instanceof Error) {
6810
8551
  if (error.message.includes("Authentication expired") || error.message.includes("Authentication failed")) {
6811
8552
  reject(error);
@@ -6851,28 +8592,28 @@ var ServerLlmBackend = class {
6851
8592
  * Uses axios with responseType 'stream' for SSE
6852
8593
  */
6853
8594
  async makeStreamingRequest(model, messages, options) {
6854
- const axiosInstance = this.apiClient.client;
6855
- if (!axiosInstance) {
6856
- throw new Error("ApiClient axios instance not accessible");
6857
- }
6858
- const response = await axiosInstance.post(
6859
- this.completionsEndpoint,
6860
- {
6861
- model,
6862
- messages,
6863
- options: {
6864
- temperature: options.temperature,
6865
- maxTokens: options.maxTokens,
6866
- stream: true,
6867
- // Always use streaming for SSE
6868
- tools: options.tools || []
6869
- }
6870
- },
6871
- {
6872
- responseType: "stream"
6873
- // Auth header is automatically injected by ApiClient interceptor
8595
+ const axiosInstance = this.apiClient.getAxiosInstance();
8596
+ const requestBody = {
8597
+ model,
8598
+ messages,
8599
+ options: {
8600
+ temperature: options.temperature,
8601
+ maxTokens: options.maxTokens,
8602
+ stream: true,
8603
+ // Always use streaming for SSE
8604
+ tools: options.tools || []
6874
8605
  }
6875
- );
8606
+ };
8607
+ const bodyStr = JSON.stringify(requestBody);
8608
+ const bodySize = Buffer.byteLength(bodyStr, "utf-8");
8609
+ logger.debug(`\u2192 POST ${this.completionsEndpoint}`);
8610
+ logger.debug(` Body: ${logger.formatBytes(bodySize)}`);
8611
+ logger.debug(` Preview: ${bodyStr.substring(0, 200)}`);
8612
+ const response = await axiosInstance.post(this.completionsEndpoint, requestBody, {
8613
+ responseType: "stream"
8614
+ // Auth header is automatically injected by ApiClient interceptor
8615
+ });
8616
+ logger.debug(`\u2190 ${response.status} ${response.statusText}`);
6876
8617
  return response;
6877
8618
  }
6878
8619
  };
@@ -6903,14 +8644,21 @@ var ApiClient = class {
6903
8644
  (response) => response,
6904
8645
  async (error) => {
6905
8646
  const originalRequest = error.config;
8647
+ if (error.response?.status === 401) {
8648
+ logger.error("401 Unauthorized", error);
8649
+ } else if (error.response?.status === 403) {
8650
+ logger.error("403 Forbidden", error);
8651
+ }
6906
8652
  if (error.response?.status === 401 && !originalRequest._retry) {
6907
8653
  originalRequest._retry = true;
8654
+ logger.debug("AUTH: Attempting token refresh");
6908
8655
  try {
6909
8656
  const tokens = await this.configStore.getAuthTokens();
6910
8657
  if (!tokens) {
6911
8658
  throw new Error("Not authenticated");
6912
8659
  }
6913
8660
  const newTokens = await this.oauthClient.refreshToken(tokens.refreshToken);
8661
+ logger.debug("AUTH: Token refresh successful");
6914
8662
  const expiresAt = new Date(Date.now() + newTokens.expires_in * 1e3).toISOString();
6915
8663
  await this.configStore.setAuthTokens({
6916
8664
  accessToken: newTokens.access_token,
@@ -6920,6 +8668,7 @@ var ApiClient = class {
6920
8668
  // Preserve userId
6921
8669
  });
6922
8670
  originalRequest.headers.Authorization = `Bearer ${newTokens.access_token}`;
8671
+ logger.debug("AUTH: Retrying request with new token");
6923
8672
  return this.client(originalRequest);
6924
8673
  } catch (refreshError) {
6925
8674
  console.error(
@@ -6927,11 +8676,13 @@ var ApiClient = class {
6927
8676
  refreshError instanceof Error ? refreshError.message : "Unknown error"
6928
8677
  );
6929
8678
  console.log("Please run `b4m login` again to re-authenticate.");
8679
+ logger.error("AUTH: Token refresh failed", refreshError);
6930
8680
  await this.configStore.clearAuthTokens();
6931
8681
  throw new Error("Authentication expired. Please run `b4m login` again.");
6932
8682
  }
6933
8683
  }
6934
8684
  if (error.response?.status === 401 && originalRequest._retry) {
8685
+ logger.debug("AUTH: Token refresh retry failed - clearing tokens");
6935
8686
  await this.configStore.clearAuthTokens();
6936
8687
  throw new Error("Authentication failed. Please run /login to authenticate.");
6937
8688
  }
@@ -6967,6 +8718,12 @@ var ApiClient = class {
6967
8718
  const response = await this.client.delete(url, config);
6968
8719
  return response.data;
6969
8720
  }
8721
+ /**
8722
+ * Get the underlying axios instance for advanced use cases (e.g., streaming)
8723
+ */
8724
+ getAxiosInstance() {
8725
+ return this.client;
8726
+ }
6970
8727
  /**
6971
8728
  * Check if user is authenticated
6972
8729
  */
@@ -6989,6 +8746,123 @@ var ApiClient = class {
6989
8746
  }
6990
8747
  };
6991
8748
 
8749
+ // package.json
8750
+ var package_default = {
8751
+ name: "@bike4mind/cli",
8752
+ version: "0.1.5-feat-automate-cli-publishing.16920+7db73c354",
8753
+ type: "module",
8754
+ description: "Interactive CLI tool for Bike4Mind with ReAct agents",
8755
+ license: "UNLICENSED",
8756
+ author: "Bike4Mind",
8757
+ repository: {
8758
+ type: "git",
8759
+ url: "git+https://github.com/bike4mind/lumina5.git"
8760
+ },
8761
+ homepage: "https://github.com/bike4mind/lumina5#readme",
8762
+ bugs: "https://github.com/bike4mind/lumina5/issues",
8763
+ keywords: [
8764
+ "cli",
8765
+ "ai",
8766
+ "bike4mind",
8767
+ "agent",
8768
+ "mcp",
8769
+ "react-agent"
8770
+ ],
8771
+ bin: {
8772
+ b4m: "./bin/bike4mind-cli.mjs",
8773
+ bike4mind: "./bin/bike4mind-cli.mjs"
8774
+ },
8775
+ files: [
8776
+ "dist",
8777
+ "bin"
8778
+ ],
8779
+ scripts: {
8780
+ dev: "tsx src/index.tsx",
8781
+ build: "tsup",
8782
+ typecheck: "tsc --noEmit",
8783
+ start: "node dist/index.js",
8784
+ prepublishOnly: "pnpm build"
8785
+ },
8786
+ dependencies: {
8787
+ "@anthropic-ai/sdk": "^0.22.0",
8788
+ "@aws-sdk/client-apigatewaymanagementapi": "3.654.0",
8789
+ "@aws-sdk/client-bedrock-runtime": "3.654.0",
8790
+ "@aws-sdk/client-lambda": "3.654.0",
8791
+ "@aws-sdk/client-s3": "3.654.0",
8792
+ "@aws-sdk/client-sqs": "3.654.0",
8793
+ "@aws-sdk/client-transcribe": "3.654.0",
8794
+ "@aws-sdk/credential-provider-node": "3.654.0",
8795
+ "@aws-sdk/node-http-handler": "^3.374.0",
8796
+ "@aws-sdk/s3-request-presigner": "3.654.0",
8797
+ "@casl/ability": "^6.5.0",
8798
+ "@google/genai": "^1.27.0",
8799
+ "@google/generative-ai": "^0.21.0",
8800
+ "@joplin/turndown-plugin-gfm": "^1.0.62",
8801
+ "@mendable/firecrawl-js": "^1.29.3",
8802
+ "@modelcontextprotocol/sdk": "^1.7.0",
8803
+ "@octokit/rest": "^22.0.0",
8804
+ "@opensearch-project/opensearch": "2.11.0",
8805
+ "async-mutex": "^0.5.0",
8806
+ axios: "^1.13.2",
8807
+ bcryptjs: "^2.4.3",
8808
+ cheerio: "1.0.0-rc.12",
8809
+ "csv-parse": "^5.5.6",
8810
+ dayjs: "^1.11.13",
8811
+ diff: "^8.0.2",
8812
+ dotenv: "^16.3.1",
8813
+ "eventsource-parser": "^3.0.6",
8814
+ "file-type": "^18.7.0",
8815
+ "fuse.js": "^7.1.0",
8816
+ ink: "^6.5.1",
8817
+ "ink-select-input": "^6.2.0",
8818
+ "ink-spinner": "^5.0.0",
8819
+ "ink-text-input": "^6.0.0",
8820
+ jsonwebtoken: "^9.0.2",
8821
+ lodash: "^4.17.21",
8822
+ mammoth: "^1.11.0",
8823
+ marked: "^15.0.11",
8824
+ mathjs: "^14.2.0",
8825
+ "mime-types": "^2.1.35",
8826
+ mongoose: "^8.8.3",
8827
+ ollama: "^0.5.12",
8828
+ open: "^11.0.0",
8829
+ openai: "^5.12.2",
8830
+ "p-limit": "^6.2.0",
8831
+ qrcode: "^1.5.4",
8832
+ react: "^19.2.3",
8833
+ sharp: "^0.32.6",
8834
+ speakeasy: "^2.0.0",
8835
+ tiktoken: "^1.0.16",
8836
+ turndown: "^7.2.0",
8837
+ unpdf: "^0.10.0",
8838
+ uuid: "^9.0.1",
8839
+ voyageai: "^0.0.4",
8840
+ xlsx: "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
8841
+ yargs: "^17.7.2",
8842
+ yauzl: "^3.1.3",
8843
+ zod: "^3.25.76",
8844
+ "zod-validation-error": "^1.5.0",
8845
+ zustand: "^4.5.4"
8846
+ },
8847
+ devDependencies: {
8848
+ "@bike4mind/agents": "0.1.0",
8849
+ "@bike4mind/common": "2.35.1-feat-automate-cli-publishing.16920+7db73c354",
8850
+ "@bike4mind/mcp": "1.19.3-feat-automate-cli-publishing.16920+7db73c354",
8851
+ "@bike4mind/services": "2.28.2-feat-automate-cli-publishing.16920+7db73c354",
8852
+ "@bike4mind/utils": "2.0.2-feat-automate-cli-publishing.16920+7db73c354",
8853
+ "@types/diff": "^5.0.9",
8854
+ "@types/jsonwebtoken": "^9.0.4",
8855
+ "@types/node": "^22.9.0",
8856
+ "@types/react": "^19.2.7",
8857
+ "@types/uuid": "^9.0.7",
8858
+ "@types/yargs": "^17.0.32",
8859
+ tsup: "^8.5.1",
8860
+ tsx: "^4.21.0",
8861
+ typescript: "^5.9.3"
8862
+ },
8863
+ gitHead: "7db73c354ada948da60d9a2443c7a63519f4972d"
8864
+ };
8865
+
6992
8866
  // src/index.tsx
6993
8867
  process.removeAllListeners("warning");
6994
8868
  process.on("warning", (warning) => {
@@ -7022,14 +8896,14 @@ function CliApp() {
7022
8896
  if (state.session) {
7023
8897
  cleanupTasks.push(
7024
8898
  state.sessionStore.save(state.session).catch((err) => {
7025
- console.log("[CLEANUP] Session save error:", err.message);
8899
+ logger.debug(`[CLEANUP] Session save error: ${err.message}`);
7026
8900
  })
7027
8901
  );
7028
8902
  }
7029
8903
  if (state.mcpManager) {
7030
8904
  cleanupTasks.push(
7031
8905
  state.mcpManager.disconnect().catch((err) => {
7032
- console.log("[CLEANUP] MCP disconnect error:", err.message);
8906
+ logger.debug(`[CLEANUP] MCP disconnect error: ${err.message}`);
7033
8907
  })
7034
8908
  );
7035
8909
  }
@@ -7049,12 +8923,12 @@ function CliApp() {
7049
8923
  if (key.ctrl && input === "c") {
7050
8924
  const now = Date.now();
7051
8925
  if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
7052
- console.log("[EXIT] Second Ctrl+C - cleaning up and exiting...");
8926
+ logger.debug("[EXIT Second Ctrl+C - cleaning up and exiting...");
7053
8927
  performCleanup().then(() => {
7054
8928
  exit();
7055
8929
  });
7056
8930
  } else {
7057
- console.log("[EXIT] First Ctrl+C - press again to exit");
8931
+ logger.debug("[EXIT First Ctrl+C - press again to exit");
7058
8932
  exitTimestamp = now;
7059
8933
  setExitRequested(true);
7060
8934
  setTimeout(() => {
@@ -7150,6 +9024,14 @@ function CliApp() {
7150
9024
  toolCallCount: 0
7151
9025
  }
7152
9026
  };
9027
+ await logger.initialize(newSession.id);
9028
+ logger.setVerbose(process.env.B4M_VERBOSE === "1");
9029
+ logger.info(`API: ${apiBaseURL}`);
9030
+ logger.info(`Environment: ${envName}`);
9031
+ logger.info(`Model: ${modelInfo.id}`);
9032
+ logger.debug("=== Session Configuration Complete ===");
9033
+ logger.cleanupOldLogs().catch(() => {
9034
+ });
7153
9035
  const silentLogger = {
7154
9036
  log: () => {
7155
9037
  },
@@ -7372,7 +9254,7 @@ Tool Permissions:
7372
9254
  break;
7373
9255
  case "exit":
7374
9256
  case "quit":
7375
- console.log("[EXIT] /exit command - cleaning up and exiting...");
9257
+ logger.debug("[EXIT /exit command - cleaning up and exiting...");
7376
9258
  await performCleanup();
7377
9259
  exit();
7378
9260
  break;
@@ -7585,7 +9467,8 @@ var BANNER = `
7585
9467
  \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
7586
9468
  `;
7587
9469
  console.log("\x1B[36m\x1B[1m%s\x1B[0m", BANNER);
7588
- console.log("\x1B[2m v0.1.0 - AI-Powered CLI\x1B[0m\n");
9470
+ console.log(`\x1B[2m v${package_default.version} - AI-Powered CLI\x1B[0m
9471
+ `);
7589
9472
  var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "development";
7590
9473
  if (isDevMode) {
7591
9474
  console.log("\u{1F527} Running in development mode (using TypeScript source)\n");