ed-mcp 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -1
- package/dist/index.js +638 -60
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
package/dist/index.js
CHANGED
|
@@ -8,7 +8,11 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
|
8
8
|
var __getProtoOf = Object.getPrototypeOf;
|
|
9
9
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
10
|
var __commonJS = (cb, mod) => function __require() {
|
|
11
|
-
|
|
11
|
+
try {
|
|
12
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
13
|
+
} catch (e) {
|
|
14
|
+
throw mod = 0, e;
|
|
15
|
+
}
|
|
12
16
|
};
|
|
13
17
|
var __export = (target, all) => {
|
|
14
18
|
for (var name in all)
|
|
@@ -3335,6 +3339,37 @@ var require_utils = __commonJS({
|
|
|
3335
3339
|
}
|
|
3336
3340
|
return output3.join("");
|
|
3337
3341
|
}
|
|
3342
|
+
function removeDotSegmentsRelative(path15) {
|
|
3343
|
+
if (path15 === "" || path15[0] === "/") {
|
|
3344
|
+
return removeDotSegments(path15);
|
|
3345
|
+
}
|
|
3346
|
+
const segments = path15.split("/");
|
|
3347
|
+
const last = segments.length - 1;
|
|
3348
|
+
const output3 = [];
|
|
3349
|
+
let trailingDirectory = false;
|
|
3350
|
+
for (let i = 0; i <= last; i++) {
|
|
3351
|
+
const segment = segments[i];
|
|
3352
|
+
if (segment === "." || segment === "..") {
|
|
3353
|
+
if (segment === "..") {
|
|
3354
|
+
if (output3.length !== 0 && output3[output3.length - 1] !== "..") {
|
|
3355
|
+
output3.pop();
|
|
3356
|
+
} else {
|
|
3357
|
+
output3.push("..");
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
trailingDirectory = i === last;
|
|
3361
|
+
} else {
|
|
3362
|
+
output3.push(segment);
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
if (trailingDirectory && output3[output3.length - 1] !== "..") {
|
|
3366
|
+
output3.push("");
|
|
3367
|
+
}
|
|
3368
|
+
if (output3.length === 0 || output3[0] === "" || output3[0].includes(":")) {
|
|
3369
|
+
output3.unshift(".");
|
|
3370
|
+
}
|
|
3371
|
+
return output3.join("/");
|
|
3372
|
+
}
|
|
3338
3373
|
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
3339
3374
|
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
3340
3375
|
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
@@ -3595,6 +3630,7 @@ var require_utils = __commonJS({
|
|
|
3595
3630
|
encodeFragment,
|
|
3596
3631
|
escapePreservingEscapes,
|
|
3597
3632
|
removeDotSegments,
|
|
3633
|
+
removeDotSegmentsRelative,
|
|
3598
3634
|
isIPv4,
|
|
3599
3635
|
isUUID,
|
|
3600
3636
|
normalizeIPv6,
|
|
@@ -3818,16 +3854,18 @@ var require_schemes = __commonJS({
|
|
|
3818
3854
|
var require_fast_uri = __commonJS({
|
|
3819
3855
|
"node_modules/fast-uri/index.js"(exports, module) {
|
|
3820
3856
|
"use strict";
|
|
3821
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3857
|
+
var { normalizeIPv6, removeDotSegments, removeDotSegmentsRelative, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
3822
3858
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
3823
3859
|
var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
3824
3860
|
var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3861
|
+
var MALFORMED_HOST_ERROR = "URI host is malformed.";
|
|
3862
|
+
var FORBIDDEN_DOMAIN_HOST_CHARACTER = /[\0-\x20"#/:<>?@[\\\]^`{|}\x7F]/u;
|
|
3863
|
+
function validateScheme(scheme) {
|
|
3864
|
+
const stringScheme = String(scheme);
|
|
3865
|
+
if (!VALID_SCHEME.test(stringScheme)) {
|
|
3828
3866
|
throw new TypeError(MALFORMED_SCHEME_ERROR);
|
|
3829
3867
|
}
|
|
3830
|
-
return
|
|
3868
|
+
return stringScheme;
|
|
3831
3869
|
}
|
|
3832
3870
|
function normalize(uri, options) {
|
|
3833
3871
|
if (typeof uri === "string") {
|
|
@@ -3864,6 +3902,9 @@ var require_fast_uri = __commonJS({
|
|
|
3864
3902
|
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
|
|
3865
3903
|
const resolvedHost = resolved.host;
|
|
3866
3904
|
const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
3905
|
+
if (hasForbiddenDomainHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP)) {
|
|
3906
|
+
throw new Error(MALFORMED_HOST_ERROR);
|
|
3907
|
+
}
|
|
3867
3908
|
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
3868
3909
|
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
|
|
3869
3910
|
if (resolved.error && !encodedASCIIHost) {
|
|
@@ -3905,14 +3946,15 @@ var require_fast_uri = __commonJS({
|
|
|
3905
3946
|
if (relative.path[0] === "/") {
|
|
3906
3947
|
target.path = removeDotSegments(relative.path);
|
|
3907
3948
|
} else {
|
|
3908
|
-
|
|
3949
|
+
const baseHasAuthority = base2.userinfo !== void 0 || base2.host !== void 0 || base2.port !== void 0;
|
|
3950
|
+
if (baseHasAuthority && !base2.path) {
|
|
3909
3951
|
target.path = "/" + relative.path;
|
|
3910
3952
|
} else if (!base2.path) {
|
|
3911
3953
|
target.path = relative.path;
|
|
3912
3954
|
} else {
|
|
3913
3955
|
target.path = base2.path.slice(0, base2.path.lastIndexOf("/") + 1) + relative.path;
|
|
3914
3956
|
}
|
|
3915
|
-
target.path = removeDotSegments(target.path);
|
|
3957
|
+
target.path = base2.scheme === void 0 && !baseHasAuthority && target.path[0] !== "/" ? removeDotSegmentsRelative(target.path) : removeDotSegments(target.path);
|
|
3916
3958
|
}
|
|
3917
3959
|
target.query = relative.query;
|
|
3918
3960
|
}
|
|
@@ -3950,12 +3992,13 @@ var require_fast_uri = __commonJS({
|
|
|
3950
3992
|
const options = Object.assign({}, opts);
|
|
3951
3993
|
const uriTokens = [];
|
|
3952
3994
|
if (component.scheme) {
|
|
3953
|
-
component.scheme =
|
|
3995
|
+
component.scheme = validateScheme(component.scheme);
|
|
3954
3996
|
}
|
|
3955
3997
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
3956
3998
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
3957
3999
|
const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
|
|
3958
|
-
const
|
|
4000
|
+
const schemelessReference = (component.scheme === void 0 || options.reference === "suffix") && !hasAuthority;
|
|
4001
|
+
const pathNoScheme = !options.skipEscape && schemelessReference;
|
|
3959
4002
|
if (component.path !== void 0) {
|
|
3960
4003
|
if (!options.skipEscape) {
|
|
3961
4004
|
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
@@ -3964,7 +4007,7 @@ var require_fast_uri = __commonJS({
|
|
|
3964
4007
|
}
|
|
3965
4008
|
}
|
|
3966
4009
|
if (options.reference !== "suffix" && component.scheme) {
|
|
3967
|
-
component.scheme =
|
|
4010
|
+
component.scheme = validateScheme(component.scheme);
|
|
3968
4011
|
uriTokens.push(component.scheme, ":");
|
|
3969
4012
|
}
|
|
3970
4013
|
const authority = recomposeAuthority(component);
|
|
@@ -3980,9 +4023,9 @@ var require_fast_uri = __commonJS({
|
|
|
3980
4023
|
if (component.path !== void 0) {
|
|
3981
4024
|
let s = component.path;
|
|
3982
4025
|
if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
|
|
3983
|
-
s = removeDotSegments(s);
|
|
4026
|
+
s = schemelessReference && s[0] !== "/" ? removeDotSegmentsRelative(s) : removeDotSegments(s);
|
|
3984
4027
|
}
|
|
3985
|
-
if (
|
|
4028
|
+
if (schemelessReference) {
|
|
3986
4029
|
s = serializePathEncoding(s, true);
|
|
3987
4030
|
}
|
|
3988
4031
|
if (authority === void 0 && s[0] === "/" && s[1] === "/") {
|
|
@@ -4029,16 +4072,34 @@ var require_fast_uri = __commonJS({
|
|
|
4029
4072
|
return hasMalformedPercentEncoding(matches2[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches2[6]) || hasMalformedPercentEncoding(matches2[7]) || hasMalformedPercentEncoding(matches2[8]);
|
|
4030
4073
|
}
|
|
4031
4074
|
function canonicalizeHost(parsed, options, schemeHandler, isIP) {
|
|
4075
|
+
if (hasForbiddenDomainHost(parsed, options, schemeHandler, isIP)) {
|
|
4076
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
4077
|
+
return true;
|
|
4078
|
+
}
|
|
4032
4079
|
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
4080
|
+
let url2;
|
|
4033
4081
|
try {
|
|
4034
|
-
|
|
4082
|
+
url2 = new URL("http://" + parsed.host);
|
|
4035
4083
|
} catch (e) {
|
|
4036
4084
|
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
4037
4085
|
return true;
|
|
4038
4086
|
}
|
|
4087
|
+
if (!isHostOnlyURL(url2)) {
|
|
4088
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
4089
|
+
return true;
|
|
4090
|
+
}
|
|
4091
|
+
parsed.host = url2.hostname;
|
|
4039
4092
|
}
|
|
4040
4093
|
return false;
|
|
4041
4094
|
}
|
|
4095
|
+
function hasForbiddenDomainHost(parsed, options, schemeHandler, isIP) {
|
|
4096
|
+
return Boolean(
|
|
4097
|
+
parsed.host && isIP === false && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && FORBIDDEN_DOMAIN_HOST_CHARACTER.test(parsed.host)
|
|
4098
|
+
);
|
|
4099
|
+
}
|
|
4100
|
+
function isHostOnlyURL(url2) {
|
|
4101
|
+
return url2.username === "" && url2.password === "" && url2.port === "" && url2.pathname === "/" && url2.search === "" && url2.hash === "" && url2.hostname !== "";
|
|
4102
|
+
}
|
|
4042
4103
|
function parseWithStatus(uri, opts) {
|
|
4043
4104
|
const options = Object.assign({}, opts);
|
|
4044
4105
|
const parsed = {
|
|
@@ -4093,9 +4154,8 @@ var require_fast_uri = __commonJS({
|
|
|
4093
4154
|
parsed.query = matches2[7];
|
|
4094
4155
|
parsed.fragment = matches2[8];
|
|
4095
4156
|
if (parsed.scheme !== void 0) {
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
parsed.scheme = decodedScheme.toLowerCase();
|
|
4157
|
+
if (VALID_SCHEME.test(parsed.scheme)) {
|
|
4158
|
+
parsed.scheme = parsed.scheme.toLowerCase();
|
|
4099
4159
|
} else {
|
|
4100
4160
|
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
4101
4161
|
malformedScheme = true;
|
|
@@ -4123,7 +4183,7 @@ var require_fast_uri = __commonJS({
|
|
|
4123
4183
|
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
4124
4184
|
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
4125
4185
|
if (malformedIPLiteral) {
|
|
4126
|
-
parsed.error = parsed.error ||
|
|
4186
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
4127
4187
|
malformedAuthorityOrPort = true;
|
|
4128
4188
|
}
|
|
4129
4189
|
} else {
|
|
@@ -4151,7 +4211,7 @@ var require_fast_uri = __commonJS({
|
|
|
4151
4211
|
if (!isIP) {
|
|
4152
4212
|
host = normalizePercentEncoding(host.toLowerCase());
|
|
4153
4213
|
}
|
|
4154
|
-
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
4214
|
+
parsed.host = isIP || options.scheme !== "null" ? reescapeHostDelimiters(host, isIP) : host;
|
|
4155
4215
|
}
|
|
4156
4216
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
4157
4217
|
if (parsed.path) {
|
|
@@ -9803,7 +9863,7 @@ ${content.join("\n")}
|
|
|
9803
9863
|
var version = {
|
|
9804
9864
|
major: 4,
|
|
9805
9865
|
minor: 6,
|
|
9806
|
-
patch:
|
|
9866
|
+
patch: 7
|
|
9807
9867
|
};
|
|
9808
9868
|
|
|
9809
9869
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -28692,7 +28752,7 @@ async function canonical(p) {
|
|
|
28692
28752
|
}
|
|
28693
28753
|
|
|
28694
28754
|
// src/version.ts
|
|
28695
|
-
var VERSION = "0.
|
|
28755
|
+
var VERSION = "0.5.0";
|
|
28696
28756
|
|
|
28697
28757
|
// src/config.ts
|
|
28698
28758
|
var ConfigError = class extends Error {
|
|
@@ -32080,25 +32140,139 @@ function parseSpanshJson(text) {
|
|
|
32080
32140
|
return JSON.parse(text.replace(BIG_ID, '$1"$2"'));
|
|
32081
32141
|
}
|
|
32082
32142
|
|
|
32143
|
+
// src/systems/sweep.ts
|
|
32144
|
+
function stationPad(s) {
|
|
32145
|
+
if (s.has_large_pad || (s.large_pads ?? 0) > 0) return "L";
|
|
32146
|
+
if ((s.medium_pads ?? 0) > 0) return "M";
|
|
32147
|
+
if ((s.small_pads ?? 0) > 0) return "S";
|
|
32148
|
+
return null;
|
|
32149
|
+
}
|
|
32150
|
+
var skipStation = (type) => type === "Drake-Class Carrier" || /Construction Depot/.test(type ?? "");
|
|
32151
|
+
function mapSystem(raw) {
|
|
32152
|
+
const { x, y, z: z2 } = raw;
|
|
32153
|
+
if (x == null || y == null || z2 == null) return null;
|
|
32154
|
+
return {
|
|
32155
|
+
name: raw.name,
|
|
32156
|
+
id64: String(raw.id64),
|
|
32157
|
+
coords: { x, y, z: z2 },
|
|
32158
|
+
government: raw.government ?? null,
|
|
32159
|
+
allegiance: raw.allegiance ?? null,
|
|
32160
|
+
security: raw.security ?? null,
|
|
32161
|
+
controlling_power: raw.controlling_power ?? null,
|
|
32162
|
+
powers: raw.power ?? [],
|
|
32163
|
+
power_state: raw.power_state ?? null,
|
|
32164
|
+
updated_at: raw.updated_at ?? null,
|
|
32165
|
+
factions: (raw.minor_faction_presences ?? []).map((f) => ({
|
|
32166
|
+
name: f.name,
|
|
32167
|
+
government: f.government ?? null,
|
|
32168
|
+
allegiance: f.allegiance ?? null,
|
|
32169
|
+
influence: f.influence ?? null,
|
|
32170
|
+
controlling: f.name === raw.controlling_minor_faction,
|
|
32171
|
+
// Spansh leaves out active_states when a faction has one state.
|
|
32172
|
+
active_states: f.active_states ?? (f.state && f.state !== "None" ? [f.state] : []),
|
|
32173
|
+
pending_states: f.pending_states ?? []
|
|
32174
|
+
})),
|
|
32175
|
+
stations: (raw.stations ?? []).filter((s) => !skipStation(s.type)).map((s) => ({
|
|
32176
|
+
name: s.name,
|
|
32177
|
+
type: s.type ?? null,
|
|
32178
|
+
pad: stationPad(s),
|
|
32179
|
+
arrival_ls: s.distance_to_arrival ?? null
|
|
32180
|
+
})),
|
|
32181
|
+
stars: (raw.bodies ?? []).filter((b) => b.type === "Star").map((b) => ({ name: b.name, arrival_ls: b.distance_to_arrival ?? 0 }))
|
|
32182
|
+
};
|
|
32183
|
+
}
|
|
32184
|
+
function distanceLy(a, b) {
|
|
32185
|
+
return Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z);
|
|
32186
|
+
}
|
|
32187
|
+
var isAnarchy = (f) => f.government === "Anarchy";
|
|
32188
|
+
|
|
32189
|
+
// src/systems/sweep-cache.ts
|
|
32190
|
+
var SweepCache = class {
|
|
32191
|
+
constructor(ttlMs = 36e5, now = Date.now, maxSweeps = 4) {
|
|
32192
|
+
this.ttlMs = ttlMs;
|
|
32193
|
+
this.now = now;
|
|
32194
|
+
this.maxSweeps = maxSweeps;
|
|
32195
|
+
}
|
|
32196
|
+
ttlMs;
|
|
32197
|
+
now;
|
|
32198
|
+
maxSweeps;
|
|
32199
|
+
name = "spansh-sweeps";
|
|
32200
|
+
sweeps = [];
|
|
32201
|
+
find(centre, radiusLy) {
|
|
32202
|
+
this.dropExpired();
|
|
32203
|
+
const hit = this.sweeps.find(
|
|
32204
|
+
(s) => distanceLy(s.centre, centre) + radiusLy <= s.radiusLy + 1e-9
|
|
32205
|
+
);
|
|
32206
|
+
return hit?.systems.filter((s) => distanceLy(s.coords, centre) <= radiusLy);
|
|
32207
|
+
}
|
|
32208
|
+
add(centre, radiusLy, systems) {
|
|
32209
|
+
this.sweeps.push({ centre, radiusLy, systems, storedAt: this.now() });
|
|
32210
|
+
while (this.sweeps.length > this.maxSweeps) this.sweeps.shift();
|
|
32211
|
+
}
|
|
32212
|
+
clear() {
|
|
32213
|
+
const n = this.sweeps.length;
|
|
32214
|
+
this.sweeps = [];
|
|
32215
|
+
return n;
|
|
32216
|
+
}
|
|
32217
|
+
stats() {
|
|
32218
|
+
this.dropExpired();
|
|
32219
|
+
const oldest = this.sweeps[0]?.storedAt;
|
|
32220
|
+
return {
|
|
32221
|
+
name: this.name,
|
|
32222
|
+
entries: this.sweeps.length,
|
|
32223
|
+
oldestAgeSeconds: oldest === void 0 ? null : Math.round((this.now() - oldest) / 1e3)
|
|
32224
|
+
};
|
|
32225
|
+
}
|
|
32226
|
+
dropExpired() {
|
|
32227
|
+
const cutoff = this.now() - this.ttlMs;
|
|
32228
|
+
this.sweeps = this.sweeps.filter((s) => s.storedAt > cutoff);
|
|
32229
|
+
}
|
|
32230
|
+
};
|
|
32231
|
+
|
|
32083
32232
|
// src/spansh/client.ts
|
|
32084
|
-
var
|
|
32233
|
+
var TRADER_FILTERS = {
|
|
32085
32234
|
material_trader_raw: { material_trader: { value: ["Raw"] } },
|
|
32086
32235
|
material_trader_manufactured: { material_trader: { value: ["Manufactured"] } },
|
|
32087
32236
|
material_trader_encoded: { material_trader: { value: ["Encoded"] } },
|
|
32088
32237
|
tech_broker_human: { technology_broker: { value: ["Human"] } },
|
|
32089
|
-
tech_broker_guardian: { technology_broker: { value: ["Guardian"] } }
|
|
32090
|
-
|
|
32091
|
-
|
|
32238
|
+
tech_broker_guardian: { technology_broker: { value: ["Guardian"] } }
|
|
32239
|
+
};
|
|
32240
|
+
var NAMED_SERVICES = {
|
|
32241
|
+
interstellar_factors: "Interstellar Factors Contact",
|
|
32242
|
+
black_market: "Black Market",
|
|
32243
|
+
universal_cartographics: "Universal Cartographics",
|
|
32244
|
+
vista_genomics: "Vista Genomics",
|
|
32245
|
+
search_and_rescue: "Search and Rescue",
|
|
32246
|
+
refuel: "Refuel",
|
|
32247
|
+
repair: "Repair",
|
|
32248
|
+
restock: "Restock",
|
|
32249
|
+
shipyard: "Shipyard",
|
|
32250
|
+
outfitting: "Outfitting",
|
|
32251
|
+
market: "Market",
|
|
32252
|
+
apex_interstellar: "Apex Interstellar",
|
|
32253
|
+
frontline_solutions: "Frontline Solutions",
|
|
32254
|
+
pioneer_supplies: "Pioneer Supplies",
|
|
32255
|
+
bartender: "Bartender",
|
|
32256
|
+
refinery_contact: "Refinery Contact",
|
|
32257
|
+
redemption_office: "Redemption Office",
|
|
32258
|
+
crew_lounge: "Crew Lounge",
|
|
32259
|
+
squadron_bank: "Squadron Bank",
|
|
32260
|
+
construction_services: "Construction Services",
|
|
32261
|
+
powerplay: "Powerplay"
|
|
32262
|
+
};
|
|
32263
|
+
var STATION_SERVICES = [
|
|
32264
|
+
...Object.keys(TRADER_FILTERS),
|
|
32265
|
+
...Object.keys(NAMED_SERVICES)
|
|
32266
|
+
];
|
|
32267
|
+
function serviceFilter(s) {
|
|
32268
|
+
if (Object.hasOwn(TRADER_FILTERS, s)) return TRADER_FILTERS[s];
|
|
32269
|
+
return { services: [{ name: { value: [NAMED_SERVICES[s]] } }] };
|
|
32270
|
+
}
|
|
32092
32271
|
var COUNT_CAP = 1e4;
|
|
32093
32272
|
var shipKey = (s) => s.toLowerCase().replace(/[\s-]/g, "");
|
|
32094
|
-
function pad(s) {
|
|
32095
|
-
if (s.has_large_pad || (s.large_pads ?? 0) > 0) return "L";
|
|
32096
|
-
if ((s.medium_pads ?? 0) > 0) return "M";
|
|
32097
|
-
if ((s.small_pads ?? 0) > 0) return "S";
|
|
32098
|
-
return null;
|
|
32099
|
-
}
|
|
32100
32273
|
var MAX_JOBS = 100;
|
|
32101
32274
|
var SEARCH_PAGE_SIZE = 50;
|
|
32275
|
+
var SWEEP_PAGE_SIZE = 500;
|
|
32102
32276
|
var CARRIER_TYPE = "Drake-Class Carrier";
|
|
32103
32277
|
var NON_CARRIER_TYPES = [
|
|
32104
32278
|
"Asteroid base",
|
|
@@ -32131,6 +32305,7 @@ var SpanshClient = class {
|
|
|
32131
32305
|
this.now = opts.now ?? Date.now;
|
|
32132
32306
|
this.systemCache = opts.systemCache ?? cache;
|
|
32133
32307
|
this.marketCache = opts.marketCache ?? cache;
|
|
32308
|
+
this.sweepCache = opts.sweepCache ?? new SweepCache();
|
|
32134
32309
|
}
|
|
32135
32310
|
http;
|
|
32136
32311
|
cache;
|
|
@@ -32141,10 +32316,12 @@ var SpanshClient = class {
|
|
|
32141
32316
|
now;
|
|
32142
32317
|
systemCache;
|
|
32143
32318
|
marketCache;
|
|
32319
|
+
sweepCache;
|
|
32144
32320
|
// Job ids by route form, so a call after a timeout collects the job instead of resubmitting.
|
|
32145
32321
|
routeJobs = /* @__PURE__ */ new Map();
|
|
32146
32322
|
tradeJobs = /* @__PURE__ */ new Map();
|
|
32147
32323
|
tradeSubmits = /* @__PURE__ */ new Map();
|
|
32324
|
+
sweeps = /* @__PURE__ */ new Map();
|
|
32148
32325
|
gate = Promise.resolve();
|
|
32149
32326
|
lastStart = 0;
|
|
32150
32327
|
async resolveShip(name) {
|
|
@@ -32169,7 +32346,7 @@ var SpanshClient = class {
|
|
|
32169
32346
|
shipName = await this.resolveShip(q.ship);
|
|
32170
32347
|
filters.ships = [{ name: { value: [shipName] } }];
|
|
32171
32348
|
} else if (q.service) {
|
|
32172
|
-
Object.assign(filters,
|
|
32349
|
+
Object.assign(filters, serviceFilter(q.service));
|
|
32173
32350
|
}
|
|
32174
32351
|
if (q.largePad) filters.has_large_pad = { value: true };
|
|
32175
32352
|
if (q.maxDistanceLy !== void 0) filters.distance = { min: 0, max: q.maxDistanceLy };
|
|
@@ -32236,7 +32413,7 @@ var SpanshClient = class {
|
|
|
32236
32413
|
name: s.name,
|
|
32237
32414
|
type: s.type ?? null,
|
|
32238
32415
|
arrival_ls: s.distance_to_arrival ?? null,
|
|
32239
|
-
pad:
|
|
32416
|
+
pad: stationPad(s),
|
|
32240
32417
|
market: s.has_market ?? false,
|
|
32241
32418
|
market_id: s.market_id ?? null,
|
|
32242
32419
|
outfitting: s.has_outfitting ?? false,
|
|
@@ -32253,6 +32430,66 @@ var SpanshClient = class {
|
|
|
32253
32430
|
);
|
|
32254
32431
|
return detail.record.needs_permit ?? null;
|
|
32255
32432
|
}
|
|
32433
|
+
/** Populated systems within radiusLy of near. Stops paging at opts.deadline; a partial result is not cached. */
|
|
32434
|
+
async populatedSystems(near, radiusLy, opts = {}) {
|
|
32435
|
+
const centre = (await this.system(near, 0)).coords;
|
|
32436
|
+
const cached2 = this.sweepCache.find(centre, radiusLy);
|
|
32437
|
+
if (cached2) return { centre, systems: cached2, total: cached2.length, partial: false };
|
|
32438
|
+
const key = `${near.toLowerCase()}:${radiusLy}`;
|
|
32439
|
+
let running = this.sweeps.get(key);
|
|
32440
|
+
if (!running) {
|
|
32441
|
+
running = this.sweepPages(near, radiusLy, centre, opts).finally(
|
|
32442
|
+
() => this.sweeps.delete(key)
|
|
32443
|
+
);
|
|
32444
|
+
this.sweeps.set(key, running);
|
|
32445
|
+
}
|
|
32446
|
+
return running;
|
|
32447
|
+
}
|
|
32448
|
+
async sweepPages(near, radiusLy, centre, opts) {
|
|
32449
|
+
const systems = [];
|
|
32450
|
+
const seen = /* @__PURE__ */ new Set();
|
|
32451
|
+
let total = 0;
|
|
32452
|
+
let partial2 = false;
|
|
32453
|
+
for (let page2 = 0; ; page2++) {
|
|
32454
|
+
if (page2 > 0 && opts.deadline !== void 0 && this.now() >= opts.deadline) {
|
|
32455
|
+
partial2 = true;
|
|
32456
|
+
break;
|
|
32457
|
+
}
|
|
32458
|
+
const body = {
|
|
32459
|
+
filters: {
|
|
32460
|
+
distance: { min: 0, max: radiusLy },
|
|
32461
|
+
population: { comparison: "<=>", value: [1, 1e11] }
|
|
32462
|
+
},
|
|
32463
|
+
sort: [{ distance: { direction: "asc" } }],
|
|
32464
|
+
size: SWEEP_PAGE_SIZE,
|
|
32465
|
+
page: page2,
|
|
32466
|
+
reference_system: near
|
|
32467
|
+
};
|
|
32468
|
+
let res;
|
|
32469
|
+
try {
|
|
32470
|
+
res = await this.post("/systems/search", JSON.stringify(body), "application/json");
|
|
32471
|
+
} catch (err) {
|
|
32472
|
+
if (err instanceof SpanshError && err.status === 400) {
|
|
32473
|
+
throw new UserError(
|
|
32474
|
+
`Spansh rejected the search (${err.detail}). Check that "${near}" is an exact system name (spansh_system).`
|
|
32475
|
+
);
|
|
32476
|
+
}
|
|
32477
|
+
throw err;
|
|
32478
|
+
}
|
|
32479
|
+
total = res.count;
|
|
32480
|
+
const results = res.results ?? [];
|
|
32481
|
+
for (const raw of results) {
|
|
32482
|
+
const s = mapSystem(raw);
|
|
32483
|
+
if (s && !seen.has(s.id64)) {
|
|
32484
|
+
seen.add(s.id64);
|
|
32485
|
+
systems.push(s);
|
|
32486
|
+
}
|
|
32487
|
+
}
|
|
32488
|
+
if (results.length < SWEEP_PAGE_SIZE) break;
|
|
32489
|
+
}
|
|
32490
|
+
if (!partial2) this.sweepCache.add(centre, radiusLy, systems);
|
|
32491
|
+
return { centre, systems, total, partial: partial2 };
|
|
32492
|
+
}
|
|
32256
32493
|
async route(q, opts = {}) {
|
|
32257
32494
|
const form = new URLSearchParams({
|
|
32258
32495
|
from: q.from,
|
|
@@ -32548,7 +32785,7 @@ var SpanshClient = class {
|
|
|
32548
32785
|
distance_ly: s.distance ?? null,
|
|
32549
32786
|
arrival_ls: s.distance_to_arrival ?? null,
|
|
32550
32787
|
coords: { x, y, z: z2 },
|
|
32551
|
-
pad:
|
|
32788
|
+
pad: stationPad(s),
|
|
32552
32789
|
type: s.type ?? null,
|
|
32553
32790
|
planetary: s.is_planetary ?? false,
|
|
32554
32791
|
market_age_hours: Number.isNaN(updated) ? null : Math.round((now - updated) / 36e5 * 10) / 10,
|
|
@@ -32579,7 +32816,7 @@ var SpanshClient = class {
|
|
|
32579
32816
|
distance_ly: s.distance ?? 0,
|
|
32580
32817
|
arrival_ls: s.distance_to_arrival ?? null,
|
|
32581
32818
|
type: s.type ?? null,
|
|
32582
|
-
pad:
|
|
32819
|
+
pad: stationPad(s),
|
|
32583
32820
|
planetary: s.is_planetary ?? null,
|
|
32584
32821
|
price,
|
|
32585
32822
|
market_updated_at: s.market_updated_at ?? null,
|
|
@@ -34610,9 +34847,12 @@ function buildDeps(config2, opts = {}) {
|
|
|
34610
34847
|
caches.register(spanshSystemCache);
|
|
34611
34848
|
const spanshMarketCache = new TtlCache("spansh-markets", 6e5);
|
|
34612
34849
|
caches.register(spanshMarketCache);
|
|
34850
|
+
const spanshSweepCache = new SweepCache();
|
|
34851
|
+
caches.register(spanshSweepCache);
|
|
34613
34852
|
const spansh = new SpanshClient(http, spanshCache, {
|
|
34614
34853
|
systemCache: spanshSystemCache,
|
|
34615
|
-
marketCache: spanshMarketCache
|
|
34854
|
+
marketCache: spanshMarketCache,
|
|
34855
|
+
sweepCache: spanshSweepCache
|
|
34616
34856
|
});
|
|
34617
34857
|
const capiHttp = new HttpClient({ userAgent: `EDCD-EdMcp-${VERSION}`, fetchFn: opts.fetchFn });
|
|
34618
34858
|
const capiCache = new TtlCache("capi", 6e4);
|
|
@@ -41918,6 +42158,15 @@ var McpServer = class {
|
|
|
41918
42158
|
this._promptHandlersInitialized = false;
|
|
41919
42159
|
this.server = new Server(serverInfo, options);
|
|
41920
42160
|
this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
|
|
42161
|
+
if (options?.capabilities?.tools) {
|
|
42162
|
+
this.setToolRequestHandlers();
|
|
42163
|
+
}
|
|
42164
|
+
if (options?.capabilities?.resources) {
|
|
42165
|
+
this.setResourceRequestHandlers();
|
|
42166
|
+
}
|
|
42167
|
+
if (options?.capabilities?.prompts) {
|
|
42168
|
+
this.setPromptRequestHandlers();
|
|
42169
|
+
}
|
|
41921
42170
|
}
|
|
41922
42171
|
/**
|
|
41923
42172
|
* Access experimental features.
|
|
@@ -43245,10 +43494,10 @@ function slotInfo(hull, slot, item) {
|
|
|
43245
43494
|
return { slot, kind: kind2, size };
|
|
43246
43495
|
}
|
|
43247
43496
|
function bulkheadFromItem(hull, item) {
|
|
43248
|
-
const
|
|
43497
|
+
const lower2 = item.toLowerCase();
|
|
43249
43498
|
const offset = hull.bulkheads.length > 5 ? 1 : 0;
|
|
43250
|
-
if (
|
|
43251
|
-
const i = BULKHEAD_SUFFIXES.findIndex((s) =>
|
|
43499
|
+
if (lower2.endsWith("_armour_grade1_default")) return hull.bulkheads[0] ?? null;
|
|
43500
|
+
const i = BULKHEAD_SUFFIXES.findIndex((s) => lower2.endsWith(`_armour_${s}`));
|
|
43252
43501
|
return i < 0 ? null : hull.bulkheads[i + offset] ?? null;
|
|
43253
43502
|
}
|
|
43254
43503
|
function bulkheadItem(hull, module) {
|
|
@@ -44712,13 +44961,13 @@ function resolveRules(stored, override, usePreferences, auto, jumpAuto = { ly: n
|
|
|
44712
44961
|
const prefs = applyPatch(usePreferences ? stored : OFF_PREFS, override ?? {});
|
|
44713
44962
|
const p = prefs.stations.min_pad;
|
|
44714
44963
|
const fromRequest = override?.stations?.min_pad !== void 0;
|
|
44715
|
-
const
|
|
44964
|
+
const pad = p === "auto" ? auto : p === "none" ? { size: null, source: fromRequest ? "request" : "none" } : { size: p, source: fromRequest ? "request" : "preferences" };
|
|
44716
44965
|
const range = prefs.travel.jump_range_ly;
|
|
44717
44966
|
const fromRequestRange = override?.travel?.jump_range_ly !== void 0;
|
|
44718
44967
|
const { ly, source } = range !== "auto" ? { ly: range, source: fromRequestRange ? "request" : "preferences" } : jumpAuto.ly ? { ly: jumpAuto.ly, source: jumpAuto.source } : { ly: 30, source: "auto: default 30 LY" };
|
|
44719
44968
|
return {
|
|
44720
44969
|
prefs,
|
|
44721
|
-
pad
|
|
44970
|
+
pad,
|
|
44722
44971
|
travel: { ...prefs.travel, jump_range_ly: ly },
|
|
44723
44972
|
jumpRangeSource: source
|
|
44724
44973
|
};
|
|
@@ -45092,11 +45341,11 @@ function union2(a, b) {
|
|
|
45092
45341
|
async function resolveSearchRules(deps, preferences, usePreferences, largePad) {
|
|
45093
45342
|
const stored = deps.prefs && usePreferences ? await deps.prefs.get() : DEFAULT_PREFS;
|
|
45094
45343
|
const override = largePad ? { ...preferences, stations: { ...preferences?.stations, min_pad: "L" } } : preferences ?? {};
|
|
45095
|
-
let
|
|
45344
|
+
let pad = { size: null, source: "none" };
|
|
45096
45345
|
let jump2;
|
|
45097
|
-
const first = resolveRules(stored, override, usePreferences,
|
|
45346
|
+
const first = resolveRules(stored, override, usePreferences, pad);
|
|
45098
45347
|
if (first.prefs.stations.min_pad === "auto") {
|
|
45099
|
-
|
|
45348
|
+
pad = await autoPad(deps.currentShip ?? (async () => null));
|
|
45100
45349
|
}
|
|
45101
45350
|
if (first.prefs.travel.jump_range_ly === "auto") {
|
|
45102
45351
|
const max = await deps.jumpRange?.().catch(() => null);
|
|
@@ -45108,7 +45357,7 @@ async function resolveSearchRules(deps, preferences, usePreferences, largePad) {
|
|
|
45108
45357
|
};
|
|
45109
45358
|
}
|
|
45110
45359
|
}
|
|
45111
|
-
return resolveRules(stored, override, usePreferences,
|
|
45360
|
+
return resolveRules(stored, override, usePreferences, pad, jump2);
|
|
45112
45361
|
}
|
|
45113
45362
|
|
|
45114
45363
|
// src/tools/materials.ts
|
|
@@ -45299,7 +45548,7 @@ function legBetween(from, to, travel) {
|
|
|
45299
45548
|
if (same(from.system, to.system) && same(from.station, to.station)) {
|
|
45300
45549
|
return { jumps: 0, seconds: 0, distanceLy: 0 };
|
|
45301
45550
|
}
|
|
45302
|
-
const
|
|
45551
|
+
const distanceLy2 = same(from.system, to.system) ? 0 : Math.hypot(
|
|
45303
45552
|
from.coords.x - to.coords.x,
|
|
45304
45553
|
from.coords.y - to.coords.y,
|
|
45305
45554
|
from.coords.z - to.coords.z
|
|
@@ -45307,14 +45556,14 @@ function legBetween(from, to, travel) {
|
|
|
45307
45556
|
return {
|
|
45308
45557
|
...legSeconds(
|
|
45309
45558
|
{
|
|
45310
|
-
distanceLy,
|
|
45559
|
+
distanceLy: distanceLy2,
|
|
45311
45560
|
arrivalLs: to.arrivalLs,
|
|
45312
45561
|
planetary: to.planetary,
|
|
45313
45562
|
stationKnown: to.station !== null
|
|
45314
45563
|
},
|
|
45315
45564
|
travel
|
|
45316
45565
|
),
|
|
45317
|
-
distanceLy
|
|
45566
|
+
distanceLy: distanceLy2
|
|
45318
45567
|
};
|
|
45319
45568
|
}
|
|
45320
45569
|
|
|
@@ -46369,6 +46618,335 @@ function registerLoopTools(server, deps) {
|
|
|
46369
46618
|
}
|
|
46370
46619
|
}
|
|
46371
46620
|
|
|
46621
|
+
// src/systems/massacre.ts
|
|
46622
|
+
var SOURCE_RANGE_LY = 10;
|
|
46623
|
+
var NO_MASSACRES = /* @__PURE__ */ new Set(["war", "civil war", "election"]);
|
|
46624
|
+
function massacreTargets(systems, centre, opts) {
|
|
46625
|
+
const pirates = systems.flatMap(
|
|
46626
|
+
(s) => s.factions.filter(isAnarchy).map((f) => ({ faction: f.name, system: s }))
|
|
46627
|
+
);
|
|
46628
|
+
const piratesNear = /* @__PURE__ */ new Map();
|
|
46629
|
+
const piratesInRange = (s) => {
|
|
46630
|
+
let list2 = piratesNear.get(s.id64);
|
|
46631
|
+
if (!list2) {
|
|
46632
|
+
list2 = pirates.filter((p) => p.system.id64 !== s.id64).filter((p) => distanceLy(p.system.coords, s.coords) <= SOURCE_RANGE_LY).map((p) => ({ faction: p.faction, system: p.system.name }));
|
|
46633
|
+
piratesNear.set(s.id64, list2);
|
|
46634
|
+
}
|
|
46635
|
+
return list2;
|
|
46636
|
+
};
|
|
46637
|
+
const candidates = systems.filter((s) => s.stations.length > 0);
|
|
46638
|
+
const targets = [];
|
|
46639
|
+
let leftOutSpread = 0;
|
|
46640
|
+
for (const p of pirates) {
|
|
46641
|
+
const distance2 = distanceLy(p.system.coords, centre);
|
|
46642
|
+
if (distance2 > opts.radiusLy) continue;
|
|
46643
|
+
const spreadLs = Math.max(0, ...p.system.stars.map((s) => s.arrival_ls));
|
|
46644
|
+
if (spreadLs > opts.maxStarLs) {
|
|
46645
|
+
leftOutSpread++;
|
|
46646
|
+
continue;
|
|
46647
|
+
}
|
|
46648
|
+
const sources = [];
|
|
46649
|
+
const excluded = [];
|
|
46650
|
+
for (const s of candidates) {
|
|
46651
|
+
if (s.id64 === p.system.id64) continue;
|
|
46652
|
+
const toTarget = distanceLy(s.coords, p.system.coords);
|
|
46653
|
+
if (toTarget > SOURCE_RANGE_LY) continue;
|
|
46654
|
+
const blocked = s.factions.flatMap(
|
|
46655
|
+
(f) => f.active_states.filter((x) => NO_MASSACRES.has(x.toLowerCase())).map((state) => ({ system: s.name, faction: f.name, state, updated_at: s.updated_at }))
|
|
46656
|
+
);
|
|
46657
|
+
if (blocked.length) {
|
|
46658
|
+
excluded.push(...blocked);
|
|
46659
|
+
continue;
|
|
46660
|
+
}
|
|
46661
|
+
const others = piratesInRange(s).filter((x) => x.faction !== p.faction);
|
|
46662
|
+
sources.push({
|
|
46663
|
+
system: s,
|
|
46664
|
+
distanceToTargetLy: toTarget,
|
|
46665
|
+
primary: others.length === 0,
|
|
46666
|
+
otherPirates: others
|
|
46667
|
+
});
|
|
46668
|
+
}
|
|
46669
|
+
const primary = sources.filter((s) => s.primary);
|
|
46670
|
+
if (!primary.length) continue;
|
|
46671
|
+
const givers = new Set(
|
|
46672
|
+
primary.flatMap((s) => s.system.factions.filter((f) => !isAnarchy(f)).map((f) => f.name))
|
|
46673
|
+
);
|
|
46674
|
+
if (givers.size === 0) continue;
|
|
46675
|
+
sources.sort(
|
|
46676
|
+
(a, b) => Number(b.primary) - Number(a.primary) || a.distanceToTargetLy - b.distanceToTargetLy
|
|
46677
|
+
);
|
|
46678
|
+
targets.push({
|
|
46679
|
+
faction: p.faction,
|
|
46680
|
+
system: p.system,
|
|
46681
|
+
distanceLy: distance2,
|
|
46682
|
+
spreadLs,
|
|
46683
|
+
stackSize: givers.size,
|
|
46684
|
+
sources,
|
|
46685
|
+
excluded
|
|
46686
|
+
});
|
|
46687
|
+
}
|
|
46688
|
+
targets.sort(
|
|
46689
|
+
(a, b) => b.stackSize - a.stackSize || a.spreadLs - b.spreadLs || a.distanceLy - b.distanceLy
|
|
46690
|
+
);
|
|
46691
|
+
return { targets, leftOutSpread };
|
|
46692
|
+
}
|
|
46693
|
+
|
|
46694
|
+
// src/systems/state.ts
|
|
46695
|
+
var lower = (xs) => xs?.length ? new Set(xs.map((x) => x.toLowerCase())) : null;
|
|
46696
|
+
var has = (set2, v) => !set2 || v !== null && set2.has(v.toLowerCase());
|
|
46697
|
+
function hasFilter(q) {
|
|
46698
|
+
return [q.states, q.government, q.allegiance, q.power, q.security].some((x) => x?.length);
|
|
46699
|
+
}
|
|
46700
|
+
function systemsByState(systems, centre, radiusLy, q) {
|
|
46701
|
+
const states = lower(q.states);
|
|
46702
|
+
const government = lower(q.government);
|
|
46703
|
+
const allegiance = lower(q.allegiance);
|
|
46704
|
+
const power2 = lower(q.power);
|
|
46705
|
+
const security = lower(q.security);
|
|
46706
|
+
const out = [];
|
|
46707
|
+
for (const s of systems) {
|
|
46708
|
+
const d = distanceLy(s.coords, centre);
|
|
46709
|
+
if (d > radiusLy) continue;
|
|
46710
|
+
if (!has(government, s.government) || !has(allegiance, s.allegiance)) continue;
|
|
46711
|
+
if (!has(power2, s.controlling_power) || !has(security, s.security)) continue;
|
|
46712
|
+
let factions = [];
|
|
46713
|
+
if (states) {
|
|
46714
|
+
factions = s.factions.filter(
|
|
46715
|
+
(f) => (!q.controllingOnly || f.controlling) && [...f.active_states, ...q.includePending ? f.pending_states : []].some(
|
|
46716
|
+
(x) => states.has(x.toLowerCase())
|
|
46717
|
+
)
|
|
46718
|
+
);
|
|
46719
|
+
if (!factions.length) continue;
|
|
46720
|
+
}
|
|
46721
|
+
out.push({ system: s, distanceLy: d, factions });
|
|
46722
|
+
}
|
|
46723
|
+
return out.sort((a, b) => a.distanceLy - b.distanceLy);
|
|
46724
|
+
}
|
|
46725
|
+
|
|
46726
|
+
// src/tools/systems.ts
|
|
46727
|
+
var TIME_LIMIT_MS2 = 2e4;
|
|
46728
|
+
var MASSACRE_MARGIN_LY = 2 * SOURCE_RANGE_LY;
|
|
46729
|
+
var STALE_HOURS = 72;
|
|
46730
|
+
var FAR_STAR_LS = 1e3;
|
|
46731
|
+
var Radius = external_exports.number().min(1).max(50).default(50);
|
|
46732
|
+
var Limit = external_exports.number().int().min(1).max(100).default(50);
|
|
46733
|
+
var Values = external_exports.array(external_exports.string()).optional();
|
|
46734
|
+
function registerSystemTools(server, deps) {
|
|
46735
|
+
const now = deps.now ?? Date.now;
|
|
46736
|
+
const resolveNear = async (near, notes) => {
|
|
46737
|
+
if (near) return near;
|
|
46738
|
+
if (deps.journal) {
|
|
46739
|
+
try {
|
|
46740
|
+
const system = (await deps.journal.state())?.location.system;
|
|
46741
|
+
if (system) return system;
|
|
46742
|
+
} catch (err) {
|
|
46743
|
+
notes.push(`Journal unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
46744
|
+
}
|
|
46745
|
+
}
|
|
46746
|
+
const why = notes.length ? ` (${notes.join(" ")})` : "";
|
|
46747
|
+
throw new UserError(`Give near, or set up the journal to use the current system${why}.`);
|
|
46748
|
+
};
|
|
46749
|
+
const ageHours2 = (s) => s.updated_at ? Math.round((now() - Date.parse(s.updated_at)) / 36e5 * 10) / 10 : null;
|
|
46750
|
+
const leg = (ly, t) => legSeconds({ distanceLy: ly, arrivalLs: null, planetary: false, stationKnown: false }, t);
|
|
46751
|
+
const nearestWith = (s, ok2) => {
|
|
46752
|
+
const ls = s.stations.filter((x) => ok2(x.pad)).map((x) => x.arrival_ls ?? Infinity);
|
|
46753
|
+
const min3 = Math.min(...ls);
|
|
46754
|
+
return Number.isFinite(min3) ? Math.round(min3) : null;
|
|
46755
|
+
};
|
|
46756
|
+
const pads = (s) => ({
|
|
46757
|
+
L: nearestWith(s, (p) => p === "L"),
|
|
46758
|
+
M: nearestWith(s, (p) => p === "L" || p === "M"),
|
|
46759
|
+
S: nearestWith(s, (p) => p !== null)
|
|
46760
|
+
});
|
|
46761
|
+
server.registerTool(
|
|
46762
|
+
"systems_by_state",
|
|
46763
|
+
{
|
|
46764
|
+
annotations: {
|
|
46765
|
+
title: "Systems by Faction State",
|
|
46766
|
+
readOnlyHint: true,
|
|
46767
|
+
destructiveHint: false,
|
|
46768
|
+
idempotentHint: true,
|
|
46769
|
+
openWorldHint: true
|
|
46770
|
+
},
|
|
46771
|
+
description: "Find the nearest populated systems with a faction in a given state (e.g. War, Civil War, Election, Boom, Outbreak), or by government, allegiance, controlling power or security. near defaults to the journal location. War and Civil War results say conflict_zones: likely (settlements are not checked). Faction states come from player visits: check age_hours. Uses one Spansh search of all populated systems in radius_ly, cached for an hour; the first call can take 10 s.",
|
|
46772
|
+
inputSchema: {
|
|
46773
|
+
near: external_exports.string().optional().describe("Exact reference system name"),
|
|
46774
|
+
radius_ly: Radius,
|
|
46775
|
+
state: Values.describe("Faction states to match, e.g. ['War', 'Civil War']"),
|
|
46776
|
+
include_pending: external_exports.boolean().default(false),
|
|
46777
|
+
controlling_only: external_exports.boolean().default(false),
|
|
46778
|
+
government: Values,
|
|
46779
|
+
allegiance: Values,
|
|
46780
|
+
power: Values.describe("Controlling power"),
|
|
46781
|
+
security: Values,
|
|
46782
|
+
limit: Limit
|
|
46783
|
+
}
|
|
46784
|
+
},
|
|
46785
|
+
wrap(
|
|
46786
|
+
async (a) => {
|
|
46787
|
+
const q = {
|
|
46788
|
+
states: a.state,
|
|
46789
|
+
includePending: a.include_pending,
|
|
46790
|
+
controllingOnly: a.controlling_only,
|
|
46791
|
+
government: a.government,
|
|
46792
|
+
allegiance: a.allegiance,
|
|
46793
|
+
power: a.power,
|
|
46794
|
+
security: a.security
|
|
46795
|
+
};
|
|
46796
|
+
if (!hasFilter(q)) {
|
|
46797
|
+
throw new UserError(
|
|
46798
|
+
"Give at least one of state, government, allegiance, power or security."
|
|
46799
|
+
);
|
|
46800
|
+
}
|
|
46801
|
+
if (a.controlling_only && !a.state?.length) {
|
|
46802
|
+
throw new UserError(
|
|
46803
|
+
"controlling_only needs state: it limits the state match to the controlling faction."
|
|
46804
|
+
);
|
|
46805
|
+
}
|
|
46806
|
+
const notes = [];
|
|
46807
|
+
const near = await resolveNear(a.near, notes);
|
|
46808
|
+
const rules = await resolveSearchRules(deps, void 0, true);
|
|
46809
|
+
const sweep = await deps.spansh.populatedSystems(near, a.radius_ly, {
|
|
46810
|
+
deadline: now() + TIME_LIMIT_MS2
|
|
46811
|
+
});
|
|
46812
|
+
const matches2 = systemsByState(sweep.systems, sweep.centre, a.radius_ly, q);
|
|
46813
|
+
const shown = matches2.slice(0, a.limit);
|
|
46814
|
+
const stale = shown.filter((m) => (ageHours2(m.system) ?? 0) > STALE_HOURS).length;
|
|
46815
|
+
if (stale) notes.push(`${stale} results have data more than ${STALE_HOURS / 24} days old.`);
|
|
46816
|
+
if (sweep.partial) {
|
|
46817
|
+
notes.push("Spansh did not return every system in time; results may be missing.");
|
|
46818
|
+
}
|
|
46819
|
+
return {
|
|
46820
|
+
near,
|
|
46821
|
+
searched: sweep.systems.length,
|
|
46822
|
+
travel: describeRules(rules).travel,
|
|
46823
|
+
total_matches: matches2.length,
|
|
46824
|
+
partial: sweep.partial,
|
|
46825
|
+
systems: shown.map((m) => {
|
|
46826
|
+
const s = m.system;
|
|
46827
|
+
const war = m.factions.some(
|
|
46828
|
+
(f) => f.active_states.some((x) => /^(war|civil war)$/i.test(x))
|
|
46829
|
+
);
|
|
46830
|
+
const nearest3 = pads(s);
|
|
46831
|
+
const bestPad = nearest3.L !== null ? "L" : nearest3.M !== null ? "M" : nearest3.S !== null ? "S" : null;
|
|
46832
|
+
return {
|
|
46833
|
+
system: s.name,
|
|
46834
|
+
distance_ly: Math.round(m.distanceLy * 100) / 100,
|
|
46835
|
+
jumps: leg(m.distanceLy, rules.travel).jumps,
|
|
46836
|
+
matching_factions: m.factions.map((f) => ({
|
|
46837
|
+
name: f.name,
|
|
46838
|
+
controlling: f.controlling,
|
|
46839
|
+
influence: f.influence,
|
|
46840
|
+
active_states: f.active_states,
|
|
46841
|
+
pending_states: f.pending_states
|
|
46842
|
+
})),
|
|
46843
|
+
government: s.government,
|
|
46844
|
+
allegiance: s.allegiance,
|
|
46845
|
+
security: s.security,
|
|
46846
|
+
controlling_power: s.controlling_power,
|
|
46847
|
+
power_state: s.power_state,
|
|
46848
|
+
best_pad: bestPad,
|
|
46849
|
+
nearest_station_ls: bestPad ? nearest3[bestPad] : null,
|
|
46850
|
+
updated_at: s.updated_at,
|
|
46851
|
+
age_hours: ageHours2(s),
|
|
46852
|
+
...war ? { conflict_zones: "likely" } : {}
|
|
46853
|
+
};
|
|
46854
|
+
}),
|
|
46855
|
+
notes
|
|
46856
|
+
};
|
|
46857
|
+
}
|
|
46858
|
+
)
|
|
46859
|
+
);
|
|
46860
|
+
server.registerTool(
|
|
46861
|
+
"massacre_targets",
|
|
46862
|
+
{
|
|
46863
|
+
annotations: {
|
|
46864
|
+
title: "Find Massacre Targets",
|
|
46865
|
+
readOnlyHint: true,
|
|
46866
|
+
destructiveHint: false,
|
|
46867
|
+
idempotentHint: true,
|
|
46868
|
+
openWorldHint: true
|
|
46869
|
+
},
|
|
46870
|
+
description: "Find pirate (Anarchy) factions near you with good sources of stackable pirate massacre missions, as Inara's 'nearest massacre missions' does. Sources are populated systems within 10 ly of the target; a primary source has no other pirate faction in other systems within 10 ly (its own pirate faction does not count, as missions send you to another system), so all its massacre missions are for this target. Sources with a faction in War, Civil War or Election are excluded and listed. stack_size is the number of distinct giving factions in the primary sources. Targets whose farthest star is beyond max_star_ls (default 10,000) are left out, because fights there can be far from the arrival star. Ranked by stack_size, then spread_ls, then distance. Results are candidates: mission boards are not published. Pads are listed, not filtered. near defaults to the journal location.",
|
|
46871
|
+
inputSchema: {
|
|
46872
|
+
near: external_exports.string().optional().describe("Exact reference system name"),
|
|
46873
|
+
radius_ly: Radius.describe("Targets within this distance"),
|
|
46874
|
+
max_star_ls: external_exports.number().min(0).default(1e4),
|
|
46875
|
+
limit: Limit,
|
|
46876
|
+
preferences: PrefsPatchSchema.optional(),
|
|
46877
|
+
use_preferences: external_exports.boolean().default(true)
|
|
46878
|
+
}
|
|
46879
|
+
},
|
|
46880
|
+
wrap(
|
|
46881
|
+
async (a) => {
|
|
46882
|
+
const notes = [];
|
|
46883
|
+
const near = await resolveNear(a.near, notes);
|
|
46884
|
+
const rules = await resolveSearchRules(deps, a.preferences, a.use_preferences);
|
|
46885
|
+
const sweep = await deps.spansh.populatedSystems(near, a.radius_ly + MASSACRE_MARGIN_LY, {
|
|
46886
|
+
deadline: now() + TIME_LIMIT_MS2
|
|
46887
|
+
});
|
|
46888
|
+
const { targets, leftOutSpread } = massacreTargets(sweep.systems, sweep.centre, {
|
|
46889
|
+
radiusLy: a.radius_ly,
|
|
46890
|
+
maxStarLs: a.max_star_ls
|
|
46891
|
+
});
|
|
46892
|
+
notes.push(
|
|
46893
|
+
"These are candidates: whether the mission boards have massacre missions now depends on recent player activity."
|
|
46894
|
+
);
|
|
46895
|
+
if (leftOutSpread) {
|
|
46896
|
+
notes.push(
|
|
46897
|
+
`${leftOutSpread} targets left out: a star beyond ${a.max_star_ls} ls. Raise max_star_ls to see them.`
|
|
46898
|
+
);
|
|
46899
|
+
}
|
|
46900
|
+
if (sweep.partial) {
|
|
46901
|
+
notes.push(
|
|
46902
|
+
"Spansh did not return every system in time: a source can look primary when a pirate faction near the edge was missed."
|
|
46903
|
+
);
|
|
46904
|
+
}
|
|
46905
|
+
return {
|
|
46906
|
+
near,
|
|
46907
|
+
searched: sweep.systems.length,
|
|
46908
|
+
left_out_spread: leftOutSpread,
|
|
46909
|
+
partial: sweep.partial,
|
|
46910
|
+
travel: describeRules(rules).travel,
|
|
46911
|
+
targets: targets.slice(0, a.limit).map((t) => ({
|
|
46912
|
+
target_faction: t.faction,
|
|
46913
|
+
target_system: t.system.name,
|
|
46914
|
+
distance_ly: Math.round(t.distanceLy * 100) / 100,
|
|
46915
|
+
travel_min: Math.round(leg(t.distanceLy, rules.travel).seconds / 6) / 10,
|
|
46916
|
+
stack_size: t.stackSize,
|
|
46917
|
+
spread_ls: Math.round(t.spreadLs),
|
|
46918
|
+
far_stars: t.system.stars.filter((s) => s.arrival_ls > FAR_STAR_LS).map((s) => ({ name: s.name, arrival_ls: Math.round(s.arrival_ls) })),
|
|
46919
|
+
sources: t.sources.map((s) => {
|
|
46920
|
+
const counts2 = { federation: 0, empire: 0, alliance: 0, independent: 0 };
|
|
46921
|
+
for (const f of s.system.factions) {
|
|
46922
|
+
const k = f.allegiance?.toLowerCase();
|
|
46923
|
+
if (!isAnarchy(f) && k && k in counts2) counts2[k]++;
|
|
46924
|
+
}
|
|
46925
|
+
return {
|
|
46926
|
+
system: s.system.name,
|
|
46927
|
+
primary: s.primary,
|
|
46928
|
+
other_pirates: s.otherPirates,
|
|
46929
|
+
distance_to_target_ly: Math.round(s.distanceToTargetLy * 100) / 100,
|
|
46930
|
+
allegiance_counts: counts2,
|
|
46931
|
+
pads: pads(s.system),
|
|
46932
|
+
factions: s.system.factions.map((f) => ({
|
|
46933
|
+
name: f.name,
|
|
46934
|
+
allegiance: f.allegiance,
|
|
46935
|
+
active_states: f.active_states
|
|
46936
|
+
})),
|
|
46937
|
+
updated_at: s.system.updated_at,
|
|
46938
|
+
age_hours: ageHours2(s.system)
|
|
46939
|
+
};
|
|
46940
|
+
}),
|
|
46941
|
+
excluded: t.excluded
|
|
46942
|
+
})),
|
|
46943
|
+
notes
|
|
46944
|
+
};
|
|
46945
|
+
}
|
|
46946
|
+
)
|
|
46947
|
+
);
|
|
46948
|
+
}
|
|
46949
|
+
|
|
46372
46950
|
// src/tools/inara.ts
|
|
46373
46951
|
function registerInaraTools(server, deps) {
|
|
46374
46952
|
server.registerTool(
|
|
@@ -47541,15 +48119,8 @@ function registerStationTools(server, deps) {
|
|
|
47541
48119
|
}
|
|
47542
48120
|
|
|
47543
48121
|
// src/tools/spansh.ts
|
|
47544
|
-
var Service = external_exports.enum(
|
|
47545
|
-
|
|
47546
|
-
"material_trader_manufactured",
|
|
47547
|
-
"material_trader_encoded",
|
|
47548
|
-
"tech_broker_human",
|
|
47549
|
-
"tech_broker_guardian",
|
|
47550
|
-
"interstellar_factors"
|
|
47551
|
-
]);
|
|
47552
|
-
var Limit = external_exports.number().int().min(1).max(50).default(10);
|
|
48122
|
+
var Service = external_exports.enum(STATION_SERVICES);
|
|
48123
|
+
var Limit2 = external_exports.number().int().min(1).max(50).default(10);
|
|
47553
48124
|
function registerSpanshTools(server, deps) {
|
|
47554
48125
|
const { spansh } = deps;
|
|
47555
48126
|
const now = deps.now ?? Date.now;
|
|
@@ -47563,7 +48134,7 @@ function registerSpanshTools(server, deps) {
|
|
|
47563
48134
|
idempotentHint: true,
|
|
47564
48135
|
openWorldHint: true
|
|
47565
48136
|
},
|
|
47566
|
-
description: "Find the best stations near a system, ranked by travel time and cost, that sell a module or ship, or offer a service
|
|
48137
|
+
description: "Find the best stations near a system, ranked by travel time and cost, that sell a module or ship, or offer a service: material traders, tech brokers, interstellar factors, black market, Universal Cartographics, Vista Genomics, Search and Rescue, refuel, repair, restock, shipyard, outfitting, market and more (see the service enum). Give exactly one of module, ship, service. Module names are generic (e.g. 'Frame Shift Drive') with class 1-8 and rating A-I; give class and rating to compare prices. Applies the commander's stored travel preferences (arrival distance, station types, pad size from the current ship, travel-time model); override any of them for this call with `preferences` (same shape as prefs_set's patch) or ignore them with use_preferences=false. Returns total (Spansh matches before preferences; 10000 means 10000 or more), matched (matches after preferences among the stations fetched: the nearest 50, or up to 100 when the arrival limit was relaxed). Stations are ranked by estimated travel time (jumps plus supercruise, docking and planetary time) and, for purchases, by effective cost = price + travel time \xD7 the commander's credits_per_hour. Each station has jumps, travel_s, travel_min, arrival_assumed, effective_cost and discount_pct (versus the median price); recommended explains the trade-off. Stations in permit-locked systems are left out unless permit_systems is allow, which keeps them with needs_permit: true. Also returns any relaxed rules, the applied preferences and notes. Cached 5 min.",
|
|
47567
48138
|
inputSchema: {
|
|
47568
48139
|
near: external_exports.string().describe("Exact reference system name"),
|
|
47569
48140
|
module: external_exports.object({
|
|
@@ -47577,7 +48148,7 @@ function registerSpanshTools(server, deps) {
|
|
|
47577
48148
|
max_distance_ly: external_exports.number().positive().optional(),
|
|
47578
48149
|
preferences: PrefsPatchSchema.optional(),
|
|
47579
48150
|
use_preferences: external_exports.boolean().default(true),
|
|
47580
|
-
limit:
|
|
48151
|
+
limit: Limit2
|
|
47581
48152
|
}
|
|
47582
48153
|
},
|
|
47583
48154
|
wrap(
|
|
@@ -47611,7 +48182,7 @@ function registerSpanshTools(server, deps) {
|
|
|
47611
48182
|
openWorldHint: true
|
|
47612
48183
|
},
|
|
47613
48184
|
description: "Look up a system by exact name: coordinates, allegiance, permit, and its stations sorted by distance from arrival (limit applies to stations). Cached 24 h.",
|
|
47614
|
-
inputSchema: { name: external_exports.string(), limit:
|
|
48185
|
+
inputSchema: { name: external_exports.string(), limit: Limit2 }
|
|
47615
48186
|
},
|
|
47616
48187
|
wrap(async (a) => spansh.system(a.name, a.limit))
|
|
47617
48188
|
);
|
|
@@ -48230,6 +48801,13 @@ function createServer3(deps) {
|
|
|
48230
48801
|
currentShip: deps.currentShip,
|
|
48231
48802
|
jumpRange: deps.jumpRange
|
|
48232
48803
|
});
|
|
48804
|
+
registerSystemTools(server, {
|
|
48805
|
+
spansh: deps.spansh,
|
|
48806
|
+
journal: deps.journal,
|
|
48807
|
+
prefs: deps.prefs,
|
|
48808
|
+
currentShip: deps.currentShip,
|
|
48809
|
+
jumpRange: deps.jumpRange
|
|
48810
|
+
});
|
|
48233
48811
|
}
|
|
48234
48812
|
if (deps.catalog && deps.spansh) {
|
|
48235
48813
|
registerMaterialsTools(server, {
|