@jonathanleelx/meddle 0.0.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/CONFIG_STRUCTURE.md +315 -0
- package/LICENSE +21 -0
- package/README.md +298 -0
- package/bin/commands/mock/add.js +92 -0
- package/bin/commands/mock/delete.js +58 -0
- package/bin/commands/mock/index.js +50 -0
- package/bin/commands/mock/list.js +68 -0
- package/bin/commands/mock/toggle.js +64 -0
- package/bin/commands/mock/update.js +94 -0
- package/bin/commands/route/active.js +141 -0
- package/bin/commands/route/add.js +127 -0
- package/bin/commands/route/create.js +77 -0
- package/bin/commands/route/delete.js +120 -0
- package/bin/commands/route/index.js +59 -0
- package/bin/commands/route/list.js +67 -0
- package/bin/commands/route/preview.js +159 -0
- package/bin/commands/route/show.js +111 -0
- package/bin/commands/route/update.js +126 -0
- package/bin/commands/session/create.js +166 -0
- package/bin/commands/session/delete.js +69 -0
- package/bin/commands/session/index.js +61 -0
- package/bin/commands/session/list.js +42 -0
- package/bin/commands/session/prune.js +31 -0
- package/bin/commands/start.js +43 -0
- package/bin/commands/status.js +115 -0
- package/bin/commands/supervise.js +106 -0
- package/bin/commands/url.js +30 -0
- package/bin/doctor.js +356 -0
- package/bin/index +130 -0
- package/bin/lib/api-client.js +72 -0
- package/bin/lib/file-access.js +337 -0
- package/bin/lib/meddle-home.js +20 -0
- package/bin/lib/output.js +146 -0
- package/bin/lib/parsers.js +127 -0
- package/bin/lib/proxy-detect.js +105 -0
- package/bin/lib/route-preview.js +180 -0
- package/bin/lib/session-args.js +54 -0
- package/bin/lib/sessions.js +213 -0
- package/dist/cert.d.ts +10 -0
- package/dist/cert.d.ts.map +1 -0
- package/dist/cert.js +184 -0
- package/dist/cert.js.map +1 -0
- package/dist/core/application-identity.d.ts +55 -0
- package/dist/core/application-identity.d.ts.map +1 -0
- package/dist/core/application-identity.js +321 -0
- package/dist/core/application-identity.js.map +1 -0
- package/dist/core/body-utils.d.ts +2 -0
- package/dist/core/body-utils.d.ts.map +1 -0
- package/dist/core/body-utils.js +21 -0
- package/dist/core/body-utils.js.map +1 -0
- package/dist/core/browser.d.ts +2 -0
- package/dist/core/browser.d.ts.map +1 -0
- package/dist/core/browser.js +92 -0
- package/dist/core/browser.js.map +1 -0
- package/dist/core/client-identity.d.ts +27 -0
- package/dist/core/client-identity.d.ts.map +1 -0
- package/dist/core/client-identity.js +136 -0
- package/dist/core/client-identity.js.map +1 -0
- package/dist/core/config-diagnostics.d.ts +14 -0
- package/dist/core/config-diagnostics.d.ts.map +1 -0
- package/dist/core/config-diagnostics.js +137 -0
- package/dist/core/config-diagnostics.js.map +1 -0
- package/dist/core/connect-tunnel.d.ts +5 -0
- package/dist/core/connect-tunnel.d.ts.map +1 -0
- package/dist/core/connect-tunnel.js +45 -0
- package/dist/core/connect-tunnel.js.map +1 -0
- package/dist/core/content-encoding.d.ts +2 -0
- package/dist/core/content-encoding.d.ts.map +1 -0
- package/dist/core/content-encoding.js +77 -0
- package/dist/core/content-encoding.js.map +1 -0
- package/dist/core/custom-plugin-loader.d.ts +18 -0
- package/dist/core/custom-plugin-loader.d.ts.map +1 -0
- package/dist/core/custom-plugin-loader.js +178 -0
- package/dist/core/custom-plugin-loader.js.map +1 -0
- package/dist/core/h2-pool.d.ts +4 -0
- package/dist/core/h2-pool.d.ts.map +1 -0
- package/dist/core/h2-pool.js +180 -0
- package/dist/core/h2-pool.js.map +1 -0
- package/dist/core/inspection-dispatch.d.ts +5 -0
- package/dist/core/inspection-dispatch.d.ts.map +1 -0
- package/dist/core/inspection-dispatch.js +146 -0
- package/dist/core/inspection-dispatch.js.map +1 -0
- package/dist/core/log-rate-limit.d.ts +34 -0
- package/dist/core/log-rate-limit.d.ts.map +1 -0
- package/dist/core/log-rate-limit.js +52 -0
- package/dist/core/log-rate-limit.js.map +1 -0
- package/dist/core/map-local.d.ts +4 -0
- package/dist/core/map-local.d.ts.map +1 -0
- package/dist/core/map-local.js +195 -0
- package/dist/core/map-local.js.map +1 -0
- package/dist/core/meddle-home.d.ts +13 -0
- package/dist/core/meddle-home.d.ts.map +1 -0
- package/dist/core/meddle-home.js +56 -0
- package/dist/core/meddle-home.js.map +1 -0
- package/dist/core/mock-gate.d.ts +3 -0
- package/dist/core/mock-gate.d.ts.map +1 -0
- package/dist/core/mock-gate.js +20 -0
- package/dist/core/mock-gate.js.map +1 -0
- package/dist/core/mock-handler.d.ts +5 -0
- package/dist/core/mock-handler.d.ts.map +1 -0
- package/dist/core/mock-handler.js +339 -0
- package/dist/core/mock-handler.js.map +1 -0
- package/dist/core/on-mode-gate.d.ts +4 -0
- package/dist/core/on-mode-gate.d.ts.map +1 -0
- package/dist/core/on-mode-gate.js +64 -0
- package/dist/core/on-mode-gate.js.map +1 -0
- package/dist/core/pipeline-gate.d.ts +3 -0
- package/dist/core/pipeline-gate.d.ts.map +1 -0
- package/dist/core/pipeline-gate.js +34 -0
- package/dist/core/pipeline-gate.js.map +1 -0
- package/dist/core/pipeline.d.ts +6 -0
- package/dist/core/pipeline.d.ts.map +1 -0
- package/dist/core/pipeline.js +228 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/plugin-bootstrap-runner.d.ts +6 -0
- package/dist/core/plugin-bootstrap-runner.d.ts.map +1 -0
- package/dist/core/plugin-bootstrap-runner.js +148 -0
- package/dist/core/plugin-bootstrap-runner.js.map +1 -0
- package/dist/core/plugin-bootstrap.d.ts +3 -0
- package/dist/core/plugin-bootstrap.d.ts.map +1 -0
- package/dist/core/plugin-bootstrap.js +15 -0
- package/dist/core/plugin-bootstrap.js.map +1 -0
- package/dist/core/plugin-compiler.d.ts +37 -0
- package/dist/core/plugin-compiler.d.ts.map +1 -0
- package/dist/core/plugin-compiler.js +172 -0
- package/dist/core/plugin-compiler.js.map +1 -0
- package/dist/core/plugin-context-factory.d.ts +29 -0
- package/dist/core/plugin-context-factory.d.ts.map +1 -0
- package/dist/core/plugin-context-factory.js +206 -0
- package/dist/core/plugin-context-factory.js.map +1 -0
- package/dist/core/plugin-generator.d.ts +47 -0
- package/dist/core/plugin-generator.d.ts.map +1 -0
- package/dist/core/plugin-generator.js +697 -0
- package/dist/core/plugin-generator.js.map +1 -0
- package/dist/core/plugin-health.d.ts +3 -0
- package/dist/core/plugin-health.d.ts.map +1 -0
- package/dist/core/plugin-health.js +54 -0
- package/dist/core/plugin-health.js.map +1 -0
- package/dist/core/plugin-intercept.d.ts +17 -0
- package/dist/core/plugin-intercept.d.ts.map +1 -0
- package/dist/core/plugin-intercept.js +133 -0
- package/dist/core/plugin-intercept.js.map +1 -0
- package/dist/core/plugin-runtime.d.ts +33 -0
- package/dist/core/plugin-runtime.d.ts.map +1 -0
- package/dist/core/plugin-runtime.js +254 -0
- package/dist/core/plugin-runtime.js.map +1 -0
- package/dist/core/proxy-context.d.ts +3 -0
- package/dist/core/proxy-context.d.ts.map +1 -0
- package/dist/core/proxy-context.js +129 -0
- package/dist/core/proxy-context.js.map +1 -0
- package/dist/core/proxy-record.d.ts +3 -0
- package/dist/core/proxy-record.d.ts.map +1 -0
- package/dist/core/proxy-record.js +32 -0
- package/dist/core/proxy-record.js.map +1 -0
- package/dist/core/refactor-config.d.ts +6 -0
- package/dist/core/refactor-config.d.ts.map +1 -0
- package/dist/core/refactor-config.js +45 -0
- package/dist/core/refactor-config.js.map +1 -0
- package/dist/core/refactor-status.d.ts +3 -0
- package/dist/core/refactor-status.d.ts.map +1 -0
- package/dist/core/refactor-status.js +19 -0
- package/dist/core/refactor-status.js.map +1 -0
- package/dist/core/remote-access.d.ts +40 -0
- package/dist/core/remote-access.d.ts.map +1 -0
- package/dist/core/remote-access.js +281 -0
- package/dist/core/remote-access.js.map +1 -0
- package/dist/core/route-decision.d.ts +3 -0
- package/dist/core/route-decision.d.ts.map +1 -0
- package/dist/core/route-decision.js +20 -0
- package/dist/core/route-decision.js.map +1 -0
- package/dist/core/route-loader.d.ts +14 -0
- package/dist/core/route-loader.d.ts.map +1 -0
- package/dist/core/route-loader.js +94 -0
- package/dist/core/route-loader.js.map +1 -0
- package/dist/core/route-preview.d.ts +15 -0
- package/dist/core/route-preview.d.ts.map +1 -0
- package/dist/core/route-preview.js +91 -0
- package/dist/core/route-preview.js.map +1 -0
- package/dist/core/runtime-health.d.ts +92 -0
- package/dist/core/runtime-health.d.ts.map +1 -0
- package/dist/core/runtime-health.js +205 -0
- package/dist/core/runtime-health.js.map +1 -0
- package/dist/core/sessions.d.ts +19 -0
- package/dist/core/sessions.d.ts.map +1 -0
- package/dist/core/sessions.js +93 -0
- package/dist/core/sessions.js.map +1 -0
- package/dist/core/shadow-compare.d.ts +8 -0
- package/dist/core/shadow-compare.d.ts.map +1 -0
- package/dist/core/shadow-compare.js +83 -0
- package/dist/core/shadow-compare.js.map +1 -0
- package/dist/core/shadow-readiness.d.ts +4 -0
- package/dist/core/shadow-readiness.d.ts.map +1 -0
- package/dist/core/shadow-readiness.js +118 -0
- package/dist/core/shadow-readiness.js.map +1 -0
- package/dist/core/short-response.d.ts +5 -0
- package/dist/core/short-response.d.ts.map +1 -0
- package/dist/core/short-response.js +18 -0
- package/dist/core/short-response.js.map +1 -0
- package/dist/core/static-server.d.ts +9 -0
- package/dist/core/static-server.d.ts.map +1 -0
- package/dist/core/static-server.js +136 -0
- package/dist/core/static-server.js.map +1 -0
- package/dist/core/types.d.ts +590 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -0
- package/dist/helpers.d.ts +36 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +290 -0
- package/dist/helpers.js.map +1 -0
- package/dist/plugins/builtin/index.d.ts +3 -0
- package/dist/plugins/builtin/index.d.ts.map +1 -0
- package/dist/plugins/builtin/index.js +23 -0
- package/dist/plugins/builtin/index.js.map +1 -0
- package/dist/plugins/builtin/logger-plugin.d.ts +8 -0
- package/dist/plugins/builtin/logger-plugin.d.ts.map +1 -0
- package/dist/plugins/builtin/logger-plugin.js +121 -0
- package/dist/plugins/builtin/logger-plugin.js.map +1 -0
- package/dist/plugins/builtin/mock-plugin.d.ts +3 -0
- package/dist/plugins/builtin/mock-plugin.d.ts.map +1 -0
- package/dist/plugins/builtin/mock-plugin.js +99 -0
- package/dist/plugins/builtin/mock-plugin.js.map +1 -0
- package/dist/plugins/builtin/router-plugin.d.ts +3 -0
- package/dist/plugins/builtin/router-plugin.d.ts.map +1 -0
- package/dist/plugins/builtin/router-plugin.js +36 -0
- package/dist/plugins/builtin/router-plugin.js.map +1 -0
- package/dist/server/agent/model.d.ts +30 -0
- package/dist/server/agent/model.d.ts.map +1 -0
- package/dist/server/agent/model.js +143 -0
- package/dist/server/agent/model.js.map +1 -0
- package/dist/server/agent/routes.d.ts +4 -0
- package/dist/server/agent/routes.d.ts.map +1 -0
- package/dist/server/agent/routes.js +188 -0
- package/dist/server/agent/routes.js.map +1 -0
- package/dist/server/agent/runtime.d.ts +12 -0
- package/dist/server/agent/runtime.d.ts.map +1 -0
- package/dist/server/agent/runtime.js +208 -0
- package/dist/server/agent/runtime.js.map +1 -0
- package/dist/server/agent/tools.d.ts +13 -0
- package/dist/server/agent/tools.d.ts.map +1 -0
- package/dist/server/agent/tools.js +1201 -0
- package/dist/server/agent/tools.js.map +1 -0
- package/dist/server/agent/types.d.ts +56 -0
- package/dist/server/agent/types.d.ts.map +1 -0
- package/dist/server/agent/types.js +3 -0
- package/dist/server/agent/types.js.map +1 -0
- package/dist/server/config.d.ts +9 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +124 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/health.d.ts +4 -0
- package/dist/server/health.d.ts.map +1 -0
- package/dist/server/health.js +22 -0
- package/dist/server/health.js.map +1 -0
- package/dist/server/index.d.ts +239 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +44 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/logs.d.ts +4 -0
- package/dist/server/logs.d.ts.map +1 -0
- package/dist/server/logs.js +40 -0
- package/dist/server/logs.js.map +1 -0
- package/dist/server/mocks.d.ts +9 -0
- package/dist/server/mocks.d.ts.map +1 -0
- package/dist/server/mocks.js +103 -0
- package/dist/server/mocks.js.map +1 -0
- package/dist/server/pipeline.d.ts +16 -0
- package/dist/server/pipeline.d.ts.map +1 -0
- package/dist/server/pipeline.js +159 -0
- package/dist/server/pipeline.js.map +1 -0
- package/dist/server/plugins.d.ts +32 -0
- package/dist/server/plugins.d.ts.map +1 -0
- package/dist/server/plugins.js +795 -0
- package/dist/server/plugins.js.map +1 -0
- package/dist/server/refactor.d.ts +4 -0
- package/dist/server/refactor.d.ts.map +1 -0
- package/dist/server/refactor.js +86 -0
- package/dist/server/refactor.js.map +1 -0
- package/dist/server/rule-files.d.ts +34 -0
- package/dist/server/rule-files.d.ts.map +1 -0
- package/dist/server/rule-files.js +389 -0
- package/dist/server/rule-files.js.map +1 -0
- package/dist/server/rules.d.ts +4 -0
- package/dist/server/rules.d.ts.map +1 -0
- package/dist/server/rules.js +40 -0
- package/dist/server/rules.js.map +1 -0
- package/dist/server/sessions.d.ts +4 -0
- package/dist/server/sessions.d.ts.map +1 -0
- package/dist/server/sessions.js +20 -0
- package/dist/server/sessions.js.map +1 -0
- package/index.js +950 -0
- package/mcp-server.js +794 -0
- package/package.json +92 -0
- package/plugins/builtin/index.ts +25 -0
- package/plugins/builtin/logger-plugin.ts +126 -0
- package/plugins/builtin/mock-plugin.ts +112 -0
- package/plugins/builtin/router-plugin.ts +35 -0
- package/web/README.md +111 -0
- package/web/dist/assets/ai-config-store-DQXVJVn_.js +1 -0
- package/web/dist/assets/alert-COF-DIAE.js +1 -0
- package/web/dist/assets/arrow-right-B4Mrlj2-.js +1 -0
- package/web/dist/assets/babel-DsprIpXZ.js +15 -0
- package/web/dist/assets/body-diff-view-D7dCWajG.js +6 -0
- package/web/dist/assets/button-BTToMaBW.js +1 -0
- package/web/dist/assets/checkbox-DIBarrJx.js +1 -0
- package/web/dist/assets/chevron-down-CPngAx4M.js +1 -0
- package/web/dist/assets/chevron-right-V1DGIDbL.js +1 -0
- package/web/dist/assets/circle-alert-Q8cZk9Rv.js +1 -0
- package/web/dist/assets/circle-x-C9USji_R.js +1 -0
- package/web/dist/assets/code-5_eJ_XpJ.js +1 -0
- package/web/dist/assets/code-fixer-fQtWZ5He.js +20 -0
- package/web/dist/assets/copy-BYjq1hdN.js +1 -0
- package/web/dist/assets/detail-panel-BX_hxeI0.js +3 -0
- package/web/dist/assets/dist-B8XUKgXe.js +1 -0
- package/web/dist/assets/dist-Bb9TeEub.js +1 -0
- package/web/dist/assets/dist-C5nMErq8.js +1 -0
- package/web/dist/assets/dist-C6eaYCUo.js +1 -0
- package/web/dist/assets/dist-Cn0y0IRn.js +1 -0
- package/web/dist/assets/dist-CxdY_A67.js +5 -0
- package/web/dist/assets/dist-D5Dj17P8.js +1 -0
- package/web/dist/assets/dist-DZJ9SMlJ.js +1 -0
- package/web/dist/assets/es2015-CfXu88Y7.js +41 -0
- package/web/dist/assets/estree-BP7k0NIt.js +44 -0
- package/web/dist/assets/file-system-access-CtcvBfj4.js +1 -0
- package/web/dist/assets/global-panel-shell-ChdO0dWd.js +6 -0
- package/web/dist/assets/health-panel-CQeCQ7gr.js +1 -0
- package/web/dist/assets/html-6FIWbqxX.js +25 -0
- package/web/dist/assets/index-CjqaTnE_.css +2 -0
- package/web/dist/assets/index-L7uWxTnp.js +18 -0
- package/web/dist/assets/mobile-proxy-panel-Dj1iLami.js +8 -0
- package/web/dist/assets/mock-config-BTTCw7RK.js +1 -0
- package/web/dist/assets/mock-editor-panel-C-aVfFme.js +1 -0
- package/web/dist/assets/monaco-editor-B3brLg8j.js +11 -0
- package/web/dist/assets/pencil-line-Cqe41E4h.js +1 -0
- package/web/dist/assets/plugin-code-editor-CPjCbJ9g.js +2 -0
- package/web/dist/assets/plugin-config-BzmCkvz3.js +1 -0
- package/web/dist/assets/plugin-generator-BnEafDF9.js +2 -0
- package/web/dist/assets/plugin-test-dialog-BWoWsWV4.js +7 -0
- package/web/dist/assets/plus-tVxSydJ8.js +1 -0
- package/web/dist/assets/postcss-Dnf30fQU.js +61 -0
- package/web/dist/assets/refresh-cw-CG3HaiQT.js +1 -0
- package/web/dist/assets/rotate-ccw-CAYM6Vv0.js +1 -0
- package/web/dist/assets/route-preview-BX7sfY0O.js +1 -0
- package/web/dist/assets/rule-ai-assistant-panel-WBiOOUw5.js +47 -0
- package/web/dist/assets/rule-config-CHpVij2M.css +1 -0
- package/web/dist/assets/rule-config-fTIZVwg1.js +15 -0
- package/web/dist/assets/select-CkFOB61l.js +1 -0
- package/web/dist/assets/server-Cpj4KNwN.js +1 -0
- package/web/dist/assets/settings-panel-COCYiIUg.js +1 -0
- package/web/dist/assets/shield-check-OqUmVLQt.js +1 -0
- package/web/dist/assets/smartphone-DKeDuJTU.js +1 -0
- package/web/dist/assets/standalone-CjxtLKw-.js +29 -0
- package/web/dist/assets/switch-C-Xk56TK.js +1 -0
- package/web/dist/assets/syntax-highlight-DVhPdkuV.js +5 -0
- package/web/dist/assets/table-CT97lAYq.js +1 -0
- package/web/dist/assets/textarea-C6NooVK1.js +1 -0
- package/web/dist/assets/wand-sparkles-wkul5vje.js +1 -0
- package/web/dist/assets/wifi-D9M35Ajj.js +1 -0
- package/web/dist/assets/zap-Djo4cgux.js +1 -0
- package/web/dist/index.html +24 -0
- package/web/dist/vite.svg +1 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle route update - Update a rule in a route file
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const output = require('../../lib/output')
|
|
6
|
+
const { isProxyRunning } = require('../../lib/proxy-detect')
|
|
7
|
+
const { apiGet, apiPut } = require('../../lib/api-client')
|
|
8
|
+
const { getRuleFileContent, saveRuleFileContent, parseEprc, ruleMapToEprcText } = require('../../lib/file-access')
|
|
9
|
+
|
|
10
|
+
// Parse arguments (skip 'route' and 'update' from argv)
|
|
11
|
+
const args = process.argv.slice(4)
|
|
12
|
+
const jsonFlag = args.includes('--json')
|
|
13
|
+
output.setJsonMode(jsonFlag)
|
|
14
|
+
|
|
15
|
+
// Get positional arguments: file, pattern, newTarget
|
|
16
|
+
const positionalArgs = args.filter(a => !a.startsWith('--'))
|
|
17
|
+
const fileName = positionalArgs[0]
|
|
18
|
+
const pattern = positionalArgs[1]
|
|
19
|
+
const newTarget = positionalArgs[2]
|
|
20
|
+
|
|
21
|
+
async function run() {
|
|
22
|
+
if (!fileName) {
|
|
23
|
+
output.error('Missing required argument: <file>')
|
|
24
|
+
process.exit(1)
|
|
25
|
+
}
|
|
26
|
+
if (!pattern) {
|
|
27
|
+
output.error('Missing required argument: <pattern>')
|
|
28
|
+
process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
if (!newTarget) {
|
|
31
|
+
output.error('Missing required argument: <target>')
|
|
32
|
+
process.exit(1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const running = await isProxyRunning()
|
|
36
|
+
|
|
37
|
+
const pat = pattern.trim()
|
|
38
|
+
const tgt = newTarget.trim()
|
|
39
|
+
|
|
40
|
+
// Handle [marker] syntax for lookup
|
|
41
|
+
const bm = pat.match(/\[([^\]]+)\]/)
|
|
42
|
+
const internalKey = bm ? pat.replace(bm[0], bm[1]) : pat
|
|
43
|
+
|
|
44
|
+
if (running) {
|
|
45
|
+
try {
|
|
46
|
+
let content = await apiGet(`/api/rule-files/${encodeURIComponent(fileName)}/content`)
|
|
47
|
+
const text = typeof content === 'string' ? content : String(content)
|
|
48
|
+
const ruleMap = parseEprc(text)
|
|
49
|
+
|
|
50
|
+
if (!Object.prototype.hasOwnProperty.call(ruleMap, internalKey)) {
|
|
51
|
+
output.error(`Pattern not found: ${pat}`)
|
|
52
|
+
process.exit(1)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
ruleMap[internalKey] = bm ? tgt + bm[0] : tgt
|
|
56
|
+
const newContent = ruleMapToEprcText(ruleMap)
|
|
57
|
+
await apiPut(`/api/rule-files/${encodeURIComponent(fileName)}/content`, { content: newContent })
|
|
58
|
+
|
|
59
|
+
if (jsonFlag) {
|
|
60
|
+
output.jsonRaw({ success: true, file: fileName, pattern: pat, newTarget: tgt })
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
output.header('Route Rule Updated')
|
|
65
|
+
output.success(`${pat} -> ${tgt}`)
|
|
66
|
+
output.kv('File', fileName)
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// Fallback to file mode
|
|
69
|
+
const content = getRuleFileContent(fileName)
|
|
70
|
+
if (!content) {
|
|
71
|
+
output.error(`Route file not found: ${fileName}`)
|
|
72
|
+
process.exit(1)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const ruleMap = parseEprc(content)
|
|
76
|
+
if (!Object.prototype.hasOwnProperty.call(ruleMap, internalKey)) {
|
|
77
|
+
output.error(`Pattern not found: ${pat}`)
|
|
78
|
+
process.exit(1)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ruleMap[internalKey] = bm ? tgt + bm[0] : tgt
|
|
82
|
+
const newContent = ruleMapToEprcText(ruleMap)
|
|
83
|
+
saveRuleFileContent(fileName, newContent)
|
|
84
|
+
|
|
85
|
+
if (jsonFlag) {
|
|
86
|
+
output.jsonRaw({ success: true, file: fileName, pattern: pat, newTarget: tgt })
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
output.header('Route Rule Updated')
|
|
91
|
+
output.success(`${pat} -> ${tgt}`)
|
|
92
|
+
output.kv('File', fileName)
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
// File mode
|
|
96
|
+
const content = getRuleFileContent(fileName)
|
|
97
|
+
if (!content) {
|
|
98
|
+
output.error(`Route file not found: ${fileName}`)
|
|
99
|
+
process.exit(1)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const ruleMap = parseEprc(content)
|
|
103
|
+
if (!Object.prototype.hasOwnProperty.call(ruleMap, internalKey)) {
|
|
104
|
+
output.error(`Pattern not found: ${pat}`)
|
|
105
|
+
process.exit(1)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
ruleMap[internalKey] = bm ? tgt + bm[0] : tgt
|
|
109
|
+
const newContent = ruleMapToEprcText(ruleMap)
|
|
110
|
+
saveRuleFileContent(fileName, newContent)
|
|
111
|
+
|
|
112
|
+
if (jsonFlag) {
|
|
113
|
+
output.jsonRaw({ success: true, file: fileName, pattern: pat, newTarget: tgt })
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
output.header('Route Rule Updated')
|
|
118
|
+
output.success(`${pat} -> ${tgt}`)
|
|
119
|
+
output.kv('File', fileName)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
run().catch(e => {
|
|
124
|
+
output.error(e.message)
|
|
125
|
+
process.exit(1)
|
|
126
|
+
})
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle session create - Spawn a new isolated proxy session.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Generate session id (label-timestamp) and allocate a free port
|
|
6
|
+
* 2. Ensure the session's MEDDLE_HOME directory exists
|
|
7
|
+
* 3. Spawn `node index.js` with MEDDLE_HOME + PORT env
|
|
8
|
+
* 4. Wait for the child's HTTP API to become reachable
|
|
9
|
+
* 5. Register in sessions.json (pid, port, meddleHome)
|
|
10
|
+
* 6. Print session id + proxy URL
|
|
11
|
+
*
|
|
12
|
+
* The child is spawned detached with stdio inherited so the user sees
|
|
13
|
+
* proxy logs. The child listens for parent disconnect and self-terminates
|
|
14
|
+
* to avoid orphan processes.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs')
|
|
18
|
+
const path = require('path')
|
|
19
|
+
const { spawn } = require('child_process')
|
|
20
|
+
const chalk = require('chalk')
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
sessionsDir,
|
|
24
|
+
sessionDir,
|
|
25
|
+
allocatePort,
|
|
26
|
+
generateId,
|
|
27
|
+
createSession,
|
|
28
|
+
readRegistry,
|
|
29
|
+
writeRegistry,
|
|
30
|
+
} = require('../../lib/sessions')
|
|
31
|
+
const { resolveMeddleHome } = require('../../lib/meddle-home')
|
|
32
|
+
const { GLOBAL_DEFAULT_PORT } = require('../../lib/session-args')
|
|
33
|
+
|
|
34
|
+
function parseArgs(argv) {
|
|
35
|
+
const args = argv.slice(4) // after 'meddle' 'session' 'create'
|
|
36
|
+
const out = { name: null, port: null, json: false }
|
|
37
|
+
for (let i = 0; i < args.length; i++) {
|
|
38
|
+
const a = args[i]
|
|
39
|
+
if (a === '--name' && args[i + 1]) { out.name = args[i + 1]; i++; continue }
|
|
40
|
+
if (a.startsWith('--name=')) { out.name = a.slice('--name='.length); continue }
|
|
41
|
+
if (a === '--port' && args[i + 1]) { out.port = Number(args[i + 1]); i++; continue }
|
|
42
|
+
if (a.startsWith('--port=')) { out.port = Number(a.slice('--port='.length)); continue }
|
|
43
|
+
if (a === '--json') { out.json = true; continue }
|
|
44
|
+
}
|
|
45
|
+
return out
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function waitForProxyReady(port, timeoutMs = 8000) {
|
|
49
|
+
const start = Date.now()
|
|
50
|
+
const interval = 100
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const check = () => {
|
|
53
|
+
fetch(`http://127.0.0.1:${port}/api/mocks`, { method: 'GET', signal: AbortSignal.timeout(500) })
|
|
54
|
+
.then((r) => { if (r.ok) resolve(); else retry() })
|
|
55
|
+
.catch(() => retry())
|
|
56
|
+
}
|
|
57
|
+
function retry() {
|
|
58
|
+
if (Date.now() - start > timeoutMs) {
|
|
59
|
+
return reject(new Error(`proxy did not become ready on port ${port} within ${timeoutMs}ms`))
|
|
60
|
+
}
|
|
61
|
+
setTimeout(check, interval)
|
|
62
|
+
}
|
|
63
|
+
check()
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function main() {
|
|
68
|
+
const opts = parseArgs(process.argv)
|
|
69
|
+
|
|
70
|
+
// 1. id + port
|
|
71
|
+
const id = generateId(opts.name)
|
|
72
|
+
let port = opts.port
|
|
73
|
+
if (port) {
|
|
74
|
+
// user-specified port: validate range and freeness
|
|
75
|
+
if (port < 9000 || port > 9999) {
|
|
76
|
+
throw new Error(`--port must be in [9000, 9999] (got ${port}); default session uses ${GLOBAL_DEFAULT_PORT}`)
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
const registry = readRegistry()
|
|
80
|
+
port = allocatePort(registry)
|
|
81
|
+
if (!port) throw new Error('no free port in [9000, 9999]; run `meddle session prune` to clean orphaned records')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 2. MEDDLE_HOME dir
|
|
85
|
+
const meddleHome = sessionDir(id)
|
|
86
|
+
fs.mkdirSync(meddleHome, { recursive: true })
|
|
87
|
+
// subdirs that proxy expects to exist or create — let the child create them
|
|
88
|
+
// via its existing startup logic. We only ensure the root exists.
|
|
89
|
+
|
|
90
|
+
// 3. spawn child
|
|
91
|
+
const indexPath = path.join(__dirname, '..', '..', '..', 'index.js')
|
|
92
|
+
const childEnv = {
|
|
93
|
+
...process.env,
|
|
94
|
+
MEDDLE_HOME: meddleHome,
|
|
95
|
+
PORT: String(port),
|
|
96
|
+
MEDDLE_SESSION_ID: id,
|
|
97
|
+
// Suppress the CA trust prompt — sessions reuse the shared CA
|
|
98
|
+
// from ~/.meddle/ca which the user already trusted when setting up
|
|
99
|
+
// the default session. Also avoid interactive prompts blocking
|
|
100
|
+
// a non-interactive agent spawn.
|
|
101
|
+
MEDDLE_HEADLESS: '1',
|
|
102
|
+
// Disable MCP file discovery for non-default sessions; the MCP
|
|
103
|
+
// proxy-url file is only meaningful for the default session.
|
|
104
|
+
MEDDLE_MCP: '',
|
|
105
|
+
}
|
|
106
|
+
const child = spawn(process.execPath, [indexPath], {
|
|
107
|
+
cwd: process.cwd(),
|
|
108
|
+
env: childEnv,
|
|
109
|
+
stdio: 'inherit',
|
|
110
|
+
detached: false,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
if (!child.pid) {
|
|
114
|
+
throw new Error('failed to spawn proxy process')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Kill child if parent exits unexpectedly (orphan prevention).
|
|
118
|
+
child.on('exit', (code, signal) => {
|
|
119
|
+
if (code !== 0 && code !== null) {
|
|
120
|
+
console.error(chalk.yellow(`session ${id} exited early (code=${code} signal=${signal || ''})`))
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
process.on('exit', () => {
|
|
124
|
+
try { if (child.exitCode === null) child.kill('SIGTERM') } catch (_) {}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// 4. wait for HTTP ready
|
|
128
|
+
try {
|
|
129
|
+
await waitForProxyReady(port)
|
|
130
|
+
} catch (err) {
|
|
131
|
+
try { child.kill('SIGTERM') } catch (_) {}
|
|
132
|
+
throw err
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 5. register (optimistic: re-read, verify port not taken, write)
|
|
136
|
+
const freshRegistry = readRegistry()
|
|
137
|
+
const portConflict = Object.keys(freshRegistry).some((k) => freshRegistry[k].port === port)
|
|
138
|
+
if (portConflict && !opts.port) {
|
|
139
|
+
try { child.kill('SIGTERM') } catch (_) {}
|
|
140
|
+
throw new Error(`port ${port} was claimed by another session during startup; please retry`)
|
|
141
|
+
}
|
|
142
|
+
createSession({ id, label: opts.name || '', port, pid: child.pid, meddleHome })
|
|
143
|
+
|
|
144
|
+
// 6. report
|
|
145
|
+
if (opts.json) {
|
|
146
|
+
console.log(JSON.stringify({
|
|
147
|
+
id,
|
|
148
|
+
port,
|
|
149
|
+
pid: child.pid,
|
|
150
|
+
meddleHome,
|
|
151
|
+
proxyUrl: `http://127.0.0.1:${port}`,
|
|
152
|
+
}))
|
|
153
|
+
} else {
|
|
154
|
+
console.log(chalk.green('Created session:'), chalk.cyan(id))
|
|
155
|
+
console.log(` ${chalk.gray('Proxy URL:')} http://127.0.0.1:${port}`)
|
|
156
|
+
console.log(` ${chalk.gray('PID:')} ${child.pid}`)
|
|
157
|
+
console.log(` ${chalk.gray('MEDDLE_HOME:')} ${meddleHome}`)
|
|
158
|
+
console.log(chalk.gray('\nUse this session with:'))
|
|
159
|
+
console.log(` meddle --session ${id} route list`)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
main().catch((err) => {
|
|
164
|
+
console.error(chalk.red('error:'), err.message)
|
|
165
|
+
process.exit(1)
|
|
166
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle session delete <id> - Stop a session and remove from registry.
|
|
3
|
+
*
|
|
4
|
+
* --clean also deletes the session's MEDDLE_HOME data directory.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs')
|
|
8
|
+
const path = require('path')
|
|
9
|
+
const chalk = require('chalk')
|
|
10
|
+
const { getSession, deleteSession, isPidAlive } = require('../../lib/sessions')
|
|
11
|
+
|
|
12
|
+
const args = process.argv.slice(4)
|
|
13
|
+
const id = args.find((a) => !a.startsWith('--'))
|
|
14
|
+
const cleanFlag = args.includes('--clean')
|
|
15
|
+
const jsonFlag = args.includes('--json')
|
|
16
|
+
|
|
17
|
+
if (!id) {
|
|
18
|
+
console.error(chalk.red('error:'), 'session id required')
|
|
19
|
+
console.error(chalk.gray('usage: meddle session delete <id> [--clean]'))
|
|
20
|
+
process.exit(1)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const record = getSession(id)
|
|
24
|
+
if (!record) {
|
|
25
|
+
console.error(chalk.red('error:'), `session not found: ${id}`)
|
|
26
|
+
process.exit(1)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 1. kill process if alive
|
|
30
|
+
let killed = false
|
|
31
|
+
if (isPidAlive(record.pid)) {
|
|
32
|
+
try {
|
|
33
|
+
process.kill(record.pid, 'SIGTERM')
|
|
34
|
+
killed = true
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error(chalk.yellow('warning:'), `could not kill pid ${record.pid}: ${err.message}`)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. remove from registry
|
|
41
|
+
deleteSession(id)
|
|
42
|
+
|
|
43
|
+
// 3. optionally clean data dir
|
|
44
|
+
let cleaned = false
|
|
45
|
+
if (cleanFlag) {
|
|
46
|
+
try {
|
|
47
|
+
if (fs.existsSync(record.meddleHome)) {
|
|
48
|
+
// only delete if inside ~/.meddle/sessions/ — safety guard
|
|
49
|
+
const sessionsRoot = path.dirname(record.meddleHome)
|
|
50
|
+
const expectedRoot = path.join(require('../../lib/meddle-home').resolveMeddleHome(), 'sessions')
|
|
51
|
+
if (path.resolve(sessionsRoot) === path.resolve(expectedRoot)) {
|
|
52
|
+
fs.rmSync(record.meddleHome, { recursive: true, force: true })
|
|
53
|
+
cleaned = true
|
|
54
|
+
} else {
|
|
55
|
+
console.error(chalk.yellow('warning:'), `refusing to delete ${record.meddleHome} (outside sessions dir)`)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error(chalk.yellow('warning:'), `could not clean data dir: ${err.message}`)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (jsonFlag) {
|
|
64
|
+
console.log(JSON.stringify({ id, killed, cleaned, meddleHome: record.meddleHome }))
|
|
65
|
+
} else {
|
|
66
|
+
console.log(chalk.green('Deleted session:'), chalk.cyan(id))
|
|
67
|
+
if (killed) console.log(chalk.gray(` stopped pid ${record.pid}`))
|
|
68
|
+
if (cleaned) console.log(chalk.gray(` removed ${record.meddleHome}`))
|
|
69
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle session - Multi-instance session management (preview)
|
|
3
|
+
*
|
|
4
|
+
* Each session is an independent `meddle` process with its own MEDDLE_HOME
|
|
5
|
+
* (route-rules, mocks, plugins, settings) and port. The CA store
|
|
6
|
+
* (~/.meddle/ca) is shared across all sessions so HTTPS interception
|
|
7
|
+
* works without re-installing certificates per session.
|
|
8
|
+
*
|
|
9
|
+
* See docs/proposals/multi-session-isolation.md for the full design.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const args = process.argv.slice(3)
|
|
13
|
+
const subcommand = args[0]
|
|
14
|
+
|
|
15
|
+
switch (subcommand) {
|
|
16
|
+
case 'create':
|
|
17
|
+
require('./create.js')
|
|
18
|
+
break
|
|
19
|
+
case 'list':
|
|
20
|
+
case 'ls':
|
|
21
|
+
require('./list.js')
|
|
22
|
+
break
|
|
23
|
+
case 'delete':
|
|
24
|
+
case 'rm':
|
|
25
|
+
require('./delete.js')
|
|
26
|
+
break
|
|
27
|
+
case 'prune':
|
|
28
|
+
require('./prune.js')
|
|
29
|
+
break
|
|
30
|
+
default:
|
|
31
|
+
console.log(`
|
|
32
|
+
Session Commands (multi-instance isolation, preview):
|
|
33
|
+
|
|
34
|
+
meddle session create [--name <label>] [--port <port>]
|
|
35
|
+
Create a new proxy session with its own
|
|
36
|
+
MEDDLE_HOME and port. Returns the session id.
|
|
37
|
+
meddle session list List all sessions with liveness check.
|
|
38
|
+
meddle session delete <id> [--clean]
|
|
39
|
+
Stop the session, remove from registry.
|
|
40
|
+
--clean also deletes its data directory.
|
|
41
|
+
meddle session prune Remove registry entries whose process is
|
|
42
|
+
no longer alive (keeps data directories).
|
|
43
|
+
|
|
44
|
+
Operating on a specific session:
|
|
45
|
+
meddle --session <id> route list
|
|
46
|
+
meddle --session <id> mock add --name "API" --pattern "api.test.com"
|
|
47
|
+
meddle --session <id> status
|
|
48
|
+
|
|
49
|
+
Notes:
|
|
50
|
+
- The default session (no --session) uses ~/.meddle and is fully backward
|
|
51
|
+
compatible. Existing commands and tests are unaffected.
|
|
52
|
+
- --session and the MEDDLE_HOME environment variable are mutually exclusive.
|
|
53
|
+
- All sessions share the same CA (~/.meddle/ca); only config is isolated.
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
meddle session create --name my-debug
|
|
57
|
+
meddle --session my-debug-1718283600 route list
|
|
58
|
+
meddle session delete my-debug-1718283600 --clean
|
|
59
|
+
`)
|
|
60
|
+
process.exit(subcommand ? 1 : 0)
|
|
61
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle session list - List all sessions with liveness check.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const chalk = require('chalk')
|
|
6
|
+
const { listSessions } = require('../../lib/sessions')
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(4)
|
|
9
|
+
const jsonFlag = args.includes('--json')
|
|
10
|
+
|
|
11
|
+
const sessions = listSessions()
|
|
12
|
+
|
|
13
|
+
if (jsonFlag) {
|
|
14
|
+
console.log(JSON.stringify(sessions, null, 2))
|
|
15
|
+
process.exit(0)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (sessions.length === 0) {
|
|
19
|
+
console.log(chalk.gray('No sessions registered.'))
|
|
20
|
+
console.log(chalk.gray('Create one with: meddle session create --name <label>'))
|
|
21
|
+
process.exit(0)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
console.log(chalk.bold.cyan(`Sessions (${sessions.length})`))
|
|
25
|
+
console.log(chalk.gray('─'.repeat(80)))
|
|
26
|
+
for (const s of sessions) {
|
|
27
|
+
const status = s.alive ? chalk.green('● running') : chalk.red('○ orphaned')
|
|
28
|
+
const id = chalk.cyan(s.id)
|
|
29
|
+
console.log(` ${status} ${id}`)
|
|
30
|
+
console.log(` ${chalk.gray('port:')} ${s.port}`)
|
|
31
|
+
console.log(` ${chalk.gray('pid:')} ${s.pid}`)
|
|
32
|
+
console.log(` ${chalk.gray('meddleHome:')} ${s.meddleHome}`)
|
|
33
|
+
console.log(` ${chalk.gray('created:')} ${s.createdAt}`)
|
|
34
|
+
if (s.label) console.log(` ${chalk.gray('label:')} ${s.label}`)
|
|
35
|
+
console.log('')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const orphaned = sessions.filter((s) => !s.alive)
|
|
39
|
+
if (orphaned.length > 0) {
|
|
40
|
+
console.log(chalk.yellow(`${orphaned.length} orphaned session(s) detected.`))
|
|
41
|
+
console.log(chalk.gray(`Run 'meddle session prune' to clean up registry entries.`))
|
|
42
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle session prune - Remove registry entries whose process is gone.
|
|
3
|
+
*
|
|
4
|
+
* Keeps the data directories intact (use `meddle session delete <id> --clean`
|
|
5
|
+
* for full cleanup of a specific session).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk')
|
|
9
|
+
const { pruneOrphaned } = require('../../lib/sessions')
|
|
10
|
+
|
|
11
|
+
const args = process.argv.slice(4)
|
|
12
|
+
const jsonFlag = args.includes('--json')
|
|
13
|
+
|
|
14
|
+
const removed = pruneOrphaned()
|
|
15
|
+
|
|
16
|
+
if (jsonFlag) {
|
|
17
|
+
console.log(JSON.stringify({ pruned: removed }))
|
|
18
|
+
process.exit(0)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (removed.length === 0) {
|
|
22
|
+
console.log(chalk.gray('No orphaned sessions.'))
|
|
23
|
+
process.exit(0)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(chalk.green(`Pruned ${removed.length} orphaned session(s):`))
|
|
27
|
+
for (const s of removed) {
|
|
28
|
+
console.log(` ${chalk.cyan(s.id)} ${chalk.gray(`(pid ${s.pid}, port ${s.port})`)}`)
|
|
29
|
+
console.log(chalk.gray(` data dir kept: ${s.meddleHome}`))
|
|
30
|
+
}
|
|
31
|
+
console.log(chalk.gray('\nTo also delete data directories, run: meddle session delete <id> --clean'))
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle start - Start proxy server
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { spawn } = require('child_process')
|
|
6
|
+
const path = require('path')
|
|
7
|
+
|
|
8
|
+
// Parse arguments
|
|
9
|
+
const args = process.argv.slice(3)
|
|
10
|
+
const openFlag = args.includes('--open')
|
|
11
|
+
const remoteFlag = args.includes('--remote')
|
|
12
|
+
const interceptHttpsFlag = args.includes('--intercept-https')
|
|
13
|
+
const noInterceptHttpsFlag = args.includes('--no-intercept-https')
|
|
14
|
+
|
|
15
|
+
// Start proxy
|
|
16
|
+
const indexPath = path.join(__dirname, '..', 'index.js')
|
|
17
|
+
const spawnEnv = { ...process.env, DEBUG: process.env.DEBUG || '' }
|
|
18
|
+
if (openFlag) spawnEnv.MEDDLE_OPEN = '1'
|
|
19
|
+
if (remoteFlag) spawnEnv.MEDDLE_REMOTE = '1'
|
|
20
|
+
if (interceptHttpsFlag) spawnEnv.MEDDLE_INTERCEPT_HTTPS = '1'
|
|
21
|
+
if (noInterceptHttpsFlag) spawnEnv.MEDDLE_INTERCEPT_HTTPS = '0'
|
|
22
|
+
|
|
23
|
+
const remoteTokenIndex = args.indexOf('--remote-token')
|
|
24
|
+
if (remoteTokenIndex >= 0 && args[remoteTokenIndex + 1]) {
|
|
25
|
+
spawnEnv.MEDDLE_REMOTE_TOKEN = args[remoteTokenIndex + 1]
|
|
26
|
+
}
|
|
27
|
+
const inlineRemoteToken = args.find(arg => arg.startsWith('--remote-token='))
|
|
28
|
+
if (inlineRemoteToken) spawnEnv.MEDDLE_REMOTE_TOKEN = inlineRemoteToken.slice('--remote-token='.length)
|
|
29
|
+
|
|
30
|
+
const child = spawn(process.execPath, [indexPath], {
|
|
31
|
+
env: spawnEnv,
|
|
32
|
+
stdio: 'inherit',
|
|
33
|
+
cwd: process.cwd()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
child.on('error', (err) => {
|
|
37
|
+
console.error('启动代理失败:', err)
|
|
38
|
+
process.exit(1)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
child.on('exit', (code) => {
|
|
42
|
+
process.exit(code || 0)
|
|
43
|
+
})
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* meddle status - Get proxy status
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { getProxyUrl, isProxyRunning } = require('../lib/proxy-detect')
|
|
6
|
+
const { apiGet } = require('../lib/api-client')
|
|
7
|
+
const { loadMockRules, listRuleFiles, getActiveRuleFileNames } = require('../lib/file-access')
|
|
8
|
+
const output = require('../lib/output')
|
|
9
|
+
|
|
10
|
+
// Check for --json flag
|
|
11
|
+
const args = process.argv.slice(2)
|
|
12
|
+
const jsonFlag = args.includes('--json')
|
|
13
|
+
output.setJsonMode(jsonFlag)
|
|
14
|
+
|
|
15
|
+
async function run() {
|
|
16
|
+
const url = getProxyUrl()
|
|
17
|
+
const running = await isProxyRunning()
|
|
18
|
+
|
|
19
|
+
let mockCount = 0
|
|
20
|
+
let activeMocks = 0
|
|
21
|
+
let routeFiles = []
|
|
22
|
+
let activeRoutes = 0
|
|
23
|
+
let health = null
|
|
24
|
+
|
|
25
|
+
if (running) {
|
|
26
|
+
try {
|
|
27
|
+
// Get mock rules from API
|
|
28
|
+
const mocks = await apiGet('/api/mocks')
|
|
29
|
+
mockCount = mocks.length
|
|
30
|
+
activeMocks = mocks.filter(m => m.enabled).length
|
|
31
|
+
|
|
32
|
+
// Get route files from API
|
|
33
|
+
routeFiles = await apiGet('/api/rule-files')
|
|
34
|
+
activeRoutes = routeFiles.filter(f => f.enabled).length
|
|
35
|
+
|
|
36
|
+
health = await apiGet('/api/health')
|
|
37
|
+
} catch (_) {
|
|
38
|
+
// Fallback to file access
|
|
39
|
+
const mocks = loadMockRules()
|
|
40
|
+
mockCount = mocks.length
|
|
41
|
+
activeMocks = mocks.filter(m => m.enabled).length
|
|
42
|
+
|
|
43
|
+
routeFiles = listRuleFiles()
|
|
44
|
+
activeRoutes = routeFiles.filter(f => f.enabled).length
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
// File mode
|
|
48
|
+
const mocks = loadMockRules()
|
|
49
|
+
mockCount = mocks.length
|
|
50
|
+
activeMocks = mocks.filter(m => m.enabled).length
|
|
51
|
+
|
|
52
|
+
routeFiles = listRuleFiles()
|
|
53
|
+
activeRoutes = routeFiles.filter(f => f.enabled).length
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const totalRouteRules = routeFiles.reduce((sum, f) => sum + f.ruleCount, 0)
|
|
57
|
+
const totalExclusions = routeFiles.reduce((sum, f) => sum + f.excludeCount, 0)
|
|
58
|
+
|
|
59
|
+
if (output.isJsonMode()) {
|
|
60
|
+
output.jsonRaw({
|
|
61
|
+
proxyUrl: url,
|
|
62
|
+
running,
|
|
63
|
+
health,
|
|
64
|
+
mocks: { total: mockCount, active: activeMocks },
|
|
65
|
+
routes: { files: routeFiles.length, activeFiles: activeRoutes, totalRules: totalRouteRules, totalExclusions }
|
|
66
|
+
})
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
output.header('Proxy Status')
|
|
71
|
+
output.kv('Proxy URL', url)
|
|
72
|
+
output.kv('Status', running ? 'Running' : 'Not Running')
|
|
73
|
+
if (health) {
|
|
74
|
+
output.kv('Health', health.status)
|
|
75
|
+
output.kv('PID', health.pid)
|
|
76
|
+
output.kv('CPU', `${health.cpu.percent}%`)
|
|
77
|
+
output.kv('RSS', formatBytes(health.memory.rss))
|
|
78
|
+
output.kv('Connections', health.connections.total)
|
|
79
|
+
output.kv('MITM Servers', health.mitmServers.count)
|
|
80
|
+
output.kv('Watchdog', health.watchdog.config.enabled ? `${health.watchdog.config.action}, failures=${health.watchdog.consecutiveFailures}` : 'disabled')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
output.section('Mock Rules')
|
|
84
|
+
output.kv('Total', mockCount)
|
|
85
|
+
output.kv('Active', activeMocks)
|
|
86
|
+
|
|
87
|
+
output.section('Route Rules')
|
|
88
|
+
output.kv('Files', routeFiles.length)
|
|
89
|
+
output.kv('Active Files', activeRoutes)
|
|
90
|
+
output.kv('Total Rules', totalRouteRules)
|
|
91
|
+
if (totalExclusions > 0) {
|
|
92
|
+
output.kv('Total Exclusions', totalExclusions)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (routeFiles.length > 0) {
|
|
96
|
+
output.plain('')
|
|
97
|
+
routeFiles.forEach(f => {
|
|
98
|
+
const excludeInfo = f.excludeCount > 0 ? `, ${f.excludeCount} exclusions` : ''
|
|
99
|
+
output.bullet(`${f.name} (${f.ruleCount} rules${excludeInfo})`, f.enabled)
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
run().catch(e => {
|
|
105
|
+
output.error(e.message)
|
|
106
|
+
process.exit(1)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
function formatBytes(bytes) {
|
|
110
|
+
if (!Number.isFinite(bytes)) return 'n/a'
|
|
111
|
+
if (bytes >= 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024 / 1024).toFixed(1)} GB`
|
|
112
|
+
if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`
|
|
113
|
+
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
|
114
|
+
return `${bytes} B`
|
|
115
|
+
}
|