open-agents-ai 0.185.24 → 0.185.26
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/index.js +816 -240
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11914,8 +11914,8 @@ async function loadTranscribeCli() {
|
|
|
11914
11914
|
const nvmBase = join19(homedir7(), ".nvm", "versions", "node");
|
|
11915
11915
|
if (existsSync16(nvmBase)) {
|
|
11916
11916
|
try {
|
|
11917
|
-
const { readdirSync:
|
|
11918
|
-
for (const ver of
|
|
11917
|
+
const { readdirSync: readdirSync24 } = await import("node:fs");
|
|
11918
|
+
for (const ver of readdirSync24(nvmBase)) {
|
|
11919
11919
|
const tcPath = join19(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
|
|
11920
11920
|
if (existsSync16(join19(tcPath, "dist", "index.js"))) {
|
|
11921
11921
|
const { createRequire: createRequire6 } = await import("node:module");
|
|
@@ -13960,8 +13960,8 @@ var init_memory_metabolism = __esm({
|
|
|
13960
13960
|
const trajDir = join23(this.cwd, ".oa", "rlm-trajectories");
|
|
13961
13961
|
let lessons = [];
|
|
13962
13962
|
try {
|
|
13963
|
-
const { readdirSync:
|
|
13964
|
-
const files =
|
|
13963
|
+
const { readdirSync: readdirSync24, readFileSync: readFileSync45 } = await import("node:fs");
|
|
13964
|
+
const files = readdirSync24(trajDir).filter((f) => f.endsWith(".jsonl")).sort().reverse().slice(0, 3);
|
|
13965
13965
|
for (const file of files) {
|
|
13966
13966
|
const lines = readFileSync45(join23(trajDir, file), "utf8").split("\n").filter((l) => l.trim());
|
|
13967
13967
|
for (const line of lines) {
|
|
@@ -30460,8 +30460,8 @@ var init_listen = __esm({
|
|
|
30460
30460
|
const nvmBase = join48(homedir11(), ".nvm", "versions", "node");
|
|
30461
30461
|
if (existsSync32(nvmBase)) {
|
|
30462
30462
|
try {
|
|
30463
|
-
const { readdirSync:
|
|
30464
|
-
for (const ver of
|
|
30463
|
+
const { readdirSync: readdirSync24 } = await import("node:fs");
|
|
30464
|
+
for (const ver of readdirSync24(nvmBase)) {
|
|
30465
30465
|
const tcPath = join48(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
|
|
30466
30466
|
if (existsSync32(join48(tcPath, "dist", "index.js"))) {
|
|
30467
30467
|
const { createRequire: createRequire6 } = await import("node:module");
|
|
@@ -35595,6 +35595,351 @@ connect();
|
|
|
35595
35595
|
</body>
|
|
35596
35596
|
</html>`;
|
|
35597
35597
|
}
|
|
35598
|
+
function generatePersonaPlexHTML(ppWsUrl, textPrompt, voicePrompt) {
|
|
35599
|
+
return `<!DOCTYPE html>
|
|
35600
|
+
<html lang="en">
|
|
35601
|
+
<head>
|
|
35602
|
+
<meta charset="UTF-8">
|
|
35603
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
35604
|
+
<title>OA \u2014 PersonaPlex Voice</title>
|
|
35605
|
+
<style>
|
|
35606
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
35607
|
+
body {
|
|
35608
|
+
background: #1a1a1e;
|
|
35609
|
+
color: #b0b0b0;
|
|
35610
|
+
font-family: 'SF Mono', 'Cascadia Code', 'Fira Code', monospace;
|
|
35611
|
+
display: flex;
|
|
35612
|
+
flex-direction: column;
|
|
35613
|
+
min-height: 100vh;
|
|
35614
|
+
}
|
|
35615
|
+
#header {
|
|
35616
|
+
background: #1e1e22;
|
|
35617
|
+
padding: 8px 16px;
|
|
35618
|
+
display: flex;
|
|
35619
|
+
align-items: center;
|
|
35620
|
+
gap: 12px;
|
|
35621
|
+
border-bottom: 1px solid #2a2a30;
|
|
35622
|
+
}
|
|
35623
|
+
#header .accent { color: #b2920a; font-weight: bold; font-size: 0.8rem; }
|
|
35624
|
+
#header .badge { font-size: 0.6rem; color: #555; background: #2a2a30; padding: 2px 6px; border-radius: 2px; }
|
|
35625
|
+
#header .status { font-size: 0.7rem; color: #555; }
|
|
35626
|
+
#header .status.live { color: #b2920a; }
|
|
35627
|
+
#waveform {
|
|
35628
|
+
height: 48px;
|
|
35629
|
+
display: flex;
|
|
35630
|
+
align-items: center;
|
|
35631
|
+
justify-content: center;
|
|
35632
|
+
font-size: 1.6rem;
|
|
35633
|
+
line-height: 1;
|
|
35634
|
+
letter-spacing: 0.04em;
|
|
35635
|
+
white-space: nowrap;
|
|
35636
|
+
overflow: hidden;
|
|
35637
|
+
user-select: none;
|
|
35638
|
+
background: #1e1e22;
|
|
35639
|
+
border-bottom: 1px solid #2a2a30;
|
|
35640
|
+
}
|
|
35641
|
+
#conversation {
|
|
35642
|
+
flex: 1;
|
|
35643
|
+
overflow-y: auto;
|
|
35644
|
+
padding: 12px 16px;
|
|
35645
|
+
}
|
|
35646
|
+
.msg {
|
|
35647
|
+
padding: 6px 0;
|
|
35648
|
+
font-size: 0.82rem;
|
|
35649
|
+
line-height: 1.4;
|
|
35650
|
+
}
|
|
35651
|
+
.msg.user { color: #888; }
|
|
35652
|
+
.msg.user::before { content: '\\25B8 '; color: #555; }
|
|
35653
|
+
.msg.agent { color: #b2920a; }
|
|
35654
|
+
.msg.agent::before { content: '\\25B9 '; color: #b2920a; }
|
|
35655
|
+
#footer {
|
|
35656
|
+
background: #1e1e22;
|
|
35657
|
+
padding: 8px 16px;
|
|
35658
|
+
display: flex;
|
|
35659
|
+
align-items: center;
|
|
35660
|
+
gap: 10px;
|
|
35661
|
+
border-top: 1px solid #2a2a30;
|
|
35662
|
+
}
|
|
35663
|
+
button {
|
|
35664
|
+
background: #2a2a30;
|
|
35665
|
+
border: 1px solid #3a3a42;
|
|
35666
|
+
color: #b2920a;
|
|
35667
|
+
padding: 6px 16px;
|
|
35668
|
+
border-radius: 3px;
|
|
35669
|
+
font-family: inherit;
|
|
35670
|
+
font-size: 0.75rem;
|
|
35671
|
+
cursor: pointer;
|
|
35672
|
+
transition: background 0.15s;
|
|
35673
|
+
}
|
|
35674
|
+
button:hover { background: #3a3a42; }
|
|
35675
|
+
button.active { background: #3a2a10; border-color: #b2920a; }
|
|
35676
|
+
#footer .hint { font-size: 0.65rem; color: #444; }
|
|
35677
|
+
</style>
|
|
35678
|
+
</head>
|
|
35679
|
+
<body>
|
|
35680
|
+
<div id="header">
|
|
35681
|
+
<span class="accent">OA</span>
|
|
35682
|
+
<span class="badge">PersonaPlex 7B</span>
|
|
35683
|
+
<span id="status" class="status">connecting</span>
|
|
35684
|
+
</div>
|
|
35685
|
+
<div id="waveform"></div>
|
|
35686
|
+
<div id="conversation"></div>
|
|
35687
|
+
<div id="footer">
|
|
35688
|
+
<button id="micBtn" onclick="toggleMic()">mic</button>
|
|
35689
|
+
<span class="hint" id="hint">tap to start full-duplex voice</span>
|
|
35690
|
+
</div>
|
|
35691
|
+
|
|
35692
|
+
<script>
|
|
35693
|
+
const waveEl = document.getElementById('waveform');
|
|
35694
|
+
const statusEl = document.getElementById('status');
|
|
35695
|
+
const convoEl = document.getElementById('conversation');
|
|
35696
|
+
const micBtn = document.getElementById('micBtn');
|
|
35697
|
+
const hintEl = document.getElementById('hint');
|
|
35698
|
+
|
|
35699
|
+
// PersonaPlex WebSocket URL (moshi.server)
|
|
35700
|
+
const PP_WS_URL = ${JSON.stringify(ppWsUrl)};
|
|
35701
|
+
const TEXT_PROMPT = ${JSON.stringify(textPrompt)};
|
|
35702
|
+
const VOICE_PROMPT = ${JSON.stringify(voicePrompt)};
|
|
35703
|
+
|
|
35704
|
+
// Braille waveform
|
|
35705
|
+
const DENSITY = ['\\u2800','\\u2840','\\u28C0','\\u28C4','\\u28E4','\\u28E6','\\u28F6','\\u28F7','\\u28FF'];
|
|
35706
|
+
const WAVE = [...DENSITY, ...DENSITY.slice(1,-1).reverse()];
|
|
35707
|
+
const THEME_IDLE = ['#1e1e22','#2a2a30','#333338','#3e3e44','#4a4a52','#555560','#666670','#777780','#888890'];
|
|
35708
|
+
const THEME_SPEAKING = ['#1e1e22','#2a2510','#3a3518','#4a4520','#5a5528','#6a6530','#8a7a20','#a89010','#b2920a'];
|
|
35709
|
+
const THEME_LISTENING = ['#1e1e22','#2a2818','#3a3820','#4a4828','#5a5830','#6a6838','#8a8830','#a8a020','#b2920a'];
|
|
35710
|
+
function buildColorRamp(r) { return [...r, ...r.slice(1,-1).reverse()]; }
|
|
35711
|
+
|
|
35712
|
+
let waveFrame = 0, waveState = 'idle', micLevel = 0;
|
|
35713
|
+
function renderWave() {
|
|
35714
|
+
const cols = Math.min(40, Math.floor(waveEl.offsetWidth / 20)) || 30;
|
|
35715
|
+
const cl = WAVE.length;
|
|
35716
|
+
const theme = waveState==='speaking'?THEME_SPEAKING:waveState==='listening'?THEME_LISTENING:THEME_IDLE;
|
|
35717
|
+
const cr = buildColorRamp(theme);
|
|
35718
|
+
const bp = Math.sin(waveFrame*0.06);
|
|
35719
|
+
const speed = waveState==='speaking'?2.5+bp*0.5:waveState==='listening'?2.0+micLevel*3.0+bp*0.3:1.2+bp*0.8;
|
|
35720
|
+
const ds = waveState==='idle'?0.35+bp*0.15:waveState==='listening'?0.3+micLevel*0.7:0.6+bp*0.2;
|
|
35721
|
+
const sa = 1.5+ds*2.0;
|
|
35722
|
+
let html='';
|
|
35723
|
+
for(let c=0;c<cols;c++){
|
|
35724
|
+
const so=Math.sin(c*0.1+waveFrame*0.02)*sa;
|
|
35725
|
+
const rp=c*speed+waveFrame+so;
|
|
35726
|
+
const np=((rp%cl)+cl)%cl;
|
|
35727
|
+
let wi=Math.round(np)%cl;
|
|
35728
|
+
let amp=wi<=8?wi:16-wi;
|
|
35729
|
+
const sc=Math.round(amp*ds);
|
|
35730
|
+
let si=wi<=8?sc:16-sc;
|
|
35731
|
+
si=Math.max(0,Math.min(cl-1,si));
|
|
35732
|
+
html+='<span style="color:'+cr[si]+'">'+WAVE[si]+'</span>';
|
|
35733
|
+
}
|
|
35734
|
+
waveEl.innerHTML=html;
|
|
35735
|
+
}
|
|
35736
|
+
setInterval(()=>{waveFrame++;renderWave();},80);
|
|
35737
|
+
|
|
35738
|
+
// \u2500\u2500 PersonaPlex connection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
35739
|
+
let ppWs = null;
|
|
35740
|
+
let micCtx = null, micStream = null, micActive = false;
|
|
35741
|
+
let playbackCtx = null;
|
|
35742
|
+
let handshakeReceived = false;
|
|
35743
|
+
let opusEncoder = null, opusDecoder = null;
|
|
35744
|
+
let agentText = '';
|
|
35745
|
+
|
|
35746
|
+
function connectPP() {
|
|
35747
|
+
const url = PP_WS_URL + '/api/chat?text_prompt=' + encodeURIComponent(TEXT_PROMPT) +
|
|
35748
|
+
'&voice_prompt=' + encodeURIComponent(VOICE_PROMPT) + '&seed=-1';
|
|
35749
|
+
statusEl.textContent = 'connecting to PersonaPlex...';
|
|
35750
|
+
ppWs = new WebSocket(url);
|
|
35751
|
+
ppWs.binaryType = 'arraybuffer';
|
|
35752
|
+
|
|
35753
|
+
ppWs.onopen = () => {
|
|
35754
|
+
statusEl.textContent = 'loading persona...';
|
|
35755
|
+
statusEl.className = 'status';
|
|
35756
|
+
};
|
|
35757
|
+
|
|
35758
|
+
ppWs.onmessage = (evt) => {
|
|
35759
|
+
if (!(evt.data instanceof ArrayBuffer)) return;
|
|
35760
|
+
const bytes = new Uint8Array(evt.data);
|
|
35761
|
+
if (bytes.length === 0) return;
|
|
35762
|
+
|
|
35763
|
+
const kind = bytes[0];
|
|
35764
|
+
if (kind === 0x00) {
|
|
35765
|
+
// Handshake \u2014 server is ready
|
|
35766
|
+
handshakeReceived = true;
|
|
35767
|
+
statusEl.textContent = 'ready \u2014 full duplex';
|
|
35768
|
+
statusEl.className = 'status live';
|
|
35769
|
+
hintEl.textContent = 'tap mic to start talking';
|
|
35770
|
+
} else if (kind === 0x01) {
|
|
35771
|
+
// Audio (Opus) from PersonaPlex
|
|
35772
|
+
const opusData = bytes.slice(1);
|
|
35773
|
+
playOpus(opusData);
|
|
35774
|
+
waveState = 'speaking';
|
|
35775
|
+
} else if (kind === 0x02) {
|
|
35776
|
+
// Text token from PersonaPlex
|
|
35777
|
+
const text = new TextDecoder().decode(bytes.slice(1));
|
|
35778
|
+
agentText += text;
|
|
35779
|
+
// Flush on sentence boundary
|
|
35780
|
+
if (/[.!?]\\s*$/.test(agentText) || agentText.length > 200) {
|
|
35781
|
+
addTranscript('agent', agentText.trim());
|
|
35782
|
+
agentText = '';
|
|
35783
|
+
}
|
|
35784
|
+
}
|
|
35785
|
+
};
|
|
35786
|
+
|
|
35787
|
+
ppWs.onclose = () => {
|
|
35788
|
+
handshakeReceived = false;
|
|
35789
|
+
statusEl.textContent = 'disconnected';
|
|
35790
|
+
statusEl.className = 'status';
|
|
35791
|
+
if (agentText.trim()) { addTranscript('agent', agentText.trim()); agentText=''; }
|
|
35792
|
+
setTimeout(connectPP, 3000);
|
|
35793
|
+
};
|
|
35794
|
+
|
|
35795
|
+
ppWs.onerror = () => {};
|
|
35796
|
+
}
|
|
35797
|
+
|
|
35798
|
+
function addTranscript(speaker, text) {
|
|
35799
|
+
const div = document.createElement('div');
|
|
35800
|
+
div.className = 'msg ' + speaker;
|
|
35801
|
+
div.textContent = text;
|
|
35802
|
+
convoEl.appendChild(div);
|
|
35803
|
+
convoEl.scrollTop = convoEl.scrollHeight;
|
|
35804
|
+
}
|
|
35805
|
+
|
|
35806
|
+
// \u2500\u2500 Opus playback via WebAudio \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
35807
|
+
// PersonaPlex sends Opus-encoded audio via sphn. We decode via AudioDecoder or fallback.
|
|
35808
|
+
|
|
35809
|
+
async function playOpus(opusData) {
|
|
35810
|
+
// Use raw PCM fallback \u2014 decode Opus in a worker or use AudioContext.decodeAudioData
|
|
35811
|
+
// For simplicity, we'll use the AudioDecoder API if available
|
|
35812
|
+
if (!playbackCtx) playbackCtx = new AudioContext({ sampleRate: 24000 });
|
|
35813
|
+
|
|
35814
|
+
try {
|
|
35815
|
+
// Wrap opus in a minimal ogg-like container isn't practical,
|
|
35816
|
+
// so we attempt raw decode via AudioDecoder (Chrome 94+)
|
|
35817
|
+
if (typeof AudioDecoder !== 'undefined' && !opusDecoder) {
|
|
35818
|
+
opusDecoder = new AudioDecoder({
|
|
35819
|
+
output: (frame) => {
|
|
35820
|
+
const buf = playbackCtx.createBuffer(1, frame.numberOfFrames, frame.sampleRate);
|
|
35821
|
+
frame.copyTo(buf.getChannelData(0), { planeIndex: 0 });
|
|
35822
|
+
const src = playbackCtx.createBufferSource();
|
|
35823
|
+
src.buffer = buf;
|
|
35824
|
+
src.connect(playbackCtx.destination);
|
|
35825
|
+
src.start();
|
|
35826
|
+
frame.close();
|
|
35827
|
+
},
|
|
35828
|
+
error: (e) => { console.warn('opus decode err:', e); }
|
|
35829
|
+
});
|
|
35830
|
+
opusDecoder.configure({
|
|
35831
|
+
codec: 'opus',
|
|
35832
|
+
sampleRate: 24000,
|
|
35833
|
+
numberOfChannels: 1,
|
|
35834
|
+
});
|
|
35835
|
+
}
|
|
35836
|
+
if (opusDecoder && opusDecoder.state === 'configured') {
|
|
35837
|
+
opusDecoder.decode(new EncodedAudioChunk({
|
|
35838
|
+
type: 'key',
|
|
35839
|
+
timestamp: 0,
|
|
35840
|
+
data: opusData,
|
|
35841
|
+
}));
|
|
35842
|
+
}
|
|
35843
|
+
} catch (e) {
|
|
35844
|
+
// Fallback: skip frame
|
|
35845
|
+
console.warn('Opus playback unavailable:', e);
|
|
35846
|
+
}
|
|
35847
|
+
}
|
|
35848
|
+
|
|
35849
|
+
// \u2500\u2500 Microphone \u2192 Opus \u2192 PersonaPlex \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
35850
|
+
|
|
35851
|
+
let scriptProcessor = null;
|
|
35852
|
+
|
|
35853
|
+
async function toggleMic() {
|
|
35854
|
+
if (micActive) { stopMic(); return; }
|
|
35855
|
+
if (!handshakeReceived) {
|
|
35856
|
+
hintEl.textContent = 'waiting for PersonaPlex...';
|
|
35857
|
+
return;
|
|
35858
|
+
}
|
|
35859
|
+
try {
|
|
35860
|
+
if (!micCtx) micCtx = new AudioContext({ sampleRate: 24000 });
|
|
35861
|
+
micStream = await navigator.mediaDevices.getUserMedia({
|
|
35862
|
+
audio: { sampleRate: 24000, channelCount: 1, echoCancellation: true, noiseSuppression: true }
|
|
35863
|
+
});
|
|
35864
|
+
const source = micCtx.createMediaStreamSource(micStream);
|
|
35865
|
+
scriptProcessor = micCtx.createScriptProcessor(1920, 1, 1); // 80ms at 24kHz
|
|
35866
|
+
|
|
35867
|
+
// Use AudioEncoder to send Opus to PersonaPlex
|
|
35868
|
+
let audioEncoder = null;
|
|
35869
|
+
if (typeof AudioEncoder !== 'undefined') {
|
|
35870
|
+
audioEncoder = new AudioEncoder({
|
|
35871
|
+
output: (chunk) => {
|
|
35872
|
+
if (!ppWs || ppWs.readyState !== 1) return;
|
|
35873
|
+
const buf = new ArrayBuffer(chunk.byteLength + 1);
|
|
35874
|
+
const view = new Uint8Array(buf);
|
|
35875
|
+
view[0] = 0x01; // audio kind byte
|
|
35876
|
+
chunk.copyTo(view.subarray(1));
|
|
35877
|
+
ppWs.send(buf);
|
|
35878
|
+
},
|
|
35879
|
+
error: (e) => { console.warn('encode err:', e); }
|
|
35880
|
+
});
|
|
35881
|
+
audioEncoder.configure({
|
|
35882
|
+
codec: 'opus',
|
|
35883
|
+
sampleRate: 24000,
|
|
35884
|
+
numberOfChannels: 1,
|
|
35885
|
+
bitrate: 24000,
|
|
35886
|
+
});
|
|
35887
|
+
}
|
|
35888
|
+
|
|
35889
|
+
let frameCounter = 0;
|
|
35890
|
+
scriptProcessor.onaudioprocess = (e) => {
|
|
35891
|
+
if (!micActive || !ppWs || ppWs.readyState !== 1) return;
|
|
35892
|
+
const input = e.inputBuffer.getChannelData(0);
|
|
35893
|
+
micLevel = Math.min(1, rms(input) * 5);
|
|
35894
|
+
|
|
35895
|
+
if (audioEncoder && audioEncoder.state === 'configured') {
|
|
35896
|
+
const data = new AudioData({
|
|
35897
|
+
format: 'f32',
|
|
35898
|
+
sampleRate: 24000,
|
|
35899
|
+
numberOfFrames: input.length,
|
|
35900
|
+
numberOfChannels: 1,
|
|
35901
|
+
timestamp: frameCounter * (input.length / 24000) * 1e6,
|
|
35902
|
+
data: input,
|
|
35903
|
+
});
|
|
35904
|
+
audioEncoder.encode(data);
|
|
35905
|
+
data.close();
|
|
35906
|
+
frameCounter++;
|
|
35907
|
+
}
|
|
35908
|
+
};
|
|
35909
|
+
source.connect(scriptProcessor);
|
|
35910
|
+
scriptProcessor.connect(micCtx.destination);
|
|
35911
|
+
micActive = true;
|
|
35912
|
+
waveState = 'listening';
|
|
35913
|
+
micBtn.textContent = 'stop';
|
|
35914
|
+
micBtn.classList.add('active');
|
|
35915
|
+
hintEl.textContent = 'full-duplex active \u2014 speak freely';
|
|
35916
|
+
} catch (err) {
|
|
35917
|
+
hintEl.textContent = 'mic error: ' + err.message;
|
|
35918
|
+
}
|
|
35919
|
+
}
|
|
35920
|
+
|
|
35921
|
+
function stopMic() {
|
|
35922
|
+
micActive = false;
|
|
35923
|
+
micLevel = 0;
|
|
35924
|
+
waveState = 'idle';
|
|
35925
|
+
if (scriptProcessor) { scriptProcessor.disconnect(); scriptProcessor = null; }
|
|
35926
|
+
if (micStream) { micStream.getTracks().forEach(t => t.stop()); micStream = null; }
|
|
35927
|
+
micBtn.textContent = 'mic';
|
|
35928
|
+
micBtn.classList.remove('active');
|
|
35929
|
+
hintEl.textContent = 'tap mic to start talking';
|
|
35930
|
+
}
|
|
35931
|
+
|
|
35932
|
+
function rms(arr) {
|
|
35933
|
+
let sum = 0;
|
|
35934
|
+
for (let i = 0; i < arr.length; i++) sum += arr[i] * arr[i];
|
|
35935
|
+
return Math.sqrt(sum / arr.length);
|
|
35936
|
+
}
|
|
35937
|
+
|
|
35938
|
+
connectPP();
|
|
35939
|
+
</script>
|
|
35940
|
+
</body>
|
|
35941
|
+
</html>`;
|
|
35942
|
+
}
|
|
35598
35943
|
function renderVoiceSessionStart(tunnelUrl) {
|
|
35599
35944
|
process.stdout.write(`
|
|
35600
35945
|
${c2.cyan("\u2601")} ${c2.bold("Live Voice Session")}
|
|
@@ -35641,6 +35986,10 @@ var init_voice_session = __esm({
|
|
|
35641
35986
|
runtimeTimer = null;
|
|
35642
35987
|
idleTimer = null;
|
|
35643
35988
|
ttsSpeaking = false;
|
|
35989
|
+
/** When set, serve PersonaPlex frontend that connects directly to this moshi.server WS URL */
|
|
35990
|
+
personaPlexWsUrl = null;
|
|
35991
|
+
personaPlexTextPrompt = "You enjoy having a good conversation.";
|
|
35992
|
+
personaPlexVoicePrompt = "NATF2.pt";
|
|
35644
35993
|
/** Idle timeout before auto-closing (ms). Default 60s — no users connected → session ends. */
|
|
35645
35994
|
idleTimeoutMs = 6e4;
|
|
35646
35995
|
/** Callback invoked when user audio chunk arrives (PCM 16kHz 16-bit mono) */
|
|
@@ -35829,7 +36178,11 @@ var init_voice_session = __esm({
|
|
|
35829
36178
|
handleHTTP(req, res) {
|
|
35830
36179
|
if (req.url === "/" || req.url === "/index.html") {
|
|
35831
36180
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
35832
|
-
|
|
36181
|
+
if (this.personaPlexWsUrl) {
|
|
36182
|
+
res.end(generatePersonaPlexHTML(this.personaPlexWsUrl, this.personaPlexTextPrompt, this.personaPlexVoicePrompt));
|
|
36183
|
+
} else {
|
|
36184
|
+
res.end(generateFrontendHTML());
|
|
36185
|
+
}
|
|
35833
36186
|
} else if (req.url === "/health") {
|
|
35834
36187
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
35835
36188
|
res.end(JSON.stringify({
|
|
@@ -40727,18 +41080,24 @@ var init_tui_select = __esm({
|
|
|
40727
41080
|
// packages/cli/dist/tui/personaplex.js
|
|
40728
41081
|
var personaplex_exports = {};
|
|
40729
41082
|
__export(personaplex_exports, {
|
|
41083
|
+
autoSetupPersonaPlex: () => autoSetupPersonaPlex,
|
|
41084
|
+
clonePersonaPlexVoice: () => clonePersonaPlexVoice,
|
|
40730
41085
|
detectPersonaPlexCapability: () => detectPersonaPlexCapability,
|
|
40731
41086
|
getPersonaPlexWSUrl: () => getPersonaPlexWSUrl,
|
|
40732
41087
|
installPersonaPlex: () => installPersonaPlex,
|
|
40733
41088
|
isPersonaPlexInstalled: () => isPersonaPlexInstalled,
|
|
40734
41089
|
isPersonaPlexRunning: () => isPersonaPlexRunning,
|
|
41090
|
+
listPersonaPlexVoices: () => listPersonaPlexVoices,
|
|
41091
|
+
patchFrontendVoiceList: () => patchFrontendVoiceList,
|
|
41092
|
+
provisionShippedVoices: () => provisionShippedVoices,
|
|
40735
41093
|
startPersonaPlexDaemon: () => startPersonaPlexDaemon,
|
|
40736
41094
|
stopPersonaPlex: () => stopPersonaPlex
|
|
40737
41095
|
});
|
|
40738
|
-
import { existsSync as existsSync37, writeFileSync as writeFileSync16, readFileSync as readFileSync28, mkdirSync as mkdirSync15 } from "node:fs";
|
|
40739
|
-
import { join as join54 } from "node:path";
|
|
41096
|
+
import { existsSync as existsSync37, writeFileSync as writeFileSync16, readFileSync as readFileSync28, mkdirSync as mkdirSync15, copyFileSync as copyFileSync2, readdirSync as readdirSync11 } from "node:fs";
|
|
41097
|
+
import { join as join54, dirname as dirname18 } from "node:path";
|
|
40740
41098
|
import { homedir as homedir13 } from "node:os";
|
|
40741
41099
|
import { execSync as execSync27, spawn as spawn19 } from "node:child_process";
|
|
41100
|
+
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
40742
41101
|
function detectPersonaPlexCapability() {
|
|
40743
41102
|
try {
|
|
40744
41103
|
const nvsmi = execSync27("nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits", {
|
|
@@ -40786,7 +41145,7 @@ function getPersonaPlexWSUrl() {
|
|
|
40786
41145
|
if (!existsSync37(PORT_FILE))
|
|
40787
41146
|
return null;
|
|
40788
41147
|
const port = parseInt(readFileSync28(PORT_FILE, "utf8").trim(), 10);
|
|
40789
|
-
return isNaN(port) ? null : `
|
|
41148
|
+
return isNaN(port) ? null : `wss://127.0.0.1:${port}`;
|
|
40790
41149
|
}
|
|
40791
41150
|
function isPersonaPlexInstalled() {
|
|
40792
41151
|
return existsSync37(join54(PERSONAPLEX_DIR, "model_ready"));
|
|
@@ -40807,167 +41166,57 @@ async function installPersonaPlex(onInfo) {
|
|
|
40807
41166
|
}
|
|
40808
41167
|
const pip = process.platform === "win32" ? join54(venvDir, "Scripts", "pip.exe") : join54(venvDir, "bin", "pip");
|
|
40809
41168
|
const python = process.platform === "win32" ? join54(venvDir, "Scripts", "python.exe") : join54(venvDir, "bin", "python3");
|
|
40810
|
-
log("
|
|
41169
|
+
log("Checking system dependencies (libopus)...");
|
|
40811
41170
|
try {
|
|
40812
|
-
|
|
40813
|
-
|
|
40814
|
-
|
|
40815
|
-
|
|
41171
|
+
if (process.platform === "linux") {
|
|
41172
|
+
execSync27("dpkg -l libopus-dev 2>/dev/null || sudo apt-get install -y libopus-dev", { timeout: 3e4, stdio: "pipe" });
|
|
41173
|
+
} else if (process.platform === "darwin") {
|
|
41174
|
+
execSync27("brew list opus 2>/dev/null || brew install opus", { timeout: 6e4, stdio: "pipe" });
|
|
41175
|
+
}
|
|
41176
|
+
} catch {
|
|
40816
41177
|
}
|
|
40817
|
-
log("
|
|
41178
|
+
log("Installing PersonaPlex (moshi package)...");
|
|
41179
|
+
const repoDir = join54(PERSONAPLEX_DIR, "personaplex-repo");
|
|
40818
41180
|
try {
|
|
40819
|
-
|
|
41181
|
+
if (!existsSync37(repoDir)) {
|
|
41182
|
+
execSync27(`git clone https://github.com/NVIDIA/personaplex.git "${repoDir}"`, { timeout: 12e4, stdio: "pipe" });
|
|
41183
|
+
}
|
|
41184
|
+
execSync27(`"${pip}" install --quiet "${join54(repoDir, "moshi")}/."`, { timeout: 3e5, stdio: "pipe" });
|
|
40820
41185
|
} catch (err) {
|
|
40821
|
-
log(`
|
|
41186
|
+
log(`Moshi install failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
41187
|
+
try {
|
|
41188
|
+
execSync27(`"${pip}" install --quiet torch torchaudio websockets soundfile huggingface_hub`, { timeout: 3e5, stdio: "pipe" });
|
|
41189
|
+
} catch {
|
|
41190
|
+
}
|
|
40822
41191
|
return false;
|
|
40823
41192
|
}
|
|
41193
|
+
const serverPy = join54(venvDir, "lib", `python3.${process.versions.node ? "12" : "10"}`, "site-packages", "moshi", "server.py");
|
|
41194
|
+
try {
|
|
41195
|
+
const sitePackages = execSync27(`"${python}" -c "import moshi, os; print(os.path.dirname(moshi.__file__))"`, {
|
|
41196
|
+
encoding: "utf8",
|
|
41197
|
+
timeout: 5e3,
|
|
41198
|
+
stdio: "pipe"
|
|
41199
|
+
}).trim();
|
|
41200
|
+
const serverFile = join54(sitePackages, "server.py");
|
|
41201
|
+
if (existsSync37(serverFile)) {
|
|
41202
|
+
let src = readFileSync28(serverFile, "utf8");
|
|
41203
|
+
if (src.includes('int(request["seed"])')) {
|
|
41204
|
+
src = src.replace('int(request["seed"])', 'int(request.query["seed"])');
|
|
41205
|
+
writeFileSync16(serverFile, src);
|
|
41206
|
+
log("Applied seed parameter bug fix to moshi server.");
|
|
41207
|
+
}
|
|
41208
|
+
}
|
|
41209
|
+
} catch {
|
|
41210
|
+
}
|
|
41211
|
+
log("PersonaPlex installed. Model will download on first launch (~14GB).");
|
|
40824
41212
|
writeFileSync16(join54(PERSONAPLEX_DIR, "model_ready"), (/* @__PURE__ */ new Date()).toISOString());
|
|
40825
41213
|
log("PersonaPlex installed successfully.");
|
|
40826
41214
|
return true;
|
|
40827
41215
|
}
|
|
40828
|
-
function writeDaemonScript() {
|
|
40829
|
-
const scriptPath2 = join54(PERSONAPLEX_DIR, "daemon.py");
|
|
40830
|
-
const modelDir2 = join54(PERSONAPLEX_DIR, "model");
|
|
40831
|
-
const script = `#!/usr/bin/env python3
|
|
40832
|
-
"""PersonaPlex daemon \u2014 full-duplex voice WebSocket server."""
|
|
40833
|
-
import asyncio
|
|
40834
|
-
import json
|
|
40835
|
-
import os
|
|
40836
|
-
import signal
|
|
40837
|
-
import sys
|
|
40838
|
-
import numpy as np
|
|
40839
|
-
|
|
40840
|
-
# Write PID file
|
|
40841
|
-
pid_file = os.path.join(os.path.dirname(__file__), "daemon.pid")
|
|
40842
|
-
with open(pid_file, "w") as f:
|
|
40843
|
-
f.write(str(os.getpid()))
|
|
40844
|
-
|
|
40845
|
-
try:
|
|
40846
|
-
import torch
|
|
40847
|
-
import torchaudio
|
|
40848
|
-
from transformers import AutoModel, AutoProcessor
|
|
40849
|
-
import websockets
|
|
40850
|
-
import soundfile as sf
|
|
40851
|
-
except ImportError as e:
|
|
40852
|
-
print(f"Missing dependency: {e}", file=sys.stderr)
|
|
40853
|
-
sys.exit(1)
|
|
40854
|
-
|
|
40855
|
-
MODEL_DIR = ${JSON.stringify(modelDir2)}
|
|
40856
|
-
SAMPLE_RATE = 24000
|
|
40857
|
-
PORT = int(os.environ.get("PERSONAPLEX_PORT", "0")) # 0 = auto-assign
|
|
40858
|
-
|
|
40859
|
-
print("Loading PersonaPlex model...", file=sys.stderr)
|
|
40860
|
-
device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
40861
|
-
dtype = torch.bfloat16 if device == "cuda" else torch.float32
|
|
40862
|
-
|
|
40863
|
-
try:
|
|
40864
|
-
model = AutoModel.from_pretrained(MODEL_DIR, trust_remote_code=True, torch_dtype=dtype)
|
|
40865
|
-
model = model.to(device)
|
|
40866
|
-
model.eval()
|
|
40867
|
-
processor = AutoProcessor.from_pretrained(MODEL_DIR, trust_remote_code=True)
|
|
40868
|
-
print(f"Model loaded on {device}", file=sys.stderr)
|
|
40869
|
-
except Exception as e:
|
|
40870
|
-
print(f"Model load failed: {e}", file=sys.stderr)
|
|
40871
|
-
sys.exit(1)
|
|
40872
|
-
|
|
40873
|
-
# Persona state
|
|
40874
|
-
persona_text = "You are a helpful voice assistant."
|
|
40875
|
-
voice_prompt = None # Audio conditioning tensor
|
|
40876
|
-
|
|
40877
|
-
async def handle_client(websocket, path):
|
|
40878
|
-
"""Handle a single WebSocket client connection."""
|
|
40879
|
-
global persona_text, voice_prompt
|
|
40880
|
-
print(f"Client connected from {websocket.remote_address}", file=sys.stderr)
|
|
40881
|
-
|
|
40882
|
-
try:
|
|
40883
|
-
async for message in websocket:
|
|
40884
|
-
if isinstance(message, str):
|
|
40885
|
-
# JSON control message
|
|
40886
|
-
try:
|
|
40887
|
-
msg = json.loads(message)
|
|
40888
|
-
if msg.get("type") == "persona":
|
|
40889
|
-
persona_text = msg.get("text", persona_text)
|
|
40890
|
-
print(f"Persona updated: {persona_text[:60]}", file=sys.stderr)
|
|
40891
|
-
elif msg.get("type") == "voice_prompt":
|
|
40892
|
-
# Audio conditioning: base64 PCM \u2192 tensor
|
|
40893
|
-
import base64
|
|
40894
|
-
pcm_bytes = base64.b64decode(msg["audio"])
|
|
40895
|
-
audio = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32) / 32768.0
|
|
40896
|
-
voice_prompt = torch.tensor(audio, dtype=dtype, device=device).unsqueeze(0)
|
|
40897
|
-
print(f"Voice prompt set ({len(audio)} samples)", file=sys.stderr)
|
|
40898
|
-
elif msg.get("type") == "keepalive":
|
|
40899
|
-
await websocket.send(json.dumps({"type": "pong"}))
|
|
40900
|
-
except json.JSONDecodeError:
|
|
40901
|
-
pass
|
|
40902
|
-
else:
|
|
40903
|
-
# Binary audio data: PCM Int16 @ 24kHz
|
|
40904
|
-
pcm = np.frombuffer(message, dtype=np.int16).astype(np.float32) / 32768.0
|
|
40905
|
-
audio_tensor = torch.tensor(pcm, dtype=dtype, device=device).unsqueeze(0)
|
|
40906
|
-
|
|
40907
|
-
# Generate response
|
|
40908
|
-
with torch.no_grad():
|
|
40909
|
-
inputs = processor(
|
|
40910
|
-
audio=audio_tensor,
|
|
40911
|
-
text=persona_text,
|
|
40912
|
-
sampling_rate=SAMPLE_RATE,
|
|
40913
|
-
return_tensors="pt"
|
|
40914
|
-
).to(device)
|
|
40915
|
-
|
|
40916
|
-
if voice_prompt is not None:
|
|
40917
|
-
inputs["voice_prompt"] = voice_prompt
|
|
40918
|
-
|
|
40919
|
-
outputs = model.generate(**inputs, max_new_tokens=512)
|
|
40920
|
-
|
|
40921
|
-
# Extract audio response
|
|
40922
|
-
if hasattr(outputs, "audio"):
|
|
40923
|
-
response_pcm = outputs.audio.cpu().numpy().flatten()
|
|
40924
|
-
else:
|
|
40925
|
-
response_pcm = outputs[0].cpu().numpy().flatten()
|
|
40926
|
-
|
|
40927
|
-
# Convert to Int16 and send
|
|
40928
|
-
response_int16 = (response_pcm * 32767).astype(np.int16)
|
|
40929
|
-
await websocket.send(response_int16.tobytes())
|
|
40930
|
-
|
|
40931
|
-
# Send text if available
|
|
40932
|
-
if hasattr(outputs, "text") and outputs.text:
|
|
40933
|
-
await websocket.send(json.dumps({
|
|
40934
|
-
"type": "transcript",
|
|
40935
|
-
"speaker": "agent",
|
|
40936
|
-
"text": outputs.text
|
|
40937
|
-
}))
|
|
40938
|
-
|
|
40939
|
-
except websockets.exceptions.ConnectionClosed:
|
|
40940
|
-
print("Client disconnected", file=sys.stderr)
|
|
40941
|
-
|
|
40942
|
-
async def main():
|
|
40943
|
-
port = PORT
|
|
40944
|
-
server = await websockets.serve(handle_client, "127.0.0.1", port)
|
|
40945
|
-
actual_port = server.sockets[0].getsockname()[1]
|
|
40946
|
-
|
|
40947
|
-
# Write port file
|
|
40948
|
-
port_file = os.path.join(os.path.dirname(__file__), "daemon.port")
|
|
40949
|
-
with open(port_file, "w") as f:
|
|
40950
|
-
f.write(str(actual_port))
|
|
40951
|
-
|
|
40952
|
-
print(f"PersonaPlex daemon listening on ws://127.0.0.1:{actual_port}", file=sys.stderr)
|
|
40953
|
-
|
|
40954
|
-
# Handle shutdown
|
|
40955
|
-
loop = asyncio.get_event_loop()
|
|
40956
|
-
stop = asyncio.Future()
|
|
40957
|
-
for sig in (signal.SIGINT, signal.SIGTERM):
|
|
40958
|
-
loop.add_signal_handler(sig, stop.set_result, None)
|
|
40959
|
-
await stop
|
|
40960
|
-
server.close()
|
|
40961
|
-
await server.wait_closed()
|
|
40962
|
-
|
|
40963
|
-
asyncio.run(main())
|
|
40964
|
-
`;
|
|
40965
|
-
writeFileSync16(scriptPath2, script, "utf8");
|
|
40966
|
-
return scriptPath2;
|
|
40967
|
-
}
|
|
40968
41216
|
async function startPersonaPlexDaemon(onInfo) {
|
|
40969
41217
|
const log = onInfo ?? (() => {
|
|
40970
41218
|
});
|
|
41219
|
+
const PORT = 8998;
|
|
40971
41220
|
if (isPersonaPlexRunning()) {
|
|
40972
41221
|
const url = getPersonaPlexWSUrl();
|
|
40973
41222
|
if (url) {
|
|
@@ -40980,29 +41229,63 @@ async function startPersonaPlexDaemon(onInfo) {
|
|
|
40980
41229
|
return null;
|
|
40981
41230
|
}
|
|
40982
41231
|
mkdirSync15(PERSONAPLEX_DIR, { recursive: true });
|
|
40983
|
-
const scriptPath2 = writeDaemonScript();
|
|
40984
41232
|
const venvPython2 = process.platform === "win32" ? join54(PERSONAPLEX_DIR, "venv", "Scripts", "python.exe") : join54(PERSONAPLEX_DIR, "venv", "bin", "python3");
|
|
40985
|
-
|
|
40986
|
-
|
|
41233
|
+
const sslDir = join54(PERSONAPLEX_DIR, "ssl");
|
|
41234
|
+
mkdirSync15(sslDir, { recursive: true });
|
|
41235
|
+
log("Starting PersonaPlex daemon (loading ~7B model)...");
|
|
41236
|
+
const child = spawn19(venvPython2, [
|
|
41237
|
+
"-m",
|
|
41238
|
+
"moshi.server",
|
|
41239
|
+
"--host",
|
|
41240
|
+
"0.0.0.0",
|
|
41241
|
+
"--port",
|
|
41242
|
+
String(PORT),
|
|
41243
|
+
"--ssl",
|
|
41244
|
+
sslDir,
|
|
41245
|
+
"--device",
|
|
41246
|
+
"cuda"
|
|
41247
|
+
], {
|
|
40987
41248
|
stdio: ["ignore", "pipe", "pipe"],
|
|
40988
41249
|
detached: true,
|
|
40989
|
-
env: { ...process.env
|
|
41250
|
+
env: { ...process.env },
|
|
41251
|
+
cwd: PERSONAPLEX_DIR
|
|
40990
41252
|
});
|
|
41253
|
+
if (child.pid) {
|
|
41254
|
+
writeFileSync16(PID_FILE, String(child.pid));
|
|
41255
|
+
writeFileSync16(PORT_FILE, String(PORT));
|
|
41256
|
+
}
|
|
40991
41257
|
child.unref();
|
|
41258
|
+
const { createWriteStream } = await import("node:fs");
|
|
41259
|
+
const logStream = createWriteStream(LOG_FILE, { flags: "w" });
|
|
41260
|
+
child.stdout?.pipe(logStream);
|
|
41261
|
+
child.stderr?.pipe(logStream);
|
|
40992
41262
|
const startTime = Date.now();
|
|
40993
|
-
const timeout =
|
|
41263
|
+
const timeout = 12e4;
|
|
40994
41264
|
while (Date.now() - startTime < timeout) {
|
|
40995
|
-
|
|
40996
|
-
|
|
40997
|
-
|
|
40998
|
-
|
|
40999
|
-
|
|
41000
|
-
|
|
41001
|
-
}
|
|
41265
|
+
try {
|
|
41266
|
+
if (child.pid)
|
|
41267
|
+
process.kill(child.pid, 0);
|
|
41268
|
+
} catch {
|
|
41269
|
+
log("PersonaPlex daemon exited unexpectedly. Check daemon.log.");
|
|
41270
|
+
return null;
|
|
41002
41271
|
}
|
|
41003
|
-
|
|
41272
|
+
try {
|
|
41273
|
+
execSync27(`curl -sk -o /dev/null -w "%{http_code}" https://127.0.0.1:${PORT}/`, {
|
|
41274
|
+
timeout: 3e3,
|
|
41275
|
+
stdio: "pipe",
|
|
41276
|
+
encoding: "utf8"
|
|
41277
|
+
});
|
|
41278
|
+
const url = `wss://127.0.0.1:${PORT}`;
|
|
41279
|
+
log(`PersonaPlex ready at ${url}`);
|
|
41280
|
+
return url;
|
|
41281
|
+
} catch {
|
|
41282
|
+
}
|
|
41283
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
41284
|
+
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
41285
|
+
if (elapsed % 10 === 0)
|
|
41286
|
+
log(`Still loading... (${elapsed}s)`);
|
|
41004
41287
|
}
|
|
41005
|
-
log("PersonaPlex daemon failed to start within
|
|
41288
|
+
log("PersonaPlex daemon failed to start within 120s. Check daemon.log.");
|
|
41006
41289
|
return null;
|
|
41007
41290
|
}
|
|
41008
41291
|
function stopPersonaPlex() {
|
|
@@ -41020,7 +41303,260 @@ function stopPersonaPlex() {
|
|
|
41020
41303
|
} catch {
|
|
41021
41304
|
}
|
|
41022
41305
|
}
|
|
41023
|
-
|
|
41306
|
+
function listPersonaPlexVoices() {
|
|
41307
|
+
const voices = [];
|
|
41308
|
+
const builtins = [
|
|
41309
|
+
"NATF0",
|
|
41310
|
+
"NATF1",
|
|
41311
|
+
"NATF2",
|
|
41312
|
+
"NATF3",
|
|
41313
|
+
"NATM0",
|
|
41314
|
+
"NATM1",
|
|
41315
|
+
"NATM2",
|
|
41316
|
+
"NATM3",
|
|
41317
|
+
"VARF0",
|
|
41318
|
+
"VARF1",
|
|
41319
|
+
"VARF2",
|
|
41320
|
+
"VARF3",
|
|
41321
|
+
"VARF4",
|
|
41322
|
+
"VARM0",
|
|
41323
|
+
"VARM1",
|
|
41324
|
+
"VARM2",
|
|
41325
|
+
"VARM3",
|
|
41326
|
+
"VARM4"
|
|
41327
|
+
];
|
|
41328
|
+
for (const name of builtins) {
|
|
41329
|
+
voices.push({ name, type: "builtin", path: `${name}.pt` });
|
|
41330
|
+
}
|
|
41331
|
+
if (existsSync37(CUSTOM_VOICES_DIR)) {
|
|
41332
|
+
try {
|
|
41333
|
+
const { readdirSync: readdirSync24 } = __require("node:fs");
|
|
41334
|
+
for (const f of readdirSync24(CUSTOM_VOICES_DIR)) {
|
|
41335
|
+
if (f.endsWith(".pt")) {
|
|
41336
|
+
const name = f.replace(/\.pt$/, "");
|
|
41337
|
+
voices.push({ name, type: "custom", path: join54(CUSTOM_VOICES_DIR, f) });
|
|
41338
|
+
}
|
|
41339
|
+
}
|
|
41340
|
+
} catch {
|
|
41341
|
+
}
|
|
41342
|
+
}
|
|
41343
|
+
return voices;
|
|
41344
|
+
}
|
|
41345
|
+
async function clonePersonaPlexVoice(inputWav, voiceName, onInfo) {
|
|
41346
|
+
const log = onInfo ?? (() => {
|
|
41347
|
+
});
|
|
41348
|
+
if (!isPersonaPlexInstalled()) {
|
|
41349
|
+
log("PersonaPlex not installed. Run /voice personaplex first.");
|
|
41350
|
+
return null;
|
|
41351
|
+
}
|
|
41352
|
+
if (!existsSync37(inputWav)) {
|
|
41353
|
+
log(`Input WAV not found: ${inputWav}`);
|
|
41354
|
+
return null;
|
|
41355
|
+
}
|
|
41356
|
+
mkdirSync15(CUSTOM_VOICES_DIR, { recursive: true });
|
|
41357
|
+
const outputPt = join54(CUSTOM_VOICES_DIR, `${voiceName}.pt`);
|
|
41358
|
+
if (existsSync37(outputPt)) {
|
|
41359
|
+
log(`Voice "${voiceName}" already exists. Delete ${outputPt} to re-clone.`);
|
|
41360
|
+
return outputPt;
|
|
41361
|
+
}
|
|
41362
|
+
const venvPython2 = process.platform === "win32" ? join54(PERSONAPLEX_DIR, "venv", "Scripts", "python.exe") : join54(PERSONAPLEX_DIR, "venv", "bin", "python3");
|
|
41363
|
+
const cloneScript = join54(PERSONAPLEX_DIR, "clone-voice.py");
|
|
41364
|
+
if (!existsSync37(cloneScript)) {
|
|
41365
|
+
log("clone-voice.py not found. Reinstall PersonaPlex.");
|
|
41366
|
+
return null;
|
|
41367
|
+
}
|
|
41368
|
+
log(`Cloning voice "${voiceName}" from ${inputWav}...`);
|
|
41369
|
+
log("This requires loading the full 7B model \u2014 may take 30-60s...");
|
|
41370
|
+
return new Promise((resolve36) => {
|
|
41371
|
+
const child = spawn19(venvPython2, [
|
|
41372
|
+
cloneScript,
|
|
41373
|
+
"--input",
|
|
41374
|
+
inputWav,
|
|
41375
|
+
"--name",
|
|
41376
|
+
voiceName,
|
|
41377
|
+
"--device",
|
|
41378
|
+
"cuda"
|
|
41379
|
+
], {
|
|
41380
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
41381
|
+
env: { ...process.env },
|
|
41382
|
+
cwd: PERSONAPLEX_DIR
|
|
41383
|
+
});
|
|
41384
|
+
let output = "";
|
|
41385
|
+
child.stdout?.on("data", (d) => {
|
|
41386
|
+
const line = d.toString();
|
|
41387
|
+
output += line;
|
|
41388
|
+
for (const l of line.split("\n").filter((s) => s.trim())) {
|
|
41389
|
+
log(l.trim());
|
|
41390
|
+
}
|
|
41391
|
+
});
|
|
41392
|
+
child.stderr?.on("data", (d) => {
|
|
41393
|
+
output += d.toString();
|
|
41394
|
+
});
|
|
41395
|
+
child.on("close", (code) => {
|
|
41396
|
+
if (code === 0 && existsSync37(outputPt)) {
|
|
41397
|
+
log(`Voice "${voiceName}" cloned successfully.`);
|
|
41398
|
+
resolve36(outputPt);
|
|
41399
|
+
} else {
|
|
41400
|
+
log(`Voice cloning failed (exit ${code}).`);
|
|
41401
|
+
resolve36(null);
|
|
41402
|
+
}
|
|
41403
|
+
});
|
|
41404
|
+
});
|
|
41405
|
+
}
|
|
41406
|
+
function getShippedVoicesDir() {
|
|
41407
|
+
const candidates = [
|
|
41408
|
+
join54(PERSONAPLEX_DIR, "shipped_voices"),
|
|
41409
|
+
// cached copy
|
|
41410
|
+
join54(process.cwd(), "voices", "personaplex")
|
|
41411
|
+
// repo root
|
|
41412
|
+
];
|
|
41413
|
+
try {
|
|
41414
|
+
const modDir = dirname18(fileURLToPath11(import.meta.url));
|
|
41415
|
+
candidates.push(join54(modDir, "..", "..", "..", "voices", "personaplex"));
|
|
41416
|
+
candidates.push(join54(modDir, "..", "..", "..", "..", "voices", "personaplex"));
|
|
41417
|
+
} catch {
|
|
41418
|
+
}
|
|
41419
|
+
for (const dir of candidates) {
|
|
41420
|
+
if (existsSync37(dir)) {
|
|
41421
|
+
try {
|
|
41422
|
+
const files = readdirSync11(dir);
|
|
41423
|
+
if (files.some((f) => f.endsWith(".pt")))
|
|
41424
|
+
return dir;
|
|
41425
|
+
} catch {
|
|
41426
|
+
}
|
|
41427
|
+
}
|
|
41428
|
+
}
|
|
41429
|
+
return null;
|
|
41430
|
+
}
|
|
41431
|
+
function provisionShippedVoices(onInfo) {
|
|
41432
|
+
const log = onInfo ?? (() => {
|
|
41433
|
+
});
|
|
41434
|
+
const shippedDir = getShippedVoicesDir();
|
|
41435
|
+
if (!shippedDir)
|
|
41436
|
+
return 0;
|
|
41437
|
+
const hfVoicesDir = getHFVoicesDir();
|
|
41438
|
+
mkdirSync15(CUSTOM_VOICES_DIR, { recursive: true });
|
|
41439
|
+
let deployed = 0;
|
|
41440
|
+
try {
|
|
41441
|
+
for (const f of readdirSync11(shippedDir)) {
|
|
41442
|
+
if (!f.endsWith(".pt"))
|
|
41443
|
+
continue;
|
|
41444
|
+
const customDst = join54(CUSTOM_VOICES_DIR, f);
|
|
41445
|
+
if (!existsSync37(customDst)) {
|
|
41446
|
+
copyFileSync2(join54(shippedDir, f), customDst);
|
|
41447
|
+
}
|
|
41448
|
+
if (hfVoicesDir) {
|
|
41449
|
+
const hfDst = join54(hfVoicesDir, f);
|
|
41450
|
+
if (!existsSync37(hfDst)) {
|
|
41451
|
+
copyFileSync2(join54(shippedDir, f), hfDst);
|
|
41452
|
+
log(`Deployed voice: ${f.replace(".pt", "")}`);
|
|
41453
|
+
deployed++;
|
|
41454
|
+
}
|
|
41455
|
+
}
|
|
41456
|
+
}
|
|
41457
|
+
} catch {
|
|
41458
|
+
}
|
|
41459
|
+
const shippedScript = join54(shippedDir, "clone-voice.py");
|
|
41460
|
+
const targetScript = join54(PERSONAPLEX_DIR, "clone-voice.py");
|
|
41461
|
+
if (existsSync37(shippedScript) && !existsSync37(targetScript)) {
|
|
41462
|
+
try {
|
|
41463
|
+
copyFileSync2(shippedScript, targetScript);
|
|
41464
|
+
} catch {
|
|
41465
|
+
}
|
|
41466
|
+
}
|
|
41467
|
+
return deployed;
|
|
41468
|
+
}
|
|
41469
|
+
function getHFVoicesDir() {
|
|
41470
|
+
const hfBase = join54(homedir13(), ".cache", "huggingface", "hub", "models--nvidia--personaplex-7b-v1");
|
|
41471
|
+
if (!existsSync37(hfBase))
|
|
41472
|
+
return null;
|
|
41473
|
+
try {
|
|
41474
|
+
const snapshots = join54(hfBase, "snapshots");
|
|
41475
|
+
if (!existsSync37(snapshots))
|
|
41476
|
+
return null;
|
|
41477
|
+
for (const snap of readdirSync11(snapshots)) {
|
|
41478
|
+
const voicesDir = join54(snapshots, snap, "voices");
|
|
41479
|
+
if (existsSync37(voicesDir))
|
|
41480
|
+
return voicesDir;
|
|
41481
|
+
}
|
|
41482
|
+
} catch {
|
|
41483
|
+
}
|
|
41484
|
+
return null;
|
|
41485
|
+
}
|
|
41486
|
+
function patchFrontendVoiceList(onInfo) {
|
|
41487
|
+
const log = onInfo ?? (() => {
|
|
41488
|
+
});
|
|
41489
|
+
const hfBase = join54(homedir13(), ".cache", "huggingface", "hub", "models--nvidia--personaplex-7b-v1");
|
|
41490
|
+
if (!existsSync37(hfBase))
|
|
41491
|
+
return;
|
|
41492
|
+
try {
|
|
41493
|
+
const snapshots = join54(hfBase, "snapshots");
|
|
41494
|
+
for (const snap of readdirSync11(snapshots)) {
|
|
41495
|
+
const distDir = join54(snapshots, snap, "dist", "assets");
|
|
41496
|
+
if (!existsSync37(distDir))
|
|
41497
|
+
continue;
|
|
41498
|
+
for (const f of readdirSync11(distDir)) {
|
|
41499
|
+
if (!f.startsWith("index-") || !f.endsWith(".js"))
|
|
41500
|
+
continue;
|
|
41501
|
+
const jsPath = join54(distDir, f);
|
|
41502
|
+
let js = readFileSync28(jsPath, "utf8");
|
|
41503
|
+
const customVoices = [];
|
|
41504
|
+
if (existsSync37(CUSTOM_VOICES_DIR)) {
|
|
41505
|
+
for (const vf of readdirSync11(CUSTOM_VOICES_DIR)) {
|
|
41506
|
+
if (vf.endsWith(".pt")) {
|
|
41507
|
+
const name = vf.replace(".pt", "");
|
|
41508
|
+
if (!js.includes(`"${vf}"`)) {
|
|
41509
|
+
customVoices.push(vf);
|
|
41510
|
+
}
|
|
41511
|
+
}
|
|
41512
|
+
}
|
|
41513
|
+
}
|
|
41514
|
+
if (customVoices.length === 0)
|
|
41515
|
+
continue;
|
|
41516
|
+
const needle = '"VARM4.pt"]';
|
|
41517
|
+
if (js.includes(needle)) {
|
|
41518
|
+
const additions = customVoices.map((v) => `"${v}"`).join(", ");
|
|
41519
|
+
js = js.replace(needle, `"VARM4.pt", ${additions}]`);
|
|
41520
|
+
writeFileSync16(jsPath, js);
|
|
41521
|
+
log(`Added ${customVoices.length} custom voice(s) to frontend: ${customVoices.map((v) => v.replace(".pt", "")).join(", ")}`);
|
|
41522
|
+
}
|
|
41523
|
+
}
|
|
41524
|
+
}
|
|
41525
|
+
} catch {
|
|
41526
|
+
}
|
|
41527
|
+
}
|
|
41528
|
+
async function autoSetupPersonaPlex(onInfo) {
|
|
41529
|
+
const log = onInfo ?? (() => {
|
|
41530
|
+
});
|
|
41531
|
+
const caps = detectPersonaPlexCapability();
|
|
41532
|
+
if (!caps.supported) {
|
|
41533
|
+
log(`PersonaPlex not available: ${caps.reason}`);
|
|
41534
|
+
return null;
|
|
41535
|
+
}
|
|
41536
|
+
log(`GPU: ${caps.gpuName} (${caps.vramGB.toFixed(0)}GB) \u2014 PersonaPlex compatible`);
|
|
41537
|
+
if (!isPersonaPlexInstalled()) {
|
|
41538
|
+
log("Installing PersonaPlex (first time setup)...");
|
|
41539
|
+
const ok = await installPersonaPlex(log);
|
|
41540
|
+
if (!ok) {
|
|
41541
|
+
log("PersonaPlex installation failed.");
|
|
41542
|
+
return null;
|
|
41543
|
+
}
|
|
41544
|
+
}
|
|
41545
|
+
const deployed = provisionShippedVoices(log);
|
|
41546
|
+
if (deployed > 0) {
|
|
41547
|
+
log(`Provisioned ${deployed} shipped voice(s)`);
|
|
41548
|
+
}
|
|
41549
|
+
patchFrontendVoiceList(log);
|
|
41550
|
+
if (isPersonaPlexRunning()) {
|
|
41551
|
+
const url = getPersonaPlexWSUrl();
|
|
41552
|
+
if (url) {
|
|
41553
|
+
log(`PersonaPlex already running at ${url}`);
|
|
41554
|
+
return url;
|
|
41555
|
+
}
|
|
41556
|
+
}
|
|
41557
|
+
return await startPersonaPlexDaemon(log);
|
|
41558
|
+
}
|
|
41559
|
+
var PERSONAPLEX_DIR, PID_FILE, PORT_FILE, LOG_FILE, CUSTOM_VOICES_DIR;
|
|
41024
41560
|
var init_personaplex = __esm({
|
|
41025
41561
|
"packages/cli/dist/tui/personaplex.js"() {
|
|
41026
41562
|
"use strict";
|
|
@@ -41029,6 +41565,7 @@ var init_personaplex = __esm({
|
|
|
41029
41565
|
PID_FILE = join54(PERSONAPLEX_DIR, "daemon.pid");
|
|
41030
41566
|
PORT_FILE = join54(PERSONAPLEX_DIR, "daemon.port");
|
|
41031
41567
|
LOG_FILE = join54(PERSONAPLEX_DIR, "daemon.log");
|
|
41568
|
+
CUSTOM_VOICES_DIR = join54(PERSONAPLEX_DIR, "custom_voices");
|
|
41032
41569
|
}
|
|
41033
41570
|
});
|
|
41034
41571
|
|
|
@@ -44539,8 +45076,8 @@ __export(voice_exports, {
|
|
|
44539
45076
|
registerCustomOnnxModel: () => registerCustomOnnxModel,
|
|
44540
45077
|
resetNarrationContext: () => resetNarrationContext
|
|
44541
45078
|
});
|
|
44542
|
-
import { existsSync as existsSync42, mkdirSync as mkdirSync18, writeFileSync as writeFileSync19, readFileSync as readFileSync31, unlinkSync as unlinkSync9, readdirSync as
|
|
44543
|
-
import { join as join58, dirname as
|
|
45079
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync18, writeFileSync as writeFileSync19, readFileSync as readFileSync31, unlinkSync as unlinkSync9, readdirSync as readdirSync12, renameSync, statSync as statSync13 } from "node:fs";
|
|
45080
|
+
import { join as join58, dirname as dirname19 } from "node:path";
|
|
44544
45081
|
import { homedir as homedir15, tmpdir as tmpdir9, platform as platform3 } from "node:os";
|
|
44545
45082
|
import { execSync as execSync30, spawn as nodeSpawn } from "node:child_process";
|
|
44546
45083
|
import { createRequire } from "node:module";
|
|
@@ -44598,7 +45135,7 @@ function writeDetectTorchScript(targetPath) {
|
|
|
44598
45135
|
if (existsSync42(targetPath))
|
|
44599
45136
|
return;
|
|
44600
45137
|
try {
|
|
44601
|
-
mkdirSync18(
|
|
45138
|
+
mkdirSync18(dirname19(targetPath), { recursive: true });
|
|
44602
45139
|
} catch {
|
|
44603
45140
|
}
|
|
44604
45141
|
const script = `#!/usr/bin/env python3
|
|
@@ -45665,7 +46202,7 @@ var init_voice = __esm({
|
|
|
45665
46202
|
if (!existsSync42(dir))
|
|
45666
46203
|
return [];
|
|
45667
46204
|
const meta = this.loadCloneMeta();
|
|
45668
|
-
const files =
|
|
46205
|
+
const files = readdirSync12(dir).filter((f) => {
|
|
45669
46206
|
const ext = f.split(".").pop()?.toLowerCase() ?? "";
|
|
45670
46207
|
return _VoiceEngine.AUDIO_EXTS.has(ext);
|
|
45671
46208
|
});
|
|
@@ -47289,7 +47826,7 @@ Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
|
47289
47826
|
// packages/cli/dist/tui/commands.js
|
|
47290
47827
|
import * as nodeOs from "node:os";
|
|
47291
47828
|
import { execSync as nodeExecSync } from "node:child_process";
|
|
47292
|
-
import { existsSync as existsSync43, readFileSync as readFileSync32, writeFileSync as writeFileSync20, mkdirSync as mkdirSync19, readdirSync as
|
|
47829
|
+
import { existsSync as existsSync43, readFileSync as readFileSync32, writeFileSync as writeFileSync20, mkdirSync as mkdirSync19, readdirSync as readdirSync13, statSync as statSync14, rmSync } from "node:fs";
|
|
47293
47830
|
import { join as join59 } from "node:path";
|
|
47294
47831
|
function safeLog(text) {
|
|
47295
47832
|
if (isNeovimActive()) {
|
|
@@ -48098,7 +48635,7 @@ async function handleSlashCommand(input, ctx) {
|
|
|
48098
48635
|
let heliaBytes = 0;
|
|
48099
48636
|
try {
|
|
48100
48637
|
if (existsSync43(ipfsLocalDir)) {
|
|
48101
|
-
const files =
|
|
48638
|
+
const files = readdirSync13(ipfsLocalDir).filter((f) => f.endsWith(".json"));
|
|
48102
48639
|
ipfsFiles = files.length;
|
|
48103
48640
|
for (const f of files) {
|
|
48104
48641
|
try {
|
|
@@ -48110,7 +48647,7 @@ async function handleSlashCommand(input, ctx) {
|
|
|
48110
48647
|
const heliaBlockDir = join59(ipfsDir, "blocks");
|
|
48111
48648
|
if (existsSync43(heliaBlockDir)) {
|
|
48112
48649
|
const walkDir = (dir) => {
|
|
48113
|
-
for (const entry of
|
|
48650
|
+
for (const entry of readdirSync13(dir, { withFileTypes: true })) {
|
|
48114
48651
|
if (entry.isDirectory())
|
|
48115
48652
|
walkDir(join59(dir, entry.name));
|
|
48116
48653
|
else {
|
|
@@ -48233,7 +48770,7 @@ async function handleSlashCommand(input, ctx) {
|
|
|
48233
48770
|
const categories = {};
|
|
48234
48771
|
const walkStorage = (dir, category) => {
|
|
48235
48772
|
try {
|
|
48236
|
-
for (const entry of
|
|
48773
|
+
for (const entry of readdirSync13(dir, { withFileTypes: true })) {
|
|
48237
48774
|
const full = join59(dir, entry.name);
|
|
48238
48775
|
if (entry.isDirectory()) {
|
|
48239
48776
|
const subCat = category || entry.name;
|
|
@@ -48267,7 +48804,7 @@ async function handleSlashCommand(input, ctx) {
|
|
|
48267
48804
|
const sensitiveFound = [];
|
|
48268
48805
|
const checkSensitive = (dir) => {
|
|
48269
48806
|
try {
|
|
48270
|
-
for (const entry of
|
|
48807
|
+
for (const entry of readdirSync13(dir, { withFileTypes: true })) {
|
|
48271
48808
|
const name = entry.name.toLowerCase();
|
|
48272
48809
|
if (sensitivePatterns.some((p) => name.includes(p))) {
|
|
48273
48810
|
sensitiveFound.push(join59(dir, entry.name).replace(oaDir + "/", ""));
|
|
@@ -48567,6 +49104,45 @@ async function handleSlashCommand(input, ctx) {
|
|
|
48567
49104
|
}
|
|
48568
49105
|
return "handled";
|
|
48569
49106
|
}
|
|
49107
|
+
if (arg.startsWith("clone")) {
|
|
49108
|
+
const parts = arg.slice(5).trim().split(/\s+/);
|
|
49109
|
+
if (parts.length < 2) {
|
|
49110
|
+
renderInfo("Usage: /voice clone <wav-file> <voice-name>");
|
|
49111
|
+
renderInfo("Example: /voice clone ~/my_voice.wav MyVoice");
|
|
49112
|
+
renderInfo("");
|
|
49113
|
+
renderInfo("Record 4-10 seconds of clean speech (24kHz mono WAV recommended).");
|
|
49114
|
+
renderInfo("The voice embedding will be extracted and saved for PersonaPlex.");
|
|
49115
|
+
return "handled";
|
|
49116
|
+
}
|
|
49117
|
+
const [wavPath, voiceName] = parts;
|
|
49118
|
+
const { clonePersonaPlexVoice: clonePersonaPlexVoice2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
|
|
49119
|
+
const result = await clonePersonaPlexVoice2(wavPath, voiceName, (msg2) => renderInfo(msg2));
|
|
49120
|
+
if (result) {
|
|
49121
|
+
renderInfo(`Use with /call by setting voice_prompt to "${voiceName}.pt"`);
|
|
49122
|
+
}
|
|
49123
|
+
return "handled";
|
|
49124
|
+
}
|
|
49125
|
+
if (arg === "list" || arg === "voices") {
|
|
49126
|
+
const { listPersonaPlexVoices: listPersonaPlexVoices2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
|
|
49127
|
+
const voices = listPersonaPlexVoices2();
|
|
49128
|
+
const builtins = voices.filter((v) => v.type === "builtin");
|
|
49129
|
+
const customs = voices.filter((v) => v.type === "custom");
|
|
49130
|
+
renderInfo("Natural voices:");
|
|
49131
|
+
renderInfo(" Female: " + builtins.filter((v) => v.name.startsWith("NATF")).map((v) => v.name).join(", "));
|
|
49132
|
+
renderInfo(" Male: " + builtins.filter((v) => v.name.startsWith("NATM")).map((v) => v.name).join(", "));
|
|
49133
|
+
renderInfo("Variety voices:");
|
|
49134
|
+
renderInfo(" Female: " + builtins.filter((v) => v.name.startsWith("VARF")).map((v) => v.name).join(", "));
|
|
49135
|
+
renderInfo(" Male: " + builtins.filter((v) => v.name.startsWith("VARM")).map((v) => v.name).join(", "));
|
|
49136
|
+
if (customs.length > 0) {
|
|
49137
|
+
renderInfo("Custom cloned voices:");
|
|
49138
|
+
for (const v of customs) {
|
|
49139
|
+
renderInfo(` ${v.name} (${v.path})`);
|
|
49140
|
+
}
|
|
49141
|
+
} else {
|
|
49142
|
+
renderInfo("No custom voices. Use /voice clone <wav> <name> to create one.");
|
|
49143
|
+
}
|
|
49144
|
+
return "handled";
|
|
49145
|
+
}
|
|
48570
49146
|
if (arg === "enable" || arg === "on") {
|
|
48571
49147
|
const msg2 = await ctx.voiceToggle();
|
|
48572
49148
|
const isOn = msg2.toLowerCase().includes("enabled") || msg2.toLowerCase().includes("on");
|
|
@@ -48858,17 +49434,6 @@ async function handleSlashCommand(input, ctx) {
|
|
|
48858
49434
|
renderWarning("Call mode not available in this context.");
|
|
48859
49435
|
return "handled";
|
|
48860
49436
|
}
|
|
48861
|
-
try {
|
|
48862
|
-
const { isPersonaPlexRunning: isPersonaPlexRunning2, getPersonaPlexWSUrl: getPersonaPlexWSUrl2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
|
|
48863
|
-
if (isPersonaPlexRunning2()) {
|
|
48864
|
-
const ppUrl = getPersonaPlexWSUrl2();
|
|
48865
|
-
if (ppUrl) {
|
|
48866
|
-
renderInfo(`PersonaPlex active \u2014 full-duplex mode (${ppUrl})`);
|
|
48867
|
-
renderInfo("257ms response latency, concurrent listen+speak");
|
|
48868
|
-
}
|
|
48869
|
-
}
|
|
48870
|
-
} catch {
|
|
48871
|
-
}
|
|
48872
49437
|
if (arg === "stop" || arg === "off") {
|
|
48873
49438
|
if (ctx.isCallActive?.()) {
|
|
48874
49439
|
await ctx.callStop?.();
|
|
@@ -50178,7 +50743,7 @@ async function showCohereDashboard(ctx) {
|
|
|
50178
50743
|
} else if (idResult.key === "history") {
|
|
50179
50744
|
const snapDir = join59(ctx.repoRoot, ".oa", "identity", "snapshots");
|
|
50180
50745
|
if (existsSync43(snapDir)) {
|
|
50181
|
-
const snaps =
|
|
50746
|
+
const snaps = readdirSync13(snapDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
50182
50747
|
const snapItems = snaps.slice(0, 20).map((f) => ({
|
|
50183
50748
|
key: f,
|
|
50184
50749
|
label: f.replace(".json", ""),
|
|
@@ -50503,14 +51068,14 @@ async function handleVoiceMenu(ctx, save, hasLocal) {
|
|
|
50503
51068
|
continue;
|
|
50504
51069
|
}
|
|
50505
51070
|
const { basename: basename18, join: pathJoin } = await import("node:path");
|
|
50506
|
-
const { copyFileSync:
|
|
51071
|
+
const { copyFileSync: copyFileSync3, mkdirSync: mkdirSync31, existsSync: exists } = await import("node:fs");
|
|
50507
51072
|
const { homedir: homedir21 } = await import("node:os");
|
|
50508
51073
|
const modelName = basename18(onnxDrop.path, ".onnx").replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
50509
51074
|
const destDir = pathJoin(homedir21(), ".open-agents", "voice", "models", modelName);
|
|
50510
51075
|
if (!exists(destDir))
|
|
50511
51076
|
mkdirSync31(destDir, { recursive: true });
|
|
50512
|
-
|
|
50513
|
-
|
|
51077
|
+
copyFileSync3(onnxDrop.path, pathJoin(destDir, "model.onnx"));
|
|
51078
|
+
copyFileSync3(jsonDrop.path, pathJoin(destDir, "config.json"));
|
|
50514
51079
|
const { registerCustomOnnxModel: registerCustomOnnxModel2 } = await Promise.resolve().then(() => (init_voice(), voice_exports));
|
|
50515
51080
|
registerCustomOnnxModel2(modelName, modelName);
|
|
50516
51081
|
await ctx.voiceSetModel(modelName);
|
|
@@ -50636,8 +51201,8 @@ async function handleVoiceList(ctx, focusFilename) {
|
|
|
50636
51201
|
helpers.getInput("Export to:", defaultPath).then((destPath) => {
|
|
50637
51202
|
if (destPath !== null && destPath.trim()) {
|
|
50638
51203
|
try {
|
|
50639
|
-
const { copyFileSync:
|
|
50640
|
-
|
|
51204
|
+
const { copyFileSync: copyFileSync3 } = __require("node:fs");
|
|
51205
|
+
copyFileSync3(clone.path, destPath.trim());
|
|
50641
51206
|
renderInfo(`Exported "${clone.name}" \u2192 ${destPath.trim()}`);
|
|
50642
51207
|
} catch (err) {
|
|
50643
51208
|
renderError(`Export failed: ${err.message}`);
|
|
@@ -51348,9 +51913,9 @@ async function handlePeerEndpoint(peerId, authKey, ctx, local) {
|
|
|
51348
51913
|
if (models.length > 0) {
|
|
51349
51914
|
try {
|
|
51350
51915
|
const { writeFileSync: writeFileSync30, mkdirSync: mkdirSync31 } = await import("node:fs");
|
|
51351
|
-
const { join: join77, dirname:
|
|
51916
|
+
const { join: join77, dirname: dirname24 } = await import("node:path");
|
|
51352
51917
|
const cachePath = join77(ctx.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
|
|
51353
|
-
mkdirSync31(
|
|
51918
|
+
mkdirSync31(dirname24(cachePath), { recursive: true });
|
|
51354
51919
|
writeFileSync30(cachePath, JSON.stringify({
|
|
51355
51920
|
peerId,
|
|
51356
51921
|
cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -51549,11 +52114,11 @@ async function handleUpdate(subcommand, ctx) {
|
|
|
51549
52114
|
let currentVersion = "0.0.0";
|
|
51550
52115
|
try {
|
|
51551
52116
|
const { createRequire: createRequire6 } = await import("node:module");
|
|
51552
|
-
const { fileURLToPath:
|
|
51553
|
-
const { dirname:
|
|
52117
|
+
const { fileURLToPath: fileURLToPath16 } = await import("node:url");
|
|
52118
|
+
const { dirname: dirname24, join: join77 } = await import("node:path");
|
|
51554
52119
|
const { existsSync: existsSync57 } = await import("node:fs");
|
|
51555
52120
|
const req = createRequire6(import.meta.url);
|
|
51556
|
-
const thisDir =
|
|
52121
|
+
const thisDir = dirname24(fileURLToPath16(import.meta.url));
|
|
51557
52122
|
const candidates = [
|
|
51558
52123
|
join77(thisDir, "..", "package.json"),
|
|
51559
52124
|
join77(thisDir, "..", "..", "package.json"),
|
|
@@ -52414,7 +52979,7 @@ var init_commands = __esm({
|
|
|
52414
52979
|
});
|
|
52415
52980
|
|
|
52416
52981
|
// packages/cli/dist/tui/project-context.js
|
|
52417
|
-
import { existsSync as existsSync44, readFileSync as readFileSync33, readdirSync as
|
|
52982
|
+
import { existsSync as existsSync44, readFileSync as readFileSync33, readdirSync as readdirSync14 } from "node:fs";
|
|
52418
52983
|
import { join as join60, basename as basename12 } from "node:path";
|
|
52419
52984
|
import { execSync as execSync31 } from "node:child_process";
|
|
52420
52985
|
import { homedir as homedir17, platform as platform4, release } from "node:os";
|
|
@@ -52515,7 +53080,7 @@ function loadMemoryDir(memDir, scope) {
|
|
|
52515
53080
|
return "";
|
|
52516
53081
|
const lines = [];
|
|
52517
53082
|
try {
|
|
52518
|
-
const files =
|
|
53083
|
+
const files = readdirSync14(memDir).filter((f) => f.endsWith(".json"));
|
|
52519
53084
|
for (const file of files.slice(0, 10)) {
|
|
52520
53085
|
try {
|
|
52521
53086
|
const raw = readFileSync33(join60(memDir, file), "utf-8");
|
|
@@ -53873,8 +54438,8 @@ function listBannerDesigns(workDir) {
|
|
|
53873
54438
|
if (!existsSync45(dir))
|
|
53874
54439
|
return [];
|
|
53875
54440
|
try {
|
|
53876
|
-
const { readdirSync:
|
|
53877
|
-
return
|
|
54441
|
+
const { readdirSync: readdirSync24 } = __require("node:fs");
|
|
54442
|
+
return readdirSync24(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
|
|
53878
54443
|
} catch {
|
|
53879
54444
|
return [];
|
|
53880
54445
|
}
|
|
@@ -54228,7 +54793,7 @@ var init_banner = __esm({
|
|
|
54228
54793
|
});
|
|
54229
54794
|
|
|
54230
54795
|
// packages/cli/dist/tui/carousel-descriptors.js
|
|
54231
|
-
import { existsSync as existsSync46, readFileSync as readFileSync35, writeFileSync as writeFileSync22, mkdirSync as mkdirSync21, readdirSync as
|
|
54796
|
+
import { existsSync as existsSync46, readFileSync as readFileSync35, writeFileSync as writeFileSync22, mkdirSync as mkdirSync21, readdirSync as readdirSync15 } from "node:fs";
|
|
54232
54797
|
import { join as join62, basename as basename13 } from "node:path";
|
|
54233
54798
|
function loadToolProfile(repoRoot) {
|
|
54234
54799
|
const filePath = join62(repoRoot, OA_DIR, "context", TOOL_PROFILE_FILE);
|
|
@@ -54441,7 +55006,7 @@ function extractFromMemory(repoRoot, tags) {
|
|
|
54441
55006
|
try {
|
|
54442
55007
|
if (!existsSync46(memoryDir))
|
|
54443
55008
|
return;
|
|
54444
|
-
const files =
|
|
55009
|
+
const files = readdirSync15(memoryDir).filter((f) => f.endsWith(".json"));
|
|
54445
55010
|
for (const file of files) {
|
|
54446
55011
|
const topic = file.replace(/\.json$/, "").replace(/[-_]/g, " ");
|
|
54447
55012
|
tags.push(topic);
|
|
@@ -55233,8 +55798,8 @@ var init_edit_history = __esm({
|
|
|
55233
55798
|
|
|
55234
55799
|
// packages/cli/dist/tui/promptLoader.js
|
|
55235
55800
|
import { readFileSync as readFileSync36, existsSync as existsSync47 } from "node:fs";
|
|
55236
|
-
import { join as join64, dirname as
|
|
55237
|
-
import { fileURLToPath as
|
|
55801
|
+
import { join as join64, dirname as dirname20 } from "node:path";
|
|
55802
|
+
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
55238
55803
|
function loadPrompt3(promptPath, vars) {
|
|
55239
55804
|
let content = cache3.get(promptPath);
|
|
55240
55805
|
if (content === void 0) {
|
|
@@ -55253,8 +55818,8 @@ var __filename3, __dirname6, devPath2, publishedPath2, PROMPTS_DIR3, cache3;
|
|
|
55253
55818
|
var init_promptLoader3 = __esm({
|
|
55254
55819
|
"packages/cli/dist/tui/promptLoader.js"() {
|
|
55255
55820
|
"use strict";
|
|
55256
|
-
__filename3 =
|
|
55257
|
-
__dirname6 =
|
|
55821
|
+
__filename3 = fileURLToPath12(import.meta.url);
|
|
55822
|
+
__dirname6 = dirname20(__filename3);
|
|
55258
55823
|
devPath2 = join64(__dirname6, "..", "..", "prompts");
|
|
55259
55824
|
publishedPath2 = join64(__dirname6, "..", "prompts");
|
|
55260
55825
|
PROMPTS_DIR3 = existsSync47(devPath2) ? devPath2 : publishedPath2;
|
|
@@ -55263,7 +55828,7 @@ var init_promptLoader3 = __esm({
|
|
|
55263
55828
|
});
|
|
55264
55829
|
|
|
55265
55830
|
// packages/cli/dist/tui/dream-engine.js
|
|
55266
|
-
import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync23, readFileSync as readFileSync37, existsSync as existsSync48, cpSync, rmSync as rmSync2, readdirSync as
|
|
55831
|
+
import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync23, readFileSync as readFileSync37, existsSync as existsSync48, cpSync, rmSync as rmSync2, readdirSync as readdirSync16 } from "node:fs";
|
|
55267
55832
|
import { join as join65, basename as basename14 } from "node:path";
|
|
55268
55833
|
import { execSync as execSync32 } from "node:child_process";
|
|
55269
55834
|
function loadAutoresearchMemory(repoRoot) {
|
|
@@ -56469,7 +57034,7 @@ Each proposal includes implementation entrypoints and estimated effort.
|
|
|
56469
57034
|
/** Update the master proposal index */
|
|
56470
57035
|
updateProposalIndex() {
|
|
56471
57036
|
try {
|
|
56472
|
-
const files =
|
|
57037
|
+
const files = readdirSync16(this.dreamsDir).filter((f) => f.endsWith(".md") && f !== "PROPOSAL-INDEX.md" && f !== "dream-state.json").sort();
|
|
56473
57038
|
const index = `# Dream Proposals Index
|
|
56474
57039
|
|
|
56475
57040
|
**Last updated**: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}
|
|
@@ -56871,7 +57436,7 @@ var init_bless_engine = __esm({
|
|
|
56871
57436
|
});
|
|
56872
57437
|
|
|
56873
57438
|
// packages/cli/dist/tui/dmn-engine.js
|
|
56874
|
-
import { existsSync as existsSync49, readFileSync as readFileSync38, writeFileSync as writeFileSync24, mkdirSync as mkdirSync24, readdirSync as
|
|
57439
|
+
import { existsSync as existsSync49, readFileSync as readFileSync38, writeFileSync as writeFileSync24, mkdirSync as mkdirSync24, readdirSync as readdirSync17, unlinkSync as unlinkSync10 } from "node:fs";
|
|
56875
57440
|
import { join as join66, basename as basename15 } from "node:path";
|
|
56876
57441
|
function buildDMNGatherPrompt(recentTaskSummaries, dueReminders, attentionItems, memoryTopics, capabilities, competence, reflectionBuffer) {
|
|
56877
57442
|
const competenceReport = competence.length > 0 ? competence.map((c3) => {
|
|
@@ -57590,7 +58155,7 @@ OUTPUT: Call task_complete with JSON:
|
|
|
57590
58155
|
if (!existsSync49(dir))
|
|
57591
58156
|
continue;
|
|
57592
58157
|
try {
|
|
57593
|
-
const files =
|
|
58158
|
+
const files = readdirSync17(dir).filter((f) => f.endsWith(".json"));
|
|
57594
58159
|
for (const f of files) {
|
|
57595
58160
|
const topic = basename15(f, ".json");
|
|
57596
58161
|
if (!topics.includes(topic))
|
|
@@ -57621,7 +58186,7 @@ OUTPUT: Call task_complete with JSON:
|
|
|
57621
58186
|
try {
|
|
57622
58187
|
const filename = `cycle-${result.cycleNumber}-${Date.now()}.json`;
|
|
57623
58188
|
writeFileSync24(join66(this.historyDir, filename), JSON.stringify(result, null, 2) + "\n", "utf-8");
|
|
57624
|
-
const files =
|
|
58189
|
+
const files = readdirSync17(this.historyDir).filter((f) => f.startsWith("cycle-") && f.endsWith(".json")).sort();
|
|
57625
58190
|
if (files.length > 50) {
|
|
57626
58191
|
for (const old of files.slice(0, files.length - 50)) {
|
|
57627
58192
|
try {
|
|
@@ -57638,7 +58203,7 @@ OUTPUT: Call task_complete with JSON:
|
|
|
57638
58203
|
});
|
|
57639
58204
|
|
|
57640
58205
|
// packages/cli/dist/tui/snr-engine.js
|
|
57641
|
-
import { existsSync as existsSync50, readdirSync as
|
|
58206
|
+
import { existsSync as existsSync50, readdirSync as readdirSync18, readFileSync as readFileSync39 } from "node:fs";
|
|
57642
58207
|
import { join as join67, basename as basename16 } from "node:path";
|
|
57643
58208
|
function computeDPrime(signalScores, noiseScores) {
|
|
57644
58209
|
if (signalScores.length === 0 || noiseScores.length === 0)
|
|
@@ -57886,7 +58451,7 @@ Call task_complete with the JSON array when done.`, onEvent)
|
|
|
57886
58451
|
if (!existsSync50(dir))
|
|
57887
58452
|
continue;
|
|
57888
58453
|
try {
|
|
57889
|
-
const files =
|
|
58454
|
+
const files = readdirSync18(dir).filter((f) => f.endsWith(".json"));
|
|
57890
58455
|
for (const f of files) {
|
|
57891
58456
|
const topic = basename16(f, ".json");
|
|
57892
58457
|
if (topics.length > 0 && !topics.includes(topic))
|
|
@@ -58459,7 +59024,7 @@ var init_tool_policy = __esm({
|
|
|
58459
59024
|
});
|
|
58460
59025
|
|
|
58461
59026
|
// packages/cli/dist/tui/telegram-bridge.js
|
|
58462
|
-
import { mkdirSync as mkdirSync25, existsSync as existsSync51, unlinkSync as unlinkSync11, readdirSync as
|
|
59027
|
+
import { mkdirSync as mkdirSync25, existsSync as existsSync51, unlinkSync as unlinkSync11, readdirSync as readdirSync19, statSync as statSync15 } from "node:fs";
|
|
58463
59028
|
import { join as join68, resolve as resolve30 } from "node:path";
|
|
58464
59029
|
import { writeFile as writeFileAsync } from "node:fs/promises";
|
|
58465
59030
|
function convertMarkdownToTelegramHTML(md) {
|
|
@@ -63571,7 +64136,7 @@ var init_direct_input = __esm({
|
|
|
63571
64136
|
});
|
|
63572
64137
|
|
|
63573
64138
|
// packages/cli/dist/api/profiles.js
|
|
63574
|
-
import { existsSync as existsSync52, readFileSync as readFileSync41, writeFileSync as writeFileSync25, mkdirSync as mkdirSync26, readdirSync as
|
|
64139
|
+
import { existsSync as existsSync52, readFileSync as readFileSync41, writeFileSync as writeFileSync25, mkdirSync as mkdirSync26, readdirSync as readdirSync20, unlinkSync as unlinkSync12 } from "node:fs";
|
|
63575
64140
|
import { join as join69 } from "node:path";
|
|
63576
64141
|
import { homedir as homedir18 } from "node:os";
|
|
63577
64142
|
import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, randomBytes as randomBytes15, scryptSync as scryptSync3, createHash as createHash5 } from "node:crypto";
|
|
@@ -63586,7 +64151,7 @@ function listProfiles(projectDir) {
|
|
|
63586
64151
|
const seen = /* @__PURE__ */ new Set();
|
|
63587
64152
|
const projDir = projectProfileDir(projectDir);
|
|
63588
64153
|
if (existsSync52(projDir)) {
|
|
63589
|
-
for (const f of
|
|
64154
|
+
for (const f of readdirSync20(projDir).filter((f2) => f2.endsWith(".json"))) {
|
|
63590
64155
|
try {
|
|
63591
64156
|
const raw = JSON.parse(readFileSync41(join69(projDir, f), "utf8"));
|
|
63592
64157
|
const name = f.replace(".json", "");
|
|
@@ -63603,7 +64168,7 @@ function listProfiles(projectDir) {
|
|
|
63603
64168
|
}
|
|
63604
64169
|
const globDir = globalProfileDir();
|
|
63605
64170
|
if (existsSync52(globDir)) {
|
|
63606
|
-
for (const f of
|
|
64171
|
+
for (const f of readdirSync20(globDir).filter((f2) => f2.endsWith(".json"))) {
|
|
63607
64172
|
const name = f.replace(".json", "");
|
|
63608
64173
|
if (seen.has(name))
|
|
63609
64174
|
continue;
|
|
@@ -63746,15 +64311,15 @@ __export(serve_exports, {
|
|
|
63746
64311
|
import * as http from "node:http";
|
|
63747
64312
|
import * as https from "node:https";
|
|
63748
64313
|
import { createRequire as createRequire3 } from "node:module";
|
|
63749
|
-
import { fileURLToPath as
|
|
63750
|
-
import { dirname as
|
|
64314
|
+
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
64315
|
+
import { dirname as dirname21, join as join70, resolve as resolve31 } from "node:path";
|
|
63751
64316
|
import { spawn as spawn21 } from "node:child_process";
|
|
63752
|
-
import { mkdirSync as mkdirSync27, writeFileSync as writeFileSync26, readFileSync as readFileSync42, readdirSync as
|
|
64317
|
+
import { mkdirSync as mkdirSync27, writeFileSync as writeFileSync26, readFileSync as readFileSync42, readdirSync as readdirSync21, existsSync as existsSync53 } from "node:fs";
|
|
63753
64318
|
import { randomBytes as randomBytes16 } from "node:crypto";
|
|
63754
64319
|
function getVersion3() {
|
|
63755
64320
|
try {
|
|
63756
64321
|
const require2 = createRequire3(import.meta.url);
|
|
63757
|
-
const thisDir =
|
|
64322
|
+
const thisDir = dirname21(fileURLToPath13(import.meta.url));
|
|
63758
64323
|
const candidates = [
|
|
63759
64324
|
join70(thisDir, "..", "package.json"),
|
|
63760
64325
|
join70(thisDir, "..", "..", "package.json"),
|
|
@@ -64075,7 +64640,7 @@ function listJobs() {
|
|
|
64075
64640
|
const dir = jobsDir();
|
|
64076
64641
|
if (!existsSync53(dir))
|
|
64077
64642
|
return [];
|
|
64078
|
-
const files =
|
|
64643
|
+
const files = readdirSync21(dir).filter((f) => f.endsWith(".json")).sort();
|
|
64079
64644
|
const jobs = [];
|
|
64080
64645
|
for (const file of files) {
|
|
64081
64646
|
try {
|
|
@@ -65172,10 +65737,10 @@ var init_serve = __esm({
|
|
|
65172
65737
|
|
|
65173
65738
|
// packages/cli/dist/tui/interactive.js
|
|
65174
65739
|
import { cwd } from "node:process";
|
|
65175
|
-
import { resolve as resolve32, join as join71, dirname as
|
|
65740
|
+
import { resolve as resolve32, join as join71, dirname as dirname22, extname as extname11 } from "node:path";
|
|
65176
65741
|
import { createRequire as createRequire4 } from "node:module";
|
|
65177
|
-
import { fileURLToPath as
|
|
65178
|
-
import { readFileSync as readFileSync43, writeFileSync as writeFileSync27, appendFileSync as appendFileSync4, rmSync as rmSync3, readdirSync as
|
|
65742
|
+
import { fileURLToPath as fileURLToPath14 } from "node:url";
|
|
65743
|
+
import { readFileSync as readFileSync43, writeFileSync as writeFileSync27, appendFileSync as appendFileSync4, rmSync as rmSync3, readdirSync as readdirSync22, mkdirSync as mkdirSync28 } from "node:fs";
|
|
65179
65744
|
import { existsSync as existsSync54 } from "node:fs";
|
|
65180
65745
|
import { execSync as execSync34 } from "node:child_process";
|
|
65181
65746
|
import { homedir as homedir19 } from "node:os";
|
|
@@ -65195,7 +65760,7 @@ function formatTimeAgo(date) {
|
|
|
65195
65760
|
function getVersion4() {
|
|
65196
65761
|
try {
|
|
65197
65762
|
const require2 = createRequire4(import.meta.url);
|
|
65198
|
-
const thisDir =
|
|
65763
|
+
const thisDir = dirname22(fileURLToPath14(import.meta.url));
|
|
65199
65764
|
const candidates = [
|
|
65200
65765
|
join71(thisDir, "..", "package.json"),
|
|
65201
65766
|
join71(thisDir, "..", "..", "package.json"),
|
|
@@ -65445,7 +66010,7 @@ function gatherMemorySnippets(root) {
|
|
|
65445
66010
|
if (!existsSync54(dir))
|
|
65446
66011
|
continue;
|
|
65447
66012
|
try {
|
|
65448
|
-
for (const f of
|
|
66013
|
+
for (const f of readdirSync22(dir).filter((f2) => f2.endsWith(".json"))) {
|
|
65449
66014
|
const data = JSON.parse(readFileSync43(join71(dir, f), "utf-8"));
|
|
65450
66015
|
for (const val of Object.values(data)) {
|
|
65451
66016
|
const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
|
|
@@ -68258,6 +68823,17 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
68258
68823
|
adminSessionKey = generateSessionKey();
|
|
68259
68824
|
}
|
|
68260
68825
|
voiceSession = new VoiceSession();
|
|
68826
|
+
try {
|
|
68827
|
+
const { isPersonaPlexRunning: isPersonaPlexRunning2, getPersonaPlexWSUrl: getPersonaPlexWSUrl2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
|
|
68828
|
+
if (isPersonaPlexRunning2()) {
|
|
68829
|
+
const ppUrl = getPersonaPlexWSUrl2();
|
|
68830
|
+
if (ppUrl) {
|
|
68831
|
+
voiceSession.personaPlexWsUrl = ppUrl;
|
|
68832
|
+
writeContent(() => renderInfo(`PersonaPlex active \u2014 full-duplex mode at ${ppUrl}`));
|
|
68833
|
+
}
|
|
68834
|
+
}
|
|
68835
|
+
} catch {
|
|
68836
|
+
}
|
|
68261
68837
|
const callState = {
|
|
68262
68838
|
transcribers: /* @__PURE__ */ new Map(),
|
|
68263
68839
|
loading: false,
|
|
@@ -70090,7 +70666,7 @@ __export(run_exports, {
|
|
|
70090
70666
|
});
|
|
70091
70667
|
import { resolve as resolve33 } from "node:path";
|
|
70092
70668
|
import { spawn as spawn22 } from "node:child_process";
|
|
70093
|
-
import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync28, readFileSync as readFileSync44, readdirSync as
|
|
70669
|
+
import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync28, readFileSync as readFileSync44, readdirSync as readdirSync23, existsSync as existsSync55 } from "node:fs";
|
|
70094
70670
|
import { randomBytes as randomBytes17 } from "node:crypto";
|
|
70095
70671
|
import { join as join72 } from "node:path";
|
|
70096
70672
|
function jobsDir2(repoPath) {
|
|
@@ -70234,7 +70810,7 @@ function statusCommand(jobId, repoPath) {
|
|
|
70234
70810
|
}
|
|
70235
70811
|
function jobsCommand(repoPath) {
|
|
70236
70812
|
const dir = jobsDir2(repoPath);
|
|
70237
|
-
const files =
|
|
70813
|
+
const files = readdirSync23(dir).filter((f) => f.endsWith(".json")).sort();
|
|
70238
70814
|
if (files.length === 0) {
|
|
70239
70815
|
console.log("No jobs found.");
|
|
70240
70816
|
return;
|
|
@@ -71160,8 +71736,8 @@ init_output();
|
|
|
71160
71736
|
init_updater();
|
|
71161
71737
|
import { parseArgs as nodeParseArgs2 } from "node:util";
|
|
71162
71738
|
import { createRequire as createRequire5 } from "node:module";
|
|
71163
|
-
import { fileURLToPath as
|
|
71164
|
-
import { dirname as
|
|
71739
|
+
import { fileURLToPath as fileURLToPath15 } from "node:url";
|
|
71740
|
+
import { dirname as dirname23, join as join76 } from "node:path";
|
|
71165
71741
|
|
|
71166
71742
|
// packages/cli/dist/cli.js
|
|
71167
71743
|
import { createInterface } from "node:readline";
|
|
@@ -71268,7 +71844,7 @@ init_output();
|
|
|
71268
71844
|
function getVersion5() {
|
|
71269
71845
|
try {
|
|
71270
71846
|
const require2 = createRequire5(import.meta.url);
|
|
71271
|
-
const pkgPath = join76(
|
|
71847
|
+
const pkgPath = join76(dirname23(fileURLToPath15(import.meta.url)), "..", "package.json");
|
|
71272
71848
|
const pkg = require2(pkgPath);
|
|
71273
71849
|
return pkg.version;
|
|
71274
71850
|
} catch {
|