@orchid-labs/pluxx 0.1.1 → 0.1.4
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 +25 -8
- package/bin/pluxx.js +19 -28
- package/dist/agents.d.ts +16 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/cli/agent.d.ts +62 -0
- package/dist/cli/agent.d.ts.map +1 -1
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/entry.d.ts +2 -0
- package/dist/cli/entry.d.ts.map +1 -0
- package/dist/cli/index.d.ts +7 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +21810 -0
- package/dist/cli/init-from-mcp.d.ts +17 -1
- package/dist/cli/init-from-mcp.d.ts.map +1 -1
- package/dist/cli/install.d.ts +1 -0
- package/dist/cli/install.d.ts.map +1 -1
- package/dist/cli/lint.d.ts +3 -1
- package/dist/cli/lint.d.ts.map +1 -1
- package/dist/cli/mcp-proxy.d.ts.map +1 -1
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/primitive-summary.d.ts +14 -0
- package/dist/cli/primitive-summary.d.ts.map +1 -0
- package/dist/cli/prompt.d.ts +1 -1
- package/dist/cli/publish.d.ts +6 -1
- package/dist/cli/publish.d.ts.map +1 -1
- package/dist/cli/sync-from-mcp.d.ts.map +1 -1
- package/dist/cli/verify-install.d.ts +25 -0
- package/dist/cli/verify-install.d.ts.map +1 -0
- package/dist/commands.d.ts +10 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/compiler-intent.d.ts +165 -0
- package/dist/compiler-intent.d.ts.map +1 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/delegation.d.ts +11 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/generators/amp/index.d.ts.map +1 -1
- package/dist/generators/base.d.ts +5 -0
- package/dist/generators/base.d.ts.map +1 -1
- package/dist/generators/claude-code/index.d.ts.map +1 -1
- package/dist/generators/cline/index.d.ts.map +1 -1
- package/dist/generators/codex/index.d.ts +4 -0
- package/dist/generators/codex/index.d.ts.map +1 -1
- package/dist/generators/cursor/index.d.ts +1 -0
- package/dist/generators/cursor/index.d.ts.map +1 -1
- package/dist/generators/gemini-cli/index.d.ts.map +1 -1
- package/dist/generators/github-copilot/index.d.ts.map +1 -1
- package/dist/generators/opencode/index.d.ts +1 -0
- package/dist/generators/opencode/index.d.ts.map +1 -1
- package/dist/generators/openhands/index.d.ts.map +1 -1
- package/dist/generators/roo-code/index.d.ts.map +1 -1
- package/dist/generators/shared/claude-family.d.ts.map +1 -1
- package/dist/generators/warp/index.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5371 -553
- package/dist/schema.d.ts +91 -42
- package/dist/schema.d.ts.map +1 -1
- package/dist/text-files.d.ts +5 -0
- package/dist/text-files.d.ts.map +1 -0
- package/dist/validation/platform-rules.d.ts +15 -1
- package/dist/validation/platform-rules.d.ts.map +1 -1
- package/package.json +15 -13
- package/src/cli/agent.ts +0 -1455
- package/src/cli/dev.ts +0 -112
- package/src/cli/doctor.ts +0 -987
- package/src/cli/eval.ts +0 -470
- package/src/cli/index.ts +0 -2933
- package/src/cli/init-from-mcp.ts +0 -2115
- package/src/cli/install.ts +0 -860
- package/src/cli/lint.ts +0 -1249
- package/src/cli/mcp-proxy.ts +0 -322
- package/src/cli/migrate.ts +0 -867
- package/src/cli/prompt.ts +0 -82
- package/src/cli/publish.ts +0 -401
- package/src/cli/runtime.ts +0 -86
- package/src/cli/sync-from-mcp.ts +0 -586
- package/src/cli/test.ts +0 -142
- package/src/compatibility/matrix.ts +0 -149
- package/src/config/define.ts +0 -20
- package/src/config/load.ts +0 -74
- package/src/generators/amp/index.ts +0 -63
- package/src/generators/base.ts +0 -188
- package/src/generators/claude-code/index.ts +0 -172
- package/src/generators/cline/index.ts +0 -35
- package/src/generators/codex/index.ts +0 -143
- package/src/generators/cursor/index.ts +0 -158
- package/src/generators/gemini-cli/index.ts +0 -83
- package/src/generators/github-copilot/index.ts +0 -32
- package/src/generators/hooks-warning.ts +0 -51
- package/src/generators/index.ts +0 -71
- package/src/generators/opencode/index.ts +0 -526
- package/src/generators/openhands/index.ts +0 -32
- package/src/generators/roo-code/index.ts +0 -35
- package/src/generators/shared/claude-family.ts +0 -215
- package/src/generators/warp/index.ts +0 -32
- package/src/hook-events.ts +0 -33
- package/src/index.ts +0 -34
- package/src/mcp/introspect.ts +0 -1107
- package/src/permissions.ts +0 -260
- package/src/schema.ts +0 -312
- package/src/user-config.ts +0 -177
- package/src/validation/platform-rules.ts +0 -686
package/src/cli/mcp-proxy.ts
DELETED
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync } from 'fs'
|
|
2
|
-
import { dirname, resolve } from 'path'
|
|
3
|
-
import * as readline from 'readline'
|
|
4
|
-
import type { Readable, Writable } from 'stream'
|
|
5
|
-
import { createMcpClient, McpIntrospectionError, type McpClient } from '../mcp/introspect'
|
|
6
|
-
import { parseMcpSourceInput } from './init-from-mcp'
|
|
7
|
-
|
|
8
|
-
interface ProxyTapeInteraction {
|
|
9
|
-
kind: 'request' | 'notify'
|
|
10
|
-
method: string
|
|
11
|
-
params?: Record<string, unknown>
|
|
12
|
-
result?: unknown
|
|
13
|
-
error?: {
|
|
14
|
-
code: number
|
|
15
|
-
message: string
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface ProxyTape {
|
|
20
|
-
version: 1
|
|
21
|
-
source?: string
|
|
22
|
-
interactions: ProxyTapeInteraction[]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface McpProxyOptions {
|
|
26
|
-
source?: string
|
|
27
|
-
recordPath?: string
|
|
28
|
-
replayPath?: string
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface ProxyIo {
|
|
32
|
-
input: Readable
|
|
33
|
-
output: Writable
|
|
34
|
-
error: Writable
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function usage(): string {
|
|
38
|
-
return [
|
|
39
|
-
'Usage: pluxx mcp proxy --from-mcp <source> [--record <tape.json>]',
|
|
40
|
-
' pluxx mcp proxy --replay <tape.json>',
|
|
41
|
-
'',
|
|
42
|
-
'Acts as a local stdio MCP proxy for development and CI.',
|
|
43
|
-
'- --record stores normalized request/response interactions as a replay tape.',
|
|
44
|
-
'- --replay serves a deterministic stdio MCP session from a recorded tape.',
|
|
45
|
-
].join('\n')
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function readOption(rawArgs: string[], flag: string): string | undefined {
|
|
49
|
-
const index = rawArgs.indexOf(flag)
|
|
50
|
-
if (index === -1) return undefined
|
|
51
|
-
|
|
52
|
-
const value = rawArgs[index + 1]
|
|
53
|
-
if (!value || value.startsWith('-')) {
|
|
54
|
-
return undefined
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return value
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function parseOptions(rawArgs: string[]): McpProxyOptions {
|
|
61
|
-
const source = readOption(rawArgs, '--from-mcp')
|
|
62
|
-
const recordPath = readOption(rawArgs, '--record')
|
|
63
|
-
const replayPath = readOption(rawArgs, '--replay')
|
|
64
|
-
|
|
65
|
-
if (recordPath && replayPath) {
|
|
66
|
-
throw new Error('Choose either --record or --replay, not both.')
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!source && !replayPath) {
|
|
70
|
-
throw new Error('Expected --from-mcp <source> for live proxying, or --replay <tape.json>.')
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if ((recordPath || source) && replayPath && source) {
|
|
74
|
-
throw new Error('Replay mode does not accept --from-mcp.')
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
source,
|
|
79
|
-
recordPath,
|
|
80
|
-
replayPath,
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function stableStringify(value: unknown): string {
|
|
85
|
-
if (value === null || typeof value !== 'object') {
|
|
86
|
-
return JSON.stringify(value)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (Array.isArray(value)) {
|
|
90
|
-
return `[${value.map((entry) => stableStringify(entry)).join(',')}]`
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const entries = Object.entries(value as Record<string, unknown>)
|
|
94
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
95
|
-
.map(([key, nested]) => `${JSON.stringify(key)}:${stableStringify(nested)}`)
|
|
96
|
-
return `{${entries.join(',')}}`
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function sameParams(
|
|
100
|
-
left: Record<string, unknown> | undefined,
|
|
101
|
-
right: Record<string, unknown> | undefined,
|
|
102
|
-
): boolean {
|
|
103
|
-
return stableStringify(left ?? null) === stableStringify(right ?? null)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function sendEnvelope(output: Writable, envelope: Record<string, unknown>): void {
|
|
107
|
-
output.write(`${JSON.stringify(envelope)}\n`)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function sendError(output: Writable, id: number | string | null, code: number, message: string): void {
|
|
111
|
-
sendEnvelope(output, {
|
|
112
|
-
jsonrpc: '2.0',
|
|
113
|
-
id,
|
|
114
|
-
error: {
|
|
115
|
-
code,
|
|
116
|
-
message,
|
|
117
|
-
},
|
|
118
|
-
})
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async function loadReplayTape(filepath: string): Promise<ProxyTape> {
|
|
122
|
-
const absolutePath = resolve(process.cwd(), filepath)
|
|
123
|
-
const tape = JSON.parse(readFileSync(absolutePath, 'utf-8')) as ProxyTape
|
|
124
|
-
if (tape.version !== 1 || !Array.isArray(tape.interactions)) {
|
|
125
|
-
throw new Error(`Replay tape is not a valid pluxx MCP tape: ${filepath}`)
|
|
126
|
-
}
|
|
127
|
-
return tape
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function writeTape(filepath: string, tape: ProxyTape): Promise<void> {
|
|
131
|
-
const absolutePath = resolve(process.cwd(), filepath)
|
|
132
|
-
mkdirSync(dirname(absolutePath), { recursive: true })
|
|
133
|
-
await Bun.write(absolutePath, `${JSON.stringify(tape, null, 2)}\n`)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function serializeError(error: unknown): { code: number; message: string } {
|
|
137
|
-
if (error instanceof McpIntrospectionError) {
|
|
138
|
-
return {
|
|
139
|
-
code: error.rpcCode ?? -32000,
|
|
140
|
-
message: error.message,
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return {
|
|
145
|
-
code: -32000,
|
|
146
|
-
message: error instanceof Error ? error.message : String(error),
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function proxyLiveSession(client: McpClient, options: McpProxyOptions, io: ProxyIo): Promise<void> {
|
|
151
|
-
const tape: ProxyTape | null = options.recordPath
|
|
152
|
-
? {
|
|
153
|
-
version: 1,
|
|
154
|
-
source: options.source,
|
|
155
|
-
interactions: [],
|
|
156
|
-
}
|
|
157
|
-
: null
|
|
158
|
-
|
|
159
|
-
const rl = readline.createInterface({
|
|
160
|
-
input: io.input,
|
|
161
|
-
crlfDelay: Infinity,
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
for await (const line of rl) {
|
|
166
|
-
if (!line.trim()) continue
|
|
167
|
-
|
|
168
|
-
let message: Record<string, unknown>
|
|
169
|
-
try {
|
|
170
|
-
message = JSON.parse(line) as Record<string, unknown>
|
|
171
|
-
} catch {
|
|
172
|
-
continue
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const method = typeof message.method === 'string' ? message.method : undefined
|
|
176
|
-
if (!method) continue
|
|
177
|
-
|
|
178
|
-
const params = (typeof message.params === 'object' && message.params !== null)
|
|
179
|
-
? message.params as Record<string, unknown>
|
|
180
|
-
: undefined
|
|
181
|
-
const id = typeof message.id === 'number' || typeof message.id === 'string'
|
|
182
|
-
? message.id
|
|
183
|
-
: null
|
|
184
|
-
|
|
185
|
-
if (id === null) {
|
|
186
|
-
await client.notify(method, params)
|
|
187
|
-
tape?.interactions.push({
|
|
188
|
-
kind: 'notify',
|
|
189
|
-
method,
|
|
190
|
-
...(params ? { params } : {}),
|
|
191
|
-
})
|
|
192
|
-
continue
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
const result = await client.request<unknown>(method, params)
|
|
197
|
-
sendEnvelope(io.output, {
|
|
198
|
-
jsonrpc: '2.0',
|
|
199
|
-
id,
|
|
200
|
-
result,
|
|
201
|
-
})
|
|
202
|
-
tape?.interactions.push({
|
|
203
|
-
kind: 'request',
|
|
204
|
-
method,
|
|
205
|
-
...(params ? { params } : {}),
|
|
206
|
-
result,
|
|
207
|
-
})
|
|
208
|
-
} catch (error) {
|
|
209
|
-
const serialized = serializeError(error)
|
|
210
|
-
sendError(io.output, id, serialized.code, serialized.message)
|
|
211
|
-
tape?.interactions.push({
|
|
212
|
-
kind: 'request',
|
|
213
|
-
method,
|
|
214
|
-
...(params ? { params } : {}),
|
|
215
|
-
error: serialized,
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
} finally {
|
|
220
|
-
rl.close()
|
|
221
|
-
await client.close()
|
|
222
|
-
if (tape && options.recordPath) {
|
|
223
|
-
await writeTape(options.recordPath, tape)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async function replaySession(filepath: string, io: ProxyIo): Promise<void> {
|
|
229
|
-
const tape = await loadReplayTape(filepath)
|
|
230
|
-
const interactions = [...tape.interactions]
|
|
231
|
-
const rl = readline.createInterface({
|
|
232
|
-
input: io.input,
|
|
233
|
-
crlfDelay: Infinity,
|
|
234
|
-
})
|
|
235
|
-
|
|
236
|
-
try {
|
|
237
|
-
for await (const line of rl) {
|
|
238
|
-
if (!line.trim()) continue
|
|
239
|
-
|
|
240
|
-
let message: Record<string, unknown>
|
|
241
|
-
try {
|
|
242
|
-
message = JSON.parse(line) as Record<string, unknown>
|
|
243
|
-
} catch {
|
|
244
|
-
continue
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const method = typeof message.method === 'string' ? message.method : undefined
|
|
248
|
-
if (!method) continue
|
|
249
|
-
|
|
250
|
-
const params = (typeof message.params === 'object' && message.params !== null)
|
|
251
|
-
? message.params as Record<string, unknown>
|
|
252
|
-
: undefined
|
|
253
|
-
const id = typeof message.id === 'number' || typeof message.id === 'string'
|
|
254
|
-
? message.id
|
|
255
|
-
: null
|
|
256
|
-
const expected = interactions.shift()
|
|
257
|
-
|
|
258
|
-
if (!expected) {
|
|
259
|
-
if (id !== null) {
|
|
260
|
-
sendError(io.output, id, -32001, `Replay tape exhausted before handling ${method}.`)
|
|
261
|
-
}
|
|
262
|
-
continue
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (expected.kind !== (id === null ? 'notify' : 'request') || expected.method !== method || !sameParams(expected.params, params)) {
|
|
266
|
-
if (id !== null) {
|
|
267
|
-
sendError(
|
|
268
|
-
io.output,
|
|
269
|
-
id,
|
|
270
|
-
-32002,
|
|
271
|
-
`Replay mismatch. Expected ${expected.kind} ${expected.method}, received ${id === null ? 'notify' : 'request'} ${method}.`,
|
|
272
|
-
)
|
|
273
|
-
}
|
|
274
|
-
continue
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (id === null) {
|
|
278
|
-
continue
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if (expected.error) {
|
|
282
|
-
sendError(io.output, id, expected.error.code, expected.error.message)
|
|
283
|
-
continue
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
sendEnvelope(io.output, {
|
|
287
|
-
jsonrpc: '2.0',
|
|
288
|
-
id,
|
|
289
|
-
result: expected.result ?? null,
|
|
290
|
-
})
|
|
291
|
-
}
|
|
292
|
-
} finally {
|
|
293
|
-
rl.close()
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export async function runMcpProxy(rawArgs: string[]): Promise<void> {
|
|
298
|
-
return await runMcpProxyWithIo(rawArgs, {
|
|
299
|
-
input: process.stdin,
|
|
300
|
-
output: process.stdout,
|
|
301
|
-
error: process.stderr,
|
|
302
|
-
})
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
export async function runMcpProxyWithIo(rawArgs: string[], io: ProxyIo): Promise<void> {
|
|
306
|
-
let options: McpProxyOptions
|
|
307
|
-
try {
|
|
308
|
-
options = parseOptions(rawArgs)
|
|
309
|
-
} catch (error) {
|
|
310
|
-
io.error.write(`${error instanceof Error ? error.message : String(error)}\n\n${usage()}\n`)
|
|
311
|
-
throw new Error('Invalid MCP proxy arguments.')
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (options.replayPath) {
|
|
315
|
-
await replaySession(options.replayPath, io)
|
|
316
|
-
return
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
const source = parseMcpSourceInput(options.source!)
|
|
320
|
-
const client = await createMcpClient(source)
|
|
321
|
-
await proxyLiveSession(client, options, io)
|
|
322
|
-
}
|