agentgui 1.0.370 → 1.0.371
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/CLAUDE.md +18 -274
- package/package.json +1 -1
- package/server.js +0 -58
- package/static/index.html +26 -2
- package/static/js/client.js +0 -128
- package/static/js/features.js +76 -307
- package/IPFS-INTEGRATION-COMPLETE.md +0 -185
- package/IPFS_DOWNLOADER.md +0 -277
- package/TASK_2C_COMPLETION.md +0 -334
- package/scripts/inject-pe-section.py +0 -185
- package/setup-npm-token.sh +0 -68
- package/static/js/progress-dialog.js +0 -130
- package/static/js/tts-websocket-handler.js +0 -152
- package/telemetry-id +0 -1
- package/test-websocket-broadcast.js +0 -147
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Injects a .bun section into a Windows PE executable (bun standalone format).
|
|
4
|
-
Usage: inject-pe-section.py <bun-exe> <bundle-bytes> <output-exe>
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import sys
|
|
8
|
-
import struct
|
|
9
|
-
import os
|
|
10
|
-
|
|
11
|
-
def align_up(value, alignment):
|
|
12
|
-
return (value + alignment - 1) & ~(alignment - 1)
|
|
13
|
-
|
|
14
|
-
def read_u16(data, offset): return struct.unpack_from('<H', data, offset)[0]
|
|
15
|
-
def read_u32(data, offset): return struct.unpack_from('<I', data, offset)[0]
|
|
16
|
-
def read_u64(data, offset): return struct.unpack_from('<Q', data, offset)[0]
|
|
17
|
-
def write_u16(data, offset, val): struct.pack_into('<H', data, offset, val)
|
|
18
|
-
def write_u32(data, offset, val): struct.pack_into('<I', data, offset, val)
|
|
19
|
-
def write_u64(data, offset, val): struct.pack_into('<Q', data, offset, val)
|
|
20
|
-
|
|
21
|
-
def add_bun_section(pe_bytes, bundle_bytes):
|
|
22
|
-
data = bytearray(pe_bytes)
|
|
23
|
-
|
|
24
|
-
# PE header offset at 0x3C
|
|
25
|
-
e_lfanew = read_u32(data, 0x3C)
|
|
26
|
-
pe_sig = data[e_lfanew:e_lfanew+4]
|
|
27
|
-
assert pe_sig == b'PE\0\0', f"Not a PE file: {pe_sig!r}"
|
|
28
|
-
|
|
29
|
-
coff_offset = e_lfanew + 4
|
|
30
|
-
machine = read_u16(data, coff_offset)
|
|
31
|
-
num_sections = read_u16(data, coff_offset + 2)
|
|
32
|
-
size_of_optional = read_u16(data, coff_offset + 16)
|
|
33
|
-
|
|
34
|
-
opt_offset = coff_offset + 20
|
|
35
|
-
magic = read_u16(data, opt_offset)
|
|
36
|
-
assert magic == 0x20B, f"Expected PE32+ (0x20B), got 0x{magic:X}"
|
|
37
|
-
|
|
38
|
-
# Optional header fields (PE32+)
|
|
39
|
-
file_alignment = read_u32(data, opt_offset + 36)
|
|
40
|
-
section_alignment = read_u32(data, opt_offset + 32)
|
|
41
|
-
size_of_image = read_u32(data, opt_offset + 56)
|
|
42
|
-
size_of_headers = read_u32(data, opt_offset + 60)
|
|
43
|
-
checksum_offset = opt_offset + 64
|
|
44
|
-
# Data directories start at opt_offset + 112 for PE32+
|
|
45
|
-
# Security directory is entry 4
|
|
46
|
-
security_dd_offset = opt_offset + 112 + 4 * 8 # entry 4, each entry 8 bytes
|
|
47
|
-
|
|
48
|
-
# Section headers follow the optional header
|
|
49
|
-
section_headers_offset = opt_offset + size_of_optional
|
|
50
|
-
SECTION_HEADER_SIZE = 40
|
|
51
|
-
|
|
52
|
-
# Parse existing sections
|
|
53
|
-
sections = []
|
|
54
|
-
for i in range(num_sections):
|
|
55
|
-
off = section_headers_offset + i * SECTION_HEADER_SIZE
|
|
56
|
-
name = data[off:off+8]
|
|
57
|
-
virtual_size = read_u32(data, off + 8)
|
|
58
|
-
virtual_addr = read_u32(data, off + 12)
|
|
59
|
-
raw_size = read_u32(data, off + 16)
|
|
60
|
-
raw_ptr = read_u32(data, off + 20)
|
|
61
|
-
chars = read_u32(data, off + 36)
|
|
62
|
-
sections.append({
|
|
63
|
-
'name': name, 'virtual_size': virtual_size, 'virtual_addr': virtual_addr,
|
|
64
|
-
'raw_size': raw_size, 'raw_ptr': raw_ptr, 'chars': chars, 'off': off
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
if name == b'.bun\0\0\0\0':
|
|
68
|
-
raise ValueError("PE already has a .bun section")
|
|
69
|
-
|
|
70
|
-
assert num_sections < 96, "Too many sections"
|
|
71
|
-
|
|
72
|
-
# Check header space
|
|
73
|
-
new_sh_off = section_headers_offset + num_sections * SECTION_HEADER_SIZE
|
|
74
|
-
new_sh_end = new_sh_off + SECTION_HEADER_SIZE
|
|
75
|
-
first_raw = min((s['raw_ptr'] for s in sections if s['raw_size'] > 0), default=len(data))
|
|
76
|
-
new_size_of_headers = align_up(new_sh_end, file_alignment)
|
|
77
|
-
assert new_size_of_headers <= first_raw, f"Insufficient header space: need {new_size_of_headers}, first_raw={first_raw}"
|
|
78
|
-
|
|
79
|
-
# Find last file end and last va end
|
|
80
|
-
last_file_end = max((s['raw_ptr'] + s['raw_size'] for s in sections), default=0)
|
|
81
|
-
last_va_end = max(
|
|
82
|
-
(s['virtual_addr'] + align_up(max(s['virtual_size'], s['raw_size']), section_alignment)
|
|
83
|
-
for s in sections),
|
|
84
|
-
default=0
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
payload_len = len(bundle_bytes) + 8 # 8-byte u64 LE length prefix
|
|
88
|
-
raw_size_new = align_up(payload_len, file_alignment)
|
|
89
|
-
new_va = align_up(last_va_end, section_alignment)
|
|
90
|
-
new_raw = align_up(last_file_end, file_alignment)
|
|
91
|
-
|
|
92
|
-
# Extend data buffer
|
|
93
|
-
new_file_size = new_raw + raw_size_new
|
|
94
|
-
if len(data) < new_file_size:
|
|
95
|
-
data.extend(b'\0' * (new_file_size - len(data)))
|
|
96
|
-
else:
|
|
97
|
-
# Zero the new section area
|
|
98
|
-
data[new_raw:new_file_size] = b'\0' * raw_size_new
|
|
99
|
-
|
|
100
|
-
# Write new section header
|
|
101
|
-
IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040
|
|
102
|
-
IMAGE_SCN_MEM_READ = 0x40000000
|
|
103
|
-
sh = bytearray(SECTION_HEADER_SIZE)
|
|
104
|
-
sh[0:8] = b'.bun\0\0\0\0'
|
|
105
|
-
struct.pack_into('<I', sh, 8, payload_len) # VirtualSize
|
|
106
|
-
struct.pack_into('<I', sh, 12, new_va) # VirtualAddress
|
|
107
|
-
struct.pack_into('<I', sh, 16, raw_size_new) # SizeOfRawData
|
|
108
|
-
struct.pack_into('<I', sh, 20, new_raw) # PointerToRawData
|
|
109
|
-
struct.pack_into('<I', sh, 36, IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ)
|
|
110
|
-
data[new_sh_off:new_sh_off + SECTION_HEADER_SIZE] = sh
|
|
111
|
-
|
|
112
|
-
# Write payload: u64 LE length + bundle bytes
|
|
113
|
-
write_u64(data, new_raw, len(bundle_bytes))
|
|
114
|
-
data[new_raw + 8: new_raw + 8 + len(bundle_bytes)] = bundle_bytes
|
|
115
|
-
|
|
116
|
-
# Update COFF: number_of_sections
|
|
117
|
-
write_u16(data, coff_offset + 2, num_sections + 1)
|
|
118
|
-
|
|
119
|
-
# Update optional header
|
|
120
|
-
if size_of_headers < new_size_of_headers:
|
|
121
|
-
write_u32(data, opt_offset + 60, new_size_of_headers)
|
|
122
|
-
|
|
123
|
-
# size_of_image: aligned end of new section
|
|
124
|
-
new_size_of_image = align_up(new_va + payload_len, section_alignment)
|
|
125
|
-
write_u32(data, opt_offset + 56, new_size_of_image)
|
|
126
|
-
|
|
127
|
-
# Clear security directory (signature invalidated)
|
|
128
|
-
data[security_dd_offset:security_dd_offset + 8] = b'\0' * 8
|
|
129
|
-
|
|
130
|
-
# Recompute PE checksum
|
|
131
|
-
data = recompute_checksum(data, checksum_offset)
|
|
132
|
-
|
|
133
|
-
return bytes(data)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def recompute_checksum(data, checksum_offset):
|
|
137
|
-
# Zero out the checksum field first
|
|
138
|
-
write_u32(data, checksum_offset, 0)
|
|
139
|
-
|
|
140
|
-
checksum = 0
|
|
141
|
-
# Process data as u16 pairs
|
|
142
|
-
for i in range(0, len(data) - 1, 2):
|
|
143
|
-
val = struct.unpack_from('<H', data, i)[0]
|
|
144
|
-
checksum += val
|
|
145
|
-
if checksum > 0xFFFFFFFF:
|
|
146
|
-
checksum = (checksum & 0xFFFFFFFF) + (checksum >> 32)
|
|
147
|
-
|
|
148
|
-
if len(data) % 2:
|
|
149
|
-
checksum += data[-1]
|
|
150
|
-
|
|
151
|
-
# Fold to 32 bits
|
|
152
|
-
checksum = (checksum & 0xFFFF) + (checksum >> 16)
|
|
153
|
-
checksum += checksum >> 16
|
|
154
|
-
checksum &= 0xFFFF
|
|
155
|
-
checksum += len(data)
|
|
156
|
-
|
|
157
|
-
write_u32(data, checksum_offset, checksum)
|
|
158
|
-
return data
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if __name__ == '__main__':
|
|
162
|
-
if len(sys.argv) != 4:
|
|
163
|
-
print(f"Usage: {sys.argv[0]} <bun-exe> <bundle-js> <output-exe>")
|
|
164
|
-
sys.exit(1)
|
|
165
|
-
|
|
166
|
-
bun_path, bundle_path, out_path = sys.argv[1], sys.argv[2], sys.argv[3]
|
|
167
|
-
|
|
168
|
-
with open(bun_path, 'rb') as f:
|
|
169
|
-
pe_bytes = f.read()
|
|
170
|
-
|
|
171
|
-
with open(bundle_path, 'rb') as f:
|
|
172
|
-
bundle_bytes = f.read()
|
|
173
|
-
|
|
174
|
-
print(f"Bun exe: {len(pe_bytes)//1024//1024}MB, Bundle: {len(bundle_bytes)//1024}KB")
|
|
175
|
-
|
|
176
|
-
result = add_bun_section(pe_bytes, bundle_bytes)
|
|
177
|
-
|
|
178
|
-
with open(out_path, 'wb') as f:
|
|
179
|
-
f.write(result)
|
|
180
|
-
|
|
181
|
-
try:
|
|
182
|
-
os.chmod(out_path, 0o755)
|
|
183
|
-
except (PermissionError, OSError):
|
|
184
|
-
pass
|
|
185
|
-
print(f"Output: {len(result)//1024//1024}MB -> {out_path}")
|
package/setup-npm-token.sh
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
echo "=========================================="
|
|
5
|
-
echo "npm Granular Access Token Setup (3-Month)"
|
|
6
|
-
echo "=========================================="
|
|
7
|
-
echo ""
|
|
8
|
-
echo "STEP 1: Manual token generation on npm.org"
|
|
9
|
-
echo "=========================================="
|
|
10
|
-
echo ""
|
|
11
|
-
echo "Please follow these steps:"
|
|
12
|
-
echo ""
|
|
13
|
-
echo "1. Open your browser and go to:"
|
|
14
|
-
echo " https://www.npmjs.com/settings/lanmower/tokens"
|
|
15
|
-
echo ""
|
|
16
|
-
echo "2. Click 'Generate New Token'"
|
|
17
|
-
echo ""
|
|
18
|
-
echo "3. Select 'Granular Access Token'"
|
|
19
|
-
echo ""
|
|
20
|
-
echo "4. Fill in the form:"
|
|
21
|
-
echo " - Token name: github-actions-3month"
|
|
22
|
-
echo " - Description: GitHub Actions npm publishing (3 month validity)"
|
|
23
|
-
echo " - Permissions: Read and write"
|
|
24
|
-
echo " - Packages: agentgui"
|
|
25
|
-
echo " - Expiration: 90 days"
|
|
26
|
-
echo " - Bypass 2FA: CHECKED"
|
|
27
|
-
echo ""
|
|
28
|
-
echo "5. Click 'Generate'"
|
|
29
|
-
echo ""
|
|
30
|
-
echo "6. COPY the token value (shown only once!)"
|
|
31
|
-
echo ""
|
|
32
|
-
echo "=========================================="
|
|
33
|
-
echo "STEP 2: Add token to GitHub Actions secret"
|
|
34
|
-
echo "=========================================="
|
|
35
|
-
echo ""
|
|
36
|
-
read -p "Paste your npm token here: " NPM_TOKEN
|
|
37
|
-
|
|
38
|
-
if [ -z "$NPM_TOKEN" ]; then
|
|
39
|
-
echo "Error: Token cannot be empty"
|
|
40
|
-
exit 1
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
echo ""
|
|
44
|
-
echo "Setting GitHub Actions secret..."
|
|
45
|
-
|
|
46
|
-
gh secret set NPM_TOKEN --body "$NPM_TOKEN" --repo AnEntrypoint/agentgui
|
|
47
|
-
|
|
48
|
-
echo ""
|
|
49
|
-
echo "Verifying secret was set..."
|
|
50
|
-
gh secret list --repo AnEntrypoint/agentgui | grep NPM_TOKEN
|
|
51
|
-
|
|
52
|
-
echo ""
|
|
53
|
-
echo "=========================================="
|
|
54
|
-
echo "STEP 3: Test the workflow"
|
|
55
|
-
echo "=========================================="
|
|
56
|
-
echo ""
|
|
57
|
-
echo "Creating test commit to trigger workflow..."
|
|
58
|
-
|
|
59
|
-
git -C /home/user/agentgui commit --allow-empty -m "test: verify npm publishing with 3-month token"
|
|
60
|
-
git -C /home/user/agentgui push
|
|
61
|
-
|
|
62
|
-
echo ""
|
|
63
|
-
echo "Monitor the workflow at:"
|
|
64
|
-
echo "https://github.com/AnEntrypoint/agentgui/actions"
|
|
65
|
-
echo ""
|
|
66
|
-
echo "=========================================="
|
|
67
|
-
echo "Setup Complete!"
|
|
68
|
-
echo "=========================================="
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Progress Dialog
|
|
3
|
-
* Modal dialog for displaying download/upload progress
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
class ProgressDialog {
|
|
7
|
-
constructor(config = {}) {
|
|
8
|
-
this.title = config.title || 'Progress';
|
|
9
|
-
this.message = config.message || 'Processing...';
|
|
10
|
-
this.percentage = config.percentage || 0;
|
|
11
|
-
this.cancellable = config.cancellable || false;
|
|
12
|
-
this.onCancel = config.onCancel || null;
|
|
13
|
-
this.overlay = null;
|
|
14
|
-
this.progressBar = null;
|
|
15
|
-
this.progressText = null;
|
|
16
|
-
this.messageEl = null;
|
|
17
|
-
this._create();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
_create() {
|
|
21
|
-
this.overlay = document.createElement('div');
|
|
22
|
-
this.overlay.className = 'folder-modal-overlay visible';
|
|
23
|
-
this.overlay.style.zIndex = '3000';
|
|
24
|
-
|
|
25
|
-
const modal = document.createElement('div');
|
|
26
|
-
modal.className = 'folder-modal';
|
|
27
|
-
modal.style.width = '400px';
|
|
28
|
-
|
|
29
|
-
const header = document.createElement('div');
|
|
30
|
-
header.className = 'folder-modal-header';
|
|
31
|
-
header.innerHTML = `
|
|
32
|
-
<h3>${this._escapeHtml(this.title)}</h3>
|
|
33
|
-
${this.cancellable ? '<button class="folder-modal-close" aria-label="Cancel">×</button>' : ''}
|
|
34
|
-
`;
|
|
35
|
-
|
|
36
|
-
const body = document.createElement('div');
|
|
37
|
-
body.style.padding = '1.5rem 1rem';
|
|
38
|
-
|
|
39
|
-
this.messageEl = document.createElement('div');
|
|
40
|
-
this.messageEl.style.marginBottom = '1rem';
|
|
41
|
-
this.messageEl.style.fontSize = '0.875rem';
|
|
42
|
-
this.messageEl.style.color = 'var(--color-text-primary)';
|
|
43
|
-
this.messageEl.textContent = this.message;
|
|
44
|
-
|
|
45
|
-
const progressContainer = document.createElement('div');
|
|
46
|
-
progressContainer.style.marginBottom = '0.5rem';
|
|
47
|
-
|
|
48
|
-
this.progressBar = document.createElement('div');
|
|
49
|
-
this.progressBar.style.width = '100%';
|
|
50
|
-
this.progressBar.style.height = '8px';
|
|
51
|
-
this.progressBar.style.backgroundColor = 'var(--color-bg-secondary)';
|
|
52
|
-
this.progressBar.style.borderRadius = '4px';
|
|
53
|
-
this.progressBar.style.overflow = 'hidden';
|
|
54
|
-
|
|
55
|
-
const progressFill = document.createElement('div');
|
|
56
|
-
progressFill.className = 'progress-fill';
|
|
57
|
-
progressFill.style.height = '100%';
|
|
58
|
-
progressFill.style.backgroundColor = 'var(--color-primary)';
|
|
59
|
-
progressFill.style.width = this.percentage + '%';
|
|
60
|
-
progressFill.style.transition = 'width 0.3s ease';
|
|
61
|
-
|
|
62
|
-
this.progressBar.appendChild(progressFill);
|
|
63
|
-
progressContainer.appendChild(this.progressBar);
|
|
64
|
-
|
|
65
|
-
this.progressText = document.createElement('div');
|
|
66
|
-
this.progressText.style.fontSize = '0.75rem';
|
|
67
|
-
this.progressText.style.color = 'var(--color-text-secondary)';
|
|
68
|
-
this.progressText.style.textAlign = 'right';
|
|
69
|
-
this.progressText.style.marginTop = '0.25rem';
|
|
70
|
-
this.progressText.textContent = Math.round(this.percentage) + '%';
|
|
71
|
-
|
|
72
|
-
body.appendChild(this.messageEl);
|
|
73
|
-
body.appendChild(progressContainer);
|
|
74
|
-
body.appendChild(this.progressText);
|
|
75
|
-
|
|
76
|
-
modal.appendChild(header);
|
|
77
|
-
modal.appendChild(body);
|
|
78
|
-
this.overlay.appendChild(modal);
|
|
79
|
-
|
|
80
|
-
if (this.cancellable) {
|
|
81
|
-
const closeBtn = header.querySelector('.folder-modal-close');
|
|
82
|
-
closeBtn.addEventListener('click', () => this._handleCancel());
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
document.body.appendChild(this.overlay);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
update(percentage, message) {
|
|
89
|
-
if (message !== undefined) {
|
|
90
|
-
this.message = message;
|
|
91
|
-
if (this.messageEl) {
|
|
92
|
-
this.messageEl.textContent = message;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (percentage !== undefined) {
|
|
97
|
-
this.percentage = Math.min(100, Math.max(0, percentage));
|
|
98
|
-
const fill = this.progressBar?.querySelector('.progress-fill');
|
|
99
|
-
if (fill) {
|
|
100
|
-
fill.style.width = this.percentage + '%';
|
|
101
|
-
}
|
|
102
|
-
if (this.progressText) {
|
|
103
|
-
this.progressText.textContent = Math.round(this.percentage) + '%';
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
close() {
|
|
109
|
-
if (this.overlay && this.overlay.parentNode) {
|
|
110
|
-
this.overlay.remove();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
_handleCancel() {
|
|
115
|
-
if (this.onCancel) {
|
|
116
|
-
this.onCancel();
|
|
117
|
-
}
|
|
118
|
-
this.close();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
_escapeHtml(text) {
|
|
122
|
-
const div = document.createElement('div');
|
|
123
|
-
div.textContent = text;
|
|
124
|
-
return div.innerHTML;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (typeof module !== 'undefined' && module.exports) {
|
|
129
|
-
module.exports = ProgressDialog;
|
|
130
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
(function() {
|
|
2
|
-
class TTSWebSocketHandler {
|
|
3
|
-
constructor(wsManager) {
|
|
4
|
-
this.wsManager = wsManager;
|
|
5
|
-
this.streamBuffers = new Map();
|
|
6
|
-
this.playbackBuffers = new Map();
|
|
7
|
-
this.sequenceTrackers = new Map();
|
|
8
|
-
this.MIN_BUFFER_CHUNKS = 2;
|
|
9
|
-
this.JITTER_BUFFER_SIZE = 10;
|
|
10
|
-
this.chunkTimeoutMs = 5000;
|
|
11
|
-
this.chunkTimers = new Map();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
initStream(streamId) {
|
|
15
|
-
if (!this.streamBuffers.has(streamId)) {
|
|
16
|
-
this.streamBuffers.set(streamId, []);
|
|
17
|
-
this.playbackBuffers.set(streamId, []);
|
|
18
|
-
this.sequenceTrackers.set(streamId, {
|
|
19
|
-
lastSeq: -1,
|
|
20
|
-
missing: [],
|
|
21
|
-
outOfOrder: 0,
|
|
22
|
-
complete: false
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
receiveChunk(streamId, chunk, seq, isLast) {
|
|
28
|
-
this.initStream(streamId);
|
|
29
|
-
const tracker = this.sequenceTrackers.get(streamId);
|
|
30
|
-
const buffer = this.streamBuffers.get(streamId);
|
|
31
|
-
|
|
32
|
-
clearTimeout(this.chunkTimers.get(`${streamId}:${seq}`));
|
|
33
|
-
|
|
34
|
-
if (seq <= tracker.lastSeq) {
|
|
35
|
-
tracker.outOfOrder++;
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (seq > tracker.lastSeq + 1) {
|
|
40
|
-
for (let i = tracker.lastSeq + 1; i < seq; i++) {
|
|
41
|
-
tracker.missing.push(i);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
tracker.lastSeq = seq;
|
|
46
|
-
buffer.push({ chunk, seq, isLast, receivedAt: Date.now() });
|
|
47
|
-
|
|
48
|
-
if (buffer.length > this.JITTER_BUFFER_SIZE) {
|
|
49
|
-
buffer.shift();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (isLast) {
|
|
53
|
-
this.markStreamComplete(streamId);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
this.setChunkTimeout(streamId, seq);
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
setChunkTimeout(streamId, seq) {
|
|
61
|
-
const key = `${streamId}:${seq}`;
|
|
62
|
-
const timer = setTimeout(() => {
|
|
63
|
-
const tracker = this.sequenceTrackers.get(streamId);
|
|
64
|
-
if (tracker && !tracker.missing.includes(seq)) {
|
|
65
|
-
tracker.missing.push(seq);
|
|
66
|
-
}
|
|
67
|
-
}, this.chunkTimeoutMs);
|
|
68
|
-
this.chunkTimers.set(key, timer);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
getPlayableChunks(streamId) {
|
|
72
|
-
const buffer = this.streamBuffers.get(streamId);
|
|
73
|
-
if (!buffer || buffer.length === 0) return [];
|
|
74
|
-
|
|
75
|
-
const playback = this.playbackBuffers.get(streamId);
|
|
76
|
-
const lastPlayedSeq = playback.length > 0
|
|
77
|
-
? playback[playback.length - 1].seq
|
|
78
|
-
: -1;
|
|
79
|
-
|
|
80
|
-
const chunks = buffer.filter(c => c.seq > lastPlayedSeq);
|
|
81
|
-
return chunks;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
markChunksPlayed(streamId, upToSeq) {
|
|
85
|
-
const buffer = this.streamBuffers.get(streamId);
|
|
86
|
-
const playback = this.playbackBuffers.get(streamId);
|
|
87
|
-
|
|
88
|
-
const toPlay = buffer.filter(c => c.seq <= upToSeq);
|
|
89
|
-
playback.push(...toPlay);
|
|
90
|
-
|
|
91
|
-
const newBuffer = buffer.filter(c => c.seq > upToSeq);
|
|
92
|
-
this.streamBuffers.set(streamId, newBuffer);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
canStartPlayback(streamId) {
|
|
96
|
-
const buffer = this.streamBuffers.get(streamId);
|
|
97
|
-
const playback = this.playbackBuffers.get(streamId);
|
|
98
|
-
const tracker = this.sequenceTrackers.get(streamId);
|
|
99
|
-
|
|
100
|
-
if (!buffer) return false;
|
|
101
|
-
if (buffer.length === 0 && !tracker.complete) return false;
|
|
102
|
-
|
|
103
|
-
return buffer.length >= this.MIN_BUFFER_CHUNKS || tracker.complete;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
markStreamComplete(streamId) {
|
|
107
|
-
const tracker = this.sequenceTrackers.get(streamId);
|
|
108
|
-
if (tracker) tracker.complete = true;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
isStreamComplete(streamId) {
|
|
112
|
-
const tracker = this.sequenceTrackers.get(streamId);
|
|
113
|
-
return tracker && tracker.complete;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
hasLostPackets(streamId) {
|
|
117
|
-
const tracker = this.sequenceTrackers.get(streamId);
|
|
118
|
-
return tracker && tracker.missing.length > 0;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
getStreamStats(streamId) {
|
|
122
|
-
const tracker = this.sequenceTrackers.get(streamId);
|
|
123
|
-
const buffer = this.streamBuffers.get(streamId);
|
|
124
|
-
const playback = this.playbackBuffers.get(streamId);
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
buffered: buffer ? buffer.length : 0,
|
|
128
|
-
played: playback ? playback.length : 0,
|
|
129
|
-
totalSeq: tracker ? tracker.lastSeq + 1 : 0,
|
|
130
|
-
missing: tracker ? tracker.missing.length : 0,
|
|
131
|
-
outOfOrder: tracker ? tracker.outOfOrder : 0,
|
|
132
|
-
complete: tracker ? tracker.complete : false
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
cleanupStream(streamId) {
|
|
137
|
-
this.streamBuffers.delete(streamId);
|
|
138
|
-
this.playbackBuffers.delete(streamId);
|
|
139
|
-
this.sequenceTrackers.delete(streamId);
|
|
140
|
-
|
|
141
|
-
const keys = Array.from(this.chunkTimers.keys());
|
|
142
|
-
keys.forEach(key => {
|
|
143
|
-
if (key.startsWith(`${streamId}:`)) {
|
|
144
|
-
clearTimeout(this.chunkTimers.get(key));
|
|
145
|
-
this.chunkTimers.delete(key);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
window.TTSWebSocketHandler = TTSWebSocketHandler;
|
|
152
|
-
})();
|
package/telemetry-id
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
814cd4b3-ffd4-44c7-abbd-b2d9e757f0fc
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { WebSocketServer } from 'ws';
|
|
2
|
-
import http from 'http';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import os from 'os';
|
|
5
|
-
|
|
6
|
-
const PORT = 8899;
|
|
7
|
-
const server = http.createServer();
|
|
8
|
-
const wss = new WebSocketServer({ server, clientTracking: true });
|
|
9
|
-
|
|
10
|
-
let broadcastedMessages = [];
|
|
11
|
-
let clientConnected = false;
|
|
12
|
-
|
|
13
|
-
wss.on('connection', (ws) => {
|
|
14
|
-
clientConnected = true;
|
|
15
|
-
console.log('[WS] Client connected');
|
|
16
|
-
|
|
17
|
-
ws.on('message', (data) => {
|
|
18
|
-
const msg = JSON.parse(data);
|
|
19
|
-
console.log('[WS] Client sent:', msg.type);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
ws.on('close', () => {
|
|
23
|
-
clientConnected = false;
|
|
24
|
-
console.log('[WS] Client disconnected');
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
function broadcastSync(event) {
|
|
29
|
-
if (wss.clients.size === 0) return;
|
|
30
|
-
const data = JSON.stringify(event);
|
|
31
|
-
broadcastedMessages.push(event);
|
|
32
|
-
for (const client of wss.clients) {
|
|
33
|
-
if (client.readyState === 1) {
|
|
34
|
-
client.send(data);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function broadcastModelProgress(progress) {
|
|
40
|
-
const broadcastData = {
|
|
41
|
-
type: 'model_download_progress',
|
|
42
|
-
modelId: progress.type || 'unknown',
|
|
43
|
-
bytesDownloaded: progress.bytesDownloaded || 0,
|
|
44
|
-
bytesRemaining: progress.bytesRemaining || 0,
|
|
45
|
-
totalBytes: progress.totalBytes || 0,
|
|
46
|
-
downloadSpeed: progress.downloadSpeed || 0,
|
|
47
|
-
eta: progress.eta || 0,
|
|
48
|
-
retryCount: progress.retryCount || 0,
|
|
49
|
-
currentGateway: progress.currentGateway || '',
|
|
50
|
-
status: progress.status || (progress.done ? 'completed' : progress.downloading ? 'downloading' : 'paused'),
|
|
51
|
-
percentComplete: progress.percentComplete || 0,
|
|
52
|
-
completedFiles: progress.completedFiles || 0,
|
|
53
|
-
totalFiles: progress.totalFiles || 0,
|
|
54
|
-
timestamp: Date.now(),
|
|
55
|
-
...progress
|
|
56
|
-
};
|
|
57
|
-
broadcastSync(broadcastData);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
server.listen(PORT, () => {
|
|
61
|
-
console.log(`[Server] Listening on ws://localhost:${PORT}`);
|
|
62
|
-
|
|
63
|
-
setTimeout(() => {
|
|
64
|
-
console.log('\n[Test] Simulating model download progress...\n');
|
|
65
|
-
|
|
66
|
-
broadcastModelProgress({
|
|
67
|
-
type: 'stt',
|
|
68
|
-
started: true,
|
|
69
|
-
downloading: true,
|
|
70
|
-
completedFiles: 0,
|
|
71
|
-
totalFiles: 10
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
setTimeout(() => {
|
|
75
|
-
broadcastModelProgress({
|
|
76
|
-
type: 'stt',
|
|
77
|
-
bytesDownloaded: 5242880,
|
|
78
|
-
bytesRemaining: 20971520,
|
|
79
|
-
totalBytes: 26214400,
|
|
80
|
-
downloadSpeed: 1048576,
|
|
81
|
-
eta: 20,
|
|
82
|
-
retryCount: 0,
|
|
83
|
-
currentGateway: 'https://huggingface.co/',
|
|
84
|
-
status: 'downloading',
|
|
85
|
-
percentComplete: 20,
|
|
86
|
-
completedFiles: 2,
|
|
87
|
-
totalFiles: 10
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
setTimeout(() => {
|
|
91
|
-
broadcastModelProgress({
|
|
92
|
-
type: 'stt',
|
|
93
|
-
bytesDownloaded: 15728640,
|
|
94
|
-
bytesRemaining: 10485760,
|
|
95
|
-
totalBytes: 26214400,
|
|
96
|
-
downloadSpeed: 2097152,
|
|
97
|
-
eta: 5,
|
|
98
|
-
retryCount: 0,
|
|
99
|
-
currentGateway: 'https://huggingface.co/',
|
|
100
|
-
status: 'downloading',
|
|
101
|
-
percentComplete: 60,
|
|
102
|
-
completedFiles: 6,
|
|
103
|
-
totalFiles: 10
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
setTimeout(() => {
|
|
107
|
-
broadcastModelProgress({
|
|
108
|
-
type: 'stt',
|
|
109
|
-
started: true,
|
|
110
|
-
done: true,
|
|
111
|
-
downloading: false,
|
|
112
|
-
completedFiles: 10,
|
|
113
|
-
totalFiles: 10,
|
|
114
|
-
status: 'completed'
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
setTimeout(() => {
|
|
118
|
-
console.log('\n[Test] Broadcasting complete. Results:\n');
|
|
119
|
-
console.log(`Broadcasted messages: ${broadcastedMessages.length}`);
|
|
120
|
-
console.log(`Client connected: ${clientConnected}`);
|
|
121
|
-
|
|
122
|
-
console.log('\nMessage types:');
|
|
123
|
-
broadcastedMessages.forEach((msg, idx) => {
|
|
124
|
-
console.log(` [${idx + 1}] Type: ${msg.type}`);
|
|
125
|
-
console.log(` Status: ${msg.status}`);
|
|
126
|
-
console.log(` Complete: ${msg.percentComplete || msg.completedFiles}%`);
|
|
127
|
-
console.log(` Speed: ${msg.downloadSpeed ? (msg.downloadSpeed / 1024 / 1024).toFixed(2) + 'MB/s' : 'N/A'}`);
|
|
128
|
-
console.log(` ETA: ${msg.eta || 0}s`);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const requiredFields = ['modelId', 'bytesDownloaded', 'bytesRemaining', 'downloadSpeed', 'eta', 'retryCount', 'currentGateway', 'status'];
|
|
132
|
-
const allFieldsPresent = broadcastedMessages.every(msg =>
|
|
133
|
-
requiredFields.every(field => field in msg)
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
console.log(`\nAll required fields present: ${allFieldsPresent ? 'PASS' : 'FAIL'}`);
|
|
137
|
-
console.log(`Message count >= 3: ${broadcastedMessages.length >= 3 ? 'PASS' : 'FAIL'}`);
|
|
138
|
-
|
|
139
|
-
server.close(() => {
|
|
140
|
-
process.exit(allFieldsPresent && broadcastedMessages.length >= 3 ? 0 : 1);
|
|
141
|
-
});
|
|
142
|
-
}, 500);
|
|
143
|
-
}, 500);
|
|
144
|
-
}, 500);
|
|
145
|
-
}, 500);
|
|
146
|
-
}, 500);
|
|
147
|
-
});
|