@melaya/runner 1.1.36 → 1.1.37

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.
@@ -136,14 +136,40 @@ export async function connect(opts) {
136
136
  _assistantPrewarmed = true;
137
137
  try {
138
138
  const { getLocalSharedVersion } = await import("./sharedVendor.js");
139
- const localVersion = getLocalSharedVersion();
140
- if (!localVersion)
141
- return; // nothing cached yet — let assistant_start do it
139
+ let localVersion = getLocalSharedVersion();
140
+ const freshSetup = !localVersion;
141
+ if (!localVersion) {
142
+ // FIRST LAUNCH: no shared bundle on disk yet. Download it now, on
143
+ // connect, so the venv is built UP-FRONT instead of making the user wait
144
+ // 1-2 min mid-chat on their first assistant/pipeline run. Shown to the
145
+ // user (not verbose-gated) — a silent multi-minute pause reads as "stuck"
146
+ // and users leave; a clear one-time-setup line keeps their trust.
147
+ console.log(chalk.hex("#7C6FF0")("\n ⚙ First-time setup: preparing your local AI runtime (one-time)…"));
148
+ console.log(chalk.gray(" downloading runtime modules…"));
149
+ try {
150
+ await ensureSharedModules(opts.serverUrl, "latest", opts.token);
151
+ localVersion = getLocalSharedVersion();
152
+ }
153
+ catch (e) {
154
+ console.log(chalk.gray(` setup deferred (${e?.message || e}) — it will finish on your first run`));
155
+ return;
156
+ }
157
+ if (!localVersion)
158
+ return;
159
+ }
142
160
  const { ensurePythonEnv, getCertBundlePath } = await import("./pythonEnv.js");
161
+ if (freshSetup)
162
+ console.log(chalk.gray(" installing Python dependencies (~1-2 min)…"));
143
163
  const envResult = await ensurePythonEnv(opts.pythonPath, localVersion, (m) => { if (opts.verbose)
144
164
  console.log(chalk.gray(` [assistant prewarm] ${m}`)); });
145
- if (opts.verbose)
165
+ if (freshSetup) {
166
+ console.log(envResult.ok
167
+ ? chalk.green(" ✓ Local AI runtime ready — your first chat will start instantly\n")
168
+ : chalk.yellow(` ⚠ runtime setup did not finish (${envResult.reason || "unknown"}); it will retry on your first run\n`));
169
+ }
170
+ else if (opts.verbose) {
146
171
  console.log(chalk.gray(" [assistant prewarm] ready"));
172
+ }
147
173
  // ALSO pre-import the heavy Python stack so the first real run/chat
148
174
  // doesn't pay the ~6s cold `import shared.runtime.registry`. Spawn it
149
175
  // detached + fire-and-forget with the SAME PYTHONPATH/env a real
@@ -11,8 +11,10 @@
11
11
  */
12
12
  export declare function venvPython(): string;
13
13
  export declare function getCertBundlePath(): string;
14
- export declare function ensurePythonEnv(systemPython: string, expectedVersion: string, onProgress?: (msg: string) => void): Promise<{
14
+ type EnsureResult = {
15
15
  ok: boolean;
16
16
  pythonPath: string;
17
17
  reason?: string;
18
- }>;
18
+ };
19
+ export declare function ensurePythonEnv(systemPython: string, expectedVersion: string, onProgress?: (msg: string) => void): Promise<EnsureResult>;
20
+ export {};
package/dist/pythonEnv.js CHANGED
@@ -389,7 +389,24 @@ async function ensureNltkData(onProgress) {
389
389
  }
390
390
  // One-time-per-process guard for the cache-hit self-heal (NLTK + cert bundle).
391
391
  let _selfHealedThisProcess = false;
392
- export async function ensurePythonEnv(systemPython, expectedVersion, onProgress = (m) => console.log(chalk.gray(` [venv] ${m}`))) {
392
+ const _ensureInFlight = new Map();
393
+ export function ensurePythonEnv(systemPython, expectedVersion, onProgress = (m) => console.log(chalk.gray(` [venv] ${m}`))) {
394
+ // A valid venv resolves cheaply and must NOT be blocked behind an in-flight
395
+ // build for a different version — run the impl directly (its own early-return
396
+ // handles the cache hit). Only route through the singleflight when a build is
397
+ // actually needed, so two build requests coalesce.
398
+ if (venvIsValid(expectedVersion) && !_ensureInFlight.has(expectedVersion)) {
399
+ return _ensurePythonEnvImpl(systemPython, expectedVersion, onProgress);
400
+ }
401
+ const existing = _ensureInFlight.get(expectedVersion);
402
+ if (existing)
403
+ return existing;
404
+ const p = _ensurePythonEnvImpl(systemPython, expectedVersion, onProgress)
405
+ .finally(() => { _ensureInFlight.delete(expectedVersion); });
406
+ _ensureInFlight.set(expectedVersion, p);
407
+ return p;
408
+ }
409
+ async function _ensurePythonEnvImpl(systemPython, expectedVersion, onProgress = (m) => console.log(chalk.gray(` [venv] ${m}`))) {
393
410
  if (venvIsValid(expectedVersion)) {
394
411
  // Self-heal: ensure NLTK data + the cert bundle are present even when the
395
412
  // venv marker says we're up to date (this is what previously broke for
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melaya/runner",
3
- "version": "1.1.36",
3
+ "version": "1.1.37",
4
4
  "description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,