toolcraft 0.0.350 → 0.0.352
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/composition.json +3 -3
- package/dist/composition.json +3 -3
- package/node_modules/tiny-http-mcp-server/dist/composition.json +1 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/README.md +14 -2
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +12 -9
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +2 -0
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/loopback-authorization.js +7 -4
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resource-bound-store.js +9 -8
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js +4 -1
- package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-grant.js +4 -1
- package/node_modules/tiny-http-mcp-server/package.json +1 -1
- package/node_modules/tiny-mcp-client/README.md +5 -0
- package/node_modules/tiny-mcp-client/dist/index.js +43 -24
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
package/composition.json
CHANGED
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"name": "tiny-http-mcp-server",
|
|
101
|
-
"version": "0.1.
|
|
101
|
+
"version": "0.1.82",
|
|
102
102
|
"license": "MIT"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.352",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.352",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
package/dist/composition.json
CHANGED
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"name": "tiny-http-mcp-server",
|
|
101
|
-
"version": "0.1.
|
|
101
|
+
"version": "0.1.82",
|
|
102
102
|
"license": "MIT"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.352",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.352",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -123,6 +123,9 @@ A browser callback cannot redirect a later code exchange by changing caller
|
|
|
123
123
|
metadata. Metadata loaded from a host session store is copied before clock or
|
|
124
124
|
fetch callbacks, retaining the original refresh endpoint and persisted extensions. Explicit authentication owns metadata when lazy discovery returns;
|
|
125
125
|
the selected discovery method retains its original receiver and live host state.
|
|
126
|
+
Browser and standalone loopback snapshots retain declared redirects, deadlines,
|
|
127
|
+
cancellation and selected host hooks regardless of property enumerability.
|
|
128
|
+
Browser callback hooks retain their original receiver and live host state.
|
|
126
129
|
|
|
127
130
|
Provider request inputs accept an optional `signal`. It reaches callback waits,
|
|
128
131
|
registration, token requests and bounded token-body reads. Cancellation retains
|
|
@@ -171,7 +174,14 @@ refresh retains the previous granted set. Mismatched responses never activate
|
|
|
171
174
|
credentials; an unusable refresh response retains the pending refresh record.
|
|
172
175
|
`resourceIdentity` selects a native-owned logical server within its optional
|
|
173
176
|
persistence namespace. One encrypted, locked document owns that identity's
|
|
174
|
-
current resource URL, sessions and registrations.
|
|
177
|
+
current resource URL, sessions and registrations. Explicit imports and resets
|
|
178
|
+
reject fragment components, including an empty trailing `#`; imported protected-
|
|
179
|
+
resource metadata and stored client issuers obey the same fragment-free policy.
|
|
180
|
+
Issuer identifiers must also omit query components, including an empty `?`.
|
|
181
|
+
Direct and stored authorization flows enforce the same issuer URL policy before
|
|
182
|
+
consent or token redemption. New discovery identity and endpoint policy checks
|
|
183
|
+
run before acquiring the session transaction, so invalid replacement metadata
|
|
184
|
+
cannot retire a valid old grant. Escaped delimiter data stays valid. Changing the URL retires its
|
|
175
185
|
credentials permanently; returning to the old URL does not restore them.
|
|
176
186
|
Retired identities also withhold stale initial grants; authorize again or select
|
|
177
187
|
a fresh explicit profile to import a new grant. This option requires native persistence; custom
|
|
@@ -194,7 +204,9 @@ using the original configured client. Persisted sessions take precedence,
|
|
|
194
204
|
including sessions whose tokens have been cleared; an import cannot revive them.
|
|
195
205
|
Input tokens are copied before any host clock anchors a relative lifetime;
|
|
196
206
|
clock mutation cannot replace the selected access/refresh/scope values. Invalid
|
|
197
|
-
expiry values fail before authorization.
|
|
207
|
+
expiry values fail before authorization. Relative-lifetime clock anchors must
|
|
208
|
+
also be valid epoch milliseconds; adding the lifetime cannot make an invalid
|
|
209
|
+
issuance clock acceptable. This applies to raw-grant parsing and token responses.
|
|
198
210
|
For a raw OAuth response, `parseOAuthTokenGrant(response, { issuedAt, expiresAt })`
|
|
199
211
|
returns normalized `StoredOAuthTokens`. Timing options are captured before the
|
|
200
212
|
host clock runs, so it cannot bypass their earlier validation. The parser accepts
|
|
@@ -6,7 +6,7 @@ import { isIP } from "node:net";
|
|
|
6
6
|
import { fetchMcpResponse } from "../http-fetch.js";
|
|
7
7
|
import { URL } from "node:url";
|
|
8
8
|
import { createAuthStoreClientStore, createAuthStoreSessionStore, assertPersistenceNamespace } from "./auth-store-session-store.js";
|
|
9
|
-
import { createLoopbackAuthorizationSession, loopbackTarget } from "./loopback-authorization.js";
|
|
9
|
+
import { createLoopbackAuthorizationSession, loopbackTarget, snapshotLoopbackAuthorizationOptions } from "./loopback-authorization.js";
|
|
10
10
|
import { createAuthorizationState } from "./authorization-state.js";
|
|
11
11
|
import { generateCodeChallenge, generateCodeVerifier } from "./pkce.js";
|
|
12
12
|
import { exchangeAuthorizationCode, OAuthError, refreshAccessToken, isRetryableOAuthError, readOAuthJsonObjectResponse } from "./token-endpoint.js";
|
|
@@ -21,8 +21,7 @@ export function createOAuthClientProvider(options) {
|
|
|
21
21
|
return createDefaultOAuthClientProvider(options);
|
|
22
22
|
}
|
|
23
23
|
export function createDefaultOAuthClientProvider(options) {
|
|
24
|
-
const browser =
|
|
25
|
-
...(options.browser.landingPage === undefined ? {} : { landingPage: { ...options.browser.landingPage } }) };
|
|
24
|
+
const browser = snapshotLoopbackAuthorizationOptions(options.browser);
|
|
26
25
|
const clientMode = options.client.mode;
|
|
27
26
|
const interactiveEnabled = options.allowInteractive !== false;
|
|
28
27
|
const sessionLockTimeoutMs = options.sessionLockTimeoutMs;
|
|
@@ -172,6 +171,11 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
172
171
|
};
|
|
173
172
|
async function ensureAuthorizedSession(resource, discovery, fetch, allowInteractive, forceRefresh = false, signal, rejectedTokens) {
|
|
174
173
|
signal?.throwIfAborted();
|
|
174
|
+
if (discovery !== undefined) {
|
|
175
|
+
if (getOwnString(discovery.authorizationServerMetadata, "issuer") !== discovery.authorizationServer)
|
|
176
|
+
throw new Error("OAuth discovery authorization-server issuer mismatch");
|
|
177
|
+
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
178
|
+
}
|
|
175
179
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
176
180
|
return withOAuthSessionTransaction(sessionStore, canonicalResource, async () => {
|
|
177
181
|
let session = await loadSession(canonicalResource);
|
|
@@ -187,9 +191,6 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
187
191
|
if (session !== null && initialGrant?.resource === canonicalResource)
|
|
188
192
|
initialGrantConsumed = true;
|
|
189
193
|
signal?.throwIfAborted();
|
|
190
|
-
if (discovery !== undefined && getOwnString(discovery.authorizationServerMetadata, "issuer") !== discovery.authorizationServer) {
|
|
191
|
-
throw new Error("OAuth discovery authorization-server issuer mismatch");
|
|
192
|
-
}
|
|
193
194
|
if (session !== null && (canonicalizeResourceIndicator(session.resource) !== canonicalResource
|
|
194
195
|
|| getOwnString(session.discovery.authorizationServerMetadata, "issuer") !== session.authorizationServer
|
|
195
196
|
|| (discovery !== undefined && discovery.authorizationServer !== session.authorizationServer))) {
|
|
@@ -198,7 +199,6 @@ export function createDefaultOAuthClientProvider(options) {
|
|
|
198
199
|
}
|
|
199
200
|
if (session === null && discovery !== undefined && !initialGrantConsumed && initialGrant?.resource === canonicalResource &&
|
|
200
201
|
initialGrant.tokens !== undefined && initialGrant.client !== null) {
|
|
201
|
-
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
202
202
|
session = { resource: canonicalResource, authorizationServer: discovery.authorizationServer,
|
|
203
203
|
client: initialGrant.client, tokens: initialGrant.tokens,
|
|
204
204
|
...(requestedScope === undefined ? {} : { requestedScope }), discovery: toStoredDiscovery(discovery) };
|
|
@@ -766,14 +766,17 @@ function assertSecureUrl(value, label) {
|
|
|
766
766
|
throw new Error(`${label} must not include credentials or fragment`);
|
|
767
767
|
}
|
|
768
768
|
if (url.protocol === "https:") {
|
|
769
|
-
return;
|
|
769
|
+
return url;
|
|
770
770
|
}
|
|
771
771
|
if (url.protocol === "http:" && isLoopbackHostname(url.hostname)) {
|
|
772
|
-
return;
|
|
772
|
+
return url;
|
|
773
773
|
}
|
|
774
774
|
throw new Error(`${label} must use https unless it targets a loopback host`);
|
|
775
775
|
}
|
|
776
776
|
function assertSecureOAuthFlowEndpoints(metadata) {
|
|
777
|
+
const issuer = assertSecureUrl(requireOwnString(metadata, "issuer", "Authorization server metadata"), "Authorization server issuer");
|
|
778
|
+
if (issuer.href.includes("?"))
|
|
779
|
+
throw new Error("Authorization server issuer must not include query or fragment");
|
|
777
780
|
const authorizationEndpoint = requireOwnString(metadata, "authorization_endpoint", "Authorization server metadata");
|
|
778
781
|
const tokenEndpoint = requireOwnString(metadata, "token_endpoint", "Authorization server metadata");
|
|
779
782
|
const registrationEndpoint = getOwnString(metadata, "registration_endpoint");
|
|
@@ -20,6 +20,8 @@ export interface LoopbackAuthorizationSession {
|
|
|
20
20
|
waitForCode(authorizationUrl: string): Promise<string>;
|
|
21
21
|
close(): void;
|
|
22
22
|
}
|
|
23
|
+
/** Capture declared configuration regardless of property enumerability. */
|
|
24
|
+
export declare function snapshotLoopbackAuthorizationOptions(options: LoopbackAuthorizationOptions): LoopbackAuthorizationOptions;
|
|
23
25
|
/** Authorization callback denial, retaining the provider diagnostic for host observers. */
|
|
24
26
|
export declare class OAuthAuthorizationError extends Error {
|
|
25
27
|
readonly error: string;
|
|
@@ -2,6 +2,12 @@ import http from "node:http";
|
|
|
2
2
|
import { parseAuthorizationState } from "./authorization-state.js";
|
|
3
3
|
const authorizationErrorBrand = Symbol.for("poe-platform.mcp-oauth.OAuthAuthorizationError");
|
|
4
4
|
const oauthCallbackParameters = ["code", "state", "iss", "error", "error_description", "error_uri"];
|
|
5
|
+
/** Capture declared configuration regardless of property enumerability. */
|
|
6
|
+
export function snapshotLoopbackAuthorizationOptions(options) {
|
|
7
|
+
return { ...options, openBrowser: options.openBrowser?.bind(options), readLine: options.readLine?.bind(options), createServer: options.createServer?.bind(options),
|
|
8
|
+
callbackPath: options.callbackPath, redirectUri: options.redirectUri, signal: options.signal, timeoutMs: options.timeoutMs,
|
|
9
|
+
landingPage: options.landingPage === undefined ? undefined : { ...options.landingPage } };
|
|
10
|
+
}
|
|
5
11
|
/** Authorization callback denial, retaining the provider diagnostic for host observers. */
|
|
6
12
|
export class OAuthAuthorizationError extends Error {
|
|
7
13
|
error;
|
|
@@ -19,10 +25,7 @@ export class OAuthAuthorizationError extends Error {
|
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
27
|
export async function createLoopbackAuthorizationSession(options = {}) {
|
|
22
|
-
|
|
23
|
-
options = { ...selected, createServer: selected.createServer?.bind(options),
|
|
24
|
-
openBrowser: selected.openBrowser?.bind(options), readLine: selected.readLine?.bind(options),
|
|
25
|
-
landingPage: selected.landingPage === undefined ? undefined : { ...selected.landingPage } };
|
|
28
|
+
options = snapshotLoopbackAuthorizationOptions(options);
|
|
26
29
|
options.signal?.throwIfAborted();
|
|
27
30
|
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
28
31
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2_147_483_647)
|
package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/resource-bound-store.js
CHANGED
|
@@ -18,7 +18,7 @@ export function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
18
18
|
catch {
|
|
19
19
|
throw new Error("OAuth reset resource must be an absolute HTTP URL");
|
|
20
20
|
}
|
|
21
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.
|
|
21
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.href.includes("#"))
|
|
22
22
|
throw new Error("OAuth reset resource must be an HTTP URL without credentials or fragment");
|
|
23
23
|
await replace({ version: 1, resource: canonicalizeResourceIndicator(url), generation: 1, session: null, clients: {} }, options);
|
|
24
24
|
},
|
|
@@ -28,11 +28,12 @@ export function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
28
28
|
try {
|
|
29
29
|
if (!isStoredOAuthSession(session) || session.tokens === undefined || session.refreshState !== undefined)
|
|
30
30
|
throw new Error("Invalid session");
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
if (typeof session.discovery.resourceMetadata.resource !== "string")
|
|
32
|
+
throw new Error("Invalid resource metadata");
|
|
33
|
+
const resource = new URL(session.resource), issuer = new URL(session.authorizationServer), advertisedResource = new URL(session.discovery.resourceMetadata.resource);
|
|
34
|
+
if ([resource, issuer, advertisedResource].some(url => !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.href.includes("#")) ||
|
|
35
|
+
issuer.href.includes("?") || session.discovery.authorizationServerMetadata.issuer !== session.authorizationServer ||
|
|
36
|
+
canonicalizeResourceIndicator(advertisedResource) !== canonicalizeResourceIndicator(resource))
|
|
36
37
|
throw new Error("Invalid binding");
|
|
37
38
|
session.client = normalizeStoredOAuthClient(session.client);
|
|
38
39
|
if (session.client.registration !== undefined)
|
|
@@ -92,7 +93,7 @@ export function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
92
93
|
catch {
|
|
93
94
|
throw new Error("Invalid stored OAuth resource identity URL");
|
|
94
95
|
}
|
|
95
|
-
if (!["http:", "https:"].includes(resourceUrl.protocol) || resourceUrl.username || resourceUrl.password || resourceUrl.
|
|
96
|
+
if (!["http:", "https:"].includes(resourceUrl.protocol) || resourceUrl.username || resourceUrl.password || resourceUrl.href.includes("#"))
|
|
96
97
|
throw new Error("Invalid stored OAuth resource identity URL");
|
|
97
98
|
for (const [issuer, client] of Object.entries(record.clients)) {
|
|
98
99
|
const normalized = normalizeStoredOAuthClient(client);
|
|
@@ -103,7 +104,7 @@ export function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
103
104
|
catch {
|
|
104
105
|
throw new Error("Invalid stored OAuth resource client issuer");
|
|
105
106
|
}
|
|
106
|
-
if (normalized === null || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.
|
|
107
|
+
if (normalized === null || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.href.includes("?") || url.href.includes("#"))
|
|
107
108
|
throw new Error("Invalid stored OAuth resource client");
|
|
108
109
|
Object.defineProperty(record.clients, issuer, { value: normalized, enumerable: true, configurable: true, writable: true });
|
|
109
110
|
}
|
package/node_modules/tiny-http-mcp-server/node_modules/mcp-oauth/dist/client/token-endpoint.js
CHANGED
|
@@ -130,7 +130,10 @@ async function requestTokens(input) {
|
|
|
130
130
|
expiresIn < 0) {
|
|
131
131
|
throw new Error("OAuth token response has invalid expires_in");
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
const issuedAt = input.now();
|
|
134
|
+
if (!Number.isSafeInteger(issuedAt) || Math.abs(issuedAt) > MAX_JS_DATE_MS)
|
|
135
|
+
throw new Error("OAuth token response has invalid expires_in");
|
|
136
|
+
expiresAt = issuedAt + expiresIn * 1000;
|
|
134
137
|
if (!Number.isSafeInteger(expiresAt) ||
|
|
135
138
|
expiresAt > MAX_JS_DATE_MS ||
|
|
136
139
|
!Number.isFinite(new Date(expiresAt).getTime())) {
|
|
@@ -34,7 +34,10 @@ export function parseOAuthTokenGrant(value, options = {}) {
|
|
|
34
34
|
const absoluteSeconds = typeof seconds === "number" ? seconds * 1000 : undefined;
|
|
35
35
|
if (absoluteSeconds !== undefined && !validTimestamp(absoluteSeconds))
|
|
36
36
|
throw invalid();
|
|
37
|
-
const
|
|
37
|
+
const anchor = typeof lifetime === "number" ? options.issuedAt ?? (options.now ?? Date.now)() : undefined;
|
|
38
|
+
if (typeof lifetime === "number" && !validTimestamp(anchor))
|
|
39
|
+
throw invalid();
|
|
40
|
+
const relative = typeof lifetime === "number" ? anchor + lifetime * 1000 : undefined;
|
|
38
41
|
if (relative !== undefined && !validTimestamp(relative))
|
|
39
42
|
throw invalid();
|
|
40
43
|
const expiresAt = options.expiresAt ?? (typeof milliseconds === "number" ? milliseconds : undefined) ?? absoluteSeconds ?? relative ?? null;
|
|
@@ -63,6 +63,11 @@ embedded credentials or fragments; endpoint changes close the connection.
|
|
|
63
63
|
Empty fragment delimiters are rejected too; escaped hashes in an endpoint path
|
|
64
64
|
or query remain literal data.
|
|
65
65
|
Both transports accept the same headers, OAuth provider and response limits.
|
|
66
|
+
The original HTTP transport target must be an absolute HTTP/HTTPS URL without
|
|
67
|
+
credentials or a fragment, including an empty trailing `#`. Invalid targets fail
|
|
68
|
+
before OAuth provider construction or clock reads. Empty queries and escaped
|
|
69
|
+
path/query delimiters remain valid; accepted target strings keep their spelling.
|
|
70
|
+
|
|
66
71
|
Static HTTP headers are copied and validated when the transport is constructed,
|
|
67
72
|
before OAuth callbacks run. Invalid headers fail without reflecting their values.
|
|
68
73
|
Use `snapshotHttpTransportHeaders(headers)` to apply the same ownership and safe
|
|
@@ -3518,6 +3518,19 @@ function getOwnEntry(record2, key2) {
|
|
|
3518
3518
|
// ../mcp-oauth/dist/client/loopback-authorization.js
|
|
3519
3519
|
var authorizationErrorBrand = /* @__PURE__ */ Symbol.for("poe-platform.mcp-oauth.OAuthAuthorizationError");
|
|
3520
3520
|
var oauthCallbackParameters = ["code", "state", "iss", "error", "error_description", "error_uri"];
|
|
3521
|
+
function snapshotLoopbackAuthorizationOptions(options) {
|
|
3522
|
+
return {
|
|
3523
|
+
...options,
|
|
3524
|
+
openBrowser: options.openBrowser?.bind(options),
|
|
3525
|
+
readLine: options.readLine?.bind(options),
|
|
3526
|
+
createServer: options.createServer?.bind(options),
|
|
3527
|
+
callbackPath: options.callbackPath,
|
|
3528
|
+
redirectUri: options.redirectUri,
|
|
3529
|
+
signal: options.signal,
|
|
3530
|
+
timeoutMs: options.timeoutMs,
|
|
3531
|
+
landingPage: options.landingPage === void 0 ? void 0 : { ...options.landingPage }
|
|
3532
|
+
};
|
|
3533
|
+
}
|
|
3521
3534
|
var OAuthAuthorizationError = class extends Error {
|
|
3522
3535
|
error;
|
|
3523
3536
|
errorDescription;
|
|
@@ -3534,14 +3547,7 @@ var OAuthAuthorizationError = class extends Error {
|
|
|
3534
3547
|
}
|
|
3535
3548
|
};
|
|
3536
3549
|
async function createLoopbackAuthorizationSession(options = {}) {
|
|
3537
|
-
|
|
3538
|
-
options = {
|
|
3539
|
-
...selected,
|
|
3540
|
-
createServer: selected.createServer?.bind(options),
|
|
3541
|
-
openBrowser: selected.openBrowser?.bind(options),
|
|
3542
|
-
readLine: selected.readLine?.bind(options),
|
|
3543
|
-
landingPage: selected.landingPage === void 0 ? void 0 : { ...selected.landingPage }
|
|
3544
|
-
};
|
|
3550
|
+
options = snapshotLoopbackAuthorizationOptions(options);
|
|
3545
3551
|
options.signal?.throwIfAborted();
|
|
3546
3552
|
const timeoutMs = options.timeoutMs ?? 12e4;
|
|
3547
3553
|
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647)
|
|
@@ -4801,7 +4807,7 @@ function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
4801
4807
|
} catch {
|
|
4802
4808
|
throw new Error("OAuth reset resource must be an absolute HTTP URL");
|
|
4803
4809
|
}
|
|
4804
|
-
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.
|
|
4810
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.href.includes("#"))
|
|
4805
4811
|
throw new Error("OAuth reset resource must be an HTTP URL without credentials or fragment");
|
|
4806
4812
|
await replace({ version: 1, resource: canonicalizeResourceIndicator(url), generation: 1, session: null, clients: {} }, options2);
|
|
4807
4813
|
},
|
|
@@ -4811,8 +4817,10 @@ function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
4811
4817
|
try {
|
|
4812
4818
|
if (!isStoredOAuthSession(session) || session.tokens === void 0 || session.refreshState !== void 0)
|
|
4813
4819
|
throw new Error("Invalid session");
|
|
4814
|
-
|
|
4815
|
-
|
|
4820
|
+
if (typeof session.discovery.resourceMetadata.resource !== "string")
|
|
4821
|
+
throw new Error("Invalid resource metadata");
|
|
4822
|
+
const resource = new URL(session.resource), issuer = new URL(session.authorizationServer), advertisedResource = new URL(session.discovery.resourceMetadata.resource);
|
|
4823
|
+
if ([resource, issuer, advertisedResource].some((url) => !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.href.includes("#")) || issuer.href.includes("?") || session.discovery.authorizationServerMetadata.issuer !== session.authorizationServer || canonicalizeResourceIndicator(advertisedResource) !== canonicalizeResourceIndicator(resource))
|
|
4816
4824
|
throw new Error("Invalid binding");
|
|
4817
4825
|
session.client = normalizeStoredOAuthClient(session.client);
|
|
4818
4826
|
if (session.client.registration !== void 0)
|
|
@@ -4868,7 +4876,7 @@ function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
4868
4876
|
} catch {
|
|
4869
4877
|
throw new Error("Invalid stored OAuth resource identity URL");
|
|
4870
4878
|
}
|
|
4871
|
-
if (!["http:", "https:"].includes(resourceUrl.protocol) || resourceUrl.username || resourceUrl.password || resourceUrl.
|
|
4879
|
+
if (!["http:", "https:"].includes(resourceUrl.protocol) || resourceUrl.username || resourceUrl.password || resourceUrl.href.includes("#"))
|
|
4872
4880
|
throw new Error("Invalid stored OAuth resource identity URL");
|
|
4873
4881
|
for (const [issuer, client] of Object.entries(record2.clients)) {
|
|
4874
4882
|
const normalized = normalizeStoredOAuthClient(client);
|
|
@@ -4878,7 +4886,7 @@ function createResourceBoundOAuthStores(options, namespace, identity) {
|
|
|
4878
4886
|
} catch {
|
|
4879
4887
|
throw new Error("Invalid stored OAuth resource client issuer");
|
|
4880
4888
|
}
|
|
4881
|
-
if (normalized === null || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.
|
|
4889
|
+
if (normalized === null || !["http:", "https:"].includes(url.protocol) || url.username || url.password || url.href.includes("?") || url.href.includes("#"))
|
|
4882
4890
|
throw new Error("Invalid stored OAuth resource client");
|
|
4883
4891
|
Object.defineProperty(record2.clients, issuer, { value: normalized, enumerable: true, configurable: true, writable: true });
|
|
4884
4892
|
}
|
|
@@ -5178,7 +5186,10 @@ async function requestTokens(input) {
|
|
|
5178
5186
|
if (typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || !Number.isInteger(expiresIn) || expiresIn < 0) {
|
|
5179
5187
|
throw new Error("OAuth token response has invalid expires_in");
|
|
5180
5188
|
}
|
|
5181
|
-
|
|
5189
|
+
const issuedAt = input.now();
|
|
5190
|
+
if (!Number.isSafeInteger(issuedAt) || Math.abs(issuedAt) > MAX_JS_DATE_MS2)
|
|
5191
|
+
throw new Error("OAuth token response has invalid expires_in");
|
|
5192
|
+
expiresAt = issuedAt + expiresIn * 1e3;
|
|
5182
5193
|
if (!Number.isSafeInteger(expiresAt) || expiresAt > MAX_JS_DATE_MS2 || !Number.isFinite(new Date(expiresAt).getTime())) {
|
|
5183
5194
|
throw new Error("OAuth token response has invalid expires_in");
|
|
5184
5195
|
}
|
|
@@ -5316,10 +5327,7 @@ function createOAuthClientProvider(options) {
|
|
|
5316
5327
|
return createDefaultOAuthClientProvider(options);
|
|
5317
5328
|
}
|
|
5318
5329
|
function createDefaultOAuthClientProvider(options) {
|
|
5319
|
-
const browser =
|
|
5320
|
-
...options.browser,
|
|
5321
|
-
...options.browser.landingPage === void 0 ? {} : { landingPage: { ...options.browser.landingPage } }
|
|
5322
|
-
};
|
|
5330
|
+
const browser = snapshotLoopbackAuthorizationOptions(options.browser);
|
|
5323
5331
|
const clientMode = options.client.mode;
|
|
5324
5332
|
const interactiveEnabled = options.allowInteractive !== false;
|
|
5325
5333
|
const sessionLockTimeoutMs = options.sessionLockTimeoutMs;
|
|
@@ -5462,6 +5470,11 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5462
5470
|
};
|
|
5463
5471
|
async function ensureAuthorizedSession(resource, discovery, fetch2, allowInteractive, forceRefresh = false, signal, rejectedTokens) {
|
|
5464
5472
|
signal?.throwIfAborted();
|
|
5473
|
+
if (discovery !== void 0) {
|
|
5474
|
+
if (getOwnString2(discovery.authorizationServerMetadata, "issuer") !== discovery.authorizationServer)
|
|
5475
|
+
throw new Error("OAuth discovery authorization-server issuer mismatch");
|
|
5476
|
+
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
5477
|
+
}
|
|
5465
5478
|
const canonicalResource = canonicalizeResourceIndicator(resource);
|
|
5466
5479
|
return withOAuthSessionTransaction(sessionStore, canonicalResource, async () => {
|
|
5467
5480
|
let session = await loadSession(canonicalResource);
|
|
@@ -5477,15 +5490,11 @@ function createDefaultOAuthClientProvider(options) {
|
|
|
5477
5490
|
if (session !== null && initialGrant?.resource === canonicalResource)
|
|
5478
5491
|
initialGrantConsumed = true;
|
|
5479
5492
|
signal?.throwIfAborted();
|
|
5480
|
-
if (discovery !== void 0 && getOwnString2(discovery.authorizationServerMetadata, "issuer") !== discovery.authorizationServer) {
|
|
5481
|
-
throw new Error("OAuth discovery authorization-server issuer mismatch");
|
|
5482
|
-
}
|
|
5483
5493
|
if (session !== null && (canonicalizeResourceIndicator(session.resource) !== canonicalResource || getOwnString2(session.discovery.authorizationServerMetadata, "issuer") !== session.authorizationServer || discovery !== void 0 && discovery.authorizationServer !== session.authorizationServer)) {
|
|
5484
5494
|
await clearSession(canonicalResource);
|
|
5485
5495
|
session = null;
|
|
5486
5496
|
}
|
|
5487
5497
|
if (session === null && discovery !== void 0 && !initialGrantConsumed && initialGrant?.resource === canonicalResource && initialGrant.tokens !== void 0 && initialGrant.client !== null) {
|
|
5488
|
-
assertSecureOAuthFlowEndpoints(discovery.authorizationServerMetadata);
|
|
5489
5498
|
session = {
|
|
5490
5499
|
resource: canonicalResource,
|
|
5491
5500
|
authorizationServer: discovery.authorizationServer,
|
|
@@ -6021,14 +6030,17 @@ function assertSecureUrl(value, label) {
|
|
|
6021
6030
|
throw new Error(`${label} must not include credentials or fragment`);
|
|
6022
6031
|
}
|
|
6023
6032
|
if (url.protocol === "https:") {
|
|
6024
|
-
return;
|
|
6033
|
+
return url;
|
|
6025
6034
|
}
|
|
6026
6035
|
if (url.protocol === "http:" && isLoopbackHostname(url.hostname)) {
|
|
6027
|
-
return;
|
|
6036
|
+
return url;
|
|
6028
6037
|
}
|
|
6029
6038
|
throw new Error(`${label} must use https unless it targets a loopback host`);
|
|
6030
6039
|
}
|
|
6031
6040
|
function assertSecureOAuthFlowEndpoints(metadata) {
|
|
6041
|
+
const issuer = assertSecureUrl(requireOwnString(metadata, "issuer", "Authorization server metadata"), "Authorization server issuer");
|
|
6042
|
+
if (issuer.href.includes("?"))
|
|
6043
|
+
throw new Error("Authorization server issuer must not include query or fragment");
|
|
6032
6044
|
const authorizationEndpoint = requireOwnString(metadata, "authorization_endpoint", "Authorization server metadata");
|
|
6033
6045
|
const tokenEndpoint = requireOwnString(metadata, "token_endpoint", "Authorization server metadata");
|
|
6034
6046
|
const registrationEndpoint = getOwnString2(metadata, "registration_endpoint");
|
|
@@ -7747,6 +7759,13 @@ var HttpTransport = class {
|
|
|
7747
7759
|
}) {
|
|
7748
7760
|
if (!Number.isSafeInteger(maxResponseBytes) || maxResponseBytes < 1)
|
|
7749
7761
|
throw new Error("HTTP response byte limit must be a positive safe integer");
|
|
7762
|
+
try {
|
|
7763
|
+
const target = new URL(url);
|
|
7764
|
+
if (!["http:", "https:"].includes(target.protocol) || target.username || target.password || target.href.includes("#"))
|
|
7765
|
+
throw new Error("Invalid HTTP transport target");
|
|
7766
|
+
} catch {
|
|
7767
|
+
throw new Error("HTTP transport URL must be an absolute HTTP URL without credentials or fragment");
|
|
7768
|
+
}
|
|
7750
7769
|
this.maxResponseBytes = maxResponseBytes;
|
|
7751
7770
|
this.url = url;
|
|
7752
7771
|
this.mode = mode;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.352",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"yaml"
|
|
159
159
|
],
|
|
160
160
|
"optionalDependencies": {
|
|
161
|
-
"toolcraft-schema": "0.0.
|
|
161
|
+
"toolcraft-schema": "0.0.352",
|
|
162
162
|
"toolcraft-design": "*",
|
|
163
163
|
"@poe-code/frontmatter": "*",
|
|
164
164
|
"@poe-code/agent-mcp-config": "*",
|