threadnote 1.4.4 → 1.4.5

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/threadnote.cjs +156 -18
  2. package/package.json +1 -1
@@ -13703,6 +13703,7 @@ function isUpdateNotificationDisabled() {
13703
13703
  }
13704
13704
 
13705
13705
  // src/lifecycle.ts
13706
+ var INSTALL_OUTPUT_TAIL_CHARS = 64e3;
13706
13707
  async function runDoctor(config, options) {
13707
13708
  const checks = await collectDoctorChecks(config, options);
13708
13709
  for (const check of checks) {
@@ -14487,32 +14488,169 @@ async function runInstallCommands(config, preferred, force, dryRun) {
14487
14488
  continue;
14488
14489
  }
14489
14490
  console.log(`Running: ${formatShellCommand(installCommand.executable, installCommand.args)}`);
14490
- const exitCode = await runInteractive(installCommand.executable, installCommand.args);
14491
- if (exitCode !== 0) {
14492
- printOpenVikingInstallFailureHelp(installCommand);
14493
- throw new Error(`${formatShellCommand(installCommand.executable, installCommand.args)} exited with ${exitCode}.`);
14491
+ const result = await runInstallCommand(installCommand);
14492
+ if (result.exitCode !== 0) {
14493
+ const commandOutput = `${result.stderr}
14494
+ ${result.stdout}`;
14495
+ const retry = openVikingSourceBuildRetryForArchiveFailure(installCommand, commandOutput);
14496
+ if (retry) {
14497
+ console.error("");
14498
+ console.error(
14499
+ "The prebuilt llama-cpp-python wheel failed ZIP archive validation; retrying with a local source build."
14500
+ );
14501
+ console.error("This avoids the rejected wheel and can take 10-20 minutes.");
14502
+ console.log(`Running: ${formatInstallCommand(retry.command, retry.env)}`);
14503
+ const retryResult = await runInstallCommand(retry.command, retry.env);
14504
+ if (retryResult.exitCode === 0) {
14505
+ continue;
14506
+ }
14507
+ printOpenVikingInstallFailureHelp(retry.command, `${retryResult.stderr}
14508
+ ${retryResult.stdout}`);
14509
+ throw new Error(
14510
+ `${formatInstallCommand(retry.command, retry.env)} exited with ${retryResult.exitCode} after automatic source-build retry.`
14511
+ );
14512
+ }
14513
+ printOpenVikingInstallFailureHelp(installCommand, commandOutput);
14514
+ throw new Error(
14515
+ `${formatShellCommand(installCommand.executable, installCommand.args)} exited with ${result.exitCode}.`
14516
+ );
14494
14517
  }
14495
14518
  }
14496
14519
  }
14497
- function printOpenVikingInstallFailureHelp(failedCommand) {
14498
- console.error("");
14499
- console.error("OpenViking install did not complete.");
14500
- console.error(
14501
- "openviking[local-embed] includes llama-cpp-python, which compiles from source when no prebuilt wheel matches your Python/platform \u2014 that build can run 10-20 minutes and is memory-heavy, so it may be killed by the OS (out of memory) or look stuck."
14502
- );
14503
- console.error("Re-run it directly to see full output without any wrapper timeout:");
14504
- console.error(` ${formatShellCommand(failedCommand.executable, failedCommand.args)}`);
14505
- console.error("Cap memory use during the compile with: CMAKE_BUILD_PARALLEL_LEVEL=2 <command above>");
14520
+ async function runInstallCommand(command2, env = {}) {
14521
+ return new Promise((resolvePromise) => {
14522
+ let stdout2 = "";
14523
+ let stderr = "";
14524
+ let settled = false;
14525
+ const child = (0, import_node_child_process4.spawn)(command2.executable, command2.args, {
14526
+ env: Object.keys(env).length === 0 ? process.env : { ...process.env, ...env },
14527
+ stdio: ["inherit", "pipe", "pipe"]
14528
+ });
14529
+ child.stdout?.on("data", (chunk) => {
14530
+ const text = String(chunk);
14531
+ import_node_process4.stdout.write(text);
14532
+ stdout2 = appendInstallOutputTail(stdout2, text);
14533
+ });
14534
+ child.stderr?.on("data", (chunk) => {
14535
+ const text = String(chunk);
14536
+ import_node_process4.stderr.write(text);
14537
+ stderr = appendInstallOutputTail(stderr, text);
14538
+ });
14539
+ child.on("error", (err) => {
14540
+ if (settled) {
14541
+ return;
14542
+ }
14543
+ settled = true;
14544
+ const message = errorMessage(err);
14545
+ import_node_process4.stderr.write(`${message}
14546
+ `);
14547
+ resolvePromise({ exitCode: 1, stderr: appendInstallOutputTail(stderr, message), stdout: stdout2 });
14548
+ });
14549
+ child.on("close", (code) => {
14550
+ if (settled) {
14551
+ return;
14552
+ }
14553
+ settled = true;
14554
+ resolvePromise({ exitCode: code ?? 1, stderr, stdout: stdout2 });
14555
+ });
14556
+ });
14557
+ }
14558
+ function appendInstallOutputTail(current, chunk) {
14559
+ const next = `${current}${chunk}`;
14560
+ return next.length <= INSTALL_OUTPUT_TAIL_CHARS ? next : next.slice(next.length - INSTALL_OUTPUT_TAIL_CHARS);
14561
+ }
14562
+ function printOpenVikingInstallFailureHelp(failedCommand, commandOutput) {
14563
+ for (const line of openVikingInstallFailureHelpLines(failedCommand, commandOutput)) {
14564
+ console.error(line);
14565
+ }
14566
+ }
14567
+ function openVikingInstallFailureHelpLines(failedCommand, commandOutput = "") {
14568
+ if (isLlamaWheelArchiveExtractionFailure(commandOutput)) {
14569
+ return [
14570
+ "",
14571
+ "OpenViking install did not complete.",
14572
+ "The prebuilt llama-cpp-python wheel failed ZIP archive validation. Threadnote only falls back to a source build automatically when the failed command came from a prebuilt wheel index."
14573
+ ];
14574
+ }
14575
+ const lines = [
14576
+ "",
14577
+ "OpenViking install did not complete.",
14578
+ "openviking[local-embed] includes llama-cpp-python, which compiles from source when no prebuilt wheel matches your Python/platform \u2014 that build can run 10-20 minutes and is memory-heavy, so it may be killed by the OS (out of memory) or look stuck.",
14579
+ "The package-manager output above contains the underlying build or download error."
14580
+ ];
14506
14581
  if (failedCommand.executable === "uv") {
14507
14582
  if (failedCommand.args.includes("--python")) {
14508
- console.error(
14509
- "If uv could not fetch a managed CPython (offline or restricted network), drop the version pin and retry: THREADNOTE_OPENVIKING_PYTHON= threadnote install --force"
14583
+ lines.push(
14584
+ "If uv could not fetch managed CPython, Threadnote cannot complete the local install until that Python download is available."
14510
14585
  );
14511
14586
  }
14512
- console.error("If it was killed mid-build, clear the partial install first, then retry:");
14513
- console.error(" uv cache clean");
14514
- console.error(' rm -rf "$(uv tool dir)/openviking"');
14515
14587
  }
14588
+ return lines;
14589
+ }
14590
+ function isLlamaWheelArchiveExtractionFailure(output2) {
14591
+ const normalized = output2.toLowerCase();
14592
+ return (normalized.includes("llama-cpp-python") || normalized.includes("llama_cpp_python")) && (normalized.includes("failed to extract archive") || normalized.includes("zip file contains trailing contents after the end-of-central-directory record"));
14593
+ }
14594
+ function openVikingSourceBuildRetryForArchiveFailure(failedCommand, commandOutput) {
14595
+ if (!isLlamaWheelArchiveExtractionFailure(commandOutput)) {
14596
+ return void 0;
14597
+ }
14598
+ const command2 = withoutExtraIndexUrl(failedCommand);
14599
+ if (!command2) {
14600
+ return void 0;
14601
+ }
14602
+ return { command: command2, env: sourceBuildEnvironment(failedCommand) };
14603
+ }
14604
+ function withoutExtraIndexUrl(command2) {
14605
+ const args = [];
14606
+ let changed = false;
14607
+ for (let index = 0; index < command2.args.length; index += 1) {
14608
+ const arg = command2.args[index];
14609
+ const next = command2.args[index + 1];
14610
+ if (arg === "--extra-index-url" && next !== void 0) {
14611
+ changed = true;
14612
+ index += 1;
14613
+ continue;
14614
+ }
14615
+ if (arg === "--pip-args" && next?.includes("--extra-index-url") === true) {
14616
+ changed = true;
14617
+ index += 1;
14618
+ continue;
14619
+ }
14620
+ args.push(arg);
14621
+ }
14622
+ return changed ? { ...command2, args } : void 0;
14623
+ }
14624
+ function formatInstallCommand(command2, env = {}) {
14625
+ return [...formatEnvironmentAssignments(env), formatShellCommand(command2.executable, command2.args)].join(" ");
14626
+ }
14627
+ function formatEnvironmentAssignments(env) {
14628
+ return Object.entries(env).map(
14629
+ ([key, value]) => key === "CMAKE_ARGS" ? `${key}="${value.replaceAll('"', '\\"')}"` : `${key}=${shellQuote(value)}`
14630
+ );
14631
+ }
14632
+ function sourceBuildEnvironment(command2) {
14633
+ const env = {};
14634
+ if (extraIndexUrl(command2)?.replace(/\/+$/, "").toLowerCase().endsWith("/metal") === true) {
14635
+ env.CMAKE_ARGS = "-DGGML_METAL=on";
14636
+ }
14637
+ env.CMAKE_BUILD_PARALLEL_LEVEL = "2";
14638
+ return env;
14639
+ }
14640
+ function extraIndexUrl(command2) {
14641
+ for (let index = 0; index < command2.args.length; index += 1) {
14642
+ const arg = command2.args[index];
14643
+ if (arg === "--extra-index-url") {
14644
+ return command2.args[index + 1];
14645
+ }
14646
+ if (arg === "--pip-args") {
14647
+ const match = command2.args[index + 1]?.match(/(?:^|\s)--extra-index-url\s+(\S+)/);
14648
+ if (match) {
14649
+ return match[1];
14650
+ }
14651
+ }
14652
+ }
14653
+ return void 0;
14516
14654
  }
14517
14655
  async function offerToInstallUv() {
14518
14656
  if (import_node_process4.stdin.isTTY !== true || import_node_process4.stdout.isTTY !== true) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadnote",
3
- "version": "1.4.4",
3
+ "version": "1.4.5",
4
4
  "type": "commonjs",
5
5
  "main": "dist/threadnote.cjs",
6
6
  "description": "Shared local context and handoffs for development agents",