@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.
Files changed (103) hide show
  1. package/README.md +25 -8
  2. package/bin/pluxx.js +19 -28
  3. package/dist/agents.d.ts +16 -0
  4. package/dist/agents.d.ts.map +1 -0
  5. package/dist/cli/agent.d.ts +62 -0
  6. package/dist/cli/agent.d.ts.map +1 -1
  7. package/dist/cli/doctor.d.ts +2 -0
  8. package/dist/cli/doctor.d.ts.map +1 -1
  9. package/dist/cli/entry.d.ts +2 -0
  10. package/dist/cli/entry.d.ts.map +1 -0
  11. package/dist/cli/index.d.ts +7 -1
  12. package/dist/cli/index.d.ts.map +1 -1
  13. package/dist/cli/index.js +21810 -0
  14. package/dist/cli/init-from-mcp.d.ts +17 -1
  15. package/dist/cli/init-from-mcp.d.ts.map +1 -1
  16. package/dist/cli/install.d.ts +1 -0
  17. package/dist/cli/install.d.ts.map +1 -1
  18. package/dist/cli/lint.d.ts +3 -1
  19. package/dist/cli/lint.d.ts.map +1 -1
  20. package/dist/cli/mcp-proxy.d.ts.map +1 -1
  21. package/dist/cli/migrate.d.ts.map +1 -1
  22. package/dist/cli/primitive-summary.d.ts +14 -0
  23. package/dist/cli/primitive-summary.d.ts.map +1 -0
  24. package/dist/cli/prompt.d.ts +1 -1
  25. package/dist/cli/publish.d.ts +6 -1
  26. package/dist/cli/publish.d.ts.map +1 -1
  27. package/dist/cli/sync-from-mcp.d.ts.map +1 -1
  28. package/dist/cli/verify-install.d.ts +25 -0
  29. package/dist/cli/verify-install.d.ts.map +1 -0
  30. package/dist/commands.d.ts +10 -0
  31. package/dist/commands.d.ts.map +1 -0
  32. package/dist/compiler-intent.d.ts +165 -0
  33. package/dist/compiler-intent.d.ts.map +1 -0
  34. package/dist/config/load.d.ts.map +1 -1
  35. package/dist/delegation.d.ts +11 -0
  36. package/dist/delegation.d.ts.map +1 -0
  37. package/dist/generators/amp/index.d.ts.map +1 -1
  38. package/dist/generators/base.d.ts +5 -0
  39. package/dist/generators/base.d.ts.map +1 -1
  40. package/dist/generators/claude-code/index.d.ts.map +1 -1
  41. package/dist/generators/cline/index.d.ts.map +1 -1
  42. package/dist/generators/codex/index.d.ts +4 -0
  43. package/dist/generators/codex/index.d.ts.map +1 -1
  44. package/dist/generators/cursor/index.d.ts +1 -0
  45. package/dist/generators/cursor/index.d.ts.map +1 -1
  46. package/dist/generators/gemini-cli/index.d.ts.map +1 -1
  47. package/dist/generators/github-copilot/index.d.ts.map +1 -1
  48. package/dist/generators/opencode/index.d.ts +1 -0
  49. package/dist/generators/opencode/index.d.ts.map +1 -1
  50. package/dist/generators/openhands/index.d.ts.map +1 -1
  51. package/dist/generators/roo-code/index.d.ts.map +1 -1
  52. package/dist/generators/shared/claude-family.d.ts.map +1 -1
  53. package/dist/generators/warp/index.d.ts.map +1 -1
  54. package/dist/index.d.ts +4 -1
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +5371 -553
  57. package/dist/schema.d.ts +91 -42
  58. package/dist/schema.d.ts.map +1 -1
  59. package/dist/text-files.d.ts +5 -0
  60. package/dist/text-files.d.ts.map +1 -0
  61. package/dist/validation/platform-rules.d.ts +15 -1
  62. package/dist/validation/platform-rules.d.ts.map +1 -1
  63. package/package.json +15 -13
  64. package/src/cli/agent.ts +0 -1455
  65. package/src/cli/dev.ts +0 -112
  66. package/src/cli/doctor.ts +0 -987
  67. package/src/cli/eval.ts +0 -470
  68. package/src/cli/index.ts +0 -2933
  69. package/src/cli/init-from-mcp.ts +0 -2115
  70. package/src/cli/install.ts +0 -860
  71. package/src/cli/lint.ts +0 -1249
  72. package/src/cli/mcp-proxy.ts +0 -322
  73. package/src/cli/migrate.ts +0 -867
  74. package/src/cli/prompt.ts +0 -82
  75. package/src/cli/publish.ts +0 -401
  76. package/src/cli/runtime.ts +0 -86
  77. package/src/cli/sync-from-mcp.ts +0 -586
  78. package/src/cli/test.ts +0 -142
  79. package/src/compatibility/matrix.ts +0 -149
  80. package/src/config/define.ts +0 -20
  81. package/src/config/load.ts +0 -74
  82. package/src/generators/amp/index.ts +0 -63
  83. package/src/generators/base.ts +0 -188
  84. package/src/generators/claude-code/index.ts +0 -172
  85. package/src/generators/cline/index.ts +0 -35
  86. package/src/generators/codex/index.ts +0 -143
  87. package/src/generators/cursor/index.ts +0 -158
  88. package/src/generators/gemini-cli/index.ts +0 -83
  89. package/src/generators/github-copilot/index.ts +0 -32
  90. package/src/generators/hooks-warning.ts +0 -51
  91. package/src/generators/index.ts +0 -71
  92. package/src/generators/opencode/index.ts +0 -526
  93. package/src/generators/openhands/index.ts +0 -32
  94. package/src/generators/roo-code/index.ts +0 -35
  95. package/src/generators/shared/claude-family.ts +0 -215
  96. package/src/generators/warp/index.ts +0 -32
  97. package/src/hook-events.ts +0 -33
  98. package/src/index.ts +0 -34
  99. package/src/mcp/introspect.ts +0 -1107
  100. package/src/permissions.ts +0 -260
  101. package/src/schema.ts +0 -312
  102. package/src/user-config.ts +0 -177
  103. package/src/validation/platform-rules.ts +0 -686
@@ -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
- }