@modwrench/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth.d.ts +46 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +143 -0
- package/dist/auth.js.map +1 -0
- package/dist/http.d.ts +55 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +211 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token shape persisted in the OS keychain after an OAuth flow.
|
|
3
|
+
* Stored as a JSON-stringified string under one keychain entry per service.
|
|
4
|
+
*/
|
|
5
|
+
export type StoredToken = {
|
|
6
|
+
access_token: string;
|
|
7
|
+
refresh_token?: string;
|
|
8
|
+
expires_at: number | null;
|
|
9
|
+
saved_at: number;
|
|
10
|
+
};
|
|
11
|
+
type KeychainStatus = "unknown" | "available" | "unavailable";
|
|
12
|
+
/**
|
|
13
|
+
* Public status of the OS keychain integration on this system. Useful for
|
|
14
|
+
* tools that want to surface a clearer error or recommend the env-var path
|
|
15
|
+
* proactively.
|
|
16
|
+
*/
|
|
17
|
+
export declare function getKeychainStatus(): KeychainStatus;
|
|
18
|
+
export declare function getStoredToken(name: string): StoredToken | null;
|
|
19
|
+
export declare function setStoredToken(name: string, token: StoredToken): void;
|
|
20
|
+
export declare function deleteStoredToken(name: string): boolean;
|
|
21
|
+
export type Credential = {
|
|
22
|
+
source: "keychain";
|
|
23
|
+
accessToken: string;
|
|
24
|
+
refreshToken?: string;
|
|
25
|
+
expiresAt: number | null;
|
|
26
|
+
} | {
|
|
27
|
+
source: "env";
|
|
28
|
+
apiKey: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Resolve a credential by trying the OS keychain first, then an env var, then
|
|
32
|
+
* failing with a hint that points the user at `auth login`. The shape of the
|
|
33
|
+
* returned credential differs by source — callers should branch on `source`
|
|
34
|
+
* to decide which auth header (Bearer vs platform-specific) to send.
|
|
35
|
+
*
|
|
36
|
+
* If the keychain is unavailable (Steam Deck Game Mode, headless Linux, etc.)
|
|
37
|
+
* the thrown error explicitly says so rather than just reporting "no credential
|
|
38
|
+
* found" — which helps users fix the actual problem.
|
|
39
|
+
*/
|
|
40
|
+
export declare function loadCredential(opts: {
|
|
41
|
+
service: string;
|
|
42
|
+
envVar: string;
|
|
43
|
+
authHint: string;
|
|
44
|
+
}): Credential;
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAQF,KAAK,cAAc,GAAG,SAAS,GAAG,WAAW,GAAG,aAAa,CAAC;AAI9D;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,CAElD;AA8CD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAc/D;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAkBrE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAavD;AAMD,MAAM,MAAM,UAAU,GAClB;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GACD;IAAE,MAAM,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,UAAU,CAyBb"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Entry } from "@napi-rs/keyring";
|
|
2
|
+
// ─── Keychain storage ─────────────────────────────────────────────────────────
|
|
3
|
+
// Tokens land in the OS-native secrets store: Windows Credential Manager,
|
|
4
|
+
// macOS Keychain, or Linux libsecret. Service identifiers are prefixed with
|
|
5
|
+
// "modwrench-" so a user inspecting their keychain sees a recognizable owner.
|
|
6
|
+
//
|
|
7
|
+
// On Linux specifically — and especially on Steam Deck Game Mode or headless
|
|
8
|
+
// installs — libsecret / D-Bus may not be available. We detect that case and
|
|
9
|
+
// surface a clear error message instead of silently sending the user to env
|
|
10
|
+
// vars without explanation.
|
|
11
|
+
const KEYCHAIN_ACCOUNT = "default";
|
|
12
|
+
function service(name) {
|
|
13
|
+
return `modwrench-${name}`;
|
|
14
|
+
}
|
|
15
|
+
let keychainStatus = "unknown";
|
|
16
|
+
let unavailableWarningEmitted = false;
|
|
17
|
+
/**
|
|
18
|
+
* Public status of the OS keychain integration on this system. Useful for
|
|
19
|
+
* tools that want to surface a clearer error or recommend the env-var path
|
|
20
|
+
* proactively.
|
|
21
|
+
*/
|
|
22
|
+
export function getKeychainStatus() {
|
|
23
|
+
return keychainStatus;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Classify a keychain error as either "no entry exists" (the keychain works,
|
|
27
|
+
* we just haven't stored anything yet) or "unavailable" (the keychain service
|
|
28
|
+
* itself can't be reached). The distinction matters because the first case
|
|
29
|
+
* is normal and the second case usually means the user is on Steam Deck Game
|
|
30
|
+
* Mode, a headless Linux box, a container, or otherwise lacks libsecret.
|
|
31
|
+
*/
|
|
32
|
+
function classifyKeychainError(err) {
|
|
33
|
+
const msg = err instanceof Error ? err.message.toLowerCase() : String(err).toLowerCase();
|
|
34
|
+
if (msg.includes("d-bus") ||
|
|
35
|
+
msg.includes("dbus") ||
|
|
36
|
+
msg.includes("libsecret") ||
|
|
37
|
+
msg.includes("schema") ||
|
|
38
|
+
msg.includes("no such file") ||
|
|
39
|
+
msg.includes("could not connect") ||
|
|
40
|
+
msg.includes("not running") ||
|
|
41
|
+
msg.includes("session bus")) {
|
|
42
|
+
return "unavailable";
|
|
43
|
+
}
|
|
44
|
+
return "no-entry";
|
|
45
|
+
}
|
|
46
|
+
function emitUnavailableWarningOnce(err) {
|
|
47
|
+
if (unavailableWarningEmitted)
|
|
48
|
+
return;
|
|
49
|
+
unavailableWarningEmitted = true;
|
|
50
|
+
// Write directly to stderr — avoid importing log() from index.ts which would
|
|
51
|
+
// create a circular dependency back to this module.
|
|
52
|
+
process.stderr.write(JSON.stringify({
|
|
53
|
+
ts: new Date().toISOString(),
|
|
54
|
+
level: "warn",
|
|
55
|
+
msg: "OS keychain unavailable on this system; falling back to env vars",
|
|
56
|
+
hint: "Common on Steam Deck Game Mode, headless Linux, or systems without " +
|
|
57
|
+
"libsecret/D-Bus. Set the platform's API_KEY env var in your .env to " +
|
|
58
|
+
"use the legacy auth path instead.",
|
|
59
|
+
error: err instanceof Error ? err.message : String(err),
|
|
60
|
+
}) + "\n");
|
|
61
|
+
}
|
|
62
|
+
export function getStoredToken(name) {
|
|
63
|
+
try {
|
|
64
|
+
const entry = new Entry(service(name), KEYCHAIN_ACCOUNT);
|
|
65
|
+
const raw = entry.getPassword();
|
|
66
|
+
keychainStatus = "available";
|
|
67
|
+
if (!raw)
|
|
68
|
+
return null;
|
|
69
|
+
return JSON.parse(raw);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (classifyKeychainError(err) === "unavailable") {
|
|
73
|
+
keychainStatus = "unavailable";
|
|
74
|
+
emitUnavailableWarningOnce(err);
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function setStoredToken(name, token) {
|
|
80
|
+
try {
|
|
81
|
+
const entry = new Entry(service(name), KEYCHAIN_ACCOUNT);
|
|
82
|
+
entry.setPassword(JSON.stringify(token));
|
|
83
|
+
keychainStatus = "available";
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (classifyKeychainError(err) === "unavailable") {
|
|
87
|
+
keychainStatus = "unavailable";
|
|
88
|
+
throw new Error(`[modwrench/core] Cannot save credential: OS keychain unavailable. ` +
|
|
89
|
+
`This is common on Steam Deck Game Mode, headless Linux, or systems ` +
|
|
90
|
+
`without libsecret/D-Bus. Use the legacy API-key path instead — set ` +
|
|
91
|
+
`the platform's API_KEY env var in your .env. ` +
|
|
92
|
+
`Original error: ${err instanceof Error ? err.message : String(err)}`);
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export function deleteStoredToken(name) {
|
|
98
|
+
try {
|
|
99
|
+
const entry = new Entry(service(name), KEYCHAIN_ACCOUNT);
|
|
100
|
+
const removed = entry.deletePassword();
|
|
101
|
+
keychainStatus = "available";
|
|
102
|
+
return removed;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
if (classifyKeychainError(err) === "unavailable") {
|
|
106
|
+
keychainStatus = "unavailable";
|
|
107
|
+
emitUnavailableWarningOnce(err);
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolve a credential by trying the OS keychain first, then an env var, then
|
|
114
|
+
* failing with a hint that points the user at `auth login`. The shape of the
|
|
115
|
+
* returned credential differs by source — callers should branch on `source`
|
|
116
|
+
* to decide which auth header (Bearer vs platform-specific) to send.
|
|
117
|
+
*
|
|
118
|
+
* If the keychain is unavailable (Steam Deck Game Mode, headless Linux, etc.)
|
|
119
|
+
* the thrown error explicitly says so rather than just reporting "no credential
|
|
120
|
+
* found" — which helps users fix the actual problem.
|
|
121
|
+
*/
|
|
122
|
+
export function loadCredential(opts) {
|
|
123
|
+
const stored = getStoredToken(opts.service);
|
|
124
|
+
if (stored && stored.access_token) {
|
|
125
|
+
return {
|
|
126
|
+
source: "keychain",
|
|
127
|
+
accessToken: stored.access_token,
|
|
128
|
+
refreshToken: stored.refresh_token,
|
|
129
|
+
expiresAt: stored.expires_at,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const env = process.env[opts.envVar];
|
|
133
|
+
if (env && env.trim() !== "") {
|
|
134
|
+
return { source: "env", apiKey: env };
|
|
135
|
+
}
|
|
136
|
+
const keychainNote = keychainStatus === "unavailable"
|
|
137
|
+
? "OS keychain is unavailable on this system (likely libsecret/D-Bus missing — common on Steam Deck Game Mode or headless Linux). "
|
|
138
|
+
: "Tried OS keychain and ";
|
|
139
|
+
throw new Error(`[modwrench/core] No credential found for "${opts.service}". ` +
|
|
140
|
+
`${keychainNote}env var ${opts.envVar} is not set. ` +
|
|
141
|
+
opts.authHint);
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type RetryConfig = {
|
|
2
|
+
/** Total request attempts (1 = no retry). Default 3. */
|
|
3
|
+
maxAttempts?: number;
|
|
4
|
+
/** First backoff delay in ms when no Retry-After header is present. Default 250. */
|
|
5
|
+
initialDelayMs?: number;
|
|
6
|
+
/** Upper bound for backoff in ms (excluding server-supplied Retry-After). Default 8000. */
|
|
7
|
+
maxDelayMs?: number;
|
|
8
|
+
};
|
|
9
|
+
export type HttpClientOptions = {
|
|
10
|
+
/** Required. Used as the base for relative paths in request(). */
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
/** Required. Set the User-Agent string for every outbound request. */
|
|
13
|
+
userAgent: string;
|
|
14
|
+
/**
|
|
15
|
+
* Called per request. Return headers like `{ Authorization: "Bearer ..." }`
|
|
16
|
+
* or `{ apikey: "..." }`. Return an empty object if no auth is required
|
|
17
|
+
* (e.g. Thunderstore reads).
|
|
18
|
+
*/
|
|
19
|
+
authHeaders?: () => Record<string, string>;
|
|
20
|
+
/** Max concurrent in-flight requests. Default 4. */
|
|
21
|
+
concurrencyLimit?: number;
|
|
22
|
+
/** Tuning for retries. */
|
|
23
|
+
retry?: RetryConfig;
|
|
24
|
+
/** Optional default headers attached to every request (besides UA + auth). */
|
|
25
|
+
defaultHeaders?: Record<string, string>;
|
|
26
|
+
/**
|
|
27
|
+
* Optional override for the error-code prefix in thrown ModWrenchErrors.
|
|
28
|
+
* Defaults to "http". Set to e.g. "nexus" so callers see codes like
|
|
29
|
+
* "nexus_http_error" rather than "http_error".
|
|
30
|
+
*/
|
|
31
|
+
errorCodePrefix?: string;
|
|
32
|
+
};
|
|
33
|
+
export type RequestInit = {
|
|
34
|
+
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
35
|
+
/**
|
|
36
|
+
* Query string parameters. Undefined/null/empty values are dropped (so
|
|
37
|
+
* platforms can pass optional zod-defaulted args directly).
|
|
38
|
+
*/
|
|
39
|
+
query?: Record<string, string | number | boolean | undefined | null>;
|
|
40
|
+
headers?: Record<string, string>;
|
|
41
|
+
body?: string;
|
|
42
|
+
};
|
|
43
|
+
export type HttpClient = {
|
|
44
|
+
request<T>(path: string, init?: RequestInit): Promise<T>;
|
|
45
|
+
/** Number of requests currently waiting on the concurrency gate. */
|
|
46
|
+
inFlight(): number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Parse a Retry-After header value. Per RFC 9110 §10.2.3 the value is either
|
|
50
|
+
* an integer number of seconds or an HTTP-date. Returns delay in ms, or null
|
|
51
|
+
* if the value couldn't be parsed (caller falls back to its own backoff).
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseRetryAfter(value: string | null): number | null;
|
|
54
|
+
export declare function createHttpClient(opts: HttpClientOptions): HttpClient;
|
|
55
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,WAAW,GAAG;IACxB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,oDAAoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0BAA0B;IAC1B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IACrD;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzD,oEAAoE;IACpE,QAAQ,IAAI,MAAM,CAAC;CACpB,CAAC;AAqCF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAanE;AAwDD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,UAAU,CA6GpE"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// Shared HTTP client for every MCPwrench platform package. Centralizes the
|
|
2
|
+
// concerns that each platform shouldn't re-implement individually:
|
|
3
|
+
//
|
|
4
|
+
// - 429 handling with Retry-After parsing (seconds OR HTTP date)
|
|
5
|
+
// - Exponential backoff on transient 5xx errors
|
|
6
|
+
// - Per-client concurrency cap so we never hammer a platform with parallel
|
|
7
|
+
// requests
|
|
8
|
+
// - User-Agent injection
|
|
9
|
+
// - Caller-supplied auth-header callback (keeps Bearer vs apikey routing
|
|
10
|
+
// decisions in the platform package, not in core)
|
|
11
|
+
// - Structured ModWrenchError on non-retryable failures
|
|
12
|
+
//
|
|
13
|
+
// Design rule: this client is the "polite citizen" tier. It reacts to 429s
|
|
14
|
+
// and 5xxs but does NOT do proactive rate-limit window tracking via
|
|
15
|
+
// X-RL-* response headers — different platforms expose different shapes for
|
|
16
|
+
// those and that's a v2 concern. The current 429-reactive behavior is what
|
|
17
|
+
// keeps us off blocklists.
|
|
18
|
+
import { ModWrenchError } from "./index.js";
|
|
19
|
+
// ─── Internals ─────────────────────────────────────────────────────────────
|
|
20
|
+
/**
|
|
21
|
+
* Tiny async semaphore that caps concurrent operations. Each acquire() returns
|
|
22
|
+
* a release function; callers must always release in a finally block.
|
|
23
|
+
*/
|
|
24
|
+
function createGate(limit) {
|
|
25
|
+
let active = 0;
|
|
26
|
+
const waiters = [];
|
|
27
|
+
function acquire() {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const tryProceed = () => {
|
|
30
|
+
if (active < limit) {
|
|
31
|
+
active++;
|
|
32
|
+
resolve(() => {
|
|
33
|
+
active--;
|
|
34
|
+
const next = waiters.shift();
|
|
35
|
+
if (next)
|
|
36
|
+
next();
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
waiters.push(tryProceed);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
tryProceed();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return { acquire, inFlight: () => active + waiters.length };
|
|
47
|
+
}
|
|
48
|
+
function sleep(ms) {
|
|
49
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse a Retry-After header value. Per RFC 9110 §10.2.3 the value is either
|
|
53
|
+
* an integer number of seconds or an HTTP-date. Returns delay in ms, or null
|
|
54
|
+
* if the value couldn't be parsed (caller falls back to its own backoff).
|
|
55
|
+
*/
|
|
56
|
+
export function parseRetryAfter(value) {
|
|
57
|
+
if (!value)
|
|
58
|
+
return null;
|
|
59
|
+
const trimmed = value.trim();
|
|
60
|
+
// Seconds (most common — Nexus, mod.io, GitHub all use this form).
|
|
61
|
+
if (/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
62
|
+
return Math.max(0, Number(trimmed) * 1000);
|
|
63
|
+
}
|
|
64
|
+
// HTTP-date — preserved per spec even if rarely seen on these APIs.
|
|
65
|
+
const dateMs = Date.parse(trimmed);
|
|
66
|
+
if (!Number.isNaN(dateMs)) {
|
|
67
|
+
return Math.max(0, dateMs - Date.now());
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/** Exponential backoff with jitter, clamped to maxDelayMs. */
|
|
72
|
+
function computeBackoff(attempt, initial, max) {
|
|
73
|
+
const expo = initial * Math.pow(2, attempt - 1);
|
|
74
|
+
const jittered = expo * (0.5 + Math.random());
|
|
75
|
+
return Math.min(jittered, max);
|
|
76
|
+
}
|
|
77
|
+
function buildUrl(baseUrl, path, query) {
|
|
78
|
+
const full = path.startsWith("http://") || path.startsWith("https://")
|
|
79
|
+
? path
|
|
80
|
+
: `${baseUrl}${path}`;
|
|
81
|
+
if (!query || Object.keys(query).length === 0)
|
|
82
|
+
return full;
|
|
83
|
+
const params = new URLSearchParams();
|
|
84
|
+
for (const [k, v] of Object.entries(query)) {
|
|
85
|
+
if (v === undefined || v === null || v === "")
|
|
86
|
+
continue;
|
|
87
|
+
params.append(k, String(v));
|
|
88
|
+
}
|
|
89
|
+
const qs = params.toString();
|
|
90
|
+
if (!qs)
|
|
91
|
+
return full;
|
|
92
|
+
return full.includes("?") ? `${full}&${qs}` : `${full}?${qs}`;
|
|
93
|
+
}
|
|
94
|
+
const SENSITIVE_QUERY_KEYS = new Set([
|
|
95
|
+
"access_token",
|
|
96
|
+
"api_key",
|
|
97
|
+
"apikey",
|
|
98
|
+
"authorization",
|
|
99
|
+
"client_secret",
|
|
100
|
+
"password",
|
|
101
|
+
"refresh_token",
|
|
102
|
+
"secret",
|
|
103
|
+
"token",
|
|
104
|
+
]);
|
|
105
|
+
function redactUrl(value) {
|
|
106
|
+
try {
|
|
107
|
+
const url = new URL(value);
|
|
108
|
+
for (const key of Array.from(url.searchParams.keys())) {
|
|
109
|
+
if (SENSITIVE_QUERY_KEYS.has(key.toLowerCase())) {
|
|
110
|
+
url.searchParams.set(key, "[redacted]");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return url.toString();
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// ─── Public factory ────────────────────────────────────────────────────────
|
|
120
|
+
export function createHttpClient(opts) {
|
|
121
|
+
const concurrency = opts.concurrencyLimit ?? 4;
|
|
122
|
+
const gate = createGate(concurrency);
|
|
123
|
+
const maxAttempts = opts.retry?.maxAttempts ?? 3;
|
|
124
|
+
const initialDelayMs = opts.retry?.initialDelayMs ?? 250;
|
|
125
|
+
const maxDelayMs = opts.retry?.maxDelayMs ?? 8000;
|
|
126
|
+
const errorPrefix = opts.errorCodePrefix ?? "http";
|
|
127
|
+
async function request(path, init = {}) {
|
|
128
|
+
const url = buildUrl(opts.baseUrl, path, init.query);
|
|
129
|
+
const safeUrl = redactUrl(url);
|
|
130
|
+
const method = init.method ?? "GET";
|
|
131
|
+
const auth = opts.authHeaders?.() ?? {};
|
|
132
|
+
const headers = {
|
|
133
|
+
"User-Agent": opts.userAgent,
|
|
134
|
+
Accept: "application/json",
|
|
135
|
+
...(opts.defaultHeaders ?? {}),
|
|
136
|
+
...auth,
|
|
137
|
+
...(init.headers ?? {}),
|
|
138
|
+
};
|
|
139
|
+
const release = await gate.acquire();
|
|
140
|
+
try {
|
|
141
|
+
let lastError = null;
|
|
142
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
143
|
+
let response;
|
|
144
|
+
try {
|
|
145
|
+
const fetchInit = { method, headers };
|
|
146
|
+
if (init.body !== undefined)
|
|
147
|
+
fetchInit.body = init.body;
|
|
148
|
+
response = await fetch(url, fetchInit);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
// Network-level failure (DNS, connection reset, etc.). Treat as
|
|
152
|
+
// retryable up to maxAttempts.
|
|
153
|
+
lastError = err;
|
|
154
|
+
if (attempt < maxAttempts) {
|
|
155
|
+
await sleep(computeBackoff(attempt, initialDelayMs, maxDelayMs));
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
throw new ModWrenchError(`${errorPrefix}_network_error`, `Network error contacting ${safeUrl}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
159
|
+
}
|
|
160
|
+
if (response.ok) {
|
|
161
|
+
// Most platform APIs return JSON; some return empty body (204).
|
|
162
|
+
// Treat empty as null so callers can branch on it cleanly.
|
|
163
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
164
|
+
if (response.status === 204 || !ct.includes("json")) {
|
|
165
|
+
// Best-effort: try JSON parse, fall back to raw text in result.
|
|
166
|
+
const txt = await response.text().catch(() => "");
|
|
167
|
+
if (!txt)
|
|
168
|
+
return null;
|
|
169
|
+
try {
|
|
170
|
+
return JSON.parse(txt);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return txt;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return (await response.json());
|
|
177
|
+
}
|
|
178
|
+
// 429 → respect Retry-After if present, else exponential backoff.
|
|
179
|
+
if (response.status === 429 && attempt < maxAttempts) {
|
|
180
|
+
const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
|
|
181
|
+
const delay = retryAfter ?? computeBackoff(attempt, initialDelayMs, maxDelayMs);
|
|
182
|
+
await sleep(delay);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
// 5xx → retry with exponential backoff up to maxAttempts.
|
|
186
|
+
if (response.status >= 500 && attempt < maxAttempts) {
|
|
187
|
+
await sleep(computeBackoff(attempt, initialDelayMs, maxDelayMs));
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
// Non-retryable HTTP error. Read body for diagnostics and throw.
|
|
191
|
+
const body = await response.text().catch(() => "<no body>");
|
|
192
|
+
throw new ModWrenchError(`${errorPrefix}_http_error`, `HTTP ${response.status} for ${method} ${path}`, {
|
|
193
|
+
status: response.status,
|
|
194
|
+
meta: { body: body.slice(0, 500), url: safeUrl },
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// Exhausted retries on the retryable path (429 / 5xx that never
|
|
198
|
+
// recovered). The last response's body isn't useful here so we keep
|
|
199
|
+
// it generic.
|
|
200
|
+
throw new ModWrenchError(`${errorPrefix}_retries_exhausted`, `Exhausted ${maxAttempts} attempts for ${method} ${path}`, { cause: lastError });
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
release();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
request,
|
|
208
|
+
inFlight: gate.inFlight,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retrieve a secret from environment variables.
|
|
3
|
+
* Throws a clear error if the secret is missing — fail fast, fail loud.
|
|
4
|
+
*/
|
|
5
|
+
export declare function getSecret(name: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Retrieve an optional environment variable with a fallback default.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getEnv(name: string, fallback: string): string;
|
|
10
|
+
export declare function redactSensitiveText(value: string): string;
|
|
11
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
12
|
+
/**
|
|
13
|
+
* Minimal structured logger. Writes JSON to stderr so it never interferes
|
|
14
|
+
* with MCP stdio protocol traffic (which uses stdout).
|
|
15
|
+
*/
|
|
16
|
+
export declare function log(level: LogLevel, message: string, meta?: Record<string, unknown>): void;
|
|
17
|
+
export * from "./auth.js";
|
|
18
|
+
export * from "./http.js";
|
|
19
|
+
/**
|
|
20
|
+
* Error type used across ModWrench servers for predictable error envelopes.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ModWrenchError extends Error {
|
|
23
|
+
readonly code: string;
|
|
24
|
+
readonly status?: number;
|
|
25
|
+
readonly meta?: Record<string, unknown>;
|
|
26
|
+
constructor(code: string, message: string, options?: {
|
|
27
|
+
status?: number;
|
|
28
|
+
meta?: Record<string, unknown>;
|
|
29
|
+
cause?: unknown;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwCA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS9C;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG7D;AAmBD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAczD;AAID,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAW3D;;;GAGG;AACH,wBAAgB,GAAG,CACjB,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,IAAI,CASN;AAID,cAAc,WAAW,CAAC;AAI1B,cAAc,WAAW,CAAC;AAI1B;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChC,SAAgB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAG7C,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAQjF"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { config as loadDotenv } from "dotenv";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
// ─── Locate the workspace root ────────────────────────────────────────────────
|
|
6
|
+
// Walk up from this file's location until we find a package.json that declares
|
|
7
|
+
// "workspaces" — that's the monorepo root, regardless of where the process was
|
|
8
|
+
// launched from. Falls back to process.cwd() if no workspace marker is found
|
|
9
|
+
// (which lets the core package work fine even outside a monorepo).
|
|
10
|
+
function findWorkspaceRoot(startDir) {
|
|
11
|
+
let current = startDir;
|
|
12
|
+
// Safety bound: don't walk forever even on weird filesystems.
|
|
13
|
+
for (let i = 0; i < 20; i++) {
|
|
14
|
+
const pkgPath = resolve(current, "package.json");
|
|
15
|
+
if (existsSync(pkgPath)) {
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
18
|
+
if (pkg && Array.isArray(pkg.workspaces)) {
|
|
19
|
+
return current;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Ignore malformed package.json and keep walking.
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const parent = dirname(current);
|
|
27
|
+
if (parent === current)
|
|
28
|
+
break; // hit filesystem root
|
|
29
|
+
current = parent;
|
|
30
|
+
}
|
|
31
|
+
return startDir;
|
|
32
|
+
}
|
|
33
|
+
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const WORKSPACE_ROOT = findWorkspaceRoot(HERE);
|
|
35
|
+
loadDotenv({ path: resolve(WORKSPACE_ROOT, ".env") });
|
|
36
|
+
// ─── Secret & env helpers ─────────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Retrieve a secret from environment variables.
|
|
39
|
+
* Throws a clear error if the secret is missing — fail fast, fail loud.
|
|
40
|
+
*/
|
|
41
|
+
export function getSecret(name) {
|
|
42
|
+
const value = process.env[name];
|
|
43
|
+
if (!value || value.trim() === "") {
|
|
44
|
+
throw new Error(`[modwrench/core] Missing required secret: ${name}. ` +
|
|
45
|
+
`Check your .env file at the workspace root (${WORKSPACE_ROOT}).`);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Retrieve an optional environment variable with a fallback default.
|
|
51
|
+
*/
|
|
52
|
+
export function getEnv(name, fallback) {
|
|
53
|
+
const value = process.env[name];
|
|
54
|
+
return value && value.trim() !== "" ? value : fallback;
|
|
55
|
+
}
|
|
56
|
+
const SENSITIVE_TEXT_KEYS = [
|
|
57
|
+
"access_token",
|
|
58
|
+
"api_key",
|
|
59
|
+
"apikey",
|
|
60
|
+
"authorization",
|
|
61
|
+
"client_secret",
|
|
62
|
+
"password",
|
|
63
|
+
"refresh_token",
|
|
64
|
+
"secret",
|
|
65
|
+
"security_code",
|
|
66
|
+
"token",
|
|
67
|
+
];
|
|
68
|
+
function escapeRegExp(value) {
|
|
69
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
70
|
+
}
|
|
71
|
+
export function redactSensitiveText(value) {
|
|
72
|
+
let redacted = value;
|
|
73
|
+
for (const key of SENSITIVE_TEXT_KEYS) {
|
|
74
|
+
const escaped = escapeRegExp(key);
|
|
75
|
+
redacted = redacted.replace(new RegExp(`("${escaped}"\\s*:\\s*")([^"]*)(")`, "gi"), "$1[redacted]$3");
|
|
76
|
+
redacted = redacted.replace(new RegExp(`(\\b${escaped}\\b\\s*[=:]\\s*)([^&\\s"']+)`, "gi"), "$1[redacted]");
|
|
77
|
+
}
|
|
78
|
+
return redacted;
|
|
79
|
+
}
|
|
80
|
+
const LEVEL_RANK = {
|
|
81
|
+
debug: 10,
|
|
82
|
+
info: 20,
|
|
83
|
+
warn: 30,
|
|
84
|
+
error: 40,
|
|
85
|
+
};
|
|
86
|
+
const currentLevel = getEnv("LOG_LEVEL", "info") ?? "info";
|
|
87
|
+
/**
|
|
88
|
+
* Minimal structured logger. Writes JSON to stderr so it never interferes
|
|
89
|
+
* with MCP stdio protocol traffic (which uses stdout).
|
|
90
|
+
*/
|
|
91
|
+
export function log(level, message, meta) {
|
|
92
|
+
if (LEVEL_RANK[level] < LEVEL_RANK[currentLevel])
|
|
93
|
+
return;
|
|
94
|
+
const entry = {
|
|
95
|
+
ts: new Date().toISOString(),
|
|
96
|
+
level,
|
|
97
|
+
msg: message,
|
|
98
|
+
...(meta ?? {}),
|
|
99
|
+
};
|
|
100
|
+
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
101
|
+
}
|
|
102
|
+
// ─── Auth & keychain ──────────────────────────────────────────────────────────
|
|
103
|
+
export * from "./auth.js";
|
|
104
|
+
// ─── Shared HTTP client ───────────────────────────────────────────────────────
|
|
105
|
+
export * from "./http.js";
|
|
106
|
+
// ─── Error type ───────────────────────────────────────────────────────────────
|
|
107
|
+
/**
|
|
108
|
+
* Error type used across ModWrench servers for predictable error envelopes.
|
|
109
|
+
*/
|
|
110
|
+
export class ModWrenchError extends Error {
|
|
111
|
+
code;
|
|
112
|
+
status;
|
|
113
|
+
meta;
|
|
114
|
+
constructor(code, message, options) {
|
|
115
|
+
super(message, options?.cause ? { cause: options.cause } : undefined);
|
|
116
|
+
this.name = "ModWrenchError";
|
|
117
|
+
this.code = code;
|
|
118
|
+
this.status = options?.status;
|
|
119
|
+
this.meta = options?.meta;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@modwrench/core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Shared core library for ModWrench servers — auth, secrets, logging, errors.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/171county/modwrench.git",
|
|
9
|
+
"directory": "packages/core"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/171county/modwrench#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/171county/modwrench/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.js",
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"dev": "tsc --watch",
|
|
33
|
+
"clean": "rimraf dist",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "node --import tsx --test test/*.test.ts"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@napi-rs/keyring": "^1.1.7",
|
|
39
|
+
"dotenv": "^16.4.5"
|
|
40
|
+
}
|
|
41
|
+
}
|