scad-gltf 0.1.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 +489 -0
- package/bin/scad-convert.js +221 -0
- package/bin/scad-godot.js +247 -0
- package/bin/scad-mcp.js +508 -0
- package/bin/scad-serve.js +225 -0
- package/bin/scad-web.js +206 -0
- package/editor/dist/aristea_wreck_puresky_2k.hdr +0 -0
- package/editor/dist/assets/OutputPass-Bvl6NigM.js +4317 -0
- package/editor/dist/assets/index-V9cEH4KX.js +4318 -0
- package/editor/dist/assets/index-t9MYrExo.css +1 -0
- package/editor/dist/assets/openscad-CdBCY4mx.wasm +0 -0
- package/editor/dist/assets/preview-C1zc24MJ.js +1 -0
- package/editor/dist/assets/preview-fQfL_FJ-.css +1 -0
- package/editor/dist/assets/prompt-ui-8EFRz0ju.js +126 -0
- package/editor/dist/content-loader.js +4 -0
- package/editor/dist/content.css +255 -0
- package/editor/dist/content.js +24 -0
- package/editor/dist/icon.png +0 -0
- package/editor/dist/index.html +187 -0
- package/editor/dist/manifest.json +23 -0
- package/editor/dist/manifest.webmanifest +1 -0
- package/editor/dist/preview.html +27 -0
- package/editor/dist/registerSW.js +1 -0
- package/editor/dist/sw.js +1 -0
- package/editor/dist/workbox-9c191d2f.js +1 -0
- package/godot/README.md +64 -0
- package/godot/addons/scad_importer/plugin.cfg +7 -0
- package/godot/addons/scad_importer/scad_importer.gd +197 -0
- package/godot/addons/scad_importer/scad_plugin.gd +12 -0
- package/godot/examples/README.md +82 -0
- package/godot/examples/fruit_fusion_3d.js +1574 -0
- package/godot/examples/package.json +5 -0
- package/package.json +63 -0
- package/src/convert.js +94 -0
- package/src/ext/openscad.js +14 -0
- package/src/ext/openscad.wasm +0 -0
- package/src/prompt.js +229 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import express from "express";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import { convertScadToGltf } from "../src/convert.js";
|
|
8
|
+
|
|
9
|
+
// Resolve the local paths
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = path.dirname(__filename);
|
|
12
|
+
const wasmPath = path.resolve(__dirname, "../src/ext/openscad.wasm");
|
|
13
|
+
const editorDir = path.resolve(__dirname, "../editor");
|
|
14
|
+
const editorDistDir = path.join(editorDir, "dist");
|
|
15
|
+
|
|
16
|
+
// Polyfill fetch so the WASM loader works natively in Node.js
|
|
17
|
+
const originalFetch = global.fetch;
|
|
18
|
+
global.fetch = async (url, options) => {
|
|
19
|
+
const urlStr = url.toString();
|
|
20
|
+
if (urlStr.startsWith("file://") || urlStr.endsWith(".wasm")) {
|
|
21
|
+
const normalizedPath = urlStr.startsWith("file://")
|
|
22
|
+
? fileURLToPath(urlStr)
|
|
23
|
+
: urlStr;
|
|
24
|
+
|
|
25
|
+
const buffer = fs.readFileSync(normalizedPath);
|
|
26
|
+
return new Response(buffer, {
|
|
27
|
+
status: 200,
|
|
28
|
+
headers: { "Content-Type": "application/wasm" },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return originalFetch ? originalFetch(url, options) : undefined;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// Parse CLI arguments
|
|
35
|
+
const args = process.argv.slice(2);
|
|
36
|
+
let port = 3000;
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < args.length; i++) {
|
|
39
|
+
if (args[i] === "--port") {
|
|
40
|
+
port = parseInt(args[i + 1]) || 3000;
|
|
41
|
+
i++; // Skip the port number value
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Target directory is the current working directory where the script is run
|
|
46
|
+
const workDir = process.cwd();
|
|
47
|
+
|
|
48
|
+
// ========================
|
|
49
|
+
// Editor Build Step (Only if dist is missing)
|
|
50
|
+
// ========================
|
|
51
|
+
if (!fs.existsSync(editorDistDir) && fs.existsSync(editorDir)) {
|
|
52
|
+
console.log("⚙️ Editor build missing. Attempting to build...");
|
|
53
|
+
try {
|
|
54
|
+
if (!fs.existsSync(path.join(editorDir, "node_modules"))) {
|
|
55
|
+
console.log("📦 Installing editor dependencies...");
|
|
56
|
+
execSync("npm install", { cwd: editorDir, stdio: "inherit" });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log("🛠️ Building editor...");
|
|
60
|
+
execSync("npm run build", { cwd: editorDir, stdio: "inherit" });
|
|
61
|
+
console.log("✅ Editor built successfully.\n");
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error("❌ Error building editor:", err.message);
|
|
64
|
+
console.log("⚠️ Continuing without an editor build...\n");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const app = express();
|
|
69
|
+
// Increase payload limit in case of very large SCAD files
|
|
70
|
+
app.use(express.json({ limit: "50mb" }));
|
|
71
|
+
|
|
72
|
+
// Basic CORS middleware for external web clients
|
|
73
|
+
app.use((req, res, next) => {
|
|
74
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
75
|
+
res.header(
|
|
76
|
+
"Access-Control-Allow-Methods",
|
|
77
|
+
"GET, PUT, POST, PATCH, DELETE, OPTIONS",
|
|
78
|
+
);
|
|
79
|
+
res.header("Access-Control-Allow-Headers", "Content-Type");
|
|
80
|
+
if (req.method === "OPTIONS") return res.sendStatus(200);
|
|
81
|
+
next();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Helper: Sanitize filename to prevent directory traversal attacks
|
|
85
|
+
function sanitizeFilename(filename) {
|
|
86
|
+
const safeName = path.basename(filename);
|
|
87
|
+
return safeName.endsWith(".scad") ? safeName : `${safeName}.scad`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ========================
|
|
91
|
+
// SCAD File Management API
|
|
92
|
+
// ========================
|
|
93
|
+
|
|
94
|
+
// 1. List all .scad files in the current directory
|
|
95
|
+
app.get("/api/scads", (req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const files = fs
|
|
98
|
+
.readdirSync(workDir)
|
|
99
|
+
.filter(
|
|
100
|
+
(file) =>
|
|
101
|
+
file.toLowerCase().endsWith(".scad") &&
|
|
102
|
+
fs.statSync(path.join(workDir, file)).isFile(),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
res.json({ files });
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error("Error reading directory:", err);
|
|
108
|
+
res.status(500).json({ error: "Failed to list files." });
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// 2. Read specific .scad file content
|
|
113
|
+
app.get("/api/scads/:filename", (req, res) => {
|
|
114
|
+
const filename = sanitizeFilename(req.params.filename);
|
|
115
|
+
const filePath = path.join(workDir, filename);
|
|
116
|
+
|
|
117
|
+
if (!fs.existsSync(filePath)) {
|
|
118
|
+
return res.status(404).json({ error: `File '${filename}' not found.` });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
123
|
+
res.json({ filename, content });
|
|
124
|
+
} catch (err) {
|
|
125
|
+
console.error(`Error reading ${filename}:`, err);
|
|
126
|
+
res.status(500).json({ error: "Failed to read file." });
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// 3. Create or Update a .scad file
|
|
131
|
+
app.post("/api/scads", (req, res) => {
|
|
132
|
+
const { filename: rawFilename, content } = req.body;
|
|
133
|
+
|
|
134
|
+
if (!rawFilename) {
|
|
135
|
+
return res
|
|
136
|
+
.status(400)
|
|
137
|
+
.json({ error: "Missing 'filename' in request body." });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const filename = sanitizeFilename(rawFilename);
|
|
141
|
+
const filePath = path.join(workDir, filename);
|
|
142
|
+
const isUpdate = fs.existsSync(filePath);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
fs.writeFileSync(filePath, content || "", "utf-8");
|
|
146
|
+
res.json({
|
|
147
|
+
message: isUpdate
|
|
148
|
+
? "File updated successfully"
|
|
149
|
+
: "File created successfully",
|
|
150
|
+
filename,
|
|
151
|
+
});
|
|
152
|
+
} catch (err) {
|
|
153
|
+
console.error(`Error writing to ${filename}:`, err);
|
|
154
|
+
res.status(500).json({ error: "Failed to write file." });
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// 4. Delete a .scad file
|
|
159
|
+
app.delete("/api/scads/:filename", (req, res) => {
|
|
160
|
+
const filename = sanitizeFilename(req.params.filename);
|
|
161
|
+
const filePath = path.join(workDir, filename);
|
|
162
|
+
|
|
163
|
+
if (!fs.existsSync(filePath)) {
|
|
164
|
+
return res.status(404).json({ error: `File '${filename}' not found.` });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
fs.unlinkSync(filePath);
|
|
169
|
+
res.json({ message: "File deleted successfully", filename });
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.error(`Error deleting ${filename}:`, err);
|
|
172
|
+
res.status(500).json({ error: "Failed to delete file." });
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// 5. Convert SCAD to GLB
|
|
177
|
+
app.post("/api/convert", async (req, res) => {
|
|
178
|
+
const { content, options } = req.body;
|
|
179
|
+
|
|
180
|
+
const additionalFiles = (options && options.additionalFiles) || {};
|
|
181
|
+
|
|
182
|
+
if (!content) {
|
|
183
|
+
return res
|
|
184
|
+
.status(400)
|
|
185
|
+
.json({ error: "Missing 'content' in request body." });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
// Pass the raw SCAD directly to the WASM converter entirely in-memory
|
|
190
|
+
const glbData = await convertScadToGltf(content, {
|
|
191
|
+
wasmUrl: pathToFileURL(wasmPath).href,
|
|
192
|
+
additionalFiles,
|
|
193
|
+
variables: (options && options.variables) || undefined,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
res.setHeader("Content-Type", "model/gltf-binary");
|
|
197
|
+
res.send(Buffer.from(glbData));
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.error("SCAD Conversion Error:", err);
|
|
200
|
+
res
|
|
201
|
+
.status(500)
|
|
202
|
+
.json({ error: "Failed to convert SCAD to GLB: " + err.toString() });
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// ========================
|
|
207
|
+
// Serve Built Editor UI
|
|
208
|
+
// ========================
|
|
209
|
+
if (fs.existsSync(editorDistDir)) {
|
|
210
|
+
// Serve the static files exactly how Vite bundled them
|
|
211
|
+
app.use(express.static(editorDistDir));
|
|
212
|
+
} else {
|
|
213
|
+
console.warn(
|
|
214
|
+
"⚠️ Editor dist directory not found. Editor UI will not be available.",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Start Server
|
|
219
|
+
app.listen(port, () => {
|
|
220
|
+
console.log(`🚀 scad-serve listening on http://localhost:${port}`);
|
|
221
|
+
if (fs.existsSync(editorDistDir)) {
|
|
222
|
+
console.log(`🌐 Editor available at: http://localhost:${port}/`);
|
|
223
|
+
}
|
|
224
|
+
console.log(`📁 Managing .scad files in directory: ${workDir}`);
|
|
225
|
+
});
|
package/bin/scad-web.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import url from "node:url";
|
|
6
|
+
import readline from "node:readline";
|
|
7
|
+
|
|
8
|
+
// Safely resolve symlinks to find the actual package directory
|
|
9
|
+
const __filename = fs.realpathSync(url.fileURLToPath(import.meta.url));
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
const DIR = path.resolve(__dirname, "..");
|
|
12
|
+
|
|
13
|
+
// Safely detect if actual data is being piped into the script via STDIN
|
|
14
|
+
function hasStdinData() {
|
|
15
|
+
try {
|
|
16
|
+
const stat = fs.fstatSync(0); // 0 is the file descriptor for STDIN
|
|
17
|
+
// isFIFO means piped (echo "foo" | script)
|
|
18
|
+
// isFile means redirected (script < foo.txt)
|
|
19
|
+
return stat.isFIFO() || stat.isFile();
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Writes text to system clipboard
|
|
26
|
+
async function writeToClipboard(text) {
|
|
27
|
+
const clipboardy = (await import("clipboardy")).default;
|
|
28
|
+
await clipboardy.write(text);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function waitForEnter(message) {
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
// If standard input was piped/redirected, we need to bypass it and read from the actual terminal
|
|
34
|
+
if (!process.stdin.isTTY) {
|
|
35
|
+
try {
|
|
36
|
+
const tty = process.platform === "win32" ? "CONIN$" : "/dev/tty";
|
|
37
|
+
const fd = fs.openSync(tty, "rs");
|
|
38
|
+
process.stdout.write(message);
|
|
39
|
+
const buf = Buffer.alloc(1);
|
|
40
|
+
fs.readSync(fd, buf, 0, 1, null);
|
|
41
|
+
fs.closeSync(fd);
|
|
42
|
+
console.log();
|
|
43
|
+
resolve();
|
|
44
|
+
return;
|
|
45
|
+
} catch (e) {
|
|
46
|
+
console.log(
|
|
47
|
+
message +
|
|
48
|
+
" (Auto-continuing due to non-interactive terminal environment)",
|
|
49
|
+
);
|
|
50
|
+
resolve();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// For standard TTY terminals
|
|
56
|
+
const rl = readline.createInterface({
|
|
57
|
+
input: process.stdin,
|
|
58
|
+
output: process.stdout,
|
|
59
|
+
});
|
|
60
|
+
rl.question(message, () => {
|
|
61
|
+
rl.close();
|
|
62
|
+
resolve();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function main() {
|
|
68
|
+
let task = "";
|
|
69
|
+
let optionsStr = "{}";
|
|
70
|
+
|
|
71
|
+
// 1. Read TASK and OPTIONS
|
|
72
|
+
if (hasStdinData()) {
|
|
73
|
+
try {
|
|
74
|
+
task = fs.readFileSync(0, "utf-8").trim();
|
|
75
|
+
} catch (e) {
|
|
76
|
+
console.error("Error reading from STDIN:", e);
|
|
77
|
+
}
|
|
78
|
+
if (process.argv[2]) optionsStr = process.argv[2];
|
|
79
|
+
} else {
|
|
80
|
+
if (process.argv[2]) task = process.argv[2];
|
|
81
|
+
if (process.argv[3]) optionsStr = process.argv[3];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!task) {
|
|
85
|
+
console.error("Error: Task parameter is required.");
|
|
86
|
+
console.error(
|
|
87
|
+
'Usage: scad-web "<description of the web app to generate>" [options_json]',
|
|
88
|
+
);
|
|
89
|
+
console.error(' or: echo "<description>" | scad-web [options_json]');
|
|
90
|
+
console.error("");
|
|
91
|
+
console.error("Example with JSON options:");
|
|
92
|
+
console.error(
|
|
93
|
+
' scad-web "3D Car Configurator Web App" \'{"animation": false}\'',
|
|
94
|
+
);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 2. Parse Options JSON
|
|
99
|
+
let options = {};
|
|
100
|
+
if (optionsStr) {
|
|
101
|
+
try {
|
|
102
|
+
options = JSON.parse(optionsStr);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
console.error(`Invalid JSON options: ${optionsStr}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Disable the modelName instructions specifically for this wrapper context
|
|
110
|
+
options.modelName = false;
|
|
111
|
+
|
|
112
|
+
// 3. Dynamically import and generate prompt rules from src/prompt.js
|
|
113
|
+
let generatePrompt;
|
|
114
|
+
try {
|
|
115
|
+
const promptJsPath = path.join(DIR, "src", "prompt.js");
|
|
116
|
+
const promptModuleUrl = url.pathToFileURL(promptJsPath).href;
|
|
117
|
+
const m = await import(promptModuleUrl);
|
|
118
|
+
generatePrompt = m.generatePrompt;
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error(`Error loading ${path.join("src", "prompt.js")}:`, e);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let promptRules = "";
|
|
125
|
+
try {
|
|
126
|
+
promptRules = generatePrompt("the 3D assets for the web app", options);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
console.error("Error generating prompt rules from prompt.js:");
|
|
129
|
+
console.error(e);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 4. Construct the Main Web Prompt System Text (System Instructions)
|
|
134
|
+
const systemPrompt = `You are an expert Web 3D developer and procedural 3D technical artist.
|
|
135
|
+
|
|
136
|
+
What to generate:
|
|
137
|
+
1. 3D Web Assets (.scad):
|
|
138
|
+
- Generate procedural 3D models for the web app using OpenSCAD.
|
|
139
|
+
- CRITICAL: The SCAD to glTF converter automatically converts OpenSCAD's Z-up coordinate system to the standard glTF Y-up coordinate system. Design your models naturally in OpenSCAD.
|
|
140
|
+
- CRITICAL: You must use the custom OpenSCAD glTF extensions for PBR materials (e.g., \`roughness\`, \`metalness\`, \`emissive\`) and Skeletal Animations (\`armature()\`, \`bone()\`). The rules and syntax for these features are provided below:
|
|
141
|
+
|
|
142
|
+
=== OPENSCAD SYNTAX RULES ===
|
|
143
|
+
${promptRules}
|
|
144
|
+
=============================
|
|
145
|
+
|
|
146
|
+
2. Vite Web Project Files (npm based):
|
|
147
|
+
- Create the necessary files for a modern web application (e.g., \`package.json\`, \`index.html\`, \`main.js\`).
|
|
148
|
+
- You can use ANY web 3D library with glTF support (e.g., Three.js, Babylon.js, @google/model-viewer, PlayCanvas, A-Frame, etc.) that fits the app's requirements.
|
|
149
|
+
- In \`package.json\`, you MUST include the scad to gltf converter tool as a dev dependency:
|
|
150
|
+
\`"scad-gltf": "^0.1.0"\`
|
|
151
|
+
- In \`package.json\`, add npm scripts to automatically compile the \`.scad\` files into \`.glb\` format inside the \`public/\` folder before Vite runs its dev or build steps.
|
|
152
|
+
For example:
|
|
153
|
+
\`"predev": "scad-convert ./scad ./public/models --cache"\`
|
|
154
|
+
\`"prebuild": "scad-convert ./scad ./public/models --cache"\`
|
|
155
|
+
- Write the core application logic to load and display the converted \`.glb\` files interactively.
|
|
156
|
+
|
|
157
|
+
3. Delivery Format (Single Node.js Script):
|
|
158
|
+
- Output exactly ONE self-contained Node.js script. Do not output manual setup instructions.
|
|
159
|
+
- CRITICAL: The generated Node.js script MUST first create a root project folder (named using a slugified version of the project name) and output all files and folders inside this newly created project folder.
|
|
160
|
+
- When executed, this script must programmatically create the entire project directory structure and write all the files to disk using the \`fs\` module.
|
|
161
|
+
- The script must embed and write:
|
|
162
|
+
- Your generated \`.scad\` 3D assets.
|
|
163
|
+
- Your generated Vite web project files.
|
|
164
|
+
- Ensure all string file contents inside the Node.js script are properly escaped.`;
|
|
165
|
+
|
|
166
|
+
// 5. Format the unified system instructions clipboard output
|
|
167
|
+
let systemClipboardOutput = `### SYSTEM_PROMPT\n---\n\`\`\`\n${systemPrompt}\n\`\`\`\n\n`;
|
|
168
|
+
|
|
169
|
+
// 6. Format the input request output
|
|
170
|
+
const inputRequestOutput = `Input Task:\nDesign and implement a web-based 3D glTF app using Vite for the following concept: "${task}"`;
|
|
171
|
+
|
|
172
|
+
// 7. Write to System Clipboard (Part 1: System Instructions)
|
|
173
|
+
try {
|
|
174
|
+
await writeToClipboard(systemClipboardOutput);
|
|
175
|
+
console.log("✔️ System instructions have been copied to the clipboard.");
|
|
176
|
+
} catch (err) {
|
|
177
|
+
console.error(
|
|
178
|
+
"Error: Failed to copy system instructions to the clipboard.",
|
|
179
|
+
);
|
|
180
|
+
console.error(err.message);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 8. Await user confirmation
|
|
185
|
+
await waitForEnter(
|
|
186
|
+
"Please paste the system instructions into your LLM, then press ENTER to copy your input request...",
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
// 9. Write to System Clipboard (Part 2: Input Request)
|
|
190
|
+
try {
|
|
191
|
+
await writeToClipboard(inputRequestOutput);
|
|
192
|
+
console.log(
|
|
193
|
+
"✔️ Input request has been copied to the clipboard. You can now paste it into your LLM.",
|
|
194
|
+
);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
console.error("Error: Failed to copy input request to the clipboard.");
|
|
197
|
+
console.error(err.message);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Execute and handle unhandled runtime errors
|
|
203
|
+
main().catch((err) => {
|
|
204
|
+
console.error("An unexpected error occurred:", err);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
});
|
|
Binary file
|