micro-models-agent 0.16.5 → 0.16.7
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/dist/cli/repl.js
CHANGED
|
@@ -59,6 +59,7 @@ export class Repl {
|
|
|
59
59
|
configDir;
|
|
60
60
|
baseDir;
|
|
61
61
|
noAgentsMd;
|
|
62
|
+
pendingClipboardImage = null;
|
|
62
63
|
constructor(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd) {
|
|
63
64
|
this.agent = agent;
|
|
64
65
|
this.config = config;
|
|
@@ -738,7 +739,7 @@ export class Repl {
|
|
|
738
739
|
});
|
|
739
740
|
if (process.stdin.isTTY) {
|
|
740
741
|
readline.emitKeypressEvents(process.stdin);
|
|
741
|
-
process.stdin.on("keypress", (str, key) => {
|
|
742
|
+
process.stdin.on("keypress", async (str, key) => {
|
|
742
743
|
if (key.name === "escape") {
|
|
743
744
|
const now = Date.now();
|
|
744
745
|
if (now - this.lastEscTime < this.doubleEscDelay) {
|
|
@@ -751,6 +752,23 @@ export class Repl {
|
|
|
751
752
|
}
|
|
752
753
|
this.lastEscTime = now;
|
|
753
754
|
}
|
|
755
|
+
// Ctrl+V: try to paste image from clipboard
|
|
756
|
+
if (key.ctrl && key.name === "v" && !this.agentRunning) {
|
|
757
|
+
try {
|
|
758
|
+
const { readClipboardImage, loadFileAsDataUrl } = await import("../llm/image-utils");
|
|
759
|
+
const clipPath = await readClipboardImage();
|
|
760
|
+
if (clipPath) {
|
|
761
|
+
const { dataUrl } = loadFileAsDataUrl(clipPath);
|
|
762
|
+
this.pendingClipboardImage = dataUrl;
|
|
763
|
+
const sizeKb = Math.round((dataUrl.length * 3) / 4 / 1024);
|
|
764
|
+
console.log(pc.green(`\n${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
|
|
765
|
+
this.rl.prompt();
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
catch {
|
|
769
|
+
// clipboard read failed — ignore, let terminal paste text normally
|
|
770
|
+
}
|
|
771
|
+
}
|
|
754
772
|
});
|
|
755
773
|
}
|
|
756
774
|
// SIGINT: first Ctrl+C sends graceful shutdown, second forces exit
|
|
@@ -782,6 +800,17 @@ export class Repl {
|
|
|
782
800
|
this.agentRunning = true;
|
|
783
801
|
this.rl.pause();
|
|
784
802
|
try {
|
|
803
|
+
// Attach pending clipboard image if Ctrl+V was pressed
|
|
804
|
+
if (this.pendingClipboardImage) {
|
|
805
|
+
const contextManager = this.agent.contextManager;
|
|
806
|
+
if (contextManager) {
|
|
807
|
+
contextManager.addPendingImage({
|
|
808
|
+
type: "image_url",
|
|
809
|
+
image_url: { url: this.pendingClipboardImage },
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
this.pendingClipboardImage = null;
|
|
813
|
+
}
|
|
785
814
|
process.stdout.write("\n" + pc.green(t("repl.agent")));
|
|
786
815
|
const renderer = new Renderer({ spinner: this.config.ui?.spinner ?? true });
|
|
787
816
|
const result = await this.agent.run(input, (c) => renderer.text(c), (m) => renderer.meta(m), (ev) => {
|
package/dist/llm/image-utils.js
CHANGED
|
@@ -63,17 +63,9 @@ async function readClipboardWindows() {
|
|
|
63
63
|
try {
|
|
64
64
|
const { execSync } = await import("child_process");
|
|
65
65
|
const tmpPath = `${process.env.TEMP || process.env.TMP || "/tmp"}\\mma-clip-${Date.now()}.png`;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"if ($img -ne $null) {",
|
|
70
|
-
` $img.Save("${tmpPath}")`,
|
|
71
|
-
' Write-Output "OK"',
|
|
72
|
-
"} else {",
|
|
73
|
-
' Write-Output "EMPTY"',
|
|
74
|
-
"}",
|
|
75
|
-
].join("; ");
|
|
76
|
-
const out = execSync(`powershell -NoProfile -Command "${ps}"`, {
|
|
66
|
+
// Use single quotes in PowerShell to avoid backslash issues
|
|
67
|
+
const psScript = `Add-Type -AssemblyName System.Windows.Forms; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img -ne $null) { $img.Save('${tmpPath}'); Write-Output 'OK' } else { Write-Output 'EMPTY' }`;
|
|
68
|
+
const out = execSync(`powershell -NoProfile -Command "${psScript}"`, {
|
|
77
69
|
timeout: 5000,
|
|
78
70
|
encoding: "utf-8",
|
|
79
71
|
});
|
|
@@ -85,6 +85,16 @@ export function isCommandAllowed(command, securityConfig) {
|
|
|
85
85
|
if (!trimmedCommand) {
|
|
86
86
|
return { allowed: false, reason: "Empty command" };
|
|
87
87
|
}
|
|
88
|
+
// Shell wrappers are always blocked — they bypass all command validation.
|
|
89
|
+
// The agent should use the bash tool directly, not shell interpreters.
|
|
90
|
+
const ALWAYS_BLOCKED = ['powershell', 'pwsh', 'cmd'];
|
|
91
|
+
const baseForShellCheck = extractBaseCommand(trimmedCommand);
|
|
92
|
+
if (ALWAYS_BLOCKED.includes(baseForShellCheck)) {
|
|
93
|
+
return {
|
|
94
|
+
allowed: false,
|
|
95
|
+
reason: `Shell wrapper "${baseForShellCheck}" is blocked — use the bash tool directly`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
88
98
|
// Check dangerous operators FIRST — before parsing the base command.
|
|
89
99
|
// e.g. "curl http://x | bash" must be caught even though curl isn't blacklisted.
|
|
90
100
|
if (config.blockDangerousFlags) {
|