@hasna/mcps 0.0.14 → 0.0.16
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 +21 -0
- package/bin/index.js +967 -182
- package/bin/mcp.js +605 -95
- package/dist/index.d.ts +5 -2
- package/dist/index.js +523 -53
- package/dist/lib/credentials.d.ts +18 -0
- package/dist/lib/doctor.d.ts +4 -1
- package/dist/lib/install.d.ts +5 -1
- package/dist/lib/local-command-consent.d.ts +38 -0
- package/dist/lib/proxy.d.ts +6 -2
- package/dist/lib/registry.d.ts +4 -2
- package/dist/lib/remote.d.ts +4 -1
- package/dist/mcp/index.js +605 -95
- package/dist/types.d.ts +15 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9547,6 +9547,7 @@ function getDb() {
|
|
|
9547
9547
|
command TEXT NOT NULL,
|
|
9548
9548
|
args TEXT NOT NULL DEFAULT '[]',
|
|
9549
9549
|
env TEXT NOT NULL DEFAULT '{}',
|
|
9550
|
+
credential_refs TEXT NOT NULL DEFAULT '{}',
|
|
9550
9551
|
transport TEXT NOT NULL DEFAULT 'stdio',
|
|
9551
9552
|
url TEXT,
|
|
9552
9553
|
source TEXT NOT NULL DEFAULT 'local',
|
|
@@ -9573,6 +9574,9 @@ function getDb() {
|
|
|
9573
9574
|
try {
|
|
9574
9575
|
db.exec("ALTER TABLE servers ADD COLUMN last_error TEXT");
|
|
9575
9576
|
} catch {}
|
|
9577
|
+
try {
|
|
9578
|
+
db.exec("ALTER TABLE servers ADD COLUMN credential_refs TEXT NOT NULL DEFAULT '{}'");
|
|
9579
|
+
} catch {}
|
|
9576
9580
|
db.exec(`
|
|
9577
9581
|
CREATE TABLE IF NOT EXISTS sources (
|
|
9578
9582
|
id TEXT PRIMARY KEY,
|
|
@@ -16627,17 +16631,17 @@ __export(exports_sources, {
|
|
|
16627
16631
|
clearCache: () => clearCache,
|
|
16628
16632
|
addSource: () => addSource
|
|
16629
16633
|
});
|
|
16630
|
-
import { mkdirSync as mkdirSync6, existsSync as
|
|
16631
|
-
import { join as
|
|
16634
|
+
import { mkdirSync as mkdirSync6, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync2, readdirSync as readdirSync3, unlinkSync } from "fs";
|
|
16635
|
+
import { join as join8 } from "path";
|
|
16632
16636
|
function getCacheFile(sourceId) {
|
|
16633
|
-
return
|
|
16637
|
+
return join8(CACHE_DIR, `${sourceId}.json`);
|
|
16634
16638
|
}
|
|
16635
16639
|
function readCache(sourceId) {
|
|
16636
16640
|
try {
|
|
16637
16641
|
const file = getCacheFile(sourceId);
|
|
16638
|
-
if (!
|
|
16642
|
+
if (!existsSync7(file))
|
|
16639
16643
|
return null;
|
|
16640
|
-
const data = JSON.parse(
|
|
16644
|
+
const data = JSON.parse(readFileSync3(file, "utf-8"));
|
|
16641
16645
|
return data;
|
|
16642
16646
|
} catch {
|
|
16643
16647
|
return null;
|
|
@@ -16651,7 +16655,7 @@ function writeCache(sourceId, results) {
|
|
|
16651
16655
|
}
|
|
16652
16656
|
function clearCache(sourceId) {
|
|
16653
16657
|
try {
|
|
16654
|
-
if (!
|
|
16658
|
+
if (!existsSync7(CACHE_DIR))
|
|
16655
16659
|
return;
|
|
16656
16660
|
const files = readdirSync3(CACHE_DIR);
|
|
16657
16661
|
for (const file of files) {
|
|
@@ -16659,7 +16663,7 @@ function clearCache(sourceId) {
|
|
|
16659
16663
|
continue;
|
|
16660
16664
|
if (!sourceId || file.startsWith(`${sourceId}.`)) {
|
|
16661
16665
|
try {
|
|
16662
|
-
unlinkSync(
|
|
16666
|
+
unlinkSync(join8(CACHE_DIR, file));
|
|
16663
16667
|
} catch {}
|
|
16664
16668
|
}
|
|
16665
16669
|
}
|
|
@@ -16905,12 +16909,166 @@ var CACHE_DIR, DEFAULT_TTL_MS;
|
|
|
16905
16909
|
var init_sources = __esm(() => {
|
|
16906
16910
|
init_db();
|
|
16907
16911
|
init_config2();
|
|
16908
|
-
CACHE_DIR =
|
|
16912
|
+
CACHE_DIR = join8(MCPS_DIR, "cache");
|
|
16909
16913
|
DEFAULT_TTL_MS = 10 * 60 * 1000;
|
|
16910
16914
|
});
|
|
16911
16915
|
|
|
16912
16916
|
// src/lib/registry.ts
|
|
16913
16917
|
init_db();
|
|
16918
|
+
|
|
16919
|
+
// src/lib/credentials.ts
|
|
16920
|
+
init_config2();
|
|
16921
|
+
import { existsSync as existsSync6, readFileSync as readFileSync2 } from "fs";
|
|
16922
|
+
import { join as join7 } from "path";
|
|
16923
|
+
|
|
16924
|
+
class CredentialReferenceError extends Error {
|
|
16925
|
+
constructor(message) {
|
|
16926
|
+
super(message);
|
|
16927
|
+
this.name = "CredentialReferenceError";
|
|
16928
|
+
}
|
|
16929
|
+
}
|
|
16930
|
+
var SECRET_KEY_PATTERN = /(?:^|[_-])(api[_-]?key|token|secret|password|passwd|credential|auth|private[_-]?key)(?:$|[_-])/i;
|
|
16931
|
+
var SECRET_VALUE_PATTERN = /^(sk_(?:live|test)_[A-Za-z0-9_]+|ghp_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+|xox[baprs]-[A-Za-z0-9-]+|AIza[A-Za-z0-9_-]{20,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/;
|
|
16932
|
+
var REDACTED_CREDENTIAL_VALUE = "<redacted>";
|
|
16933
|
+
function normalizeKey(key) {
|
|
16934
|
+
return key.trim();
|
|
16935
|
+
}
|
|
16936
|
+
function isRecord(value) {
|
|
16937
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16938
|
+
}
|
|
16939
|
+
function isSecretLikeEnvKey(key) {
|
|
16940
|
+
return SECRET_KEY_PATTERN.test(key);
|
|
16941
|
+
}
|
|
16942
|
+
function isSecretLikeValue(value) {
|
|
16943
|
+
return SECRET_VALUE_PATTERN.test(value.trim());
|
|
16944
|
+
}
|
|
16945
|
+
function normalizeCredentialRef(ref) {
|
|
16946
|
+
const source = ref.source;
|
|
16947
|
+
if (source !== "env" && source !== "local-vault" && source !== "hosted") {
|
|
16948
|
+
throw new CredentialReferenceError(`Unsupported credential reference source: ${String(source)}`);
|
|
16949
|
+
}
|
|
16950
|
+
const name = ref.name?.trim();
|
|
16951
|
+
if (!name) {
|
|
16952
|
+
throw new CredentialReferenceError("Credential reference name is required");
|
|
16953
|
+
}
|
|
16954
|
+
return {
|
|
16955
|
+
source,
|
|
16956
|
+
name,
|
|
16957
|
+
required: ref.required !== false,
|
|
16958
|
+
...ref.description ? { description: ref.description } : {}
|
|
16959
|
+
};
|
|
16960
|
+
}
|
|
16961
|
+
function normalizeCredentialRefs(refs) {
|
|
16962
|
+
const normalized = {};
|
|
16963
|
+
for (const [rawKey, ref] of Object.entries(refs ?? {})) {
|
|
16964
|
+
const key = normalizeKey(rawKey);
|
|
16965
|
+
if (!key)
|
|
16966
|
+
throw new CredentialReferenceError("Credential reference env key is required");
|
|
16967
|
+
normalized[key] = normalizeCredentialRef(ref);
|
|
16968
|
+
}
|
|
16969
|
+
return normalized;
|
|
16970
|
+
}
|
|
16971
|
+
function parseCredentialRefs(value) {
|
|
16972
|
+
if (!isRecord(value))
|
|
16973
|
+
return {};
|
|
16974
|
+
const refs = {};
|
|
16975
|
+
for (const [key, ref] of Object.entries(value)) {
|
|
16976
|
+
if (!isRecord(ref))
|
|
16977
|
+
continue;
|
|
16978
|
+
const source = ref.source;
|
|
16979
|
+
const name = ref.name;
|
|
16980
|
+
if ((source === "env" || source === "local-vault" || source === "hosted") && typeof name === "string" && name.trim()) {
|
|
16981
|
+
refs[key] = normalizeCredentialRef({
|
|
16982
|
+
source,
|
|
16983
|
+
name,
|
|
16984
|
+
required: typeof ref.required === "boolean" ? ref.required : true,
|
|
16985
|
+
description: typeof ref.description === "string" ? ref.description : undefined
|
|
16986
|
+
});
|
|
16987
|
+
}
|
|
16988
|
+
}
|
|
16989
|
+
return refs;
|
|
16990
|
+
}
|
|
16991
|
+
function normalizeLiteralEnv(env) {
|
|
16992
|
+
const normalized = {};
|
|
16993
|
+
for (const [rawKey, rawValue] of Object.entries(env ?? {})) {
|
|
16994
|
+
const key = normalizeKey(rawKey);
|
|
16995
|
+
if (!key)
|
|
16996
|
+
continue;
|
|
16997
|
+
const value = String(rawValue);
|
|
16998
|
+
if (isSecretLikeEnvKey(key) || isSecretLikeValue(value)) {
|
|
16999
|
+
throw new CredentialReferenceError(`Refusing to store raw secret-like env value for "${key}". Use a credential reference instead.`);
|
|
17000
|
+
}
|
|
17001
|
+
normalized[key] = value;
|
|
17002
|
+
}
|
|
17003
|
+
return normalized;
|
|
17004
|
+
}
|
|
17005
|
+
function redactEnv(env) {
|
|
17006
|
+
const redacted = {};
|
|
17007
|
+
for (const [key, value] of Object.entries(env)) {
|
|
17008
|
+
redacted[key] = isSecretLikeEnvKey(key) || isSecretLikeValue(value) ? REDACTED_CREDENTIAL_VALUE : value;
|
|
17009
|
+
}
|
|
17010
|
+
return redacted;
|
|
17011
|
+
}
|
|
17012
|
+
function redactServerCredentials(server) {
|
|
17013
|
+
return {
|
|
17014
|
+
...server,
|
|
17015
|
+
env: redactEnv(server.env),
|
|
17016
|
+
credentialRefs: normalizeCredentialRefs(server.credentialRefs)
|
|
17017
|
+
};
|
|
17018
|
+
}
|
|
17019
|
+
function readLocalVault() {
|
|
17020
|
+
const path = process.env.HASNA_MCPS_CREDENTIAL_VAULT_PATH ?? join7(MCPS_DIR, "credentials.local.json");
|
|
17021
|
+
if (!existsSync6(path))
|
|
17022
|
+
return {};
|
|
17023
|
+
const parsed = JSON.parse(readFileSync2(path, "utf-8"));
|
|
17024
|
+
if (!isRecord(parsed))
|
|
17025
|
+
return {};
|
|
17026
|
+
const values = {};
|
|
17027
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
17028
|
+
if (typeof value === "string")
|
|
17029
|
+
values[key] = value;
|
|
17030
|
+
}
|
|
17031
|
+
return values;
|
|
17032
|
+
}
|
|
17033
|
+
function resolveCredentialRef(envKey, ref) {
|
|
17034
|
+
if (ref.source === "env") {
|
|
17035
|
+
const value = process.env[ref.name];
|
|
17036
|
+
if (value === undefined && ref.required !== false) {
|
|
17037
|
+
throw new CredentialReferenceError(`Missing required environment credential "${ref.name}" for "${envKey}"`);
|
|
17038
|
+
}
|
|
17039
|
+
return value;
|
|
17040
|
+
}
|
|
17041
|
+
if (ref.source === "local-vault") {
|
|
17042
|
+
const value = readLocalVault()[ref.name];
|
|
17043
|
+
if (value === undefined && ref.required !== false) {
|
|
17044
|
+
throw new CredentialReferenceError(`Missing required local vault credential "${ref.name}" for "${envKey}"`);
|
|
17045
|
+
}
|
|
17046
|
+
return value;
|
|
17047
|
+
}
|
|
17048
|
+
if (ref.required !== false) {
|
|
17049
|
+
throw new CredentialReferenceError(`Hosted credential "${ref.name}" for "${envKey}" cannot be resolved by the local runtime`);
|
|
17050
|
+
}
|
|
17051
|
+
return;
|
|
17052
|
+
}
|
|
17053
|
+
function resolveServerEnv(server) {
|
|
17054
|
+
const resolved = { ...server.env };
|
|
17055
|
+
const refs = normalizeCredentialRefs(server.credentialRefs);
|
|
17056
|
+
for (const [envKey, ref] of Object.entries(refs)) {
|
|
17057
|
+
const value = resolveCredentialRef(envKey, ref);
|
|
17058
|
+
if (value !== undefined)
|
|
17059
|
+
resolved[envKey] = value;
|
|
17060
|
+
}
|
|
17061
|
+
return resolved;
|
|
17062
|
+
}
|
|
17063
|
+
function credentialRefPlaceholders(refs) {
|
|
17064
|
+
const placeholders = {};
|
|
17065
|
+
for (const key of Object.keys(refs ?? {})) {
|
|
17066
|
+
placeholders[key] = REDACTED_CREDENTIAL_VALUE;
|
|
17067
|
+
}
|
|
17068
|
+
return placeholders;
|
|
17069
|
+
}
|
|
17070
|
+
|
|
17071
|
+
// src/lib/registry.ts
|
|
16914
17072
|
function parseRow(row) {
|
|
16915
17073
|
return {
|
|
16916
17074
|
id: row.id,
|
|
@@ -16919,6 +17077,7 @@ function parseRow(row) {
|
|
|
16919
17077
|
command: row.command,
|
|
16920
17078
|
args: safeJsonParse(row.args, []),
|
|
16921
17079
|
env: safeJsonParse(row.env, {}),
|
|
17080
|
+
credentialRefs: parseCredentialRefs(safeJsonParse(row.credential_refs, {})),
|
|
16922
17081
|
transport: row.transport,
|
|
16923
17082
|
url: row.url || null,
|
|
16924
17083
|
source: row.source,
|
|
@@ -16986,9 +17145,9 @@ function addServer(opts) {
|
|
|
16986
17145
|
if (!id) {
|
|
16987
17146
|
throw new Error("Unable to generate a valid server ID");
|
|
16988
17147
|
}
|
|
16989
|
-
const row = db2.prepare(`INSERT INTO servers (id, name, description, command, args, env, transport, url, source)
|
|
16990
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
16991
|
-
RETURNING *`).get(id, name, opts.description || null, command, JSON.stringify(opts.args || []), JSON.stringify(opts.env
|
|
17148
|
+
const row = db2.prepare(`INSERT INTO servers (id, name, description, command, args, env, credential_refs, transport, url, source)
|
|
17149
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
17150
|
+
RETURNING *`).get(id, name, opts.description || null, command, JSON.stringify(opts.args || []), JSON.stringify(normalizeLiteralEnv(opts.env)), JSON.stringify(normalizeCredentialRefs(opts.credentialRefs)), opts.transport || "stdio", opts.url || null, opts.source || "local");
|
|
16992
17151
|
return parseRow(row);
|
|
16993
17152
|
}
|
|
16994
17153
|
function removeServer(id) {
|
|
@@ -17027,7 +17186,11 @@ function updateServer(id, updates) {
|
|
|
17027
17186
|
}
|
|
17028
17187
|
if (updates.env !== undefined) {
|
|
17029
17188
|
sets.push("env = ?");
|
|
17030
|
-
values.push(JSON.stringify(updates.env));
|
|
17189
|
+
values.push(JSON.stringify(normalizeLiteralEnv(updates.env)));
|
|
17190
|
+
}
|
|
17191
|
+
if (updates.credentialRefs !== undefined) {
|
|
17192
|
+
sets.push("credential_refs = ?");
|
|
17193
|
+
values.push(JSON.stringify(normalizeCredentialRefs(updates.credentialRefs)));
|
|
17031
17194
|
}
|
|
17032
17195
|
if (updates.transport !== undefined) {
|
|
17033
17196
|
sets.push("transport = ?");
|
|
@@ -17061,7 +17224,7 @@ function setServerEnv(id, key, value) {
|
|
|
17061
17224
|
if (!server)
|
|
17062
17225
|
throw new Error(`Server "${id}" not found`);
|
|
17063
17226
|
const env = { ...server.env, [key]: value };
|
|
17064
|
-
db2.prepare("UPDATE servers SET env = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(env), id);
|
|
17227
|
+
db2.prepare("UPDATE servers SET env = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(normalizeLiteralEnv(env)), id);
|
|
17065
17228
|
}
|
|
17066
17229
|
function unsetServerEnv(id, key) {
|
|
17067
17230
|
const db2 = getDb();
|
|
@@ -17072,6 +17235,23 @@ function unsetServerEnv(id, key) {
|
|
|
17072
17235
|
delete env[key];
|
|
17073
17236
|
db2.prepare("UPDATE servers SET env = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(env), id);
|
|
17074
17237
|
}
|
|
17238
|
+
function setServerCredentialRef(id, key, ref) {
|
|
17239
|
+
const db2 = getDb();
|
|
17240
|
+
const server = getServer(id);
|
|
17241
|
+
if (!server)
|
|
17242
|
+
throw new Error(`Server "${id}" not found`);
|
|
17243
|
+
const credentialRefs = normalizeCredentialRefs({ ...server.credentialRefs ?? {}, [key]: ref });
|
|
17244
|
+
db2.prepare("UPDATE servers SET credential_refs = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(credentialRefs), id);
|
|
17245
|
+
}
|
|
17246
|
+
function unsetServerCredentialRef(id, key) {
|
|
17247
|
+
const db2 = getDb();
|
|
17248
|
+
const server = getServer(id);
|
|
17249
|
+
if (!server)
|
|
17250
|
+
throw new Error(`Server "${id}" not found`);
|
|
17251
|
+
const credentialRefs = { ...server.credentialRefs ?? {} };
|
|
17252
|
+
delete credentialRefs[key];
|
|
17253
|
+
db2.prepare("UPDATE servers SET credential_refs = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(normalizeCredentialRefs(credentialRefs)), id);
|
|
17254
|
+
}
|
|
17075
17255
|
function cacheTools(serverId, tools) {
|
|
17076
17256
|
const db2 = getDb();
|
|
17077
17257
|
const insert = db2.prepare("INSERT INTO tool_cache (server_id, name, description, input_schema) VALUES (?, ?, ?, ?)");
|
|
@@ -17111,6 +17291,7 @@ function cloneServer(id, newName) {
|
|
|
17111
17291
|
command: server.command,
|
|
17112
17292
|
args: server.args,
|
|
17113
17293
|
env: server.env,
|
|
17294
|
+
credentialRefs: server.credentialRefs,
|
|
17114
17295
|
transport: server.transport,
|
|
17115
17296
|
url: server.url ?? undefined,
|
|
17116
17297
|
source: server.source
|
|
@@ -25242,6 +25423,185 @@ class StreamableHTTPClientTransport {
|
|
|
25242
25423
|
// src/lib/proxy.ts
|
|
25243
25424
|
init_config2();
|
|
25244
25425
|
init_db();
|
|
25426
|
+
|
|
25427
|
+
// src/lib/local-command-consent.ts
|
|
25428
|
+
class LocalCommandConsentError extends Error {
|
|
25429
|
+
review;
|
|
25430
|
+
constructor(message, review) {
|
|
25431
|
+
super(message);
|
|
25432
|
+
this.name = "LocalCommandConsentError";
|
|
25433
|
+
this.review = review;
|
|
25434
|
+
}
|
|
25435
|
+
}
|
|
25436
|
+
var SHELL_COMMANDS = new Set(["bash", "sh", "zsh", "fish", "cmd", "cmd.exe", "powershell", "powershell.exe", "pwsh"]);
|
|
25437
|
+
var DESTRUCTIVE_COMMANDS = new Set([
|
|
25438
|
+
"rm",
|
|
25439
|
+
"dd",
|
|
25440
|
+
"mkfs",
|
|
25441
|
+
"shutdown",
|
|
25442
|
+
"reboot",
|
|
25443
|
+
"poweroff",
|
|
25444
|
+
"halt",
|
|
25445
|
+
"killall",
|
|
25446
|
+
"pkill"
|
|
25447
|
+
]);
|
|
25448
|
+
var SHELL_EVAL_FLAGS = new Set(["-c", "/c", "-Command", "-command", "-EncodedCommand", "-encodedcommand"]);
|
|
25449
|
+
var SECRET_FLAG_PATTERN = /(?:^|[-_])(api[-_]?key|token|secret|password|passwd|credential|auth|private[-_]?key)(?:$|[-_])/i;
|
|
25450
|
+
var SECRET_KEY_PATTERN2 = /(?:^|[_-])(api[_-]?key|token|secret|password|passwd|credential|auth|private[_-]?key)(?:$|[_-])/i;
|
|
25451
|
+
var SECRET_VALUE_PATTERN2 = /^(sk_(?:live|test)_[A-Za-z0-9_]+|ghp_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+|xox[baprs]-[A-Za-z0-9-]+|AIza[A-Za-z0-9_-]{20,}|eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/;
|
|
25452
|
+
var SHELL_META_PATTERN = /[;&|`<>]|\$\(/;
|
|
25453
|
+
function commandBase(command) {
|
|
25454
|
+
return command.trim().split(/[\\/]/).pop()?.toLowerCase() || command.trim().toLowerCase();
|
|
25455
|
+
}
|
|
25456
|
+
function normalizeArgs(args) {
|
|
25457
|
+
return (args ?? []).map((arg) => String(arg));
|
|
25458
|
+
}
|
|
25459
|
+
function isSecretKey(key) {
|
|
25460
|
+
return SECRET_KEY_PATTERN2.test(key) || SECRET_FLAG_PATTERN.test(key);
|
|
25461
|
+
}
|
|
25462
|
+
function isSecretValue(value) {
|
|
25463
|
+
return SECRET_VALUE_PATTERN2.test(value.trim());
|
|
25464
|
+
}
|
|
25465
|
+
function isSecretAssignment(arg) {
|
|
25466
|
+
const eqIdx = arg.indexOf("=");
|
|
25467
|
+
if (eqIdx <= 0)
|
|
25468
|
+
return false;
|
|
25469
|
+
const key = arg.slice(0, eqIdx);
|
|
25470
|
+
const value = arg.slice(eqIdx + 1);
|
|
25471
|
+
return isSecretKey(key) || isSecretValue(value);
|
|
25472
|
+
}
|
|
25473
|
+
function isSecretArg(args, index) {
|
|
25474
|
+
const arg = args[index] ?? "";
|
|
25475
|
+
const previous = args[index - 1] ?? "";
|
|
25476
|
+
return isSecretAssignment(arg) || isSecretValue(arg) || SECRET_FLAG_PATTERN.test(previous);
|
|
25477
|
+
}
|
|
25478
|
+
function quoteArg(value) {
|
|
25479
|
+
return JSON.stringify(value);
|
|
25480
|
+
}
|
|
25481
|
+
function displayCommand(command, args) {
|
|
25482
|
+
return [quoteArg(command), ...args.map((arg, index) => quoteArg(isSecretArg(args, index) ? "<redacted>" : arg))].join(" ");
|
|
25483
|
+
}
|
|
25484
|
+
function pushRisk(risks, risk) {
|
|
25485
|
+
if (risks.some((existing) => existing.code === risk.code && existing.evidence === risk.evidence))
|
|
25486
|
+
return;
|
|
25487
|
+
risks.push(risk);
|
|
25488
|
+
}
|
|
25489
|
+
function inspectRisks(command, args, env) {
|
|
25490
|
+
const risks = [];
|
|
25491
|
+
const base = commandBase(command);
|
|
25492
|
+
const joined = [command, ...args].join(" ");
|
|
25493
|
+
if (SHELL_COMMANDS.has(base)) {
|
|
25494
|
+
pushRisk(risks, {
|
|
25495
|
+
code: "shell_interpreter",
|
|
25496
|
+
severity: "warning",
|
|
25497
|
+
message: "Command launches a shell interpreter.",
|
|
25498
|
+
evidence: base
|
|
25499
|
+
});
|
|
25500
|
+
if (args.some((arg) => SHELL_EVAL_FLAGS.has(arg))) {
|
|
25501
|
+
pushRisk(risks, {
|
|
25502
|
+
code: "shell_eval",
|
|
25503
|
+
severity: "danger",
|
|
25504
|
+
message: "Shell command evaluates an inline script.",
|
|
25505
|
+
evidence: base
|
|
25506
|
+
});
|
|
25507
|
+
}
|
|
25508
|
+
}
|
|
25509
|
+
if (base === "sudo") {
|
|
25510
|
+
pushRisk(risks, {
|
|
25511
|
+
code: "privilege_escalation",
|
|
25512
|
+
severity: "danger",
|
|
25513
|
+
message: "Command requests elevated privileges.",
|
|
25514
|
+
evidence: base
|
|
25515
|
+
});
|
|
25516
|
+
}
|
|
25517
|
+
if (DESTRUCTIVE_COMMANDS.has(base) || /\brm\s+-[^\s]*[rf][^\s]*\b/.test(joined) || /--no-preserve-root\b/.test(joined)) {
|
|
25518
|
+
pushRisk(risks, {
|
|
25519
|
+
code: "destructive_command",
|
|
25520
|
+
severity: "danger",
|
|
25521
|
+
message: "Command includes a destructive system operation.",
|
|
25522
|
+
evidence: base
|
|
25523
|
+
});
|
|
25524
|
+
}
|
|
25525
|
+
if (/\b(curl|wget)\b[\s\S]*\|[\s\S]*\b(sh|bash|zsh|fish)\b/.test(joined)) {
|
|
25526
|
+
pushRisk(risks, {
|
|
25527
|
+
code: "download_pipe_shell",
|
|
25528
|
+
severity: "danger",
|
|
25529
|
+
message: "Command downloads remote content and pipes it to a shell."
|
|
25530
|
+
});
|
|
25531
|
+
}
|
|
25532
|
+
if ([command, ...args].some((part) => SHELL_META_PATTERN.test(part))) {
|
|
25533
|
+
pushRisk(risks, {
|
|
25534
|
+
code: "shell_metacharacters",
|
|
25535
|
+
severity: "warning",
|
|
25536
|
+
message: "Command or arguments contain shell metacharacters."
|
|
25537
|
+
});
|
|
25538
|
+
}
|
|
25539
|
+
if (args.some((arg, index) => isSecretArg(args, index))) {
|
|
25540
|
+
pushRisk(risks, {
|
|
25541
|
+
code: "inline_secret",
|
|
25542
|
+
severity: "danger",
|
|
25543
|
+
message: "Command arguments appear to contain inline secret material."
|
|
25544
|
+
});
|
|
25545
|
+
}
|
|
25546
|
+
const secretEnvKeys = Object.keys(env).filter(isSecretKey).sort();
|
|
25547
|
+
if (secretEnvKeys.length > 0) {
|
|
25548
|
+
pushRisk(risks, {
|
|
25549
|
+
code: "secret_env",
|
|
25550
|
+
severity: "warning",
|
|
25551
|
+
message: "Environment contains secret-like keys; values are redacted from consent output.",
|
|
25552
|
+
evidence: secretEnvKeys.join(", ")
|
|
25553
|
+
});
|
|
25554
|
+
}
|
|
25555
|
+
return risks;
|
|
25556
|
+
}
|
|
25557
|
+
function inspectLocalCommand(input) {
|
|
25558
|
+
const args = normalizeArgs(input.args);
|
|
25559
|
+
const env = input.env ?? {};
|
|
25560
|
+
const transport = input.transport ?? "stdio";
|
|
25561
|
+
const risks = inspectRisks(input.command, args, env);
|
|
25562
|
+
return {
|
|
25563
|
+
requiresConsent: transport === "stdio",
|
|
25564
|
+
operation: input.operation ?? "launch",
|
|
25565
|
+
command: input.command,
|
|
25566
|
+
args,
|
|
25567
|
+
displayCommand: displayCommand(input.command, args),
|
|
25568
|
+
envKeys: Object.keys(env).sort(),
|
|
25569
|
+
risks,
|
|
25570
|
+
hasDangerousRisk: risks.some((risk) => risk.severity === "danger")
|
|
25571
|
+
};
|
|
25572
|
+
}
|
|
25573
|
+
function formatLocalCommandReview(review) {
|
|
25574
|
+
const lines = [
|
|
25575
|
+
`Command: ${review.displayCommand}`,
|
|
25576
|
+
review.envKeys.length > 0 ? `Env keys: ${review.envKeys.join(", ")}` : "Env keys: <none>"
|
|
25577
|
+
];
|
|
25578
|
+
if (review.risks.length > 0) {
|
|
25579
|
+
lines.push("Risks:");
|
|
25580
|
+
for (const risk of review.risks) {
|
|
25581
|
+
lines.push(`- ${risk.severity}: ${risk.code} - ${risk.message}${risk.evidence ? ` (${risk.evidence})` : ""}`);
|
|
25582
|
+
}
|
|
25583
|
+
} else {
|
|
25584
|
+
lines.push("Risks: none detected");
|
|
25585
|
+
}
|
|
25586
|
+
return lines.join(`
|
|
25587
|
+
`);
|
|
25588
|
+
}
|
|
25589
|
+
function assertLocalCommandConsent(input, consent = {}) {
|
|
25590
|
+
const review = inspectLocalCommand(input);
|
|
25591
|
+
if (!review.requiresConsent)
|
|
25592
|
+
return review;
|
|
25593
|
+
if (consent.approved !== true) {
|
|
25594
|
+
throw new LocalCommandConsentError(`local stdio command approval is required before ${review.operation}.
|
|
25595
|
+
${formatLocalCommandReview(review)}`, review);
|
|
25596
|
+
}
|
|
25597
|
+
if (review.hasDangerousRisk && consent.allowRisky !== true) {
|
|
25598
|
+
throw new LocalCommandConsentError(`risky command approval is required before ${review.operation}.
|
|
25599
|
+
${formatLocalCommandReview(review)}`, review);
|
|
25600
|
+
}
|
|
25601
|
+
return review;
|
|
25602
|
+
}
|
|
25603
|
+
|
|
25604
|
+
// src/lib/proxy.ts
|
|
25245
25605
|
var connections = new Map;
|
|
25246
25606
|
var inflightConnections = new Map;
|
|
25247
25607
|
function buildEnv(extra) {
|
|
@@ -25267,7 +25627,7 @@ function requireUrl(entry) {
|
|
|
25267
25627
|
throw new Error(`Server "${entry.id}" has an invalid URL: ${entry.url}`);
|
|
25268
25628
|
}
|
|
25269
25629
|
}
|
|
25270
|
-
async function connectToServer(entry) {
|
|
25630
|
+
async function connectToServer(entry, options = {}) {
|
|
25271
25631
|
if (connections.has(entry.id)) {
|
|
25272
25632
|
return connections.get(entry.id);
|
|
25273
25633
|
}
|
|
@@ -25283,10 +25643,17 @@ async function connectToServer(entry) {
|
|
|
25283
25643
|
if (!entry.command?.trim()) {
|
|
25284
25644
|
throw new Error(`Server "${entry.id}" is missing a command`);
|
|
25285
25645
|
}
|
|
25646
|
+
assertLocalCommandConsent({
|
|
25647
|
+
command: entry.command,
|
|
25648
|
+
args: entry.args,
|
|
25649
|
+
env: { ...entry.env, ...credentialRefPlaceholders(entry.credentialRefs) },
|
|
25650
|
+
transport: entry.transport,
|
|
25651
|
+
operation: "launch"
|
|
25652
|
+
}, options.localCommandConsent);
|
|
25286
25653
|
transport = new StdioClientTransport({
|
|
25287
25654
|
command: entry.command,
|
|
25288
25655
|
args: entry.args,
|
|
25289
|
-
env: buildEnv(entry
|
|
25656
|
+
env: buildEnv(resolveServerEnv(entry))
|
|
25290
25657
|
});
|
|
25291
25658
|
} else if (entry.transport === "sse") {
|
|
25292
25659
|
transport = new SSEClientTransport(requireUrl(entry));
|
|
@@ -25418,13 +25785,28 @@ async function refreshTools(id) {
|
|
|
25418
25785
|
}
|
|
25419
25786
|
|
|
25420
25787
|
// src/lib/doctor.ts
|
|
25421
|
-
async function diagnoseServer(server) {
|
|
25788
|
+
async function diagnoseServer(server, options = {}) {
|
|
25422
25789
|
const checks3 = [];
|
|
25790
|
+
let hasLocalConsent = true;
|
|
25423
25791
|
if (server.transport === "stdio") {
|
|
25792
|
+
try {
|
|
25793
|
+
assertLocalCommandConsent({
|
|
25794
|
+
command: server.command,
|
|
25795
|
+
args: server.args,
|
|
25796
|
+
env: { ...server.env, ...credentialRefPlaceholders(server.credentialRefs) },
|
|
25797
|
+
transport: server.transport,
|
|
25798
|
+
operation: "diagnose"
|
|
25799
|
+
}, options.localCommandConsent);
|
|
25800
|
+
} catch (err) {
|
|
25801
|
+
hasLocalConsent = false;
|
|
25802
|
+
checks3.push({ name: "local command consent", pass: false, message: err.message });
|
|
25803
|
+
}
|
|
25424
25804
|
try {
|
|
25425
25805
|
const path = execFileSync("which", [server.command], { stdio: "pipe" }).toString().trim();
|
|
25426
25806
|
let version2 = "";
|
|
25427
25807
|
try {
|
|
25808
|
+
if (!hasLocalConsent)
|
|
25809
|
+
throw new Error("local stdio command approval is required before version probing");
|
|
25428
25810
|
version2 = execFileSync(server.command, ["--version"], { stdio: "pipe" }).toString().trim().split(`
|
|
25429
25811
|
`)[0];
|
|
25430
25812
|
} catch {}
|
|
@@ -25440,12 +25822,29 @@ async function diagnoseServer(server) {
|
|
|
25440
25822
|
}
|
|
25441
25823
|
}
|
|
25442
25824
|
const missingEnv = Object.entries(server.env).filter(([, v]) => !v);
|
|
25443
|
-
|
|
25444
|
-
|
|
25445
|
-
|
|
25446
|
-
|
|
25825
|
+
const credentialRefCount = Object.keys(server.credentialRefs ?? {}).length;
|
|
25826
|
+
let credentialError = null;
|
|
25827
|
+
try {
|
|
25828
|
+
resolveServerEnv(server);
|
|
25829
|
+
} catch (err) {
|
|
25830
|
+
credentialError = err.message;
|
|
25831
|
+
}
|
|
25832
|
+
if (Object.keys(server.env).length === 0 && credentialRefCount === 0) {
|
|
25833
|
+
checks3.push({ name: "env vars", pass: true, message: "no env vars or credential refs required" });
|
|
25834
|
+
} else if (missingEnv.length > 0 || credentialError) {
|
|
25835
|
+
const parts = [];
|
|
25836
|
+
if (missingEnv.length > 0)
|
|
25837
|
+
parts.push(`missing literal values for: ${missingEnv.map(([k]) => k).join(", ")}`);
|
|
25838
|
+
if (credentialError)
|
|
25839
|
+
parts.push(credentialError);
|
|
25840
|
+
checks3.push({ name: "env vars", pass: false, message: parts.join("; ") });
|
|
25447
25841
|
} else {
|
|
25448
|
-
|
|
25842
|
+
const literalCount = Object.keys(server.env).length;
|
|
25843
|
+
checks3.push({
|
|
25844
|
+
name: "env vars",
|
|
25845
|
+
pass: true,
|
|
25846
|
+
message: `${literalCount} literal env var(s), ${credentialRefCount} credential ref(s) available`
|
|
25847
|
+
});
|
|
25449
25848
|
}
|
|
25450
25849
|
if (server.transport !== "stdio" && server.url) {
|
|
25451
25850
|
try {
|
|
@@ -25455,10 +25854,10 @@ async function diagnoseServer(server) {
|
|
|
25455
25854
|
checks3.push({ name: "URL reachable", pass: false, message: `unreachable: ${err.message}` });
|
|
25456
25855
|
}
|
|
25457
25856
|
}
|
|
25458
|
-
if (server.enabled) {
|
|
25857
|
+
if (server.enabled && hasLocalConsent) {
|
|
25459
25858
|
try {
|
|
25460
25859
|
await Promise.race([
|
|
25461
|
-
connectToServer(server),
|
|
25860
|
+
connectToServer(server, { localCommandConsent: options.localCommandConsent }),
|
|
25462
25861
|
new Promise((_, reject) => setTimeout(() => reject(new Error("timeout after 10s")), 1e4))
|
|
25463
25862
|
]);
|
|
25464
25863
|
await disconnectServer(server.id);
|
|
@@ -25466,8 +25865,10 @@ async function diagnoseServer(server) {
|
|
|
25466
25865
|
} catch (err) {
|
|
25467
25866
|
checks3.push({ name: "connect & list tools", pass: false, message: err.message });
|
|
25468
25867
|
}
|
|
25469
|
-
} else {
|
|
25868
|
+
} else if (!server.enabled) {
|
|
25470
25869
|
checks3.push({ name: "connect & list tools", pass: true, message: "skipped (server disabled)" });
|
|
25870
|
+
} else {
|
|
25871
|
+
checks3.push({ name: "connect & list tools", pass: false, message: "skipped until local stdio command is approved" });
|
|
25471
25872
|
}
|
|
25472
25873
|
return {
|
|
25473
25874
|
server,
|
|
@@ -25507,7 +25908,7 @@ async function getRegistryServer(id) {
|
|
|
25507
25908
|
const all = entries.map(parseRegistryEntry);
|
|
25508
25909
|
return all.find((s) => s.id === id) || null;
|
|
25509
25910
|
}
|
|
25510
|
-
async function installFromRegistry(id) {
|
|
25911
|
+
async function installFromRegistry(id, options = {}) {
|
|
25511
25912
|
const server = await getRegistryServer(id);
|
|
25512
25913
|
if (!server) {
|
|
25513
25914
|
throw new Error(`Server "${id}" not found in registry`);
|
|
@@ -25527,6 +25928,13 @@ async function installFromRegistry(id) {
|
|
|
25527
25928
|
transport = pkg.transport.type;
|
|
25528
25929
|
}
|
|
25529
25930
|
}
|
|
25931
|
+
assertLocalCommandConsent({
|
|
25932
|
+
command,
|
|
25933
|
+
args,
|
|
25934
|
+
transport,
|
|
25935
|
+
env: {},
|
|
25936
|
+
operation: "register"
|
|
25937
|
+
}, options.localCommandConsent);
|
|
25530
25938
|
return addServer({
|
|
25531
25939
|
name: server.name,
|
|
25532
25940
|
description: server.description,
|
|
@@ -25763,11 +26171,19 @@ function installProviderProfile(id, options = {}) {
|
|
|
25763
26171
|
if (!command) {
|
|
25764
26172
|
throw new Error(`Provider profile "${id}" does not define an install fallback command`);
|
|
25765
26173
|
}
|
|
26174
|
+
assertLocalCommandConsent({
|
|
26175
|
+
command,
|
|
26176
|
+
args,
|
|
26177
|
+
env: fallback?.env ?? {},
|
|
26178
|
+
transport: useFallback ? "stdio" : profile.transport,
|
|
26179
|
+
operation: "register"
|
|
26180
|
+
}, options.localCommandConsent);
|
|
25766
26181
|
return addServer({
|
|
25767
26182
|
name: options.name ?? profile.displayName,
|
|
25768
26183
|
description: profile.description ?? undefined,
|
|
25769
26184
|
command,
|
|
25770
26185
|
args,
|
|
26186
|
+
env: fallback?.env,
|
|
25771
26187
|
transport: useFallback ? "stdio" : profile.transport,
|
|
25772
26188
|
url: useFallback ? fallback?.url : profile.endpoint ?? undefined,
|
|
25773
26189
|
source: "provider-profile"
|
|
@@ -25787,8 +26203,8 @@ init_provider_profile_seeds();
|
|
|
25787
26203
|
|
|
25788
26204
|
// src/lib/install.ts
|
|
25789
26205
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
25790
|
-
import { existsSync as
|
|
25791
|
-
import { join as
|
|
26206
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync7 } from "fs";
|
|
26207
|
+
import { join as join9 } from "path";
|
|
25792
26208
|
import { homedir as homedir8 } from "os";
|
|
25793
26209
|
function installToClaude(entry) {
|
|
25794
26210
|
try {
|
|
@@ -25800,7 +26216,7 @@ function installToClaude(entry) {
|
|
|
25800
26216
|
"--scope",
|
|
25801
26217
|
"user"
|
|
25802
26218
|
];
|
|
25803
|
-
for (const [k, v] of Object.entries(entry
|
|
26219
|
+
for (const [k, v] of Object.entries(assertAgentInstallEnv(entry))) {
|
|
25804
26220
|
args.push("--env", `${k}=${v}`);
|
|
25805
26221
|
}
|
|
25806
26222
|
args.push(entry.id, "--", entry.command, ...entry.args);
|
|
@@ -25812,9 +26228,9 @@ function installToClaude(entry) {
|
|
|
25812
26228
|
}
|
|
25813
26229
|
function installToCodex(entry) {
|
|
25814
26230
|
try {
|
|
25815
|
-
const configDir =
|
|
25816
|
-
const configPath =
|
|
25817
|
-
if (!
|
|
26231
|
+
const configDir = join9(homedir8(), ".codex");
|
|
26232
|
+
const configPath = join9(configDir, "config.toml");
|
|
26233
|
+
if (!existsSync8(configDir)) {
|
|
25818
26234
|
mkdirSync7(configDir, { recursive: true });
|
|
25819
26235
|
}
|
|
25820
26236
|
const block = `
|
|
@@ -25822,7 +26238,7 @@ function installToCodex(entry) {
|
|
|
25822
26238
|
` + `command = ${JSON.stringify(entry.command)}
|
|
25823
26239
|
` + `args = [${entry.args.map((a) => JSON.stringify(a)).join(", ")}]
|
|
25824
26240
|
`;
|
|
25825
|
-
const existing =
|
|
26241
|
+
const existing = existsSync8(configPath) ? readFileSync4(configPath, "utf-8") : "";
|
|
25826
26242
|
if (existing.includes(`[mcp_servers.${entry.id}]`)) {
|
|
25827
26243
|
return { agent: "codex", success: true };
|
|
25828
26244
|
}
|
|
@@ -25834,21 +26250,22 @@ function installToCodex(entry) {
|
|
|
25834
26250
|
}
|
|
25835
26251
|
function installToGemini(entry) {
|
|
25836
26252
|
try {
|
|
25837
|
-
const configDir =
|
|
25838
|
-
const configPath =
|
|
25839
|
-
if (!
|
|
26253
|
+
const configDir = join9(homedir8(), ".gemini");
|
|
26254
|
+
const configPath = join9(configDir, "settings.json");
|
|
26255
|
+
if (!existsSync8(configDir)) {
|
|
25840
26256
|
mkdirSync7(configDir, { recursive: true });
|
|
25841
26257
|
}
|
|
25842
26258
|
let settings = {};
|
|
25843
|
-
if (
|
|
25844
|
-
settings = JSON.parse(
|
|
26259
|
+
if (existsSync8(configPath)) {
|
|
26260
|
+
settings = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
25845
26261
|
}
|
|
25846
26262
|
if (!settings.mcpServers)
|
|
25847
26263
|
settings.mcpServers = {};
|
|
26264
|
+
const env = assertAgentInstallEnv(entry);
|
|
25848
26265
|
settings.mcpServers[entry.id] = {
|
|
25849
26266
|
command: entry.command,
|
|
25850
26267
|
args: entry.args,
|
|
25851
|
-
...Object.keys(
|
|
26268
|
+
...Object.keys(env).length > 0 ? { env } : {}
|
|
25852
26269
|
};
|
|
25853
26270
|
writeFileSync3(configPath, JSON.stringify(settings, null, 2), "utf-8");
|
|
25854
26271
|
return { agent: "gemini", success: true };
|
|
@@ -25856,7 +26273,43 @@ function installToGemini(entry) {
|
|
|
25856
26273
|
return { agent: "gemini", success: false, error: err.message };
|
|
25857
26274
|
}
|
|
25858
26275
|
}
|
|
25859
|
-
function
|
|
26276
|
+
function assertAgentInstallEnv(entry) {
|
|
26277
|
+
const refs = entry.credentialRefs ?? {};
|
|
26278
|
+
if (Object.keys(refs).length > 0) {
|
|
26279
|
+
throw new CredentialReferenceError(`Server "${entry.id}" uses credential references; refusing to materialize secrets into local agent config files`);
|
|
26280
|
+
}
|
|
26281
|
+
for (const [key, value] of Object.entries(entry.env)) {
|
|
26282
|
+
if (isSecretLikeEnvKey(key) || isSecretLikeValue(value)) {
|
|
26283
|
+
throw new CredentialReferenceError(`Server "${entry.id}" has legacy raw secret-like env "${key}"; move it to a credential reference before installing to agents`);
|
|
26284
|
+
}
|
|
26285
|
+
}
|
|
26286
|
+
return entry.env;
|
|
26287
|
+
}
|
|
26288
|
+
function installToAgents(entry, targets = ["claude", "codex", "gemini"], options = {}) {
|
|
26289
|
+
try {
|
|
26290
|
+
assertLocalCommandConsent({
|
|
26291
|
+
command: entry.command,
|
|
26292
|
+
args: entry.args,
|
|
26293
|
+
env: { ...entry.env, ...credentialRefPlaceholders(entry.credentialRefs) },
|
|
26294
|
+
transport: entry.transport,
|
|
26295
|
+
operation: "install"
|
|
26296
|
+
}, options.localCommandConsent);
|
|
26297
|
+
} catch (err) {
|
|
26298
|
+
return targets.map((target) => ({
|
|
26299
|
+
agent: target,
|
|
26300
|
+
success: false,
|
|
26301
|
+
error: err.message
|
|
26302
|
+
}));
|
|
26303
|
+
}
|
|
26304
|
+
try {
|
|
26305
|
+
assertAgentInstallEnv(entry);
|
|
26306
|
+
} catch (err) {
|
|
26307
|
+
return targets.map((target) => ({
|
|
26308
|
+
agent: target,
|
|
26309
|
+
success: false,
|
|
26310
|
+
error: err.message
|
|
26311
|
+
}));
|
|
26312
|
+
}
|
|
25860
26313
|
return targets.map((target) => {
|
|
25861
26314
|
if (target === "claude")
|
|
25862
26315
|
return installToClaude(entry);
|
|
@@ -26066,11 +26519,11 @@ function seedDefaultMachines() {
|
|
|
26066
26519
|
// src/lib/fleet.ts
|
|
26067
26520
|
init_config2();
|
|
26068
26521
|
import { spawn as spawn2 } from "child_process";
|
|
26069
|
-
import { existsSync as
|
|
26070
|
-
import { join as
|
|
26522
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
26523
|
+
import { join as join10 } from "path";
|
|
26071
26524
|
var NPM_SEARCH_URL = "https://registry.npmjs.org/-/v1/search";
|
|
26072
26525
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org";
|
|
26073
|
-
var CATALOG_CACHE_PATH =
|
|
26526
|
+
var CATALOG_CACHE_PATH = join10(MCPS_DIR, "cache", "hasna-catalog.json");
|
|
26074
26527
|
var DEFAULT_CATALOG_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
26075
26528
|
var DEFAULT_REMOTE_TIMEOUT_MS = 180000;
|
|
26076
26529
|
var DEFAULT_HANDSHAKE_TIMEOUT_MS = 2500;
|
|
@@ -26080,9 +26533,9 @@ function normalizeQueryList(values) {
|
|
|
26080
26533
|
}
|
|
26081
26534
|
function readCatalogCache(maxAgeMs) {
|
|
26082
26535
|
try {
|
|
26083
|
-
if (!
|
|
26536
|
+
if (!existsSync9(CATALOG_CACHE_PATH))
|
|
26084
26537
|
return null;
|
|
26085
|
-
const parsed = JSON.parse(
|
|
26538
|
+
const parsed = JSON.parse(readFileSync5(CATALOG_CACHE_PATH, "utf-8"));
|
|
26086
26539
|
if (!parsed.cachedAt || !Array.isArray(parsed.entries))
|
|
26087
26540
|
return null;
|
|
26088
26541
|
if (Date.now() - parsed.cachedAt > maxAgeMs)
|
|
@@ -26094,7 +26547,7 @@ function readCatalogCache(maxAgeMs) {
|
|
|
26094
26547
|
}
|
|
26095
26548
|
function writeCatalogCache(entries) {
|
|
26096
26549
|
try {
|
|
26097
|
-
mkdirSync8(
|
|
26550
|
+
mkdirSync8(join10(MCPS_DIR, "cache"), { recursive: true });
|
|
26098
26551
|
writeFileSync4(CATALOG_CACHE_PATH, JSON.stringify({ cachedAt: Date.now(), entries }, null, 2), "utf-8");
|
|
26099
26552
|
} catch {}
|
|
26100
26553
|
}
|
|
@@ -26765,22 +27218,22 @@ async function runFleetInstall(options = {}, dependencies = {}) {
|
|
|
26765
27218
|
}));
|
|
26766
27219
|
}
|
|
26767
27220
|
// src/lib/version.ts
|
|
26768
|
-
import { existsSync as
|
|
26769
|
-
import { dirname as dirname3, join as
|
|
27221
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
27222
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
26770
27223
|
import { fileURLToPath } from "url";
|
|
26771
27224
|
var FALLBACK_VERSION = "0.0.1";
|
|
26772
27225
|
function readPackageVersion(moduleUrl, fallback = FALLBACK_VERSION) {
|
|
26773
27226
|
const baseDir = dirname3(fileURLToPath(moduleUrl));
|
|
26774
27227
|
const candidates = [
|
|
26775
|
-
|
|
26776
|
-
|
|
26777
|
-
|
|
27228
|
+
join11(baseDir, "..", "..", "package.json"),
|
|
27229
|
+
join11(baseDir, "..", "package.json"),
|
|
27230
|
+
join11(baseDir, "package.json")
|
|
26778
27231
|
];
|
|
26779
27232
|
for (const candidate of candidates) {
|
|
26780
|
-
if (!
|
|
27233
|
+
if (!existsSync10(candidate))
|
|
26781
27234
|
continue;
|
|
26782
27235
|
try {
|
|
26783
|
-
const pkg = JSON.parse(
|
|
27236
|
+
const pkg = JSON.parse(readFileSync6(candidate, "utf-8"));
|
|
26784
27237
|
if (pkg.version)
|
|
26785
27238
|
return pkg.version;
|
|
26786
27239
|
} catch {}
|
|
@@ -26796,19 +27249,27 @@ export {
|
|
|
26796
27249
|
updateServer,
|
|
26797
27250
|
updateMachine,
|
|
26798
27251
|
unsetServerEnv,
|
|
27252
|
+
unsetServerCredentialRef,
|
|
26799
27253
|
setServerEnv,
|
|
27254
|
+
setServerCredentialRef,
|
|
26800
27255
|
seedDefaultProviderProfiles,
|
|
26801
27256
|
seedDefaultMachines,
|
|
26802
27257
|
searchRegistry,
|
|
26803
27258
|
searchProviderProfiles,
|
|
26804
27259
|
runFleetInstall,
|
|
26805
27260
|
runFleetHealthCheck,
|
|
27261
|
+
resolveServerEnv,
|
|
26806
27262
|
removeSource,
|
|
26807
27263
|
removeServer,
|
|
26808
27264
|
removeProviderProfile,
|
|
26809
27265
|
removeMachine,
|
|
26810
27266
|
refreshTools,
|
|
27267
|
+
redactServerCredentials,
|
|
27268
|
+
redactEnv,
|
|
26811
27269
|
readPackageVersion,
|
|
27270
|
+
normalizeLiteralEnv,
|
|
27271
|
+
normalizeCredentialRefs,
|
|
27272
|
+
normalizeCredentialRef,
|
|
26812
27273
|
listSources,
|
|
26813
27274
|
listServers,
|
|
26814
27275
|
listProviderProfiles,
|
|
@@ -26816,9 +27277,12 @@ export {
|
|
|
26816
27277
|
listHasnaMcpCatalog,
|
|
26817
27278
|
listAwesomeServers,
|
|
26818
27279
|
listAllTools,
|
|
27280
|
+
isSecretLikeValue,
|
|
27281
|
+
isSecretLikeEnvKey,
|
|
26819
27282
|
installToAgents,
|
|
26820
27283
|
installProviderProfile,
|
|
26821
27284
|
installFromRegistry,
|
|
27285
|
+
inspectLocalCommand,
|
|
26822
27286
|
getToolCounts,
|
|
26823
27287
|
getSource,
|
|
26824
27288
|
getServer,
|
|
@@ -26827,6 +27291,7 @@ export {
|
|
|
26827
27291
|
getMachine,
|
|
26828
27292
|
getDb,
|
|
26829
27293
|
getCachedTools,
|
|
27294
|
+
formatLocalCommandReview,
|
|
26830
27295
|
findServers,
|
|
26831
27296
|
enableSource,
|
|
26832
27297
|
enableServer,
|
|
@@ -26837,13 +27302,18 @@ export {
|
|
|
26837
27302
|
disableServer,
|
|
26838
27303
|
disableProviderProfile,
|
|
26839
27304
|
diagnoseServer,
|
|
27305
|
+
credentialRefPlaceholders,
|
|
26840
27306
|
connectToServer,
|
|
26841
27307
|
closeDb,
|
|
26842
27308
|
cloneServer,
|
|
26843
27309
|
callTool,
|
|
27310
|
+
assertLocalCommandConsent,
|
|
26844
27311
|
addSource,
|
|
26845
27312
|
addServer,
|
|
26846
27313
|
addMachine,
|
|
27314
|
+
REDACTED_CREDENTIAL_VALUE,
|
|
27315
|
+
LocalCommandConsentError,
|
|
26847
27316
|
DEFAULT_PROVIDER_PROFILE_SEEDS,
|
|
26848
|
-
DEFAULT_MACHINE_SEEDS
|
|
27317
|
+
DEFAULT_MACHINE_SEEDS,
|
|
27318
|
+
CredentialReferenceError
|
|
26849
27319
|
};
|