@adyingdeath/meatproxy 0.0.0-stage → 1.0.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/LICENSE +7 -0
- package/README.md +47 -2
- package/dist/index.js +330 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 adyingdeath
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,48 @@
|
|
|
1
|
-
#
|
|
1
|
+
# MeatProxy
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> A meat proxy is a person who acts as an unthinking relay for AI-generated text, code, or other outputs by copying and pasting them to others without reading, validating, or adding personal insight.
|
|
4
|
+
|
|
5
|
+
MeatProxy helps you be a better meat proxy.
|
|
6
|
+
|
|
7
|
+
It is a small bridge between your clipboard and chatbots: paste the prompt into the chat, ask for a change, copy the reply, then run `mtp` in the terminal. The code runs against your project, and everything it prints is copied back to the clipboard, ready for the next round.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Node.js 22.12 or newer
|
|
12
|
+
- PowerShell (`pwsh` or Windows PowerShell) if you use the `pwsh` tool
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install -g meatproxy
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This provides the `mtp` command (also available as `meatproxy`).
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
# 1. copy the agent prompt to the clipboard
|
|
26
|
+
mtp init
|
|
27
|
+
|
|
28
|
+
# 2. paste it into a web chatbot and say what you want
|
|
29
|
+
|
|
30
|
+
# 3. copy the chatbot's reply, then run
|
|
31
|
+
mtp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`mtp init` copies the prompt and prints what to do next. `mtp` reads the clipboard, extracts the snippet, runs it, prints the output, and copies that output back to the clipboard.
|
|
35
|
+
|
|
36
|
+
The snippet runs with the same access as your terminal, so only paste replies you trust.
|
|
37
|
+
|
|
38
|
+
## Tools
|
|
39
|
+
|
|
40
|
+
The snippet gets a `tools` object with four methods: `read`, `write`, `edit` and `pwsh`, enough to inspect files, change them, and run PowerShell commands.
|
|
41
|
+
|
|
42
|
+
## Development
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
npm install
|
|
46
|
+
npm run build # bundles to dist/
|
|
47
|
+
npm run dev # runs src/index.ts directly
|
|
48
|
+
```
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/core.ts
|
|
7
|
+
import clipboard from "clipboardy";
|
|
8
|
+
|
|
9
|
+
// src/console.ts
|
|
10
|
+
import { format } from "util";
|
|
11
|
+
var OutputConsole = class {
|
|
12
|
+
/** Everything written so far, one entry per console call. */
|
|
13
|
+
lines = [];
|
|
14
|
+
log(...args) {
|
|
15
|
+
console.log(...args);
|
|
16
|
+
this.lines.push(format(...args));
|
|
17
|
+
}
|
|
18
|
+
info(...args) {
|
|
19
|
+
console.info(...args);
|
|
20
|
+
this.lines.push(format(...args));
|
|
21
|
+
}
|
|
22
|
+
warn(...args) {
|
|
23
|
+
console.warn(...args);
|
|
24
|
+
this.lines.push(format(...args));
|
|
25
|
+
}
|
|
26
|
+
error(...args) {
|
|
27
|
+
console.error(...args);
|
|
28
|
+
this.lines.push(format(...args));
|
|
29
|
+
}
|
|
30
|
+
debug(...args) {
|
|
31
|
+
console.debug(...args);
|
|
32
|
+
this.lines.push(format(...args));
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// src/tools.ts
|
|
37
|
+
import { spawnSync } from "child_process";
|
|
38
|
+
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
39
|
+
import path from "path";
|
|
40
|
+
var DEFAULT_PWSH_TIMEOUT = 12e4;
|
|
41
|
+
var MAX_PWSH_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
42
|
+
var PWSH_PRELUDE = [
|
|
43
|
+
"$ProgressPreference = 'SilentlyContinue'",
|
|
44
|
+
"try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) } catch {}"
|
|
45
|
+
].join("\n");
|
|
46
|
+
var CLIXML_ERROR_PATTERN = /<S S="Error">([\s\S]*?)<\/S>/g;
|
|
47
|
+
function unescapeClixml(text) {
|
|
48
|
+
return text.replace(/_x([0-9a-f]{4})_/gi, (_match, code) => String.fromCharCode(Number.parseInt(code, 16))).replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
49
|
+
}
|
|
50
|
+
function decodePowerShellStderr(stderr) {
|
|
51
|
+
if (!stderr.startsWith("#< CLIXML")) {
|
|
52
|
+
return stderr;
|
|
53
|
+
}
|
|
54
|
+
const messages = Array.from(
|
|
55
|
+
stderr.matchAll(CLIXML_ERROR_PATTERN),
|
|
56
|
+
(match) => unescapeClixml(match[1] ?? "")
|
|
57
|
+
);
|
|
58
|
+
return messages.length > 0 ? messages.join("") : stderr;
|
|
59
|
+
}
|
|
60
|
+
function shellCandidates() {
|
|
61
|
+
return process.platform === "win32" ? ["pwsh", "powershell.exe"] : ["pwsh"];
|
|
62
|
+
}
|
|
63
|
+
function describeError(error) {
|
|
64
|
+
return error instanceof Error ? error.message : String(error);
|
|
65
|
+
}
|
|
66
|
+
function requirePath(value, method) {
|
|
67
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
68
|
+
throw new TypeError(`${method} failed: file_path must be a non-empty string.`);
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
function requireText(value, method, field) {
|
|
73
|
+
if (typeof value !== "string") {
|
|
74
|
+
throw new TypeError(`${method} failed: ${field} must be a string.`);
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
function countOccurrences(text, search) {
|
|
79
|
+
let count = 0;
|
|
80
|
+
let index = text.indexOf(search);
|
|
81
|
+
while (index !== -1) {
|
|
82
|
+
count += 1;
|
|
83
|
+
index = text.indexOf(search, index + search.length);
|
|
84
|
+
}
|
|
85
|
+
return count;
|
|
86
|
+
}
|
|
87
|
+
function replaceFirst(text, search, replacement) {
|
|
88
|
+
const index = text.indexOf(search);
|
|
89
|
+
if (index === -1) {
|
|
90
|
+
return text;
|
|
91
|
+
}
|
|
92
|
+
return text.slice(0, index) + replacement + text.slice(index + search.length);
|
|
93
|
+
}
|
|
94
|
+
var Tools = class {
|
|
95
|
+
root;
|
|
96
|
+
constructor(root = process.cwd()) {
|
|
97
|
+
this.root = path.resolve(root);
|
|
98
|
+
}
|
|
99
|
+
/** Read a UTF-8 text file and return its full contents. */
|
|
100
|
+
read(request) {
|
|
101
|
+
const filePath = requirePath(request?.file_path, "read");
|
|
102
|
+
const target = this.resolve(filePath);
|
|
103
|
+
try {
|
|
104
|
+
return readFileSync(target, "utf8");
|
|
105
|
+
} catch (error) {
|
|
106
|
+
throw new Error(`Read failed for ${filePath}: ${describeError(error)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Create or fully overwrite a UTF-8 text file, creating parent directories. */
|
|
110
|
+
write(request) {
|
|
111
|
+
const filePath = requirePath(request?.file_path, "write");
|
|
112
|
+
const content = requireText(request?.content, "write", "content");
|
|
113
|
+
const target = this.resolve(filePath);
|
|
114
|
+
try {
|
|
115
|
+
mkdirSync(path.dirname(target), { recursive: true });
|
|
116
|
+
writeFileSync(target, content, "utf8");
|
|
117
|
+
} catch (error) {
|
|
118
|
+
throw new Error(`Write failed for ${filePath}: ${describeError(error)}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Replace literal text in a file.
|
|
123
|
+
*
|
|
124
|
+
* `old_string` has to be unique unless `replace_all` is true, and an empty
|
|
125
|
+
* `old_string` is rejected because it would match everywhere.
|
|
126
|
+
*/
|
|
127
|
+
edit(request) {
|
|
128
|
+
const filePath = requirePath(request?.file_path, "edit");
|
|
129
|
+
const oldText = requireText(request?.old_string, "edit", "old_string");
|
|
130
|
+
const newText = requireText(request?.new_string, "edit", "new_string");
|
|
131
|
+
if (oldText.length === 0) {
|
|
132
|
+
throw new Error("Edit failed: old_string must not be empty.");
|
|
133
|
+
}
|
|
134
|
+
const original = this.read({ file_path: filePath });
|
|
135
|
+
const occurrences = countOccurrences(original, oldText);
|
|
136
|
+
if (occurrences === 0) {
|
|
137
|
+
throw new Error(`Edit failed for ${filePath}: old_string was not found.`);
|
|
138
|
+
}
|
|
139
|
+
if (occurrences > 1 && !request.replace_all) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Edit failed for ${filePath}: old_string appears ${occurrences} times. Make it unique or pass replace_all = true.`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const updated = request.replace_all ? original.split(oldText).join(newText) : replaceFirst(original, oldText, newText);
|
|
145
|
+
this.write({ file_path: filePath, content: updated });
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Run a PowerShell command synchronously and return its output.
|
|
149
|
+
*
|
|
150
|
+
* A non-zero `exitCode` is returned instead of thrown, so the code can inspect
|
|
151
|
+
* `stdout` and `stderr` itself. The command runs with `pwsh` when it is
|
|
152
|
+
* available and falls back to `powershell.exe` on Windows.
|
|
153
|
+
*/
|
|
154
|
+
pwsh(request) {
|
|
155
|
+
const command = requireText(request?.command, "pwsh", "command");
|
|
156
|
+
const timeout = request.timeoutMs ?? DEFAULT_PWSH_TIMEOUT;
|
|
157
|
+
const cwd = request.cwd ? this.resolve(request.cwd) : this.root;
|
|
158
|
+
const encoded = Buffer.from(`${PWSH_PRELUDE}
|
|
159
|
+
${command}`, "utf16le").toString("base64");
|
|
160
|
+
for (const shell of shellCandidates()) {
|
|
161
|
+
const result = spawnSync(
|
|
162
|
+
shell,
|
|
163
|
+
["-NoProfile", "-NonInteractive", "-EncodedCommand", encoded],
|
|
164
|
+
{
|
|
165
|
+
cwd,
|
|
166
|
+
encoding: "utf8",
|
|
167
|
+
maxBuffer: MAX_PWSH_OUTPUT_BYTES,
|
|
168
|
+
timeout,
|
|
169
|
+
windowsHide: true
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
if (result.error) {
|
|
173
|
+
const code = result.error.code;
|
|
174
|
+
if (code === "ENOENT") {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (code === "ETIMEDOUT") {
|
|
178
|
+
throw new Error(`Pwsh timed out after ${timeout} ms.`);
|
|
179
|
+
}
|
|
180
|
+
throw new Error(`Pwsh failed: ${result.error.message}`);
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
stdout: result.stdout,
|
|
184
|
+
stderr: decodePowerShellStderr(result.stderr),
|
|
185
|
+
exitCode: result.status
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
throw new Error("Pwsh failed: PowerShell is not available on this machine.");
|
|
189
|
+
}
|
|
190
|
+
resolve(filePath) {
|
|
191
|
+
return path.resolve(this.root, filePath);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// src/core.ts
|
|
196
|
+
var HEAD_MARKER = "<<<<<<< MEATPROXY";
|
|
197
|
+
var TAIL_MARKER = ">>>>>>> MEATPROXY";
|
|
198
|
+
var INVALID_MARKER_MESSAGE = `Error: Invalid format.
|
|
199
|
+
Expected format:
|
|
200
|
+
${HEAD_MARKER}
|
|
201
|
+
...code...
|
|
202
|
+
${TAIL_MARKER}`;
|
|
203
|
+
function extractCodeBlock(text) {
|
|
204
|
+
const lines = text.split(/\r?\n/);
|
|
205
|
+
const heads = [];
|
|
206
|
+
const tails = [];
|
|
207
|
+
lines.forEach((line, index) => {
|
|
208
|
+
const trimmed = line.trim();
|
|
209
|
+
if (trimmed === HEAD_MARKER) {
|
|
210
|
+
heads.push(index);
|
|
211
|
+
} else if (trimmed === TAIL_MARKER) {
|
|
212
|
+
tails.push(index);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
if (heads.length === 0 && tails.length === 0) {
|
|
216
|
+
console.log(INVALID_MARKER_MESSAGE);
|
|
217
|
+
return "";
|
|
218
|
+
}
|
|
219
|
+
if (heads.length > 1 || tails.length > 1) {
|
|
220
|
+
console.log("Error: Found more than one pair of MEATPROXY markers.");
|
|
221
|
+
return "";
|
|
222
|
+
}
|
|
223
|
+
const head = heads[0];
|
|
224
|
+
const tail = tails[0];
|
|
225
|
+
if (head === void 0) {
|
|
226
|
+
console.log(INVALID_MARKER_MESSAGE);
|
|
227
|
+
return "";
|
|
228
|
+
}
|
|
229
|
+
if (tail === void 0) {
|
|
230
|
+
console.log(INVALID_MARKER_MESSAGE);
|
|
231
|
+
return "";
|
|
232
|
+
}
|
|
233
|
+
if (tail < head) {
|
|
234
|
+
console.log(INVALID_MARKER_MESSAGE);
|
|
235
|
+
return "";
|
|
236
|
+
}
|
|
237
|
+
const code = lines.slice(head + 1, tail).join("\n").trim();
|
|
238
|
+
if (code.length === 0) {
|
|
239
|
+
console.log("No code. Nothing happens.");
|
|
240
|
+
return "";
|
|
241
|
+
}
|
|
242
|
+
return code;
|
|
243
|
+
}
|
|
244
|
+
var AsyncFunction = Object.getPrototypeOf(async function() {
|
|
245
|
+
}).constructor;
|
|
246
|
+
async function run() {
|
|
247
|
+
let text;
|
|
248
|
+
try {
|
|
249
|
+
text = await clipboard.read();
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.log("could not read the clipboard");
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const code = extractCodeBlock(text);
|
|
255
|
+
if (code.length === 0) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const captured = new OutputConsole();
|
|
259
|
+
try {
|
|
260
|
+
const func = new AsyncFunction("tools", "console", code);
|
|
261
|
+
await func(new Tools(), captured);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
captured.log(error);
|
|
264
|
+
}
|
|
265
|
+
if (captured.lines.length === 0) {
|
|
266
|
+
console.log("No output to copy.");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
await clipboard.write(captured.lines.join("\n"));
|
|
271
|
+
} catch (error) {
|
|
272
|
+
console.log("could not write to the clipboard");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
console.log("Output copied to the clipboard. Paste it back to the agent.");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/commands/init.ts
|
|
279
|
+
import clipboard2 from "clipboardy";
|
|
280
|
+
|
|
281
|
+
// src/prompt.ts
|
|
282
|
+
var INIT_PROMPT = [
|
|
283
|
+
"You help through MeatProxy: the user pastes your replies into a terminal, which runs the JavaScript you send locally.",
|
|
284
|
+
"",
|
|
285
|
+
"To inspect or edit files, reply with one snippet shaped like this:",
|
|
286
|
+
"",
|
|
287
|
+
"```js",
|
|
288
|
+
"<<<<<<< MEATPROXY",
|
|
289
|
+
"// your JavaScript here",
|
|
290
|
+
">>>>>>> MEATPROXY",
|
|
291
|
+
"```",
|
|
292
|
+
"",
|
|
293
|
+
"Keep the marker lines exactly as shown, each on its own line, inside the code block. The terminal reads everything between them and ignores the rest of your reply.",
|
|
294
|
+
"",
|
|
295
|
+
"Guidelines:",
|
|
296
|
+
"- Use relative paths; the working directory is the project root.",
|
|
297
|
+
"- Do one step per snippet: the user runs it, then pastes the output back to you.",
|
|
298
|
+
"- Use console.log for results and progress; everything printed is copied to the clipboard automatically.",
|
|
299
|
+
"- Keep explanations outside the code block.",
|
|
300
|
+
"",
|
|
301
|
+
"The snippet runs in Node with a `tools` object:",
|
|
302
|
+
"tools.read({ file_path }) -> string",
|
|
303
|
+
"tools.write({ file_path, content })",
|
|
304
|
+
"tools.edit({ file_path, old_string, new_string, replace_all? })",
|
|
305
|
+
"tools.pwsh({ command, cwd?, timeoutMs? }) -> { stdout, stderr, exitCode }",
|
|
306
|
+
"",
|
|
307
|
+
""
|
|
308
|
+
].join("\n");
|
|
309
|
+
|
|
310
|
+
// src/commands/init.ts
|
|
311
|
+
function registerInit(program2) {
|
|
312
|
+
program2.command("init").description("Copy the agent prompt to the clipboard").action(async () => {
|
|
313
|
+
try {
|
|
314
|
+
await clipboard2.write(INIT_PROMPT);
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.log("could not write to the clipboard");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
console.log("Prompt copied to the clipboard.");
|
|
320
|
+
console.log("Paste it into a web chatbot, add what you want it to do, and send.");
|
|
321
|
+
console.log("Then copy the chatbot's reply back here and run `mtp`.");
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// src/index.ts
|
|
326
|
+
var program = new Command();
|
|
327
|
+
program.name("mp").description("MeatProxy CLI").version("1.0.0").action(run);
|
|
328
|
+
registerInit(program);
|
|
329
|
+
await program.parseAsync();
|
|
330
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/console.ts","../src/tools.ts","../src/commands/init.ts","../src/prompt.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { run } from \"./core.js\";\nimport { registerInit } from \"./commands/init.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"mp\")\n .description(\"MeatProxy CLI\")\n .version(\"1.0.0\")\n .action(run);\n\nregisterInit(program);\n\nawait program.parseAsync();\n","import clipboard from \"clipboardy\";\r\nimport { OutputConsole } from \"./console.js\";\r\nimport { Tools } from \"./tools.js\";\r\n\r\nexport const HEAD_MARKER = \"<<<<<<< MEATPROXY\";\r\nexport const TAIL_MARKER = \">>>>>>> MEATPROXY\";\r\n\r\nconst INVALID_MARKER_MESSAGE = `Error: Invalid format.\r\nExpected format:\r\n${HEAD_MARKER}\r\n...code...\r\n${TAIL_MARKER}`\r\n\r\n/**\r\n * Extract the code between the MEATPROXY markers.\r\n *\r\n * Exactly one head marker and one tail marker are expected, with the tail after\r\n * the head.\r\n */\r\nexport function extractCodeBlock(text: string): string {\r\n const lines = text.split(/\\r?\\n/);\r\n const heads: number[] = [];\r\n const tails: number[] = [];\r\n\r\n lines.forEach((line, index) => {\r\n const trimmed = line.trim();\r\n if (trimmed === HEAD_MARKER) {\r\n heads.push(index);\r\n } else if (trimmed === TAIL_MARKER) {\r\n tails.push(index);\r\n }\r\n });\r\n\r\n if (heads.length === 0 && tails.length === 0) {\r\n console.log(INVALID_MARKER_MESSAGE);\r\n return \"\";\r\n }\r\n\r\n if (heads.length > 1 || tails.length > 1) {\r\n console.log(\"Error: Found more than one pair of MEATPROXY markers.\");\r\n return \"\";\r\n }\r\n\r\n const head = heads[0];\r\n const tail = tails[0];\r\n\r\n if (head === undefined) {\r\n console.log(INVALID_MARKER_MESSAGE);\r\n return \"\";\r\n }\r\n\r\n if (tail === undefined) {\r\n console.log(INVALID_MARKER_MESSAGE);\r\n return \"\";\r\n }\r\n\r\n if (tail < head) {\r\n console.log(INVALID_MARKER_MESSAGE);\r\n return \"\";\r\n }\r\n\r\n const code = lines\r\n .slice(head + 1, tail)\r\n .join(\"\\n\")\r\n .trim();\r\n\r\n if (code.length === 0) {\r\n console.log(\"No code. Nothing happens.\");\r\n return \"\";\r\n }\r\n\r\n return code;\r\n}\r\n\r\n\r\n// Function bodies are normally sync; an async body lets the clipboard code use\r\n// top-level await (for example `await tools.pwsh(...)`).\r\nconst AsyncFunction = Object.getPrototypeOf(async function () {}).constructor as new (\r\n ...args: string[]\r\n) => (...params: unknown[]) => Promise<unknown>;\r\n\r\n/**\r\n * Read the clipboard, pull the MEATPROXY code block out of it, run that code and\r\n * copy whatever it printed back to the clipboard for the next round.\r\n */\r\nexport async function run(): Promise<void> {\r\n let text: string;\r\n\r\n try {\r\n text = await clipboard.read();\r\n } catch (error) {\r\n console.log(\"could not read the clipboard\");\r\n return;\r\n }\r\n\r\n const code = extractCodeBlock(text);\r\n if (code.length === 0) {\r\n return;\r\n }\r\n\r\n const captured = new OutputConsole();\r\n\r\n try {\r\n const func = new AsyncFunction(\"tools\", \"console\", code);\r\n await func(new Tools(), captured);\r\n } catch (error) {\r\n captured.log(error);\r\n }\r\n\r\n if (captured.lines.length === 0) {\r\n console.log(\"No output to copy.\");\r\n return;\r\n }\r\n\r\n try {\r\n await clipboard.write(captured.lines.join(\"\\n\"));\r\n } catch (error) {\r\n console.log(\"could not write to the clipboard\");\r\n return;\r\n }\r\n\r\n console.log(\"Output copied to the clipboard. Paste it back to the agent.\");\r\n}\r\n","import { format } from \"node:util\";\n\n/**\n * Console handed to the clipboard code as the `console` parameter.\n *\n * Every call is forwarded to the real console and also recorded as formatted\n * text, so the output can be copied to the clipboard afterwards. Methods are\n * bound, which keeps `const { log }console` working.\n */\nexport class OutputConsole {\n /** Everything written so far, one entry per console call. */\n readonly lines: string[] = [];\n\n log(...args: unknown[]): void {\n console.log(...args);\n this.lines.push(format(...args));\n };\n\n info(...args: unknown[]): void {\n console.info(...args);\n this.lines.push(format(...args));\n };\n\n warn(...args: unknown[]): void {\n console.warn(...args);\n this.lines.push(format(...args));\n };\n\n error(...args: unknown[]): void {\n console.error(...args);\n this.lines.push(format(...args));\n };\n\n debug(...args: unknown[]): void {\n console.debug(...args);\n this.lines.push(format(...args));\n };\n}\n","import { spawnSync } from \"node:child_process\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport path from \"path\";\n\nexport interface ReadRequest {\n /** Path of the file to read, resolved against the tool root. */\n file_path: string;\n}\n\nexport interface WriteRequest {\n /** Path of the file to write, resolved against the tool root. */\n file_path: string;\n /** Full UTF-8 contents of the file. */\n content: string;\n}\n\nexport interface EditRequest {\n /** Path of the file to edit, resolved against the tool root. */\n file_path: string;\n /** Literal text to look for. It has to be unique unless `replace_all` is set. */\n old_string: string;\n /** Replacement text. An empty string deletes the match. */\n new_string: string;\n /** Replace every occurrence instead of requiring a unique match. */\n replace_all?: boolean;\n}\n\nexport interface PwshRequest {\n /** PowerShell command or script to run. */\n command: string;\n /** Working directory, resolved against the tool root. Defaults to the tool root. */\n cwd?: string;\n /** Timeout in milliseconds. Defaults to 120000. */\n timeoutMs?: number;\n}\n\nexport interface PwshResult {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\nconst DEFAULT_PWSH_TIMEOUT = 120_000;\nconst MAX_PWSH_OUTPUT_BYTES = 16 * 1024 * 1024;\n\n// PowerShell encodes redirected output with the console code page by default, which\n// mangles non-ASCII text, and it reports progress records on stderr. Switch the\n// output to UTF-8 without a BOM and silence progress before running anything.\nconst PWSH_PRELUDE = [\n \"$ProgressPreference = 'SilentlyContinue'\",\n \"try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) } catch {}\",\n].join(\"\\n\");\n\n// Windows PowerShell serializes error records as CLIXML when stderr is redirected.\nconst CLIXML_ERROR_PATTERN = /<S S=\"Error\">([\\s\\S]*?)<\\/S>/g;\n\nfunction unescapeClixml(text: string): string {\n return text\n .replace(/_x([0-9a-f]{4})_/gi, (_match, code: string) => String.fromCharCode(Number.parseInt(code, 16)))\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/&/g, '&');\n}\n\nfunction decodePowerShellStderr(stderr: string): string {\n if (!stderr.startsWith(\"#< CLIXML\")) {\n return stderr;\n }\n\n const messages = Array.from(stderr.matchAll(CLIXML_ERROR_PATTERN), (match) =>\n unescapeClixml(match[1] ?? \"\"),\n );\n\n return messages.length > 0 ? messages.join(\"\") : stderr;\n}\n\nfunction shellCandidates(): string[] {\n // PowerShell 7 first (it supports && and friends), Windows PowerShell as fallback.\n return process.platform === \"win32\" ? [\"pwsh\", \"powershell.exe\"] : [\"pwsh\"];\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction requirePath(value: unknown, method: string): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new TypeError(`${method} failed: file_path must be a non-empty string.`);\n }\n\n return value;\n}\n\nfunction requireText(value: unknown, method: string, field: string): string {\n if (typeof value !== \"string\") {\n throw new TypeError(`${method} failed: ${field} must be a string.`);\n }\n\n return value;\n}\n\nfunction countOccurrences(text: string, search: string): number {\n let count = 0;\n let index = text.indexOf(search);\n\n while (index !== -1) {\n count += 1;\n index = text.indexOf(search, index + search.length);\n }\n\n return count;\n}\n\nfunction replaceFirst(text: string, search: string, replacement: string): string {\n const index = text.indexOf(search);\n if (index === -1) {\n return text;\n }\n\n return text.slice(0, index) + replacement + text.slice(index + search.length);\n}\n\n/**\n * Editing tools handed to the clipboard code, so agent can use this object to\n * do edit.\n *\n * The code is compiled with a single parameter named `tools`, so it can call\n * `tools.read({ file_path })`, `tools.write({ file_path, content })`,\n * `tools.edit({ file_path, old_string, new_string })` and `tools.pwsh({ command })`.\n * All paths are resolved against `root` (the current working directory by default).\n */\nexport class Tools {\n private readonly root: string;\n\n constructor(root: string = process.cwd()) {\n this.root = path.resolve(root);\n }\n\n /** Read a UTF-8 text file and return its full contents. */\n read(request: ReadRequest): string {\n const filePath = requirePath(request?.file_path, \"read\");\n const target = this.resolve(filePath);\n\n try {\n return readFileSync(target, \"utf8\");\n } catch (error) {\n throw new Error(`Read failed for ${filePath}: ${describeError(error)}`);\n }\n }\n\n /** Create or fully overwrite a UTF-8 text file, creating parent directories. */\n write(request: WriteRequest): void {\n const filePath = requirePath(request?.file_path, \"write\");\n const content = requireText(request?.content, \"write\", \"content\");\n const target = this.resolve(filePath);\n\n try {\n mkdirSync(path.dirname(target), { recursive: true });\n writeFileSync(target, content, \"utf8\");\n } catch (error) {\n throw new Error(`Write failed for ${filePath}: ${describeError(error)}`);\n }\n }\n\n /**\n * Replace literal text in a file.\n *\n * `old_string` has to be unique unless `replace_all` is true, and an empty\n * `old_string` is rejected because it would match everywhere.\n */\n edit(request: EditRequest): void {\n const filePath = requirePath(request?.file_path, \"edit\");\n const oldText = requireText(request?.old_string, \"edit\", \"old_string\");\n const newText = requireText(request?.new_string, \"edit\", \"new_string\");\n\n if (oldText.length === 0) {\n throw new Error(\"Edit failed: old_string must not be empty.\");\n }\n\n const original = this.read({ file_path: filePath });\n const occurrences = countOccurrences(original, oldText);\n\n if (occurrences === 0) {\n throw new Error(`Edit failed for ${filePath}: old_string was not found.`);\n }\n\n if (occurrences > 1 && !request.replace_all) {\n throw new Error(\n `Edit failed for ${filePath}: old_string appears ${occurrences} times. ` +\n \"Make it unique or pass replace_all = true.\",\n );\n }\n\n const updated = request.replace_all\n ? original.split(oldText).join(newText)\n : replaceFirst(original, oldText, newText);\n\n this.write({ file_path: filePath, content: updated });\n }\n\n /**\n * Run a PowerShell command synchronously and return its output.\n *\n * A non-zero `exitCode` is returned instead of thrown, so the code can inspect\n * `stdout` and `stderr` itself. The command runs with `pwsh` when it is\n * available and falls back to `powershell.exe` on Windows.\n */\n pwsh(request: PwshRequest): PwshResult {\n const command = requireText(request?.command, \"pwsh\", \"command\");\n const timeout = request.timeoutMs ?? DEFAULT_PWSH_TIMEOUT;\n const cwd = request.cwd ? this.resolve(request.cwd) : this.root;\n const encoded = Buffer.from(`${PWSH_PRELUDE}\\n${command}`, \"utf16le\").toString(\"base64\");\n\n for (const shell of shellCandidates()) {\n const result = spawnSync(\n shell,\n [\"-NoProfile\", \"-NonInteractive\", \"-EncodedCommand\", encoded],\n {\n cwd,\n encoding: \"utf8\",\n maxBuffer: MAX_PWSH_OUTPUT_BYTES,\n timeout,\n windowsHide: true,\n },\n );\n\n if (result.error) {\n const code = (result.error as NodeJS.ErrnoException).code;\n\n if (code === \"ENOENT\") {\n continue;\n }\n\n if (code === \"ETIMEDOUT\") {\n throw new Error(`Pwsh timed out after ${timeout} ms.`);\n }\n\n throw new Error(`Pwsh failed: ${result.error.message}`);\n }\n\n return {\n stdout: result.stdout,\n stderr: decodePowerShellStderr(result.stderr),\n exitCode: result.status,\n };\n }\n\n throw new Error(\"Pwsh failed: PowerShell is not available on this machine.\");\n }\n\n private resolve(filePath: string): string {\n return path.resolve(this.root, filePath);\n }\n}\n","import clipboard from \"clipboardy\";\nimport type { Command } from \"commander\";\nimport { INIT_PROMPT } from \"../prompt.js\";\n\nexport function registerInit(program: Command) {\n program\n .command(\"init\")\n .description(\"Copy the agent prompt to the clipboard\")\n .action(async () => {\n try {\n await clipboard.write(INIT_PROMPT);\n } catch (error) {\n console.log(\"could not write to the clipboard\");\n return;\n }\n\n console.log(\"Prompt copied to the clipboard.\");\n console.log(\"Paste it into a web chatbot, add what you want it to do, and send.\");\n console.log(\"Then copy the chatbot's reply back here and run `mtp`.\");\n });\n}\n","/**\n * Text copied to the clipboard by `mtp init`.\n *\n * It is pasted into a web chatbot so the agent knows how to format the code that\n * this CLI will read back from the clipboard and execute.\n */\nexport const INIT_PROMPT = [\n \"You help through MeatProxy: the user pastes your replies into a terminal, which runs the JavaScript you send locally.\",\n \"\",\n \"To inspect or edit files, reply with one snippet shaped like this:\",\n \"\",\n \"```js\",\n \"<<<<<<< MEATPROXY\",\n \"// your JavaScript here\",\n \">>>>>>> MEATPROXY\",\n \"```\",\n \"\",\n \"Keep the marker lines exactly as shown, each on its own line, inside the code block. The terminal reads everything between them and ignores the rest of your reply.\",\n \"\",\n \"Guidelines:\",\n \"- Use relative paths; the working directory is the project root.\",\n \"- Do one step per snippet: the user runs it, then pastes the output back to you.\",\n \"- Use console.log for results and progress; everything printed is copied to the clipboard automatically.\",\n \"- Keep explanations outside the code block.\",\n \"\",\n \"The snippet runs in Node with a `tools` object:\",\n \"tools.read({ file_path }) -> string\",\n \"tools.write({ file_path, content })\",\n \"tools.edit({ file_path, old_string, new_string, replace_all? })\",\n \"tools.pwsh({ command, cwd?, timeoutMs? }) -> { stdout, stderr, exitCode }\",\n \"\",\n \"\"\n].join(\"\\n\");\n"],"mappings":"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","names":["clipboard","program","clipboard"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adyingdeath/meatproxy",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MeatProxy is a small bridge between your clipboard and chatbots.",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "tsx src/index.ts",
|
|
7
|
+
"build": "tsup",
|
|
8
|
+
"typecheck": "tsc --noEmit",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"prepublishOnly": "npm run typecheck && npm run build"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["cli", "ai", "llm", "agent", "agent harness"],
|
|
13
|
+
"author": "adyingdeath",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/adyingdeath/meatproxy.git"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/adyingdeath/meatproxy",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/adyingdeath/meatproxy/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"bin": {
|
|
25
|
+
"mtp": "./dist/index.js",
|
|
26
|
+
"meatproxy": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=22.12.0"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"clipboardy": "^5.3.2",
|
|
36
|
+
"commander": "^15.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^26.6.5",
|
|
40
|
+
"tsup": "^9.0.0",
|
|
41
|
+
"tsx": "^4.23.15",
|
|
42
|
+
"typescript": "^7.0.2"
|
|
43
|
+
}
|
|
44
|
+
}
|