@bike4mind/cli 0.1.3 → 0.1.5-feat-automate-cli-publishing.16903

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-XANFVWI6.js";
8
- import "./chunk-TPSQ5MIR.js";
9
- import "./chunk-KZGZGNHL.js";
7
+ } from "./chunk-ZJBAQHHQ.js";
8
+ import "./chunk-PMPXEVZQ.js";
9
+ import "./chunk-PYV7BIVP.js";
10
10
  import {
11
11
  BFLImageService,
12
12
  BaseStorage,
@@ -15,7 +15,7 @@ import {
15
15
  OpenAIBackend,
16
16
  OpenAIImageService,
17
17
  XAIImageService
18
- } from "./chunk-B3YFSFVU.js";
18
+ } from "./chunk-G2XBD676.js";
19
19
  import {
20
20
  Logger
21
21
  } from "./chunk-CC67R4RB.js";
@@ -72,7 +72,7 @@ import {
72
72
  XAI_IMAGE_MODELS,
73
73
  b4mLLMTools,
74
74
  getMcpProviderMetadata
75
- } from "./chunk-MKO2KCCS.js";
75
+ } from "./chunk-PG4BD2XZ.js";
76
76
  import {
77
77
  __require
78
78
  } from "./chunk-PDX44BCA.js";
@@ -207,6 +207,7 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
207
207
  const [selectedIndex, setSelectedIndex] = useState(0);
208
208
  const [historyIndex, setHistoryIndex] = useState(-1);
209
209
  const [tempInput, setTempInput] = useState("");
210
+ const [inputKey, setInputKey] = useState(0);
210
211
  const shouldShowAutocomplete = value.startsWith("/") && !disabled;
211
212
  const query = shouldShowAutocomplete ? value.slice(1) : "";
212
213
  const filteredCommands = useMemo(() => {
@@ -253,6 +254,12 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
253
254
  if (shouldShowAutocomplete && filteredCommands.length > 0) {
254
255
  const selectedCommand = filteredCommands[selectedIndex];
255
256
  const commandText = `/${selectedCommand.name}`;
257
+ if (selectedCommand.args) {
258
+ setValue(commandText + " ");
259
+ setHistoryIndex(-1);
260
+ setInputKey((prev) => prev + 1);
261
+ return;
262
+ }
256
263
  setValue("");
257
264
  setHistoryIndex(-1);
258
265
  onSubmit(commandText);
@@ -272,6 +279,7 @@ function InputPrompt({ onSubmit, disabled = false, history = [] }) {
272
279
  return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "cyan" }, "> "), /* @__PURE__ */ React3.createElement(
273
280
  TextInput,
274
281
  {
282
+ key: inputKey,
275
283
  value,
276
284
  onChange: handleChange,
277
285
  onSubmit: handleSubmit,
@@ -463,6 +471,7 @@ import React9, { useState as useState2, useEffect as useEffect2 } from "react";
463
471
  import { Box as Box9, Text as Text8 } from "ink";
464
472
  import Spinner2 from "ink-spinner";
465
473
  import jwt from "jsonwebtoken";
474
+ import open from "open";
466
475
 
467
476
  // src/auth/OAuthClient.ts
468
477
  import axios from "axios";
@@ -614,6 +623,13 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
614
623
  };
615
624
  runLoginFlow();
616
625
  }, [apiUrl, configStore, onSuccess, onError]);
626
+ useEffect2(() => {
627
+ if (deviceFlow && status === "waiting") {
628
+ open(deviceFlow.verification_uri_complete).catch((err) => {
629
+ console.error("Failed to auto-open browser:", err);
630
+ });
631
+ }
632
+ }, [deviceFlow, status]);
617
633
  if (status === "initiating") {
618
634
  return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan" }, /* @__PURE__ */ React9.createElement(Spinner2, { type: "dots" }), " Initiating device authorization...")));
619
635
  }
@@ -623,7 +639,7 @@ function LoginFlow({ apiUrl = "http://localhost:3000", configStore, onSuccess, o
623
639
  if (status === "success") {
624
640
  return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "green", bold: true }, "\u2714 Successfully authenticated!")), /* @__PURE__ */ React9.createElement(Box9, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "You can now use B4M CLI with your account.")));
625
641
  }
626
- return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "Please visit the following URL to authorize this device:")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "Enter this code when prompted:")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan" }, /* @__PURE__ */ React9.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
642
+ return /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "column", padding: 1, borderStyle: "round", borderColor: "cyan" }, /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan", bold: true }, "\u{1F510} Device Authorization")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "Opening browser automatically... If it doesn't open, please visit:")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { color: "blue", bold: true }, deviceFlow?.verification_uri)), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, null, "And enter this code when prompted:")), /* @__PURE__ */ React9.createElement(Box9, { marginBottom: 1, paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, deviceFlow?.user_code)), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan" }, /* @__PURE__ */ React9.createElement(Spinner2, { type: "dots" }), " ", statusMessage)), /* @__PURE__ */ React9.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "Expires in ", deviceFlow ? Math.floor(deviceFlow.expires_in / 60) : 0, " minutes")));
627
643
  }
628
644
 
629
645
  // src/storage/SessionStore.ts
@@ -860,7 +876,6 @@ var ConfigStore = class {
860
876
  } else {
861
877
  rawConfig.apiConfig = {};
862
878
  }
863
- console.log("[ConfigStore] Auto-migrated API config from old environment format");
864
879
  }
865
880
  const validated = CliConfigSchema.parse(rawConfig);
866
881
  this.config = {
@@ -882,10 +897,6 @@ var ConfigStore = class {
882
897
  },
883
898
  trustedTools: validated.trustedTools || []
884
899
  };
885
- console.log("[ConfigStore] Loaded config with auth:", !!this.config.auth);
886
- if (this.config.auth) {
887
- console.log("[ConfigStore] Auth userId:", this.config.auth.userId);
888
- }
889
900
  return this.config;
890
901
  } catch (error) {
891
902
  if (error.code === "ENOENT") {
@@ -929,13 +940,8 @@ var ConfigStore = class {
929
940
  throw new Error("No configuration to save");
930
941
  }
931
942
  try {
932
- console.log("[ConfigStore] Saving config with auth:", !!this.config.auth);
933
- if (this.config.auth) {
934
- console.log("[ConfigStore] Auth userId:", this.config.auth.userId);
935
- }
936
943
  await fs2.writeFile(this.configPath, JSON.stringify(this.config, null, 2), "utf-8");
937
944
  await fs2.chmod(this.configPath, 384);
938
- console.log("[ConfigStore] Config saved to:", this.configPath);
939
945
  } catch (error) {
940
946
  console.error("Failed to save config:", error);
941
947
  throw error;
@@ -3630,8 +3636,8 @@ async function processAndStoreImages(images, context) {
3630
3636
  const buffer = await downloadImage(image);
3631
3637
  const fileType = await fileTypeFromBuffer2(buffer);
3632
3638
  const filename = `${uuidv45()}.${fileType?.ext}`;
3633
- const path10 = await context.imageGenerateStorage.upload(buffer, filename, {});
3634
- return path10;
3639
+ const path11 = await context.imageGenerateStorage.upload(buffer, filename, {});
3640
+ return path11;
3635
3641
  }));
3636
3642
  }
3637
3643
  async function updateQuestAndReturnMarkdown(storedImageUrls, context) {
@@ -4106,15 +4112,126 @@ var mermaidChartTool = {
4106
4112
  };
4107
4113
 
4108
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
+ };
4109
4157
  var getCurrentDateTime = async (parameters = {}) => {
4110
- 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;
4111
4159
  try {
4112
4160
  const now = /* @__PURE__ */ new Date();
4113
4161
  try {
4114
4162
  Intl.DateTimeFormat(void 0, { timeZone: timezone2 });
4115
- } catch (error) {
4163
+ } catch {
4116
4164
  throw new Error(`Invalid timezone: ${timezone2}. Please use IANA timezone format (e.g., "America/New_York", "Europe/London")`);
4117
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
+ }
4118
4235
  const formatOptions = {
4119
4236
  timeZone: timezone2,
4120
4237
  year: "numeric",
@@ -4126,7 +4243,23 @@ var getCurrentDateTime = async (parameters = {}) => {
4126
4243
  ...includeTimezone && { timeZoneName: "short" }
4127
4244
  };
4128
4245
  switch (format) {
4129
- 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": {
4130
4263
  const offset = now.getTimezoneOffset() * 6e4;
4131
4264
  const localISOTime = new Date(now.getTime() - offset).toISOString();
4132
4265
  const formatter = new Intl.DateTimeFormat("en", {
@@ -4137,10 +4270,12 @@ var getCurrentDateTime = async (parameters = {}) => {
4137
4270
  const timeZonePart = parts.find((part) => part.type === "timeZoneName");
4138
4271
  const timezoneOffset = timeZonePart?.value || "Z";
4139
4272
  return `${localISOTime.slice(0, -1)}${timezoneOffset === "GMT" ? "Z" : timezoneOffset}`;
4140
- case "human":
4273
+ }
4274
+ case "human": {
4141
4275
  const humanFormat = new Intl.DateTimeFormat("en-US", formatOptions);
4142
4276
  return humanFormat.format(now);
4143
- case "detailed":
4277
+ }
4278
+ case "detailed": {
4144
4279
  const detailedFormat = new Intl.DateTimeFormat("en-US", {
4145
4280
  timeZone: timezone2,
4146
4281
  weekday: "long",
@@ -4152,9 +4287,15 @@ var getCurrentDateTime = async (parameters = {}) => {
4152
4287
  timeZoneName: "long"
4153
4288
  });
4154
4289
  const formatted = detailedFormat.format(now);
4155
- 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
+ }
4156
4297
  default:
4157
- 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`);
4158
4299
  }
4159
4300
  } catch (error) {
4160
4301
  if (error instanceof Error) {
@@ -4180,7 +4321,7 @@ var currentDateTimeTool = {
4180
4321
  },
4181
4322
  toolSchema: {
4182
4323
  name: "current_datetime",
4183
- 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.",
4184
4325
  parameters: {
4185
4326
  type: "object",
4186
4327
  properties: {
@@ -4190,8 +4331,8 @@ var currentDateTimeTool = {
4190
4331
  },
4191
4332
  format: {
4192
4333
  type: "string",
4193
- description: "Output format for the date/time",
4194
- 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"]
4195
4336
  },
4196
4337
  includeTimezone: {
4197
4338
  type: "boolean",
@@ -4200,6 +4341,18 @@ var currentDateTimeTool = {
4200
4341
  includeWeekday: {
4201
4342
  type: "boolean",
4202
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".'
4203
4356
  }
4204
4357
  },
4205
4358
  additionalProperties: false,
@@ -4668,8 +4821,8 @@ async function processAndStoreImage(imageUrl, context) {
4668
4821
  const buffer = await downloadImage2(imageUrl);
4669
4822
  const fileType = await fileTypeFromBuffer3(buffer);
4670
4823
  const filename = `${uuidv46()}.${fileType?.ext}`;
4671
- const path10 = await context.imageGenerateStorage.upload(buffer, filename, {});
4672
- return path10;
4824
+ const path11 = await context.imageGenerateStorage.upload(buffer, filename, {});
4825
+ return path11;
4673
4826
  }
4674
4827
  async function updateQuestAndReturnMarkdown2(storedImagePath, context) {
4675
4828
  await context.onFinish?.("edit_image", storedImagePath);
@@ -5169,9 +5322,9 @@ var blogDraftTool = {
5169
5322
  implementation: (context) => ({
5170
5323
  toolFn: async (value) => {
5171
5324
  const params = value;
5172
- const { logger, llm } = context;
5325
+ const { logger: logger2, llm } = context;
5173
5326
  const outputFormat = params.outputFormat || "blog";
5174
- logger.info("Drafting blog content", {
5327
+ logger2.info("Drafting blog content", {
5175
5328
  userId: context.userId,
5176
5329
  outputFormat,
5177
5330
  hasVoiceGuide: !!params.voiceGuide,
@@ -5184,7 +5337,7 @@ var blogDraftTool = {
5184
5337
  additionalInstructions: params.additionalInstructions
5185
5338
  });
5186
5339
  const modelToUse = context.model || "claude-sonnet-4-5-20250929";
5187
- logger.info("Using model for blog draft:", { model: modelToUse });
5340
+ logger2.info("Using model for blog draft:", { model: modelToUse });
5188
5341
  let llmResponse = "";
5189
5342
  await llm.complete(modelToUse, [
5190
5343
  { role: "system", content: systemPrompt },
@@ -5200,9 +5353,9 @@ ${params.sourceContent}`
5200
5353
  }, async (texts) => {
5201
5354
  llmResponse = texts.filter((t) => t !== null && t !== void 0).join("");
5202
5355
  });
5203
- logger.info("Blog draft complete", { responseLength: llmResponse.length });
5356
+ logger2.info("Blog draft complete", { responseLength: llmResponse.length });
5204
5357
  const result = parseTransformationResult(llmResponse);
5205
- logger.info("Blog draft created successfully", {
5358
+ logger2.info("Blog draft created successfully", {
5206
5359
  title: result.title,
5207
5360
  contentLength: result.content.length,
5208
5361
  tagsCount: result.suggestedTags.length
@@ -5216,7 +5369,7 @@ ${JSON.stringify(result, null, 2)}
5216
5369
  \u{1F4CB} The preview card will appear below for you to review and edit before publishing.
5217
5370
  `;
5218
5371
  } catch (error) {
5219
- logger.error("Blog draft creation failed:", error);
5372
+ logger2.error("Blog draft creation failed:", error);
5220
5373
  throw error;
5221
5374
  }
5222
5375
  },
@@ -5250,199 +5403,1454 @@ ${JSON.stringify(result, null, 2)}
5250
5403
  })
5251
5404
  };
5252
5405
 
5253
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
5254
- import { promises as fs5 } from "fs";
5255
- import { existsSync, statSync } from "fs";
5256
- import path4 from "path";
5257
- var MAX_FILE_SIZE = 10 * 1024 * 1024;
5258
- async function readFileContent(params) {
5259
- const { path: filePath, encoding = "utf-8", maxLines } = params;
5260
- const normalizedPath = path4.normalize(filePath);
5261
- const resolvedPath = path4.resolve(process.cwd(), normalizedPath);
5262
- const cwd = path4.resolve(process.cwd());
5263
- if (!resolvedPath.startsWith(cwd)) {
5264
- throw new Error(`Access denied: Cannot read files outside of current working directory`);
5265
- }
5266
- if (!existsSync(resolvedPath)) {
5267
- throw new Error(`File not found: ${filePath}`);
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
+ };
5268
5414
  }
5269
- const stats = statSync(resolvedPath);
5270
- if (stats.isDirectory()) {
5271
- throw new Error(`Path is a directory, not a file: ${filePath}`);
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
+ };
5272
5421
  }
5273
- if (stats.size > MAX_FILE_SIZE) {
5274
- throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${MAX_FILE_SIZE / 1024 / 1024}MB)`);
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
+ };
5275
5428
  }
5276
- const isBinary = await checkIfBinary(resolvedPath);
5277
- if (isBinary && encoding === "utf-8") {
5278
- throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
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);
5279
5437
  }
5280
- const content = await fs5.readFile(resolvedPath, encoding);
5281
- if (maxLines && typeof content === "string") {
5282
- const lines = content.split("\n");
5283
- if (lines.length > maxLines) {
5284
- const truncatedContent = lines.slice(0, maxLines).join("\n");
5285
- return `${truncatedContent}
5286
-
5287
- ... (truncated: ${lines.length - maxLines} more lines, ${stats.size} bytes total)`;
5288
- }
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);
5289
5442
  }
5290
- return typeof content === "string" ? content : `[Binary content, ${stats.size} bytes, base64 encoded]
5291
- ${content}`;
5292
- }
5293
- async function checkIfBinary(filePath) {
5294
- const buffer = Buffer.alloc(8192);
5295
- const fd = await fs5.open(filePath, "r");
5443
+ return events;
5444
+ };
5445
+ var getWikipediaOnThisDay = async (parameters = {}) => {
5446
+ const { date, type = "selected", limit = 10, year_filter, decade_filter } = parameters;
5296
5447
  try {
5297
- const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
5298
- const chunk = buffer.slice(0, bytesRead);
5299
- return chunk.includes(0);
5300
- } finally {
5301
- await fd.close();
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)}`);
5302
5522
  }
5303
- }
5304
- var fileReadTool = {
5305
- name: "file_read",
5523
+ };
5524
+ var wikipediaOnThisDayTool = {
5525
+ name: "wikipedia_on_this_day",
5306
5526
  implementation: (context) => ({
5307
5527
  toolFn: async (value) => {
5308
5528
  const params = value;
5309
- context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
5529
+ context.logger.log("\u{1F4C5} WikipediaOnThisDay: Starting execution", params);
5310
5530
  try {
5311
- const content = await readFileContent(params);
5312
- const stats = statSync(path4.resolve(process.cwd(), path4.normalize(params.path)));
5313
- context.logger.info("\u2705 FileRead: Success", {
5314
- path: params.path,
5315
- size: stats.size,
5316
- lines: typeof content === "string" ? content.split("\n").length : "binary"
5317
- });
5318
- return content;
5531
+ const result = await getWikipediaOnThisDay(params);
5532
+ context.logger.log("\u2705 WikipediaOnThisDay: Execution completed");
5533
+ return result;
5319
5534
  } catch (error) {
5320
- context.logger.error("\u274C FileRead: Failed", error);
5535
+ context.logger.error("\u274C WikipediaOnThisDay: Execution failed", error);
5321
5536
  throw error;
5322
5537
  }
5323
5538
  },
5324
5539
  toolSchema: {
5325
- name: "file_read",
5326
- description: "Read the contents of a file from the local filesystem. Supports text files with various encodings. Files are restricted to the current working directory and subdirectories for security.",
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.',
5327
5542
  parameters: {
5328
5543
  type: "object",
5329
5544
  properties: {
5330
- path: {
5545
+ date: {
5331
5546
  type: "string",
5332
- description: "Path to the file to read (relative to current working directory or absolute path within working directory)"
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.'
5333
5548
  },
5334
- encoding: {
5549
+ type: {
5335
5550
  type: "string",
5336
- description: "File encoding (default: utf-8). Use base64 for binary files.",
5337
- enum: ["utf-8", "ascii", "base64"]
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"]
5338
5553
  },
5339
- maxLines: {
5554
+ limit: {
5340
5555
  type: "number",
5341
- description: "Maximum number of lines to return (optional). Useful for previewing large files. Returns full file by default."
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."
5342
5565
  }
5343
5566
  },
5344
- required: ["path"]
5567
+ additionalProperties: false,
5568
+ required: []
5345
5569
  }
5346
5570
  }
5347
5571
  })
5348
5572
  };
5349
5573
 
5350
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
5351
- import { promises as fs6 } from "fs";
5352
- import { existsSync as existsSync2 } from "fs";
5353
- import path5 from "path";
5354
- async function createFile(params) {
5355
- const { path: filePath, content, createDirectories = true } = params;
5356
- const normalizedPath = path5.normalize(filePath);
5357
- const resolvedPath = path5.resolve(process.cwd(), normalizedPath);
5358
- const cwd = path5.resolve(process.cwd());
5359
- if (!resolvedPath.startsWith(cwd)) {
5360
- throw new Error(`Access denied: Cannot create files outside of current working directory`);
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();
5361
5653
  }
5362
- const fileExists = existsSync2(resolvedPath);
5363
- const action = fileExists ? "overwritten" : "created";
5364
- if (createDirectories) {
5365
- const dir = path5.dirname(resolvedPath);
5366
- await fs6.mkdir(dir, { recursive: true });
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.`);
5367
5657
  }
5368
- await fs6.writeFile(resolvedPath, content, "utf-8");
5369
- const stats = await fs6.stat(resolvedPath);
5370
- const lines = content.split("\n").length;
5371
- return `File ${action} successfully: ${filePath}
5372
- Size: ${stats.size} bytes
5373
- Lines: ${lines}`;
5374
- }
5375
- var createFileTool = {
5376
- name: "create_file",
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",
5377
5729
  implementation: (context) => ({
5378
5730
  toolFn: async (value) => {
5379
5731
  const params = value;
5380
- const fileExists = existsSync2(path5.resolve(process.cwd(), path5.normalize(params.path)));
5381
- context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
5382
- path: params.path,
5383
- size: params.content.length
5384
- });
5732
+ context.logger.log("\u{1F319} MoonPhase: Starting execution", params);
5385
5733
  try {
5386
- const result = await createFile(params);
5387
- context.logger.info("\u2705 CreateFile: Success", { path: params.path });
5734
+ const result = await getMoonPhase(params);
5735
+ context.logger.log("\u2705 MoonPhase: Execution completed");
5388
5736
  return result;
5389
5737
  } catch (error) {
5390
- context.logger.error("\u274C CreateFile: Failed", error);
5738
+ context.logger.error("\u274C MoonPhase: Execution failed", error);
5391
5739
  throw error;
5392
5740
  }
5393
5741
  },
5394
5742
  toolSchema: {
5395
- name: "create_file",
5396
- description: "Create a new file or overwrite an existing file with the provided content. Will create parent directories if they do not exist. Restricted to current working directory for security. Always prompts user for confirmation before writing.",
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.",
5397
5745
  parameters: {
5398
5746
  type: "object",
5399
5747
  properties: {
5400
- path: {
5748
+ date: {
5401
5749
  type: "string",
5402
- description: "Path where the file should be created (relative to current working directory)"
5750
+ description: "The date to check moon phase for. Use YYYY-MM-DD format. Defaults to today if not specified."
5403
5751
  },
5404
- content: {
5752
+ timezone: {
5405
5753
  type: "string",
5406
- description: "Content to write to the file"
5407
- },
5408
- createDirectories: {
5409
- type: "boolean",
5410
- description: "Create parent directories if they do not exist (default: true)"
5754
+ description: 'IANA timezone identifier (e.g., "America/New_York"). Used for date display. Defaults to UTC.'
5411
5755
  }
5412
5756
  },
5413
- required: ["path", "content"]
5757
+ additionalProperties: false,
5758
+ required: []
5414
5759
  }
5415
5760
  }
5416
5761
  })
5417
5762
  };
5418
5763
 
5419
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
5420
- import { readdir } from "fs/promises";
5421
- import { statSync as statSync2 } from "fs";
5422
- import path6 from "path";
5423
- function matchGlob(filePath, pattern) {
5424
- let regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
5425
- regexPattern = regexPattern.replace(/\{\{GLOBSTAR\}\}\//g, "(.*/)?").replace(/\/\{\{GLOBSTAR\}\}/g, "(/.*)?").replace(/\{\{GLOBSTAR\}\}/g, ".*");
5426
- const regex = new RegExp(`^${regexPattern}$`);
5427
- return regex.test(filePath);
5428
- }
5429
- async function walkDir(dir, pattern, includeHidden, maxResults, basePath = "") {
5430
- const matches = [];
5431
- try {
5432
- const entries = await readdir(dir, { withFileTypes: true });
5433
- for (const entry of entries) {
5434
- if (matches.length >= maxResults) {
5435
- break;
5436
- }
5437
- if (!includeHidden && entry.name.startsWith(".")) {
5438
- continue;
5439
- }
5440
- const relativePath = basePath ? path6.join(basePath, entry.name) : entry.name;
5441
- const fullPath = path6.join(dir, entry.name);
5442
- if (matchGlob(relativePath, pattern)) {
5443
- matches.push(relativePath);
5444
- }
5445
- if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
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
+ }
6599
+ }
6600
+ }
6601
+ if (conjunctions.length > 0) {
6602
+ results.push("**Planetary Conjunctions:**");
6603
+ conjunctions.forEach((c) => results.push(`- ${c}`));
6604
+ results.push("");
6605
+ }
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}`);
6610
+ }
6611
+ }
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",
6618
+ implementation: (context) => ({
6619
+ toolFn: async (value) => {
6620
+ const params = value;
6621
+ context.logger.log("\u{1FA90} PlanetVisibility: Starting execution", params);
6622
+ try {
6623
+ const result = await getPlanetVisibility(params);
6624
+ context.logger.log("\u2705 PlanetVisibility: Execution completed");
6625
+ return result;
6626
+ } catch (error) {
6627
+ context.logger.error("\u274C PlanetVisibility: Execution failed", error);
6628
+ throw error;
6629
+ }
6630
+ },
6631
+ toolSchema: {
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.",
6634
+ parameters: {
6635
+ type: "object",
6636
+ properties: {
6637
+ latitude: {
6638
+ type: "number",
6639
+ description: "Latitude in decimal degrees (-90 to 90). Example: 40.7128 for New York City."
6640
+ },
6641
+ longitude: {
6642
+ type: "number",
6643
+ description: "Longitude in decimal degrees (-180 to 180). Example: -74.0060 for New York City."
6644
+ },
6645
+ date: {
6646
+ type: "string",
6647
+ description: "The date to check. Use YYYY-MM-DD format. Defaults to today if not specified."
6648
+ },
6649
+ timezone: {
6650
+ type: "string",
6651
+ description: 'IANA timezone identifier for time display (e.g., "America/New_York"). Defaults to UTC.'
6652
+ }
6653
+ },
6654
+ additionalProperties: false,
6655
+ required: ["latitude", "longitude"]
6656
+ }
6657
+ }
6658
+ })
6659
+ };
6660
+
6661
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/fileRead/index.js
6662
+ import { promises as fs5 } from "fs";
6663
+ import { existsSync, statSync } from "fs";
6664
+ import path4 from "path";
6665
+ var MAX_FILE_SIZE = 10 * 1024 * 1024;
6666
+ async function readFileContent(params) {
6667
+ const { path: filePath, encoding = "utf-8", maxLines } = params;
6668
+ const normalizedPath = path4.normalize(filePath);
6669
+ const resolvedPath = path4.resolve(process.cwd(), normalizedPath);
6670
+ const cwd = path4.resolve(process.cwd());
6671
+ if (!resolvedPath.startsWith(cwd)) {
6672
+ throw new Error(`Access denied: Cannot read files outside of current working directory`);
6673
+ }
6674
+ if (!existsSync(resolvedPath)) {
6675
+ throw new Error(`File not found: ${filePath}`);
6676
+ }
6677
+ const stats = statSync(resolvedPath);
6678
+ if (stats.isDirectory()) {
6679
+ throw new Error(`Path is a directory, not a file: ${filePath}`);
6680
+ }
6681
+ if (stats.size > MAX_FILE_SIZE) {
6682
+ throw new Error(`File too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB (max ${MAX_FILE_SIZE / 1024 / 1024}MB)`);
6683
+ }
6684
+ const isBinary = await checkIfBinary(resolvedPath);
6685
+ if (isBinary && encoding === "utf-8") {
6686
+ throw new Error(`File appears to be binary. Use encoding 'base64' to read binary files, or specify a different encoding.`);
6687
+ }
6688
+ const content = await fs5.readFile(resolvedPath, encoding);
6689
+ if (maxLines && typeof content === "string") {
6690
+ const lines = content.split("\n");
6691
+ if (lines.length > maxLines) {
6692
+ const truncatedContent = lines.slice(0, maxLines).join("\n");
6693
+ return `${truncatedContent}
6694
+
6695
+ ... (truncated: ${lines.length - maxLines} more lines, ${stats.size} bytes total)`;
6696
+ }
6697
+ }
6698
+ return typeof content === "string" ? content : `[Binary content, ${stats.size} bytes, base64 encoded]
6699
+ ${content}`;
6700
+ }
6701
+ async function checkIfBinary(filePath) {
6702
+ const buffer = Buffer.alloc(8192);
6703
+ const fd = await fs5.open(filePath, "r");
6704
+ try {
6705
+ const { bytesRead } = await fd.read(buffer, 0, 8192, 0);
6706
+ const chunk = buffer.slice(0, bytesRead);
6707
+ return chunk.includes(0);
6708
+ } finally {
6709
+ await fd.close();
6710
+ }
6711
+ }
6712
+ var fileReadTool = {
6713
+ name: "file_read",
6714
+ implementation: (context) => ({
6715
+ toolFn: async (value) => {
6716
+ const params = value;
6717
+ context.logger.info("\u{1F4C4} FileRead: Reading file", { path: params.path });
6718
+ try {
6719
+ const content = await readFileContent(params);
6720
+ const stats = statSync(path4.resolve(process.cwd(), path4.normalize(params.path)));
6721
+ context.logger.info("\u2705 FileRead: Success", {
6722
+ path: params.path,
6723
+ size: stats.size,
6724
+ lines: typeof content === "string" ? content.split("\n").length : "binary"
6725
+ });
6726
+ return content;
6727
+ } catch (error) {
6728
+ context.logger.error("\u274C FileRead: Failed", error);
6729
+ throw error;
6730
+ }
6731
+ },
6732
+ toolSchema: {
6733
+ name: "file_read",
6734
+ description: "Read the contents of a file from the local filesystem. Supports text files with various encodings. Files are restricted to the current working directory and subdirectories for security.",
6735
+ parameters: {
6736
+ type: "object",
6737
+ properties: {
6738
+ path: {
6739
+ type: "string",
6740
+ description: "Path to the file to read (relative to current working directory or absolute path within working directory)"
6741
+ },
6742
+ encoding: {
6743
+ type: "string",
6744
+ description: "File encoding (default: utf-8). Use base64 for binary files.",
6745
+ enum: ["utf-8", "ascii", "base64"]
6746
+ },
6747
+ maxLines: {
6748
+ type: "number",
6749
+ description: "Maximum number of lines to return (optional). Useful for previewing large files. Returns full file by default."
6750
+ }
6751
+ },
6752
+ required: ["path"]
6753
+ }
6754
+ }
6755
+ })
6756
+ };
6757
+
6758
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/createFile/index.js
6759
+ import { promises as fs6 } from "fs";
6760
+ import { existsSync as existsSync2 } from "fs";
6761
+ import path5 from "path";
6762
+ async function createFile(params) {
6763
+ const { path: filePath, content, createDirectories = true } = params;
6764
+ const normalizedPath = path5.normalize(filePath);
6765
+ const resolvedPath = path5.resolve(process.cwd(), normalizedPath);
6766
+ const cwd = path5.resolve(process.cwd());
6767
+ if (!resolvedPath.startsWith(cwd)) {
6768
+ throw new Error(`Access denied: Cannot create files outside of current working directory`);
6769
+ }
6770
+ const fileExists = existsSync2(resolvedPath);
6771
+ const action = fileExists ? "overwritten" : "created";
6772
+ if (createDirectories) {
6773
+ const dir = path5.dirname(resolvedPath);
6774
+ await fs6.mkdir(dir, { recursive: true });
6775
+ }
6776
+ await fs6.writeFile(resolvedPath, content, "utf-8");
6777
+ const stats = await fs6.stat(resolvedPath);
6778
+ const lines = content.split("\n").length;
6779
+ return `File ${action} successfully: ${filePath}
6780
+ Size: ${stats.size} bytes
6781
+ Lines: ${lines}`;
6782
+ }
6783
+ var createFileTool = {
6784
+ name: "create_file",
6785
+ implementation: (context) => ({
6786
+ toolFn: async (value) => {
6787
+ const params = value;
6788
+ const fileExists = existsSync2(path5.resolve(process.cwd(), path5.normalize(params.path)));
6789
+ context.logger.info(`\u{1F4DD} CreateFile: ${fileExists ? "Overwriting" : "Creating"} file`, {
6790
+ path: params.path,
6791
+ size: params.content.length
6792
+ });
6793
+ try {
6794
+ const result = await createFile(params);
6795
+ context.logger.info("\u2705 CreateFile: Success", { path: params.path });
6796
+ return result;
6797
+ } catch (error) {
6798
+ context.logger.error("\u274C CreateFile: Failed", error);
6799
+ throw error;
6800
+ }
6801
+ },
6802
+ toolSchema: {
6803
+ name: "create_file",
6804
+ description: "Create a new file or overwrite an existing file with the provided content. Will create parent directories if they do not exist. Restricted to current working directory for security. Always prompts user for confirmation before writing.",
6805
+ parameters: {
6806
+ type: "object",
6807
+ properties: {
6808
+ path: {
6809
+ type: "string",
6810
+ description: "Path where the file should be created (relative to current working directory)"
6811
+ },
6812
+ content: {
6813
+ type: "string",
6814
+ description: "Content to write to the file"
6815
+ },
6816
+ createDirectories: {
6817
+ type: "boolean",
6818
+ description: "Create parent directories if they do not exist (default: true)"
6819
+ }
6820
+ },
6821
+ required: ["path", "content"]
6822
+ }
6823
+ }
6824
+ })
6825
+ };
6826
+
6827
+ // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/globFiles/index.js
6828
+ import { readdir } from "fs/promises";
6829
+ import { statSync as statSync2 } from "fs";
6830
+ import path6 from "path";
6831
+ function matchGlob(filePath, pattern) {
6832
+ let regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/\{([^}]+)\}/g, (_, options) => `(${options.split(",").join("|")})`).replace(/\[([^\]]+)\]/g, "[$1]");
6833
+ regexPattern = regexPattern.replace(/\{\{GLOBSTAR\}\}\//g, "(.*/)?").replace(/\/\{\{GLOBSTAR\}\}/g, "(/.*)?").replace(/\{\{GLOBSTAR\}\}/g, ".*");
6834
+ const regex = new RegExp(`^${regexPattern}$`);
6835
+ return regex.test(filePath);
6836
+ }
6837
+ async function walkDir(dir, pattern, includeHidden, maxResults, basePath = "") {
6838
+ const matches = [];
6839
+ try {
6840
+ const entries = await readdir(dir, { withFileTypes: true });
6841
+ for (const entry of entries) {
6842
+ if (matches.length >= maxResults) {
6843
+ break;
6844
+ }
6845
+ if (!includeHidden && entry.name.startsWith(".")) {
6846
+ continue;
6847
+ }
6848
+ const relativePath = basePath ? path6.join(basePath, entry.name) : entry.name;
6849
+ const fullPath = path6.join(dir, entry.name);
6850
+ if (matchGlob(relativePath, pattern)) {
6851
+ matches.push(relativePath);
6852
+ }
6853
+ if (entry.isDirectory() && (pattern.includes("**") || pattern.includes("/"))) {
5446
6854
  const subMatches = await walkDir(fullPath, pattern, includeHidden, maxResults - matches.length, relativePath);
5447
6855
  matches.push(...subMatches);
5448
6856
  }
@@ -5843,17 +7251,23 @@ var tools = {
5843
7251
  blog_publish: blogPublishTool,
5844
7252
  blog_edit: blogEditTool,
5845
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,
5846
7260
  file_read: fileReadTool,
5847
7261
  create_file: createFileTool,
5848
7262
  glob_files: globFilesTool,
5849
7263
  grep_search: grepSearchTool,
5850
7264
  delete_file: deleteFileTool
5851
7265
  };
5852
- 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) => {
5853
7267
  const context = {
5854
7268
  userId,
5855
7269
  user,
5856
- logger,
7270
+ logger: logger2,
5857
7271
  db,
5858
7272
  storage,
5859
7273
  imageGenerateStorage,
@@ -5868,8 +7282,19 @@ var generateTools = (userId, user, logger, { db }, storage, imageGenerateStorage
5868
7282
  [key]: tool.implementation(context, config[key])
5869
7283
  }), {});
5870
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.";
5871
7286
  var generateMcpTools = async (mcpData) => {
5872
- 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
+ }
5873
7298
  const toolList = Array.isArray(tools2) ? tools2 : tools2?.tools || [];
5874
7299
  if (!Array.isArray(tools2)) {
5875
7300
  console.warn(`MCP server ${mcpData.serverName} returned unexpected tools payload:`, JSON.stringify(tools2));
@@ -5910,13 +7335,12 @@ var generateMcpTools = async (mcpData) => {
5910
7335
  } catch (error) {
5911
7336
  const serverName2 = mcpData.serverName?.toLowerCase();
5912
7337
  if (serverName2 === "atlassian") {
7338
+ const errorName = error instanceof Error ? error.name : "";
5913
7339
  const errorMessage = error instanceof Error ? error.message : String(error);
5914
- 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");
5915
7341
  if (isTokenError) {
5916
7342
  console.warn(`Atlassian token may be expired for tool ${name}, error:`, errorMessage);
5917
- return `\u26A0\uFE0F Atlassian authentication error: ${errorMessage}
5918
-
5919
- The access token may have expired. Please disconnect and reconnect your Atlassian account in Settings > Connected Apps.`;
7343
+ return ATLASSIAN_RECONNECT_MESSAGE;
5920
7344
  }
5921
7345
  }
5922
7346
  throw error;
@@ -6096,7 +7520,7 @@ async function generateFileDeletePreview(args) {
6096
7520
  if (!existsSync4(args.path)) {
6097
7521
  return `[File does not exist: ${args.path}]`;
6098
7522
  }
6099
- 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));
6100
7524
  return `[File will be deleted]
6101
7525
 
6102
7526
  Path: ${args.path}
@@ -6112,21 +7536,21 @@ var NoOpStorage = class extends BaseStorage {
6112
7536
  async upload(input, destination, options) {
6113
7537
  return `/tmp/${destination}`;
6114
7538
  }
6115
- async download(path10) {
7539
+ async download(path11) {
6116
7540
  throw new Error("Download not supported in CLI");
6117
7541
  }
6118
- async delete(path10) {
7542
+ async delete(path11) {
6119
7543
  }
6120
- async getSignedUrl(path10) {
6121
- return `/tmp/${path10}`;
7544
+ async getSignedUrl(path11) {
7545
+ return `/tmp/${path11}`;
6122
7546
  }
6123
- getPublicUrl(path10) {
6124
- return `/tmp/${path10}`;
7547
+ getPublicUrl(path11) {
7548
+ return `/tmp/${path11}`;
6125
7549
  }
6126
- async getPreview(path10) {
6127
- return `/tmp/${path10}`;
7550
+ async getPreview(path11) {
7551
+ return `/tmp/${path11}`;
6128
7552
  }
6129
- async getMetadata(path10) {
7553
+ async getMetadata(path11) {
6130
7554
  return { size: 0, contentType: "application/octet-stream" };
6131
7555
  }
6132
7556
  };
@@ -6199,7 +7623,7 @@ function wrapToolWithPermission(tool, permissionManager, showPermissionPrompt, a
6199
7623
  };
6200
7624
  }
6201
7625
  function generateCliTools(userId, llm, model, permissionManager, showPermissionPrompt, agentContext, toolApiKeys) {
6202
- const logger = new CliLogger();
7626
+ const logger2 = new CliLogger();
6203
7627
  const storage = new NoOpStorage();
6204
7628
  const user = {
6205
7629
  _id: userId,
@@ -6270,7 +7694,7 @@ function generateCliTools(userId, llm, model, permissionManager, showPermissionP
6270
7694
  const toolsMap = generateTools(
6271
7695
  userId,
6272
7696
  user,
6273
- logger,
7697
+ logger2,
6274
7698
  dbAdapters,
6275
7699
  storage,
6276
7700
  storage,
@@ -6451,10 +7875,265 @@ function getEnvironmentName(configApiConfig) {
6451
7875
  return "Bike4Mind";
6452
7876
  }
6453
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
+
6454
8133
  // ../../b4m-core/packages/mcp/dist/src/client.js
6455
8134
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
6456
8135
  import { Client as Client2 } from "@modelcontextprotocol/sdk/client/index.js";
6457
- import path9 from "path";
8136
+ import path10 from "path";
6458
8137
  import { existsSync as existsSync5, readdirSync } from "fs";
6459
8138
  var MCPClient = class {
6460
8139
  // Note: This class handles MCP server communication with repository filtering
@@ -6480,16 +8159,16 @@ var MCPClient = class {
6480
8159
  const root = process.env.INIT_CWD || process.cwd();
6481
8160
  const candidatePaths = [
6482
8161
  // When running from SST Lambda with node_modules structure (copyFiles)
6483
- 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`),
6484
8163
  // When running from SST Lambda deployed environment (/var/task)
6485
- 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`),
6486
8165
  // When running from SST Lambda (.sst/artifacts/mcpHandler-dev), navigate to monorepo root (3 levels up)
6487
- 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`),
6488
8167
  // When running from packages/client (Next.js app), navigate to monorepo root (2 levels up)
6489
- 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`),
6490
8169
  // Original paths (backward compatibility)
6491
- path9.join(root, `/b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
6492
- 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")
6493
8172
  ];
6494
8173
  const serverScriptPath = candidatePaths.find((p) => existsSync5(p));
6495
8174
  if (!serverScriptPath) {
@@ -6589,18 +8268,18 @@ var McpManager = class {
6589
8268
  async initialize() {
6590
8269
  const enabledServers = this.config.mcpServers.filter((s) => s.enabled);
6591
8270
  if (enabledServers.length === 0) {
6592
- console.log("\u{1F4E1} No MCP servers enabled");
8271
+ logger.info("\u{1F4E1} No MCP servers enabled");
6593
8272
  return;
6594
8273
  }
6595
- console.log(`\u{1F4E1} Initializing ${enabledServers.length} MCP server(s)...`);
8274
+ logger.info(`\u{1F4E1} Initializing ${enabledServers.length} MCP server(s)...`);
6596
8275
  const results = await Promise.allSettled(enabledServers.map((serverConfig) => this.connectServer(serverConfig)));
6597
8276
  const successful = results.filter((r) => r.status === "fulfilled").length;
6598
8277
  const failed = results.filter((r) => r.status === "rejected").length;
6599
8278
  if (successful > 0) {
6600
- console.log(`\u2705 Connected to ${successful} MCP server(s)`);
8279
+ logger.info(`\u2705 Connected to ${successful} MCP server(s)`);
6601
8280
  }
6602
8281
  if (failed > 0) {
6603
- 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)`);
6604
8283
  }
6605
8284
  }
6606
8285
  /**
@@ -6608,7 +8287,7 @@ var McpManager = class {
6608
8287
  */
6609
8288
  async connectServer(serverConfig) {
6610
8289
  try {
6611
- console.log(`\u{1F504} Connecting to ${serverConfig.name}...`);
8290
+ logger.debug(`\u{1F504} Connecting to ${serverConfig.name}...`);
6612
8291
  const envVariables = Object.entries(serverConfig.env).map(([key, value]) => ({
6613
8292
  key,
6614
8293
  value
@@ -6629,9 +8308,9 @@ var McpManager = class {
6629
8308
  client,
6630
8309
  tools: tools2
6631
8310
  });
6632
- console.log(`\u2705 Connected to ${serverConfig.name} (${tools2.length} tools)`);
8311
+ logger.info(`\u2705 Connected to ${serverConfig.name} (${tools2.length} tools)`);
6633
8312
  } catch (error) {
6634
- console.error(`\u274C Failed to connect to ${serverConfig.name}:`, error);
8313
+ logger.error(`\u274C Failed to connect to ${serverConfig.name}:`, error);
6635
8314
  throw error;
6636
8315
  }
6637
8316
  }
@@ -6661,13 +8340,13 @@ var McpManager = class {
6661
8340
  if (this.servers.size === 0) {
6662
8341
  return;
6663
8342
  }
6664
- 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)...`);
6665
8344
  const disconnectPromises = Array.from(this.servers.values()).map(async (server) => {
6666
8345
  try {
6667
8346
  await server.client.disconnect();
6668
- console.log(`\u2705 Disconnected from ${server.name}`);
8347
+ logger.debug(`\u2705 Disconnected from ${server.name}`);
6669
8348
  } catch (error) {
6670
- console.warn(`\u26A0\uFE0F Error disconnecting from ${server.name}:`, error);
8349
+ logger.error(`\u26A0\uFE0F Error disconnecting from ${server.name}:`, error);
6671
8350
  }
6672
8351
  });
6673
8352
  await Promise.allSettled(disconnectPromises);
@@ -6689,12 +8368,13 @@ var McpManager = class {
6689
8368
 
6690
8369
  // src/llm/ServerLlmBackend.ts
6691
8370
  import { createParser } from "eventsource-parser";
8371
+ import { isAxiosError } from "axios";
6692
8372
  function stripThinkingBlocks(text) {
6693
8373
  return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
6694
8374
  }
6695
8375
  var ServerLlmBackend = class {
6696
8376
  constructor(options) {
6697
- this.completionsEndpoint = "/api/cli/completions";
8377
+ this.completionsEndpoint = "/api/ai/v1/completions";
6698
8378
  this.apiClient = options.apiClient;
6699
8379
  this.currentModel = options.model;
6700
8380
  }
@@ -6703,12 +8383,12 @@ var ServerLlmBackend = class {
6703
8383
  * Parses SSE stream and invokes callback for each event
6704
8384
  */
6705
8385
  async complete(model, messages, options, callback) {
6706
- console.log("[ServerLlmBackend] Starting complete() with model:", model);
8386
+ logger.debug(`[ServerLlmBackend] Starting complete() with model: ${model}`);
6707
8387
  return new Promise(async (resolve, reject) => {
6708
8388
  try {
6709
- console.log("[ServerLlmBackend] Making streaming request...");
8389
+ logger.debug("[ServerLlmBackend] Making streaming request...");
6710
8390
  const response = await this.makeStreamingRequest(model, messages, options);
6711
- console.log("[ServerLlmBackend] Got response, setting up SSE parser");
8391
+ logger.debug("[ServerLlmBackend] Got response, setting up SSE parser");
6712
8392
  let eventCount = 0;
6713
8393
  let accumulatedText = "";
6714
8394
  let lastUsageInfo = {};
@@ -6717,28 +8397,28 @@ var ServerLlmBackend = class {
6717
8397
  const parser = createParser({
6718
8398
  onEvent: (event) => {
6719
8399
  eventCount++;
6720
- 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)}`);
6721
8401
  const data = event.data;
6722
8402
  if (data === "[DONE]") {
6723
8403
  receivedDone = true;
6724
- console.log("[ServerLlmBackend] Received [DONE], accumulated text length:", accumulatedText.length);
8404
+ logger.debug(`[ServerLlmBackend] Received [DONE], accumulated text length: ${accumulatedText.length}`);
6725
8405
  const cleanedText = stripThinkingBlocks(accumulatedText);
6726
8406
  if (toolsUsed.length > 0) {
6727
8407
  const info = {
6728
8408
  toolsUsed,
6729
8409
  ...lastUsageInfo
6730
8410
  };
6731
- console.log("[ServerLlmBackend] Calling callback with tools, will wait for completion");
8411
+ logger.debug("[ServerLlmBackend] Calling callback with tools, will wait for completion");
6732
8412
  callback([cleanedText], info).catch((err) => {
6733
- console.error("[ServerLlmBackend] Callback error:", err);
8413
+ logger.error("[ServerLlmBackend] Callback error:", err);
6734
8414
  reject(err);
6735
8415
  }).then(() => {
6736
- console.log("[ServerLlmBackend] Callback completed, resolving");
8416
+ logger.debug("[ServerLlmBackend] Callback completed, resolving");
6737
8417
  resolve();
6738
8418
  });
6739
8419
  } else if (cleanedText) {
6740
8420
  callback([cleanedText], lastUsageInfo).catch((err) => {
6741
- console.error("[ServerLlmBackend] Callback error:", err);
8421
+ logger.error("[ServerLlmBackend] Callback error:", err);
6742
8422
  reject(err);
6743
8423
  }).then(() => resolve());
6744
8424
  } else {
@@ -6748,9 +8428,9 @@ var ServerLlmBackend = class {
6748
8428
  }
6749
8429
  try {
6750
8430
  const parsed = JSON.parse(data);
6751
- console.log("[ServerLlmBackend] Parsed event type:", parsed.type);
8431
+ logger.debug(`[ServerLlmBackend] Parsed event type: ${parsed.type}`);
6752
8432
  if (parsed.type === "error") {
6753
- console.error("[ServerLlmBackend] Error event:", parsed.message);
8433
+ logger.error("[ServerLlmBackend] Error event:", parsed);
6754
8434
  reject(new Error(parsed.message || "Server error"));
6755
8435
  return;
6756
8436
  }
@@ -6763,9 +8443,20 @@ var ServerLlmBackend = class {
6763
8443
  outputTokens: parsed.usage.outputTokens
6764
8444
  };
6765
8445
  }
6766
- console.log("[ServerLlmBackend] Accumulated text length:", accumulatedText.length);
8446
+ logger.debug(`[ServerLlmBackend] Accumulated text length: ${accumulatedText.length}`);
6767
8447
  } else if (parsed.type === "tool_use") {
6768
- 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
+ }
6769
8460
  const textChunk = parsed.text || "";
6770
8461
  if (textChunk) {
6771
8462
  accumulatedText += textChunk;
@@ -6781,7 +8472,7 @@ var ServerLlmBackend = class {
6781
8472
  }
6782
8473
  }
6783
8474
  } catch (parseError) {
6784
- console.error("[ServerLlmBackend] Failed to parse SSE event:", parseError);
8475
+ logger.error("[ServerLlmBackend] Failed to parse SSE event:", parseError);
6785
8476
  }
6786
8477
  }
6787
8478
  });
@@ -6790,19 +8481,75 @@ var ServerLlmBackend = class {
6790
8481
  });
6791
8482
  response.data.on("end", () => {
6792
8483
  if (!receivedDone) {
6793
- console.log("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
8484
+ logger.debug("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
6794
8485
  resolve();
6795
8486
  } else {
6796
- console.log("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
8487
+ logger.debug("[ServerLlmBackend] Stream ended, [DONE] handler will resolve");
6797
8488
  }
6798
8489
  });
6799
8490
  response.data.on("error", (error) => {
6800
8491
  reject(error);
6801
8492
  });
6802
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
+ }
6803
8550
  if (error instanceof Error) {
6804
- if (error.message.includes("Authentication expired")) {
6805
- reject(new Error("Authentication expired. Please run `b4m login` again."));
8551
+ if (error.message.includes("Authentication expired") || error.message.includes("Authentication failed")) {
8552
+ reject(error);
6806
8553
  } else if (error.message.includes("ECONNREFUSED")) {
6807
8554
  reject(new Error("Cannot connect to Bike4Mind server. Please check your internet connection."));
6808
8555
  } else if (error.message.includes("Rate limit exceeded")) {
@@ -6845,28 +8592,28 @@ var ServerLlmBackend = class {
6845
8592
  * Uses axios with responseType 'stream' for SSE
6846
8593
  */
6847
8594
  async makeStreamingRequest(model, messages, options) {
6848
- const axiosInstance = this.apiClient.client;
6849
- if (!axiosInstance) {
6850
- throw new Error("ApiClient axios instance not accessible");
6851
- }
6852
- const response = await axiosInstance.post(
6853
- this.completionsEndpoint,
6854
- {
6855
- model,
6856
- messages,
6857
- options: {
6858
- temperature: options.temperature,
6859
- maxTokens: options.maxTokens,
6860
- stream: true,
6861
- // Always use streaming for SSE
6862
- tools: options.tools || []
6863
- }
6864
- },
6865
- {
6866
- responseType: "stream"
6867
- // 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 || []
6868
8605
  }
6869
- );
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}`);
6870
8617
  return response;
6871
8618
  }
6872
8619
  };
@@ -6897,15 +8644,21 @@ var ApiClient = class {
6897
8644
  (response) => response,
6898
8645
  async (error) => {
6899
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
+ }
6900
8652
  if (error.response?.status === 401 && !originalRequest._retry) {
6901
8653
  originalRequest._retry = true;
8654
+ logger.debug("AUTH: Attempting token refresh");
6902
8655
  try {
6903
8656
  const tokens = await this.configStore.getAuthTokens();
6904
8657
  if (!tokens) {
6905
8658
  throw new Error("Not authenticated");
6906
8659
  }
6907
- console.log("\u{1F504} Access token expired, refreshing...");
6908
8660
  const newTokens = await this.oauthClient.refreshToken(tokens.refreshToken);
8661
+ logger.debug("AUTH: Token refresh successful");
6909
8662
  const expiresAt = new Date(Date.now() + newTokens.expires_in * 1e3).toISOString();
6910
8663
  await this.configStore.setAuthTokens({
6911
8664
  accessToken: newTokens.access_token,
@@ -6914,8 +8667,8 @@ var ApiClient = class {
6914
8667
  userId: tokens.userId
6915
8668
  // Preserve userId
6916
8669
  });
6917
- console.log("\u2705 Token refreshed successfully");
6918
8670
  originalRequest.headers.Authorization = `Bearer ${newTokens.access_token}`;
8671
+ logger.debug("AUTH: Retrying request with new token");
6919
8672
  return this.client(originalRequest);
6920
8673
  } catch (refreshError) {
6921
8674
  console.error(
@@ -6923,10 +8676,16 @@ var ApiClient = class {
6923
8676
  refreshError instanceof Error ? refreshError.message : "Unknown error"
6924
8677
  );
6925
8678
  console.log("Please run `b4m login` again to re-authenticate.");
8679
+ logger.error("AUTH: Token refresh failed", refreshError);
6926
8680
  await this.configStore.clearAuthTokens();
6927
8681
  throw new Error("Authentication expired. Please run `b4m login` again.");
6928
8682
  }
6929
8683
  }
8684
+ if (error.response?.status === 401 && originalRequest._retry) {
8685
+ logger.debug("AUTH: Token refresh retry failed - clearing tokens");
8686
+ await this.configStore.clearAuthTokens();
8687
+ throw new Error("Authentication failed. Please run /login to authenticate.");
8688
+ }
6930
8689
  return Promise.reject(error);
6931
8690
  }
6932
8691
  );
@@ -6959,6 +8718,12 @@ var ApiClient = class {
6959
8718
  const response = await this.client.delete(url, config);
6960
8719
  return response.data;
6961
8720
  }
8721
+ /**
8722
+ * Get the underlying axios instance for advanced use cases (e.g., streaming)
8723
+ */
8724
+ getAxiosInstance() {
8725
+ return this.client;
8726
+ }
6962
8727
  /**
6963
8728
  * Check if user is authenticated
6964
8729
  */
@@ -6981,6 +8746,123 @@ var ApiClient = class {
6981
8746
  }
6982
8747
  };
6983
8748
 
8749
+ // package.json
8750
+ var package_default = {
8751
+ name: "@bike4mind/cli",
8752
+ version: "0.1.5-feat-automate-cli-publishing.16903+b709a6e8f",
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.16903+b709a6e8f",
8850
+ "@bike4mind/mcp": "1.19.3-feat-automate-cli-publishing.16903+b709a6e8f",
8851
+ "@bike4mind/services": "2.28.1-feat-automate-cli-publishing.16903+b709a6e8f",
8852
+ "@bike4mind/utils": "2.0.1-feat-automate-cli-publishing.16903+b709a6e8f",
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: "b709a6e8ff6c4b9c1b3ce3ab0d3c548c7feb7cb4"
8864
+ };
8865
+
6984
8866
  // src/index.tsx
6985
8867
  process.removeAllListeners("warning");
6986
8868
  process.on("warning", (warning) => {
@@ -7014,14 +8896,14 @@ function CliApp() {
7014
8896
  if (state.session) {
7015
8897
  cleanupTasks.push(
7016
8898
  state.sessionStore.save(state.session).catch((err) => {
7017
- console.log("[CLEANUP] Session save error:", err.message);
8899
+ logger.debug(`[CLEANUP] Session save error: ${err.message}`);
7018
8900
  })
7019
8901
  );
7020
8902
  }
7021
8903
  if (state.mcpManager) {
7022
8904
  cleanupTasks.push(
7023
8905
  state.mcpManager.disconnect().catch((err) => {
7024
- console.log("[CLEANUP] MCP disconnect error:", err.message);
8906
+ logger.debug(`[CLEANUP] MCP disconnect error: ${err.message}`);
7025
8907
  })
7026
8908
  );
7027
8909
  }
@@ -7041,12 +8923,12 @@ function CliApp() {
7041
8923
  if (key.ctrl && input === "c") {
7042
8924
  const now = Date.now();
7043
8925
  if (exitTimestamp && now - exitTimestamp < EXIT_TIMEOUT_MS) {
7044
- console.log("[EXIT] Second Ctrl+C - cleaning up and exiting...");
8926
+ logger.debug("[EXIT Second Ctrl+C - cleaning up and exiting...");
7045
8927
  performCleanup().then(() => {
7046
8928
  exit();
7047
8929
  });
7048
8930
  } else {
7049
- console.log("[EXIT] First Ctrl+C - press again to exit");
8931
+ logger.debug("[EXIT First Ctrl+C - press again to exit");
7050
8932
  exitTimestamp = now;
7051
8933
  setExitRequested(true);
7052
8934
  setTimeout(() => {
@@ -7142,6 +9024,14 @@ function CliApp() {
7142
9024
  toolCallCount: 0
7143
9025
  }
7144
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
+ });
7145
9035
  const silentLogger = {
7146
9036
  log: () => {
7147
9037
  },
@@ -7327,6 +9217,13 @@ Remember: Use context from previous messages to understand follow-up questions.`
7327
9217
  setStoreSession(updatedSession);
7328
9218
  await state.sessionStore.save(updatedSession);
7329
9219
  } catch (error) {
9220
+ if (error instanceof Error) {
9221
+ if (error.message.includes("Authentication failed") || error.message.includes("Authentication expired")) {
9222
+ console.log("\n\u274C Authentication failed");
9223
+ console.log("\u{1F4A1} Run /login to authenticate with your API environment.\n");
9224
+ return;
9225
+ }
9226
+ }
7330
9227
  console.error("Error processing message:", error);
7331
9228
  }
7332
9229
  };
@@ -7357,7 +9254,7 @@ Tool Permissions:
7357
9254
  break;
7358
9255
  case "exit":
7359
9256
  case "quit":
7360
- console.log("[EXIT] /exit command - cleaning up and exiting...");
9257
+ logger.debug("[EXIT /exit command - cleaning up and exiting...");
7361
9258
  await performCleanup();
7362
9259
  exit();
7363
9260
  break;
@@ -7418,14 +9315,20 @@ Tool Permissions:
7418
9315
  return;
7419
9316
  }
7420
9317
  await state.configStore.setCustomApiUrl(url);
9318
+ await state.configStore.clearAuthTokens();
7421
9319
  console.log(`
7422
- \u2705 Connected to self-hosted instance: ${url}`);
9320
+ \u2705 API URL updated: ${url}`);
9321
+ console.log("\u{1F513} Authentication cleared");
9322
+ console.log(`\u{1F4A1} Run /login to authenticate with ${url}`);
7423
9323
  console.log("Please restart the CLI for changes to take effect.\n");
7424
9324
  break;
7425
9325
  }
7426
9326
  case "reset-api":
7427
9327
  await state.configStore.setCustomApiUrl(null);
7428
- console.log("\n\u2705 Reset to Bike4Mind main service");
9328
+ await state.configStore.clearAuthTokens();
9329
+ console.log("\n\u2705 API URL reset to Bike4Mind main service");
9330
+ console.log("\u{1F513} Authentication cleared");
9331
+ console.log("\u{1F4A1} Run /login to authenticate");
7429
9332
  console.log("Please restart the CLI for changes to take effect.\n");
7430
9333
  break;
7431
9334
  case "api-info": {
@@ -7564,7 +9467,8 @@ var BANNER = `
7564
9467
  \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D
7565
9468
  `;
7566
9469
  console.log("\x1B[36m\x1B[1m%s\x1B[0m", BANNER);
7567
- 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
+ `);
7568
9472
  var isDevMode = import.meta.url.includes("/src/") || process.env.NODE_ENV === "development";
7569
9473
  if (isDevMode) {
7570
9474
  console.log("\u{1F527} Running in development mode (using TypeScript source)\n");