terminal-commands 0.1.3 → 0.2.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 +101 -4
- package/dist/agent.js +626 -124
- package/dist/cli.js +875 -140
- package/dist/index.js +1064 -357
- package/dist/native/GO-LICENSE +27 -0
- package/dist/native/darwin-arm64/fs-helper +0 -0
- package/dist/native/darwin-x64/fs-helper +0 -0
- package/dist/native/linux-arm64/fs-helper +0 -0
- package/dist/native/linux-x64/fs-helper +0 -0
- package/dist/native/win32-arm64/fs-helper.exe +0 -0
- package/dist/native/win32-x64/fs-helper.exe +0 -0
- package/dist/stdio.js +784 -179
- package/package.json +5 -4
package/dist/agent.js
CHANGED
|
@@ -4154,13 +4154,14 @@ var require_fast_uri = __commonJS({
|
|
|
4154
4154
|
if (!malformedIPLiteral) {
|
|
4155
4155
|
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
|
|
4156
4156
|
}
|
|
4157
|
-
if (
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
4162
|
-
}
|
|
4157
|
+
if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
|
|
4158
|
+
let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
4159
|
+
if (!isIP) {
|
|
4160
|
+
host = normalizePercentEncoding(host.toLowerCase());
|
|
4163
4161
|
}
|
|
4162
|
+
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
4163
|
+
}
|
|
4164
|
+
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
4164
4165
|
if (parsed.path) {
|
|
4165
4166
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
4166
4167
|
}
|
|
@@ -32302,6 +32303,9 @@ var Protocol = class {
|
|
|
32302
32303
|
this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
|
|
32303
32304
|
const handleTaskResult = async () => {
|
|
32304
32305
|
const taskId = request.params.taskId;
|
|
32306
|
+
if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
|
|
32307
|
+
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
|
|
32308
|
+
}
|
|
32305
32309
|
if (this._taskMessageQueue) {
|
|
32306
32310
|
let queuedMessage;
|
|
32307
32311
|
while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
|
|
@@ -32332,12 +32336,12 @@ var Protocol = class {
|
|
|
32332
32336
|
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
|
|
32333
32337
|
}
|
|
32334
32338
|
if (!isTerminal(task.status)) {
|
|
32335
|
-
await this._waitForTaskUpdate(taskId, extra.signal);
|
|
32339
|
+
await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
|
|
32336
32340
|
return await handleTaskResult();
|
|
32337
32341
|
}
|
|
32338
32342
|
if (isTerminal(task.status)) {
|
|
32339
32343
|
const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
|
|
32340
|
-
this._clearTaskQueue(taskId);
|
|
32344
|
+
this._clearTaskQueue(taskId, extra.sessionId);
|
|
32341
32345
|
return {
|
|
32342
32346
|
...result,
|
|
32343
32347
|
_meta: {
|
|
@@ -32374,7 +32378,7 @@ var Protocol = class {
|
|
|
32374
32378
|
throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
|
|
32375
32379
|
}
|
|
32376
32380
|
await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
|
|
32377
|
-
this._clearTaskQueue(request.params.taskId);
|
|
32381
|
+
this._clearTaskQueue(request.params.taskId, extra.sessionId);
|
|
32378
32382
|
const cancelledTask = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
|
|
32379
32383
|
if (!cancelledTask) {
|
|
32380
32384
|
throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request.params.taskId}`);
|
|
@@ -32502,6 +32506,19 @@ var Protocol = class {
|
|
|
32502
32506
|
const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
|
|
32503
32507
|
const capturedTransport = this._transport;
|
|
32504
32508
|
const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
|
|
32509
|
+
const sessionId = capturedTransport?.sessionId;
|
|
32510
|
+
const store = this._taskStore;
|
|
32511
|
+
let relatedTaskFound = true;
|
|
32512
|
+
let relatedTaskLookup;
|
|
32513
|
+
if (relatedTaskId && store && this._taskMessageQueue && sessionId !== void 0) {
|
|
32514
|
+
relatedTaskFound = false;
|
|
32515
|
+
relatedTaskLookup = (async () => {
|
|
32516
|
+
if (!await store.getTask(relatedTaskId, sessionId)) {
|
|
32517
|
+
throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
|
|
32518
|
+
}
|
|
32519
|
+
relatedTaskFound = true;
|
|
32520
|
+
})();
|
|
32521
|
+
}
|
|
32505
32522
|
if (handler === void 0) {
|
|
32506
32523
|
const errorResponse = {
|
|
32507
32524
|
jsonrpc: "2.0",
|
|
@@ -32511,7 +32528,10 @@ var Protocol = class {
|
|
|
32511
32528
|
message: "Method not found"
|
|
32512
32529
|
}
|
|
32513
32530
|
};
|
|
32514
|
-
if (relatedTaskId &&
|
|
32531
|
+
if (relatedTaskId && relatedTaskLookup) {
|
|
32532
|
+
const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
|
|
32533
|
+
relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error62) => this._onerror(new Error(`Failed to send an error response: ${error62}`)));
|
|
32534
|
+
} else if (relatedTaskId && this._taskMessageQueue) {
|
|
32515
32535
|
this._enqueueTaskMessage(relatedTaskId, {
|
|
32516
32536
|
type: "error",
|
|
32517
32537
|
message: errorResponse,
|
|
@@ -32562,7 +32582,10 @@ var Protocol = class {
|
|
|
32562
32582
|
closeSSEStream: extra?.closeSSEStream,
|
|
32563
32583
|
closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
|
|
32564
32584
|
};
|
|
32565
|
-
Promise.resolve().then(() => {
|
|
32585
|
+
(relatedTaskLookup ?? Promise.resolve()).then(() => {
|
|
32586
|
+
if (relatedTaskLookup && abortController.signal.aborted) {
|
|
32587
|
+
throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
|
|
32588
|
+
}
|
|
32566
32589
|
if (taskCreationParams) {
|
|
32567
32590
|
this.assertTaskHandlerCapability(request.method);
|
|
32568
32591
|
}
|
|
@@ -32597,7 +32620,7 @@ var Protocol = class {
|
|
|
32597
32620
|
...error62["data"] !== void 0 && { data: error62["data"] }
|
|
32598
32621
|
}
|
|
32599
32622
|
};
|
|
32600
|
-
if (relatedTaskId && this._taskMessageQueue) {
|
|
32623
|
+
if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
|
|
32601
32624
|
await this._enqueueTaskMessage(relatedTaskId, {
|
|
32602
32625
|
type: "error",
|
|
32603
32626
|
message: errorResponse,
|
|
@@ -33077,7 +33100,7 @@ var Protocol = class {
|
|
|
33077
33100
|
throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
|
|
33078
33101
|
}
|
|
33079
33102
|
const maxQueueSize = this._options?.maxTaskQueueSize;
|
|
33080
|
-
await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
|
|
33103
|
+
await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
|
|
33081
33104
|
}
|
|
33082
33105
|
/**
|
|
33083
33106
|
* Clears the message queue for a task and rejects any pending request resolvers.
|
|
@@ -33106,12 +33129,13 @@ var Protocol = class {
|
|
|
33106
33129
|
* Uses polling to check for updates at the task's configured poll interval.
|
|
33107
33130
|
* @param taskId The task ID to wait for
|
|
33108
33131
|
* @param signal Abort signal to cancel the wait
|
|
33132
|
+
* @param sessionId Session of the request that waits, passed to the task store
|
|
33109
33133
|
* @returns Promise that resolves when an update occurs or rejects if aborted
|
|
33110
33134
|
*/
|
|
33111
|
-
async _waitForTaskUpdate(taskId, signal) {
|
|
33135
|
+
async _waitForTaskUpdate(taskId, signal, sessionId) {
|
|
33112
33136
|
let interval = this._options?.defaultTaskPollInterval ?? 1e3;
|
|
33113
33137
|
try {
|
|
33114
|
-
const task = await this._taskStore?.getTask(taskId);
|
|
33138
|
+
const task = await this._taskStore?.getTask(taskId, sessionId);
|
|
33115
33139
|
if (task?.pollInterval) {
|
|
33116
33140
|
interval = task.pollInterval;
|
|
33117
33141
|
}
|
|
@@ -34026,6 +34050,68 @@ function createFetchWithInit(baseFetch = fetch, baseInit) {
|
|
|
34026
34050
|
return baseFetch(url2, mergedInit);
|
|
34027
34051
|
};
|
|
34028
34052
|
}
|
|
34053
|
+
var MAX_REDIRECTS = 5;
|
|
34054
|
+
var REDIRECT_STATUSES = [301, 302, 303, 307, 308];
|
|
34055
|
+
function isWithinOrigin(from, to) {
|
|
34056
|
+
if (from.protocol === to.protocol && from.hostname === to.hostname && from.port === to.port) {
|
|
34057
|
+
return true;
|
|
34058
|
+
}
|
|
34059
|
+
return from.hostname === to.hostname && from.protocol === "http:" && from.port === "" && to.protocol === "https:" && to.port === "";
|
|
34060
|
+
}
|
|
34061
|
+
function redirectTarget(response, requestUrl) {
|
|
34062
|
+
const location = REDIRECT_STATUSES.includes(response.status) ? response.headers.get("location") : null;
|
|
34063
|
+
try {
|
|
34064
|
+
return location ? new URL(location, requestUrl) : void 0;
|
|
34065
|
+
} catch {
|
|
34066
|
+
return void 0;
|
|
34067
|
+
}
|
|
34068
|
+
}
|
|
34069
|
+
function followWithinOrigin(baseFetch, url2, init, response, followed = 0) {
|
|
34070
|
+
const target = redirectTarget(response, url2);
|
|
34071
|
+
const keepsMethod = response.status === 307 || response.status === 308 || (init?.method ?? "GET").toUpperCase() === "GET";
|
|
34072
|
+
if (!target || followed === MAX_REDIRECTS || !keepsMethod) {
|
|
34073
|
+
return void 0;
|
|
34074
|
+
}
|
|
34075
|
+
const from = new URL(url2);
|
|
34076
|
+
const addsUserinfo = (target.username || target.password) && (target.username !== from.username || target.password !== from.password);
|
|
34077
|
+
if (addsUserinfo || !isWithinOrigin(from, target)) {
|
|
34078
|
+
return void 0;
|
|
34079
|
+
}
|
|
34080
|
+
return Promise.resolve(response.body?.cancel()).then(() => baseFetch(target, { ...init, redirect: "manual" })).then((next) => followWithinOrigin(baseFetch, target, init, next, followed + 1) ?? next);
|
|
34081
|
+
}
|
|
34082
|
+
var followingFetches = /* @__PURE__ */ new WeakSet();
|
|
34083
|
+
function fetchLeavingRedirects(baseFetch) {
|
|
34084
|
+
const following = (url2, init) => (baseFetch ?? fetch)(url2, init);
|
|
34085
|
+
followingFetches.add(following);
|
|
34086
|
+
return following;
|
|
34087
|
+
}
|
|
34088
|
+
function fetchWithinOrigin(baseFetch = fetch) {
|
|
34089
|
+
if (followingFetches.has(baseFetch)) {
|
|
34090
|
+
return baseFetch;
|
|
34091
|
+
}
|
|
34092
|
+
return (url2, init) => {
|
|
34093
|
+
if (init?.redirect === "error" || init?.redirect === "manual") {
|
|
34094
|
+
return baseFetch(url2, init);
|
|
34095
|
+
}
|
|
34096
|
+
return Promise.resolve(baseFetch(url2, { ...init, redirect: "manual" })).then((response) => followWithinOrigin(baseFetch, url2, init, response) ?? response);
|
|
34097
|
+
};
|
|
34098
|
+
}
|
|
34099
|
+
function unfollowedRedirect(response, requestUrl) {
|
|
34100
|
+
if (response.type === "opaqueredirect") {
|
|
34101
|
+
return "Redirect not followed: this runtime does not expose the redirect target";
|
|
34102
|
+
}
|
|
34103
|
+
const from = response.url || requestUrl;
|
|
34104
|
+
const target = redirectTarget(response, from);
|
|
34105
|
+
if (!target) {
|
|
34106
|
+
return void 0;
|
|
34107
|
+
}
|
|
34108
|
+
target.username = target.password = target.search = target.hash = "";
|
|
34109
|
+
if (target.protocol === "http:" && new URL(from).protocol === "https:") {
|
|
34110
|
+
target.protocol = "https:";
|
|
34111
|
+
return `Redirect to plain http not followed; try ${target.href} instead`;
|
|
34112
|
+
}
|
|
34113
|
+
return `Redirect to ${target.href} not followed`;
|
|
34114
|
+
}
|
|
34029
34115
|
|
|
34030
34116
|
// node_modules/pkce-challenge/dist/index.node.js
|
|
34031
34117
|
var crypto;
|
|
@@ -34172,7 +34258,12 @@ var OAuthTokensSchema = object2({
|
|
|
34172
34258
|
token_type: string2(),
|
|
34173
34259
|
expires_in: coerce_exports.number().optional(),
|
|
34174
34260
|
scope: string2().optional(),
|
|
34175
|
-
refresh_token: string2().optional()
|
|
34261
|
+
refresh_token: string2().optional(),
|
|
34262
|
+
/**
|
|
34263
|
+
* Not part of the wire format: the authorization server this value was obtained from, added by
|
|
34264
|
+
* the client's `auth()` before it is stored and compared when it is read back.
|
|
34265
|
+
*/
|
|
34266
|
+
issuer: string2().optional().catch(void 0)
|
|
34176
34267
|
}).strip();
|
|
34177
34268
|
var OAuthErrorResponseSchema = object2({
|
|
34178
34269
|
error: string2(),
|
|
@@ -34202,7 +34293,12 @@ var OAuthClientInformationSchema = object2({
|
|
|
34202
34293
|
client_id: string2(),
|
|
34203
34294
|
client_secret: string2().optional(),
|
|
34204
34295
|
client_id_issued_at: number2().optional(),
|
|
34205
|
-
client_secret_expires_at: number2().optional()
|
|
34296
|
+
client_secret_expires_at: number2().optional(),
|
|
34297
|
+
/**
|
|
34298
|
+
* Not part of the wire format: the authorization server this value was obtained from, added by
|
|
34299
|
+
* the client's `auth()` before it is stored and compared when it is read back.
|
|
34300
|
+
*/
|
|
34301
|
+
issuer: string2().optional().catch(void 0)
|
|
34206
34302
|
}).strip();
|
|
34207
34303
|
var OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(OAuthClientInformationSchema);
|
|
34208
34304
|
var OAuthClientRegistrationErrorSchema = object2({
|
|
@@ -34400,10 +34496,31 @@ async function parseErrorResponse(input2) {
|
|
|
34400
34496
|
const errorClass = OAUTH_ERRORS[error62] || ServerError;
|
|
34401
34497
|
return new errorClass(error_description || "", error_uri);
|
|
34402
34498
|
} catch (error62) {
|
|
34403
|
-
const
|
|
34499
|
+
const redirect = input2 instanceof Response ? unfollowedRedirect(input2, input2.url) : void 0;
|
|
34500
|
+
const errorMessage = `${statusCode ? `HTTP ${statusCode}: ` : ""}${redirect ?? `Invalid OAuth error response: ${error62}. Raw body: ${body}`}`;
|
|
34404
34501
|
return new ServerError(errorMessage);
|
|
34405
34502
|
}
|
|
34406
34503
|
}
|
|
34504
|
+
function issuersMatch(a, b) {
|
|
34505
|
+
let [x, y] = [a, b];
|
|
34506
|
+
try {
|
|
34507
|
+
[x, y] = [new URL(a).href, new URL(b).href];
|
|
34508
|
+
} catch {
|
|
34509
|
+
}
|
|
34510
|
+
return x === y || x.endsWith("/") && x.slice(0, -1) === y || y.endsWith("/") && y.slice(0, -1) === x;
|
|
34511
|
+
}
|
|
34512
|
+
function discardIfIssuerMismatch(stored, issuer) {
|
|
34513
|
+
if (!stored)
|
|
34514
|
+
return void 0;
|
|
34515
|
+
if (typeof stored.issuer !== "string")
|
|
34516
|
+
return stored.issuer == null ? stored : { ...stored, issuer: void 0 };
|
|
34517
|
+
return issuersMatch(stored.issuer, issuer) ? stored : void 0;
|
|
34518
|
+
}
|
|
34519
|
+
var TokenResponseSchema = OAuthTokensSchema.omit({ issuer: true });
|
|
34520
|
+
var RegistrationResponseSchema = OAuthClientInformationFullSchema.omit({ issuer: true });
|
|
34521
|
+
function boundElsewhereError(stored, issuer) {
|
|
34522
|
+
return new Error(`OAuth client information is bound to authorization server ${stored.issuer} and is not presented to ${issuer}. Clear the stored client information, or correct \`expectedIssuer\`, if the authorization server has moved.`);
|
|
34523
|
+
}
|
|
34407
34524
|
async function auth(provider, options) {
|
|
34408
34525
|
try {
|
|
34409
34526
|
return await authInternal(provider, options);
|
|
@@ -34457,9 +34574,24 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
|
|
34457
34574
|
authorizationServerMetadata: metadata
|
|
34458
34575
|
});
|
|
34459
34576
|
}
|
|
34460
|
-
const
|
|
34577
|
+
const issuer = String(authorizationServerUrl);
|
|
34578
|
+
const selectedResource = await selectResourceURL(serverUrl, provider, resourceMetadata);
|
|
34579
|
+
const resource = selectedResource && resourceMetadata && !provider.validateResourceURL ? resourceMetadata.resource : selectedResource;
|
|
34461
34580
|
const resolvedScope = scope || resourceMetadata?.scopes_supported?.join(" ") || provider.clientMetadata.scope;
|
|
34462
|
-
|
|
34581
|
+
const storedClientInformation = await Promise.resolve(provider.clientInformation());
|
|
34582
|
+
let clientInformation = discardIfIssuerMismatch(storedClientInformation, issuer);
|
|
34583
|
+
const canRegisterAgain = provider.saveClientInformation !== void 0 && !!provider.redirectUrl;
|
|
34584
|
+
if (storedClientInformation && !clientInformation && !canRegisterAgain) {
|
|
34585
|
+
throw boundElsewhereError(storedClientInformation, issuer);
|
|
34586
|
+
}
|
|
34587
|
+
const unstampedClientInformation = clientInformation?.issuer == null ? clientInformation : void 0;
|
|
34588
|
+
const bindClientInformation = async () => {
|
|
34589
|
+
try {
|
|
34590
|
+
if (unstampedClientInformation)
|
|
34591
|
+
await provider.saveClientInformation?.({ ...unstampedClientInformation, issuer });
|
|
34592
|
+
} catch {
|
|
34593
|
+
}
|
|
34594
|
+
};
|
|
34463
34595
|
if (!clientInformation) {
|
|
34464
34596
|
if (authorizationCode !== void 0) {
|
|
34465
34597
|
throw new Error("Existing OAuth client information is required when exchanging an authorization code");
|
|
@@ -34471,9 +34603,7 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
|
|
34471
34603
|
}
|
|
34472
34604
|
const shouldUseUrlBasedClientId = supportsUrlBasedClientId && clientMetadataUrl;
|
|
34473
34605
|
if (shouldUseUrlBasedClientId) {
|
|
34474
|
-
clientInformation = {
|
|
34475
|
-
client_id: clientMetadataUrl
|
|
34476
|
-
};
|
|
34606
|
+
clientInformation = { client_id: clientMetadataUrl, issuer };
|
|
34477
34607
|
await provider.saveClientInformation?.(clientInformation);
|
|
34478
34608
|
} else {
|
|
34479
34609
|
if (!provider.saveClientInformation) {
|
|
@@ -34485,8 +34615,8 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
|
|
34485
34615
|
scope: resolvedScope,
|
|
34486
34616
|
fetchFn
|
|
34487
34617
|
});
|
|
34488
|
-
|
|
34489
|
-
clientInformation
|
|
34618
|
+
clientInformation = { ...fullInformation, issuer };
|
|
34619
|
+
await provider.saveClientInformation(clientInformation);
|
|
34490
34620
|
}
|
|
34491
34621
|
}
|
|
34492
34622
|
const nonInteractiveFlow = !provider.redirectUrl;
|
|
@@ -34497,10 +34627,14 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
|
|
34497
34627
|
authorizationCode,
|
|
34498
34628
|
fetchFn
|
|
34499
34629
|
});
|
|
34500
|
-
await
|
|
34630
|
+
await bindClientInformation();
|
|
34631
|
+
await provider.saveTokens({ ...tokens2, issuer });
|
|
34501
34632
|
return "AUTHORIZED";
|
|
34502
34633
|
}
|
|
34503
|
-
const tokens = await provider.tokens();
|
|
34634
|
+
const tokens = discardIfIssuerMismatch(await provider.tokens(), issuer);
|
|
34635
|
+
if (tokens?.refresh_token && tokens.issuer == null) {
|
|
34636
|
+
console.warn("[mcp-sdk] stored OAuth tokens have no 'issuer' property (saved by an earlier version, or by a provider that does not keep it) and are used as-is; make sure your OAuthClientProvider stores what saveTokens() and saveClientInformation() receive unchanged.");
|
|
34637
|
+
}
|
|
34504
34638
|
if (tokens?.refresh_token) {
|
|
34505
34639
|
try {
|
|
34506
34640
|
const newTokens = await refreshAuthorization(authorizationServerUrl, {
|
|
@@ -34511,7 +34645,8 @@ async function authInternal(provider, { serverUrl, authorizationCode, scope, res
|
|
|
34511
34645
|
addClientAuthentication: provider.addClientAuthentication,
|
|
34512
34646
|
fetchFn
|
|
34513
34647
|
});
|
|
34514
|
-
await
|
|
34648
|
+
await bindClientInformation();
|
|
34649
|
+
await provider.saveTokens({ ...newTokens, issuer });
|
|
34515
34650
|
return "AUTHORIZED";
|
|
34516
34651
|
} catch (error62) {
|
|
34517
34652
|
if (!(error62 instanceof OAuthError) || error62 instanceof ServerError) {
|
|
@@ -34610,7 +34745,7 @@ async function discoverOAuthProtectedResourceMetadata(serverUrl, opts, fetchFn =
|
|
|
34610
34745
|
}
|
|
34611
34746
|
async function fetchWithCorsRetry(url2, headers, fetchFn = fetch) {
|
|
34612
34747
|
try {
|
|
34613
|
-
return await fetchFn(url2, { headers });
|
|
34748
|
+
return await fetchWithinOrigin(fetchFn)(url2, { headers });
|
|
34614
34749
|
} catch (error62) {
|
|
34615
34750
|
if (error62 instanceof TypeError) {
|
|
34616
34751
|
if (headers) {
|
|
@@ -34635,7 +34770,7 @@ async function tryMetadataDiscovery(url2, protocolVersion, fetchFn = fetch) {
|
|
|
34635
34770
|
return await fetchWithCorsRetry(url2, headers, fetchFn);
|
|
34636
34771
|
}
|
|
34637
34772
|
function shouldAttemptFallback(response, pathname) {
|
|
34638
|
-
return !response || response.
|
|
34773
|
+
return !response || !response.ok && response.status < 500 && pathname !== "/";
|
|
34639
34774
|
}
|
|
34640
34775
|
async function discoverMetadataWithFallback(serverUrl, wellKnownType, fetchFn, opts) {
|
|
34641
34776
|
const issuer = new URL(serverUrl);
|
|
@@ -34701,7 +34836,7 @@ async function discoverAuthorizationServerMetadata(authorizationServerUrl, { fet
|
|
|
34701
34836
|
}
|
|
34702
34837
|
if (!response.ok) {
|
|
34703
34838
|
await response.body?.cancel();
|
|
34704
|
-
if (response.status
|
|
34839
|
+
if (response.status < 500) {
|
|
34705
34840
|
continue;
|
|
34706
34841
|
}
|
|
34707
34842
|
throw new Error(`HTTP ${response.status} trying to load ${type === "oauth" ? "OAuth" : "OpenID provider"} metadata from ${endpointUrl}`);
|
|
@@ -34734,6 +34869,9 @@ async function discoverOAuthServerInfo(serverUrl, opts) {
|
|
|
34734
34869
|
resourceMetadata
|
|
34735
34870
|
};
|
|
34736
34871
|
}
|
|
34872
|
+
function resourceIndicatorToString(resource) {
|
|
34873
|
+
return typeof resource === "string" ? resource : resource.href;
|
|
34874
|
+
}
|
|
34737
34875
|
async function startAuthorization(authorizationServerUrl, { metadata, clientInformation, redirectUrl, scope, state, resource }) {
|
|
34738
34876
|
let authorizationUrl;
|
|
34739
34877
|
if (metadata) {
|
|
@@ -34765,7 +34903,7 @@ async function startAuthorization(authorizationServerUrl, { metadata, clientInfo
|
|
|
34765
34903
|
authorizationUrl.searchParams.append("prompt", "consent");
|
|
34766
34904
|
}
|
|
34767
34905
|
if (resource) {
|
|
34768
|
-
authorizationUrl.searchParams.set("resource", resource
|
|
34906
|
+
authorizationUrl.searchParams.set("resource", resourceIndicatorToString(resource));
|
|
34769
34907
|
}
|
|
34770
34908
|
return { authorizationUrl, codeVerifier };
|
|
34771
34909
|
}
|
|
@@ -34784,7 +34922,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
|
|
|
34784
34922
|
Accept: "application/json"
|
|
34785
34923
|
});
|
|
34786
34924
|
if (resource) {
|
|
34787
|
-
tokenRequestParams.set("resource", resource
|
|
34925
|
+
tokenRequestParams.set("resource", resourceIndicatorToString(resource));
|
|
34788
34926
|
}
|
|
34789
34927
|
if (addClientAuthentication) {
|
|
34790
34928
|
await addClientAuthentication(headers, tokenRequestParams, tokenUrl, metadata);
|
|
@@ -34793,7 +34931,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
|
|
|
34793
34931
|
const authMethod = selectClientAuthMethod(clientInformation, supportedMethods);
|
|
34794
34932
|
applyClientAuthentication(authMethod, clientInformation, headers, tokenRequestParams);
|
|
34795
34933
|
}
|
|
34796
|
-
const response = await (fetchFn ?? fetch)(tokenUrl, {
|
|
34934
|
+
const response = await fetchWithinOrigin(fetchFn ?? fetch)(tokenUrl, {
|
|
34797
34935
|
method: "POST",
|
|
34798
34936
|
headers,
|
|
34799
34937
|
body: tokenRequestParams
|
|
@@ -34801,7 +34939,7 @@ async function executeTokenRequest(authorizationServerUrl, { metadata, tokenRequ
|
|
|
34801
34939
|
if (!response.ok) {
|
|
34802
34940
|
throw await parseErrorResponse(response);
|
|
34803
34941
|
}
|
|
34804
|
-
return
|
|
34942
|
+
return TokenResponseSchema.parse(await response.json());
|
|
34805
34943
|
}
|
|
34806
34944
|
async function refreshAuthorization(authorizationServerUrl, { metadata, clientInformation, refreshToken, resource, addClientAuthentication, fetchFn }) {
|
|
34807
34945
|
const tokenRequestParams = new URLSearchParams({
|
|
@@ -34819,6 +34957,15 @@ async function refreshAuthorization(authorizationServerUrl, { metadata, clientIn
|
|
|
34819
34957
|
return { refresh_token: refreshToken, ...tokens };
|
|
34820
34958
|
}
|
|
34821
34959
|
async function fetchToken(provider, authorizationServerUrl, { metadata, resource, authorizationCode, fetchFn } = {}) {
|
|
34960
|
+
const readClientInformation = async () => {
|
|
34961
|
+
const storedClientInformation = await provider.clientInformation();
|
|
34962
|
+
const checked = discardIfIssuerMismatch(storedClientInformation, String(authorizationServerUrl));
|
|
34963
|
+
if (storedClientInformation && !checked) {
|
|
34964
|
+
throw boundElsewhereError(storedClientInformation, String(authorizationServerUrl));
|
|
34965
|
+
}
|
|
34966
|
+
return checked;
|
|
34967
|
+
};
|
|
34968
|
+
let clientInformation = await readClientInformation();
|
|
34822
34969
|
const scope = provider.clientMetadata.scope;
|
|
34823
34970
|
let tokenRequestParams;
|
|
34824
34971
|
if (provider.prepareTokenRequest) {
|
|
@@ -34834,11 +34981,11 @@ async function fetchToken(provider, authorizationServerUrl, { metadata, resource
|
|
|
34834
34981
|
const codeVerifier = await provider.codeVerifier();
|
|
34835
34982
|
tokenRequestParams = prepareAuthorizationCodeRequest(authorizationCode, codeVerifier, provider.redirectUrl);
|
|
34836
34983
|
}
|
|
34837
|
-
|
|
34984
|
+
clientInformation ?? (clientInformation = await readClientInformation());
|
|
34838
34985
|
return executeTokenRequest(authorizationServerUrl, {
|
|
34839
34986
|
metadata,
|
|
34840
34987
|
tokenRequestParams,
|
|
34841
|
-
clientInformation
|
|
34988
|
+
clientInformation,
|
|
34842
34989
|
addClientAuthentication: provider.addClientAuthentication,
|
|
34843
34990
|
resource,
|
|
34844
34991
|
fetchFn
|
|
@@ -34854,7 +35001,7 @@ async function registerClient(authorizationServerUrl, { metadata, clientMetadata
|
|
|
34854
35001
|
} else {
|
|
34855
35002
|
registrationUrl = new URL("/register", authorizationServerUrl);
|
|
34856
35003
|
}
|
|
34857
|
-
const response = await (fetchFn ?? fetch)(registrationUrl, {
|
|
35004
|
+
const response = await fetchWithinOrigin(fetchFn ?? fetch)(registrationUrl, {
|
|
34858
35005
|
method: "POST",
|
|
34859
35006
|
headers: {
|
|
34860
35007
|
"Content-Type": "application/json"
|
|
@@ -34867,7 +35014,7 @@ async function registerClient(authorizationServerUrl, { metadata, clientMetadata
|
|
|
34867
35014
|
if (!response.ok) {
|
|
34868
35015
|
throw await parseErrorResponse(response);
|
|
34869
35016
|
}
|
|
34870
|
-
return
|
|
35017
|
+
return RegistrationResponseSchema.parse(await response.json());
|
|
34871
35018
|
}
|
|
34872
35019
|
|
|
34873
35020
|
// node_modules/eventsource-parser/dist/index.js
|
|
@@ -35094,9 +35241,19 @@ var StreamableHTTPClientTransport = class {
|
|
|
35094
35241
|
this._authProvider = opts?.authProvider;
|
|
35095
35242
|
this._fetch = opts?.fetch;
|
|
35096
35243
|
this._fetchWithInit = createFetchWithInit(opts?.fetch, opts?.requestInit);
|
|
35244
|
+
this._followRedirects = opts?.redirectPolicy === "follow";
|
|
35245
|
+
if (this._followRedirects) {
|
|
35246
|
+
this._fetch = fetchLeavingRedirects(this._fetch);
|
|
35247
|
+
this._fetchWithInit = fetchLeavingRedirects(this._fetchWithInit);
|
|
35248
|
+
}
|
|
35097
35249
|
this._sessionId = opts?.sessionId;
|
|
35098
35250
|
this._reconnectionOptions = opts?.reconnectionOptions ?? DEFAULT_STREAMABLE_HTTP_RECONNECTION_OPTIONS;
|
|
35099
35251
|
}
|
|
35252
|
+
/** Error text for a redirect `response` that was not followed, or `undefined` for any other response. */
|
|
35253
|
+
_unfollowedRedirect(response, url2) {
|
|
35254
|
+
const text = unfollowedRedirect(response, url2);
|
|
35255
|
+
return text && !this._followRedirects ? `${text} (redirectPolicy: 'same-origin')` : text;
|
|
35256
|
+
}
|
|
35100
35257
|
async _authThenStart() {
|
|
35101
35258
|
if (!this._authProvider) {
|
|
35102
35259
|
throw new UnauthorizedError("No auth provider");
|
|
@@ -35146,11 +35303,13 @@ var StreamableHTTPClientTransport = class {
|
|
|
35146
35303
|
if (resumptionToken) {
|
|
35147
35304
|
headers.set("last-event-id", resumptionToken);
|
|
35148
35305
|
}
|
|
35149
|
-
const
|
|
35150
|
-
|
|
35151
|
-
|
|
35152
|
-
|
|
35153
|
-
|
|
35306
|
+
const fetchFn = this._fetch ?? fetch;
|
|
35307
|
+
const init = { method: "GET", headers, signal: this._abortController?.signal };
|
|
35308
|
+
let response = await fetchFn(this._url, this._followRedirects ? init : { ...init, redirect: "manual" });
|
|
35309
|
+
const followed = this._followRedirects ? void 0 : followWithinOrigin(fetchFn, this._url, init, response);
|
|
35310
|
+
if (followed) {
|
|
35311
|
+
response = await followed;
|
|
35312
|
+
}
|
|
35154
35313
|
if (!response.ok) {
|
|
35155
35314
|
await response.body?.cancel();
|
|
35156
35315
|
if (response.status === 401 && this._authProvider) {
|
|
@@ -35159,7 +35318,7 @@ var StreamableHTTPClientTransport = class {
|
|
|
35159
35318
|
if (response.status === 405) {
|
|
35160
35319
|
return;
|
|
35161
35320
|
}
|
|
35162
|
-
throw new StreamableHTTPError(response.status, `Failed to open SSE stream: ${response.statusText}`);
|
|
35321
|
+
throw new StreamableHTTPError(response.status, `Failed to open SSE stream: ${this._unfollowedRedirect(response, this._url) ?? response.statusText}`);
|
|
35163
35322
|
}
|
|
35164
35323
|
this._handleSseStream(response.body, options, true);
|
|
35165
35324
|
} catch (error62) {
|
|
@@ -35322,7 +35481,7 @@ var StreamableHTTPClientTransport = class {
|
|
|
35322
35481
|
body: JSON.stringify(message),
|
|
35323
35482
|
signal: this._abortController?.signal
|
|
35324
35483
|
};
|
|
35325
|
-
const response = await (this._fetch ?? fetch)(this._url, init);
|
|
35484
|
+
const response = await fetchWithinOrigin(this._fetch ?? fetch)(this._url, init);
|
|
35326
35485
|
const sessionId = response.headers.get("mcp-session-id");
|
|
35327
35486
|
if (sessionId) {
|
|
35328
35487
|
this._sessionId = sessionId;
|
|
@@ -35374,7 +35533,7 @@ var StreamableHTTPClientTransport = class {
|
|
|
35374
35533
|
return this.send(message);
|
|
35375
35534
|
}
|
|
35376
35535
|
}
|
|
35377
|
-
throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${text}`);
|
|
35536
|
+
throw new StreamableHTTPError(response.status, `Error POSTing to endpoint: ${this._unfollowedRedirect(response, this._url) ?? text}`);
|
|
35378
35537
|
}
|
|
35379
35538
|
this._hasCompletedAuthFlow = false;
|
|
35380
35539
|
this._lastUpscopingHeader = void 0;
|
|
@@ -35436,10 +35595,10 @@ var StreamableHTTPClientTransport = class {
|
|
|
35436
35595
|
headers,
|
|
35437
35596
|
signal: this._abortController?.signal
|
|
35438
35597
|
};
|
|
35439
|
-
const response = await (this._fetch ?? fetch)(this._url, init);
|
|
35598
|
+
const response = await fetchWithinOrigin(this._fetch ?? fetch)(this._url, init);
|
|
35440
35599
|
await response.body?.cancel();
|
|
35441
35600
|
if (!response.ok && response.status !== 405) {
|
|
35442
|
-
throw new StreamableHTTPError(response.status, `Failed to terminate session: ${response.statusText}`);
|
|
35601
|
+
throw new StreamableHTTPError(response.status, `Failed to terminate session: ${this._unfollowedRedirect(response, this._url) ?? response.statusText}`);
|
|
35443
35602
|
}
|
|
35444
35603
|
this._sessionId = void 0;
|
|
35445
35604
|
} catch (error62) {
|
|
@@ -35479,88 +35638,394 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
35479
35638
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
35480
35639
|
var wrapper_default = import_websocket.default;
|
|
35481
35640
|
|
|
35641
|
+
// src/process-event-forwarder.ts
|
|
35642
|
+
var eventSchema = external_exports.object({
|
|
35643
|
+
sessionId: external_exports.string().uuid(),
|
|
35644
|
+
taskId: external_exports.string().uuid().optional(),
|
|
35645
|
+
sequence: external_exports.number().int().positive(),
|
|
35646
|
+
kind: external_exports.enum(["started", "stdout", "stderr", "finished"]),
|
|
35647
|
+
offset: external_exports.number().int().nonnegative().optional(),
|
|
35648
|
+
data: external_exports.string().max(4096).optional(),
|
|
35649
|
+
exitCode: external_exports.number().int().nullable().optional(),
|
|
35650
|
+
signal: external_exports.string().nullable().optional(),
|
|
35651
|
+
timedOut: external_exports.boolean().optional()
|
|
35652
|
+
});
|
|
35653
|
+
var batchSchema = external_exports.object({
|
|
35654
|
+
sessionId: external_exports.string().uuid(),
|
|
35655
|
+
events: external_exports.array(eventSchema).max(32),
|
|
35656
|
+
nextSequence: external_exports.number().int().nonnegative(),
|
|
35657
|
+
missedEvents: external_exports.boolean(),
|
|
35658
|
+
finished: external_exports.boolean()
|
|
35659
|
+
});
|
|
35660
|
+
var processListSchema = external_exports.object({
|
|
35661
|
+
processes: external_exports.array(
|
|
35662
|
+
external_exports.object({
|
|
35663
|
+
sessionId: external_exports.string().uuid(),
|
|
35664
|
+
taskId: external_exports.string().uuid().optional()
|
|
35665
|
+
})
|
|
35666
|
+
)
|
|
35667
|
+
});
|
|
35668
|
+
function structuredContent(response) {
|
|
35669
|
+
if (!response || typeof response !== "object") {
|
|
35670
|
+
throw new Error("Local process tool returned an invalid response.");
|
|
35671
|
+
}
|
|
35672
|
+
const result = response;
|
|
35673
|
+
if (result.isError || result.structuredContent === void 0) {
|
|
35674
|
+
throw new Error("Local process event tool failed or returned no content.");
|
|
35675
|
+
}
|
|
35676
|
+
return result.structuredContent;
|
|
35677
|
+
}
|
|
35678
|
+
var ProcessEventForwarder = class {
|
|
35679
|
+
constructor(callLocalTool, send, log, acknowledged = /* @__PURE__ */ new Map(), ackTimeoutMs = 3e3, onFatal) {
|
|
35680
|
+
this.callLocalTool = callLocalTool;
|
|
35681
|
+
this.send = send;
|
|
35682
|
+
this.log = log;
|
|
35683
|
+
this.acknowledged = acknowledged;
|
|
35684
|
+
this.ackTimeoutMs = ackTimeoutMs;
|
|
35685
|
+
this.onFatal = onFatal;
|
|
35686
|
+
}
|
|
35687
|
+
callLocalTool;
|
|
35688
|
+
send;
|
|
35689
|
+
log;
|
|
35690
|
+
acknowledged;
|
|
35691
|
+
ackTimeoutMs;
|
|
35692
|
+
onFatal;
|
|
35693
|
+
#watchers = /* @__PURE__ */ new Map();
|
|
35694
|
+
#ackWaiters = /* @__PURE__ */ new Map();
|
|
35695
|
+
#lastSent = /* @__PURE__ */ new Map();
|
|
35696
|
+
#closed = false;
|
|
35697
|
+
acknowledge(sessionId, sequence) {
|
|
35698
|
+
if (!external_exports.string().uuid().safeParse(sessionId).success || !Number.isSafeInteger(sequence) || sequence < 1)
|
|
35699
|
+
return;
|
|
35700
|
+
const maximum = this.#lastSent.get(sessionId) ?? 0;
|
|
35701
|
+
if (sequence > maximum) return;
|
|
35702
|
+
const current = this.acknowledged.get(sessionId) ?? 0;
|
|
35703
|
+
if (sequence <= current) return;
|
|
35704
|
+
this.acknowledged.set(sessionId, sequence);
|
|
35705
|
+
for (const wake of this.#ackWaiters.get(sessionId) ?? []) wake();
|
|
35706
|
+
}
|
|
35707
|
+
async #waitForAck(sessionId, target, signal) {
|
|
35708
|
+
if ((this.acknowledged.get(sessionId) ?? 0) >= target) return true;
|
|
35709
|
+
if (signal.aborted) return false;
|
|
35710
|
+
return new Promise((resolve) => {
|
|
35711
|
+
const listeners = this.#ackWaiters.get(sessionId) ?? /* @__PURE__ */ new Set();
|
|
35712
|
+
this.#ackWaiters.set(sessionId, listeners);
|
|
35713
|
+
let timer;
|
|
35714
|
+
const finish = () => {
|
|
35715
|
+
clearTimeout(timer);
|
|
35716
|
+
signal.removeEventListener("abort", finish);
|
|
35717
|
+
listeners.delete(wake);
|
|
35718
|
+
if (listeners.size === 0) this.#ackWaiters.delete(sessionId);
|
|
35719
|
+
resolve((this.acknowledged.get(sessionId) ?? 0) >= target);
|
|
35720
|
+
};
|
|
35721
|
+
const wake = () => {
|
|
35722
|
+
if ((this.acknowledged.get(sessionId) ?? 0) >= target) finish();
|
|
35723
|
+
};
|
|
35724
|
+
listeners.add(wake);
|
|
35725
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
35726
|
+
timer = setTimeout(finish, this.ackTimeoutMs);
|
|
35727
|
+
wake();
|
|
35728
|
+
});
|
|
35729
|
+
}
|
|
35730
|
+
async recover() {
|
|
35731
|
+
if (this.#closed) return;
|
|
35732
|
+
const result = await this.callLocalTool("list_processes", {});
|
|
35733
|
+
const parsed = processListSchema.parse(structuredContent(result));
|
|
35734
|
+
for (const process2 of parsed.processes) {
|
|
35735
|
+
if (process2.taskId) this.watch(process2.sessionId);
|
|
35736
|
+
}
|
|
35737
|
+
}
|
|
35738
|
+
watch(sessionId) {
|
|
35739
|
+
if (this.#closed || this.#watchers.has(sessionId)) return;
|
|
35740
|
+
if (!external_exports.string().uuid().safeParse(sessionId).success) return;
|
|
35741
|
+
const controller2 = new AbortController();
|
|
35742
|
+
this.#watchers.set(sessionId, controller2);
|
|
35743
|
+
void this.#pump(sessionId, controller2.signal).catch((error62) => {
|
|
35744
|
+
const failure2 = error62 instanceof Error ? error62 : new Error("Unknown process event failure.");
|
|
35745
|
+
this.log?.("Process event watch failed: " + failure2.message);
|
|
35746
|
+
if (!controller2.signal.aborted) this.onFatal?.(failure2);
|
|
35747
|
+
}).finally(() => {
|
|
35748
|
+
if (this.#watchers.get(sessionId) === controller2) {
|
|
35749
|
+
this.#watchers.delete(sessionId);
|
|
35750
|
+
}
|
|
35751
|
+
});
|
|
35752
|
+
}
|
|
35753
|
+
async #pump(sessionId, signal) {
|
|
35754
|
+
let afterSequence = this.acknowledged.get(sessionId) ?? 0;
|
|
35755
|
+
while (!signal.aborted) {
|
|
35756
|
+
const raw = await this.callLocalTool("watch_process_events", {
|
|
35757
|
+
sessionId,
|
|
35758
|
+
afterSequence,
|
|
35759
|
+
timeoutMs: 1e4,
|
|
35760
|
+
maxEvents: 32
|
|
35761
|
+
});
|
|
35762
|
+
if (signal.aborted) break;
|
|
35763
|
+
const batch = batchSchema.parse(structuredContent(raw));
|
|
35764
|
+
if (batch.sessionId !== sessionId) {
|
|
35765
|
+
throw new Error("Process event watch returned a different session ID.");
|
|
35766
|
+
}
|
|
35767
|
+
if (batch.nextSequence < afterSequence) {
|
|
35768
|
+
throw new Error("Process event cursor moved backwards.");
|
|
35769
|
+
}
|
|
35770
|
+
if (batch.events.length === 0 && batch.finished) break;
|
|
35771
|
+
if (batch.events.length === 0) continue;
|
|
35772
|
+
let confirmed = false;
|
|
35773
|
+
for (let attempt = 0; attempt < 5 && !signal.aborted; attempt++) {
|
|
35774
|
+
for (const [index, event] of batch.events.entries()) {
|
|
35775
|
+
if (signal.aborted) break;
|
|
35776
|
+
if (event.sessionId !== sessionId) {
|
|
35777
|
+
throw new Error("Process event belongs to another session.");
|
|
35778
|
+
}
|
|
35779
|
+
this.#lastSent.set(
|
|
35780
|
+
sessionId,
|
|
35781
|
+
Math.max(this.#lastSent.get(sessionId) ?? 0, event.sequence)
|
|
35782
|
+
);
|
|
35783
|
+
this.send({
|
|
35784
|
+
type: "process_event",
|
|
35785
|
+
event,
|
|
35786
|
+
missedEvents: index === 0 && batch.missedEvents
|
|
35787
|
+
});
|
|
35788
|
+
}
|
|
35789
|
+
confirmed = await this.#waitForAck(
|
|
35790
|
+
sessionId,
|
|
35791
|
+
batch.nextSequence,
|
|
35792
|
+
signal
|
|
35793
|
+
);
|
|
35794
|
+
if (confirmed) break;
|
|
35795
|
+
}
|
|
35796
|
+
if (!confirmed) {
|
|
35797
|
+
if (signal.aborted) break;
|
|
35798
|
+
throw new Error(
|
|
35799
|
+
"PROCESS_EVENT_ACK_TIMEOUT: Gateway did not acknowledge event batch."
|
|
35800
|
+
);
|
|
35801
|
+
}
|
|
35802
|
+
afterSequence = batch.nextSequence;
|
|
35803
|
+
if (batch.finished && batch.events.length < 32) break;
|
|
35804
|
+
}
|
|
35805
|
+
}
|
|
35806
|
+
close() {
|
|
35807
|
+
this.#closed = true;
|
|
35808
|
+
for (const controller2 of this.#watchers.values()) controller2.abort();
|
|
35809
|
+
this.#watchers.clear();
|
|
35810
|
+
for (const wake of this.#ackWaiters.values()) {
|
|
35811
|
+
for (const notify of wake) notify();
|
|
35812
|
+
}
|
|
35813
|
+
this.#ackWaiters.clear();
|
|
35814
|
+
}
|
|
35815
|
+
};
|
|
35816
|
+
|
|
35482
35817
|
// src/agent-core.ts
|
|
35818
|
+
var supportedAgentTools = [
|
|
35819
|
+
"get_system_info",
|
|
35820
|
+
"list_directory",
|
|
35821
|
+
"read_text_file",
|
|
35822
|
+
"start_process",
|
|
35823
|
+
"read_process_output",
|
|
35824
|
+
"watch_process_events",
|
|
35825
|
+
"terminate_process",
|
|
35826
|
+
"list_processes",
|
|
35827
|
+
"write_text_file"
|
|
35828
|
+
];
|
|
35483
35829
|
var destructiveTools = /* @__PURE__ */ new Set([
|
|
35484
35830
|
"start_process",
|
|
35485
35831
|
"terminate_process",
|
|
35486
35832
|
"write_text_file"
|
|
35487
35833
|
]);
|
|
35488
|
-
var
|
|
35834
|
+
var requestEnvelope = external_exports.object({
|
|
35489
35835
|
type: external_exports.literal("request"),
|
|
35490
|
-
requestId: external_exports.string().uuid()
|
|
35491
|
-
|
|
35492
|
-
|
|
35493
|
-
|
|
35494
|
-
|
|
35495
|
-
|
|
35496
|
-
|
|
35497
|
-
|
|
35498
|
-
|
|
35499
|
-
|
|
35500
|
-
|
|
35501
|
-
|
|
35502
|
-
|
|
35503
|
-
|
|
35836
|
+
requestId: external_exports.string().uuid()
|
|
35837
|
+
});
|
|
35838
|
+
var requestSchema = requestEnvelope.extend({
|
|
35839
|
+
tool: external_exports.enum(supportedAgentTools),
|
|
35840
|
+
arguments: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
35841
|
+
deadlineAt: external_exports.number().int().positive().optional()
|
|
35842
|
+
});
|
|
35843
|
+
var cancelSchema = external_exports.object({
|
|
35844
|
+
type: external_exports.literal("cancel"),
|
|
35845
|
+
requestId: external_exports.string().uuid()
|
|
35846
|
+
});
|
|
35847
|
+
var ackSchema = external_exports.object({
|
|
35848
|
+
type: external_exports.literal("process_event_ack"),
|
|
35849
|
+
sessionId: external_exports.string().uuid(),
|
|
35850
|
+
sequence: external_exports.number().int().positive()
|
|
35851
|
+
});
|
|
35852
|
+
var DEFAULT_REQUEST_LIFETIME_MS = 6e4;
|
|
35853
|
+
function assertActive(signal) {
|
|
35854
|
+
if (signal?.aborted) {
|
|
35855
|
+
throw new Error(
|
|
35856
|
+
"REQUEST_EXPIRED: The operation was cancelled or its approval deadline expired."
|
|
35857
|
+
);
|
|
35858
|
+
}
|
|
35859
|
+
}
|
|
35860
|
+
async function authorizeToolCall(tool, args, allowDestructive, approve, signal) {
|
|
35861
|
+
assertActive(signal);
|
|
35504
35862
|
if (!destructiveTools.has(tool)) return;
|
|
35505
35863
|
if (!allowDestructive) {
|
|
35506
35864
|
throw new Error(
|
|
35507
35865
|
"This action requires local approval. Commands and writes are disabled unless explicitly enabled."
|
|
35508
35866
|
);
|
|
35509
35867
|
}
|
|
35510
|
-
if (!approve || !await approve(tool, args)) {
|
|
35868
|
+
if (!approve || !await approve(tool, args, signal)) {
|
|
35869
|
+
assertActive(signal);
|
|
35511
35870
|
throw new Error("The action was not approved on the connected machine.");
|
|
35512
35871
|
}
|
|
35872
|
+
assertActive(signal);
|
|
35513
35873
|
}
|
|
35514
|
-
async function connectOnce(options) {
|
|
35874
|
+
async function connectOnce(options, acknowledged) {
|
|
35515
35875
|
const { config: config3, signal } = options;
|
|
35876
|
+
if (!config3.MACHINE_TERMINAL_DEVICE_SECRET) {
|
|
35877
|
+
throw new Error(
|
|
35878
|
+
"DEVICE_PAIRING_REQUIRED: Connect using the CLI or set MACHINE_TERMINAL_DEVICE_SECRET."
|
|
35879
|
+
);
|
|
35880
|
+
}
|
|
35516
35881
|
const client = new Client({
|
|
35517
35882
|
name: "terminal-commands-cli",
|
|
35518
|
-
version: "0.
|
|
35883
|
+
version: "0.2.0"
|
|
35519
35884
|
});
|
|
35520
|
-
|
|
35521
|
-
|
|
35522
|
-
|
|
35523
|
-
|
|
35524
|
-
|
|
35525
|
-
|
|
35526
|
-
|
|
35527
|
-
|
|
35528
|
-
|
|
35529
|
-
|
|
35530
|
-
|
|
35531
|
-
|
|
35532
|
-
|
|
35533
|
-
JSON.stringify({
|
|
35534
|
-
type: "hello",
|
|
35535
|
-
deviceId: config3.MACHINE_TERMINAL_DEVICE_ID,
|
|
35536
|
-
name: config3.MACHINE_TERMINAL_DEVICE_NAME,
|
|
35537
|
-
platform: `${os.platform()}-${os.arch()}`
|
|
35538
|
-
})
|
|
35885
|
+
let disposeSocket;
|
|
35886
|
+
try {
|
|
35887
|
+
await client.connect(
|
|
35888
|
+
new StreamableHTTPClientTransport(
|
|
35889
|
+
new URL(config3.MACHINE_TERMINAL_LOCAL_MCP_URL),
|
|
35890
|
+
{
|
|
35891
|
+
requestInit: {
|
|
35892
|
+
headers: config3.MACHINE_TERMINAL_LOCAL_AGENT_TOKEN ? {
|
|
35893
|
+
"X-Terminal-Commands-Agent-Token": config3.MACHINE_TERMINAL_LOCAL_AGENT_TOKEN
|
|
35894
|
+
} : {}
|
|
35895
|
+
}
|
|
35896
|
+
}
|
|
35897
|
+
)
|
|
35539
35898
|
);
|
|
35540
|
-
|
|
35541
|
-
|
|
35542
|
-
|
|
35543
|
-
|
|
35544
|
-
|
|
35545
|
-
|
|
35546
|
-
|
|
35547
|
-
|
|
35548
|
-
|
|
35899
|
+
const token = options.getToken ? await options.getToken() : config3.MACHINE_TERMINAL_DEVICE_TOKEN;
|
|
35900
|
+
const socket = new wrapper_default(config3.MACHINE_TERMINAL_GATEWAY_URL, {
|
|
35901
|
+
headers: { Authorization: "Bearer " + token }
|
|
35902
|
+
});
|
|
35903
|
+
disposeSocket = () => socket.terminate();
|
|
35904
|
+
const pending = /* @__PURE__ */ new Map();
|
|
35905
|
+
const forwarder = new ProcessEventForwarder(
|
|
35906
|
+
async (name, args) => client.callTool({ name, arguments: args }),
|
|
35907
|
+
(message) => {
|
|
35908
|
+
if (socket.readyState !== wrapper_default.OPEN) return;
|
|
35909
|
+
if (socket.bufferedAmount > 512e3) {
|
|
35910
|
+
socket.close(1013, "Process event backpressure");
|
|
35911
|
+
throw new Error(
|
|
35912
|
+
"Process event WebSocket backpressure limit reached."
|
|
35913
|
+
);
|
|
35914
|
+
}
|
|
35915
|
+
socket.send(JSON.stringify(message));
|
|
35916
|
+
},
|
|
35917
|
+
options.log,
|
|
35918
|
+
acknowledged,
|
|
35919
|
+
3e3,
|
|
35920
|
+
() => {
|
|
35921
|
+
if (socket.readyState === wrapper_default.OPEN) {
|
|
35922
|
+
socket.close(1013, "Process event delivery stalled");
|
|
35923
|
+
}
|
|
35924
|
+
}
|
|
35925
|
+
);
|
|
35926
|
+
const stop2 = () => socket.close(1e3, "Client stopping.");
|
|
35927
|
+
signal?.addEventListener("abort", stop2, { once: true });
|
|
35928
|
+
socket.on("open", () => {
|
|
35929
|
+
socket.send(
|
|
35930
|
+
JSON.stringify({
|
|
35931
|
+
type: "hello",
|
|
35932
|
+
deviceId: config3.MACHINE_TERMINAL_DEVICE_ID,
|
|
35933
|
+
deviceSecret: config3.MACHINE_TERMINAL_DEVICE_SECRET,
|
|
35934
|
+
name: config3.MACHINE_TERMINAL_DEVICE_NAME,
|
|
35935
|
+
platform: os.platform() + "-" + os.arch(),
|
|
35936
|
+
protocolVersion: 2,
|
|
35937
|
+
capabilities: supportedAgentTools
|
|
35938
|
+
})
|
|
35939
|
+
);
|
|
35940
|
+
});
|
|
35941
|
+
socket.on("message", (raw) => {
|
|
35942
|
+
try {
|
|
35943
|
+
const message = JSON.parse(raw.toString());
|
|
35944
|
+
const cancelled = cancelSchema.safeParse(message);
|
|
35945
|
+
if (cancelled.success) {
|
|
35946
|
+
pending.get(cancelled.data.requestId)?.abort();
|
|
35947
|
+
return;
|
|
35948
|
+
}
|
|
35949
|
+
const ack = ackSchema.safeParse(message);
|
|
35950
|
+
if (ack.success) {
|
|
35951
|
+
forwarder.acknowledge(ack.data.sessionId, ack.data.sequence);
|
|
35952
|
+
return;
|
|
35953
|
+
}
|
|
35954
|
+
if (message && typeof message === "object" && "type" in message && message.type === "ready") {
|
|
35955
|
+
void forwarder.recover().catch(
|
|
35956
|
+
(error62) => options.log?.(
|
|
35957
|
+
"Could not recover local process streams: " + (error62 instanceof Error ? error62.message : "unknown error")
|
|
35958
|
+
)
|
|
35959
|
+
);
|
|
35960
|
+
return;
|
|
35961
|
+
}
|
|
35962
|
+
} catch {
|
|
35963
|
+
options.log?.("Ignoring a malformed gateway message.");
|
|
35964
|
+
return;
|
|
35965
|
+
}
|
|
35966
|
+
void handleMessage(options, client, socket, raw, pending, forwarder);
|
|
35967
|
+
});
|
|
35968
|
+
socket.on("close", () => {
|
|
35969
|
+
forwarder.close();
|
|
35970
|
+
for (const controller2 of pending.values()) controller2.abort();
|
|
35971
|
+
});
|
|
35972
|
+
await new Promise((resolve, reject) => {
|
|
35973
|
+
socket.once("close", resolve);
|
|
35974
|
+
socket.once("error", reject);
|
|
35975
|
+
}).finally(() => signal?.removeEventListener("abort", stop2));
|
|
35976
|
+
} finally {
|
|
35977
|
+
disposeSocket?.();
|
|
35978
|
+
await client.close().catch(() => void 0);
|
|
35979
|
+
}
|
|
35549
35980
|
}
|
|
35550
|
-
async function handleMessage(options, client, socket, raw) {
|
|
35981
|
+
async function handleMessage(options, client, socket, raw, pending, forwarder) {
|
|
35551
35982
|
let requestId;
|
|
35983
|
+
let controller2;
|
|
35984
|
+
let deadlineTimer;
|
|
35552
35985
|
try {
|
|
35553
|
-
const
|
|
35554
|
-
|
|
35986
|
+
const payload = JSON.parse(raw.toString());
|
|
35987
|
+
const envelope = requestEnvelope.safeParse(payload);
|
|
35988
|
+
if (!envelope.success) {
|
|
35989
|
+
options.log?.("Ignoring a request with an invalid envelope.");
|
|
35990
|
+
return;
|
|
35991
|
+
}
|
|
35992
|
+
requestId = envelope.data.requestId;
|
|
35993
|
+
const parsed = requestSchema.safeParse(payload);
|
|
35994
|
+
if (!parsed.success) {
|
|
35995
|
+
const tool = payload && typeof payload === "object" && "tool" in payload ? payload.tool : void 0;
|
|
35996
|
+
if (typeof tool === "string" && !supportedAgentTools.includes(tool)) {
|
|
35997
|
+
throw new Error(
|
|
35998
|
+
"UNSUPPORTED_TOOL: " + tool + ". Update the local companion."
|
|
35999
|
+
);
|
|
36000
|
+
}
|
|
36001
|
+
throw new Error("INVALID_REQUEST: Invalid tool arguments or deadline.");
|
|
36002
|
+
}
|
|
36003
|
+
if (pending.has(requestId)) {
|
|
36004
|
+
throw new Error(
|
|
36005
|
+
"DUPLICATE_REQUEST: This request is already in progress."
|
|
36006
|
+
);
|
|
36007
|
+
}
|
|
36008
|
+
const deadlineAt = parsed.data.deadlineAt ?? Date.now() + DEFAULT_REQUEST_LIFETIME_MS;
|
|
36009
|
+
if (deadlineAt <= Date.now()) {
|
|
36010
|
+
throw new Error("REQUEST_EXPIRED: The request deadline has passed.");
|
|
36011
|
+
}
|
|
36012
|
+
controller2 = new AbortController();
|
|
36013
|
+
pending.set(requestId, controller2);
|
|
36014
|
+
deadlineTimer = setTimeout(
|
|
36015
|
+
() => controller2?.abort(),
|
|
36016
|
+
Math.max(0, deadlineAt - Date.now())
|
|
36017
|
+
);
|
|
35555
36018
|
await authorizeToolCall(
|
|
35556
|
-
parsed.tool,
|
|
35557
|
-
parsed.arguments,
|
|
36019
|
+
parsed.data.tool,
|
|
36020
|
+
parsed.data.arguments,
|
|
35558
36021
|
options.config.MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE === "1",
|
|
35559
|
-
options.approve
|
|
36022
|
+
options.approve,
|
|
36023
|
+
controller2.signal
|
|
35560
36024
|
);
|
|
36025
|
+
assertActive(controller2.signal);
|
|
35561
36026
|
const result = await client.callTool({
|
|
35562
|
-
name: parsed.tool,
|
|
35563
|
-
arguments: parsed.arguments
|
|
36027
|
+
name: parsed.data.tool,
|
|
36028
|
+
arguments: parsed.data.arguments
|
|
35564
36029
|
});
|
|
35565
36030
|
if (result.isError) {
|
|
35566
36031
|
const content = Array.isArray(result.content) ? result.content : [];
|
|
@@ -35577,6 +36042,12 @@ async function handleMessage(options, client, socket, raw) {
|
|
|
35577
36042
|
result: result.structuredContent ?? { content: result.content }
|
|
35578
36043
|
})
|
|
35579
36044
|
);
|
|
36045
|
+
if (parsed.data.tool === "start_process") {
|
|
36046
|
+
const snapshot = result.structuredContent;
|
|
36047
|
+
if (snapshot && typeof snapshot === "object" && "sessionId" in snapshot && typeof snapshot.sessionId === "string" && "taskId" in snapshot && typeof snapshot.taskId === "string") {
|
|
36048
|
+
forwarder.watch(snapshot.sessionId);
|
|
36049
|
+
}
|
|
36050
|
+
}
|
|
35580
36051
|
}
|
|
35581
36052
|
} catch (error62) {
|
|
35582
36053
|
if (!requestId || socket.readyState !== wrapper_default.OPEN) return;
|
|
@@ -35587,18 +36058,24 @@ async function handleMessage(options, client, socket, raw) {
|
|
|
35587
36058
|
error: error62 instanceof Error ? error62.message : "Local request failed."
|
|
35588
36059
|
})
|
|
35589
36060
|
);
|
|
36061
|
+
} finally {
|
|
36062
|
+
if (deadlineTimer) clearTimeout(deadlineTimer);
|
|
36063
|
+
if (requestId && controller2 && pending.get(requestId) === controller2) {
|
|
36064
|
+
pending.delete(requestId);
|
|
36065
|
+
}
|
|
35590
36066
|
}
|
|
35591
36067
|
}
|
|
35592
36068
|
async function runAgent(options) {
|
|
35593
36069
|
let reconnectDelayMs = 1e3;
|
|
36070
|
+
const acknowledged = /* @__PURE__ */ new Map();
|
|
35594
36071
|
while (!options.signal?.aborted) {
|
|
35595
36072
|
try {
|
|
35596
|
-
await connectOnce(options);
|
|
36073
|
+
await connectOnce(options, acknowledged);
|
|
35597
36074
|
reconnectDelayMs = 1e3;
|
|
35598
36075
|
} catch (error62) {
|
|
35599
36076
|
if (options.signal?.aborted) break;
|
|
35600
36077
|
options.log?.(
|
|
35601
|
-
|
|
36078
|
+
"Connection failed: " + (error62 instanceof Error ? error62.message : "unknown error")
|
|
35602
36079
|
);
|
|
35603
36080
|
}
|
|
35604
36081
|
if (options.signal?.aborted) break;
|
|
@@ -35622,7 +36099,9 @@ import os2 from "node:os";
|
|
|
35622
36099
|
var schema = external_exports.object({
|
|
35623
36100
|
MACHINE_TERMINAL_GATEWAY_URL: external_exports.string().url(),
|
|
35624
36101
|
MACHINE_TERMINAL_DEVICE_TOKEN: external_exports.string().min(1),
|
|
36102
|
+
MACHINE_TERMINAL_LOCAL_AGENT_TOKEN: external_exports.string().regex(/^[a-f0-9]{64}$/i).optional(),
|
|
35625
36103
|
MACHINE_TERMINAL_DEVICE_ID: external_exports.string().min(1).max(128),
|
|
36104
|
+
MACHINE_TERMINAL_DEVICE_SECRET: external_exports.string().regex(/^[a-f0-9]{64}$/i).optional(),
|
|
35626
36105
|
MACHINE_TERMINAL_DEVICE_NAME: external_exports.string().min(1).max(128).default(os2.hostname()),
|
|
35627
36106
|
MACHINE_TERMINAL_LOCAL_MCP_URL: external_exports.string().url().default("http://127.0.0.1:3333/mcp"),
|
|
35628
36107
|
MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE: external_exports.enum(["0", "1"]).default("0"),
|
|
@@ -35670,55 +36149,78 @@ var dim = (text) => paint("dim", text);
|
|
|
35670
36149
|
var warn = (text) => paint("yellow", text);
|
|
35671
36150
|
|
|
35672
36151
|
// src/approval.ts
|
|
36152
|
+
var DEFAULT_APPROVAL_TIMEOUT_MS = 3e4;
|
|
36153
|
+
function safeLabel(value) {
|
|
36154
|
+
return JSON.stringify(String(value));
|
|
36155
|
+
}
|
|
35673
36156
|
function approvalSummary(tool, args) {
|
|
35674
36157
|
if (tool === "write_text_file") {
|
|
35675
36158
|
const bytes = Buffer.byteLength(String(args.contents ?? ""));
|
|
35676
|
-
return
|
|
36159
|
+
return "Write " + bytes + " bytes to " + safeLabel(args.path ?? "an unknown path") + (args.overwrite ? " (overwrite)" : "");
|
|
35677
36160
|
}
|
|
35678
36161
|
if (tool === "start_process") {
|
|
35679
36162
|
const command = [
|
|
35680
|
-
|
|
35681
|
-
...Array.isArray(args.args) ? args.args.map(
|
|
36163
|
+
safeLabel(args.program ?? ""),
|
|
36164
|
+
...Array.isArray(args.args) ? args.args.map(safeLabel) : []
|
|
35682
36165
|
];
|
|
35683
|
-
return
|
|
36166
|
+
return "Start: " + command.join(" ") + " (cwd: " + safeLabel(args.cwd ?? "configured root") + ")";
|
|
35684
36167
|
}
|
|
35685
36168
|
if (tool === "terminate_process") {
|
|
35686
|
-
return
|
|
36169
|
+
return "Terminate process session " + safeLabel(args.sessionId ?? "");
|
|
35687
36170
|
}
|
|
35688
|
-
return
|
|
36171
|
+
return "Run destructive tool: " + safeLabel(tool);
|
|
35689
36172
|
}
|
|
35690
36173
|
function createAutoApprover() {
|
|
35691
|
-
return (tool, args) => {
|
|
36174
|
+
return (tool, args, signal) => {
|
|
36175
|
+
if (signal?.aborted) return Promise.resolve(false);
|
|
35692
36176
|
console.log(
|
|
35693
|
-
|
|
35694
|
-
${warn("Approval requested:")} ${approvalSummary(tool, args)} ${dim("(auto-approved)")}`
|
|
36177
|
+
"\n" + warn("Approval requested:") + " " + approvalSummary(tool, args) + " " + dim("(auto-approved)")
|
|
35695
36178
|
);
|
|
35696
36179
|
return Promise.resolve(true);
|
|
35697
36180
|
};
|
|
35698
36181
|
}
|
|
35699
|
-
function createTerminalApprover() {
|
|
35700
|
-
|
|
35701
|
-
|
|
35702
|
-
|
|
35703
|
-
|
|
36182
|
+
function createTerminalApprover(timeoutMs = DEFAULT_APPROVAL_TIMEOUT_MS) {
|
|
36183
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1) {
|
|
36184
|
+
throw new Error("Approval timeout must be a positive integer.");
|
|
36185
|
+
}
|
|
36186
|
+
let queue = Promise.resolve();
|
|
36187
|
+
return (tool, args, signal) => {
|
|
36188
|
+
const next = queue.then(async () => {
|
|
36189
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY || signal?.aborted) {
|
|
36190
|
+
return false;
|
|
36191
|
+
}
|
|
36192
|
+
const controller2 = new AbortController();
|
|
36193
|
+
const abort = () => controller2.abort();
|
|
36194
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
36195
|
+
const timer = setTimeout(abort, timeoutMs);
|
|
35704
36196
|
const prompt = createInterface({
|
|
35705
36197
|
input: process.stdin,
|
|
35706
36198
|
output: process.stdout
|
|
35707
36199
|
});
|
|
35708
36200
|
try {
|
|
36201
|
+
if (signal?.aborted) return false;
|
|
35709
36202
|
console.log(
|
|
35710
|
-
|
|
35711
|
-
${warn("Approval requested:")} ${approvalSummary(tool, args)}`
|
|
36203
|
+
"\n" + warn("Approval requested:") + " " + approvalSummary(tool, args)
|
|
35712
36204
|
);
|
|
35713
36205
|
const answer = await prompt.question(
|
|
35714
|
-
|
|
36206
|
+
bold("Approve this action?") + " [y/N] ",
|
|
36207
|
+
{ signal: controller2.signal }
|
|
35715
36208
|
);
|
|
35716
|
-
return (/* @__PURE__ */ new Set(["y", "yes"])).has(answer.trim().toLowerCase());
|
|
36209
|
+
return !controller2.signal.aborted && (/* @__PURE__ */ new Set(["y", "yes"])).has(answer.trim().toLowerCase());
|
|
36210
|
+
} catch (error62) {
|
|
36211
|
+
if (controller2.signal.aborted) return false;
|
|
36212
|
+
throw error62;
|
|
35717
36213
|
} finally {
|
|
36214
|
+
clearTimeout(timer);
|
|
36215
|
+
signal?.removeEventListener("abort", abort);
|
|
35718
36216
|
prompt.close();
|
|
35719
36217
|
}
|
|
35720
36218
|
});
|
|
35721
|
-
|
|
36219
|
+
queue = next.then(
|
|
36220
|
+
() => void 0,
|
|
36221
|
+
() => void 0
|
|
36222
|
+
);
|
|
36223
|
+
return next;
|
|
35722
36224
|
};
|
|
35723
36225
|
}
|
|
35724
36226
|
|