@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/README.md +58 -1
- package/bin/bike4mind-cli.mjs +21 -0
- package/dist/{artifactExtractor-QN34RFDM.js → artifactExtractor-Y5UYA5XK.js} +1 -1
- package/dist/{chunk-B3YFSFVU.js → chunk-G2XBD676.js} +262 -204
- package/dist/{chunk-MKO2KCCS.js → chunk-PG4BD2XZ.js} +51 -1
- package/dist/{chunk-TPSQ5MIR.js → chunk-PMPXEVZQ.js} +2 -2
- package/dist/{chunk-KZGZGNHL.js → chunk-PYV7BIVP.js} +2 -2
- package/dist/{chunk-XANFVWI6.js → chunk-ZJBAQHHQ.js} +2 -2
- package/dist/{create-DGRMWHIN.js → create-2BDFUWVK.js} +3 -3
- package/dist/index.js +2163 -259
- package/dist/{llmMarkdownGenerator-JBDLN44A.js → llmMarkdownGenerator-SPSSOLJI.js} +1 -1
- package/dist/{markdownGenerator-MM5N3H5I.js → markdownGenerator-ZBS2ID6T.js} +1 -1
- package/dist/{mementoService-23ROW7I4.js → mementoService-RHDGBSB3.js} +3 -3
- package/dist/{src-K5MFQICX.js → src-3SNCFJQX.js} +2 -2
- package/dist/{src-OVEHYUVN.js → src-FPGIEUD3.js} +1 -1
- package/dist/{subtractCredits-YAWCGKMX.js → subtractCredits-D7M2BCM2.js} +3 -3
- package/package.json +11 -7
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
getEffectiveApiKey,
|
|
5
5
|
getOpenWeatherKey,
|
|
6
6
|
getSerperKey
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
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, "
|
|
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
|
|
3634
|
-
return
|
|
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
|
|
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 "
|
|
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
|
-
|
|
4273
|
+
}
|
|
4274
|
+
case "human": {
|
|
4141
4275
|
const humanFormat = new Intl.DateTimeFormat("en-US", formatOptions);
|
|
4142
4276
|
return humanFormat.format(now);
|
|
4143
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
4672
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5356
|
+
logger2.info("Blog draft complete", { responseLength: llmResponse.length });
|
|
5204
5357
|
const result = parseTransformationResult(llmResponse);
|
|
5205
|
-
|
|
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
|
-
|
|
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/
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
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
|
|
5270
|
-
if (
|
|
5271
|
-
|
|
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
|
-
|
|
5274
|
-
|
|
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
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
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
|
-
|
|
5281
|
-
|
|
5282
|
-
const
|
|
5283
|
-
|
|
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
|
|
5291
|
-
|
|
5292
|
-
}
|
|
5293
|
-
|
|
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 {
|
|
5298
|
-
const
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
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
|
|
5305
|
-
name: "
|
|
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.
|
|
5529
|
+
context.logger.log("\u{1F4C5} WikipediaOnThisDay: Starting execution", params);
|
|
5310
5530
|
try {
|
|
5311
|
-
const
|
|
5312
|
-
|
|
5313
|
-
|
|
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
|
|
5535
|
+
context.logger.error("\u274C WikipediaOnThisDay: Execution failed", error);
|
|
5321
5536
|
throw error;
|
|
5322
5537
|
}
|
|
5323
5538
|
},
|
|
5324
5539
|
toolSchema: {
|
|
5325
|
-
name: "
|
|
5326
|
-
description:
|
|
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
|
-
|
|
5545
|
+
date: {
|
|
5331
5546
|
type: "string",
|
|
5332
|
-
description:
|
|
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
|
-
|
|
5549
|
+
type: {
|
|
5335
5550
|
type: "string",
|
|
5336
|
-
description: "
|
|
5337
|
-
enum: ["
|
|
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
|
-
|
|
5554
|
+
limit: {
|
|
5340
5555
|
type: "number",
|
|
5341
|
-
description: "Maximum number of
|
|
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
|
-
|
|
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/
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
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
|
|
5363
|
-
|
|
5364
|
-
|
|
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
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
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
|
-
|
|
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
|
|
5387
|
-
context.logger.
|
|
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
|
|
5738
|
+
context.logger.error("\u274C MoonPhase: Execution failed", error);
|
|
5391
5739
|
throw error;
|
|
5392
5740
|
}
|
|
5393
5741
|
},
|
|
5394
5742
|
toolSchema: {
|
|
5395
|
-
name: "
|
|
5396
|
-
description: "
|
|
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
|
-
|
|
5748
|
+
date: {
|
|
5401
5749
|
type: "string",
|
|
5402
|
-
description: "
|
|
5750
|
+
description: "The date to check moon phase for. Use YYYY-MM-DD format. Defaults to today if not specified."
|
|
5403
5751
|
},
|
|
5404
|
-
|
|
5752
|
+
timezone: {
|
|
5405
5753
|
type: "string",
|
|
5406
|
-
description: "
|
|
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
|
-
|
|
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/
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
const
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
|
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((
|
|
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(
|
|
7539
|
+
async download(path11) {
|
|
6116
7540
|
throw new Error("Download not supported in CLI");
|
|
6117
7541
|
}
|
|
6118
|
-
async delete(
|
|
7542
|
+
async delete(path11) {
|
|
6119
7543
|
}
|
|
6120
|
-
async getSignedUrl(
|
|
6121
|
-
return `/tmp/${
|
|
7544
|
+
async getSignedUrl(path11) {
|
|
7545
|
+
return `/tmp/${path11}`;
|
|
6122
7546
|
}
|
|
6123
|
-
getPublicUrl(
|
|
6124
|
-
return `/tmp/${
|
|
7547
|
+
getPublicUrl(path11) {
|
|
7548
|
+
return `/tmp/${path11}`;
|
|
6125
7549
|
}
|
|
6126
|
-
async getPreview(
|
|
6127
|
-
return `/tmp/${
|
|
7550
|
+
async getPreview(path11) {
|
|
7551
|
+
return `/tmp/${path11}`;
|
|
6128
7552
|
}
|
|
6129
|
-
async getMetadata(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8168
|
+
path10.join(root, `../../b4m-core/packages/mcp/dist/src/${this.serverName}/index.js`),
|
|
6490
8169
|
// Original paths (backward compatibility)
|
|
6491
|
-
|
|
6492
|
-
|
|
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
|
-
|
|
8271
|
+
logger.info("\u{1F4E1} No MCP servers enabled");
|
|
6593
8272
|
return;
|
|
6594
8273
|
}
|
|
6595
|
-
|
|
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
|
-
|
|
8279
|
+
logger.info(`\u2705 Connected to ${successful} MCP server(s)`);
|
|
6601
8280
|
}
|
|
6602
8281
|
if (failed > 0) {
|
|
6603
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8311
|
+
logger.info(`\u2705 Connected to ${serverConfig.name} (${tools2.length} tools)`);
|
|
6633
8312
|
} catch (error) {
|
|
6634
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8347
|
+
logger.debug(`\u2705 Disconnected from ${server.name}`);
|
|
6669
8348
|
} catch (error) {
|
|
6670
|
-
|
|
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/
|
|
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
|
-
|
|
8386
|
+
logger.debug(`[ServerLlmBackend] Starting complete() with model: ${model}`);
|
|
6707
8387
|
return new Promise(async (resolve, reject) => {
|
|
6708
8388
|
try {
|
|
6709
|
-
|
|
8389
|
+
logger.debug("[ServerLlmBackend] Making streaming request...");
|
|
6710
8390
|
const response = await this.makeStreamingRequest(model, messages, options);
|
|
6711
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8411
|
+
logger.debug("[ServerLlmBackend] Calling callback with tools, will wait for completion");
|
|
6732
8412
|
callback([cleanedText], info).catch((err) => {
|
|
6733
|
-
|
|
8413
|
+
logger.error("[ServerLlmBackend] Callback error:", err);
|
|
6734
8414
|
reject(err);
|
|
6735
8415
|
}).then(() => {
|
|
6736
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8431
|
+
logger.debug(`[ServerLlmBackend] Parsed event type: ${parsed.type}`);
|
|
6752
8432
|
if (parsed.type === "error") {
|
|
6753
|
-
|
|
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
|
-
|
|
8446
|
+
logger.debug(`[ServerLlmBackend] Accumulated text length: ${accumulatedText.length}`);
|
|
6767
8447
|
} else if (parsed.type === "tool_use") {
|
|
6768
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8484
|
+
logger.debug("[ServerLlmBackend] Stream ended without [DONE], resolving anyway");
|
|
6794
8485
|
resolve();
|
|
6795
8486
|
} else {
|
|
6796
|
-
|
|
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(
|
|
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.
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8926
|
+
logger.debug("[EXIT Second Ctrl+C - cleaning up and exiting...");
|
|
7045
8927
|
performCleanup().then(() => {
|
|
7046
8928
|
exit();
|
|
7047
8929
|
});
|
|
7048
8930
|
} else {
|
|
7049
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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");
|