@infersec/conduit 1.6.1 → 1.6.2

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.js CHANGED
@@ -6,7 +6,7 @@ const __dirname = __pathDirname(__filename);
6
6
 
7
7
  import { parseArgs } from 'node:util';
8
8
  import 'node:crypto';
9
- import { a as asError, s as startInferenceAgent } from './start-BS3RcUet.js';
9
+ import { a as asError, s as startInferenceAgent } from './start-CQayYNYZ.js';
10
10
  import 'argon2';
11
11
  import 'node:child_process';
12
12
  import 'node:stream';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ const __filename = __fileURLToPath(import.meta.url);
5
5
  const __dirname = __pathDirname(__filename);
6
6
 
7
7
  import 'node:crypto';
8
- import { s as startInferenceAgent, a as asError } from './start-BS3RcUet.js';
8
+ import { s as startInferenceAgent, a as asError } from './start-CQayYNYZ.js';
9
9
  import 'argon2';
10
10
  import 'node:child_process';
11
11
  import 'node:stream';
@@ -31,7 +31,7 @@ import require$$1$5, { fileURLToPath } from 'node:url';
31
31
  import require$$1$6 from 'node:async_hooks';
32
32
  import require$$1$7 from 'node:console';
33
33
  import require$$0$b, { mkdir, readFile, writeFile, stat, unlink, rename, realpath, readlink, readdir, lstat } from 'node:fs/promises';
34
- import path$1, { dirname, join, win32, posix } from 'node:path';
34
+ import path$1, { join, dirname, win32, posix } from 'node:path';
35
35
  import require$$1$8 from 'node:dns';
36
36
  import require$$2$4 from 'node:sqlite';
37
37
  import require$$0$c from 'path';
@@ -96521,7 +96521,9 @@ async function downloadModelViaHuggingFace({ format, huggingFaceToken, modelSlug
96521
96521
  modelSlug,
96522
96522
  revision
96523
96523
  });
96524
- if (rangeInfo.supportsRanges) {
96524
+ const { partialPath } = getDownloadPaths({ filePath: file.path, targetDirectory });
96525
+ const shouldAttemptRange = rangeInfo.supportsRanges || existsSync(partialPath);
96526
+ if (shouldAttemptRange) {
96525
96527
  try {
96526
96528
  await downloadFileWithRange({
96527
96529
  accessToken,
@@ -96571,6 +96573,12 @@ function encodePathSegments(path) {
96571
96573
  .map(segment => encodeURIComponent(segment))
96572
96574
  .join("/");
96573
96575
  }
96576
+ function encodeRepoSlug(modelSlug) {
96577
+ return modelSlug
96578
+ .split("/")
96579
+ .map(segment => encodeURIComponent(segment))
96580
+ .join("/");
96581
+ }
96574
96582
  function parseModelRevision(modelSlugWithRevision) {
96575
96583
  const revisionIndex = modelSlugWithRevision.indexOf("@");
96576
96584
  if (revisionIndex === -1) {
@@ -96589,11 +96597,17 @@ function getDownloadPaths({ filePath, targetDirectory }) {
96589
96597
  };
96590
96598
  }
96591
96599
  function getResolveURL({ filePath, modelSlug, revision }) {
96592
- const encodedRepo = encodeURIComponent(modelSlug);
96600
+ const encodedRepo = encodeRepoSlug(modelSlug);
96593
96601
  const encodedRevision = encodeURIComponent(revision);
96594
96602
  const encodedPath = encodePathSegments(filePath);
96595
96603
  return `https://huggingface.co/${encodedRepo}/resolve/${encodedRevision}/${encodedPath}`;
96596
96604
  }
96605
+ function getResolveDownloadURL({ filePath, modelSlug, revision }) {
96606
+ const baseUrl = getResolveURL({ filePath, modelSlug, revision });
96607
+ const resolvedUrl = new URL(baseUrl);
96608
+ resolvedUrl.searchParams.set("download", "1");
96609
+ return resolvedUrl.toString();
96610
+ }
96597
96611
  class RangeNotSupportedError extends Error {
96598
96612
  constructor(message) {
96599
96613
  super(message);
@@ -96608,39 +96622,128 @@ function getAuthHeaders(accessToken) {
96608
96622
  Authorization: `Bearer ${accessToken}`
96609
96623
  };
96610
96624
  }
96611
- async function getRangeInfo({ accessToken, filePath, fileSize, modelSlug, revision }) {
96612
- const url = getResolveURL({ filePath, modelSlug, revision });
96625
+ function shouldSendAuthHeader(url) {
96626
+ try {
96627
+ const hostname = new URL(url).hostname.toLowerCase();
96628
+ return hostname.endsWith("huggingface.co") || hostname.endsWith("hf.co");
96629
+ }
96630
+ catch {
96631
+ return false;
96632
+ }
96633
+ }
96634
+ function parseContentRangeTotal(contentRange) {
96635
+ if (!contentRange) {
96636
+ return null;
96637
+ }
96638
+ const totalMatch = /\/(\d+)$/.exec(contentRange);
96639
+ if (!totalMatch) {
96640
+ return null;
96641
+ }
96642
+ const totalSize = Number(totalMatch[1]);
96643
+ return Number.isFinite(totalSize) ? totalSize : null;
96644
+ }
96645
+ async function getDownloadURL({ accessToken, filePath, modelSlug, revision }) {
96646
+ const url = getResolveDownloadURL({ filePath, modelSlug, revision });
96613
96647
  try {
96614
96648
  const response = await undiciExports.fetch(url, {
96615
- method: "HEAD",
96649
+ method: "GET",
96650
+ redirect: "manual",
96616
96651
  headers: getAuthHeaders(accessToken)
96617
96652
  });
96618
- if (!response.ok) {
96653
+ try {
96654
+ if (response.status >= 300 && response.status < 400) {
96655
+ const location = response.headers.get("location");
96656
+ if (location) {
96657
+ return { reason: null, status: response.status, url: location };
96658
+ }
96659
+ }
96660
+ if (response.ok) {
96661
+ return { reason: null, status: response.status, url: response.url };
96662
+ }
96663
+ let responseBody = "<unavailable>";
96664
+ try {
96665
+ responseBody = await response.text();
96666
+ }
96667
+ catch {
96668
+ responseBody = "<failed to read body>";
96669
+ }
96670
+ if (responseBody.length > 2000) {
96671
+ responseBody = responseBody.slice(0, 2000);
96672
+ }
96673
+ console.warn(`Resolve request failed body for ${filePath}: ${responseBody}`);
96619
96674
  return {
96620
- reason: `HEAD request failed with status ${response.status}`,
96621
- supportsRanges: false,
96622
- totalSize: null
96675
+ reason: `Resolve request failed with status ${response.status}`,
96676
+ status: response.status,
96677
+ url: null
96623
96678
  };
96624
96679
  }
96625
- const acceptRanges = response.headers.get("accept-ranges")?.toLowerCase() ?? "";
96626
- const totalSize = Number(response.headers.get("content-length"));
96627
- if (acceptRanges.includes("bytes") === false) {
96680
+ finally {
96681
+ if (response.body && response.bodyUsed === false && response.body.locked === false) {
96682
+ await response.body.cancel();
96683
+ }
96684
+ }
96685
+ }
96686
+ catch (error) {
96687
+ const parsed = asError(error);
96688
+ return {
96689
+ reason: `Resolve request failed: ${parsed.message}`,
96690
+ status: null,
96691
+ url: null
96692
+ };
96693
+ }
96694
+ }
96695
+ async function getRangeInfo({ accessToken, filePath, fileSize, modelSlug, revision }) {
96696
+ const downloadUrlInfo = await getDownloadURL({
96697
+ accessToken,
96698
+ filePath,
96699
+ modelSlug,
96700
+ revision
96701
+ });
96702
+ if (!downloadUrlInfo.url) {
96703
+ return {
96704
+ reason: downloadUrlInfo.reason ?? "Resolve request failed",
96705
+ supportsRanges: false,
96706
+ totalSize: fileSize
96707
+ };
96708
+ }
96709
+ try {
96710
+ const response = await undiciExports.fetch(downloadUrlInfo.url, {
96711
+ method: "GET",
96712
+ headers: {
96713
+ ...(shouldSendAuthHeader(downloadUrlInfo.url) ? getAuthHeaders(accessToken) : {}),
96714
+ Range: "bytes=0-0"
96715
+ }
96716
+ });
96717
+ try {
96718
+ if (response.status === 206) {
96719
+ const totalSize = parseContentRangeTotal(response.headers.get("content-range"));
96720
+ return {
96721
+ reason: null,
96722
+ supportsRanges: true,
96723
+ totalSize: totalSize ?? fileSize
96724
+ };
96725
+ }
96726
+ if (response.status === 200) {
96727
+ return {
96728
+ reason: "Server returned 200 to range probe",
96729
+ supportsRanges: false,
96730
+ totalSize: fileSize
96731
+ };
96732
+ }
96628
96733
  return {
96629
- reason: "Server does not advertise range support",
96734
+ reason: `Range probe failed with status ${response.status}`,
96630
96735
  supportsRanges: false,
96631
- totalSize: Number.isFinite(totalSize) ? totalSize : fileSize
96736
+ totalSize: fileSize
96632
96737
  };
96633
96738
  }
96634
- return {
96635
- reason: null,
96636
- supportsRanges: true,
96637
- totalSize: Number.isFinite(totalSize) ? totalSize : fileSize
96638
- };
96739
+ finally {
96740
+ await response.body?.cancel();
96741
+ }
96639
96742
  }
96640
96743
  catch (error) {
96641
96744
  const parsed = asError(error);
96642
96745
  return {
96643
- reason: `HEAD request failed: ${parsed.message}`,
96746
+ reason: `Range probe failed: ${parsed.message}`,
96644
96747
  supportsRanges: false,
96645
96748
  totalSize: fileSize
96646
96749
  };
@@ -96670,6 +96773,14 @@ async function downloadFileFull({ accessToken, filePath, fileSize, modelSlug, ta
96670
96773
  let progressTimeout = null;
96671
96774
  let lastPercentage = "0.0";
96672
96775
  let lastProgressBytes = 0;
96776
+ const resetProgressTimeout = () => {
96777
+ clearTimeout(progressTimeout);
96778
+ progressTimeout = setTimeout(() => {
96779
+ input.destroy(new Error(`Timed out with no progress for ${DOWNLOAD_PROGRESS_TIMEOUT}ms while downloading ${filePath}. Last progress: ${lastPercentage}% (${lastProgressBytes} / ${fileSize})`));
96780
+ }, DOWNLOAD_PROGRESS_TIMEOUT);
96781
+ };
96782
+ input.on("data", resetProgressTimeout);
96783
+ resetProgressTimeout();
96673
96784
  meter.progress.on("progress", (totalBytes) => {
96674
96785
  lastProgressBytes = totalBytes;
96675
96786
  const percentComplete = ((totalBytes / fileSize) * 100).toFixed(1);
@@ -96677,10 +96788,6 @@ async function downloadFileFull({ accessToken, filePath, fileSize, modelSlug, ta
96677
96788
  console.log(` => ${percentComplete}% (${totalBytes} / ${fileSize})`);
96678
96789
  }
96679
96790
  lastPercentage = percentComplete;
96680
- clearTimeout(progressTimeout);
96681
- progressTimeout = setTimeout(() => {
96682
- input.destroy(new Error(`Timed out with no progress for ${DOWNLOAD_PROGRESS_TIMEOUT}ms while downloading ${filePath}. Last progress: ${lastPercentage}% (${lastProgressBytes} / ${fileSize})`));
96683
- }, DOWNLOAD_PROGRESS_TIMEOUT);
96684
96791
  });
96685
96792
  try {
96686
96793
  await pipeline(input, meter, output);
@@ -96704,7 +96811,6 @@ async function downloadFileFull({ accessToken, filePath, fileSize, modelSlug, ta
96704
96811
  }
96705
96812
  async function downloadFileWithRange({ accessToken, filePath, fileSize, modelSlug, revision, targetDirectory }) {
96706
96813
  const { finalPath, partialPath } = getDownloadPaths({ filePath, targetDirectory });
96707
- const url = getResolveURL({ filePath, modelSlug, revision });
96708
96814
  let lastError = null;
96709
96815
  for (let attempt = 1; attempt <= DOWNLOAD_RETRY_ATTEMPTS_RANGE; attempt++) {
96710
96816
  let startOffset = 0;
@@ -96724,13 +96830,38 @@ async function downloadFileWithRange({ accessToken, filePath, fileSize, modelSlu
96724
96830
  const resumeLabel = startOffset > 0 ? `resuming at ${startOffset}` : "starting";
96725
96831
  console.log("Downloading:", filePath, fileSize, `(attempt ${attempt}, ${resumeLabel})`);
96726
96832
  await mkdir(dirname(finalPath), { recursive: true });
96727
- const response = await undiciExports.fetch(url, {
96833
+ const downloadUrlInfo = await getDownloadURL({
96834
+ accessToken,
96835
+ filePath,
96836
+ modelSlug,
96837
+ revision
96838
+ });
96839
+ if (!downloadUrlInfo.url) {
96840
+ if (downloadUrlInfo.status === 400) {
96841
+ throw new RangeNotSupportedError(downloadUrlInfo.reason ?? "Resolve request failed");
96842
+ }
96843
+ throw new Error(downloadUrlInfo.reason ?? "Resolve request failed");
96844
+ }
96845
+ const response = await undiciExports.fetch(downloadUrlInfo.url, {
96728
96846
  headers: {
96729
- ...getAuthHeaders(accessToken),
96847
+ ...(shouldSendAuthHeader(downloadUrlInfo.url)
96848
+ ? getAuthHeaders(accessToken)
96849
+ : {}),
96730
96850
  Range: `bytes=${startOffset}-`
96731
96851
  }
96732
96852
  });
96733
96853
  if (!response.ok) {
96854
+ let responseBody = "<unavailable>";
96855
+ try {
96856
+ responseBody = await response.text();
96857
+ }
96858
+ catch {
96859
+ responseBody = "<failed to read body>";
96860
+ }
96861
+ if (responseBody.length > 2000) {
96862
+ responseBody = responseBody.slice(0, 2000);
96863
+ }
96864
+ console.warn(`Range request failed response body for ${filePath}: ${responseBody}`);
96734
96865
  throw new Error(`Range request failed with status ${response.status} for ${filePath}`);
96735
96866
  }
96736
96867
  if (startOffset > 0 && response.status !== 206) {
@@ -96745,6 +96876,14 @@ async function downloadFileWithRange({ accessToken, filePath, fileSize, modelSlu
96745
96876
  let progressTimeout = null;
96746
96877
  let lastPercentage = ((startOffset / fileSize) * 100).toFixed(1);
96747
96878
  let lastProgressBytes = startOffset;
96879
+ const resetProgressTimeout = () => {
96880
+ clearTimeout(progressTimeout);
96881
+ progressTimeout = setTimeout(() => {
96882
+ input.destroy(new Error(`Timed out with no progress for ${DOWNLOAD_PROGRESS_TIMEOUT}ms while downloading ${filePath}. Last progress: ${lastPercentage}% (${lastProgressBytes} / ${fileSize})`));
96883
+ }, DOWNLOAD_PROGRESS_TIMEOUT);
96884
+ };
96885
+ input.on("data", resetProgressTimeout);
96886
+ resetProgressTimeout();
96748
96887
  meter.progress.on("progress", (totalBytes) => {
96749
96888
  lastProgressBytes = startOffset + totalBytes;
96750
96889
  const percentComplete = ((lastProgressBytes / fileSize) * 100).toFixed(1);
@@ -96752,10 +96891,6 @@ async function downloadFileWithRange({ accessToken, filePath, fileSize, modelSlu
96752
96891
  console.log(` => ${percentComplete}% (${lastProgressBytes} / ${fileSize})`);
96753
96892
  }
96754
96893
  lastPercentage = percentComplete;
96755
- clearTimeout(progressTimeout);
96756
- progressTimeout = setTimeout(() => {
96757
- input.destroy(new Error(`Timed out with no progress for ${DOWNLOAD_PROGRESS_TIMEOUT}ms while downloading ${filePath}. Last progress: ${lastPercentage}% (${lastProgressBytes} / ${fileSize})`));
96758
- }, DOWNLOAD_PROGRESS_TIMEOUT);
96759
96894
  });
96760
96895
  try {
96761
96896
  await pipeline(input, meter, output);
@@ -96772,6 +96907,9 @@ async function downloadFileWithRange({ accessToken, filePath, fileSize, modelSlu
96772
96907
  }
96773
96908
  catch (error) {
96774
96909
  lastError = asError(error);
96910
+ if (lastError instanceof RangeNotSupportedError) {
96911
+ throw lastError;
96912
+ }
96775
96913
  if (attempt < DOWNLOAD_RETRY_ATTEMPTS_RANGE) {
96776
96914
  console.warn(`Retrying range download (${attempt}/${DOWNLOAD_RETRY_ATTEMPTS_RANGE}) for ${filePath} due to error: ${lastError.message}`);
96777
96915
  continue;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@infersec/conduit",
3
3
  "description": "End user conduit agent for connecting local LLMs to the cloud.",
4
- "version": "1.6.1",
4
+ "version": "1.6.2",
5
5
  "bin": {
6
6
  "infersec-conduit": "./dist/cli.js"
7
7
  },