@infersec/conduit 1.60.1 → 1.61.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/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import path$1, { join, dirname, win32, posix, resolve } from 'node:path';
|
|
|
10
10
|
import * as require$$3$1 from 'node:fs';
|
|
11
11
|
import require$$3__default, { existsSync, createWriteStream, statSync, readFileSync, appendFileSync, writeFileSync, createReadStream } from 'node:fs';
|
|
12
12
|
import process$5, { platform, hrtime, execPath, execArgv } from 'node:process';
|
|
13
|
-
import crypto
|
|
13
|
+
import crypto from 'node:crypto';
|
|
14
14
|
import require$$0$8, { Readable, Transform, PassThrough, getDefaultHighWaterMark, Duplex, Writable } from 'node:stream';
|
|
15
15
|
import 'argon2';
|
|
16
16
|
import require$$1$7, { setDefaultResultOrder } from 'node:dns';
|
|
@@ -120764,8 +120764,36 @@ async function startLlamacpp({ enginePort, targetDirectory }) {
|
|
|
120764
120764
|
return processManager;
|
|
120765
120765
|
}
|
|
120766
120766
|
|
|
120767
|
+
const SAFE_CHARS = /[a-zA-Z0-9\-_.]/;
|
|
120768
|
+
const SEPARATOR = "__";
|
|
120769
|
+
function sanitizeSegment(value) {
|
|
120770
|
+
return value
|
|
120771
|
+
.split("")
|
|
120772
|
+
.map(ch => {
|
|
120773
|
+
if (ch === "/")
|
|
120774
|
+
return SEPARATOR;
|
|
120775
|
+
if (ch === "@")
|
|
120776
|
+
return "_rev_";
|
|
120777
|
+
if (ch === ":")
|
|
120778
|
+
return SEPARATOR;
|
|
120779
|
+
if (SAFE_CHARS.test(ch))
|
|
120780
|
+
return ch;
|
|
120781
|
+
return "";
|
|
120782
|
+
})
|
|
120783
|
+
.join("")
|
|
120784
|
+
.replace(new RegExp(`${SEPARATOR}{2,}`, "g"), SEPARATOR);
|
|
120785
|
+
}
|
|
120786
|
+
function createModelStorageKey(model) {
|
|
120787
|
+
const identifier = model.source.type === "huggingface" ? model.source.slug : model.source.irid;
|
|
120788
|
+
return `${model.source.type}${SEPARATOR}${sanitizeSegment(identifier)}`;
|
|
120789
|
+
}
|
|
120790
|
+
|
|
120767
120791
|
// 2 hours
|
|
120768
120792
|
const ENGINE_FETCH_TIMEOUT_MS$1 = 7200000;
|
|
120793
|
+
// 20 minutes
|
|
120794
|
+
const DOWNLOAD_LOCK_TIMEOUT_MS = 20 * 60 * 1000;
|
|
120795
|
+
// 5 seconds
|
|
120796
|
+
const DOWNLOAD_LOCK_POLL_INTERVAL_MS = 5000;
|
|
120769
120797
|
const ENGINE_AGENT = new undiciExports.Agent({
|
|
120770
120798
|
bodyTimeout: ENGINE_FETCH_TIMEOUT_MS$1,
|
|
120771
120799
|
headersTimeout: ENGINE_FETCH_TIMEOUT_MS$1
|
|
@@ -120805,12 +120833,7 @@ class ModelManager extends EventEmitter {
|
|
|
120805
120833
|
this.parallelism = typeof parallelism === "number" ? parallelism : null;
|
|
120806
120834
|
// this.providerSlugentifier = source.identifier;
|
|
120807
120835
|
this.logger = logger;
|
|
120808
|
-
|
|
120809
|
-
.update(this.model.source.type === "huggingface"
|
|
120810
|
-
? this.model.source.slug
|
|
120811
|
-
: this.model.source.irid)
|
|
120812
|
-
.digest("hex");
|
|
120813
|
-
this.uniqueName = `${this.model.id}-${uniqueHash}`;
|
|
120836
|
+
this.uniqueName = createModelStorageKey(this.model);
|
|
120814
120837
|
this.modelsDirectory = join(root, "models");
|
|
120815
120838
|
}
|
|
120816
120839
|
async fetchOpenAI(path, opts) {
|
|
@@ -120874,14 +120897,20 @@ class ModelManager extends EventEmitter {
|
|
|
120874
120897
|
if (this.model.source.type !== "huggingface") {
|
|
120875
120898
|
throw new Error(`Model source not implemented: ${this.model.source.type}`);
|
|
120876
120899
|
}
|
|
120877
|
-
await
|
|
120878
|
-
|
|
120879
|
-
|
|
120880
|
-
|
|
120881
|
-
|
|
120882
|
-
|
|
120883
|
-
|
|
120884
|
-
|
|
120900
|
+
await this.acquireDownloadLock();
|
|
120901
|
+
try {
|
|
120902
|
+
await downloadModelViaHuggingFace({
|
|
120903
|
+
format: this.model.format,
|
|
120904
|
+
huggingFaceToken: this.model.source.modelSecret,
|
|
120905
|
+
modelSlug: this.model.source.slug,
|
|
120906
|
+
onProgress: onDownloadProgress,
|
|
120907
|
+
progressFilePath: join(this.modelsDirectory, `${this.uniqueName}.progress.json`),
|
|
120908
|
+
targetDirectory: join(this.modelsDirectory, this.uniqueName)
|
|
120909
|
+
});
|
|
120910
|
+
}
|
|
120911
|
+
finally {
|
|
120912
|
+
await this.releaseDownloadLock();
|
|
120913
|
+
}
|
|
120885
120914
|
break;
|
|
120886
120915
|
// case "ollama":
|
|
120887
120916
|
// this.logger.info("Loading model", {
|
|
@@ -121065,6 +121094,63 @@ class ModelManager extends EventEmitter {
|
|
|
121065
121094
|
});
|
|
121066
121095
|
}, 15_000);
|
|
121067
121096
|
}
|
|
121097
|
+
get lockFilePath() {
|
|
121098
|
+
return join(this.modelsDirectory, `${this.uniqueName}.lock`);
|
|
121099
|
+
}
|
|
121100
|
+
async acquireDownloadLock() {
|
|
121101
|
+
await mkdir(this.modelsDirectory, { recursive: true });
|
|
121102
|
+
if (existsSync(this.lockFilePath)) {
|
|
121103
|
+
const age = await this.getLockAge();
|
|
121104
|
+
if (age !== null && age < DOWNLOAD_LOCK_TIMEOUT_MS) {
|
|
121105
|
+
this.logger.info("Download lock held by another process, waiting", {
|
|
121106
|
+
lockAgeMs: age,
|
|
121107
|
+
lockFilePath: this.lockFilePath
|
|
121108
|
+
});
|
|
121109
|
+
await this.waitForDownloadLock();
|
|
121110
|
+
}
|
|
121111
|
+
else {
|
|
121112
|
+
this.logger.info("Stale download lock found, breaking", {
|
|
121113
|
+
lockFilePath: this.lockFilePath
|
|
121114
|
+
});
|
|
121115
|
+
await this.releaseDownloadLock();
|
|
121116
|
+
}
|
|
121117
|
+
}
|
|
121118
|
+
await writeFile(this.lockFilePath, JSON.stringify({ acquiredAt: Date.now() }));
|
|
121119
|
+
this.logger.info("Acquired download lock", { lockFilePath: this.lockFilePath });
|
|
121120
|
+
}
|
|
121121
|
+
async getLockAge() {
|
|
121122
|
+
try {
|
|
121123
|
+
const raw = await readFile(this.lockFilePath, "utf-8");
|
|
121124
|
+
const data = JSON.parse(raw);
|
|
121125
|
+
return Date.now() - data.acquiredAt;
|
|
121126
|
+
}
|
|
121127
|
+
catch {
|
|
121128
|
+
return null;
|
|
121129
|
+
}
|
|
121130
|
+
}
|
|
121131
|
+
async releaseDownloadLock() {
|
|
121132
|
+
try {
|
|
121133
|
+
await unlink(this.lockFilePath);
|
|
121134
|
+
}
|
|
121135
|
+
catch {
|
|
121136
|
+
// already released
|
|
121137
|
+
}
|
|
121138
|
+
}
|
|
121139
|
+
async waitForDownloadLock() {
|
|
121140
|
+
const start = Date.now();
|
|
121141
|
+
while (Date.now() - start < DOWNLOAD_LOCK_TIMEOUT_MS) {
|
|
121142
|
+
await new Promise(resolve => setTimeout(resolve, DOWNLOAD_LOCK_POLL_INTERVAL_MS));
|
|
121143
|
+
if (!existsSync(this.lockFilePath)) {
|
|
121144
|
+
return;
|
|
121145
|
+
}
|
|
121146
|
+
const age = await this.getLockAge();
|
|
121147
|
+
if (age === null || age >= DOWNLOAD_LOCK_TIMEOUT_MS) {
|
|
121148
|
+
await this.releaseDownloadLock();
|
|
121149
|
+
return;
|
|
121150
|
+
}
|
|
121151
|
+
}
|
|
121152
|
+
await this.releaseDownloadLock();
|
|
121153
|
+
}
|
|
121068
121154
|
bindEngineProcessEvents(processManager) {
|
|
121069
121155
|
let hasTerminated = false;
|
|
121070
121156
|
processManager.on("stderr", line => {
|
|
@@ -47,6 +47,11 @@ export declare class ModelManager extends EventEmitter<ModelManagerEvents> {
|
|
|
47
47
|
private waitForEngineReady;
|
|
48
48
|
private clearHealthPoll;
|
|
49
49
|
private startHealthPoll;
|
|
50
|
+
private get lockFilePath();
|
|
51
|
+
private acquireDownloadLock;
|
|
52
|
+
private getLockAge;
|
|
53
|
+
private releaseDownloadLock;
|
|
54
|
+
private waitForDownloadLock;
|
|
50
55
|
private bindEngineProcessEvents;
|
|
51
56
|
private startEngineProcess;
|
|
52
57
|
}
|