@daniel.stefan/metalink 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -0
- package/package.json +96 -0
- package/packages/cli/dist/bin/cli.d.ts +3 -0
- package/packages/cli/dist/bin/cli.d.ts.map +1 -0
- package/packages/cli/dist/bin/cli.js +4 -0
- package/packages/cli/dist/bin/cli.js.map +1 -0
- package/packages/cli/dist/commands/config/init.d.ts +9 -0
- package/packages/cli/dist/commands/config/init.d.ts.map +1 -0
- package/packages/cli/dist/commands/config/init.js +38 -0
- package/packages/cli/dist/commands/config/init.js.map +1 -0
- package/packages/cli/dist/commands/config/validate.d.ts +9 -0
- package/packages/cli/dist/commands/config/validate.d.ts.map +1 -0
- package/packages/cli/dist/commands/config/validate.js +34 -0
- package/packages/cli/dist/commands/config/validate.js.map +1 -0
- package/packages/cli/dist/commands/daemon/restart.d.ts +15 -0
- package/packages/cli/dist/commands/daemon/restart.d.ts.map +1 -0
- package/packages/cli/dist/commands/daemon/restart.js +184 -0
- package/packages/cli/dist/commands/daemon/restart.js.map +1 -0
- package/packages/cli/dist/commands/daemon/start.d.ts +7 -0
- package/packages/cli/dist/commands/daemon/start.d.ts.map +1 -0
- package/packages/cli/dist/commands/daemon/start.js +85 -0
- package/packages/cli/dist/commands/daemon/start.js.map +1 -0
- package/packages/cli/dist/commands/daemon/status.d.ts +7 -0
- package/packages/cli/dist/commands/daemon/status.d.ts.map +1 -0
- package/packages/cli/dist/commands/daemon/status.js +69 -0
- package/packages/cli/dist/commands/daemon/status.js.map +1 -0
- package/packages/cli/dist/commands/daemon/stop.d.ts +8 -0
- package/packages/cli/dist/commands/daemon/stop.d.ts.map +1 -0
- package/packages/cli/dist/commands/daemon/stop.js +77 -0
- package/packages/cli/dist/commands/daemon/stop.js.map +1 -0
- package/packages/cli/dist/commands/import/mcpm.d.ts +10 -0
- package/packages/cli/dist/commands/import/mcpm.d.ts.map +1 -0
- package/packages/cli/dist/commands/import/mcpm.js +58 -0
- package/packages/cli/dist/commands/import/mcpm.js.map +1 -0
- package/packages/cli/dist/commands/safety/add-risky-pattern.d.ts +16 -0
- package/packages/cli/dist/commands/safety/add-risky-pattern.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/add-risky-pattern.js +72 -0
- package/packages/cli/dist/commands/safety/add-risky-pattern.js.map +1 -0
- package/packages/cli/dist/commands/safety/add-risky.d.ts +12 -0
- package/packages/cli/dist/commands/safety/add-risky.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/add-risky.js +52 -0
- package/packages/cli/dist/commands/safety/add-risky.js.map +1 -0
- package/packages/cli/dist/commands/safety/add-safe-pattern.d.ts +16 -0
- package/packages/cli/dist/commands/safety/add-safe-pattern.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/add-safe-pattern.js +72 -0
- package/packages/cli/dist/commands/safety/add-safe-pattern.js.map +1 -0
- package/packages/cli/dist/commands/safety/add-safe.d.ts +12 -0
- package/packages/cli/dist/commands/safety/add-safe.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/add-safe.js +52 -0
- package/packages/cli/dist/commands/safety/add-safe.js.map +1 -0
- package/packages/cli/dist/commands/safety/check.d.ts +9 -0
- package/packages/cli/dist/commands/safety/check.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/check.js +36 -0
- package/packages/cli/dist/commands/safety/check.js.map +1 -0
- package/packages/cli/dist/commands/safety/export.d.ts +10 -0
- package/packages/cli/dist/commands/safety/export.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/export.js +48 -0
- package/packages/cli/dist/commands/safety/export.js.map +1 -0
- package/packages/cli/dist/commands/safety/import.d.ts +12 -0
- package/packages/cli/dist/commands/safety/import.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/import.js +78 -0
- package/packages/cli/dist/commands/safety/import.js.map +1 -0
- package/packages/cli/dist/commands/safety/index.d.ts +8 -0
- package/packages/cli/dist/commands/safety/index.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/index.js +46 -0
- package/packages/cli/dist/commands/safety/index.js.map +1 -0
- package/packages/cli/dist/commands/safety/list.d.ts +6 -0
- package/packages/cli/dist/commands/safety/list.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/list.js +77 -0
- package/packages/cli/dist/commands/safety/list.js.map +1 -0
- package/packages/cli/dist/commands/safety/remove.d.ts +9 -0
- package/packages/cli/dist/commands/safety/remove.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/remove.js +46 -0
- package/packages/cli/dist/commands/safety/remove.js.map +1 -0
- package/packages/cli/dist/commands/safety/reset.d.ts +9 -0
- package/packages/cli/dist/commands/safety/reset.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/reset.js +46 -0
- package/packages/cli/dist/commands/safety/reset.js.map +1 -0
- package/packages/cli/dist/commands/safety/validate.d.ts +9 -0
- package/packages/cli/dist/commands/safety/validate.d.ts.map +1 -0
- package/packages/cli/dist/commands/safety/validate.js +51 -0
- package/packages/cli/dist/commands/safety/validate.js.map +1 -0
- package/packages/cli/dist/commands/secret/get.d.ts +9 -0
- package/packages/cli/dist/commands/secret/get.d.ts.map +1 -0
- package/packages/cli/dist/commands/secret/get.js +26 -0
- package/packages/cli/dist/commands/secret/get.js.map +1 -0
- package/packages/cli/dist/commands/secret/set.d.ts +10 -0
- package/packages/cli/dist/commands/secret/set.d.ts.map +1 -0
- package/packages/cli/dist/commands/secret/set.js +22 -0
- package/packages/cli/dist/commands/secret/set.js.map +1 -0
- package/packages/cli/dist/commands/server/__tests__/server-management-e2e.test.d.ts +2 -0
- package/packages/cli/dist/commands/server/__tests__/server-management-e2e.test.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/__tests__/server-management-e2e.test.js +234 -0
- package/packages/cli/dist/commands/server/__tests__/server-management-e2e.test.js.map +1 -0
- package/packages/cli/dist/commands/server/add.d.ts +14 -0
- package/packages/cli/dist/commands/server/add.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/add.js +86 -0
- package/packages/cli/dist/commands/server/add.js.map +1 -0
- package/packages/cli/dist/commands/server/available.d.ts +10 -0
- package/packages/cli/dist/commands/server/available.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/available.js +62 -0
- package/packages/cli/dist/commands/server/available.js.map +1 -0
- package/packages/cli/dist/commands/server/debug.d.ts +18 -0
- package/packages/cli/dist/commands/server/debug.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/debug.js +165 -0
- package/packages/cli/dist/commands/server/debug.js.map +1 -0
- package/packages/cli/dist/commands/server/info.d.ts +13 -0
- package/packages/cli/dist/commands/server/info.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/info.js +62 -0
- package/packages/cli/dist/commands/server/info.js.map +1 -0
- package/packages/cli/dist/commands/server/list.d.ts +10 -0
- package/packages/cli/dist/commands/server/list.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/list.js +105 -0
- package/packages/cli/dist/commands/server/list.js.map +1 -0
- package/packages/cli/dist/commands/server/refresh-tools.d.ts +13 -0
- package/packages/cli/dist/commands/server/refresh-tools.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/refresh-tools.js +46 -0
- package/packages/cli/dist/commands/server/refresh-tools.js.map +1 -0
- package/packages/cli/dist/commands/server/remove.d.ts +12 -0
- package/packages/cli/dist/commands/server/remove.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/remove.js +39 -0
- package/packages/cli/dist/commands/server/remove.js.map +1 -0
- package/packages/cli/dist/commands/server/restart.d.ts +9 -0
- package/packages/cli/dist/commands/server/restart.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/restart.js +30 -0
- package/packages/cli/dist/commands/server/restart.js.map +1 -0
- package/packages/cli/dist/commands/server/start.d.ts +9 -0
- package/packages/cli/dist/commands/server/start.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/start.js +37 -0
- package/packages/cli/dist/commands/server/start.js.map +1 -0
- package/packages/cli/dist/commands/server/status.d.ts +9 -0
- package/packages/cli/dist/commands/server/status.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/status.js +30 -0
- package/packages/cli/dist/commands/server/status.js.map +1 -0
- package/packages/cli/dist/commands/server/stop.d.ts +9 -0
- package/packages/cli/dist/commands/server/stop.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/stop.js +31 -0
- package/packages/cli/dist/commands/server/stop.js.map +1 -0
- package/packages/cli/dist/commands/server/validate.d.ts +15 -0
- package/packages/cli/dist/commands/server/validate.d.ts.map +1 -0
- package/packages/cli/dist/commands/server/validate.js +87 -0
- package/packages/cli/dist/commands/server/validate.js.map +1 -0
- package/packages/cli/dist/commands/stdio.d.ts +36 -0
- package/packages/cli/dist/commands/stdio.d.ts.map +1 -0
- package/packages/cli/dist/commands/stdio.js +85 -0
- package/packages/cli/dist/commands/stdio.js.map +1 -0
- package/packages/cli/dist/commands/tool/execute-confirm.d.ts +12 -0
- package/packages/cli/dist/commands/tool/execute-confirm.d.ts.map +1 -0
- package/packages/cli/dist/commands/tool/execute-confirm.js +98 -0
- package/packages/cli/dist/commands/tool/execute-confirm.js.map +1 -0
- package/packages/cli/dist/commands/tool/execute.d.ts +12 -0
- package/packages/cli/dist/commands/tool/execute.d.ts.map +1 -0
- package/packages/cli/dist/commands/tool/execute.js +55 -0
- package/packages/cli/dist/commands/tool/execute.js.map +1 -0
- package/packages/cli/dist/commands/tool/list.d.ts +13 -0
- package/packages/cli/dist/commands/tool/list.d.ts.map +1 -0
- package/packages/cli/dist/commands/tool/list.js +91 -0
- package/packages/cli/dist/commands/tool/list.js.map +1 -0
- package/packages/cli/dist/commands/tool/search.d.ts +12 -0
- package/packages/cli/dist/commands/tool/search.d.ts.map +1 -0
- package/packages/cli/dist/commands/tool/search.js +87 -0
- package/packages/cli/dist/commands/tool/search.js.map +1 -0
- package/packages/cli/dist/index.d.ts +2 -0
- package/packages/cli/dist/index.d.ts.map +1 -0
- package/packages/cli/dist/index.js +9 -0
- package/packages/cli/dist/index.js.map +1 -0
- package/packages/cli/dist/utils/daemon-checker.d.ts +18 -0
- package/packages/cli/dist/utils/daemon-checker.d.ts.map +1 -0
- package/packages/cli/dist/utils/daemon-checker.js +69 -0
- package/packages/cli/dist/utils/daemon-checker.js.map +1 -0
- package/packages/cli/dist/utils/daemon-endpoint.d.ts +7 -0
- package/packages/cli/dist/utils/daemon-endpoint.d.ts.map +1 -0
- package/packages/cli/dist/utils/daemon-endpoint.js +13 -0
- package/packages/cli/dist/utils/daemon-endpoint.js.map +1 -0
- package/packages/cli/dist/utils/get-configured-port.d.ts +43 -0
- package/packages/cli/dist/utils/get-configured-port.d.ts.map +1 -0
- package/packages/cli/dist/utils/get-configured-port.js +141 -0
- package/packages/cli/dist/utils/get-configured-port.js.map +1 -0
- package/packages/cli/dist/utils/stdio-bridge.d.ts +48 -0
- package/packages/cli/dist/utils/stdio-bridge.d.ts.map +1 -0
- package/packages/cli/dist/utils/stdio-bridge.js +181 -0
- package/packages/cli/dist/utils/stdio-bridge.js.map +1 -0
- package/packages/cli/package.json +48 -0
- package/packages/core/dist/config/defaults.d.ts +36 -0
- package/packages/core/dist/config/defaults.d.ts.map +1 -0
- package/packages/core/dist/config/defaults.js +324 -0
- package/packages/core/dist/config/defaults.js.map +1 -0
- package/packages/core/dist/config/index.d.ts +9 -0
- package/packages/core/dist/config/index.d.ts.map +1 -0
- package/packages/core/dist/config/index.js +14 -0
- package/packages/core/dist/config/index.js.map +1 -0
- package/packages/core/dist/config/loader.d.ts +269 -0
- package/packages/core/dist/config/loader.d.ts.map +1 -0
- package/packages/core/dist/config/loader.js +777 -0
- package/packages/core/dist/config/loader.js.map +1 -0
- package/packages/core/dist/config/registry.d.ts +212 -0
- package/packages/core/dist/config/registry.d.ts.map +1 -0
- package/packages/core/dist/config/registry.js +754 -0
- package/packages/core/dist/config/registry.js.map +1 -0
- package/packages/core/dist/config/schema.d.ts +4352 -0
- package/packages/core/dist/config/schema.d.ts.map +1 -0
- package/packages/core/dist/config/schema.js +267 -0
- package/packages/core/dist/config/schema.js.map +1 -0
- package/packages/core/dist/daemon.d.ts +7 -0
- package/packages/core/dist/daemon.d.ts.map +1 -0
- package/packages/core/dist/daemon.js +116 -0
- package/packages/core/dist/daemon.js.map +1 -0
- package/packages/core/dist/http-client-retry.d.ts +67 -0
- package/packages/core/dist/http-client-retry.d.ts.map +1 -0
- package/packages/core/dist/http-client-retry.js +133 -0
- package/packages/core/dist/http-client-retry.js.map +1 -0
- package/packages/core/dist/http-client-updated.d.ts +147 -0
- package/packages/core/dist/http-client-updated.d.ts.map +1 -0
- package/packages/core/dist/http-client-updated.js +452 -0
- package/packages/core/dist/http-client-updated.js.map +1 -0
- package/packages/core/dist/http-client.d.ts +207 -0
- package/packages/core/dist/http-client.d.ts.map +1 -0
- package/packages/core/dist/http-client.js +704 -0
- package/packages/core/dist/http-client.js.map +1 -0
- package/packages/core/dist/index.d.ts +13 -0
- package/packages/core/dist/index.d.ts.map +1 -0
- package/packages/core/dist/index.js +23 -0
- package/packages/core/dist/index.js.map +1 -0
- package/packages/core/dist/logging/index.d.ts +46 -0
- package/packages/core/dist/logging/index.d.ts.map +1 -0
- package/packages/core/dist/logging/index.js +74 -0
- package/packages/core/dist/logging/index.js.map +1 -0
- package/packages/core/dist/metrics/index.d.ts +339 -0
- package/packages/core/dist/metrics/index.d.ts.map +1 -0
- package/packages/core/dist/metrics/index.js +792 -0
- package/packages/core/dist/metrics/index.js.map +1 -0
- package/packages/core/dist/plugins/index.d.ts +49 -0
- package/packages/core/dist/plugins/index.d.ts.map +1 -0
- package/packages/core/dist/plugins/index.js +82 -0
- package/packages/core/dist/plugins/index.js.map +1 -0
- package/packages/core/dist/secrets/index.d.ts +6 -0
- package/packages/core/dist/secrets/index.d.ts.map +1 -0
- package/packages/core/dist/secrets/index.js +5 -0
- package/packages/core/dist/secrets/index.js.map +1 -0
- package/packages/core/dist/secrets/keyring.d.ts +54 -0
- package/packages/core/dist/secrets/keyring.d.ts.map +1 -0
- package/packages/core/dist/secrets/keyring.js +141 -0
- package/packages/core/dist/secrets/keyring.js.map +1 -0
- package/packages/core/dist/server/batch-executor.d.ts +83 -0
- package/packages/core/dist/server/batch-executor.d.ts.map +1 -0
- package/packages/core/dist/server/batch-executor.js +291 -0
- package/packages/core/dist/server/batch-executor.js.map +1 -0
- package/packages/core/dist/server/circuit-breaker.d.ts +215 -0
- package/packages/core/dist/server/circuit-breaker.d.ts.map +1 -0
- package/packages/core/dist/server/circuit-breaker.js +330 -0
- package/packages/core/dist/server/circuit-breaker.js.map +1 -0
- package/packages/core/dist/server/client-detection.d.ts +40 -0
- package/packages/core/dist/server/client-detection.d.ts.map +1 -0
- package/packages/core/dist/server/client-detection.js +242 -0
- package/packages/core/dist/server/client-detection.js.map +1 -0
- package/packages/core/dist/server/client-profiles.d.ts +102 -0
- package/packages/core/dist/server/client-profiles.d.ts.map +1 -0
- package/packages/core/dist/server/client-profiles.js +254 -0
- package/packages/core/dist/server/client-profiles.js.map +1 -0
- package/packages/core/dist/server/http.d.ts +386 -0
- package/packages/core/dist/server/http.d.ts.map +1 -0
- package/packages/core/dist/server/http.js +4253 -0
- package/packages/core/dist/server/http.js.map +1 -0
- package/packages/core/dist/server/index.d.ts +7 -0
- package/packages/core/dist/server/index.d.ts.map +1 -0
- package/packages/core/dist/server/index.js +6 -0
- package/packages/core/dist/server/index.js.map +1 -0
- package/packages/core/dist/server/manager.d.ts +458 -0
- package/packages/core/dist/server/manager.d.ts.map +1 -0
- package/packages/core/dist/server/manager.js +3255 -0
- package/packages/core/dist/server/manager.js.map +1 -0
- package/packages/core/dist/server/managers/HttpConnectionManager.d.ts +69 -0
- package/packages/core/dist/server/managers/HttpConnectionManager.d.ts.map +1 -0
- package/packages/core/dist/server/managers/HttpConnectionManager.js +214 -0
- package/packages/core/dist/server/managers/HttpConnectionManager.js.map +1 -0
- package/packages/core/dist/server/managers/ProcessManager.d.ts +128 -0
- package/packages/core/dist/server/managers/ProcessManager.d.ts.map +1 -0
- package/packages/core/dist/server/managers/ProcessManager.js +443 -0
- package/packages/core/dist/server/managers/ProcessManager.js.map +1 -0
- package/packages/core/dist/server/managers/SchemaCacheManager.d.ts +152 -0
- package/packages/core/dist/server/managers/SchemaCacheManager.d.ts.map +1 -0
- package/packages/core/dist/server/managers/SchemaCacheManager.js +426 -0
- package/packages/core/dist/server/managers/SchemaCacheManager.js.map +1 -0
- package/packages/core/dist/server/managers/index.d.ts +9 -0
- package/packages/core/dist/server/managers/index.d.ts.map +1 -0
- package/packages/core/dist/server/managers/index.js +9 -0
- package/packages/core/dist/server/managers/index.js.map +1 -0
- package/packages/core/dist/server/metrics.d.ts +134 -0
- package/packages/core/dist/server/metrics.d.ts.map +1 -0
- package/packages/core/dist/server/metrics.js +273 -0
- package/packages/core/dist/server/metrics.js.map +1 -0
- package/packages/core/dist/server/prompts.d.ts +58 -0
- package/packages/core/dist/server/prompts.d.ts.map +1 -0
- package/packages/core/dist/server/prompts.js +405 -0
- package/packages/core/dist/server/prompts.js.map +1 -0
- package/packages/core/dist/server/protocol-versions.d.ts +49 -0
- package/packages/core/dist/server/protocol-versions.d.ts.map +1 -0
- package/packages/core/dist/server/protocol-versions.js +173 -0
- package/packages/core/dist/server/protocol-versions.js.map +1 -0
- package/packages/core/dist/server/resources.d.ts +64 -0
- package/packages/core/dist/server/resources.d.ts.map +1 -0
- package/packages/core/dist/server/resources.js +243 -0
- package/packages/core/dist/server/resources.js.map +1 -0
- package/packages/core/dist/server/schema-store.d.ts +84 -0
- package/packages/core/dist/server/schema-store.d.ts.map +1 -0
- package/packages/core/dist/server/schema-store.js +234 -0
- package/packages/core/dist/server/schema-store.js.map +1 -0
- package/packages/core/dist/server/schema-validator.d.ts +51 -0
- package/packages/core/dist/server/schema-validator.d.ts.map +1 -0
- package/packages/core/dist/server/schema-validator.js +208 -0
- package/packages/core/dist/server/schema-validator.js.map +1 -0
- package/packages/core/dist/server/token-calculator.d.ts +44 -0
- package/packages/core/dist/server/token-calculator.d.ts.map +1 -0
- package/packages/core/dist/server/token-calculator.js +53 -0
- package/packages/core/dist/server/token-calculator.js.map +1 -0
- package/packages/core/dist/server/types.d.ts +45 -0
- package/packages/core/dist/server/types.d.ts.map +1 -0
- package/packages/core/dist/server/types.js +5 -0
- package/packages/core/dist/server/types.js.map +1 -0
- package/packages/core/dist/utils/file-lock.d.ts +73 -0
- package/packages/core/dist/utils/file-lock.d.ts.map +1 -0
- package/packages/core/dist/utils/file-lock.js +235 -0
- package/packages/core/dist/utils/file-lock.js.map +1 -0
- package/packages/core/package.json +36 -0
- package/packages/dashboard/dist/assets/index-B7hvkCMu.css +1 -0
- package/packages/dashboard/dist/assets/index-yZhLPpzr.js +1 -0
- package/packages/dashboard/dist/index.html +14 -0
- package/packages/dashboard/package.json +24 -0
- package/packages/shared/dist/version.d.ts +2 -0
- package/packages/shared/dist/version.d.ts.map +1 -0
- package/packages/shared/dist/version.js +2 -0
- package/packages/shared/dist/version.js.map +1 -0
- package/packages/shared/package.json +18 -0
|
@@ -0,0 +1,777 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as yaml from 'js-yaml';
|
|
4
|
+
import { validateConfig, } from './schema.js';
|
|
5
|
+
/**
|
|
6
|
+
* ConfigLoader - Handles loading and validation of metalink configurations
|
|
7
|
+
* Supports YAML and JSON formats with environment variable interpolation
|
|
8
|
+
*/
|
|
9
|
+
export class ConfigLoader {
|
|
10
|
+
constructor(configPath) {
|
|
11
|
+
this.config = null;
|
|
12
|
+
// Priority: provided path > METALINK_CONFIG env > default locations
|
|
13
|
+
this.configPath =
|
|
14
|
+
configPath ||
|
|
15
|
+
process.env.METALINK_CONFIG ||
|
|
16
|
+
this.findDefaultConfigPath();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Finds default config location
|
|
20
|
+
* Tries: ./metalink.config.yaml, ./metalink.config.json, ~/.config/metalink/config.yaml
|
|
21
|
+
*/
|
|
22
|
+
findDefaultConfigPath() {
|
|
23
|
+
const candidates = [
|
|
24
|
+
path.join(process.cwd(), 'metalink.config.yaml'),
|
|
25
|
+
path.join(process.cwd(), 'metalink.config.json'),
|
|
26
|
+
path.join(process.env.HOME || '~', '.config', 'metalink', 'config.yaml'),
|
|
27
|
+
path.join(process.env.HOME || '~', '.config', 'metalink', 'config.json'),
|
|
28
|
+
];
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
if (fs.existsSync(candidate)) {
|
|
31
|
+
return candidate;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Return default if none exist (will fail gracefully on load)
|
|
35
|
+
return path.join(process.cwd(), 'metalink.config.yaml');
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Load configuration from file
|
|
39
|
+
*/
|
|
40
|
+
async load() {
|
|
41
|
+
if (this.config) {
|
|
42
|
+
return this.config;
|
|
43
|
+
}
|
|
44
|
+
if (!fs.existsSync(this.configPath)) {
|
|
45
|
+
throw new Error(`Config file not found: ${this.configPath}`);
|
|
46
|
+
}
|
|
47
|
+
const fileContent = fs.readFileSync(this.configPath, 'utf-8');
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
// Detect format based on file extension
|
|
51
|
+
if (this.configPath.endsWith('.yaml') || this.configPath.endsWith('.yml')) {
|
|
52
|
+
parsed = yaml.load(fileContent);
|
|
53
|
+
}
|
|
54
|
+
else if (this.configPath.endsWith('.json')) {
|
|
55
|
+
parsed = JSON.parse(fileContent);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Try YAML first, fall back to JSON
|
|
59
|
+
try {
|
|
60
|
+
parsed = yaml.load(fileContent);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
parsed = JSON.parse(fileContent);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw new Error(`Failed to parse config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
69
|
+
}
|
|
70
|
+
// Interpolate environment variables
|
|
71
|
+
const interpolated = this.interpolateEnv(parsed);
|
|
72
|
+
// Validate against schema
|
|
73
|
+
try {
|
|
74
|
+
this.config = validateConfig(interpolated);
|
|
75
|
+
// v1.3.59: Load base_servers from registry immediately after config validation
|
|
76
|
+
this.loadBaseServersFromRegistry();
|
|
77
|
+
return this.config;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw new Error(`Config validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Load config synchronously
|
|
85
|
+
*/
|
|
86
|
+
loadSync() {
|
|
87
|
+
if (this.config) {
|
|
88
|
+
return this.config;
|
|
89
|
+
}
|
|
90
|
+
if (!fs.existsSync(this.configPath)) {
|
|
91
|
+
throw new Error(`Config file not found: ${this.configPath}`);
|
|
92
|
+
}
|
|
93
|
+
const fileContent = fs.readFileSync(this.configPath, 'utf-8');
|
|
94
|
+
let parsed;
|
|
95
|
+
try {
|
|
96
|
+
if (this.configPath.endsWith('.yaml') || this.configPath.endsWith('.yml')) {
|
|
97
|
+
parsed = yaml.load(fileContent);
|
|
98
|
+
}
|
|
99
|
+
else if (this.configPath.endsWith('.json')) {
|
|
100
|
+
parsed = JSON.parse(fileContent);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
try {
|
|
104
|
+
parsed = yaml.load(fileContent);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
parsed = JSON.parse(fileContent);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
throw new Error(`Failed to parse config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
113
|
+
}
|
|
114
|
+
const interpolated = this.interpolateEnv(parsed);
|
|
115
|
+
try {
|
|
116
|
+
this.config = validateConfig(interpolated);
|
|
117
|
+
// v1.3.59: Load base_servers from registry immediately after config validation
|
|
118
|
+
this.loadBaseServersFromRegistry();
|
|
119
|
+
return this.config;
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
throw new Error(`Config validation failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Load base_servers from mcp-registry.json and merge into config
|
|
127
|
+
* v1.3.59: Registry is source of truth for base_servers
|
|
128
|
+
*/
|
|
129
|
+
loadBaseServersFromRegistry() {
|
|
130
|
+
if (!this.config)
|
|
131
|
+
return;
|
|
132
|
+
const registryPath = this.getRegistryPath();
|
|
133
|
+
if (!fs.existsSync(registryPath))
|
|
134
|
+
return;
|
|
135
|
+
try {
|
|
136
|
+
const registryContent = fs.readFileSync(registryPath, 'utf8');
|
|
137
|
+
const registry = JSON.parse(registryContent);
|
|
138
|
+
if (registry.base_servers && Array.isArray(registry.base_servers)) {
|
|
139
|
+
this.config.base_servers = registry.base_servers;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Registry read failed, use config defaults
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Interpolate environment variables in config
|
|
148
|
+
* Supports ${VAR_NAME} and ${secret:KEY} syntax
|
|
149
|
+
*/
|
|
150
|
+
interpolateEnv(obj) {
|
|
151
|
+
if (typeof obj === 'string') {
|
|
152
|
+
// Handle ${VAR_NAME} syntax
|
|
153
|
+
return obj.replace(/\$\{([^}]+)\}/g, (match, key) => {
|
|
154
|
+
if (key.startsWith('secret:')) {
|
|
155
|
+
// Secrets are resolved separately (prepare placeholder)
|
|
156
|
+
return match; // Keep as-is for secret resolution phase
|
|
157
|
+
}
|
|
158
|
+
return process.env[key] || match;
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
162
|
+
if (Array.isArray(obj)) {
|
|
163
|
+
return obj.map((item) => this.interpolateEnv(item));
|
|
164
|
+
}
|
|
165
|
+
const result = {};
|
|
166
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
167
|
+
result[key] = this.interpolateEnv(value);
|
|
168
|
+
}
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
return obj;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get specific server configuration by name
|
|
175
|
+
*/
|
|
176
|
+
getServer(name) {
|
|
177
|
+
if (!this.config) {
|
|
178
|
+
throw new Error('Config not loaded. Call load() or loadSync() first.');
|
|
179
|
+
}
|
|
180
|
+
// Check config servers first
|
|
181
|
+
let server = this.config.servers?.find((s) => s.name === name);
|
|
182
|
+
if (server)
|
|
183
|
+
return server;
|
|
184
|
+
// Check persistent registry if available
|
|
185
|
+
const registryPath = this.getRegistryPath();
|
|
186
|
+
if (fs.existsSync(registryPath)) {
|
|
187
|
+
try {
|
|
188
|
+
const registryContent = fs.readFileSync(registryPath, 'utf8');
|
|
189
|
+
const registry = JSON.parse(registryContent);
|
|
190
|
+
server = registry.servers?.find((s) => s.name === name);
|
|
191
|
+
if (server)
|
|
192
|
+
return server;
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.warn(`Failed to load registry from ${registryPath}:`, error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Get daemon configuration
|
|
202
|
+
*/
|
|
203
|
+
getDaemon() {
|
|
204
|
+
if (!this.config) {
|
|
205
|
+
throw new Error('Config not loaded. Call load() or loadSync() first.');
|
|
206
|
+
}
|
|
207
|
+
return (this.config.daemon || {
|
|
208
|
+
port: 3000,
|
|
209
|
+
host: '0.0.0.0',
|
|
210
|
+
logLevel: 'info',
|
|
211
|
+
auth: { enabled: false, type: 'bearer' },
|
|
212
|
+
rateLimit: { enabled: true, windowMs: 60000, max: 500, keyGenerator: 'ip' },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Get all available servers (including those not currently exposed)
|
|
217
|
+
*/
|
|
218
|
+
getAllServers() {
|
|
219
|
+
if (!this.config) {
|
|
220
|
+
throw new Error('Config not loaded. Call load() or loadSync() first.');
|
|
221
|
+
}
|
|
222
|
+
// Start with config servers
|
|
223
|
+
let servers = [...(this.config.servers || [])];
|
|
224
|
+
// Migrate registry from old location if needed (one-time migration)
|
|
225
|
+
this.migrateRegistry();
|
|
226
|
+
// Load from persistent mcp-registry.json location
|
|
227
|
+
const registryPath = this.getRegistryPath();
|
|
228
|
+
if (fs.existsSync(registryPath)) {
|
|
229
|
+
try {
|
|
230
|
+
const registryContent = fs.readFileSync(registryPath, 'utf8');
|
|
231
|
+
const registry = JSON.parse(registryContent);
|
|
232
|
+
// Add registry servers (avoid duplicates)
|
|
233
|
+
const configNames = new Set(servers.map(s => s.name));
|
|
234
|
+
for (const server of registry.servers || []) {
|
|
235
|
+
if (!configNames.has(server.name)) {
|
|
236
|
+
servers.push(server);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
// Registry read failed, use config servers only
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return servers;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get all servers from registry
|
|
248
|
+
* v1.3.58: Removed expose_servers/disable_servers filtering - all servers in registry are available
|
|
249
|
+
* Use base_servers config to control which servers auto-start on daemon init
|
|
250
|
+
*/
|
|
251
|
+
getServers() {
|
|
252
|
+
return this.getAllServers();
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Get configuration by profile name
|
|
256
|
+
*/
|
|
257
|
+
getProfile(profileName) {
|
|
258
|
+
if (!this.config) {
|
|
259
|
+
throw new Error('Config not loaded. Call load() or loadSync() first.');
|
|
260
|
+
}
|
|
261
|
+
return this.config.profiles?.find((p) => p.name === profileName);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Set current config (useful for testing)
|
|
265
|
+
*/
|
|
266
|
+
setConfig(config) {
|
|
267
|
+
this.config = config;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get currently loaded config
|
|
271
|
+
*/
|
|
272
|
+
getConfig() {
|
|
273
|
+
if (!this.config) {
|
|
274
|
+
// Auto-load if cache was invalidated to prevent "Config not loaded" errors
|
|
275
|
+
this.config = this.loadSync();
|
|
276
|
+
}
|
|
277
|
+
return this.config;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get base servers (read from config, never hardcoded)
|
|
281
|
+
* These servers are directly exposed in tools/list and auto-started on daemon init
|
|
282
|
+
*/
|
|
283
|
+
getBaseServers() {
|
|
284
|
+
const config = this.getConfig();
|
|
285
|
+
return config.base_servers || [
|
|
286
|
+
'memory',
|
|
287
|
+
'time',
|
|
288
|
+
'fetch',
|
|
289
|
+
'sequential-thinking',
|
|
290
|
+
'duckduckgo',
|
|
291
|
+
'sysinstruct',
|
|
292
|
+
];
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Get tool safety classification rules
|
|
296
|
+
* Used to determine if a tool requires execute_tool (safe) or execute_tool_confirm (risky)
|
|
297
|
+
* Reads from mcp-registry.json tool_safety section (NOT from config file)
|
|
298
|
+
*/
|
|
299
|
+
getToolSafetyRules() {
|
|
300
|
+
// Read from mcp-registry.json (where toolSafetyRules is defined)
|
|
301
|
+
const registryPath = this.getRegistryPath();
|
|
302
|
+
let toolSafety = null;
|
|
303
|
+
try {
|
|
304
|
+
// Use synchronous fs.readFileSync (fs already imported at top)
|
|
305
|
+
const registryContent = fs.readFileSync(registryPath, 'utf8');
|
|
306
|
+
const registry = JSON.parse(registryContent);
|
|
307
|
+
toolSafety = registry.toolSafetyRules;
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
// Registry file doesn't exist or can't be read - use defaults
|
|
311
|
+
}
|
|
312
|
+
// Return tool_safety from registry or defaults
|
|
313
|
+
return toolSafety || {
|
|
314
|
+
safePatterns: [
|
|
315
|
+
'.*search.*',
|
|
316
|
+
'.*list.*',
|
|
317
|
+
'.*get.*',
|
|
318
|
+
'.*read.*',
|
|
319
|
+
'.*fetch.*',
|
|
320
|
+
'.*describe.*',
|
|
321
|
+
'.*find.*',
|
|
322
|
+
'.*status.*',
|
|
323
|
+
'.*query.*',
|
|
324
|
+
'.*open.*', // Added: open_nodes and similar read operations
|
|
325
|
+
'.*convert.*', // Added: convert_time and similar transformations
|
|
326
|
+
],
|
|
327
|
+
riskyPatterns: [
|
|
328
|
+
'.*create.*',
|
|
329
|
+
'.*delete.*',
|
|
330
|
+
'.*update.*',
|
|
331
|
+
'.*execute.*',
|
|
332
|
+
'.*write.*',
|
|
333
|
+
'.*commit.*',
|
|
334
|
+
'.*add.*',
|
|
335
|
+
'.*remove.*',
|
|
336
|
+
'.*set.*',
|
|
337
|
+
'.*modify.*',
|
|
338
|
+
'.*change.*',
|
|
339
|
+
'.*insert.*',
|
|
340
|
+
'.*drop.*',
|
|
341
|
+
'.*alter.*',
|
|
342
|
+
],
|
|
343
|
+
safeToolOverrides: [
|
|
344
|
+
'memory:open_nodes', // Read/query operation
|
|
345
|
+
'time:convert_time', // Safe transformation
|
|
346
|
+
'sequential-thinking:sequentialthinking', // Read-only reasoning
|
|
347
|
+
// Note: Server-specific overrides (e.g., meta*:retrieve) should be in user's
|
|
348
|
+
// mcp-registry.json toolSafetyRules, not hardcoded here
|
|
349
|
+
],
|
|
350
|
+
riskyToolOverrides: [
|
|
351
|
+
'time:wait', // Time delay can be used maliciously
|
|
352
|
+
],
|
|
353
|
+
// v1.1.55: Argument inspection rules for SQL-based tools
|
|
354
|
+
// Supports wildcards (*, ?) for matching multiple server instances
|
|
355
|
+
// Note: Server-specific rules (e.g., meta*:execute) should be in user's
|
|
356
|
+
// mcp-registry.json toolSafetyRules, not hardcoded here
|
|
357
|
+
argumentInspectionRules: [],
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Set tool safety rules in registry with atomic write and backup
|
|
362
|
+
* Phase 1: Core persistence method
|
|
363
|
+
*/
|
|
364
|
+
async setToolSafetyRules(rules) {
|
|
365
|
+
const registryPath = this.getRegistryPath();
|
|
366
|
+
const registryDir = path.dirname(registryPath);
|
|
367
|
+
// Ensure directory exists
|
|
368
|
+
if (!fs.existsSync(registryDir)) {
|
|
369
|
+
fs.mkdirSync(registryDir, { recursive: true });
|
|
370
|
+
}
|
|
371
|
+
try {
|
|
372
|
+
let registry = {};
|
|
373
|
+
// Read existing registry if it exists
|
|
374
|
+
if (fs.existsSync(registryPath)) {
|
|
375
|
+
const registryContent = fs.readFileSync(registryPath, 'utf8');
|
|
376
|
+
registry = JSON.parse(registryContent);
|
|
377
|
+
}
|
|
378
|
+
// Merge new rules with existing registry
|
|
379
|
+
registry.toolSafetyRules = {
|
|
380
|
+
...registry.toolSafetyRules,
|
|
381
|
+
...rules,
|
|
382
|
+
};
|
|
383
|
+
// Create backup before write
|
|
384
|
+
const backupPath = `${registryPath}.backup`;
|
|
385
|
+
if (fs.existsSync(registryPath)) {
|
|
386
|
+
fs.copyFileSync(registryPath, backupPath);
|
|
387
|
+
}
|
|
388
|
+
// Atomic write: write to temp file, then rename
|
|
389
|
+
const tempPath = `${registryPath}.tmp`;
|
|
390
|
+
fs.writeFileSync(tempPath, JSON.stringify(registry, null, 2), 'utf8');
|
|
391
|
+
fs.renameSync(tempPath, registryPath);
|
|
392
|
+
// Invalidate cache to force reload
|
|
393
|
+
this.invalidateCache();
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
throw new Error(`Failed to set tool safety rules: ${error instanceof Error ? error.message : String(error)}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Add a safe tool override
|
|
401
|
+
*/
|
|
402
|
+
async addSafeToolOverride(tool) {
|
|
403
|
+
if (!this.validateToolFormat(tool)) {
|
|
404
|
+
throw new Error(`Invalid tool format: ${tool}. Expected format: server:tool or server:*`);
|
|
405
|
+
}
|
|
406
|
+
const rules = this.getToolSafetyRules();
|
|
407
|
+
rules.safeToolOverrides = rules.safeToolOverrides || [];
|
|
408
|
+
// Add if not already present (no duplicates)
|
|
409
|
+
if (!rules.safeToolOverrides.includes(tool)) {
|
|
410
|
+
rules.safeToolOverrides.push(tool);
|
|
411
|
+
await this.setToolSafetyRules(rules);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Remove a safe tool override
|
|
416
|
+
*/
|
|
417
|
+
async removeSafeToolOverride(tool) {
|
|
418
|
+
const rules = this.getToolSafetyRules();
|
|
419
|
+
if (rules.safeToolOverrides) {
|
|
420
|
+
const originalLength = rules.safeToolOverrides.length;
|
|
421
|
+
rules.safeToolOverrides = rules.safeToolOverrides.filter((t) => t !== tool);
|
|
422
|
+
// Only write if something was actually removed
|
|
423
|
+
if (rules.safeToolOverrides.length < originalLength) {
|
|
424
|
+
await this.setToolSafetyRules(rules);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Add a risky tool override
|
|
430
|
+
*/
|
|
431
|
+
async addRiskyToolOverride(tool) {
|
|
432
|
+
if (!this.validateToolFormat(tool)) {
|
|
433
|
+
throw new Error(`Invalid tool format: ${tool}. Expected format: server:tool or server:*`);
|
|
434
|
+
}
|
|
435
|
+
const rules = this.getToolSafetyRules();
|
|
436
|
+
rules.riskyToolOverrides = rules.riskyToolOverrides || [];
|
|
437
|
+
// Add if not already present (no duplicates)
|
|
438
|
+
if (!rules.riskyToolOverrides.includes(tool)) {
|
|
439
|
+
rules.riskyToolOverrides.push(tool);
|
|
440
|
+
await this.setToolSafetyRules(rules);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Remove a risky tool override
|
|
445
|
+
*/
|
|
446
|
+
async removeRiskyToolOverride(tool) {
|
|
447
|
+
const rules = this.getToolSafetyRules();
|
|
448
|
+
if (rules.riskyToolOverrides) {
|
|
449
|
+
const originalLength = rules.riskyToolOverrides.length;
|
|
450
|
+
rules.riskyToolOverrides = rules.riskyToolOverrides.filter((t) => t !== tool);
|
|
451
|
+
// Only write if something was actually removed
|
|
452
|
+
if (rules.riskyToolOverrides.length < originalLength) {
|
|
453
|
+
await this.setToolSafetyRules(rules);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Add a safe pattern
|
|
459
|
+
*/
|
|
460
|
+
async addSafePattern(pattern) {
|
|
461
|
+
if (!this.validateRegexPattern(pattern)) {
|
|
462
|
+
throw new Error(`Invalid regex pattern: ${pattern}`);
|
|
463
|
+
}
|
|
464
|
+
const rules = this.getToolSafetyRules();
|
|
465
|
+
rules.safePatterns = rules.safePatterns || [];
|
|
466
|
+
// Add if not already present
|
|
467
|
+
if (!rules.safePatterns.includes(pattern)) {
|
|
468
|
+
rules.safePatterns.push(pattern);
|
|
469
|
+
await this.setToolSafetyRules(rules);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Remove a safe pattern
|
|
474
|
+
*/
|
|
475
|
+
async removeSafePattern(pattern) {
|
|
476
|
+
const rules = this.getToolSafetyRules();
|
|
477
|
+
if (rules.safePatterns) {
|
|
478
|
+
const originalLength = rules.safePatterns.length;
|
|
479
|
+
rules.safePatterns = rules.safePatterns.filter((p) => p !== pattern);
|
|
480
|
+
// Only write if something was actually removed
|
|
481
|
+
if (rules.safePatterns.length < originalLength) {
|
|
482
|
+
await this.setToolSafetyRules(rules);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Add a risky pattern
|
|
488
|
+
*/
|
|
489
|
+
async addRiskyPattern(pattern) {
|
|
490
|
+
if (!this.validateRegexPattern(pattern)) {
|
|
491
|
+
throw new Error(`Invalid regex pattern: ${pattern}`);
|
|
492
|
+
}
|
|
493
|
+
const rules = this.getToolSafetyRules();
|
|
494
|
+
rules.riskyPatterns = rules.riskyPatterns || [];
|
|
495
|
+
// Add if not already present
|
|
496
|
+
if (!rules.riskyPatterns.includes(pattern)) {
|
|
497
|
+
rules.riskyPatterns.push(pattern);
|
|
498
|
+
await this.setToolSafetyRules(rules);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Remove a risky pattern
|
|
503
|
+
*/
|
|
504
|
+
async removeRiskyPattern(pattern) {
|
|
505
|
+
const rules = this.getToolSafetyRules();
|
|
506
|
+
if (rules.riskyPatterns) {
|
|
507
|
+
const originalLength = rules.riskyPatterns.length;
|
|
508
|
+
rules.riskyPatterns = rules.riskyPatterns.filter((p) => p !== pattern);
|
|
509
|
+
// Only write if something was actually removed
|
|
510
|
+
if (rules.riskyPatterns.length < originalLength) {
|
|
511
|
+
await this.setToolSafetyRules(rules);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Reset safety rules to defaults
|
|
517
|
+
*/
|
|
518
|
+
async resetToDefaults() {
|
|
519
|
+
const registryPath = this.getRegistryPath();
|
|
520
|
+
const backupPath = `${registryPath}.backup.before-reset`;
|
|
521
|
+
try {
|
|
522
|
+
// Create backup before reset
|
|
523
|
+
if (fs.existsSync(registryPath)) {
|
|
524
|
+
fs.copyFileSync(registryPath, backupPath);
|
|
525
|
+
}
|
|
526
|
+
// Get current argumentInspectionRules to preserve them (v1.1.29)
|
|
527
|
+
const currentRules = this.getToolSafetyRules();
|
|
528
|
+
const argumentInspectionRules = currentRules.argumentInspectionRules || [];
|
|
529
|
+
// Reset to defaults (but preserve argumentInspectionRules)
|
|
530
|
+
const defaults = {
|
|
531
|
+
safePatterns: [
|
|
532
|
+
'.*search.*',
|
|
533
|
+
'.*list.*',
|
|
534
|
+
'.*get.*',
|
|
535
|
+
'.*read.*',
|
|
536
|
+
'.*fetch.*',
|
|
537
|
+
'.*describe.*',
|
|
538
|
+
'.*find.*',
|
|
539
|
+
'.*status.*',
|
|
540
|
+
'.*query.*',
|
|
541
|
+
],
|
|
542
|
+
riskyPatterns: [
|
|
543
|
+
'.*create.*',
|
|
544
|
+
'.*delete.*',
|
|
545
|
+
'.*update.*',
|
|
546
|
+
'.*execute.*',
|
|
547
|
+
'.*write.*',
|
|
548
|
+
'.*commit.*',
|
|
549
|
+
'.*add.*',
|
|
550
|
+
'.*remove.*',
|
|
551
|
+
'.*set.*',
|
|
552
|
+
'.*modify.*',
|
|
553
|
+
'.*change.*',
|
|
554
|
+
'.*insert.*',
|
|
555
|
+
'.*drop.*',
|
|
556
|
+
'.*alter.*',
|
|
557
|
+
],
|
|
558
|
+
safeToolOverrides: [],
|
|
559
|
+
riskyToolOverrides: [],
|
|
560
|
+
};
|
|
561
|
+
// Preserve argumentInspectionRules if they exist (v1.1.29)
|
|
562
|
+
if (argumentInspectionRules.length > 0) {
|
|
563
|
+
defaults.argumentInspectionRules = argumentInspectionRules;
|
|
564
|
+
}
|
|
565
|
+
await this.setToolSafetyRules(defaults);
|
|
566
|
+
}
|
|
567
|
+
catch (error) {
|
|
568
|
+
throw new Error(`Failed to reset safety rules: ${error instanceof Error ? error.message : String(error)}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Validate tool format (server:tool or server:*)
|
|
573
|
+
*/
|
|
574
|
+
validateToolFormat(tool) {
|
|
575
|
+
const pattern = /^[a-zA-Z0-9_-]+:[a-zA-Z0-9_*-]+$/;
|
|
576
|
+
return pattern.test(tool);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Validate regex pattern (try to compile it)
|
|
580
|
+
*/
|
|
581
|
+
validateRegexPattern(pattern) {
|
|
582
|
+
try {
|
|
583
|
+
new RegExp(pattern);
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
catch {
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Reload configuration
|
|
592
|
+
*/
|
|
593
|
+
async reload() {
|
|
594
|
+
this.config = null;
|
|
595
|
+
return this.load();
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Invalidate config cache and reload
|
|
599
|
+
* Used after registry changes to ensure fresh config
|
|
600
|
+
*/
|
|
601
|
+
invalidateCache() {
|
|
602
|
+
this.config = null;
|
|
603
|
+
// Auto-reload immediately to prevent "Config not loaded" errors
|
|
604
|
+
this.loadSync();
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Get the persistent registry file path
|
|
608
|
+
* Defaults to ~/.config/metalink/mcp-registry.json
|
|
609
|
+
* Respects METALINK_CONFIG_DIR environment variable if set
|
|
610
|
+
*/
|
|
611
|
+
getRegistryPath() {
|
|
612
|
+
const configDir = process.env.METALINK_CONFIG_DIR ||
|
|
613
|
+
path.join(process.env.HOME || process.env.USERPROFILE || '~', '.config', 'metalink');
|
|
614
|
+
return path.join(configDir, 'mcp-registry.json');
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Migrate registry from old location (current dir) to persistent location if needed
|
|
618
|
+
* This is called on first run to preserve user-added servers
|
|
619
|
+
*/
|
|
620
|
+
migrateRegistry() {
|
|
621
|
+
const oldRegistryPath = path.join(process.cwd(), 'mcp-registry.json');
|
|
622
|
+
const newRegistryPath = this.getRegistryPath();
|
|
623
|
+
// Skip if old registry doesn't exist
|
|
624
|
+
if (!fs.existsSync(oldRegistryPath)) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
// Skip if new registry already exists (migration already done)
|
|
628
|
+
if (fs.existsSync(newRegistryPath)) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
try {
|
|
632
|
+
// Ensure new directory exists
|
|
633
|
+
const registryDir = path.dirname(newRegistryPath);
|
|
634
|
+
if (!fs.existsSync(registryDir)) {
|
|
635
|
+
fs.mkdirSync(registryDir, { recursive: true });
|
|
636
|
+
}
|
|
637
|
+
// Copy registry to new location
|
|
638
|
+
fs.copyFileSync(oldRegistryPath, newRegistryPath);
|
|
639
|
+
console.log(`[ConfigLoader] Migrated registry from ${oldRegistryPath} to ${newRegistryPath}`);
|
|
640
|
+
}
|
|
641
|
+
catch (error) {
|
|
642
|
+
console.warn(`[ConfigLoader] Failed to migrate registry: ${error instanceof Error ? error.message : String(error)}`);
|
|
643
|
+
// Don't throw - migration failure shouldn't break startup
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Save a new server to the registry file
|
|
648
|
+
* Automatically invalidates config cache to force reload
|
|
649
|
+
*/
|
|
650
|
+
async saveServerToRegistry(config, options = {}) {
|
|
651
|
+
const { RegistryManager } = await import('./registry.js');
|
|
652
|
+
const registryPath = this.getRegistryPath();
|
|
653
|
+
const registry = new RegistryManager(registryPath);
|
|
654
|
+
const server = await registry.addServer(config, options);
|
|
655
|
+
// Invalidate cache so next load picks up the new server
|
|
656
|
+
this.invalidateCache();
|
|
657
|
+
return server;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Remove a server from the registry file
|
|
661
|
+
* Automatically invalidates config cache to force reload
|
|
662
|
+
*/
|
|
663
|
+
async removeServerFromRegistry(name, options = {}) {
|
|
664
|
+
const { RegistryManager } = await import('./registry.js');
|
|
665
|
+
const registryPath = this.getRegistryPath();
|
|
666
|
+
const registry = new RegistryManager(registryPath);
|
|
667
|
+
await registry.removeServer(name, options);
|
|
668
|
+
// Invalidate cache so next load picks up the removal
|
|
669
|
+
this.invalidateCache();
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Create config from object (useful for testing)
|
|
673
|
+
*/
|
|
674
|
+
static fromObject(obj) {
|
|
675
|
+
const loader = new ConfigLoader();
|
|
676
|
+
loader.setConfig(obj);
|
|
677
|
+
return loader;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Create and load config from file path
|
|
681
|
+
*/
|
|
682
|
+
static async loadFrom(filePath) {
|
|
683
|
+
const loader = new ConfigLoader(filePath);
|
|
684
|
+
await loader.load();
|
|
685
|
+
return loader;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Create and load config from file path synchronously
|
|
689
|
+
*/
|
|
690
|
+
static loadFromSync(filePath) {
|
|
691
|
+
const loader = new ConfigLoader(filePath);
|
|
692
|
+
loader.loadSync();
|
|
693
|
+
return loader;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Load from environment variables (fallback mechanism)
|
|
697
|
+
* Uses METALINK_* prefixed env vars
|
|
698
|
+
*/
|
|
699
|
+
static fromEnv() {
|
|
700
|
+
const config = {
|
|
701
|
+
version: '1.0.0',
|
|
702
|
+
daemon: {
|
|
703
|
+
port: parseInt(process.env.METALINK_PORT || '3000', 10),
|
|
704
|
+
host: process.env.METALINK_HOST || '0.0.0.0',
|
|
705
|
+
logLevel: process.env.METALINK_LOG_LEVEL || 'info',
|
|
706
|
+
auth: { enabled: false, type: 'bearer' },
|
|
707
|
+
rateLimit: { enabled: true, windowMs: 60000, max: 500, keyGenerator: 'ip' },
|
|
708
|
+
},
|
|
709
|
+
servers: [],
|
|
710
|
+
plugins: [],
|
|
711
|
+
profiles: [],
|
|
712
|
+
environment: {},
|
|
713
|
+
secrets: { provider: 'env', keyringService: 'metalink' },
|
|
714
|
+
schemasCacheTTL: parseInt(process.env.METALINK_SCHEMAS_CACHE_TTL || '300000', 10), // Default 5 minutes
|
|
715
|
+
schemasCachePath: process.env.METALINK_SCHEMAS_CACHE_PATH || '~/.config/metalink/schemas', // v1.3.62: Global cache
|
|
716
|
+
maxSchemaCacheEntries: parseInt(process.env.METALINK_MAX_SCHEMA_CACHE_ENTRIES || '100', 10), // Default 100 entries
|
|
717
|
+
schemasBackgroundRefresh: process.env.METALINK_SCHEMAS_BACKGROUND_REFRESH !== 'false', // Default true
|
|
718
|
+
backgroundSchemaPopulation: process.env.METALINK_BACKGROUND_SCHEMA_POPULATION === 'true', // Default false (v1.3.62)
|
|
719
|
+
dynamicToolExposure: process.env.METALINK_DYNAMIC_TOOL_EXPOSURE === 'true', // Default false (v1.3.51 Speakeasy)
|
|
720
|
+
base_servers_auto_start: process.env.METALINK_BASE_SERVERS_AUTO_START !== 'false', // Default true (v1.3.57)
|
|
721
|
+
base_servers_auto_expose_tools: process.env.METALINK_BASE_SERVERS_AUTO_EXPOSE_TOOLS === 'true', // Default false (v1.3.57)
|
|
722
|
+
timeouts: {
|
|
723
|
+
serverInit: parseInt(process.env.METALINK_TIMEOUT_INIT || '10000', 10),
|
|
724
|
+
toolFetch: parseInt(process.env.METALINK_TIMEOUT_TOOL_FETCH || '60000', 10),
|
|
725
|
+
toolExecution: parseInt(process.env.METALINK_TIMEOUT_TOOL_EXECUTION || '300000', 10),
|
|
726
|
+
gracefulShutdown: parseInt(process.env.METALINK_TIMEOUT_SHUTDOWN || '5000', 10),
|
|
727
|
+
},
|
|
728
|
+
circuitBreaker: {
|
|
729
|
+
failureThreshold: parseInt(process.env.METALINK_CIRCUIT_BREAKER_FAILURE_THRESHOLD || '3', 10),
|
|
730
|
+
resetTimeout: parseInt(process.env.METALINK_CIRCUIT_BREAKER_RESET_TIMEOUT || '30000', 10),
|
|
731
|
+
halfOpenSuccessThreshold: parseInt(process.env.METALINK_CIRCUIT_BREAKER_HALF_OPEN_SUCCESS_THRESHOLD || '1', 10),
|
|
732
|
+
},
|
|
733
|
+
autoRestart: {
|
|
734
|
+
enabled: process.env.METALINK_AUTO_RESTART_ENABLED !== 'false',
|
|
735
|
+
maxRetries: parseInt(process.env.METALINK_AUTO_RESTART_MAX_RETRIES || '3', 10),
|
|
736
|
+
baseDelay: parseInt(process.env.METALINK_AUTO_RESTART_BASE_DELAY || '5000', 10),
|
|
737
|
+
maxDelay: parseInt(process.env.METALINK_AUTO_RESTART_MAX_DELAY || '300000', 10),
|
|
738
|
+
backoffMultiplier: parseFloat(process.env.METALINK_AUTO_RESTART_BACKOFF_MULTIPLIER || '2'),
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
return validateConfig(config);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Global config instance (singleton pattern)
|
|
746
|
+
*/
|
|
747
|
+
let globalConfigLoader = null;
|
|
748
|
+
/**
|
|
749
|
+
* Initialize global config
|
|
750
|
+
*/
|
|
751
|
+
export async function initializeConfig(configPath) {
|
|
752
|
+
globalConfigLoader = new ConfigLoader(configPath);
|
|
753
|
+
return globalConfigLoader.load();
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Initialize config synchronously
|
|
757
|
+
*/
|
|
758
|
+
export function initializeConfigSync(configPath) {
|
|
759
|
+
globalConfigLoader = new ConfigLoader(configPath);
|
|
760
|
+
return globalConfigLoader.loadSync();
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Get global config instance
|
|
764
|
+
*/
|
|
765
|
+
export function getConfigLoader() {
|
|
766
|
+
if (!globalConfigLoader) {
|
|
767
|
+
throw new Error('Config not initialized. Call initializeConfig() first.');
|
|
768
|
+
}
|
|
769
|
+
return globalConfigLoader;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Get global config
|
|
773
|
+
*/
|
|
774
|
+
export function getConfig() {
|
|
775
|
+
return getConfigLoader().getConfig();
|
|
776
|
+
}
|
|
777
|
+
//# sourceMappingURL=loader.js.map
|