@melaya/runner 1.0.53 → 1.0.55

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 (2) hide show
  1. package/dist/connection.js +81 -1
  2. package/package.json +1 -1
@@ -14,7 +14,7 @@ import { io } from "socket.io-client";
14
14
  import chalk from "chalk";
15
15
  import ora from "ora";
16
16
  import { spawn } from "child_process";
17
- import { writeFileSync, mkdirSync } from "fs";
17
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
18
18
  import { dirname, join } from "path";
19
19
  import { fileURLToPath } from "url";
20
20
  import { tmpdir, homedir } from "os";
@@ -170,6 +170,66 @@ export async function connect(opts) {
170
170
  mkdirSync(runDir, { recursive: true });
171
171
  writeFileSync(join(runDir, "main.py"), payload.mainPyContent, "utf-8");
172
172
  writeFileSync(join(runDir, "config.json"), payload.configJson, "utf-8");
173
+ // Write rag/ static-context files alongside main.py so
174
+ // _load_rag_context() inside the spawned pipeline sees them.
175
+ // Two paths:
176
+ // Cloud-upload mode: server ships payload.ragFiles (b64 bytes).
177
+ // Local-folder Mode B: server sets staticContextLocalFolderPath
178
+ // and omits ragFiles. We walk that folder here so the bytes
179
+ // NEVER round-trip through prod.
180
+ const _SC_EXTS = new Set([".txt", ".md", ".csv", ".json", ".pdf", ".doc", ".docx", ".pptx", ".xlsx"]);
181
+ const _SC_MAX_BYTES = 4 * 1024 * 1024;
182
+ const ragDir = join(runDir, "rag");
183
+ let scFilesWritten = 0;
184
+ const scLocalPath = (payload.staticContextLocalFolderPath || "").trim();
185
+ if (scLocalPath) {
186
+ try {
187
+ if (!existsSync(scLocalPath)) {
188
+ console.log(chalk.yellow(` ! static-context local folder not found: ${scLocalPath}`));
189
+ }
190
+ else {
191
+ const { readdirSync, statSync, readFileSync } = await import("node:fs");
192
+ const { extname, basename } = await import("node:path");
193
+ mkdirSync(ragDir, { recursive: true });
194
+ for (const name of readdirSync(scLocalPath)) {
195
+ if (name.startsWith("."))
196
+ continue;
197
+ const full = join(scLocalPath, name);
198
+ try {
199
+ const st = statSync(full);
200
+ if (!st.isFile())
201
+ continue;
202
+ if (st.size > _SC_MAX_BYTES)
203
+ continue;
204
+ if (!_SC_EXTS.has(extname(name).toLowerCase()))
205
+ continue;
206
+ writeFileSync(join(ragDir, basename(name)), readFileSync(full));
207
+ scFilesWritten++;
208
+ }
209
+ catch (err) {
210
+ console.log(chalk.yellow(` ! skip ${name}: ${err.message}`));
211
+ }
212
+ }
213
+ console.log(chalk.gray(` [rag] local-folder mode: copied ${scFilesWritten} file(s) from ${scLocalPath} to ${ragDir} (bytes never round-tripped through prod)`));
214
+ }
215
+ }
216
+ catch (err) {
217
+ console.log(chalk.yellow(` ! static-context local-folder scan failed: ${err.message}`));
218
+ }
219
+ }
220
+ else if (payload.ragFiles && Object.keys(payload.ragFiles).length > 0) {
221
+ mkdirSync(ragDir, { recursive: true });
222
+ for (const [name, b64] of Object.entries(payload.ragFiles)) {
223
+ try {
224
+ writeFileSync(join(ragDir, name), Buffer.from(b64, "base64"));
225
+ scFilesWritten++;
226
+ }
227
+ catch (err) {
228
+ console.log(chalk.yellow(` ! could not write rag/${name}: ${err.message}`));
229
+ }
230
+ }
231
+ console.log(chalk.gray(` [rag] cloud-upload mode: wrote ${scFilesWritten} static-context file(s) to ${ragDir}`));
232
+ }
173
233
  // Local-model preflight: ensure LM Studio / Ollama have the
174
234
  // pipeline's model loaded BEFORE we spawn the Python subprocess.
175
235
  // Without this, the subprocess hangs on its first chat completion
@@ -257,6 +317,26 @@ export async function connect(opts) {
257
317
  MEL_RELAY_NONCE: relay.nonce,
258
318
  LMSTUDIO_BASE_URL: "http://127.0.0.1:1234",
259
319
  OLLAMA_BASE_URL: "http://127.0.0.1:11434",
320
+ // Tool-output directory convention (2026-06-07). File-producing
321
+ // tools (meme_generate, excel writers, …) read MELAYA_OUTPUT_DIR
322
+ // and default their save_to here. For local-runner pipelines we
323
+ // point it at ~/Documents/Melaya-Pipelines/<pipeline>/outputs/ so
324
+ // the file lands somewhere persistent on the user's box (NOT the
325
+ // ephemeral runDir which the runbook expects to survive across
326
+ // runs). The pipeline_dir copy at <runDir>/outputs/ that codegen
327
+ // would create on cloud-spawn is overridden here because the
328
+ // codegen template uses `os.environ.setdefault(...)` — the
329
+ // runner-set value wins. See AddNewTool.md §2d.
330
+ MELAYA_OUTPUT_DIR: (() => {
331
+ const dir = join(homedir(), "Documents", "Melaya-Pipelines", payload.pipelineName, "outputs");
332
+ try {
333
+ mkdirSync(dir, { recursive: true });
334
+ }
335
+ catch (e) {
336
+ console.log(chalk.yellow(` ! could not create output dir ${dir}: ${e.message}`));
337
+ }
338
+ return dir;
339
+ })(),
260
340
  // Inject per-pipeline credentials (already scoped by the server)
261
341
  ...payload.credentials,
262
342
  // If a Luma browser bridge is up (i.e. the user has signed in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melaya/runner",
3
- "version": "1.0.53",
3
+ "version": "1.0.55",
4
4
  "description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,