@ai-ecoverse/mcpcat 0.14.0 → 0.14.2
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/THIRD_PARTY_NOTICES +77 -1
- package/dist/host/mcpcat.mjs +361 -211
- package/dist/kernel/mcpcat.mjs +348 -211
- package/package.json +2 -2
package/dist/kernel/mcpcat.mjs
CHANGED
|
@@ -167,6 +167,101 @@ var init_node_stub = __esm({
|
|
|
167
167
|
}
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
+
// src/config/lock.ts
|
|
171
|
+
var lock_exports = {};
|
|
172
|
+
__export(lock_exports, {
|
|
173
|
+
acquireLock: () => acquireLock
|
|
174
|
+
});
|
|
175
|
+
function isEexist(err) {
|
|
176
|
+
if (err instanceof Error && "code" in err)
|
|
177
|
+
return err.code === "EEXIST";
|
|
178
|
+
if (err instanceof Error) return err.message.includes("EEXIST");
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
async function removeStaleLockDir(fs, lockPath) {
|
|
182
|
+
try {
|
|
183
|
+
const s = await fs.stat(lockPath);
|
|
184
|
+
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
185
|
+
try {
|
|
186
|
+
await fs.rmdir(lockPath);
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
async function removeStaleLockFile(fs, lockPath) {
|
|
196
|
+
try {
|
|
197
|
+
const s = await fs.stat(lockPath);
|
|
198
|
+
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
199
|
+
try {
|
|
200
|
+
await fs.unlink(lockPath);
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
async function acquireLock(fs, configPath) {
|
|
210
|
+
const useFile = typeof fs.createExclusive === "function";
|
|
211
|
+
const lockPath = `${configPath}.lock`;
|
|
212
|
+
const start = Date.now();
|
|
213
|
+
const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
|
|
214
|
+
if (parentDir) {
|
|
215
|
+
try {
|
|
216
|
+
await fs.mkdir(parentDir);
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
while (true) {
|
|
221
|
+
try {
|
|
222
|
+
if (useFile) {
|
|
223
|
+
await fs.createExclusive(lockPath);
|
|
224
|
+
} else {
|
|
225
|
+
await fs.mkdirExclusive(lockPath);
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
} catch (err) {
|
|
229
|
+
if (!isEexist(err)) throw err;
|
|
230
|
+
if (useFile) {
|
|
231
|
+
await removeStaleLockFile(fs, lockPath);
|
|
232
|
+
} else {
|
|
233
|
+
await removeStaleLockDir(fs, lockPath);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (Date.now() - start > LOCK_MAX_WAIT_MS) {
|
|
237
|
+
throw new Error(`mcpcat: could not acquire lock ${lockPath} within ${LOCK_MAX_WAIT_MS}ms`);
|
|
238
|
+
}
|
|
239
|
+
await new Promise((r) => setTimeout(r, LOCK_POLL_MS));
|
|
240
|
+
}
|
|
241
|
+
let released = false;
|
|
242
|
+
return async () => {
|
|
243
|
+
if (released) return;
|
|
244
|
+
released = true;
|
|
245
|
+
try {
|
|
246
|
+
if (useFile && fs.unlink) {
|
|
247
|
+
await fs.unlink(lockPath);
|
|
248
|
+
} else {
|
|
249
|
+
await fs.rmdir(lockPath);
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
var LOCK_STALE_MS, LOCK_POLL_MS, LOCK_MAX_WAIT_MS;
|
|
256
|
+
var init_lock = __esm({
|
|
257
|
+
"src/config/lock.ts"() {
|
|
258
|
+
"use strict";
|
|
259
|
+
LOCK_STALE_MS = 1e4;
|
|
260
|
+
LOCK_POLL_MS = 50;
|
|
261
|
+
LOCK_MAX_WAIT_MS = 5e3;
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
170
265
|
// node_modules/@earendil-works/pi-codemode/dist/identifier.js
|
|
171
266
|
function toCodemodeIdentifier(name) {
|
|
172
267
|
let identifier = "";
|
|
@@ -5966,7 +6061,7 @@ var McpOAuthProvider = class {
|
|
|
5966
6061
|
};
|
|
5967
6062
|
|
|
5968
6063
|
// src/auth/jerk-token-source.ts
|
|
5969
|
-
var MIN_VERSION = [0,
|
|
6064
|
+
var MIN_VERSION = [0, 14, 1];
|
|
5970
6065
|
var VERSION_TIMEOUT_MS = 2e3;
|
|
5971
6066
|
var cachedAvailable;
|
|
5972
6067
|
function parseVersion(raw) {
|
|
@@ -6092,6 +6187,192 @@ var JerkTokenSource = class {
|
|
|
6092
6187
|
}
|
|
6093
6188
|
};
|
|
6094
6189
|
|
|
6190
|
+
// src/auth/pi-store.ts
|
|
6191
|
+
init_lock();
|
|
6192
|
+
|
|
6193
|
+
// src/config/paths.ts
|
|
6194
|
+
function userConfigDir(platform2) {
|
|
6195
|
+
const piDir = platform2.env.PI_CODING_AGENT_DIR;
|
|
6196
|
+
if (piDir) return piDir;
|
|
6197
|
+
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
6198
|
+
return `${home}/.pi/agent`;
|
|
6199
|
+
}
|
|
6200
|
+
function userConfigPath(platform2) {
|
|
6201
|
+
return `${userConfigDir(platform2)}/mcp.json`;
|
|
6202
|
+
}
|
|
6203
|
+
function projectConfigPath(platform2) {
|
|
6204
|
+
return `${platform2.cwd}/.pi/mcp.json`;
|
|
6205
|
+
}
|
|
6206
|
+
function stateDir(platform2) {
|
|
6207
|
+
if (platform2.env.XDG_STATE_HOME) return `${platform2.env.XDG_STATE_HOME}/mcpcat`;
|
|
6208
|
+
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
6209
|
+
if (platform2.env.LOCALAPPDATA) return `${platform2.env.LOCALAPPDATA}\\mcpcat\\state`;
|
|
6210
|
+
if (platform2.env.SLICC_KERNEL) return `${home}/.local/state/mcpcat`;
|
|
6211
|
+
return `${home}/Library/Application Support/mcpcat`;
|
|
6212
|
+
}
|
|
6213
|
+
|
|
6214
|
+
// src/auth/token-source.ts
|
|
6215
|
+
function mcpNamespace(server) {
|
|
6216
|
+
return `mcp__${server.replace(/-/g, "_")}`;
|
|
6217
|
+
}
|
|
6218
|
+
function storeKey(name, serverUrl) {
|
|
6219
|
+
const ns = mcpNamespace(name);
|
|
6220
|
+
const url = String(new URL(serverUrl));
|
|
6221
|
+
return `${ns}|${url}`;
|
|
6222
|
+
}
|
|
6223
|
+
function isKernel(platform2) {
|
|
6224
|
+
return platform2.kind === "kernel";
|
|
6225
|
+
}
|
|
6226
|
+
|
|
6227
|
+
// src/auth/pi-store.ts
|
|
6228
|
+
var AUTH_FILE = "mcp-auth.json";
|
|
6229
|
+
var FILE_MODE = 384;
|
|
6230
|
+
var DIR_MODE = 448;
|
|
6231
|
+
function parseStates(raw) {
|
|
6232
|
+
if (!raw?.trim()) return {};
|
|
6233
|
+
const parsed = JSON.parse(raw);
|
|
6234
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
6235
|
+
}
|
|
6236
|
+
function serializeStates(states) {
|
|
6237
|
+
return `${JSON.stringify(states, null, 2)}
|
|
6238
|
+
`;
|
|
6239
|
+
}
|
|
6240
|
+
function legacyKey(serverUrl) {
|
|
6241
|
+
return String(new URL(serverUrl));
|
|
6242
|
+
}
|
|
6243
|
+
var PiFileTokenSource = class {
|
|
6244
|
+
platform;
|
|
6245
|
+
authPath;
|
|
6246
|
+
dirPath;
|
|
6247
|
+
constructor(platform2) {
|
|
6248
|
+
this.platform = platform2;
|
|
6249
|
+
this.dirPath = userConfigDir(platform2);
|
|
6250
|
+
this.authPath = `${this.dirPath}/${AUTH_FILE}`;
|
|
6251
|
+
}
|
|
6252
|
+
get path() {
|
|
6253
|
+
return this.authPath;
|
|
6254
|
+
}
|
|
6255
|
+
async loadWithoutLock(name, serverUrl) {
|
|
6256
|
+
const key = storeKey(name, serverUrl);
|
|
6257
|
+
const legacy = legacyKey(serverUrl);
|
|
6258
|
+
try {
|
|
6259
|
+
const raw = await this.readRaw();
|
|
6260
|
+
const states = parseStates(raw);
|
|
6261
|
+
return states[key] ?? states[legacy];
|
|
6262
|
+
} catch {
|
|
6263
|
+
return void 0;
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
6266
|
+
async getToken(name, serverUrl) {
|
|
6267
|
+
const state = await this.load(name, serverUrl);
|
|
6268
|
+
if (!state?.tokens?.access_token) return void 0;
|
|
6269
|
+
return {
|
|
6270
|
+
accessToken: state.tokens.access_token,
|
|
6271
|
+
expiresAt: state.tokensExpireAt
|
|
6272
|
+
};
|
|
6273
|
+
}
|
|
6274
|
+
async load(name, serverUrl) {
|
|
6275
|
+
const key = storeKey(name, serverUrl);
|
|
6276
|
+
const legacy = legacyKey(serverUrl);
|
|
6277
|
+
const release = await this.acquireLock();
|
|
6278
|
+
try {
|
|
6279
|
+
const raw = await this.readRaw();
|
|
6280
|
+
const states = parseStates(raw);
|
|
6281
|
+
if (states[key]) return states[key];
|
|
6282
|
+
if (states[legacy]) {
|
|
6283
|
+
states[key] = states[legacy];
|
|
6284
|
+
delete states[legacy];
|
|
6285
|
+
await this.writeStates(states);
|
|
6286
|
+
return states[key];
|
|
6287
|
+
}
|
|
6288
|
+
return void 0;
|
|
6289
|
+
} finally {
|
|
6290
|
+
await release();
|
|
6291
|
+
}
|
|
6292
|
+
}
|
|
6293
|
+
async save(name, serverUrl, state) {
|
|
6294
|
+
const key = storeKey(name, serverUrl);
|
|
6295
|
+
const release = await this.acquireLock();
|
|
6296
|
+
try {
|
|
6297
|
+
const raw = await this.readRaw();
|
|
6298
|
+
const states = parseStates(raw);
|
|
6299
|
+
states[key] = state;
|
|
6300
|
+
await this.writeStates(states);
|
|
6301
|
+
} finally {
|
|
6302
|
+
await release();
|
|
6303
|
+
}
|
|
6304
|
+
}
|
|
6305
|
+
async remove(name, serverUrl) {
|
|
6306
|
+
const key = storeKey(name, serverUrl);
|
|
6307
|
+
const legacy = legacyKey(serverUrl);
|
|
6308
|
+
const release = await this.acquireLock();
|
|
6309
|
+
try {
|
|
6310
|
+
const raw = await this.readRaw();
|
|
6311
|
+
const states = parseStates(raw);
|
|
6312
|
+
const stored = key in states ? key : legacy in states ? legacy : void 0;
|
|
6313
|
+
if (!stored) return false;
|
|
6314
|
+
delete states[stored];
|
|
6315
|
+
await this.writeStates(states);
|
|
6316
|
+
return true;
|
|
6317
|
+
} finally {
|
|
6318
|
+
await release();
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
async acquireLock() {
|
|
6322
|
+
await this.ensureDir();
|
|
6323
|
+
return acquireLock(this.platform.fs, this.authPath);
|
|
6324
|
+
}
|
|
6325
|
+
async ensureDir() {
|
|
6326
|
+
const fs = this.platform.fs;
|
|
6327
|
+
try {
|
|
6328
|
+
await fs.mkdir(this.dirPath, { mode: DIR_MODE });
|
|
6329
|
+
} catch {
|
|
6330
|
+
}
|
|
6331
|
+
if (fs.chmod) {
|
|
6332
|
+
try {
|
|
6333
|
+
await fs.chmod(this.dirPath, DIR_MODE);
|
|
6334
|
+
} catch {
|
|
6335
|
+
}
|
|
6336
|
+
const piDir = this.dirPath.slice(0, this.dirPath.lastIndexOf("/"));
|
|
6337
|
+
if (piDir) {
|
|
6338
|
+
try {
|
|
6339
|
+
await fs.chmod(piDir, DIR_MODE);
|
|
6340
|
+
} catch {
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
}
|
|
6345
|
+
async readRaw() {
|
|
6346
|
+
try {
|
|
6347
|
+
const data = await this.platform.fs.readFile(this.authPath);
|
|
6348
|
+
return new TextDecoder().decode(data);
|
|
6349
|
+
} catch {
|
|
6350
|
+
return void 0;
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
async writeStates(states) {
|
|
6354
|
+
const fs = this.platform.fs;
|
|
6355
|
+
const content = serializeStates(states);
|
|
6356
|
+
const tmpPath = `${this.authPath}.tmp.${Date.now()}`;
|
|
6357
|
+
try {
|
|
6358
|
+
await fs.writeFile(tmpPath, content, { mode: FILE_MODE });
|
|
6359
|
+
await fs.rename(tmpPath, this.authPath);
|
|
6360
|
+
} catch (renameErr) {
|
|
6361
|
+
try {
|
|
6362
|
+
if (fs.unlink) await fs.unlink(tmpPath);
|
|
6363
|
+
} catch {
|
|
6364
|
+
}
|
|
6365
|
+
throw renameErr;
|
|
6366
|
+
}
|
|
6367
|
+
if (fs.chmod) {
|
|
6368
|
+
try {
|
|
6369
|
+
await fs.chmod(this.authPath, FILE_MODE);
|
|
6370
|
+
} catch {
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
}
|
|
6374
|
+
};
|
|
6375
|
+
|
|
6095
6376
|
// src/auth/auth-provider.ts
|
|
6096
6377
|
var CIMD_URL = "https://mcpcat.sliccy.ai/oauth/client.json";
|
|
6097
6378
|
var CLIENT_NAME = "mcpcat";
|
|
@@ -6113,10 +6394,12 @@ var SourceBackedStore = class {
|
|
|
6113
6394
|
source;
|
|
6114
6395
|
name;
|
|
6115
6396
|
serverUrl;
|
|
6116
|
-
|
|
6397
|
+
platform;
|
|
6398
|
+
constructor(source, name, serverUrl, platform2) {
|
|
6117
6399
|
this.source = source;
|
|
6118
6400
|
this.name = name;
|
|
6119
6401
|
this.serverUrl = serverUrl;
|
|
6402
|
+
this.platform = platform2;
|
|
6120
6403
|
}
|
|
6121
6404
|
async load() {
|
|
6122
6405
|
return this.source.load(this.name, this.serverUrl);
|
|
@@ -6124,6 +6407,17 @@ var SourceBackedStore = class {
|
|
|
6124
6407
|
async save(state) {
|
|
6125
6408
|
await this.source.save(this.name, this.serverUrl, state);
|
|
6126
6409
|
}
|
|
6410
|
+
async withRefreshLock(fn) {
|
|
6411
|
+
if (!(this.source instanceof PiFileTokenSource)) return fn();
|
|
6412
|
+
const refreshPath = `${this.source.path}.refresh`;
|
|
6413
|
+
const { acquireLock: acquireLock2 } = await Promise.resolve().then(() => (init_lock(), lock_exports));
|
|
6414
|
+
const release = await acquireLock2(this.platform.fs, refreshPath);
|
|
6415
|
+
try {
|
|
6416
|
+
return await fn();
|
|
6417
|
+
} finally {
|
|
6418
|
+
await release();
|
|
6419
|
+
}
|
|
6420
|
+
}
|
|
6127
6421
|
};
|
|
6128
6422
|
function cimdFn(redirectUrl) {
|
|
6129
6423
|
return (metadata) => {
|
|
@@ -6152,10 +6446,9 @@ function createMcpAuthProvider(options) {
|
|
|
6152
6446
|
function createJerkAuthProvider(options) {
|
|
6153
6447
|
const { name, serverUrl, source } = options;
|
|
6154
6448
|
let currentToken;
|
|
6449
|
+
const _fetched = false;
|
|
6155
6450
|
return {
|
|
6156
6451
|
async token() {
|
|
6157
|
-
const result = await source.getToken(name, serverUrl);
|
|
6158
|
-
currentToken = result?.accessToken;
|
|
6159
6452
|
return currentToken;
|
|
6160
6453
|
},
|
|
6161
6454
|
async onUnauthorized(context) {
|
|
@@ -6186,10 +6479,10 @@ function createJerkAuthProvider(options) {
|
|
|
6186
6479
|
}
|
|
6187
6480
|
function createPiAuthProvider(options) {
|
|
6188
6481
|
const { name, serverUrl, source, oauth, platform: platform2 } = options;
|
|
6189
|
-
const store = new SourceBackedStore(source, name, serverUrl);
|
|
6482
|
+
const store = new SourceBackedStore(source, name, serverUrl, platform2);
|
|
6190
6483
|
let refreshing;
|
|
6191
6484
|
const refresh = async (staleToken) => {
|
|
6192
|
-
refreshing ??= (async () => {
|
|
6485
|
+
refreshing ??= store.withRefreshLock(async () => {
|
|
6193
6486
|
const state = await store.load();
|
|
6194
6487
|
if (state?.tokens?.access_token !== staleToken) return;
|
|
6195
6488
|
if (!state?.tokens?.refresh_token) throw new McpOAuthAuthorizationRequiredError();
|
|
@@ -6212,7 +6505,7 @@ function createPiAuthProvider(options) {
|
|
|
6212
6505
|
fetch: timedFetch(platform2.fetch)
|
|
6213
6506
|
});
|
|
6214
6507
|
if (result === "REDIRECT") throw new McpOAuthAuthorizationRequiredError();
|
|
6215
|
-
})
|
|
6508
|
+
}).finally(() => {
|
|
6216
6509
|
refreshing = void 0;
|
|
6217
6510
|
});
|
|
6218
6511
|
return refreshing;
|
|
@@ -9192,27 +9485,6 @@ async function loadConfig(input) {
|
|
|
9192
9485
|
};
|
|
9193
9486
|
}
|
|
9194
9487
|
|
|
9195
|
-
// src/config/paths.ts
|
|
9196
|
-
function userConfigDir(platform2) {
|
|
9197
|
-
const piDir = platform2.env.PI_CODING_AGENT_DIR;
|
|
9198
|
-
if (piDir) return piDir;
|
|
9199
|
-
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
9200
|
-
return `${home}/.pi/agent`;
|
|
9201
|
-
}
|
|
9202
|
-
function userConfigPath(platform2) {
|
|
9203
|
-
return `${userConfigDir(platform2)}/mcp.json`;
|
|
9204
|
-
}
|
|
9205
|
-
function projectConfigPath(platform2) {
|
|
9206
|
-
return `${platform2.cwd}/.pi/mcp.json`;
|
|
9207
|
-
}
|
|
9208
|
-
function stateDir(platform2) {
|
|
9209
|
-
if (platform2.env.XDG_STATE_HOME) return `${platform2.env.XDG_STATE_HOME}/mcpcat`;
|
|
9210
|
-
const home = platform2.env.HOME ?? platform2.env.USERPROFILE ?? "/";
|
|
9211
|
-
if (platform2.env.LOCALAPPDATA) return `${platform2.env.LOCALAPPDATA}\\mcpcat\\state`;
|
|
9212
|
-
if (platform2.env.SLICC_KERNEL) return `${home}/.local/state/mcpcat`;
|
|
9213
|
-
return `${home}/Library/Application Support/mcpcat`;
|
|
9214
|
-
}
|
|
9215
|
-
|
|
9216
9488
|
// src/config/index.ts
|
|
9217
9489
|
var MergedConfigSource = class _MergedConfigSource {
|
|
9218
9490
|
entries;
|
|
@@ -9252,65 +9524,8 @@ var MergedConfigSource = class _MergedConfigSource {
|
|
|
9252
9524
|
}
|
|
9253
9525
|
};
|
|
9254
9526
|
|
|
9255
|
-
// src/config/lock.ts
|
|
9256
|
-
var LOCK_STALE_MS = 1e4;
|
|
9257
|
-
var LOCK_POLL_MS = 50;
|
|
9258
|
-
var LOCK_MAX_WAIT_MS = 5e3;
|
|
9259
|
-
function isEexist(err) {
|
|
9260
|
-
if (err instanceof Error && "code" in err)
|
|
9261
|
-
return err.code === "EEXIST";
|
|
9262
|
-
if (err instanceof Error) return err.message.includes("EEXIST");
|
|
9263
|
-
return false;
|
|
9264
|
-
}
|
|
9265
|
-
async function removeStaleLock(fs, lockPath) {
|
|
9266
|
-
try {
|
|
9267
|
-
const s = await fs.stat(lockPath);
|
|
9268
|
-
if (Date.now() - s.mtimeMs > LOCK_STALE_MS) {
|
|
9269
|
-
try {
|
|
9270
|
-
await fs.rmdir(lockPath);
|
|
9271
|
-
} catch {
|
|
9272
|
-
}
|
|
9273
|
-
return true;
|
|
9274
|
-
}
|
|
9275
|
-
} catch {
|
|
9276
|
-
}
|
|
9277
|
-
return false;
|
|
9278
|
-
}
|
|
9279
|
-
async function acquireLock(fs, configPath) {
|
|
9280
|
-
const lockPath = `${configPath}.lock`;
|
|
9281
|
-
const start = Date.now();
|
|
9282
|
-
const parentDir = lockPath.slice(0, lockPath.lastIndexOf("/"));
|
|
9283
|
-
if (parentDir) {
|
|
9284
|
-
try {
|
|
9285
|
-
await fs.mkdir(parentDir);
|
|
9286
|
-
} catch {
|
|
9287
|
-
}
|
|
9288
|
-
}
|
|
9289
|
-
while (true) {
|
|
9290
|
-
try {
|
|
9291
|
-
await fs.mkdirExclusive(lockPath);
|
|
9292
|
-
break;
|
|
9293
|
-
} catch (err) {
|
|
9294
|
-
if (!isEexist(err)) throw err;
|
|
9295
|
-
await removeStaleLock(fs, lockPath);
|
|
9296
|
-
}
|
|
9297
|
-
if (Date.now() - start > LOCK_MAX_WAIT_MS) {
|
|
9298
|
-
throw new Error(`mcpcat: could not acquire lock ${lockPath} within ${LOCK_MAX_WAIT_MS}ms`);
|
|
9299
|
-
}
|
|
9300
|
-
await new Promise((r) => setTimeout(r, LOCK_POLL_MS));
|
|
9301
|
-
}
|
|
9302
|
-
let released = false;
|
|
9303
|
-
return async () => {
|
|
9304
|
-
if (released) return;
|
|
9305
|
-
released = true;
|
|
9306
|
-
try {
|
|
9307
|
-
await fs.rmdir(lockPath);
|
|
9308
|
-
} catch {
|
|
9309
|
-
}
|
|
9310
|
-
};
|
|
9311
|
-
}
|
|
9312
|
-
|
|
9313
9527
|
// src/config/edit.ts
|
|
9528
|
+
init_lock();
|
|
9314
9529
|
function isRecord2(value) {
|
|
9315
9530
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9316
9531
|
}
|
|
@@ -12555,124 +12770,6 @@ async function signIn(options) {
|
|
|
12555
12770
|
}
|
|
12556
12771
|
}
|
|
12557
12772
|
|
|
12558
|
-
// src/auth/token-source.ts
|
|
12559
|
-
function mcpNamespace(server) {
|
|
12560
|
-
return `mcp__${server.replace(/-/g, "_")}`;
|
|
12561
|
-
}
|
|
12562
|
-
function storeKey(name, serverUrl) {
|
|
12563
|
-
const ns = mcpNamespace(name);
|
|
12564
|
-
const url = String(new URL(serverUrl));
|
|
12565
|
-
return `${ns}|${url}`;
|
|
12566
|
-
}
|
|
12567
|
-
function isKernel(platform2) {
|
|
12568
|
-
return platform2.kind === "kernel";
|
|
12569
|
-
}
|
|
12570
|
-
|
|
12571
|
-
// src/auth/pi-store.ts
|
|
12572
|
-
var AUTH_FILE = "mcp-auth.json";
|
|
12573
|
-
var FILE_MODE = 384;
|
|
12574
|
-
function parseStates(raw) {
|
|
12575
|
-
if (!raw?.trim()) return {};
|
|
12576
|
-
const parsed = JSON.parse(raw);
|
|
12577
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
12578
|
-
}
|
|
12579
|
-
function serializeStates(states) {
|
|
12580
|
-
return `${JSON.stringify(states, null, 2)}
|
|
12581
|
-
`;
|
|
12582
|
-
}
|
|
12583
|
-
function legacyKey(serverUrl) {
|
|
12584
|
-
return String(new URL(serverUrl));
|
|
12585
|
-
}
|
|
12586
|
-
var PiFileTokenSource = class {
|
|
12587
|
-
platform;
|
|
12588
|
-
authPath;
|
|
12589
|
-
dirPath;
|
|
12590
|
-
constructor(platform2) {
|
|
12591
|
-
this.platform = platform2;
|
|
12592
|
-
this.dirPath = userConfigDir(platform2);
|
|
12593
|
-
this.authPath = `${this.dirPath}/${AUTH_FILE}`;
|
|
12594
|
-
}
|
|
12595
|
-
get path() {
|
|
12596
|
-
return this.authPath;
|
|
12597
|
-
}
|
|
12598
|
-
async getToken(name, serverUrl) {
|
|
12599
|
-
const state = await this.load(name, serverUrl);
|
|
12600
|
-
if (!state?.tokens?.access_token) return void 0;
|
|
12601
|
-
return {
|
|
12602
|
-
accessToken: state.tokens.access_token,
|
|
12603
|
-
expiresAt: state.tokensExpireAt
|
|
12604
|
-
};
|
|
12605
|
-
}
|
|
12606
|
-
async load(name, serverUrl) {
|
|
12607
|
-
const key = storeKey(name, serverUrl);
|
|
12608
|
-
const legacy = legacyKey(serverUrl);
|
|
12609
|
-
const release = await this.acquireLock();
|
|
12610
|
-
try {
|
|
12611
|
-
const raw = await this.readRaw();
|
|
12612
|
-
const states = parseStates(raw);
|
|
12613
|
-
if (states[key]) return states[key];
|
|
12614
|
-
if (states[legacy]) {
|
|
12615
|
-
states[key] = states[legacy];
|
|
12616
|
-
delete states[legacy];
|
|
12617
|
-
await this.writeStates(states);
|
|
12618
|
-
return states[key];
|
|
12619
|
-
}
|
|
12620
|
-
return void 0;
|
|
12621
|
-
} finally {
|
|
12622
|
-
await release();
|
|
12623
|
-
}
|
|
12624
|
-
}
|
|
12625
|
-
async save(name, serverUrl, state) {
|
|
12626
|
-
const key = storeKey(name, serverUrl);
|
|
12627
|
-
const release = await this.acquireLock();
|
|
12628
|
-
try {
|
|
12629
|
-
const raw = await this.readRaw();
|
|
12630
|
-
const states = parseStates(raw);
|
|
12631
|
-
states[key] = state;
|
|
12632
|
-
await this.writeStates(states);
|
|
12633
|
-
} finally {
|
|
12634
|
-
await release();
|
|
12635
|
-
}
|
|
12636
|
-
}
|
|
12637
|
-
async remove(name, serverUrl) {
|
|
12638
|
-
const key = storeKey(name, serverUrl);
|
|
12639
|
-
const legacy = legacyKey(serverUrl);
|
|
12640
|
-
const release = await this.acquireLock();
|
|
12641
|
-
try {
|
|
12642
|
-
const raw = await this.readRaw();
|
|
12643
|
-
const states = parseStates(raw);
|
|
12644
|
-
const stored = key in states ? key : legacy in states ? legacy : void 0;
|
|
12645
|
-
if (!stored) return false;
|
|
12646
|
-
delete states[stored];
|
|
12647
|
-
await this.writeStates(states);
|
|
12648
|
-
return true;
|
|
12649
|
-
} finally {
|
|
12650
|
-
await release();
|
|
12651
|
-
}
|
|
12652
|
-
}
|
|
12653
|
-
async acquireLock() {
|
|
12654
|
-
await this.ensureDir();
|
|
12655
|
-
return acquireLock(this.platform.fs, this.authPath);
|
|
12656
|
-
}
|
|
12657
|
-
async ensureDir() {
|
|
12658
|
-
try {
|
|
12659
|
-
await this.platform.fs.mkdir(this.dirPath);
|
|
12660
|
-
} catch {
|
|
12661
|
-
}
|
|
12662
|
-
}
|
|
12663
|
-
async readRaw() {
|
|
12664
|
-
try {
|
|
12665
|
-
const data = await this.platform.fs.readFile(this.authPath);
|
|
12666
|
-
return new TextDecoder().decode(data);
|
|
12667
|
-
} catch {
|
|
12668
|
-
return void 0;
|
|
12669
|
-
}
|
|
12670
|
-
}
|
|
12671
|
-
async writeStates(states) {
|
|
12672
|
-
await this.platform.fs.writeFile(this.authPath, serializeStates(states), { mode: FILE_MODE });
|
|
12673
|
-
}
|
|
12674
|
-
};
|
|
12675
|
-
|
|
12676
12773
|
// src/cli/auth-verbs.ts
|
|
12677
12774
|
var resolvedSource;
|
|
12678
12775
|
async function createTokenSource(platform2) {
|
|
@@ -14366,6 +14463,17 @@ async function runCall(platform2, conn, toolName, dataArg, toolArgs, opts) {
|
|
|
14366
14463
|
return EXIT_USAGE;
|
|
14367
14464
|
}
|
|
14368
14465
|
fin = mergeArgs(base, c.value);
|
|
14466
|
+
} else if (!dataArg && !opts.noValidate) {
|
|
14467
|
+
const tools = await conn.listTools();
|
|
14468
|
+
const tool = tools.find((t) => t.name === toolName);
|
|
14469
|
+
const schema = tool?.inputSchema;
|
|
14470
|
+
const required = Array.isArray(schema?.required) ? schema.required : [];
|
|
14471
|
+
for (const r of required) {
|
|
14472
|
+
if (!Object.hasOwn(base, r)) {
|
|
14473
|
+
await writeErr(platform2, opts.json ?? false, "usage", `missing required flag --${r}`);
|
|
14474
|
+
return EXIT_USAGE;
|
|
14475
|
+
}
|
|
14476
|
+
}
|
|
14369
14477
|
}
|
|
14370
14478
|
return renderCallResult(
|
|
14371
14479
|
platform2,
|
|
@@ -14583,7 +14691,10 @@ async function probeServer(_name, config, platform2) {
|
|
|
14583
14691
|
const opts = { version: "probe" };
|
|
14584
14692
|
if ("url" in config && config.url) {
|
|
14585
14693
|
opts.url = config.url;
|
|
14586
|
-
if ("headers" in config)
|
|
14694
|
+
if ("headers" in config) {
|
|
14695
|
+
const expanded = expandHeaders(config.headers, platform2.env);
|
|
14696
|
+
if (expanded) opts.headers = expanded;
|
|
14697
|
+
}
|
|
14587
14698
|
} else if ("command" in config && config.command) {
|
|
14588
14699
|
opts.command = config.command;
|
|
14589
14700
|
if ("args" in config) opts.args = config.args;
|
|
@@ -14945,11 +15056,25 @@ function classifyError(err) {
|
|
|
14945
15056
|
}
|
|
14946
15057
|
async function resolveAuthProvider(platform2, hasAuthHeader, url, target, config) {
|
|
14947
15058
|
if (hasAuthHeader || !url) return void 0;
|
|
14948
|
-
const
|
|
15059
|
+
const piSource = new PiFileTokenSource(platform2);
|
|
14949
15060
|
const entry = config.entry(target);
|
|
14950
15061
|
const oauth = entry && "url" in entry.config ? entry.config.oauth : void 0;
|
|
14951
15062
|
const name = entry ? target : `url:${new URL(url).origin}${new URL(url).pathname}`;
|
|
14952
|
-
|
|
15063
|
+
const provider = createMcpAuthProvider({
|
|
15064
|
+
name,
|
|
15065
|
+
serverUrl: url,
|
|
15066
|
+
source: piSource,
|
|
15067
|
+
oauth,
|
|
15068
|
+
platform: platform2
|
|
15069
|
+
});
|
|
15070
|
+
const credentialSecrets = [];
|
|
15071
|
+
const state = await piSource.loadWithoutLock(name, url);
|
|
15072
|
+
if (state?.tokens?.access_token) credentialSecrets.push(state.tokens.access_token);
|
|
15073
|
+
if (state?.tokens?.refresh_token) credentialSecrets.push(state.tokens.refresh_token);
|
|
15074
|
+
if (state?.clientInformation && "client_secret" in state.clientInformation && state.clientInformation.client_secret) {
|
|
15075
|
+
credentialSecrets.push(state.clientInformation.client_secret);
|
|
15076
|
+
}
|
|
15077
|
+
return { provider, credentialSecrets };
|
|
14953
15078
|
}
|
|
14954
15079
|
function classifyConnectionError(err, target, interrupted) {
|
|
14955
15080
|
if (err instanceof McpAuthError || err instanceof AuthStepUpError || err instanceof JerkSignInRequiredError) {
|
|
@@ -14995,7 +15120,7 @@ async function handleInputRequired(platform2, conn, err, respondData, json) {
|
|
|
14995
15120
|
`);
|
|
14996
15121
|
return EXIT_INPUT_REQUIRED;
|
|
14997
15122
|
}
|
|
14998
|
-
const isTty = platform2.isatty(0);
|
|
15123
|
+
const isTty = platform2.isatty(0) && platform2.isatty(2);
|
|
14999
15124
|
const elicitResult = await handleElicitation(platform2, inputReq, isTty, respondData);
|
|
15000
15125
|
if ("exitCode" in elicitResult) {
|
|
15001
15126
|
await platform2.write(2, `${err.requestJson}
|
|
@@ -15088,13 +15213,20 @@ async function runClientVerb(platform2, version, parsed) {
|
|
|
15088
15213
|
};
|
|
15089
15214
|
const mergedHeaders = { ...configHeaders, ...expanded };
|
|
15090
15215
|
const hasAuthHeader = "Authorization" in mergedHeaders;
|
|
15091
|
-
const
|
|
15216
|
+
const authResolved = await resolveAuthProvider(
|
|
15092
15217
|
platform2,
|
|
15093
15218
|
hasAuthHeader,
|
|
15094
15219
|
resolved.url,
|
|
15095
15220
|
target,
|
|
15096
15221
|
config
|
|
15097
15222
|
);
|
|
15223
|
+
const authProvider = authResolved?.provider;
|
|
15224
|
+
if (authResolved?.credentialSecrets) {
|
|
15225
|
+
for (const s of authResolved.credentialSecrets) {
|
|
15226
|
+
allSecrets.add(s);
|
|
15227
|
+
verbose.secrets.add(s);
|
|
15228
|
+
}
|
|
15229
|
+
}
|
|
15098
15230
|
const roots = buildRoots(parsed.roots, !!resolved.command, platform2.cwd);
|
|
15099
15231
|
let respondData;
|
|
15100
15232
|
if (parsed.respondArg) {
|
|
@@ -15211,7 +15343,12 @@ function kernelFs(ctx) {
|
|
|
15211
15343
|
}
|
|
15212
15344
|
},
|
|
15213
15345
|
rmdir: (path) => ctx.fs.rm(path),
|
|
15214
|
-
unlink: (path) => ctx.fs.unlink(path)
|
|
15346
|
+
unlink: (path) => ctx.fs.unlink(path),
|
|
15347
|
+
async createExclusive(path) {
|
|
15348
|
+
const f = await ctx.open(path, { create: true, exclusive: true, write: true });
|
|
15349
|
+
await f.close();
|
|
15350
|
+
},
|
|
15351
|
+
chmod: ctx.fs.chmod ? (path, mode) => ctx.fs.chmod(path, mode) : void 0
|
|
15215
15352
|
};
|
|
15216
15353
|
}
|
|
15217
15354
|
async function kernelSpawn(ctx, options) {
|
|
@@ -15455,7 +15592,7 @@ async function main(ctx) {
|
|
|
15455
15592
|
packageDir: "/node_modules/@ai-ecoverse/mcpcat"
|
|
15456
15593
|
});
|
|
15457
15594
|
const args = ctx.argv.slice(1);
|
|
15458
|
-
return run(platform2, "0.14.
|
|
15595
|
+
return run(platform2, "0.14.2", args);
|
|
15459
15596
|
}
|
|
15460
15597
|
export {
|
|
15461
15598
|
main as default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/mcpcat",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.2",
|
|
4
4
|
"description": "A curl for the Model Context Protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"trust:dry": "npx fledgling sync --dry-run -y --skip-publish"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@ai-ecoverse/slicc-kernel": "1.
|
|
59
|
+
"@ai-ecoverse/slicc-kernel": "1.75.0",
|
|
60
60
|
"@ai-ecoverse/slicc-shared-web": "1.13.1",
|
|
61
61
|
"@biomejs/biome": "2.5.15",
|
|
62
62
|
"@earendil-works/pi-codemode": "1.2.0",
|