@jonathanleelx/meddle 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -0
- package/bin/commands/session/create.js +20 -17
- package/bin/commands/start.js +16 -13
- package/bin/commands/supervise.js +16 -7
- package/bin/lib/dispatch.js +34 -0
- package/bin/lib/spawn-proxy.js +51 -0
- package/bin/main.js +34 -0
- package/dist/core/body-utils.d.ts +2 -0
- package/dist/core/body-utils.d.ts.map +1 -1
- package/dist/core/body-utils.js +136 -0
- package/dist/core/body-utils.js.map +1 -1
- package/index.js +5 -5
- package/mcp-server.js +22 -16
- package/package.json +2 -2
- package/dist/core/plugin-compiler.d.ts +0 -37
- package/dist/core/plugin-compiler.d.ts.map +0 -1
- package/dist/core/plugin-compiler.js +0 -172
- package/dist/core/plugin-compiler.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,8 +5,37 @@
|
|
|
5
5
|
|
|
6
6
|
## 使用
|
|
7
7
|
|
|
8
|
+
### 方式一:npm 安装
|
|
9
|
+
|
|
8
10
|
全局安装 `npm install @jonathanleelx/meddle -g`,会创建一个 `meddle` 命令。
|
|
9
11
|
|
|
12
|
+
### 方式二:二进制安装(无需 Node.js)
|
|
13
|
+
|
|
14
|
+
**macOS / Linux:**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
curl -fsSL https://raw.githubusercontent.com/JonathanLee-LX/meddle/main/scripts/install-binary.sh | bash
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Windows (PowerShell):**
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
irm https://raw.githubusercontent.com/JonathanLee-LX/meddle/main/scripts/install-binary.ps1 | iex
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**支持平台:**
|
|
27
|
+
|
|
28
|
+
| 平台 | 产物 |
|
|
29
|
+
|---|---|
|
|
30
|
+
| Linux x64 | `meddle-linux-x64` |
|
|
31
|
+
| Linux ARM64 | `meddle-linux-arm64` |
|
|
32
|
+
| macOS Intel | `meddle-darwin-x64` |
|
|
33
|
+
| macOS Apple Silicon | `meddle-darwin-arm64` |
|
|
34
|
+
| Windows x64 | `meddle-windows-x64.exe` |
|
|
35
|
+
| Windows ARM64 | `meddle-windows-arm64.exe` |
|
|
36
|
+
|
|
37
|
+
二进制产物随 [GitHub Release](https://github.com/JonathanLee-LX/meddle/releases) 发布,也可手动下载。安装目录默认 `~/.meddle/bin`,可通过 `MEDDLE_BIN_DIR` 环境变量自定义。
|
|
38
|
+
|
|
10
39
|
### 命令
|
|
11
40
|
|
|
12
41
|
```bash
|
|
@@ -30,6 +30,7 @@ const {
|
|
|
30
30
|
} = require('../../lib/sessions')
|
|
31
31
|
const { resolveMeddleHome } = require('../../lib/meddle-home')
|
|
32
32
|
const { GLOBAL_DEFAULT_PORT } = require('../../lib/session-args')
|
|
33
|
+
const { buildProxySpawn, getReentryArgv } = require('../../lib/spawn-proxy')
|
|
33
34
|
|
|
34
35
|
function parseArgs(argv) {
|
|
35
36
|
const args = argv.slice(4) // after 'meddle' 'session' 'create'
|
|
@@ -88,24 +89,26 @@ async function main() {
|
|
|
88
89
|
// via its existing startup logic. We only ensure the root exists.
|
|
89
90
|
|
|
90
91
|
// 3. spawn child
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
92
|
+
const { args: spawnArgs, options: spawnOptions } = buildProxySpawn({
|
|
93
|
+
baseEnv: process.env,
|
|
94
|
+
reentryArgv: getReentryArgv(),
|
|
95
|
+
extraEnv: {
|
|
96
|
+
MEDDLE_HOME: meddleHome,
|
|
97
|
+
PORT: String(port),
|
|
98
|
+
MEDDLE_SESSION_ID: id,
|
|
99
|
+
// Suppress the CA trust prompt — sessions reuse the shared CA
|
|
100
|
+
// from ~/.meddle/ca which the user already trusted when setting up
|
|
101
|
+
// the default session. Also avoid interactive prompts blocking
|
|
102
|
+
// a non-interactive agent spawn.
|
|
103
|
+
MEDDLE_HEADLESS: '1',
|
|
104
|
+
// Disable MCP file discovery for non-default sessions; the MCP
|
|
105
|
+
// proxy-url file is only meaningful for the default session.
|
|
106
|
+
MEDDLE_MCP: '',
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
const child = spawn(process.execPath, spawnArgs, {
|
|
107
110
|
cwd: process.cwd(),
|
|
108
|
-
env:
|
|
111
|
+
env: spawnOptions.env,
|
|
109
112
|
stdio: 'inherit',
|
|
110
113
|
detached: false,
|
|
111
114
|
})
|
package/bin/commands/start.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const { spawn } = require('child_process')
|
|
6
|
-
const
|
|
6
|
+
const { buildProxySpawn, getReentryArgv } = require('../lib/spawn-proxy')
|
|
7
7
|
|
|
8
8
|
// Parse arguments
|
|
9
9
|
const args = process.argv.slice(3)
|
|
@@ -13,24 +13,27 @@ const interceptHttpsFlag = args.includes('--intercept-https')
|
|
|
13
13
|
const noInterceptHttpsFlag = args.includes('--no-intercept-https')
|
|
14
14
|
|
|
15
15
|
// Start proxy
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
if (
|
|
20
|
-
if (
|
|
21
|
-
if (noInterceptHttpsFlag) spawnEnv.MEDDLE_INTERCEPT_HTTPS = '0'
|
|
16
|
+
const extraEnv = { DEBUG: process.env.DEBUG || '' }
|
|
17
|
+
if (openFlag) extraEnv.MEDDLE_OPEN = '1'
|
|
18
|
+
if (remoteFlag) extraEnv.MEDDLE_REMOTE = '1'
|
|
19
|
+
if (interceptHttpsFlag) extraEnv.MEDDLE_INTERCEPT_HTTPS = '1'
|
|
20
|
+
if (noInterceptHttpsFlag) extraEnv.MEDDLE_INTERCEPT_HTTPS = '0'
|
|
22
21
|
|
|
23
22
|
const remoteTokenIndex = args.indexOf('--remote-token')
|
|
24
23
|
if (remoteTokenIndex >= 0 && args[remoteTokenIndex + 1]) {
|
|
25
|
-
|
|
24
|
+
extraEnv.MEDDLE_REMOTE_TOKEN = args[remoteTokenIndex + 1]
|
|
26
25
|
}
|
|
27
26
|
const inlineRemoteToken = args.find(arg => arg.startsWith('--remote-token='))
|
|
28
|
-
if (inlineRemoteToken)
|
|
27
|
+
if (inlineRemoteToken) extraEnv.MEDDLE_REMOTE_TOKEN = inlineRemoteToken.slice('--remote-token='.length)
|
|
29
28
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
const { args: spawnArgs, options: spawnOptions } = buildProxySpawn({
|
|
30
|
+
baseEnv: process.env,
|
|
31
|
+
reentryArgv: getReentryArgv(),
|
|
32
|
+
extraEnv,
|
|
33
|
+
})
|
|
34
|
+
const child = spawn(process.execPath, spawnArgs, {
|
|
35
|
+
...spawnOptions,
|
|
36
|
+
cwd: process.cwd(),
|
|
34
37
|
})
|
|
35
38
|
|
|
36
39
|
child.on('error', (err) => {
|
|
@@ -6,6 +6,7 @@ const fs = require('fs')
|
|
|
6
6
|
const path = require('path')
|
|
7
7
|
const { spawn } = require('child_process')
|
|
8
8
|
const { resolveMeddleHome } = require('../lib/meddle-home')
|
|
9
|
+
const { buildProxySpawn, buildCliSpawn, getReentryArgv } = require('../lib/spawn-proxy')
|
|
9
10
|
|
|
10
11
|
const rawArgs = process.argv.slice(3)
|
|
11
12
|
const daemonFlag = rawArgs.includes('--daemon')
|
|
@@ -20,9 +21,13 @@ if (daemonFlag) {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
function supervise() {
|
|
23
|
-
const indexPath = path.join(__dirname, '..', '..', 'index.js')
|
|
24
24
|
const proxyArgs = stripSupervisorArgs(rawArgs)
|
|
25
|
-
const
|
|
25
|
+
const { args: spawnArgs, options: spawnOptions } = buildProxySpawn({
|
|
26
|
+
baseEnv: process.env,
|
|
27
|
+
reentryArgv: getReentryArgv(),
|
|
28
|
+
extraArgv: proxyArgs,
|
|
29
|
+
extraEnv: { DEBUG: process.env.DEBUG || '', MEDDLE_SUPERVISED: '1' },
|
|
30
|
+
})
|
|
26
31
|
|
|
27
32
|
let child = null
|
|
28
33
|
let stopping = false
|
|
@@ -37,9 +42,9 @@ function supervise() {
|
|
|
37
42
|
process.on('SIGTERM', () => stop('SIGTERM'))
|
|
38
43
|
|
|
39
44
|
const start = () => {
|
|
40
|
-
child = spawn(process.execPath,
|
|
45
|
+
child = spawn(process.execPath, spawnArgs, {
|
|
41
46
|
cwd: process.cwd(),
|
|
42
|
-
env:
|
|
47
|
+
env: spawnOptions.env,
|
|
43
48
|
stdio: 'inherit',
|
|
44
49
|
})
|
|
45
50
|
|
|
@@ -71,12 +76,16 @@ function startDaemon() {
|
|
|
71
76
|
const meddleDir = resolveMeddleHome()
|
|
72
77
|
fs.mkdirSync(meddleDir, { recursive: true })
|
|
73
78
|
const logFd = fs.openSync(path.join(meddleDir, 'supervisor.log'), 'a')
|
|
74
|
-
const
|
|
75
|
-
|
|
79
|
+
const { args: spawnArgs, options: spawnOptions } = buildCliSpawn({
|
|
80
|
+
baseEnv: process.env,
|
|
81
|
+
reentryArgv: getReentryArgv(),
|
|
82
|
+
extraArgv: ['supervise', ...args],
|
|
83
|
+
})
|
|
84
|
+
const child = spawn(process.execPath, spawnArgs, {
|
|
76
85
|
cwd: process.cwd(),
|
|
77
86
|
detached: true,
|
|
78
87
|
stdio: ['ignore', logFd, logFd],
|
|
79
|
-
env:
|
|
88
|
+
env: spawnOptions.env,
|
|
80
89
|
})
|
|
81
90
|
child.unref()
|
|
82
91
|
console.log(`Supervisor started: pid=${child.pid}, log=${path.join(meddleDir, 'supervisor.log')}`)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Single-entry env dispatch for the meddle binary.
|
|
4
|
+
// Pure function: no process.env / process.argv reads, no side effects.
|
|
5
|
+
// The binary entrypoint (bin/main.js) calls this with explicit values and then
|
|
6
|
+
// requires the matching module. Self-spawn children set MEDDLE_ENTRY=proxy|mcp|cli
|
|
7
|
+
// instead of passing a script path, so a deno-compiled binary (which has no
|
|
8
|
+
// index.js / mcp-server.js / bin/index.js as files on disk) can still route.
|
|
9
|
+
|
|
10
|
+
/** @typedef {'proxy' | 'mcp' | 'cli'} Entry */
|
|
11
|
+
|
|
12
|
+
const ALLOWED = ['proxy', 'mcp', 'cli']
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {{ env: Record<string, string | undefined>, argv: string[] }} input
|
|
16
|
+
* @returns {{ entry: Entry, argv: string[] }}
|
|
17
|
+
*/
|
|
18
|
+
function dispatch({ env, argv }) {
|
|
19
|
+
const raw = env && typeof env.MEDDLE_ENTRY === 'string' ? env.MEDDLE_ENTRY : ''
|
|
20
|
+
const normalized = raw.trim().toLowerCase()
|
|
21
|
+
let entry
|
|
22
|
+
if (normalized === '') {
|
|
23
|
+
entry = 'cli'
|
|
24
|
+
} else if (ALLOWED.includes(normalized)) {
|
|
25
|
+
entry = normalized
|
|
26
|
+
} else {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Unknown MEDDLE_ENTRY "${raw}". Allowed values: ${ALLOWED.join(', ')} (case-insensitive). Omit to default to 'cli'.`
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
return { entry, argv: Array.isArray(argv) ? argv.slice() : [] }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = { dispatch, ALLOWED }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
// Pure constructor for the meddle self-spawn contract, parameterised by runtime
|
|
6
|
+
// context (binary vs node). All six prod self-spawn sites (mcp-server x2, supervise
|
|
7
|
+
// x2, session/create, start) build their { args, options } through this so the
|
|
8
|
+
// contract lives in ONE place.
|
|
9
|
+
//
|
|
10
|
+
// Reentry model:
|
|
11
|
+
// binary (deno compile): process.execPath IS meddle, reads MEDDLE_ENTRY, dispatches.
|
|
12
|
+
// -> reentryArgv = [] (no script path)
|
|
13
|
+
// npm/node: process.execPath is plain `node`, needs a script path to bin/main.js,
|
|
14
|
+
// which then dispatches via MEDDLE_ENTRY.
|
|
15
|
+
// -> reentryArgv = [absPathToBinMainJs]
|
|
16
|
+
// Callers pass reentryArgv explicitly via getReentryArgv() so buildProxySpawn stays
|
|
17
|
+
// pure (no process reads) and unit-testable with injected reentryArgv.
|
|
18
|
+
|
|
19
|
+
let reentryOverride = null // test hook; undefined = detect from process
|
|
20
|
+
|
|
21
|
+
function getReentryArgv() {
|
|
22
|
+
if (reentryOverride !== null) return reentryOverride.slice()
|
|
23
|
+
// The deno-compiled meddle binary's execPath does NOT look like node, and it
|
|
24
|
+
// dispatches via MEDDLE_ENTRY itself, so no script path is needed. Plain `node`
|
|
25
|
+
// needs bin/main.js to re-enter the dispatcher.
|
|
26
|
+
const exe = (process.execPath || '').toLowerCase()
|
|
27
|
+
const looksLikeNode = /(^|[\\\/])node(\.exe)?$/i.test(exe) || /[\\\/]node-[0-9]/i.test(exe)
|
|
28
|
+
if (!looksLikeNode) return [] // the meddle binary
|
|
29
|
+
// node: re-enter via bin/main.js (this file's neighbour).
|
|
30
|
+
return [path.join(__dirname, '..', 'main.js')]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function buildSpawn({ baseEnv, extraEnv, extraArgv, entry, reentryArgv } = {}) {
|
|
34
|
+
const env = Object.assign({}, baseEnv || {}, extraEnv || {}, { MEDDLE_ENTRY: entry })
|
|
35
|
+
const reentry = Array.isArray(reentryArgv) ? reentryArgv : [] // default = binary form
|
|
36
|
+
const args = reentry.concat(Array.isArray(extraArgv) ? extraArgv : [])
|
|
37
|
+
return { args, options: { env, stdio: 'inherit' } }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function buildProxySpawn(opts = {}) {
|
|
41
|
+
return buildSpawn({ ...(opts || {}), entry: 'proxy' })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildCliSpawn(opts = {}) {
|
|
45
|
+
return buildSpawn({ ...(opts || {}), entry: 'cli' })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Test-only hook; do not call from prod code.
|
|
49
|
+
function __setReentryOverrideForTest(v) { reentryOverride = v === undefined ? null : v }
|
|
50
|
+
|
|
51
|
+
module.exports = { buildProxySpawn, buildCliSpawn, getReentryArgv, __setReentryOverrideForTest }
|
package/bin/main.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
// Single injectable entrypoint for the meddle binary.
|
|
5
|
+
// - deno compile target: `deno compile bin/main.js` injects this; routing is via
|
|
6
|
+
// MEDDLE_ENTRY (proxy|mcp|cli) since the compiled binary has no index.js /
|
|
7
|
+
// mcp-server.js / bin/index.js as files on disk.
|
|
8
|
+
// - node/npm path: `meddle` still resolves to bin/index.js (see package.json "bin");
|
|
9
|
+
// this file is only required when MEDDLE_ENTRY is set (self-spawn children) or
|
|
10
|
+
// when invoked explicitly (e.g. `node bin/main.js`).
|
|
11
|
+
//
|
|
12
|
+
// Existing entry files (bin/index.js, index.js, mcp-server.js) are left runnable
|
|
13
|
+
// directly under node — npm path has zero regression.
|
|
14
|
+
|
|
15
|
+
const { dispatch } = require('./lib/dispatch')
|
|
16
|
+
|
|
17
|
+
const { entry, argv } = dispatch({ env: process.env, argv: process.argv.slice(2) })
|
|
18
|
+
|
|
19
|
+
if (entry === 'proxy') {
|
|
20
|
+
// Proxy server. process.argv must look like `node index.js [opts]` for index.js
|
|
21
|
+
// semantics (it reads process.argv via helpers like getFreePort etc.), so we
|
|
22
|
+
// splice argv into process.argv. index.js itself ignores argv for routing but
|
|
23
|
+
// MEDDLE_* env is the real config channel, so argv here is informational.
|
|
24
|
+
process.argv = [process.argv[0], 'index.js', ...argv]
|
|
25
|
+
require('../index.js')
|
|
26
|
+
} else if (entry === 'mcp') {
|
|
27
|
+
process.argv = [process.argv[0], 'mcp-server.js', ...argv]
|
|
28
|
+
require('../mcp-server.js')
|
|
29
|
+
} else {
|
|
30
|
+
// cli: bin/index.js expects argv = process.argv.slice(2) (the user command +
|
|
31
|
+
// flags), and strips --session itself. Forward verbatim.
|
|
32
|
+
process.argv = [process.argv[0], 'bin/index.js', ...argv]
|
|
33
|
+
require('./index.js')
|
|
34
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
export declare function detailDecompressInputCap(max: number): number;
|
|
1
2
|
export interface BodyDetail {
|
|
2
3
|
text: string;
|
|
3
4
|
truncated: boolean;
|
|
4
5
|
originalBytes: number;
|
|
5
6
|
}
|
|
6
7
|
export declare function safeBodyToDetail(buf: Buffer | any, max: number, encoding?: string): BodyDetail;
|
|
8
|
+
export declare function safeBodyToDetailAsync(buf: Buffer | any, max: number, encoding?: string): Promise<BodyDetail>;
|
|
7
9
|
export declare function safeBodyToString(buf: Buffer | any, max: number, encoding?: string): string;
|
|
8
10
|
//# sourceMappingURL=body-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body-utils.d.ts","sourceRoot":"","sources":["../../core/body-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"body-utils.d.ts","sourceRoot":"","sources":["../../core/body-utils.ts"],"names":[],"mappings":"AAiBA,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE5D;AA2CD,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACzB;AAMD,wBAAgB,gBAAgB,CAC5B,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAClB,UAAU,CA2BZ;AAMD,wBAAsB,qBAAqB,CACvC,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,UAAU,CAAC,CAqCrB;AAED,wBAAgB,gBAAgB,CAC5B,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,GAAG,EAAE,MAAM,EACX,QAAQ,CAAC,EAAE,MAAM,GAClB,MAAM,CAER"}
|
package/dist/core/body-utils.js
CHANGED
|
@@ -1,8 +1,104 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.detailDecompressInputCap = detailDecompressInputCap;
|
|
3
37
|
exports.safeBodyToDetail = safeBodyToDetail;
|
|
38
|
+
exports.safeBodyToDetailAsync = safeBodyToDetailAsync;
|
|
4
39
|
exports.safeBodyToString = safeBodyToString;
|
|
40
|
+
const util_1 = require("util");
|
|
41
|
+
const zlib = __importStar(require("zlib"));
|
|
5
42
|
const content_encoding_1 = require("./content-encoding");
|
|
43
|
+
const gunzipAsync = (0, util_1.promisify)(zlib.gunzip);
|
|
44
|
+
const inflateAsync = (0, util_1.promisify)(zlib.inflate);
|
|
45
|
+
const brotliDecompressAsync = (0, util_1.promisify)(zlib.brotliDecompress);
|
|
46
|
+
const zstdDecompressAsync = zlib.zstdDecompress;
|
|
47
|
+
const zstdAsync = zstdDecompressAsync ? (0, util_1.promisify)(zstdDecompressAsync) : null;
|
|
48
|
+
// Upper bound on compressed input we are willing to decompress for a detail.
|
|
49
|
+
// Since we only keep `max` bytes of the decompressed body, decompressing vastly
|
|
50
|
+
// larger inputs is pure waste (CPU + memory) and, when done synchronously, can
|
|
51
|
+
// block the event loop for minutes on huge payloads. Bodies larger than this
|
|
52
|
+
// are reported as a placeholder instead of being decompressed.
|
|
53
|
+
function detailDecompressInputCap(max) {
|
|
54
|
+
return Math.max(max * 8, 2 * 1024 * 1024);
|
|
55
|
+
}
|
|
56
|
+
async function decompressContentEncodingAsync(buffer, contentEncoding, maxInputBytes) {
|
|
57
|
+
const encodings = (contentEncoding || '')
|
|
58
|
+
.split(',')
|
|
59
|
+
.map(e => e.trim().toLowerCase())
|
|
60
|
+
.filter(Boolean);
|
|
61
|
+
if (encodings.length === 0)
|
|
62
|
+
return buffer;
|
|
63
|
+
// Refuse to decompress oversized inputs: we'd only keep `max` bytes anyway.
|
|
64
|
+
if (buffer.length > maxInputBytes)
|
|
65
|
+
return null;
|
|
66
|
+
let content = buffer;
|
|
67
|
+
for (const encoding of encodings.reverse()) {
|
|
68
|
+
try {
|
|
69
|
+
switch (encoding) {
|
|
70
|
+
case 'identity':
|
|
71
|
+
case '':
|
|
72
|
+
break;
|
|
73
|
+
case 'gzip':
|
|
74
|
+
case 'x-gzip':
|
|
75
|
+
content = await gunzipAsync(content);
|
|
76
|
+
break;
|
|
77
|
+
case 'deflate':
|
|
78
|
+
content = await inflateAsync(content);
|
|
79
|
+
break;
|
|
80
|
+
case 'br':
|
|
81
|
+
content = await brotliDecompressAsync(content);
|
|
82
|
+
break;
|
|
83
|
+
case 'zstd':
|
|
84
|
+
if (!zstdAsync)
|
|
85
|
+
return null;
|
|
86
|
+
content = await zstdAsync(content);
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch (_) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return content;
|
|
97
|
+
}
|
|
98
|
+
// Sync variant for paths that never receive compressed upstream bodies (mock
|
|
99
|
+
// responses, map-local file serving). When an encoding is present it falls back
|
|
100
|
+
// to the (bounded) sync decompressor; callers without encoding pass nothing and
|
|
101
|
+
// this is effectively a no-op.
|
|
6
102
|
function safeBodyToDetail(buf, max, encoding) {
|
|
7
103
|
if (!Buffer.isBuffer(buf) || buf.length === 0) {
|
|
8
104
|
return { text: '', truncated: false, originalBytes: 0 };
|
|
@@ -29,6 +125,46 @@ function safeBodyToDetail(buf, max, encoding) {
|
|
|
29
125
|
return { text: '(binary)', truncated: false, originalBytes: content.length };
|
|
30
126
|
}
|
|
31
127
|
}
|
|
128
|
+
// Async variant for the proxy hot path: decompresses off the event loop
|
|
129
|
+
// (libuv thread pool) and refuses oversized inputs. This prevents large
|
|
130
|
+
// compressed upstream responses from starving the event loop (watchdog
|
|
131
|
+
// eventLoopDelay critical).
|
|
132
|
+
async function safeBodyToDetailAsync(buf, max, encoding) {
|
|
133
|
+
if (!Buffer.isBuffer(buf) || buf.length === 0) {
|
|
134
|
+
return { text: '', truncated: false, originalBytes: 0 };
|
|
135
|
+
}
|
|
136
|
+
const cap = detailDecompressInputCap(max);
|
|
137
|
+
const content = await decompressContentEncodingAsync(buf, encoding, cap);
|
|
138
|
+
if (!content) {
|
|
139
|
+
// Either a genuinely unsupported encoding or an oversized input we
|
|
140
|
+
// refused to decompress. Distinguish so the detail is informative.
|
|
141
|
+
if (encoding && buf.length > cap) {
|
|
142
|
+
return {
|
|
143
|
+
text: `(compressed body too large to decompress: ${encoding}, ${buf.length} bytes)`,
|
|
144
|
+
truncated: false,
|
|
145
|
+
originalBytes: buf.length,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
text: `(compressed body: ${encoding || 'unknown'}, ${buf.length} bytes)`,
|
|
150
|
+
truncated: false,
|
|
151
|
+
originalBytes: buf.length,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (content.length > max) {
|
|
155
|
+
return {
|
|
156
|
+
text: `(truncated, ${content.length} bytes)\n` + content.slice(0, max).toString('utf8'),
|
|
157
|
+
truncated: true,
|
|
158
|
+
originalBytes: content.length,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
return { text: content.toString('utf8'), truncated: false, originalBytes: content.length };
|
|
163
|
+
}
|
|
164
|
+
catch (_) {
|
|
165
|
+
return { text: '(binary)', truncated: false, originalBytes: content.length };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
32
168
|
function safeBodyToString(buf, max, encoding) {
|
|
33
169
|
return safeBodyToDetail(buf, max, encoding).text;
|
|
34
170
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"body-utils.js","sourceRoot":"","sources":["../../core/body-utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"body-utils.js","sourceRoot":"","sources":["../../core/body-utils.ts"],"names":[],"mappings":"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"}
|
package/index.js
CHANGED
|
@@ -8,7 +8,7 @@ const { resolveTargetUrl, getFreePort } = require('./dist/helpers')
|
|
|
8
8
|
const { crtMgr, ensureRootCA, getRootCAPath } = require('./dist/cert')
|
|
9
9
|
const { decideRoute } = require('./dist/core/route-decision')
|
|
10
10
|
const { sendShortResponse } = require('./dist/core/short-response')
|
|
11
|
-
const {
|
|
11
|
+
const { safeBodyToDetailAsync } = require('./dist/core/body-utils')
|
|
12
12
|
const {
|
|
13
13
|
establishConnectTunnel,
|
|
14
14
|
isExpectedSocketError,
|
|
@@ -433,8 +433,8 @@ const proxyServer = http.createServer(async (req, res) => {
|
|
|
433
433
|
// 获取 inspection 信息
|
|
434
434
|
const inspectionStages = routeDecision.meta?._inspectionStages || []
|
|
435
435
|
const _detailBodyLimit = ctx.resolveDetailBodySizeBytes()
|
|
436
|
-
const _reqBodyDetail =
|
|
437
|
-
const _resBodyDetail =
|
|
436
|
+
const _reqBodyDetail = await safeBodyToDetailAsync(reqBody, _detailBodyLimit)
|
|
437
|
+
const _resBodyDetail = await safeBodyToDetailAsync(resBody, _detailBodyLimit, responseEncoding)
|
|
438
438
|
const detail = {
|
|
439
439
|
requestHeaders: req.headers,
|
|
440
440
|
requestBody: _reqBodyDetail.text,
|
|
@@ -686,8 +686,8 @@ proxyServer.on('connect', async (req, socket, head) => {
|
|
|
686
686
|
// 获取 inspection 信息
|
|
687
687
|
const inspectionStages = routeDecision.meta?._inspectionStages || []
|
|
688
688
|
const _detailBodyLimit = ctx.resolveDetailBodySizeBytes()
|
|
689
|
-
const _reqBodyDetail =
|
|
690
|
-
const _resBodyDetail =
|
|
689
|
+
const _reqBodyDetail = await safeBodyToDetailAsync(reqBody, _detailBodyLimit)
|
|
690
|
+
const _resBodyDetail = await safeBodyToDetailAsync(resBody, _detailBodyLimit, responseEncoding)
|
|
691
691
|
const detail = {
|
|
692
692
|
requestHeaders: req.headers,
|
|
693
693
|
requestBody: _reqBodyDetail.text,
|
package/mcp-server.js
CHANGED
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
deleteSession, isPidAlive, allocatePort, generateId, sessionDir, readRegistry
|
|
17
17
|
} = require('./bin/lib/sessions')
|
|
18
18
|
const { GLOBAL_DEFAULT_PORT } = require('./bin/lib/session-args')
|
|
19
|
+
const { buildProxySpawn, getReentryArgv } = require('./bin/lib/spawn-proxy')
|
|
19
20
|
|
|
20
21
|
const meddleDir = resolveMeddleHome()
|
|
21
22
|
const mcpFile = path.join(meddleDir, 'mcp-proxy-url.json')
|
|
@@ -208,12 +209,15 @@ mcpServer.registerTool('start_proxy', {
|
|
|
208
209
|
}
|
|
209
210
|
proxyProcess = null
|
|
210
211
|
cachedProxyUrl = null
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
212
|
+
const { args, options } = buildProxySpawn({
|
|
213
|
+
baseEnv: process.env,
|
|
214
|
+
reentryArgv: getReentryArgv(),
|
|
215
|
+
extraEnv: { MEDDLE_MCP: '1', DEBUG: process.env.DEBUG || '' },
|
|
216
|
+
})
|
|
217
|
+
const child = spawn(process.execPath, args, {
|
|
218
|
+
...options,
|
|
215
219
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
216
|
-
cwd: process.cwd()
|
|
220
|
+
cwd: process.cwd(),
|
|
217
221
|
})
|
|
218
222
|
proxyProcess = child
|
|
219
223
|
child.stdout?.on('data', (d) => process.stderr.write(d))
|
|
@@ -278,17 +282,19 @@ mcpServer.registerTool('create_session', {
|
|
|
278
282
|
fs.mkdirSync(sDir, { recursive: true })
|
|
279
283
|
|
|
280
284
|
// 3. spawn child
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
285
|
+
const { args, options } = buildProxySpawn({
|
|
286
|
+
baseEnv: process.env,
|
|
287
|
+
reentryArgv: getReentryArgv(),
|
|
288
|
+
extraEnv: {
|
|
289
|
+
MEDDLE_HOME: sDir,
|
|
290
|
+
PORT: String(targetPort),
|
|
291
|
+
MEDDLE_SESSION_ID: id,
|
|
292
|
+
MEDDLE_HEADLESS: '1',
|
|
293
|
+
MEDDLE_MCP: '1',
|
|
294
|
+
},
|
|
295
|
+
})
|
|
296
|
+
const child = spawn(process.execPath, args, {
|
|
297
|
+
...options,
|
|
292
298
|
stdio: ['ignore', 'ignore', 'ignore'],
|
|
293
299
|
cwd: process.cwd(),
|
|
294
300
|
detached: false,
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jonathanleelx/meddle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A simple development proxy server with custom routing rules, mock responses, HTTPS interception, and a plugin system",
|
|
5
|
+
"type": "commonjs",
|
|
5
6
|
"main": "index.js",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"build": "tsc",
|
|
@@ -71,7 +72,6 @@
|
|
|
71
72
|
"chalk": "^4.1.2",
|
|
72
73
|
"chokidar": "^3.6.0",
|
|
73
74
|
"debug": "^4.4.3",
|
|
74
|
-
"esbuild": "^0.27.7",
|
|
75
75
|
"express": "^5.2.1",
|
|
76
76
|
"inquirer": "^8.2.7",
|
|
77
77
|
"node-easy-cert": "^1.3.3",
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 插件编译器
|
|
3
|
-
* 将TypeScript插件代码编译为JavaScript
|
|
4
|
-
*/
|
|
5
|
-
export interface CompileResult {
|
|
6
|
-
success: boolean;
|
|
7
|
-
jsCode?: string;
|
|
8
|
-
error?: string;
|
|
9
|
-
sourceMap?: string;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* 编译TypeScript插件代码为JavaScript
|
|
13
|
-
*/
|
|
14
|
-
export declare function compilePluginCode(tsCode: string, _filename?: string): Promise<CompileResult>;
|
|
15
|
-
/**
|
|
16
|
-
* 编译插件文件
|
|
17
|
-
* 读取.ts文件,编译并保存为.js文件
|
|
18
|
-
*/
|
|
19
|
-
export declare function compilePluginFile(tsFilePath: string): Promise<CompileResult>;
|
|
20
|
-
/**
|
|
21
|
-
* 批量编译插件目录中的所有.ts文件
|
|
22
|
-
*/
|
|
23
|
-
export declare function compilePluginsDirectory(pluginsDir: string): Promise<{
|
|
24
|
-
total: number;
|
|
25
|
-
success: number;
|
|
26
|
-
failed: number;
|
|
27
|
-
errors: string[];
|
|
28
|
-
}>;
|
|
29
|
-
/**
|
|
30
|
-
* 验证编译后的JavaScript代码
|
|
31
|
-
* 检查是否包含必需的plugin导出
|
|
32
|
-
*/
|
|
33
|
-
export declare function validateCompiledPlugin(jsCode: string): {
|
|
34
|
-
valid: boolean;
|
|
35
|
-
error?: string;
|
|
36
|
-
};
|
|
37
|
-
//# sourceMappingURL=plugin-compiler.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-compiler.d.ts","sourceRoot":"","sources":["../../core/plugin-compiler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACnC,MAAM,EAAE,MAAM,EACd,SAAS,GAAE,MAAoB,GAChC,OAAO,CAAC,aAAa,CAAC,CAuBxB;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACnC,UAAU,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,CAAC,CAiCxB;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CACzC,UAAU,EAAE,MAAM,GACnB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAmC/E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAyBzF"}
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* 插件编译器
|
|
4
|
-
* 将TypeScript插件代码编译为JavaScript
|
|
5
|
-
*/
|
|
6
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
-
if (k2 === undefined) k2 = k;
|
|
8
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
-
}
|
|
12
|
-
Object.defineProperty(o, k2, desc);
|
|
13
|
-
}) : (function(o, m, k, k2) {
|
|
14
|
-
if (k2 === undefined) k2 = k;
|
|
15
|
-
o[k2] = m[k];
|
|
16
|
-
}));
|
|
17
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
-
}) : function(o, v) {
|
|
20
|
-
o["default"] = v;
|
|
21
|
-
});
|
|
22
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
-
var ownKeys = function(o) {
|
|
24
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
-
var ar = [];
|
|
26
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
-
return ar;
|
|
28
|
-
};
|
|
29
|
-
return ownKeys(o);
|
|
30
|
-
};
|
|
31
|
-
return function (mod) {
|
|
32
|
-
if (mod && mod.__esModule) return mod;
|
|
33
|
-
var result = {};
|
|
34
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
-
__setModuleDefault(result, mod);
|
|
36
|
-
return result;
|
|
37
|
-
};
|
|
38
|
-
})();
|
|
39
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.compilePluginCode = compilePluginCode;
|
|
41
|
-
exports.compilePluginFile = compilePluginFile;
|
|
42
|
-
exports.compilePluginsDirectory = compilePluginsDirectory;
|
|
43
|
-
exports.validateCompiledPlugin = validateCompiledPlugin;
|
|
44
|
-
const esbuild = __importStar(require("esbuild"));
|
|
45
|
-
const fs = __importStar(require("fs"));
|
|
46
|
-
const path = __importStar(require("path"));
|
|
47
|
-
/**
|
|
48
|
-
* 编译TypeScript插件代码为JavaScript
|
|
49
|
-
*/
|
|
50
|
-
async function compilePluginCode(tsCode, _filename = 'plugin.ts') {
|
|
51
|
-
try {
|
|
52
|
-
const result = await esbuild.transform(tsCode, {
|
|
53
|
-
loader: 'ts',
|
|
54
|
-
target: 'node18',
|
|
55
|
-
format: 'cjs',
|
|
56
|
-
platform: 'node',
|
|
57
|
-
sourcemap: 'inline',
|
|
58
|
-
minify: false,
|
|
59
|
-
keepNames: true,
|
|
60
|
-
});
|
|
61
|
-
return {
|
|
62
|
-
success: true,
|
|
63
|
-
jsCode: result.code,
|
|
64
|
-
sourceMap: result.map,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
return {
|
|
69
|
-
success: false,
|
|
70
|
-
error: error.message || '编译失败',
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* 编译插件文件
|
|
76
|
-
* 读取.ts文件,编译并保存为.js文件
|
|
77
|
-
*/
|
|
78
|
-
async function compilePluginFile(tsFilePath) {
|
|
79
|
-
try {
|
|
80
|
-
// 读取TypeScript文件
|
|
81
|
-
if (!fs.existsSync(tsFilePath)) {
|
|
82
|
-
return {
|
|
83
|
-
success: false,
|
|
84
|
-
error: '文件不存在: ' + tsFilePath,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
const tsCode = fs.readFileSync(tsFilePath, 'utf8');
|
|
88
|
-
// 编译
|
|
89
|
-
const compileResult = await compilePluginCode(tsCode, path.basename(tsFilePath));
|
|
90
|
-
if (!compileResult.success) {
|
|
91
|
-
return compileResult;
|
|
92
|
-
}
|
|
93
|
-
// 保存编译后的JavaScript文件
|
|
94
|
-
const jsFilePath = tsFilePath.replace(/\.ts$/, '.js');
|
|
95
|
-
fs.writeFileSync(jsFilePath, compileResult.jsCode, 'utf8');
|
|
96
|
-
return {
|
|
97
|
-
success: true,
|
|
98
|
-
jsCode: compileResult.jsCode,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
return {
|
|
103
|
-
success: false,
|
|
104
|
-
error: error.message || '编译文件失败',
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* 批量编译插件目录中的所有.ts文件
|
|
110
|
-
*/
|
|
111
|
-
async function compilePluginsDirectory(pluginsDir) {
|
|
112
|
-
const result = {
|
|
113
|
-
total: 0,
|
|
114
|
-
success: 0,
|
|
115
|
-
failed: 0,
|
|
116
|
-
errors: [],
|
|
117
|
-
};
|
|
118
|
-
try {
|
|
119
|
-
if (!fs.existsSync(pluginsDir)) {
|
|
120
|
-
return result;
|
|
121
|
-
}
|
|
122
|
-
const files = fs.readdirSync(pluginsDir);
|
|
123
|
-
const tsFiles = files.filter(file => file.endsWith('.ts'));
|
|
124
|
-
result.total = tsFiles.length;
|
|
125
|
-
for (const file of tsFiles) {
|
|
126
|
-
const filePath = path.join(pluginsDir, file);
|
|
127
|
-
const compileResult = await compilePluginFile(filePath);
|
|
128
|
-
if (compileResult.success) {
|
|
129
|
-
result.success++;
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
result.failed++;
|
|
133
|
-
result.errors.push(`${file}: ${compileResult.error}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return result;
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
result.errors.push('目录编译失败: ' + error.message);
|
|
140
|
-
return result;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* 验证编译后的JavaScript代码
|
|
145
|
-
* 检查是否包含必需的plugin导出
|
|
146
|
-
*/
|
|
147
|
-
function validateCompiledPlugin(jsCode) {
|
|
148
|
-
try {
|
|
149
|
-
// 检查是否包含exports.plugin或module.exports.plugin
|
|
150
|
-
if (!jsCode.includes('exports.plugin') && !jsCode.includes('module.exports')) {
|
|
151
|
-
return {
|
|
152
|
-
valid: false,
|
|
153
|
-
error: '编译后的代码没有导出plugin对象',
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
// 检查是否包含manifest
|
|
157
|
-
if (!jsCode.includes('manifest')) {
|
|
158
|
-
return {
|
|
159
|
-
valid: false,
|
|
160
|
-
error: '编译后的代码缺少manifest定义',
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
return { valid: true };
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
return {
|
|
167
|
-
valid: false,
|
|
168
|
-
error: error.message,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
//# sourceMappingURL=plugin-compiler.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-compiler.js","sourceRoot":"","sources":["../../core/plugin-compiler.ts"],"names":[],"mappings":"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"}
|