mirror of
https://github.com/koush/scrypted.git
synced 2026-02-03 14:13:28 +00:00
Compare commits
700 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d1d3727dc | ||
|
|
079878b663 | ||
|
|
0d02ea8f08 | ||
|
|
f23ad06eef | ||
|
|
3c8b513c31 | ||
|
|
35df17334c | ||
|
|
2fff8b0044 | ||
|
|
f415e4f2e1 | ||
|
|
9607bcddcf | ||
|
|
1c7f16ed9f | ||
|
|
961cb36a97 | ||
|
|
a4d28791ed | ||
|
|
c1895df062 | ||
|
|
bb902467eb | ||
|
|
7202e99ab0 | ||
|
|
38bac58fc6 | ||
|
|
af8abb6072 | ||
|
|
7ef868e42d | ||
|
|
0185680791 | ||
|
|
1349bb7433 | ||
|
|
85074aaa7a | ||
|
|
beb7ec60ba | ||
|
|
126c96904b | ||
|
|
70b7b4fa98 | ||
|
|
2cd73b5a6a | ||
|
|
d6f13c7128 | ||
|
|
df1b389ef2 | ||
|
|
976204c439 | ||
|
|
1adee0beb8 | ||
|
|
f5a10dd1cc | ||
|
|
293a940771 | ||
|
|
67728883cc | ||
|
|
5d02217a3e | ||
|
|
63a88e727a | ||
|
|
1145caeb58 | ||
|
|
2cc7ab08fd | ||
|
|
bfb8c233f4 | ||
|
|
ebe6bcc58f | ||
|
|
3b0042c922 | ||
|
|
2f4cd9807b | ||
|
|
1711d2a6f7 | ||
|
|
2818120b68 | ||
|
|
61cf589800 | ||
|
|
2c267f6b26 | ||
|
|
aa85e7ec19 | ||
|
|
e585a48084 | ||
|
|
465b4a80bb | ||
|
|
1b7e24fda7 | ||
|
|
8ec6c61784 | ||
|
|
e1f9397ef9 | ||
|
|
3e54db1658 | ||
|
|
a7cc8d0e11 | ||
|
|
be4b772436 | ||
|
|
5e0afa627c | ||
|
|
70c46f9894 | ||
|
|
fe94472282 | ||
|
|
c559212b2b | ||
|
|
10b097480f | ||
|
|
14050d4e3a | ||
|
|
370a82dc56 | ||
|
|
77dd8cf2a8 | ||
|
|
2b2a5c3dd8 | ||
|
|
6a952bf104 | ||
|
|
72c7736b2a | ||
|
|
c6771ce8ae | ||
|
|
e691c71224 | ||
|
|
d22183faa7 | ||
|
|
12ce2dc6ce | ||
|
|
b4b17d420e | ||
|
|
b69dd024e5 | ||
|
|
b43fdf83e2 | ||
|
|
c4a12fe493 | ||
|
|
3c8a3132e5 | ||
|
|
ef65a413e7 | ||
|
|
7219c8bee3 | ||
|
|
86160a74ac | ||
|
|
0dc7aec5c9 | ||
|
|
ec6ccb5826 | ||
|
|
ef55c3f366 | ||
|
|
923dff378c | ||
|
|
6356702ba3 | ||
|
|
a2576d5741 | ||
|
|
6e5782d734 | ||
|
|
7583d072cc | ||
|
|
34f0529691 | ||
|
|
4ad594074a | ||
|
|
8dba09e047 | ||
|
|
56b4a04e56 | ||
|
|
90f546c422 | ||
|
|
ace1c74ec2 | ||
|
|
99c0c53405 | ||
|
|
55fb215cab | ||
|
|
d8e17e9216 | ||
|
|
618a33028b | ||
|
|
536d8f03ae | ||
|
|
6e5c73b48c | ||
|
|
94c4b663f6 | ||
|
|
c95cca0f81 | ||
|
|
d515cc47d0 | ||
|
|
12e60efd35 | ||
|
|
9107558bab | ||
|
|
a8bb431efb | ||
|
|
22ffac1170 | ||
|
|
2f45e72bd3 | ||
|
|
5749a522db | ||
|
|
38037d31b3 | ||
|
|
dd6e5cf854 | ||
|
|
f9b8715cc0 | ||
|
|
3186480f44 | ||
|
|
25521699e8 | ||
|
|
b87906911c | ||
|
|
55e67c9eda | ||
|
|
54c56ac4ce | ||
|
|
547db5bbbd | ||
|
|
5b789b35ec | ||
|
|
bde3dfb9a8 | ||
|
|
d751ac8871 | ||
|
|
d6afbcef26 | ||
|
|
457fbc594e | ||
|
|
aadb190c13 | ||
|
|
f9a1668e5d | ||
|
|
70672e2a87 | ||
|
|
cab0afaa53 | ||
|
|
e0764a54cc | ||
|
|
1e825b84bc | ||
|
|
946e8d3414 | ||
|
|
3043b058d7 | ||
|
|
65fa8dd7f9 | ||
|
|
c6a93cf245 | ||
|
|
911b3f6014 | ||
|
|
8b5d3eaeae | ||
|
|
8099df4a2a | ||
|
|
e703efc1aa | ||
|
|
e9dc5a4254 | ||
|
|
5ae0bb10ff | ||
|
|
da417f3d5c | ||
|
|
b00fd7e684 | ||
|
|
d5ce4e24c4 | ||
|
|
1e09b62795 | ||
|
|
dd256e7a39 | ||
|
|
f6457bf475 | ||
|
|
5008220c26 | ||
|
|
8504319b27 | ||
|
|
61cc544313 | ||
|
|
4e6066a7c9 | ||
|
|
22b790c7f5 | ||
|
|
65ab977d4f | ||
|
|
6e1f5cbfa7 | ||
|
|
4d7be52b98 | ||
|
|
0b0a43fefc | ||
|
|
1449debbd3 | ||
|
|
4e24e44246 | ||
|
|
e4d62668b7 | ||
|
|
a4a3731b94 | ||
|
|
f4a55ee76b | ||
|
|
29714f82d5 | ||
|
|
c2054fc7e0 | ||
|
|
50b312b290 | ||
|
|
db8a3ec40b | ||
|
|
ef07691eef | ||
|
|
8f1c5fdf3c | ||
|
|
f7ac2883ec | ||
|
|
1a87e0daa1 | ||
|
|
2e17e58060 | ||
|
|
c9b88e6d8f | ||
|
|
eaa6da005b | ||
|
|
ca855bb9a6 | ||
|
|
774f987a66 | ||
|
|
0b6ef28ae8 | ||
|
|
677e78e328 | ||
|
|
0f1f1c56fb | ||
|
|
27e7e4c9e2 | ||
|
|
b6b193bf80 | ||
|
|
25f52eb528 | ||
|
|
69d110b234 | ||
|
|
7240f328b3 | ||
|
|
7bf133745b | ||
|
|
77ba56cf38 | ||
|
|
ea6d404f12 | ||
|
|
40a1221f11 | ||
|
|
22444eb63d | ||
|
|
2a6f542e06 | ||
|
|
ec49e4630f | ||
|
|
9de2b480ff | ||
|
|
442e8d53f7 | ||
|
|
f718d435bd | ||
|
|
8bbd112f60 | ||
|
|
6c98fa62be | ||
|
|
2e56a7f7a9 | ||
|
|
8304c1d065 | ||
|
|
21d0ca99e6 | ||
|
|
fa14f4ca83 | ||
|
|
8ae0a33cbe | ||
|
|
dea55e4fcd | ||
|
|
9eab88572e | ||
|
|
427c3e2f7b | ||
|
|
98f97a51e8 | ||
|
|
529b4d30fb | ||
|
|
eaabd02bfe | ||
|
|
7a67c70ef7 | ||
|
|
b784995ebb | ||
|
|
d4da11bb2c | ||
|
|
f556ae7ff3 | ||
|
|
8bb999aa64 | ||
|
|
de99d59162 | ||
|
|
438a6d7fe9 | ||
|
|
b9b3a48a08 | ||
|
|
5c42740ab1 | ||
|
|
e988e5fb96 | ||
|
|
9c8cbc750a | ||
|
|
01e15fb070 | ||
|
|
7aa02d6e4a | ||
|
|
9c9be9db22 | ||
|
|
cc78c072ce | ||
|
|
48c489b898 | ||
|
|
2cbcc05428 | ||
|
|
58a722cfa8 | ||
|
|
2c16c4625e | ||
|
|
60613ee947 | ||
|
|
f69dd06513 | ||
|
|
d011419208 | ||
|
|
789be6bd57 | ||
|
|
45e1b7091e | ||
|
|
f2ab923c79 | ||
|
|
4c3d5133f6 | ||
|
|
d2edfc5ecc | ||
|
|
4c5ae94c7c | ||
|
|
f30efbecec | ||
|
|
4ecb1f3c85 | ||
|
|
3ca0234530 | ||
|
|
b784399afa | ||
|
|
0f16568edb | ||
|
|
7ecee115a6 | ||
|
|
34eb2be551 | ||
|
|
27ff0c8c80 | ||
|
|
51c5df6802 | ||
|
|
328bd78771 | ||
|
|
3d2ae6384f | ||
|
|
e1ba16f708 | ||
|
|
6f47e39bf3 | ||
|
|
e38c3c975f | ||
|
|
9c75b074b5 | ||
|
|
299d926eae | ||
|
|
22d0ce4f82 | ||
|
|
53c2b7cb58 | ||
|
|
86548f6fa4 | ||
|
|
0e1e641f8f | ||
|
|
58e0a748c4 | ||
|
|
b4a58df53a | ||
|
|
b83b7ff559 | ||
|
|
de2173567e | ||
|
|
9c931b21dc | ||
|
|
5291afad6a | ||
|
|
e1ac1ace87 | ||
|
|
1f6f1a82aa | ||
|
|
70af66a875 | ||
|
|
b7bab5b2e2 | ||
|
|
5d5686a9e7 | ||
|
|
1eb5012e9b | ||
|
|
3574e72e4f | ||
|
|
b7ff4dfd5e | ||
|
|
e0ed953963 | ||
|
|
930690a4ba | ||
|
|
1aa4d45caa | ||
|
|
28fb2b0853 | ||
|
|
4fae4fba3b | ||
|
|
b72c8f59eb | ||
|
|
369ad59324 | ||
|
|
51ac5a1042 | ||
|
|
200c107e97 | ||
|
|
35139abe30 | ||
|
|
dc7f305687 | ||
|
|
2a479dd38a | ||
|
|
d32f9bb07a | ||
|
|
a33bed0b44 | ||
|
|
f9847f6f72 | ||
|
|
add53d07f3 | ||
|
|
db21159299 | ||
|
|
6fa7f06852 | ||
|
|
58387e5046 | ||
|
|
1589908698 | ||
|
|
d0183c29a8 | ||
|
|
99dcdd12cf | ||
|
|
b1861e4630 | ||
|
|
193bfce979 | ||
|
|
5b7cc826a6 | ||
|
|
8484d75e82 | ||
|
|
e8fef925bb | ||
|
|
fa200e1bbf | ||
|
|
df0991b882 | ||
|
|
93ff686000 | ||
|
|
6ae9a5618d | ||
|
|
c882b9a04e | ||
|
|
af4269be49 | ||
|
|
61ad99a3f6 | ||
|
|
d71bbf1824 | ||
|
|
74674dab00 | ||
|
|
247f860a23 | ||
|
|
a801fe1f4e | ||
|
|
6744851256 | ||
|
|
10569731aa | ||
|
|
4965b1f99a | ||
|
|
510250c60b | ||
|
|
8e33775b0e | ||
|
|
1077bd1f56 | ||
|
|
a485d8ae69 | ||
|
|
17f42762e7 | ||
|
|
49943a5408 | ||
|
|
585c638220 | ||
|
|
6767892c63 | ||
|
|
289555c03e | ||
|
|
a563e17c56 | ||
|
|
54c317b217 | ||
|
|
0df9c31480 | ||
|
|
19c8436256 | ||
|
|
b73526674a | ||
|
|
fd863f4ba3 | ||
|
|
634b65c216 | ||
|
|
548086403b | ||
|
|
867432cd82 | ||
|
|
b3cc914772 | ||
|
|
b297a4d3d6 | ||
|
|
8144588bcf | ||
|
|
f3265f5fb6 | ||
|
|
f686812f01 | ||
|
|
552787e06b | ||
|
|
3c4de5af39 | ||
|
|
e08df29373 | ||
|
|
1efb624681 | ||
|
|
09afc6c96c | ||
|
|
666d2903e4 | ||
|
|
24eb60bce1 | ||
|
|
d1951687be | ||
|
|
3c3c2c1610 | ||
|
|
0f9106c639 | ||
|
|
ab00ade016 | ||
|
|
6cfc3db05c | ||
|
|
95aa58ce38 | ||
|
|
0d88b4746b | ||
|
|
8c4beeb3a0 | ||
|
|
4846cfaddf | ||
|
|
4e14f7fd6f | ||
|
|
266be72606 | ||
|
|
6a1970c075 | ||
|
|
0575d98424 | ||
|
|
cdf42fc1a2 | ||
|
|
fc1fabc49e | ||
|
|
4e08daecb2 | ||
|
|
58b27805ba | ||
|
|
b37c6bbd06 | ||
|
|
8eca02d819 | ||
|
|
0efdb34114 | ||
|
|
1a25100de2 | ||
|
|
51e0a8836d | ||
|
|
562d0839b7 | ||
|
|
e3df6accea | ||
|
|
03d159a89c | ||
|
|
4ead4726a9 | ||
|
|
b06ef623b3 | ||
|
|
8edb157e2a | ||
|
|
155a1ceb38 | ||
|
|
1cb6212fc6 | ||
|
|
d1bfed3019 | ||
|
|
6bf10d4aff | ||
|
|
3ceef8ff87 | ||
|
|
df1155cf82 | ||
|
|
453469ed98 | ||
|
|
2e4dbceb0e | ||
|
|
c620a4e126 | ||
|
|
5698551b7e | ||
|
|
9e655c0a53 | ||
|
|
35dadaab93 | ||
|
|
76487091da | ||
|
|
3c5b8bc940 | ||
|
|
37e5c49729 | ||
|
|
16fc4407c1 | ||
|
|
3e76c1b1d3 | ||
|
|
d6c6e3c594 | ||
|
|
6c904da49b | ||
|
|
6ac790f824 | ||
|
|
3638f80cef | ||
|
|
809b632417 | ||
|
|
f53330c861 | ||
|
|
66455c8f01 | ||
|
|
e6eb61f04f | ||
|
|
adeb3d837e | ||
|
|
3da3f85513 | ||
|
|
cbe251e345 | ||
|
|
dfa2dacde4 | ||
|
|
9395253b50 | ||
|
|
e020ee1517 | ||
|
|
a7ecb9b5e5 | ||
|
|
24f9b0fca3 | ||
|
|
a78ad99f50 | ||
|
|
c26c5e94a4 | ||
|
|
2d93a69c91 | ||
|
|
1c52297e74 | ||
|
|
5bc76642cc | ||
|
|
15fa27029d | ||
|
|
553678ed1a | ||
|
|
b3b7265263 | ||
|
|
301213fc5f | ||
|
|
da393ae4e0 | ||
|
|
9376fc4ba6 | ||
|
|
51d4aa7b3e | ||
|
|
30334e5bd0 | ||
|
|
dffc05d165 | ||
|
|
172e5b3ccb | ||
|
|
cad60e7730 | ||
|
|
131458576c | ||
|
|
0d7b47e1e9 | ||
|
|
1a33384115 | ||
|
|
1fa5f66b44 | ||
|
|
1032d444fb | ||
|
|
2883824690 | ||
|
|
a505394852 | ||
|
|
1240f401d7 | ||
|
|
b49faaa033 | ||
|
|
cbb1d4533a | ||
|
|
a5a027bd6d | ||
|
|
94acd0e800 | ||
|
|
cabdd91a92 | ||
|
|
93c9b62e87 | ||
|
|
21c771d50f | ||
|
|
bb2ecd7bd8 | ||
|
|
6c0864b883 | ||
|
|
3c8ef7a2cf | ||
|
|
6afecc8185 | ||
|
|
ea16381b7a | ||
|
|
09d3ac587f | ||
|
|
3872cb391a | ||
|
|
7d985937ca | ||
|
|
cef8482b93 | ||
|
|
f729c76346 | ||
|
|
9aa9498aae | ||
|
|
afe832a32a | ||
|
|
be6a81c9a2 | ||
|
|
964bb27d48 | ||
|
|
6bca83b338 | ||
|
|
11860409f1 | ||
|
|
6743f76e09 | ||
|
|
771d90ea73 | ||
|
|
ba28899dc3 | ||
|
|
3e57c90208 | ||
|
|
e155584373 | ||
|
|
4a3968956e | ||
|
|
78c259d14e | ||
|
|
6f4b360d2a | ||
|
|
642795dd9d | ||
|
|
4b3d37b628 | ||
|
|
8e28e6d19e | ||
|
|
d13171551d | ||
|
|
ea1474c21e | ||
|
|
7abff3a91b | ||
|
|
40f11a0053 | ||
|
|
1d3450455b | ||
|
|
bd3b4ac387 | ||
|
|
6787153c30 | ||
|
|
7024daba53 | ||
|
|
28a2e0d898 | ||
|
|
a7757a9a54 | ||
|
|
84fc40e1e5 | ||
|
|
b3b8f6bc70 | ||
|
|
7962606a2b | ||
|
|
9c2ea7d2bc | ||
|
|
4e653a9942 | ||
|
|
167db0f11a | ||
|
|
49064de767 | ||
|
|
51836ca59f | ||
|
|
d64ed629b0 | ||
|
|
83a9ad2250 | ||
|
|
7f9358a3b5 | ||
|
|
9cf3d6c912 | ||
|
|
5e3d1c423c | ||
|
|
64fa68f2d0 | ||
|
|
45cc859636 | ||
|
|
1984bb44ba | ||
|
|
1164e4b15b | ||
|
|
9e5fbc5251 | ||
|
|
6f52390067 | ||
|
|
d34afab6a4 | ||
|
|
2edc74f75b | ||
|
|
e913131f90 | ||
|
|
bca752addb | ||
|
|
43fc6c9fc9 | ||
|
|
448e2c4e6e | ||
|
|
625ea7981e | ||
|
|
1be806eb8e | ||
|
|
e4b71ffbd4 | ||
|
|
16f4cafea3 | ||
|
|
f78df27341 | ||
|
|
57d4e4b9bd | ||
|
|
cb1c062b5e | ||
|
|
e3b996562c | ||
|
|
c96bf237b5 | ||
|
|
5bde86fd15 | ||
|
|
5075920308 | ||
|
|
9e4845b868 | ||
|
|
0cab8f2faf | ||
|
|
510321b7d6 | ||
|
|
efb0a39e52 | ||
|
|
467d89ccaf | ||
|
|
19832c9537 | ||
|
|
0b24e57262 | ||
|
|
f406969140 | ||
|
|
4db26a1779 | ||
|
|
bfc82d0010 | ||
|
|
df3a3d279c | ||
|
|
bb7f2a0c9b | ||
|
|
3f83d4b8f7 | ||
|
|
779fa1df9c | ||
|
|
1c08313e8b | ||
|
|
64e8dc2cc9 | ||
|
|
1914fa60ea | ||
|
|
8073a80bae | ||
|
|
cd7d45155f | ||
|
|
6f6ccff5b1 | ||
|
|
4ea8049d22 | ||
|
|
8354564157 | ||
|
|
26518f0693 | ||
|
|
72df40c422 | ||
|
|
fe1b677381 | ||
|
|
16a9abeb9e | ||
|
|
ba07aa7765 | ||
|
|
35e508a01e | ||
|
|
1e709a058d | ||
|
|
f5a4bab0a8 | ||
|
|
e373a3935e | ||
|
|
f9f9762046 | ||
|
|
4b6751785c | ||
|
|
133cbcf5f5 | ||
|
|
817c171757 | ||
|
|
9af9359b26 | ||
|
|
b684ced629 | ||
|
|
977db49f87 | ||
|
|
992fe98f5e | ||
|
|
a74157168e | ||
|
|
0eed5241f0 | ||
|
|
c0680736e7 | ||
|
|
c345f173d2 | ||
|
|
1ed10cd1cb | ||
|
|
9426db12aa | ||
|
|
a35e821f79 | ||
|
|
983794d5d0 | ||
|
|
cd3e2340b8 | ||
|
|
bb82eb6bde | ||
|
|
5006bb90fc | ||
|
|
7134ef114a | ||
|
|
2f5b1f6526 | ||
|
|
f88f0a25db | ||
|
|
d2810b09ed | ||
|
|
f103ddf660 | ||
|
|
47edffa56d | ||
|
|
86a5a73276 | ||
|
|
1a47015558 | ||
|
|
0de9812760 | ||
|
|
10d16dab21 | ||
|
|
f3f4bbc77f | ||
|
|
029f788407 | ||
|
|
89b93eb2f4 | ||
|
|
6fd35e54e6 | ||
|
|
118c404525 | ||
|
|
833ecb721f | ||
|
|
a8f1e74278 | ||
|
|
1075fb4491 | ||
|
|
f09a797ebf | ||
|
|
1e0fdee7b6 | ||
|
|
be6375e9f4 | ||
|
|
e5ba39f886 | ||
|
|
6841b74a26 | ||
|
|
a3f45e2c49 | ||
|
|
b664ccd24f | ||
|
|
3f244b586f | ||
|
|
9f828739de | ||
|
|
b2cef35bc0 | ||
|
|
3e54540db7 | ||
|
|
debd7f2c40 | ||
|
|
adbc2aaed9 | ||
|
|
6024b4ceaf | ||
|
|
3a065febb5 | ||
|
|
1e2c3e0ca7 | ||
|
|
61dfddeab2 | ||
|
|
b902873d44 | ||
|
|
a38d803b86 | ||
|
|
9c3dab18da | ||
|
|
2ceb2cd9c3 | ||
|
|
a5fd1c0278 | ||
|
|
2c717cb4fc | ||
|
|
df10c4e5f2 | ||
|
|
7c51bb420e | ||
|
|
367eafff5c | ||
|
|
adcdd18497 | ||
|
|
a95b77fe26 | ||
|
|
3ff75f0fde | ||
|
|
eecd38d271 | ||
|
|
7128af20af | ||
|
|
c651c2164b | ||
|
|
6caafd73f5 | ||
|
|
05cb505783 | ||
|
|
07baddc9c3 | ||
|
|
76ac260bf7 | ||
|
|
dfee7c6b09 | ||
|
|
b3ce6a2af3 | ||
|
|
933c0cac0f | ||
|
|
1fb1334a00 | ||
|
|
cb45a00c25 | ||
|
|
fec59af263 | ||
|
|
5d213a4c51 | ||
|
|
d444c4ab7c | ||
|
|
590f955ca9 | ||
|
|
7df4bf2723 | ||
|
|
3416347a1f | ||
|
|
c669bb8902 | ||
|
|
ce5fd2d4fd | ||
|
|
fa8a756059 | ||
|
|
73b85e1cd0 | ||
|
|
1300073712 | ||
|
|
3e296e12a5 | ||
|
|
bf98060a08 | ||
|
|
d1cd380123 | ||
|
|
1a2aadfb52 | ||
|
|
60c854a477 | ||
|
|
0790b60122 | ||
|
|
a3caa09df4 | ||
|
|
02ca8bd765 | ||
|
|
f9e1a94ab3 | ||
|
|
dd0da26df3 | ||
|
|
890f2e8daf | ||
|
|
2c8babe3ce | ||
|
|
8e31b5f970 | ||
|
|
0873a72848 | ||
|
|
145c66e1c8 | ||
|
|
2b60b45113 | ||
|
|
6f63927e2f | ||
|
|
528eabdfc0 | ||
|
|
e201ea1fc1 | ||
|
|
7790810b86 | ||
|
|
e9ec78909b | ||
|
|
26245e17ca | ||
|
|
5d87a1b2dd | ||
|
|
e1efde3868 | ||
|
|
525eb028c6 | ||
|
|
520c6a62a1 | ||
|
|
6e6898ce33 | ||
|
|
1344c9112c | ||
|
|
f2148ce26a | ||
|
|
81b00195d6 | ||
|
|
8f71778f05 | ||
|
|
2e5b8d90aa | ||
|
|
780182b94a | ||
|
|
57480f7606 | ||
|
|
1478684120 | ||
|
|
223b302bed | ||
|
|
f56cef1b50 | ||
|
|
83bfa30d4b | ||
|
|
611674af46 | ||
|
|
941ea7f346 | ||
|
|
2b9c2956d6 | ||
|
|
266d5bf8a3 | ||
|
|
d0007fc7bb | ||
|
|
75f90b78eb | ||
|
|
1e8959413e | ||
|
|
1301247ea3 | ||
|
|
2798fe4d3d | ||
|
|
55a76a86dc | ||
|
|
cebd49fadb | ||
|
|
90adb11f27 | ||
|
|
cea5c95c82 | ||
|
|
0405e13181 | ||
|
|
5659499c16 | ||
|
|
d272a4b86f | ||
|
|
f8a8ed4241 | ||
|
|
892b978065 | ||
|
|
c81c55c12e | ||
|
|
bb9d98921b | ||
|
|
4c66efc4af | ||
|
|
0547ed9a32 | ||
|
|
b046822282 | ||
|
|
b033d24451 | ||
|
|
15464229ad | ||
|
|
93ad50db73 | ||
|
|
427139e8df | ||
|
|
b1100398ec | ||
|
|
b40a2eaf6e | ||
|
|
17c9440fd9 | ||
|
|
ea63a96444 | ||
|
|
0f02f96b89 | ||
|
|
6ce538bb23 | ||
|
|
29ab0e79de | ||
|
|
e07cd13ef3 | ||
|
|
0cbb26051c | ||
|
|
fcb8d938ee | ||
|
|
98fe1d412a | ||
|
|
c19ec63f98 | ||
|
|
a41e915f69 | ||
|
|
f0db59f6d2 | ||
|
|
8e691ff2ee | ||
|
|
42e0810bc0 | ||
|
|
68e91ad996 | ||
|
|
ea628a7130 |
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -27,6 +27,11 @@ Created issues that do not meet these requirements or are improperly filled out
|
||||
1. Delete this section and everything above it.
|
||||
2. Fill out the sections below.
|
||||
|
||||
** Before You Submit**
|
||||
|
||||
- [ ] I checked that my issue isn't already filed: [Search open issues](https://github.com/koush/scrypted/issues).
|
||||
- [ ] I checked the relevant camera/device and/or plugin `Log` in the `Management Console` for errors or warnings that may help identify and resolve the issue myself.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. The issue tracker is only for reporting bugs in Scrypted, for general support check Discord. Hardrware support requests or assistance requests will be immediately closed.
|
||||
|
||||
@@ -43,6 +48,9 @@ A clear and concise description of what you expected to happen.
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Logs**
|
||||
Include a `Log` from the device/camera in the management console (and if applicable, the affacted plugin, like HomeKit).
|
||||
|
||||
**Server (please complete the following information):**
|
||||
- OS: [e.g. Ubuntu]
|
||||
- Installation Method: [e.g. Desktop App, Docker, Local]
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
2
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# disable blank issue creation
|
||||
blank_issues_enabled: false
|
||||
13
.github/workflows/docker-common.yml
vendored
13
.github/workflows/docker-common.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: self-hosted
|
||||
env:
|
||||
NODE_VERSION: '20'
|
||||
NODE_VERSION: '22'
|
||||
strategy:
|
||||
matrix:
|
||||
BASE: ["noble"]
|
||||
@@ -77,13 +77,14 @@ jobs:
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
build-nvidia:
|
||||
name: Push NVIDIA Docker image to Docker Hub
|
||||
build-vendor:
|
||||
name: Push Vendor Docker image to Docker Hub
|
||||
needs: build
|
||||
runs-on: self-hosted
|
||||
strategy:
|
||||
matrix:
|
||||
BASE: ["noble"]
|
||||
VENDOR: ["nvidia", "nvidia-legacy", "intel", "amd"]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v3
|
||||
@@ -138,11 +139,11 @@ jobs:
|
||||
build-args: |
|
||||
BASE=ghcr.io/koush/scrypted-common:${{ matrix.BASE }}-full
|
||||
context: install/docker/
|
||||
file: install/docker/Dockerfile.nvidia
|
||||
file: install/docker/Dockerfile.${{ matrix.VENDOR }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
koush/scrypted-common:${{ matrix.BASE }}-nvidia
|
||||
ghcr.io/koush/scrypted-common:${{ matrix.BASE }}-nvidia
|
||||
koush/scrypted-common:${{ matrix.BASE }}-${{ matrix.VENDOR }}
|
||||
ghcr.io/koush/scrypted-common:${{ matrix.BASE }}-${{ matrix.VENDOR }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
36
.github/workflows/docker.yml
vendored
36
.github/workflows/docker.yml
vendored
@@ -20,9 +20,13 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
BASE: [
|
||||
["noble-nvidia", ".s6"],
|
||||
["noble-full", ".s6"],
|
||||
["noble-lite", ""],
|
||||
["noble-nvidia", ".s6", "noble-nvidia", "nvidia"],
|
||||
["noble-nvidia-legacy", ".s6", "noble-nvidia-legacy", "nvidia-legacy"],
|
||||
["noble-intel", ".s6", "noble-intel", "intel"],
|
||||
["noble-amd", ".s6", "noble-amd", "amd"],
|
||||
["noble-full", ".s6", "noble-full", "full"],
|
||||
["noble-lite", "", "noble-lite", "lite"],
|
||||
["noble-lite", ".router", "noble-router", "router"],
|
||||
]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
@@ -93,17 +97,25 @@ jobs:
|
||||
file: install/docker/Dockerfile${{ matrix.BASE[1] }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
# when publishing a tag (beta or latest), platform and version, create some tags as follows.
|
||||
# using beta 0.0.1 as an example
|
||||
# koush/scrypted:v0.0.1-noble-full
|
||||
# koush/scrypted:beta
|
||||
# koush/scrypted:beta-nvidia|intel|full|router|lite
|
||||
|
||||
# using latest 0.0.2 as an example:
|
||||
# koush/scrypted:v0.0.2-noble-full
|
||||
# koush/scrypted:latest
|
||||
# koush/scrypted:nvidia|intel|full|router|lite
|
||||
tags: |
|
||||
${{ format('koush/scrypted:v{1}-{0}', matrix.BASE[0], github.event.inputs.publish_tag || steps.package-version.outputs.NPM_VERSION) }}
|
||||
${{ matrix.BASE[0] == 'noble-full' && format('koush/scrypted:{0}', github.event.inputs.tag) || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'noble-nvidia' && 'koush/scrypted:nvidia' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'noble-full' && 'koush/scrypted:full' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'noble-lite' && 'koush/scrypted:lite' || '' }}
|
||||
${{ format('koush/scrypted:v{0}-{1}', github.event.inputs.publish_tag || steps.package-version.outputs.NPM_VERSION, matrix.BASE[2]) }}
|
||||
${{ matrix.BASE[2] == 'noble-full' && format('koush/scrypted:{0}', github.event.inputs.tag) || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && format('koush/scrypted:{0}', matrix.BASE[3]) || '' }}
|
||||
${{ github.event.inputs.tag != 'latest' && format('koush/scrypted:{0}-{1}', github.event.inputs.tag, matrix.BASE[3]) || '' }}
|
||||
|
||||
${{ format('ghcr.io/koush/scrypted:v{1}-{0}', matrix.BASE[0], github.event.inputs.publish_tag || steps.package-version.outputs.NPM_VERSION) }}
|
||||
${{ matrix.BASE[0] == 'noble-full' && format('ghcr.io/koush/scrypted:{0}', github.event.inputs.tag) || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'noble-nvidia' && 'ghcr.io/koush/scrypted:nvidia' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'noble-full' && 'ghcr.io/koush/scrypted:full' || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && matrix.BASE[0] == 'noble-lite' && 'ghcr.io/koush/scrypted:lite' || '' }}
|
||||
${{ matrix.BASE[2] == 'noble-full' && format('ghcr.io/koush/scrypted:{0}', github.event.inputs.tag) || '' }}
|
||||
${{ github.event.inputs.tag == 'latest' && format('ghcr.io/koush/scrypted:{0}', matrix.BASE[3]) || ''}}
|
||||
${{ github.event.inputs.tag != 'latest' && format('ghcr.io/koush/scrypted:{0}-{1}', github.event.inputs.tag, matrix.BASE[3]) || '' }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runner: [ubuntu-latest, macos-14, macos-13, windows-latest]
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm, macos-14, macos-13, windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
3674
common/package-lock.json
generated
3674
common/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,11 +12,12 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@scrypted/types": "^0.5.27",
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"@types/node": "^20.19.11",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
|
||||
@@ -9,6 +9,16 @@ export function createAsyncQueue<T>() {
|
||||
const waiting: Deferred<T>[] = [];
|
||||
const queued: { item: T, dequeued?: Deferred<void> }[] = [];
|
||||
|
||||
const wait = async (index: number) => {
|
||||
const q = queued[index];
|
||||
if (!q)
|
||||
return;
|
||||
if (!q.dequeued) {
|
||||
q.dequeued = new Deferred<void>();
|
||||
}
|
||||
return q.dequeued.promise;
|
||||
}
|
||||
|
||||
const dequeue = async () => {
|
||||
if (queued.length) {
|
||||
const { item, dequeued: enqueue } = queued.shift()!;
|
||||
@@ -66,7 +76,7 @@ export function createAsyncQueue<T>() {
|
||||
dequeued?.reject(new Error('abort'));
|
||||
};
|
||||
|
||||
dequeued?.promise.catch(() => {}).finally(() => signal.removeEventListener('abort', h));
|
||||
dequeued?.promise.catch(() => { }).finally(() => signal.removeEventListener('abort', h));
|
||||
signal.addEventListener('abort', h);
|
||||
|
||||
return true;
|
||||
@@ -123,6 +133,9 @@ export function createAsyncQueue<T>() {
|
||||
}
|
||||
|
||||
return {
|
||||
[Symbol.dispose]() {
|
||||
end(new Error('async queue disposed'));
|
||||
},
|
||||
get ended() {
|
||||
return ended;
|
||||
},
|
||||
@@ -151,13 +164,14 @@ export function createAsyncQueue<T>() {
|
||||
dequeue,
|
||||
get queue() {
|
||||
return queue();
|
||||
}
|
||||
},
|
||||
wait,
|
||||
}
|
||||
}
|
||||
|
||||
export function createAsyncQueueFromGenerator<T>(generator: AsyncGenerator<T>) {
|
||||
const q = createAsyncQueue<T>();
|
||||
(async() => {
|
||||
(async () => {
|
||||
try {
|
||||
for await (const i of generator) {
|
||||
await q.enqueue(i);
|
||||
|
||||
@@ -7,7 +7,6 @@ const { systemManager } = sdk;
|
||||
export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
hasEnabledMixin: { [id: string]: string } = {};
|
||||
pluginsComponent: Promise<any>;
|
||||
unshiftMixin = false;
|
||||
|
||||
constructor(nativeId?: string, public autoIncludeToken = 'v4') {
|
||||
super(nativeId);
|
||||
@@ -45,6 +44,10 @@ export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
return this.hasEnabledMixin[device.id] === this.autoIncludeToken;
|
||||
}
|
||||
|
||||
shouldUnshiftMixin(device: ScryptedDevice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async maybeEnableMixin(device: ScryptedDevice) {
|
||||
if (!device || device.mixins?.includes(this.id))
|
||||
return;
|
||||
@@ -61,7 +64,7 @@ export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
|
||||
this.log.i('auto enabling mixin for ' + device.name)
|
||||
const mixins = (device.mixins || []).slice();
|
||||
if (this.unshiftMixin)
|
||||
if (this.shouldUnshiftMixin(device))
|
||||
mixins.unshift(this.id);
|
||||
else
|
||||
mixins.push(this.id);
|
||||
@@ -77,5 +80,5 @@ export abstract class AutoenableMixinProvider extends ScryptedDeviceBase {
|
||||
this.storage.setItem('hasEnabledMixin', JSON.stringify(this.hasEnabledMixin));
|
||||
}
|
||||
|
||||
abstract canMixin(type: ScryptedDeviceType, interfaces: string[]): Promise<string[] | null | undefined | void>;
|
||||
abstract canMixin(type: ScryptedDeviceType | string, interfaces: string[]): Promise<string[] | null | undefined | void>;
|
||||
}
|
||||
|
||||
5
common/src/devices.ts
Normal file
5
common/src/devices.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import type { SystemManager } from '@scrypted/types';
|
||||
|
||||
export function getAllDevices<T>(systemManager: SystemManager) {
|
||||
return Object.keys(systemManager.getSystemState()).map(id => systemManager.getDeviceById<T>(id));
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import type * as monacoEditor from 'monaco-editor';
|
||||
|
||||
export interface StandardLibs {
|
||||
'@types/node/globals.d.ts': string,
|
||||
'@types/node/module.d.ts': string,
|
||||
'@types/node/buffer.d.ts': string,
|
||||
'@types/node/process.d.ts': string,
|
||||
'@types/node/events.d.ts': string,
|
||||
|
||||
@@ -116,6 +116,7 @@ export async function scryptedEval(device: ScryptedDeviceBase, script: string, e
|
||||
export function createMonacoEvalDefaults(extraLibs: { [lib: string]: string }) {
|
||||
const standardlibs: StandardLibs = {
|
||||
"@types/node/globals.d.ts": readFileAsString('@types/node/globals.d.ts'),
|
||||
"@types/node/module.d.ts": readFileAsString('@types/node/module.d.ts'),
|
||||
"@types/node/buffer.d.ts": readFileAsString('@types/node/buffer.d.ts'),
|
||||
"@types/node/process.d.ts": readFileAsString('@types/node/process.d.ts'),
|
||||
"@types/node/events.d.ts": readFileAsString('@types/node/events.d.ts'),
|
||||
|
||||
@@ -70,11 +70,7 @@ export function getH264DecoderArgs(): CodecArgs {
|
||||
],
|
||||
};
|
||||
|
||||
if (isRaspberryPi()) {
|
||||
ret['Raspberry Pi'] = ['-c:v', 'h264_mmal'];
|
||||
ret[V4L2] = ['-c:v', 'h264_v4l2m2m'];
|
||||
}
|
||||
else if (os.platform() === 'linux') {
|
||||
if (os.platform() === 'linux') {
|
||||
ret[V4L2] = ['-c:v', 'h264_v4l2m2m'];
|
||||
}
|
||||
else if (os.platform() === 'win32') {
|
||||
|
||||
8
common/src/json.ts
Normal file
8
common/src/json.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
export function safeParseJson(value: string) {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
export interface RefreshPromise<T> {
|
||||
promise: Promise<T>;
|
||||
cacheDuration: number;
|
||||
}
|
||||
|
||||
export function singletonPromise<T>(rp: undefined | RefreshPromise<T>, method: () => Promise<T>, cacheDuration = 0) {
|
||||
if (rp?.promise)
|
||||
return rp;
|
||||
|
||||
const promise = method();
|
||||
if (!rp) {
|
||||
rp = {
|
||||
promise,
|
||||
cacheDuration,
|
||||
}
|
||||
}
|
||||
else {
|
||||
rp.promise = promise;
|
||||
}
|
||||
promise.finally(() => setTimeout(() => rp.promise = undefined, rp.cacheDuration));
|
||||
return rp;
|
||||
}
|
||||
|
||||
export class TimeoutError<T> extends Error {
|
||||
constructor(public promise: Promise<T>) {
|
||||
super('Operation Timed Out');
|
||||
}
|
||||
}
|
||||
|
||||
export function timeoutPromise<T>(timeout: number, promise: Promise<T>): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const t = setTimeout(() => reject(new TimeoutError(promise)), timeout);
|
||||
|
||||
promise
|
||||
.then(v => {
|
||||
clearTimeout(t);
|
||||
resolve(v);
|
||||
})
|
||||
.catch(e => {
|
||||
clearTimeout(t);
|
||||
reject(e);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
export function timeoutFunction<T>(timeout: number, f: (isTimedOut: () => boolean) => Promise<T>): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
let isTimedOut = false;
|
||||
const promise = f(() => isTimedOut);
|
||||
|
||||
const t = setTimeout(() => {
|
||||
isTimedOut = true;
|
||||
reject(new TimeoutError(promise));
|
||||
}, timeout);
|
||||
|
||||
promise
|
||||
.then(v => {
|
||||
clearTimeout(t);
|
||||
resolve(v);
|
||||
})
|
||||
.catch(e => {
|
||||
clearTimeout(t);
|
||||
reject(e);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
export function createPromiseDebouncer<T>() {
|
||||
let current: Promise<T>;
|
||||
|
||||
return (func: () => Promise<T>): Promise<T> => {
|
||||
if (!current)
|
||||
current = func().finally(() => current = undefined);
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
export function createMapPromiseDebouncer<T>() {
|
||||
const map = new Map<string, Promise<T>>();
|
||||
|
||||
return (key: any, debounce: number, func: () => Promise<T>): Promise<T> => {
|
||||
const keyStr = JSON.stringify(key);
|
||||
let value = map.get(keyStr);
|
||||
if (!value) {
|
||||
value = func().finally(() => {
|
||||
if (!debounce) {
|
||||
map.delete(keyStr);
|
||||
return;
|
||||
}
|
||||
setTimeout(() => map.delete(keyStr), debounce);
|
||||
});
|
||||
map.set(keyStr, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
1
common/src/promise-utils.ts
Symbolic link
1
common/src/promise-utils.ts
Symbolic link
@@ -0,0 +1 @@
|
||||
../../server/src/promise-utils.ts
|
||||
@@ -110,7 +110,9 @@ export class BrowserSignalingSession implements RTCSignalingSession {
|
||||
await this.microphone.replaceTrack(mic.getTracks()[0]);
|
||||
}
|
||||
|
||||
this.microphone.track.enabled = enabled;
|
||||
if (this.microphone?.track) {
|
||||
this.microphone.track.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
|
||||
@@ -93,8 +93,15 @@ export const H265_NAL_TYPE_AGG = 48;
|
||||
export const H265_NAL_TYPE_VPS = 32;
|
||||
export const H265_NAL_TYPE_SPS = 33;
|
||||
export const H265_NAL_TYPE_PPS = 34;
|
||||
export const H265_NAL_TYPE_IDR_N = 19;
|
||||
export const H265_NAL_TYPE_IDR_W = 20;
|
||||
export const H265_NAL_TYPE_BLA_W_LP = 16;
|
||||
export const H265_NAL_TYPE_BLA_W_RADL = 17;
|
||||
export const H265_NAL_TYPE_BLA_N_LP = 18;
|
||||
export const H265_NAL_TYPE_IDR_W_RADL = 19;
|
||||
export const H265_NAL_TYPE_IDR_N_LP = 20;
|
||||
export const H265_NAL_TYPE_CRA_NUT = 21;
|
||||
export const H265_NAL_TYPE_FU = 49;
|
||||
export const H265_NAL_TYPE_SEI_PREFIX = 39;
|
||||
export const H265_NAL_TYPE_SEI_SUFFIX = 40;
|
||||
|
||||
export function findH264NaluType(streamChunk: StreamChunk, naluType: number) {
|
||||
if (streamChunk.type !== 'h264')
|
||||
@@ -161,10 +168,10 @@ export function findH265NaluTypeInNalu(nalu: Buffer, naluType: number) {
|
||||
return;
|
||||
}
|
||||
|
||||
export function getNaluTypes(streamChunk: StreamChunk) {
|
||||
export function getStartedH264NaluTypes(streamChunk: StreamChunk) {
|
||||
if (streamChunk.type !== 'h264')
|
||||
return new Set<number>();
|
||||
return getNaluTypesInNalu(streamChunk.chunks[streamChunk.chunks.length - 1].subarray(12))
|
||||
return getNaluTypesInNalu(streamChunk.chunks[streamChunk.chunks.length - 1].subarray(12), true)
|
||||
}
|
||||
|
||||
export function getNaluTypesInNalu(nalu: Buffer, fuaRequireStart = false, fuaRequireEnd = false) {
|
||||
@@ -205,10 +212,10 @@ export function getNaluTypesInNalu(nalu: Buffer, fuaRequireStart = false, fuaReq
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function getH265NaluTypes(streamChunk: StreamChunk) {
|
||||
export function getStartedH265NaluTypes(streamChunk: StreamChunk) {
|
||||
if (streamChunk.type !== 'h265')
|
||||
return new Set<number>();
|
||||
return getNaluTypesInH265Nalu(streamChunk.chunks[streamChunk.chunks.length - 1].subarray(12))
|
||||
return getNaluTypesInH265Nalu(streamChunk.chunks[streamChunk.chunks.length - 1].subarray(12), true)
|
||||
}
|
||||
|
||||
export function getNaluTypesInH265Nalu(nalu: Buffer, fuaRequireStart = false, fuaRequireEnd = false) {
|
||||
@@ -216,7 +223,7 @@ export function getNaluTypesInH265Nalu(nalu: Buffer, fuaRequireStart = false, fu
|
||||
const naluType = parseH265NaluType(nalu[0]);
|
||||
if (naluType === H265_NAL_TYPE_AGG) {
|
||||
ret.add(H265_NAL_TYPE_AGG);
|
||||
let pos = 1;
|
||||
let pos = 2;
|
||||
while (pos < nalu.length) {
|
||||
const naluLength = nalu.readUInt16BE(pos);
|
||||
pos += 2;
|
||||
@@ -225,6 +232,23 @@ export function getNaluTypesInH265Nalu(nalu: Buffer, fuaRequireStart = false, fu
|
||||
pos += naluLength;
|
||||
}
|
||||
}
|
||||
else if (naluType === H265_NAL_TYPE_FU) {
|
||||
ret.add(H265_NAL_TYPE_FU);
|
||||
const fuaType = nalu[2] & 0x3F; // 6 bits
|
||||
if (fuaRequireStart) {
|
||||
const isFuStart = !!(nalu[2] & 0x80);
|
||||
if (isFuStart)
|
||||
ret.add(fuaType);
|
||||
}
|
||||
else if (fuaRequireEnd) {
|
||||
const isFuEnd = !!(nalu[2] & 0x40);
|
||||
if (isFuEnd)
|
||||
ret.add(fuaType);
|
||||
}
|
||||
else {
|
||||
ret.add(fuaType);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret.add(naluType);
|
||||
}
|
||||
@@ -232,6 +256,26 @@ export function getNaluTypesInH265Nalu(nalu: Buffer, fuaRequireStart = false, fu
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function isH265KeyFrameRelatedInSet(naluTypes: Set<number>, allowCodecInfo = true) {
|
||||
if (naluTypes.has(H265_NAL_TYPE_IDR_N_LP)
|
||||
|| naluTypes.has(H265_NAL_TYPE_IDR_W_RADL)
|
||||
|| naluTypes.has(H265_NAL_TYPE_CRA_NUT)
|
||||
|| naluTypes.has(H265_NAL_TYPE_BLA_N_LP)
|
||||
|| naluTypes.has(H265_NAL_TYPE_BLA_W_LP)
|
||||
|| naluTypes.has(H265_NAL_TYPE_BLA_W_RADL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (allowCodecInfo) {
|
||||
if (naluTypes.has(H265_NAL_TYPE_VPS)
|
||||
|| naluTypes.has(H265_NAL_TYPE_SPS)
|
||||
|| naluTypes.has(H265_NAL_TYPE_PPS))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
export function createRtspParser(options?: StreamParserOptions): RtspStreamParser {
|
||||
let resolve: any;
|
||||
|
||||
@@ -247,26 +291,23 @@ export function createRtspParser(options?: StreamParserOptions): RtspStreamParse
|
||||
'tcp',
|
||||
...(options?.vcodec || []),
|
||||
...(options?.acodec || []),
|
||||
// linux and windows seem to support 64000 but darwin is 32000?
|
||||
'-pkt_size', '32000',
|
||||
'-f', 'rtsp',
|
||||
],
|
||||
findSyncFrame(streamChunks: StreamChunk[]) {
|
||||
for (let prebufferIndex = 0; prebufferIndex < streamChunks.length; prebufferIndex++) {
|
||||
const streamChunk = streamChunks[prebufferIndex];
|
||||
if (streamChunk.type === 'h264') {
|
||||
const naluTypes = getNaluTypes(streamChunk);
|
||||
const naluTypes = getStartedH264NaluTypes(streamChunk);
|
||||
if (naluTypes.has(H264_NAL_TYPE_SPS) || naluTypes.has(H264_NAL_TYPE_IDR)) {
|
||||
return streamChunks.slice(prebufferIndex);
|
||||
}
|
||||
}
|
||||
else if (streamChunk.type === 'h265') {
|
||||
const naluTypes = getH265NaluTypes(streamChunk);
|
||||
const naluTypes = getStartedH265NaluTypes(streamChunk);
|
||||
|
||||
if (naluTypes.has(H265_NAL_TYPE_VPS)
|
||||
|| naluTypes.has(H265_NAL_TYPE_SPS)
|
||||
|| naluTypes.has(H265_NAL_TYPE_PPS)
|
||||
|| naluTypes.has(H265_NAL_TYPE_IDR_N)
|
||||
|| naluTypes.has(H265_NAL_TYPE_IDR_W)
|
||||
) {
|
||||
if (isH265KeyFrameRelatedInSet(naluTypes)) {
|
||||
return streamChunks.slice(prebufferIndex);
|
||||
}
|
||||
}
|
||||
@@ -394,7 +435,7 @@ export class RtspClient extends RtspBase {
|
||||
hasGetParameter = true;
|
||||
contentBase: string;
|
||||
|
||||
constructor(public url: string) {
|
||||
constructor(public readonly url: string) {
|
||||
super();
|
||||
const u = new URL(url);
|
||||
const port = parseInt(u.port) || 554;
|
||||
@@ -511,6 +552,42 @@ export class RtspClient extends RtspBase {
|
||||
}
|
||||
}
|
||||
|
||||
async *handleStream(): AsyncGenerator<{
|
||||
rtcp: boolean,
|
||||
header: Buffer,
|
||||
packet: Buffer,
|
||||
channel: number,
|
||||
}> {
|
||||
while (true) {
|
||||
const header = await readLength(this.client, 4);
|
||||
// can this even happen? since the RTSP request method isn't a fixed
|
||||
// value like the "RTSP" in the RTSP response, I don't think so?
|
||||
if (header[0] !== RTSP_FRAME_MAGIC) {
|
||||
if (header.toString() !== 'RTSP')
|
||||
throw this.createBadHeader(header);
|
||||
|
||||
this.client.unshift(header);
|
||||
|
||||
// do what with this?
|
||||
const message = await super.readMessage();
|
||||
const body = await this.readBody(parseHeaders(message));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const length = header.readUInt16BE(2);
|
||||
const packet = await readLength(this.client, length);
|
||||
const id = header.readUInt8(1);
|
||||
|
||||
yield {
|
||||
channel: id,
|
||||
rtcp: id % 2 === 1,
|
||||
header,
|
||||
packet,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async readLoop() {
|
||||
const deferred = new Deferred<void>();
|
||||
|
||||
@@ -612,8 +689,12 @@ export class RtspClient extends RtspBase {
|
||||
// @ts-ignore
|
||||
const { parseHTTPHeadersQuotedKeyValueSet } = await import('http-auth-utils/dist/utils');
|
||||
|
||||
const authedUrl = new URL(this.url);
|
||||
const username = decodeURIComponent(authedUrl.username);
|
||||
const password = decodeURIComponent(authedUrl.password);
|
||||
|
||||
if (this.wwwAuthenticate.includes('Basic')) {
|
||||
const hash = BASIC.computeHash(url);
|
||||
const hash = BASIC.computeHash({ username, password });
|
||||
return `Basic ${hash}`;
|
||||
}
|
||||
|
||||
@@ -633,10 +714,6 @@ export class RtspClient extends RtspBase {
|
||||
REQUIRED_WWW_AUTHENTICATE_KEYS,
|
||||
) as DigestWWWAuthenticateData;
|
||||
|
||||
const authedUrl = new URL(this.url);
|
||||
const username = decodeURIComponent(authedUrl.username);
|
||||
const password = decodeURIComponent(authedUrl.password);
|
||||
|
||||
const strippedUrl = new URL(url.toString());
|
||||
strippedUrl.username = '';
|
||||
strippedUrl.password = '';
|
||||
|
||||
@@ -175,6 +175,8 @@ export type RTPMap = ReturnType<typeof parseRtpMap>;
|
||||
export function parseRtpMap(mline: ReturnType<typeof parseMLine>, rtpmap: string) {
|
||||
const mlineType = mline.type;
|
||||
const match = rtpmap?.match(/a=rtpmap:([\d]+) (.*?)\/([\d]+)(\/([\d]+))?/);
|
||||
let channels = parseInt(match?.[5]) || undefined;
|
||||
let payloadType = parseInt(match?.[1]);
|
||||
|
||||
rtpmap = rtpmap?.toLowerCase();
|
||||
|
||||
@@ -222,14 +224,20 @@ export function parseRtpMap(mline: ReturnType<typeof parseMLine>, rtpmap: string
|
||||
if (mline.payloadTypes?.includes(0)) {
|
||||
codec = 'pcm_mulaw';
|
||||
ffmpegEncoder = 'pcm_mulaw';
|
||||
payloadType = 0;
|
||||
channels = 1;
|
||||
}
|
||||
else if (mline.payloadTypes?.includes(8)) {
|
||||
codec = 'pcm_alaw';
|
||||
ffmpegEncoder = 'pcm_alaw';
|
||||
payloadType = 8;
|
||||
channels = 1;
|
||||
}
|
||||
else if (mline.payloadTypes?.includes(14)) {
|
||||
codec = 'mp3';
|
||||
ffmpegEncoder = 'mp3';
|
||||
payloadType = 14;
|
||||
channels = 2;
|
||||
}
|
||||
else {
|
||||
// ffmpeg seems to omit the rtpmap type for pcm alaw when creating sdp?
|
||||
@@ -239,17 +247,29 @@ export function parseRtpMap(mline: ReturnType<typeof parseMLine>, rtpmap: string
|
||||
// https://en.wikipedia.org/wiki/RTP_payload_formats
|
||||
codec = 'pcm_alaw';
|
||||
ffmpegEncoder = 'pcm_alaw';
|
||||
payloadType = 8;
|
||||
channels = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// assigned payload types do not need to provide a clock, there is a default.
|
||||
let clock = parseInt(match?.[3]);
|
||||
if (!clock) {
|
||||
clock = undefined;
|
||||
if (codec === 'pcm_mulaw' || codec === 'pcm_alaw')
|
||||
clock = 8000;
|
||||
else if (codec === 'pcm_s16be')
|
||||
clock = 16000;
|
||||
}
|
||||
|
||||
return {
|
||||
line: rtpmap,
|
||||
codec,
|
||||
ffmpegEncoder,
|
||||
rawCodec: match?.[2],
|
||||
clock: parseInt(match?.[3]),
|
||||
channels: parseInt(match?.[5]) || undefined,
|
||||
payloadType: parseInt(match?.[1]),
|
||||
clock,
|
||||
channels,
|
||||
payloadType,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,3 +379,33 @@ export function getSpsPps(
|
||||
pps: Buffer.from(pps, 'base64'),
|
||||
}
|
||||
}
|
||||
|
||||
export function getSpsPpsVps(
|
||||
section: {
|
||||
fmtp: {
|
||||
payloadType: number;
|
||||
parameters: {
|
||||
[key: string]: string;
|
||||
};
|
||||
}[]
|
||||
}
|
||||
) {
|
||||
const parameters = section?.fmtp?.[0]?.parameters;
|
||||
if (!parameters) {
|
||||
return {
|
||||
sps: undefined,
|
||||
pps: undefined,
|
||||
vps: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const sps = parameters['sprop-sps'];
|
||||
const pps = parameters['sprop-pps'];
|
||||
const vps = parameters['sprop-vps'];
|
||||
|
||||
return {
|
||||
sps: sps ? Buffer.from(sps, 'base64') : undefined,
|
||||
pps: pps ? Buffer.from(pps, 'base64') : undefined,
|
||||
vps: vps ? Buffer.from(vps, 'base64') : undefined,
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import { Socket as DatagramSocket } from "dgram";
|
||||
import { once } from "events";
|
||||
import { Duplex } from "stream";
|
||||
import { FFMPEG_FRAGMENTED_MP4_OUTPUT_ARGS, MP4Atom, parseFragmentedMP4 } from "./ffmpeg-mp4-parser-session";
|
||||
import { readLength } from "./read-stream";
|
||||
|
||||
export interface StreamParser {
|
||||
container: string;
|
||||
@@ -25,59 +24,11 @@ export interface StreamParserOptions {
|
||||
export interface StreamChunk {
|
||||
startStream?: Buffer;
|
||||
chunks: Buffer[];
|
||||
type?: string;
|
||||
type: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
// function checkTsPacket(pkt: Buffer) {
|
||||
// const pid = ((pkt[1] & 0x1F) << 8) | pkt[2];
|
||||
// if (pid == 256) {
|
||||
// // found video stream
|
||||
// if ((pkt[3] & 0x20) && (pkt[4] > 0)) {
|
||||
// // have AF
|
||||
// if (pkt[5] & 0x40) {
|
||||
// // found keyframe
|
||||
// console.log('keyframe');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
function createLengthParser(length: number, verify?: (concat: Buffer) => void) {
|
||||
async function* parse(socket: Duplex): AsyncGenerator<StreamChunk> {
|
||||
let pending: Buffer[] = [];
|
||||
let pendingSize = 0;
|
||||
while (true) {
|
||||
const data: Buffer = socket.read();
|
||||
if (!data) {
|
||||
await once(socket, 'readable');
|
||||
continue;
|
||||
}
|
||||
pending.push(data);
|
||||
pendingSize += data.length;
|
||||
if (pendingSize < length)
|
||||
continue;
|
||||
|
||||
const concat = Buffer.concat(pending);
|
||||
|
||||
verify?.(concat);
|
||||
|
||||
const remaining = concat.length % length;
|
||||
const left = concat.slice(0, concat.length - remaining);
|
||||
const right = concat.slice(concat.length - remaining);
|
||||
pending = [right];
|
||||
pendingSize = right.length;
|
||||
|
||||
yield {
|
||||
chunks: [left],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return parse;
|
||||
}
|
||||
|
||||
export function createDgramParser() {
|
||||
async function* parse(socket: DatagramSocket, width: number, height: number, type: string) {
|
||||
while (true) {
|
||||
@@ -91,65 +42,6 @@ export function createDgramParser() {
|
||||
return parse;
|
||||
}
|
||||
|
||||
export function createMpegTsParser(options?: StreamParserOptions): StreamParser {
|
||||
return {
|
||||
container: 'mpegts',
|
||||
outputArguments: [
|
||||
...(options?.vcodec || []),
|
||||
...(options?.acodec || []),
|
||||
'-f', 'mpegts',
|
||||
],
|
||||
parse: createLengthParser(188, concat => {
|
||||
if (concat[0] != 0x47) {
|
||||
throw new Error('Invalid sync byte in mpeg-ts packet. Terminating stream.')
|
||||
}
|
||||
}),
|
||||
findSyncFrame(streamChunks): StreamChunk[] {
|
||||
for (let prebufferIndex = 0; prebufferIndex < streamChunks.length; prebufferIndex++) {
|
||||
const streamChunk = streamChunks[prebufferIndex];
|
||||
|
||||
for (let chunkIndex = 0; chunkIndex < streamChunk.chunks.length; chunkIndex++) {
|
||||
const chunk = streamChunk.chunks[chunkIndex];
|
||||
|
||||
let offset = 0;
|
||||
while (offset + 188 < chunk.length) {
|
||||
const pkt = chunk.subarray(offset, offset + 188);
|
||||
const pid = ((pkt[1] & 0x1F) << 8) | pkt[2];
|
||||
if (pid == 256) {
|
||||
// found video stream
|
||||
if ((pkt[3] & 0x20) && (pkt[4] > 0)) {
|
||||
// have AF
|
||||
if (pkt[5] & 0x40) {
|
||||
// we found the sync frame, but also need to send the pat and pmt
|
||||
// which might be at the start of this chunk before the keyframe.
|
||||
// yolo!
|
||||
return streamChunks.slice(prebufferIndex);
|
||||
// const chunks = streamChunk.chunks.slice(chunkIndex + 1);
|
||||
// const take = chunk.subarray(offset);
|
||||
// chunks.unshift(take);
|
||||
|
||||
// const remainingChunks = streamChunks.slice(prebufferIndex + 1);
|
||||
// const ret = Object.assign({}, streamChunk);
|
||||
// ret.chunks = chunks;
|
||||
// return [
|
||||
// ret,
|
||||
// ...remainingChunks
|
||||
// ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += 188;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return findSyncFrame(streamChunks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function* parseMp4StreamChunks(parser: AsyncGenerator<MP4Atom>) {
|
||||
let ftyp: MP4Atom;
|
||||
let moov: MP4Atom;
|
||||
@@ -213,54 +105,3 @@ export const PIXEL_FORMAT_RGB24: RawVideoPixelFormat = {
|
||||
name: 'rgb24',
|
||||
computeLength: (width, height) => width * height * 3,
|
||||
}
|
||||
|
||||
export function createRawVideoParser(options: RawVideoParserOptions): StreamParser {
|
||||
const pixelFormat = options?.pixelFormat || PIXEL_FORMAT_YUV420P;
|
||||
let filter: string;
|
||||
const { size, everyNFrames } = options;
|
||||
if (size) {
|
||||
filter = `scale=${size.width}:${size.height}`;
|
||||
}
|
||||
if (everyNFrames && everyNFrames > 1) {
|
||||
if (filter)
|
||||
filter += ',';
|
||||
else
|
||||
filter = '';
|
||||
filter = filter + `select=not(mod(n\\,${everyNFrames}))`
|
||||
}
|
||||
|
||||
const inputArguments: string[] = [];
|
||||
if (options.size)
|
||||
inputArguments.push('-s', `${options.size.width}x${options.size.height}`);
|
||||
|
||||
inputArguments.push('-pix_fmt', pixelFormat.name);
|
||||
return {
|
||||
inputArguments,
|
||||
container: 'rawvideo',
|
||||
outputArguments: [
|
||||
'-s', `${options.size.width}x${options.size.height}`,
|
||||
'-an',
|
||||
'-vcodec', 'rawvideo',
|
||||
'-pix_fmt', pixelFormat.name,
|
||||
'-f', 'rawvideo',
|
||||
],
|
||||
async *parse(socket: Duplex, width: number, height: number): AsyncGenerator<StreamChunk> {
|
||||
width = size?.width || width;
|
||||
height = size?.height || height
|
||||
|
||||
if (!width || !height)
|
||||
throw new Error("error parsing rawvideo, unknown width and height");
|
||||
|
||||
const toRead = pixelFormat.computeLength(width, height);
|
||||
while (true) {
|
||||
const buffer = await readLength(socket, toRead);
|
||||
yield {
|
||||
chunks: [buffer],
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
},
|
||||
findSyncFrame,
|
||||
}
|
||||
}
|
||||
98
common/src/using-holder.ts
Normal file
98
common/src/using-holder.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
export abstract class AsyncUsingHolderBase<T> {
|
||||
constructor(private _value: T) {
|
||||
}
|
||||
|
||||
get value(): T {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
async [Symbol.asyncDispose]() {
|
||||
await this.release();
|
||||
}
|
||||
|
||||
abstract asyncDispose(value: T): Promise<void>;
|
||||
|
||||
detach() {
|
||||
const value = this._value;
|
||||
this._value = undefined;
|
||||
return value;
|
||||
}
|
||||
|
||||
async replace(value: T) {
|
||||
this.release();
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
async release() {
|
||||
const released = this.detach();
|
||||
if (released)
|
||||
await this.asyncDispose(released);
|
||||
}
|
||||
}
|
||||
export abstract class UsingHolderBase<T> {
|
||||
constructor(private _value: T) {
|
||||
}
|
||||
|
||||
get value(): T {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
[Symbol.dispose]() {
|
||||
this.release();
|
||||
}
|
||||
|
||||
abstract dispose(value: T): void;
|
||||
|
||||
detach() {
|
||||
const value = this._value;
|
||||
this._value = undefined;
|
||||
return value;
|
||||
}
|
||||
|
||||
replace(value: T) {
|
||||
this.release();
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
release() {
|
||||
const released = this.detach();
|
||||
if (released)
|
||||
this.dispose(released);
|
||||
}
|
||||
}
|
||||
|
||||
export class UsingHolder<T extends Disposable> extends UsingHolderBase<T> {
|
||||
dispose(value: T) {
|
||||
value?.[Symbol.dispose]();
|
||||
}
|
||||
|
||||
transferClosure<V>(closure: (value: UsingHolder<T>) => Promise<V>) {
|
||||
return (async () => {
|
||||
using attached = new UsingHolder(this.detach());
|
||||
return await closure(attached);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
export class AsyncUsingHolder<T extends AsyncDisposable> extends AsyncUsingHolderBase<T> {
|
||||
async asyncDispose(value: T) {
|
||||
value?.[Symbol.asyncDispose]();
|
||||
}
|
||||
|
||||
transferClosure<V>(closure: (value: AsyncUsingHolder<T>) => Promise<V>) {
|
||||
return (async () => {
|
||||
await using attached = new AsyncUsingHolder(this.detach());
|
||||
return await closure(attached);
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
export class DisposableHolder<T> extends UsingHolderBase<T> {
|
||||
constructor(value: T, private _dispose: (value: T) => void) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
dispose(value: T) {
|
||||
this._dispose(value);
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,9 @@ export function createService<T, V>(options: ForkOptions, create: (t: Promise<T>
|
||||
currentFork = sdk.fork<T>(options);
|
||||
currentFork.worker.on('exit', () => currentResult = undefined);
|
||||
currentResult = create(currentFork.result);
|
||||
currentResult.catch(() => currentResult = undefined);
|
||||
currentResult.catch(() => {
|
||||
currentResult = undefined;
|
||||
});
|
||||
return currentResult;
|
||||
},
|
||||
|
||||
@@ -50,7 +52,12 @@ export function createZygote<T>(options?: ForkOptions): Zygote<T> {
|
||||
}
|
||||
|
||||
const gen = next();
|
||||
return () => gen.next().value as PluginFork<T>;
|
||||
return () => {
|
||||
const ret = gen.next();
|
||||
if (ret.done || !ret.value)
|
||||
throw new Error('Zygote exhausted');
|
||||
return ret.value;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"module": "Node16",
|
||||
"moduleResolution": "Node16",
|
||||
"target": "esnext",
|
||||
"noImplicitAny": true,
|
||||
|
||||
2
external/ring-client-api
vendored
2
external/ring-client-api
vendored
Submodule external/ring-client-api updated: d9f51b8b6d...516e96a24e
2
external/werift
vendored
2
external/werift
vendored
Submodule external/werift updated: e379126007...19ff2ef453
@@ -1,6 +1,6 @@
|
||||
# Home Assistant Addon Configuration
|
||||
name: Scrypted
|
||||
version: "v0.120.0-jammy-full"
|
||||
version: "v0.143.0-noble-full"
|
||||
slug: scrypted
|
||||
description: Scrypted is a high performance home video integration and automation platform
|
||||
url: "https://github.com/koush/scrypted"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG BASE="20-jammy-full"
|
||||
ARG BASE="noble-full"
|
||||
FROM ghcr.io/koush/scrypted-common:${BASE}
|
||||
|
||||
WORKDIR /
|
||||
@@ -16,6 +16,6 @@ ENV NODE_OPTIONS="--dns-result-order=ipv4first"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION="20240321"
|
||||
ENV SCRYPTED_BASE_VERSION="20250101"
|
||||
|
||||
CMD ["/bin/sh", "-c", "ulimit -c 0; exec npm --prefix /server exec scrypted-serve"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG BASE="16-jammy"
|
||||
ARG BASE="noble-full"
|
||||
FROM ghcr.io/koush/scrypted-common:${BASE}
|
||||
|
||||
WORKDIR /
|
||||
@@ -8,4 +8,4 @@ WORKDIR /scrypted/server
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
CMD npm run serve-no-build
|
||||
CMD ["npm", "run", "serve-no-build"]
|
||||
|
||||
7
install/docker/Dockerfile.amd
Normal file
7
install/docker/Dockerfile.amd
Normal file
@@ -0,0 +1,7 @@
|
||||
ARG BASE="ghcr.io/koush/scrypted-common:noble-amd"
|
||||
FROM $BASE
|
||||
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="amd"
|
||||
|
||||
# amd opencl
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-amd-graphics.sh | bash
|
||||
@@ -6,8 +6,8 @@
|
||||
# This common file will be used by both Docker and the linux
|
||||
# install script.
|
||||
################################################################
|
||||
ARG BASE="jammy"
|
||||
FROM ubuntu:${BASE} as header
|
||||
ARG BASE="noble"
|
||||
FROM ubuntu:${BASE} AS header
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -19,7 +19,7 @@ RUN apt-get update && apt-get -y install \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
ARG NODE_VERSION=22
|
||||
RUN apt-get install -y ca-certificates curl gnupg
|
||||
RUN mkdir -p /etc/apt/keyrings
|
||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg
|
||||
@@ -35,19 +35,6 @@ RUN apt-get -y install \
|
||||
python3-setuptools \
|
||||
python3-wheel
|
||||
|
||||
# gstreamer native https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c#install-gstreamer-on-ubuntu-or-debian
|
||||
RUN echo "Installing gstreamer."
|
||||
# python-codecs pygobject dependencies
|
||||
RUN apt-get -y install libcairo2-dev libgirepository1.0-dev
|
||||
RUN apt-get -y install \
|
||||
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \
|
||||
gstreamer1.0-vaapi
|
||||
|
||||
# python3 gstreamer bindings
|
||||
RUN echo "Installing gstreamer bindings."
|
||||
RUN apt-get -y install \
|
||||
python3-gst-1.0
|
||||
|
||||
# allow pip to install to system
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
|
||||
@@ -61,7 +48,7 @@ RUN python3 -m pip install debugpy
|
||||
################################################################
|
||||
# Begin section generated from template/Dockerfile.full.footer
|
||||
################################################################
|
||||
FROM header as base
|
||||
FROM header AS base
|
||||
|
||||
# vulkan
|
||||
RUN apt -y install libvulkan1
|
||||
@@ -69,12 +56,9 @@ RUN apt -y install libvulkan1
|
||||
# intel opencl for openvino
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-intel-graphics.sh | bash
|
||||
|
||||
# NPU driver will SIGILL on openvino prior to 2024.5.0
|
||||
# intel NPU
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-intel-npu.sh | bash
|
||||
|
||||
# amd opencl
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-amd-graphics.sh | bash
|
||||
|
||||
# python 3.9 from ppa.
|
||||
# 3.9 is the version with prebuilt support for tensorflow lite
|
||||
RUN add-apt-repository -y ppa:deadsnakes/ppa && \
|
||||
@@ -92,7 +76,7 @@ RUN python3.9 -m pip install debugpy
|
||||
# Coral Edge TPU
|
||||
# https://coral.ai/docs/accelerator/get-started/#runtime-on-linux
|
||||
RUN echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | tee /etc/apt/sources.list.d/coral-edgetpu.list
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
RUN curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /etc/apt/trusted.gpg.d/coral-edgetpu.gpg
|
||||
RUN apt-get -y update && apt-get -y install libedgetpu1-std
|
||||
|
||||
# set default shell to bash
|
||||
|
||||
16
install/docker/Dockerfile.intel
Normal file
16
install/docker/Dockerfile.intel
Normal file
@@ -0,0 +1,16 @@
|
||||
ARG BASE="ghcr.io/koush/scrypted-common:noble-intel"
|
||||
FROM $BASE
|
||||
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="intel"
|
||||
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-intel-oneapi.sh | bash
|
||||
ENV LD_LIBRARY_PATH=/opt/intel/oneapi/tcm/latest/lib
|
||||
ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/intel/oneapi/umf/latest/lib
|
||||
|
||||
# gcc4.8 does not have a latest link however, it does seem to point to a relative lib path
|
||||
ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/intel/oneapi/tbb/latest/env/../lib/intel64/gcc4.8
|
||||
ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/intel/oneapi/tbb/latest/lib
|
||||
|
||||
ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/intel/oneapi/mkl/latest/lib
|
||||
ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/intel/oneapi/compiler/latest/opt/compiler/lib
|
||||
ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/intel/oneapi/compiler/latest/lib
|
||||
@@ -1,5 +1,7 @@
|
||||
ARG BASE="jammy"
|
||||
FROM ubuntu:${BASE} as header
|
||||
ARG BASE="noble-lite"
|
||||
FROM ubuntu:${BASE} AS header
|
||||
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="lite"
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -10,7 +12,7 @@ RUN apt-get update && apt-get -y install \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
ARG NODE_VERSION=22
|
||||
RUN apt-get install -y ca-certificates curl gnupg
|
||||
RUN mkdir -p /etc/apt/keyrings
|
||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg
|
||||
@@ -21,9 +23,8 @@ ENV SCRYPTED_INSTALL_ENVIRONMENT="docker"
|
||||
ENV SCRYPTED_CAN_RESTART="true"
|
||||
ENV SCRYPTED_VOLUME="/server/volume"
|
||||
ENV SCRYPTED_INSTALL_PATH="/server"
|
||||
ENV SHELL="/bin/bash"
|
||||
|
||||
RUN test -f "/usr/bin/python3" && test -f "/usr/bin/python3.12"
|
||||
ENV SCRYPTED_PYTHON_PATH="/usr/bin/python3"
|
||||
ENV SCRYPTED_PYTHON312_PATH="/usr/bin/python3.12"
|
||||
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="lite"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
ARG BASE="ghcr.io/koush/scrypted-common:20-jammy-full"
|
||||
ARG BASE="ghcr.io/koush/scrypted-common:noble-nvidia"
|
||||
FROM $BASE
|
||||
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="nvidia"
|
||||
|
||||
ENV NVIDIA_DRIVER_CAPABILITIES=all
|
||||
ENV NVIDIA_VISIBLE_DEVICES=all
|
||||
|
||||
|
||||
11
install/docker/Dockerfile.nvidia-legacy
Normal file
11
install/docker/Dockerfile.nvidia-legacy
Normal file
@@ -0,0 +1,11 @@
|
||||
ARG BASE="ghcr.io/koush/scrypted-common:noble-nvidia-legacy"
|
||||
FROM $BASE
|
||||
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="nvidia"
|
||||
|
||||
ENV NVIDIA_DRIVER_CAPABILITIES=all
|
||||
ENV NVIDIA_VISIBLE_DEVICES=all
|
||||
|
||||
# nvidia cudnn/libcublas etc.
|
||||
# for some reason this is not provided by the nvidia container toolkit
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-nvidia-graphics-legacy.sh | bash
|
||||
60
install/docker/Dockerfile.router
Normal file
60
install/docker/Dockerfile.router
Normal file
@@ -0,0 +1,60 @@
|
||||
ARG BASE="noble-lite"
|
||||
FROM ghcr.io/koush/scrypted-common:${BASE}
|
||||
|
||||
ENV SCRYPTED_DOCKER_FLAVOR="router"
|
||||
|
||||
# tools
|
||||
RUN apt -y update && apt -y install nano net-tools dnsutils dnsmasq vlan bridge-utils netplan.io nftables isc-dhcp-client cron
|
||||
RUN rm -f /etc/systemd/system/multi-user.target.wants/dnsmasq.service
|
||||
RUN rm -f /etc/systemd/system/sysinit.target.wants/systemd-resolved.service
|
||||
|
||||
# go + caddy
|
||||
RUN GO_VERSION=1.25.1 && ARCH=$(dpkg --print-architecture) && \
|
||||
if [ "$ARCH" = "amd64" ]; then GOARCH="amd64"; \
|
||||
elif [ "$ARCH" = "arm64" ]; then GOARCH="arm64"; \
|
||||
elif [ "$ARCH" = "armhf" ]; then GOARCH="armv6l"; \
|
||||
else echo "Unsupported architecture: $ARCH" && exit 1; fi && \
|
||||
curl -LO "https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz" && \
|
||||
tar -C /usr/local -xzf "go${GO_VERSION}.linux-${GOARCH}.tar.gz" && \
|
||||
rm "go${GO_VERSION}.linux-${GOARCH}.tar.gz"
|
||||
ENV PATH=$PATH:/usr/local/go/bin
|
||||
RUN apt install -y debian-keyring debian-archive-keyring apt-transport-https
|
||||
RUN curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-xcaddy-archive-keyring.gpg
|
||||
RUN curl -1sLf 'https://dl.cloudsmith.io/public/caddy/xcaddy/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-xcaddy.list
|
||||
RUN apt -y update
|
||||
RUN apt -y install xcaddy
|
||||
RUN xcaddy build --with github.com/caddy-dns/cloudflare --output /usr/local/bin/caddy
|
||||
|
||||
# nftables
|
||||
COPY ./router/scrypted-nftables.service /etc/systemd/system
|
||||
RUN systemctl enable scrypted-nftables
|
||||
RUN bash -c 'echo include \"/etc/nftables.d/*.conf\"\; > /etc/nftables.conf'
|
||||
RUN mkdir -p /etc/nftables.d
|
||||
COPY ./router/01-scrypted.conf /etc/nftables.d
|
||||
|
||||
# ipv6 forwarding
|
||||
COPY ./router/scrypted-ip-forwarding.service /etc/systemd/system
|
||||
RUN systemctl enable scrypted-ip-forwarding
|
||||
|
||||
# install turn server, but disable it too set it up on a per interface basis.
|
||||
RUN apt -y update && apt -y install coturn && systemctl disable coturn && rm /usr/lib/systemd/system/coturn.service
|
||||
|
||||
# install usbmuxd for iphone tethering
|
||||
# ensure the pairing info stays in persistent storage
|
||||
RUN apt -y update && apt -y install usbmuxd && rm /usr/lib/systemd/system/usbmuxd.service && ln -sf /server/volume/plugins/\@scrypted/router/usbmuxd /var/lib/lockdown
|
||||
|
||||
WORKDIR /
|
||||
# cache bust
|
||||
ADD "https://www.random.org/cgi-bin/randbyte?nbytes=10&format=h" skipcache
|
||||
ARG SCRYPTED_INSTALL_VERSION="latest"
|
||||
RUN test -n "$SCRYPTED_INSTALL_VERSION"
|
||||
RUN npx -y scrypted@latest install-server ${SCRYPTED_INSTALL_VERSION}
|
||||
|
||||
COPY ./router/scrypted-dhcp-watcher.service /etc/systemd/system/scrypted-dhcp-watcher.service
|
||||
RUN systemctl enable scrypted-dhcp-watcher
|
||||
|
||||
COPY ./router/scrypted.service /etc/systemd/system/scrypted.service
|
||||
RUN systemctl enable scrypted
|
||||
|
||||
WORKDIR /
|
||||
CMD ["/sbin/init"]
|
||||
@@ -1,4 +1,4 @@
|
||||
ARG BASE="20-jammy-full"
|
||||
ARG BASE="noble-full"
|
||||
FROM ghcr.io/koush/scrypted-common:${BASE}
|
||||
|
||||
# avahi advertiser support
|
||||
@@ -8,6 +8,9 @@ RUN apt-get update && apt-get -y install \
|
||||
libavahi-compat-libdnssd-dev \
|
||||
xz-utils
|
||||
|
||||
# killall
|
||||
RUN apt -y install psmisc
|
||||
|
||||
# copy configurations and scripts
|
||||
COPY fs /
|
||||
|
||||
@@ -46,6 +49,6 @@ ENV NODE_OPTIONS="--dns-result-order=ipv4first"
|
||||
|
||||
# changing this forces pip and npm to perform reinstalls.
|
||||
# if this base image changes, this version must be updated.
|
||||
ENV SCRYPTED_BASE_VERSION="20240321"
|
||||
ENV SCRYPTED_BASE_VERSION="20250101"
|
||||
|
||||
CMD ["/bin/sh", "-c", "ulimit -c 0; exec npm --prefix /server exec scrypted-serve"]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
./docker-build.sh
|
||||
|
||||
docker build -t ghcr.io/koush/scrypted:20-jammy-full.nvidia -f Dockerfile.nvidia .
|
||||
docker build -t ghcr.io/koush/scrypted:nvidia -f Dockerfile.nvidia .
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
set -x
|
||||
|
||||
NODE_VERSION=20
|
||||
NODE_VERSION=22
|
||||
SCRYPTED_INSTALL_VERSION=beta
|
||||
IMAGE_BASE=jammy
|
||||
IMAGE_BASE=noble
|
||||
FLAVOR=full
|
||||
BASE=$NODE_VERSION-$IMAGE_BASE-$FLAVOR
|
||||
echo $BASE
|
||||
|
||||
45
install/docker/docker-compose-setup.py
Normal file
45
install/docker/docker-compose-setup.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import os
|
||||
from ruamel.yaml import YAML
|
||||
|
||||
# Define the devices to check for
|
||||
devices_to_check = [
|
||||
"/dev/dri",
|
||||
"/dev/accel",
|
||||
"/dev/apex_0",
|
||||
"/dev/apex_1",
|
||||
"/dev/kfd",
|
||||
"/dev/bus/usb"
|
||||
]
|
||||
|
||||
# Use ruamel.yaml with better formatting preservation
|
||||
yaml = YAML()
|
||||
yaml.preserve_quotes = True
|
||||
# Explicitly set roundtrip mode for comment preservation
|
||||
yaml.typ = 'rt'
|
||||
# Match the original formatting - 4 space indentation
|
||||
yaml.indent = 4
|
||||
# No special block sequence indentation
|
||||
yaml.block_seq_indent = 0
|
||||
# Don't wrap lines
|
||||
yaml.width = None
|
||||
# Preserve unicode
|
||||
yaml.allow_unicode = True
|
||||
|
||||
# Read the docker-compose.yml file
|
||||
with open('docker-compose.yml', 'r') as file:
|
||||
compose_data = yaml.load(file)
|
||||
|
||||
# Get a direct reference to the devices key
|
||||
scrypted_service = compose_data['services']['scrypted']
|
||||
devices = scrypted_service.setdefault('devices', [])
|
||||
|
||||
# Check for devices and add them if they exist
|
||||
for device_path in devices_to_check:
|
||||
if os.path.exists(device_path):
|
||||
device_mapping = f"{device_path}:{device_path}"
|
||||
if device_mapping not in devices:
|
||||
devices.append(device_mapping)
|
||||
|
||||
# Write the modified docker-compose.yml file (preserving comments and formatting)
|
||||
with open('docker-compose.yml', 'w') as file:
|
||||
yaml.dump(compose_data, file)
|
||||
9
install/docker/docker-compose-setup.sh
Normal file
9
install/docker/docker-compose-setup.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# run as privileged so all the devices can be detected and only the necessary ones passed through.
|
||||
|
||||
docker run --rm \
|
||||
--privileged \
|
||||
-v "$(pwd):/app" \
|
||||
-w /app \
|
||||
python:3.12-slim \
|
||||
sh -c "pip install -q --root-user-action=ignore ruamel.yaml && python docker-compose-setup.py"
|
||||
@@ -45,17 +45,21 @@ services:
|
||||
# - SCRYPTED_DOCKER_AVAHI=true
|
||||
|
||||
# NVIDIA (Part 1 of 2)
|
||||
# runtime: nvidia
|
||||
# nvidia runtime: nvidia
|
||||
|
||||
# NVIDIA (Part 2 of 2) - Use NVIDIA image, and remove subsequent default image.
|
||||
# image: ghcr.io/koush/scrypted:nvidia
|
||||
# Valid images:
|
||||
# ghcr.io/koush/scrypted
|
||||
# ghcr.io/koush/scrypted:nvidia
|
||||
# ghcr.io/koush/scrypted:intel
|
||||
# ghcr.io/koush/scrypted:lite
|
||||
image: ghcr.io/koush/scrypted
|
||||
|
||||
volumes:
|
||||
# Scrypted NVR Storage (Part 3 of 3)
|
||||
|
||||
# Modify to add the additional volume for Scrypted NVR.
|
||||
# The following example would mount the /mnt/sda/video path on the host
|
||||
# The following example would mount the /mnt/media/video path on the host
|
||||
# to the /nvr path inside the docker container.
|
||||
# - /mnt/media/video:/nvr
|
||||
|
||||
@@ -128,6 +132,12 @@ services:
|
||||
labels:
|
||||
- "com.centurylinklabs.watchtower.scope=scrypted"
|
||||
|
||||
# Use global DNS servers to avoid issues with some local DNS servers.
|
||||
# particularly with npm registry, etc.
|
||||
dns:
|
||||
- ${SCRYPTED_DNS_SERVER_0:-1.1.1.1}
|
||||
- ${SCRYPTED_DNS_SERVER_1:-8.8.8.8}
|
||||
|
||||
# watchtower manages updates for Scrypted.
|
||||
watchtower:
|
||||
environment:
|
||||
@@ -135,7 +145,7 @@ services:
|
||||
- WATCHTOWER_HTTP_API_UPDATE=true
|
||||
- WATCHTOWER_SCOPE=scrypted
|
||||
- WATCHTOWER_HTTP_API_PERIODIC_POLLS=${WATCHTOWER_HTTP_API_PERIODIC_POLLS:-true}
|
||||
image: containrrr/watchtower
|
||||
image: nickfedor/watchtower
|
||||
container_name: scrypted-watchtower
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
@@ -148,3 +158,11 @@ services:
|
||||
- 10444:8080
|
||||
# check for updates once an hour (interval is in seconds)
|
||||
command: --interval 3600 --cleanup --scope scrypted
|
||||
|
||||
# Use global DNS servers to avoid issues with some local DNS servers.
|
||||
# particularly with npm registry, etc.
|
||||
dns:
|
||||
- ${SCRYPTED_DNS_SERVER_0:-1.1.1.1}
|
||||
- ${SCRYPTED_DNS_SERVER_1:-8.8.8.8}
|
||||
# LXC usage only
|
||||
# lxc profiles: ["disabled"]
|
||||
|
||||
@@ -21,21 +21,34 @@ else
|
||||
distro="noble"
|
||||
fi
|
||||
|
||||
apt -y update
|
||||
apt -y install rsync gpg
|
||||
# the deb no longer seems to install a key?
|
||||
gpg --keyserver keyserver.ubuntu.com --recv-keys 9386B48A1A693C5C
|
||||
gpg --export --armor 9386B48A1A693C5C | tee /etc/apt/trusted.gpg.d/amdgpu.asc
|
||||
|
||||
# https://amdgpu-install.readthedocs.io/en/latest/install-prereq.html#installing-the-installer-package
|
||||
|
||||
FILENAME=$(curl -s -L https://repo.radeon.com/amdgpu-install/latest/ubuntu/$distro/ | grep -o 'amdgpu-install_[^ ]*' | cut -d'"' -f1)
|
||||
if [ -z "$FILENAME" ]
|
||||
then
|
||||
echo "AMD graphics package can not be installed. Could not find the package name."
|
||||
exit 1
|
||||
fi
|
||||
# AMD keeps breaking these links. Use hard links.
|
||||
|
||||
# FILENAME=$(curl -s -L https://repo.radeon.com/amdgpu-install/latest/ubuntu/$distro/ | grep -o 'amdgpu-install_[^ ]*' | cut -d'"' -f1)
|
||||
# if [ -z "$FILENAME" ]
|
||||
# then
|
||||
# echo "AMD graphics package can not be installed. Could not find the package name."
|
||||
# exit 1
|
||||
# fi
|
||||
|
||||
set -e
|
||||
mkdir -p /tmp/amd
|
||||
cd /tmp/amd
|
||||
curl -O -L http://repo.radeon.com/amdgpu-install/latest/ubuntu/$distro/$FILENAME
|
||||
apt -y install rsync
|
||||
# curl -O -L https://repo.radeon.com/amdgpu-install/latest/ubuntu/$distro/$FILENAME
|
||||
|
||||
FILENAME=amdgpu-install_7.0.1.70001-1_all.deb
|
||||
curl -O -L https://repo.radeon.com/amdgpu-install/7.0.1/ubuntu/$distro/$FILENAME
|
||||
|
||||
dpkg -i $FILENAME
|
||||
apt -y update
|
||||
|
||||
amdgpu-install --usecase=opencl --no-dkms -y --accept-eula
|
||||
cd /tmp
|
||||
rm -rf /tmp/amd
|
||||
|
||||
@@ -4,6 +4,25 @@ then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
UBUNTU_22_04=$(lsb_release -r | grep "22.04")
|
||||
UBUNTU_24_04=$(lsb_release -r | grep "24.04")
|
||||
|
||||
# needs either ubuntu 22.0.4 or 24.04
|
||||
if [ -z "$UBUNTU_22_04" ] && [ -z "$UBUNTU_24_04" ]
|
||||
then
|
||||
echo "Intel graphics package can not be installed. Ubuntu version could not be detected when checking lsb-release and /etc/os-release."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$UBUNTU_22_04" ]
|
||||
then
|
||||
distro="jammy"
|
||||
|
||||
else
|
||||
distro="noble"
|
||||
fi
|
||||
|
||||
# no errors beyond this point
|
||||
set -e
|
||||
|
||||
@@ -21,13 +40,26 @@ set -e
|
||||
|
||||
# need intel-media-va-driver-non-free, but all the other intel packages are installed from Intel github.
|
||||
echo "Installing Intel graphics packages."
|
||||
apt-get update && apt-get install -y gpg-agent &&
|
||||
rm -f /usr/share/keyrings/intel-graphics.gpg &&
|
||||
curl -L https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --yes --output /usr/share/keyrings/intel-graphics.gpg &&
|
||||
echo 'deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/graphics/ubuntu jammy arc' | tee /etc/apt/sources.list.d/intel.gpu.jammy.list &&
|
||||
apt-get -y update &&
|
||||
apt-get -y install intel-media-va-driver-non-free &&
|
||||
apt-get -y dist-upgrade;
|
||||
|
||||
if [ "$distro" == "jammy" ]
|
||||
then
|
||||
apt-get update && apt-get install -y gpg-agent &&
|
||||
rm -f /usr/share/keyrings/intel-graphics.gpg &&
|
||||
curl -L https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --yes --output /usr/share/keyrings/intel-graphics.gpg &&
|
||||
echo "deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/graphics/ubuntu $distro arc" | tee /etc/apt/sources.list.d/intel.gpu.$distro.list &&
|
||||
apt-get -y update &&
|
||||
apt-get -y install intel-media-va-driver-non-free &&
|
||||
apt-get -y dist-upgrade;
|
||||
else
|
||||
apt-get update && apt-get install -y gpg-agent &&
|
||||
rm -f /usr/share/keyrings/intel-graphics.gpg &&
|
||||
curl -L https://repositories.intel.com/gpu/intel-graphics.key | gpg --dearmor --yes --output /usr/share/keyrings/intel-graphics.gpg &&
|
||||
echo "deb [arch=amd64,i386 signed-by=/usr/share/keyrings/intel-graphics.gpg] https://repositories.intel.com/gpu/ubuntu $distro unified" | tee /etc/apt/sources.list.d/intel-gpu-$distro.list &&
|
||||
apt-get -y update &&
|
||||
apt-get -y install intel-media-va-driver-non-free &&
|
||||
apt-get -y dist-upgrade;
|
||||
fi
|
||||
|
||||
|
||||
rm -rf /tmp/gpu && mkdir -p /tmp/gpu && cd /tmp/gpu
|
||||
|
||||
@@ -37,41 +69,50 @@ apt-get install -y ocl-icd-libopencl1
|
||||
# install 24.35.30872.22 for legacy support. Then install latest.
|
||||
# https://github.com/intel/compute-runtime/issues/770#issuecomment-2515166915
|
||||
|
||||
# https://github.com/intel/compute-runtime/releases/tag/24.35.30872.22
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.20/intel-igc-core_1.0.17537.20_amd64.deb
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.20/intel-igc-opencl_1.0.17537.20_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-level-zero-gpu-dbgsym_1.3.30872.22_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-level-zero-gpu-legacy1-dbgsym_1.3.30872.22_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-level-zero-gpu-legacy1_1.3.30872.22_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-level-zero-gpu_1.3.30872.22_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-opencl-icd-dbgsym_24.35.30872.22_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-opencl-icd-legacy1-dbgsym_24.35.30872.22_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-opencl-icd-legacy1_24.35.30872.22_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-opencl-icd_24.35.30872.22_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/libigdgmm12_22.5.0_amd64.deb
|
||||
# original legacy packages
|
||||
# # https://github.com/intel/compute-runtime/releases/tag/24.35.30872.22
|
||||
# curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.20/intel-igc-core_1.0.17537.20_amd64.deb
|
||||
# curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.20/intel-igc-opencl_1.0.17537.20_amd64.deb
|
||||
# # curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-level-zero-gpu-dbgsym_1.3.30872.22_amd64.ddeb
|
||||
# # curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-level-zero-gpu-legacy1-dbgsym_1.3.30872.22_amd64.ddeb
|
||||
# curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-level-zero-gpu-legacy1_1.3.30872.22_amd64.deb
|
||||
# curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-level-zero-gpu_1.3.30872.22_amd64.deb
|
||||
# # curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-opencl-icd-dbgsym_24.35.30872.22_amd64.ddeb
|
||||
# # curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-opencl-icd-legacy1-dbgsym_24.35.30872.22_amd64.ddeb
|
||||
# curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-opencl-icd-legacy1_24.35.30872.22_amd64.deb
|
||||
# curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/intel-opencl-icd_24.35.30872.22_amd64.deb
|
||||
# curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.22/libigdgmm12_22.5.0_amd64.deb
|
||||
|
||||
# new legacy packages
|
||||
# https://github.com/intel/compute-runtime/releases/tag/24.35.30872.36
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-core_1.0.17537.24_amd64.deb
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-opencl_1.0.17537.24_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-level-zero-gpu-legacy1-dbgsym_1.5.30872.36_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-level-zero-gpu-legacy1_1.5.30872.36_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1-dbgsym_24.35.30872.36_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1_24.35.30872.36_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/libigdgmm12_22.5.0_amd64.deb
|
||||
|
||||
dpkg -i *.deb
|
||||
rm -f *.deb
|
||||
|
||||
# https://github.com/intel/compute-runtime/releases/tag/24.45.31740.9
|
||||
# https://github.com/intel/compute-runtime/releases
|
||||
# note that at time of commit, IGC supports ubuntu 24.04 only possibly due to their builder being on 24.04.
|
||||
IGC_VERSION=2_2.1.12+18087_amd64
|
||||
COMPUTE_VERSION=24.45.31740.9
|
||||
ZERO_GPU_VERSION=1.6.31740.9_amd64
|
||||
LIBIGDGMM_VERSION=22.5.2_amd64
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/v2.1.12/intel-igc-core-$IGC_VERSION.deb
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/v2.1.12/intel-igc-opencl-$IGC_VERSION.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/$COMPUTE_VERSION/intel-level-zero-gpu-dbgsym_$ZERO_GPU_VERSION.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/$COMPUTE_VERSION/intel-level-zero-gpu_$ZERO_GPU_VERSION.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/$COMPUTE_VERSION/intel-opencl-icd-dbgsym_"$COMPUTE_VERSION"_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/$COMPUTE_VERSION/intel-opencl-icd_"$COMPUTE_VERSION"_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/$COMPUTE_VERSION/libigdgmm12_$LIBIGDGMM_VERSION.deb
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/v2.18.5/intel-igc-core-2_2.18.5+19820_amd64.deb
|
||||
curl -O -L https://github.com/intel/intel-graphics-compiler/releases/download/v2.18.5/intel-igc-opencl-2_2.18.5+19820_amd64.deb
|
||||
# curl -O -L https://github.com/intel/compute-runtime/releases/download/25.35.35096.9/intel-ocloc-dbgsym_25.35.35096.9-0_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/25.35.35096.9/intel-ocloc_25.35.35096.9-0_amd64.deb
|
||||
# curl -O -L https://github.com/intel/compute-runtime/releases/download/25.35.35096.9/intel-opencl-icd-dbgsym_25.35.35096.9-0_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/25.35.35096.9/intel-opencl-icd_25.35.35096.9-0_amd64.deb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/25.35.35096.9/libigdgmm12_22.8.1_amd64.deb
|
||||
# curl -O -L https://github.com/intel/compute-runtime/releases/download/25.35.35096.9/libze-intel-gpu1-dbgsym_25.35.35096.9-0_amd64.ddeb
|
||||
curl -O -L https://github.com/intel/compute-runtime/releases/download/25.35.35096.9/libze-intel-gpu1_25.35.35096.9-0_amd64.deb
|
||||
|
||||
set +e
|
||||
dpkg -i *.deb
|
||||
set -e
|
||||
# the legacy + latest process says this may be necessary but it does not seem to be in a clean environment.
|
||||
apt-get install --fix-broken
|
||||
apt-get -y install --fix-broken
|
||||
|
||||
|
||||
cd /tmp && rm -rf /tmp/gpu
|
||||
|
||||
@@ -9,11 +9,17 @@ UBUNTU_24_04=$(lsb_release -r | grep "24.04")
|
||||
|
||||
if [ -z "$UBUNTU_22_04" ] && [ -z "$UBUNTU_24_04" ]
|
||||
then
|
||||
# proxmox is compatible with ubuntu 22.04, check for /etc/pve directory
|
||||
if [ -d "/etc/pve" ]
|
||||
then
|
||||
# proxmox is compatible with intel's ubuntu builds, check for /etc/pve directory
|
||||
# then determine debian version
|
||||
version=$(cat /etc/debian_version 2>/dev/null)
|
||||
|
||||
# Determine if it's Debian 12 or 13
|
||||
if [[ "$version" == 12* ]]; then
|
||||
UBUNTU_22_04=true
|
||||
elif [[ "$version" == 13* ]]; then
|
||||
UBUNTU_24_04=true
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# needs either ubuntu 22.0.4 or 24.04
|
||||
@@ -25,8 +31,10 @@ fi
|
||||
|
||||
if [ -n "$UBUNTU_22_04" ]
|
||||
then
|
||||
ubuntu_distro=ubuntu2204
|
||||
distro="22.04_amd64"
|
||||
else
|
||||
ubuntu_distro=ubuntu2404
|
||||
distro="24.04_amd64"
|
||||
fi
|
||||
|
||||
@@ -38,22 +46,24 @@ set -e
|
||||
rm -rf /tmp/npu && mkdir -p /tmp/npu && cd /tmp/npu
|
||||
|
||||
# level zero must also be installed
|
||||
LEVEL_ZERO_VERSION=1.19.2
|
||||
LEVEL_ZERO_VERSION=1.24.2
|
||||
# https://github.com/oneapi-src/level-zero
|
||||
curl -O -L https://github.com/oneapi-src/level-zero/releases/download/v"$LEVEL_ZERO_VERSION"/level-zero_"$LEVEL_ZERO_VERSION"+u$distro.deb
|
||||
curl -O -L https://github.com/oneapi-src/level-zero/releases/download/v"$LEVEL_ZERO_VERSION"/level-zero-devel_"$LEVEL_ZERO_VERSION"+u$distro.deb
|
||||
|
||||
# npu driver
|
||||
# https://github.com/intel/linux-npu-driver
|
||||
NPU_VERSION=1.10.0
|
||||
NPU_VERSION_DATE=20241107-11729849322
|
||||
curl -O -L https://github.com/intel/linux-npu-driver/releases/download/v"$NPU_VERSION"/intel-driver-compiler-npu_$NPU_VERSION."$NPU_VERSION_DATE"_ubuntu$distro.deb
|
||||
NPU_VERSION=1.23.0
|
||||
NPU_VERSION_DATE=20250827-17270089246
|
||||
NPU_TAR_FILENAME=linux-npu-driver-v"$NPU_VERSION"."$NPU_VERSION_DATE"-$ubuntu_distro.tar.gz
|
||||
curl -O -L https://github.com/intel/linux-npu-driver/releases/download/v"$NPU_VERSION"/"$NPU_TAR_FILENAME"
|
||||
tar xzvf "$NPU_TAR_FILENAME"
|
||||
|
||||
# firmware can only be installed on host. will cause problems inside container.
|
||||
if [ -n "$INTEL_FW_NPU" ]
|
||||
if [ ! -z "$INTEL_FW_NPU" ]
|
||||
then
|
||||
curl -O -L https://github.com/intel/linux-npu-driver/releases/download/v"$NPU_VERSION"/intel-fw-npu_$NPU_VERSION."$NPU_VERSION_DATE"_ubuntu$distro.deb
|
||||
rm *fw-npu*
|
||||
fi
|
||||
curl -O -L https://github.com/intel/linux-npu-driver/releases/download/v"$NPU_VERSION"/intel-level-zero-npu_$NPU_VERSION."$NPU_VERSION_DATE"_ubuntu$distro.deb
|
||||
|
||||
apt -y update
|
||||
apt -y install libtbb12
|
||||
|
||||
18
install/docker/install-intel-oneapi.sh
Normal file
18
install/docker/install-intel-oneapi.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
if [ "$(uname -m)" = "x86_64" ]
|
||||
then
|
||||
apt -y update
|
||||
apt -y install gpg
|
||||
|
||||
# download the key to system keyring
|
||||
curl -1sLf https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | gpg --dearmor --yes --output /usr/share/keyrings/oneapi-archive-keyring.gpg
|
||||
|
||||
# add signed entry to apt sources and configure the APT client to use Intel repository:
|
||||
echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" | tee /etc/apt/sources.list.d/oneAPI.list
|
||||
|
||||
apt -y update
|
||||
apt -y install intel-oneapi-mkl-sycl-blas intel-oneapi-runtime-dnnl intel-oneapi-runtime-compilers
|
||||
else
|
||||
echo "NVIDIA graphics will not be installed on this architecture."
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,3 +1,5 @@
|
||||
apt -y install lsb-release
|
||||
|
||||
UBUNTU_22_04=$(lsb_release -r | grep "22.04")
|
||||
UBUNTU_24_04=$(lsb_release -r | grep "24.04")
|
||||
|
||||
@@ -9,6 +11,16 @@ set -e
|
||||
# https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=24.04&target_type=deb_network
|
||||
# Do not apt install nvidia-open, must use cuda-drivers.
|
||||
|
||||
if [ -z "$UBUNTU_22_04" ] && [ -z "$UBUNTU_24_04" ]
|
||||
then
|
||||
# proxmox is compatible with ubuntu 22.04, check for /etc/pve directory
|
||||
if [ -d "/etc/pve" ]
|
||||
then
|
||||
apt -y install pve-headers-$(uname -r)
|
||||
UBUNTU_22_04=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$UBUNTU_22_04" ] && [ -z "$UBUNTU_24_04" ]
|
||||
then
|
||||
echo "NVIDIA container toolkit can not be installed. Ubuntu version could not be detected when checking lsb-release and /etc/os-release."
|
||||
@@ -36,9 +48,16 @@ curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --yes --dea
|
||||
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
||||
apt -y update
|
||||
# is there a way to get a versioned package automatically?
|
||||
apt -y install nvidia-utils-560
|
||||
apt -y install nvidia-container-toolkit
|
||||
# cuda-drivers does not work with blackwell for some reason, container toolkit it broken IIRC.
|
||||
apt -y install nvidia-open
|
||||
|
||||
nvidia-ctk runtime configure --runtime=docker
|
||||
nvidia-ctk config --set nvidia-container-cli.no-cgroups --in-place
|
||||
systemctl restart docker
|
||||
if [ ! -d "/etc/pve" ]
|
||||
then
|
||||
apt -y install nvidia-container-toolkit
|
||||
|
||||
nvidia-ctk runtime configure --runtime=docker
|
||||
systemctl restart docker
|
||||
fi
|
||||
|
||||
# need this if running inside lxc...
|
||||
# nvidia-ctk config --set nvidia-container-cli.no-cgroups --in-place
|
||||
|
||||
54
install/docker/install-nvidia-graphics-legacy.sh
Normal file
54
install/docker/install-nvidia-graphics-legacy.sh
Normal file
@@ -0,0 +1,54 @@
|
||||
if [ "$(uname -m)" = "x86_64" ]
|
||||
then
|
||||
UBUNTU_22_04=$(lsb_release -r | grep "22.04")
|
||||
UBUNTU_24_04=$(lsb_release -r | grep "24.04")
|
||||
|
||||
# needs either ubuntu 22.0.4 or 24.04
|
||||
if [ -z "$UBUNTU_22_04" ] && [ -z "$UBUNTU_24_04" ]
|
||||
then
|
||||
echo "NVIDIA graphics package can not be installed. Ubuntu version could not be detected when checking lsb-release and /etc/os-release."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$UBUNTU_22_04" ]
|
||||
then
|
||||
distro="ubuntu2204"
|
||||
else
|
||||
distro="ubuntu2404"
|
||||
fi
|
||||
|
||||
echo "Installing NVIDIA graphics packages."
|
||||
apt update -q \
|
||||
&& apt install -y wget \
|
||||
&& wget -qO /cuda-keyring.deb https://developer.download.nvidia.com/compute/cuda/repos/$distro/$(uname -m)/cuda-keyring_1.1-1_all.deb \
|
||||
&& dpkg -i /cuda-keyring.deb \
|
||||
&& apt update -q \
|
||||
&& apt install -y cuda-nvcc-12-6 libcublas-12-6 libcudnn9-cuda-12=9.10.2.21-1 cuda-libraries-12-6;
|
||||
|
||||
if [ "$?" != "0" ]
|
||||
then
|
||||
echo "Error: NVIDIA graphics packages failed to install."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Update: the libnvidia-opencl.so.1 file is not present in the container image, it is
|
||||
# mounted via the nvidia container runtime. This is why the following check is commented out.
|
||||
# this file is present but for some reason the icd file is not created by nvidia runtime.
|
||||
# if [ ! -f "/usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.1" ]
|
||||
# then
|
||||
# echo "Error: NVIDIA OpenCL library not found."
|
||||
# exit 1
|
||||
# fi
|
||||
|
||||
# the container runtime doesn't mount this file for some reason. seems to be a bug.
|
||||
# https://github.com/NVIDIA/nvidia-container-toolkit/issues/682
|
||||
# but the contents are simply the .so file, which is a symlink the nvidia runtime
|
||||
# will mount in.
|
||||
mkdir -p /etc/OpenCL/vendors/
|
||||
echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd
|
||||
else
|
||||
echo "NVIDIA graphics will not be installed on this architecture."
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -23,7 +23,7 @@ then
|
||||
&& wget -qO /cuda-keyring.deb https://developer.download.nvidia.com/compute/cuda/repos/$distro/$(uname -m)/cuda-keyring_1.1-1_all.deb \
|
||||
&& dpkg -i /cuda-keyring.deb \
|
||||
&& apt update -q \
|
||||
&& apt install -y cuda-nvcc-12-6 libcublas-12-6 libcudnn9-cuda-12 cuda-libraries-12-6;
|
||||
&& apt install -y cuda-nvcc-12-9 libcublas-12-9 libcudnn9-cuda-12 cuda-libraries-12-9;
|
||||
|
||||
if [ "$?" != "0" ]
|
||||
then
|
||||
|
||||
@@ -13,6 +13,8 @@ then
|
||||
fi
|
||||
|
||||
function readyn() {
|
||||
echo
|
||||
echo
|
||||
if [ ! -z "$SCRYPTED_NONINTERACTIVE" ]
|
||||
then
|
||||
yn="y"
|
||||
@@ -51,6 +53,9 @@ rm -rf $SCRYPTED_HOME/install.json
|
||||
rm -rf $SCRYPTED_HOME/package.json
|
||||
rm -rf $SCRYPTED_HOME/package-lock.json
|
||||
|
||||
# must get this value as grep returns non zero if empty
|
||||
HAS_NVIDIA=$(lspci | grep -i nvidia)
|
||||
|
||||
set -e
|
||||
cd $SCRYPTED_HOME
|
||||
|
||||
@@ -93,6 +98,24 @@ else
|
||||
sudo apt -y purge apparmor || true
|
||||
fi
|
||||
|
||||
if [ ! -z "$HAS_NVIDIA" ]
|
||||
then
|
||||
readyn "NVIDIA GPU detected. Use NVIDIA image for GPU acceleration?"
|
||||
if [ "$yn" == "y" ]
|
||||
then
|
||||
readyn "NVIDIA image requires the NVIDIA Drivers and Container Toolkit to be installed. This script can install them for you. Install NVIDIA Drivers and Container Toolkit for GPU acceleration?"
|
||||
if [ "$yn" == "y" ]
|
||||
then
|
||||
curl -fsSL https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-nvidia-container-toolkit.sh -o install-nvidia-container-toolkit.sh
|
||||
chmod +x install-nvidia-container-toolkit.sh
|
||||
./install-nvidia-container-toolkit.sh
|
||||
rm install-nvidia-container-toolkit.sh
|
||||
fi
|
||||
sed -i 's/'#' nvidia //g' $DOCKER_COMPOSE_YML
|
||||
sed -i 's/ghcr.io\/koush\/scrypted/ghcr.io\/koush\/scrypted:nvidia/g' $DOCKER_COMPOSE_YML
|
||||
fi
|
||||
fi
|
||||
|
||||
readyn "Install avahi-daemon? This is the recommended for reliable HomeKit discovery and pairing."
|
||||
if [ "$yn" == "y" ]
|
||||
then
|
||||
@@ -163,4 +186,4 @@ echo
|
||||
echo
|
||||
echo "Optional:"
|
||||
echo "Scrypted NVR Recording storage directory can be configured with an additional script located at:"
|
||||
echo "https://docs.scrypted.app/scrypted-nvr/recording-storage.html#docker-volume"
|
||||
echo "https://docs.scrypted.app/scrypted-nvr/storage/docker.html"
|
||||
|
||||
55
install/docker/router/01-scrypted.conf
Normal file
55
install/docker/router/01-scrypted.conf
Normal file
@@ -0,0 +1,55 @@
|
||||
table ip nat {
|
||||
chain POSTROUTING {
|
||||
type nat hook postrouting priority srcnat; policy accept;
|
||||
jump postrouting_scrypted
|
||||
}
|
||||
|
||||
chain postrouting_scrypted {
|
||||
}
|
||||
|
||||
chain PREROUTING {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
jump prerouting_scrypted;
|
||||
}
|
||||
|
||||
chain prerouting_scrypted {
|
||||
}
|
||||
}
|
||||
|
||||
table ip filter {
|
||||
chain FORWARD {
|
||||
type filter hook forward priority filter; policy drop;
|
||||
jump forward_scrypted
|
||||
}
|
||||
|
||||
chain forward_scrypted {
|
||||
}
|
||||
}
|
||||
|
||||
table ip6 nat {
|
||||
chain POSTROUTING {
|
||||
type nat hook postrouting priority srcnat; policy accept;
|
||||
jump postrouting_scrypted
|
||||
}
|
||||
|
||||
chain postrouting_scrypted {
|
||||
}
|
||||
|
||||
chain PREROUTING {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
jump prerouting_scrypted;
|
||||
}
|
||||
|
||||
chain prerouting_scrypted {
|
||||
}
|
||||
}
|
||||
|
||||
table ip6 filter {
|
||||
chain FORWARD {
|
||||
type filter hook forward priority filter; policy drop;
|
||||
jump forward_scrypted
|
||||
}
|
||||
|
||||
chain forward_scrypted {
|
||||
}
|
||||
}
|
||||
11
install/docker/router/scrypted-dhcp-watcher.service
Normal file
11
install/docker/router/scrypted-dhcp-watcher.service
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Scrypted DHCP Watcher
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/etc/dhcp/scrypted-dhcp-watcher
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
17
install/docker/router/scrypted-ip-forwarding.service
Normal file
17
install/docker/router/scrypted-ip-forwarding.service
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=ipv6 forwarding
|
||||
After=network.target
|
||||
Conflicts=shutdown.target
|
||||
DefaultDependencies=no
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
StandardInput=null
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
ExecStart=bash -c "sysctl -w net.ipv4.ip_forward=1 && sysctl -w net.ipv6.conf.all.forwarding=1 && sysctl -w net.ipv6.conf.default.forwarding=1"
|
||||
ExecReload=bash -c "sysctl -w net.ipv4.ip_forward=1 && sysctl -w net.ipv6.conf.all.forwarding=1 && sysctl -w net.ipv6.conf.default.forwarding=1"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
18
install/docker/router/scrypted-nftables.service
Normal file
18
install/docker/router/scrypted-nftables.service
Normal file
@@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=nftables
|
||||
Documentation=man:nft(8) http://wiki.nftables.org
|
||||
After=network.target
|
||||
Conflicts=shutdown.target
|
||||
DefaultDependencies=no
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
StandardInput=null
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.conf
|
||||
ExecReload=/usr/sbin/nft -f /etc/nftables.conf
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
24
install/docker/router/scrypted.service
Normal file
24
install/docker/router/scrypted.service
Normal file
@@ -0,0 +1,24 @@
|
||||
[Unit]
|
||||
Description=Scrypted
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/server
|
||||
ExecStart=/bin/sh -c "ulimit -c 0; exec npm exec scrypted-serve"
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
Environment="SCRYPTED_INSTALL_ENVIRONMENT=docker"
|
||||
Environment="SCRYPTED_CAN_RESTART=true"
|
||||
Environment="SCRYPTED_VOLUME=/server/volume"
|
||||
Environment="SCRYPTED_INSTALL_PATH=/server"
|
||||
Environment="SCRYPTED_PYTHON_PATH=/usr/bin/python3"
|
||||
Environment="SCRYPTED_PYTHON312_PATH=/usr/bin/python3.12"
|
||||
Environment="SCRYPTED_DOCKER_FLAVOR=lite"
|
||||
Environment="DEBIAN_FRONTEND=noninteractive"
|
||||
Environment="NODE_OPTIONS=--dns-result-order=ipv4first"
|
||||
Environment="SCRYPTED_BASE_VERSION=20250101"
|
||||
Environment="SHELL=/bin/bash"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,7 +1,7 @@
|
||||
################################################################
|
||||
# Begin section generated from template/Dockerfile.full.footer
|
||||
################################################################
|
||||
FROM header as base
|
||||
FROM header AS base
|
||||
|
||||
# vulkan
|
||||
RUN apt -y install libvulkan1
|
||||
@@ -9,12 +9,9 @@ RUN apt -y install libvulkan1
|
||||
# intel opencl for openvino
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-intel-graphics.sh | bash
|
||||
|
||||
# NPU driver will SIGILL on openvino prior to 2024.5.0
|
||||
# intel NPU
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-intel-npu.sh | bash
|
||||
|
||||
# amd opencl
|
||||
RUN curl https://raw.githubusercontent.com/koush/scrypted/main/install/docker/install-amd-graphics.sh | bash
|
||||
|
||||
# python 3.9 from ppa.
|
||||
# 3.9 is the version with prebuilt support for tensorflow lite
|
||||
RUN add-apt-repository -y ppa:deadsnakes/ppa && \
|
||||
@@ -32,7 +29,7 @@ RUN python3.9 -m pip install debugpy
|
||||
# Coral Edge TPU
|
||||
# https://coral.ai/docs/accelerator/get-started/#runtime-on-linux
|
||||
RUN echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | tee /etc/apt/sources.list.d/coral-edgetpu.list
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
RUN curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /etc/apt/trusted.gpg.d/coral-edgetpu.gpg
|
||||
RUN apt-get -y update && apt-get -y install libedgetpu1-std
|
||||
|
||||
# set default shell to bash
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# This common file will be used by both Docker and the linux
|
||||
# install script.
|
||||
################################################################
|
||||
ARG BASE="jammy"
|
||||
FROM ubuntu:${BASE} as header
|
||||
ARG BASE="noble"
|
||||
FROM ubuntu:${BASE} AS header
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN apt-get update && apt-get -y install \
|
||||
apt-get -y update && \
|
||||
apt-get -y upgrade
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
ARG NODE_VERSION=22
|
||||
RUN apt-get install -y ca-certificates curl gnupg
|
||||
RUN mkdir -p /etc/apt/keyrings
|
||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg
|
||||
@@ -32,19 +32,6 @@ RUN apt-get -y install \
|
||||
python3-setuptools \
|
||||
python3-wheel
|
||||
|
||||
# gstreamer native https://gstreamer.freedesktop.org/documentation/installing/on-linux.html?gi-language=c#install-gstreamer-on-ubuntu-or-debian
|
||||
RUN echo "Installing gstreamer."
|
||||
# python-codecs pygobject dependencies
|
||||
RUN apt-get -y install libcairo2-dev libgirepository1.0-dev
|
||||
RUN apt-get -y install \
|
||||
gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \
|
||||
gstreamer1.0-vaapi
|
||||
|
||||
# python3 gstreamer bindings
|
||||
RUN echo "Installing gstreamer bindings."
|
||||
RUN apt-get -y install \
|
||||
python3-gst-1.0
|
||||
|
||||
# allow pip to install to system
|
||||
RUN rm -f /usr/lib/python**/EXTERNALLY-MANAGED
|
||||
|
||||
|
||||
@@ -39,15 +39,13 @@ launchctl unload ~/Library/LaunchAgents/app.scrypted.server.plist || echo ""
|
||||
echo "Installing Scrypted dependencies..."
|
||||
RUN_IGNORE xcode-select --install
|
||||
RUN brew update
|
||||
RUN_IGNORE brew install node@20
|
||||
# dlib
|
||||
RUN brew install cmake
|
||||
|
||||
# seems to be necessary for python-codecs' pycairo dependency or something?
|
||||
RUN_IGNORE gobject-introspection libffi pkg-config
|
||||
|
||||
# gstreamer plugins
|
||||
RUN_IGNORE brew install gstreamer
|
||||
# in sequoia, brew node is unusable because it is not signed and can't access local network unless run as root.
|
||||
# https://developer.apple.com/forums/thread/766270
|
||||
RUN_IGNORE curl -L https://nodejs.org/dist/v22.14.0/node-v22.14.0.pkg -o /tmp/node.pkg
|
||||
RUN_IGNORE sudo installer -pkg /tmp/node.pkg -target /
|
||||
NODE_PATH=/usr/local # used to pass var test
|
||||
NODE_BIN_PATH=/usr/local/bin
|
||||
|
||||
ARCH=$(arch)
|
||||
if [ "$ARCH" = "arm64" ]
|
||||
@@ -82,17 +80,15 @@ echo "Installing Scrypted Launch Agent..."
|
||||
|
||||
RUN mkdir -p ~/Library/LaunchAgents
|
||||
|
||||
NODE_PATH=$(brew --prefix node@20)
|
||||
if [ ! -d "$NODE_PATH" ]
|
||||
then
|
||||
echo "Unable to determine node@20 path."
|
||||
echo "Unable to determine node path."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NODE_BIN_PATH=$NODE_PATH/bin
|
||||
if [ ! -d "$NODE_BIN_PATH" ]
|
||||
then
|
||||
echo "Unable to determine node@20 bin path."
|
||||
echo "Unable to determine node bin path."
|
||||
echo "$NODE_BIN_PATH does not exist."
|
||||
exit 1
|
||||
fi
|
||||
@@ -155,7 +151,7 @@ cat > ~/Library/LaunchAgents/app.scrypted.server.plist <<EOT
|
||||
<key>NODE_OPTIONS</key>
|
||||
<string>$NODE_OPTIONS</string>
|
||||
<key>PATH</key>
|
||||
<string>$NODE_BIN_PATH:$PYTHON_BIN_PATH:$BREW_PREFIX/bin:$BREW_PREFIX/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
<string>$PYTHON_BIN_PATH:$NODE_BIN_PATH:$BREW_PREFIX/bin:$BREW_PREFIX/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
<key>HOME</key>
|
||||
<string>/Users/$USER</string>
|
||||
<key>SCRYPTED_PYTHON_PATH</key>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
#Requires -RunAsAdministrator
|
||||
# Check if the script is running as administrator
|
||||
$IsAdmin = [System.Security.Principal.WindowsPrincipal] [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$AdminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
|
||||
|
||||
if (-not $IsAdmin.IsInRole($AdminRole)) {
|
||||
# If not, relaunch the script with elevated privileges
|
||||
$ScriptPath = $PSCommandPath
|
||||
Start-Process powershell -ArgumentList "-File `"$ScriptPath`"" -Verb RunAs
|
||||
exit
|
||||
}
|
||||
|
||||
# Set-PSDebug -Trace 1
|
||||
|
||||
@@ -10,7 +19,7 @@ sc.exe stop scrypted.exe
|
||||
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
||||
|
||||
# Install node.js
|
||||
choco upgrade -y nodejs-lts --version=20.18.0
|
||||
choco upgrade -y nodejs-lts --version=22.21.0
|
||||
|
||||
# Install VC Redist, which is necessary for portable python
|
||||
choco install -y vcredist140
|
||||
@@ -25,7 +34,7 @@ $SCRYPTED_WINDOWS_PYTHON_VERSION="-3.9"
|
||||
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
|
||||
|
||||
# Workaround Windows Node no longer creating %APPDATA%\npm which causes npx to fail
|
||||
# Fixed in newer versions of NPM but not the one bundled with Node 20
|
||||
# Fixed in newer versions of NPM but not the one bundled with Node 2x
|
||||
# https://github.com/nodejs/node/issues/53538
|
||||
npm i -g npm
|
||||
|
||||
|
||||
@@ -4,15 +4,37 @@ cd /root/.scrypted
|
||||
# always immediately upgrade everything in case there's a broken update.
|
||||
# this will also be preferable for troubleshooting via lxc reboot.
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
yes | dpkg --configure -a
|
||||
apt -y --fix-broken install && apt -y update && apt -y dist-upgrade
|
||||
|
||||
# auto updates may break the system?
|
||||
# watchtower stopped working after a docker update, so disabling for now.
|
||||
# yes | dpkg --configure -a
|
||||
# apt -y --fix-broken install && apt -y update && apt -y dist-upgrade
|
||||
|
||||
function cleanup() {
|
||||
IS_UP=$(docker compose ps scrypted -a | grep Up)
|
||||
# Only clean up when scrypted is running to safely free space without risking its image deletion
|
||||
if [ -z "$IS_UP" ]; then
|
||||
echo "scrypted is not running, skipping cleanup to preserve its image"
|
||||
return
|
||||
fi
|
||||
echo $(date) > .last_cleanup
|
||||
echo "scrypted is running, proceeding with cleanup to free space"
|
||||
docker container prune -f
|
||||
docker image prune -a -f
|
||||
}
|
||||
|
||||
# force a pull to ensure we have the latest images.
|
||||
# not using --pull always cause that fails everything on network down
|
||||
docker compose pull
|
||||
|
||||
(sleep 60 && cleanup) &
|
||||
|
||||
# do not daemonize, when it exits, systemd will restart it.
|
||||
# force a recreate as .env may have changed.
|
||||
# furthermore force recreate gets the container back into a known state
|
||||
# which is preferable in case the user has made manual changes and then restarts.
|
||||
WATCHTOWER_HTTP_API_TOKEN=$(echo $RANDOM | md5sum | head -c 32) docker compose up --force-recreate --abort-on-container-exit
|
||||
WATCHTOWER_HTTP_API_TOKEN=$(echo $RANDOM | md5sum | head -c 32) docker compose up --force-recreate
|
||||
|
||||
# abort on container exit is problematic if watchtower is the one that aborts.
|
||||
# this is also redundant now that watchtower is disabled.
|
||||
# WATCHTOWER_HTTP_API_TOKEN=$(echo $RANDOM | md5sum | head -c 32) docker compose up --force-recreate --abort-on-container-exit
|
||||
|
||||
@@ -18,7 +18,10 @@ function readyn() {
|
||||
}
|
||||
|
||||
cd /tmp
|
||||
SCRYPTED_VERSION=v0.120.0
|
||||
if [ -z "$SCRYPTED_VERSION" ]
|
||||
then
|
||||
SCRYPTED_VERSION=v0.143.0
|
||||
fi
|
||||
SCRYPTED_TAR_ZST=scrypted-$SCRYPTED_VERSION.tar.zst
|
||||
if [ -z "$VMID" ]
|
||||
then
|
||||
@@ -139,7 +142,12 @@ then
|
||||
echo "#############################################################################"
|
||||
echo -e "\033[32mPaste the following command into this shell to install to local-lvm instead:\033[0m"
|
||||
echo ""
|
||||
echo "bash $0 --storage local-lvm"
|
||||
if [ -n "$SCRYPTED_RESTORE" ]
|
||||
then
|
||||
echo "bash $0 --storage local-lvm"
|
||||
else
|
||||
echo "SCRYPTED_RESTORE=true bash $0 --storage local-lvm"
|
||||
fi
|
||||
echo "#############################################################################"
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
@@ -27,7 +27,7 @@ echo "external/werift > npm install"
|
||||
npm install
|
||||
popd
|
||||
|
||||
for directory in rtsp amcrest onvif hikvision reolink unifi-protect webrtc homekit
|
||||
for directory in rtsp ffmpeg-camera amcrest onvif hikvision reolink unifi-protect webrtc homekit
|
||||
do
|
||||
echo "$directory > npm install"
|
||||
pushd plugins/$directory
|
||||
|
||||
@@ -27,7 +27,8 @@ async function getAuth(options: AuthFetchOptions, url: string | URL, method: str
|
||||
++credential.count;
|
||||
const nc = ('00000000' + credential.count).slice(-8);
|
||||
const cnonce = [...Array(24)].map(() => Math.floor(Math.random() * 16).toString(16)).join('');
|
||||
const uri = new URL(url).pathname;
|
||||
const parsedURL = new URL(url);
|
||||
const uri = parsedURL.pathname + parsedURL.search;
|
||||
|
||||
const { DIGEST, buildAuthorizationHeader } = await import('http-auth-utils');
|
||||
|
||||
|
||||
312
packages/cli/package-lock.json
generated
312
packages/cli/package-lock.json
generated
@@ -9,23 +9,23 @@
|
||||
"version": "1.3.20",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/client": "^1.3.3",
|
||||
"@scrypted/types": "^0.3.30",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"@scrypted/client": "^1.3.13",
|
||||
"@scrypted/types": "^0.5.9",
|
||||
"engine.io-client": "^6.6.3",
|
||||
"readline-sync": "^1.4.10",
|
||||
"semver": "^7.5.4",
|
||||
"tslib": "^2.6.2"
|
||||
"semver": "^7.7.1",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"bin": {
|
||||
"scrypted": "dist/packages/cli/src/main.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.9.4",
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/readline-sync": "^1.4.8",
|
||||
"@types/semver": "^7.5.6",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.3.2"
|
||||
"@types/semver": "^7.5.8",
|
||||
"rimraf": "^6.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
@@ -44,6 +44,7 @@
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
@@ -81,30 +82,24 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/client": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/client/-/client-1.3.3.tgz",
|
||||
"integrity": "sha512-Wuy7x02TCRy1buaDNX8NOIaL1j4ZXu4dqTTJsKHlPe3+umsBvpwbylD+YyyU8ghQJC6a40Bs5UMsvnCvNa/1fg==",
|
||||
"version": "1.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/client/-/client-1.3.13.tgz",
|
||||
"integrity": "sha512-jxXnGCoHIwuB7PobPJyqYy9THljR2UJOILxO++HiNq/i0nqRUECYvVfU5frN/ZnP6nmQoiRKrl8ErGWVBT7ecg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.3.4",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"follow-redirects": "^1.15.4",
|
||||
"rimraf": "^5.0.5"
|
||||
"engine.io-client": "^6.6.3",
|
||||
"follow-redirects": "^1.15.9",
|
||||
"rimraf": "^6.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@scrypted/types": "^0.5.9"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/types": {
|
||||
"version": "0.3.30",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.30.tgz",
|
||||
"integrity": "sha512-1k+JVSR6WSNmE/5mLdqfrTmV3uRbvZp0OwKb8ikNi39ysBuC000tQGcEdXZqhYqRgWdhDTWtxXe9XsYoAZGKmA==",
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.5.9.tgz",
|
||||
"integrity": "sha512-Qt/gLdzDqYwgOArpLrEErPb91mhAOmN0NFFOFwX9G/5vSV5Xvm6ixQhPWF4f+up3G9ecPVPMPtZCsWmhxAD1hA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
@@ -137,12 +132,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz",
|
||||
"integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==",
|
||||
"version": "22.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/readline-sync": {
|
||||
@@ -152,10 +148,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/semver": {
|
||||
"version": "7.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
|
||||
"integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
|
||||
"dev": true
|
||||
"version": "7.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
|
||||
"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.8.2",
|
||||
@@ -179,9 +176,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -193,6 +191,7 @@
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -209,12 +208,14 @@
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
@@ -223,6 +224,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
@@ -233,7 +235,8 @@
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
@@ -242,9 +245,10 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
@@ -282,23 +286,26 @@
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.5.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
|
||||
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
|
||||
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.11.0",
|
||||
"xmlhttprequest-ssl": "~2.0.0"
|
||||
"ws": "~8.17.1",
|
||||
"xmlhttprequest-ssl": "~2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
@@ -310,15 +317,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
@@ -329,11 +337,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.0",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -344,21 +353,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.3.10",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
|
||||
"integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz",
|
||||
"integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^2.3.5",
|
||||
"minimatch": "^9.0.1",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
|
||||
"path-scurry": "^1.10.1"
|
||||
"jackspeak": "^4.0.1",
|
||||
"minimatch": "^10.0.0",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
@@ -368,6 +379,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -375,34 +387,31 @@
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
|
||||
"integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz",
|
||||
"integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"version": "11.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz",
|
||||
"integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
@@ -412,23 +421,25 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
|
||||
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
|
||||
"integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
@@ -438,37 +449,37 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/package-json-from-dist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
|
||||
"integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
|
||||
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^9.1.1 || ^10.0.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.3.tgz",
|
||||
"integrity": "sha512-B7gr+F6MkqB3uzINHXNctGieGsRTMwIBgxkp0yq/5BwcuDzD4A8wQpHQW6vDAm1uKSLQghmRdD9sKqf2vJ1cEg==",
|
||||
"engines": {
|
||||
"node": "14 || >=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/readline-sync": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz",
|
||||
@@ -478,29 +489,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
|
||||
"integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz",
|
||||
"integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"glob": "^10.3.7"
|
||||
"glob": "^11.0.0",
|
||||
"package-json-from-dist": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
@@ -512,6 +523,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
@@ -523,6 +535,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -531,6 +544,7 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
@@ -542,6 +556,7 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
@@ -559,6 +574,7 @@
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
@@ -572,6 +588,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -579,12 +596,14 @@
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
@@ -596,6 +615,7 @@
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
@@ -611,6 +631,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
@@ -622,15 +643,17 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"version": "10.9.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
@@ -670,15 +693,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz",
|
||||
"integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==",
|
||||
"version": "5.8.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
|
||||
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -688,10 +713,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
@@ -703,6 +729,7 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
@@ -717,6 +744,7 @@
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
@@ -734,6 +762,7 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
@@ -750,6 +779,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -758,6 +788,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
@@ -771,12 +802,14 @@
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
@@ -790,6 +823,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
@@ -798,15 +832,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
|
||||
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
@@ -818,18 +853,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
|
||||
"integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
|
||||
@@ -16,19 +16,19 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/client": "^1.3.3",
|
||||
"@scrypted/types": "^0.3.30",
|
||||
"engine.io-client": "^6.5.3",
|
||||
"@scrypted/client": "^1.3.13",
|
||||
"@scrypted/types": "^0.5.9",
|
||||
"engine.io-client": "^6.6.3",
|
||||
"readline-sync": "^1.4.10",
|
||||
"semver": "^7.5.4",
|
||||
"tslib": "^2.6.2"
|
||||
"semver": "^7.7.1",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.9.4",
|
||||
"@types/node": "^22.13.10",
|
||||
"@types/readline-sync": "^1.4.8",
|
||||
"@types/semver": "^7.5.6",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.3.2"
|
||||
"@types/semver": "^7.5.8",
|
||||
"rimraf": "^6.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.8.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,6 @@ async function main() {
|
||||
}
|
||||
}
|
||||
const args = ffmpegInput.inputArguments ? [...ffmpegInput.inputArguments] : [];
|
||||
if (ffmpegInput.h264FilterArguments)
|
||||
args.push(...ffmpegInput.h264FilterArguments);
|
||||
console.log('ffplay', ...args);
|
||||
child_process.spawn('ffplay', args, {
|
||||
stdio: 'inherit',
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function connectShell(sdk: ScryptedStatic, ...cmd: string[]) {
|
||||
throw Error("@scrypted/core does not provide a Terminal Service");
|
||||
}
|
||||
|
||||
const termSvcDirect = await sdk.connectRPCObject<StreamService>(termSvc);
|
||||
const termSvcDirect = await sdk.connectRPCObject<StreamService<Buffer|string, Buffer>>(termSvc);
|
||||
const dataQueue = createAsyncQueue<Buffer>();
|
||||
const ctrlQueue = createAsyncQueue<any>();
|
||||
|
||||
|
||||
385
packages/client/package-lock.json
generated
385
packages/client/package-lock.json
generated
@@ -1,25 +1,27 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.26",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.26",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.3.92",
|
||||
"engine.io-client": "^6.6.1",
|
||||
"engine.io-client": "^6.6.3",
|
||||
"follow-redirects": "^1.15.9",
|
||||
"rimraf": "^6.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/ws": "^8.5.13",
|
||||
"@types/node": "^24.0.10",
|
||||
"@types/ws": "^8.18.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.2"
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@scrypted/types": "^0.5.52"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
@@ -27,6 +29,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
@@ -34,10 +37,32 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/brace-expansion": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/balanced-match": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
@@ -51,87 +76,101 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
|
||||
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
|
||||
"dev": true
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/types": {
|
||||
"version": "0.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.92.tgz",
|
||||
"integrity": "sha512-/M1Lg42/yoFWusj5+Lyp2S0JCiWDDWcmsjiUnTf1DahZ6/M2oZ3bwR/0KX3D9vJE79owWST1Gm0+Rdvpxuil9A==",
|
||||
"license": "ISC"
|
||||
"version": "0.5.52",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.5.52.tgz",
|
||||
"integrity": "sha512-c1ra1ENnoC8MqVHf7QQcXIU+5BvQnhU4x5oqx4b20LtoB0/TTXthYFFvEDBvLenBivUr8Bb6dWrji7TZXVax1g==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"openai": "^6.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
|
||||
"integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"dev": true
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ip": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ip/-/ip-1.1.3.tgz",
|
||||
"integrity": "sha512-64waoJgkXFTYnCYDUWgSATJ/dXEBanVkaP5d4Sbk7P6U7cTTMhxVyROTckc6JKdwCrgnAjZMn0k3177aQxtDEA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
|
||||
"integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
|
||||
"version": "24.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.1.tgz",
|
||||
"integrity": "sha512-/SQdmUP2xa+1rdx7VwB9yPq8PaKej8TD5cQ+XfKDPWWC+VDJU4rvVVagXqKUzhKjtFoNA8rXDJAkCxQPAe00+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
"undici-types": "~7.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
|
||||
"integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==",
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -139,10 +178,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -151,18 +191,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz",
|
||||
"integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==",
|
||||
"version": "8.3.4",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
|
||||
"integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -171,9 +216,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -185,25 +231,14 @@
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
@@ -214,13 +249,15 @@
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
@@ -237,11 +274,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
@@ -257,6 +295,7 @@
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
@@ -264,17 +303,20 @@
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.6.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz",
|
||||
"integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==",
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
|
||||
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
@@ -283,24 +325,47 @@
|
||||
"xmlhttprequest-ssl": "~2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client/node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz",
|
||||
"integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==",
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
@@ -311,11 +376,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
|
||||
"integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.0",
|
||||
"cross-spawn": "^7.0.6",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -326,13 +392,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz",
|
||||
"integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==",
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
|
||||
"integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^4.0.1",
|
||||
"minimatch": "^10.0.0",
|
||||
"foreground-child": "^3.3.1",
|
||||
"jackspeak": "^4.1.1",
|
||||
"minimatch": "^10.0.3",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
@@ -351,6 +418,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -358,12 +426,14 @@
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz",
|
||||
"integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
|
||||
"integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
@@ -375,9 +445,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz",
|
||||
"integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==",
|
||||
"version": "11.2.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz",
|
||||
"integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
@@ -386,14 +457,16 @@
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
|
||||
"integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
|
||||
"integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
@@ -406,24 +479,50 @@
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/openai": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/openai/-/openai-6.1.0.tgz",
|
||||
"integrity": "sha512-5sqb1wK67HoVgGlsPwcH2bUbkg66nnoIYKoyV9zi5pZPqh7EWlmSrSDjAh4O5jaIg/0rIlcDKBtWvZBuacmGZg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"openai": "bin/cli"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"ws": "^8.18.0",
|
||||
"zod": "^3.25 || ^4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"ws": {
|
||||
"optional": true
|
||||
},
|
||||
"zod": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/package-json-from-dist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -432,6 +531,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
|
||||
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
@@ -447,6 +547,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz",
|
||||
"integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"glob": "^11.0.0",
|
||||
"package-json-from-dist": "^1.0.0"
|
||||
@@ -465,6 +566,7 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
@@ -476,6 +578,7 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -484,6 +587,7 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
@@ -495,6 +599,7 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
@@ -512,6 +617,7 @@
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
@@ -525,6 +631,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -532,12 +639,14 @@
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
@@ -546,9 +655,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
|
||||
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
@@ -564,6 +674,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
@@ -575,6 +686,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -584,6 +696,7 @@
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
|
||||
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
@@ -623,10 +736,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
||||
"version": "5.9.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
|
||||
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -636,21 +750,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"dev": true
|
||||
"version": "7.12.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz",
|
||||
"integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
@@ -665,6 +782,7 @@
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
@@ -682,6 +800,7 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
@@ -698,6 +817,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -706,6 +826,7 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
@@ -719,12 +840,14 @@
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
@@ -738,6 +861,7 @@
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
@@ -745,30 +869,10 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
|
||||
"integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
@@ -778,6 +882,7 @@
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.26",
|
||||
"description": "",
|
||||
"main": "dist/packages/client/src/index.js",
|
||||
"scripts": {
|
||||
@@ -13,14 +13,16 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/ws": "^8.5.13",
|
||||
"@types/node": "^24.0.10",
|
||||
"@types/ws": "^8.18.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.2"
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@scrypted/types": "^0.5.52"
|
||||
},
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.3.92",
|
||||
"engine.io-client": "^6.6.1",
|
||||
"engine.io-client": "^6.6.3",
|
||||
"follow-redirects": "^1.15.9",
|
||||
"rimraf": "^6.0.1"
|
||||
}
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { MediaObjectCreateOptions, RTCConnectionManagement, RTCSignalingSession, ScryptedStatic } from "@scrypted/types";
|
||||
import { ConnectRPCObjectOptions, ForkOptions, ForkWorker, MediaObjectCreateOptions, PluginFork, ScryptedInterface, ScryptedInterfaceProperty, ScryptedStatic } from "@scrypted/types";
|
||||
import * as eio from 'engine.io-client';
|
||||
import { SocketOptions } from 'engine.io-client';
|
||||
import { Deferred } from "../../../common/src/deferred";
|
||||
import { timeoutPromise } from "../../../common/src/promise-utils";
|
||||
import { BrowserSignalingSession, waitPeerConnectionIceConnected, waitPeerIceConnectionClosed } from "../../../common/src/rtc-signaling";
|
||||
import { DataChannelDebouncer } from "../../../plugins/webrtc/src/datachannel-debouncer";
|
||||
import type { ClusterObject, ConnectRPCObject } from '../../../server/src/cluster/connect-rpc-object';
|
||||
import { domFetch } from "../../../server/src/fetch";
|
||||
import { httpFetch } from '../../../server/src/fetch/http-fetch';
|
||||
import type { IOSocket } from '../../../server/src/io';
|
||||
import { MediaObject } from '../../../server/src/plugin/mediaobject';
|
||||
import { PluginAPIProxy, PluginRemote } from "../../../server/src/plugin/plugin-api";
|
||||
import { attachPluginRemote } from '../../../server/src/plugin/plugin-remote';
|
||||
import { RpcPeer } from '../../../server/src/rpc';
|
||||
import { createRpcDuplexSerializer, createRpcSerializer } from '../../../server/src/rpc-serializer';
|
||||
import packageJson from '../package.json';
|
||||
import { isIPAddress } from "./ip";
|
||||
|
||||
import { domFetch } from "../../../server/src/fetch";
|
||||
import { httpFetch } from '../../../server/src/fetch/http-fetch';
|
||||
export * as rpc from '../../../server/src/rpc';
|
||||
export * as rpc_serializer from '../../../server/src/rpc-serializer';
|
||||
|
||||
let fetcher: typeof httpFetch | typeof domFetch;
|
||||
try {
|
||||
@@ -33,6 +31,15 @@ const sourcePeerId = RpcPeer.generateId();
|
||||
|
||||
type IOClientSocket = eio.Socket & IOSocket;
|
||||
|
||||
interface InternalFork extends Pick<ScryptedClientStatic, 'loginResult' | 'username' | 'address' | 'connectionType'> {
|
||||
extraHeaders: {
|
||||
[header: string]: string,
|
||||
};
|
||||
transports?: string[] | undefined;
|
||||
clientName?: string;
|
||||
admin: boolean;
|
||||
};
|
||||
|
||||
function once(socket: IOClientSocket, event: 'open' | 'message') {
|
||||
return new Promise<any[]>((resolve, reject) => {
|
||||
const err = (e: any) => {
|
||||
@@ -52,7 +59,13 @@ function once(socket: IOClientSocket, event: 'open' | 'message') {
|
||||
});
|
||||
}
|
||||
|
||||
export type ScryptedClientConnectionType = 'http' | 'webrtc' | 'http-direct';
|
||||
/**
|
||||
* The type of connection used by the Scrypted client.
|
||||
* http-cloud is through Scrypted Cloud
|
||||
* http-direct is a direct connection to the Scrypted server via one of the local network interfaces or public IP addresses.
|
||||
* http is a direct connection with the base url or browser url.
|
||||
*/
|
||||
export type ScryptedClientConnectionType = 'http-cloud' | 'http-direct' | 'http';
|
||||
|
||||
export interface ScryptedClientStatic extends ScryptedStatic {
|
||||
userId?: string;
|
||||
@@ -60,18 +73,16 @@ export interface ScryptedClientStatic extends ScryptedStatic {
|
||||
admin: boolean;
|
||||
disconnect(): void;
|
||||
onClose?: Function;
|
||||
rtcConnectionManagement?: RTCConnectionManagement;
|
||||
browserSignalingSession?: BrowserSignalingSession;
|
||||
address?: string;
|
||||
connectionType: ScryptedClientConnectionType;
|
||||
rpcPeer: RpcPeer;
|
||||
loginResult: ScryptedClientLoginResult;
|
||||
fork<T>(options: ForkOptions & { worker: Worker }): PluginFork<T>;
|
||||
}
|
||||
|
||||
export interface ScryptedConnectionOptions {
|
||||
direct?: boolean;
|
||||
local?: boolean;
|
||||
webrtc?: boolean;
|
||||
baseUrl?: string;
|
||||
previousLoginResult?: ScryptedClientLoginResult;
|
||||
}
|
||||
@@ -112,19 +123,59 @@ export async function logoutScryptedClient(baseUrl?: string) {
|
||||
return response.body;
|
||||
}
|
||||
|
||||
export function getCurrentBaseUrl() {
|
||||
// an endpoint within scrypted will be served at /endpoint/[org/][id]
|
||||
// find the endpoint prefix and anything prior to that will be the server base url.
|
||||
const url = new URL(window.location.href);
|
||||
url.search = '';
|
||||
url.hash = '';
|
||||
let endpointPath = window.location.pathname;
|
||||
const parts = endpointPath.split('/');
|
||||
const index = parts.findIndex(p => p === 'endpoint');
|
||||
if (index === -1) {
|
||||
// console.warn('path not recognized, does not contain the segment "endpoint".')
|
||||
return undefined;
|
||||
function getBaseUrl(href: string) {
|
||||
try {
|
||||
// an endpoint within scrypted will be served at /endpoint/[org/][id]
|
||||
// find the endpoint prefix and anything prior to that will be the server base url.
|
||||
const url = new URL(href);
|
||||
url.search = '';
|
||||
url.hash = '';
|
||||
let endpointPath = url.pathname;
|
||||
const parts = endpointPath.split('/');
|
||||
const index = parts.findIndex(p => p === 'endpoint');
|
||||
if (index === -1) {
|
||||
// console.warn('path not recognized, does not contain the segment "endpoint".')
|
||||
return;
|
||||
}
|
||||
return { url, parts, index };
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
function importMetaUrlWithoutAssetsPath() {
|
||||
// @ts-ignore
|
||||
const url = new URL(import.meta.url);
|
||||
const parts = url.pathname.split('/');
|
||||
parts.pop();
|
||||
parts.pop();
|
||||
parts.push('public')
|
||||
parts.push('');
|
||||
url.pathname = parts.join('/');
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
export function getCurrentBaseUrlRaw() {
|
||||
const url = getBaseUrl(window.location.href)
|
||||
|| getBaseUrl(document.baseURI)
|
||||
|| getBaseUrl(importMetaUrlWithoutAssetsPath());
|
||||
|
||||
if (!url) {
|
||||
try {
|
||||
return getBaseUrl(process.env.SCRYPTED_ENDPOINT_PATH);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
export function getCurrentBaseUrl() {
|
||||
const s = getCurrentBaseUrlRaw();
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
const { url, parts, index } = s;
|
||||
const keep = parts.slice(0, index);
|
||||
keep.push('');
|
||||
url.pathname = keep.join('/');
|
||||
@@ -385,11 +436,10 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
const eioPath = `endpoint/${pluginId}/engine.io/api`;
|
||||
const eioEndpoint = baseUrl ? new URL(eioPath, baseUrl).pathname : '/' + eioPath;
|
||||
// https://github.com/socketio/engine.io/issues/690
|
||||
const cacehBust = Math.random().toString(36).substring(3, 10);
|
||||
const eioOptions: Partial<SocketOptions> = {
|
||||
const eioOptions: eio.SocketOptions = {
|
||||
path: eioEndpoint,
|
||||
query: {
|
||||
cacehBust,
|
||||
cacheBust: Math.random().toString(36).substring(3, 10),
|
||||
},
|
||||
withCredentials: true,
|
||||
extraHeaders,
|
||||
@@ -399,10 +449,6 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
|
||||
const explicitBaseUrl = baseUrl || `${globalThis.location.protocol}//${globalThis.location.host}`;
|
||||
|
||||
// underlying webrtc rpc transport may queue up messages before its ready to be to be handled.
|
||||
// watch for this flush.
|
||||
const flush = new Deferred<void>();
|
||||
|
||||
const addresses: string[] = [];
|
||||
const localAddressDefault = isNotChromeOrIsInstalledApp;
|
||||
|
||||
@@ -426,28 +472,12 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
}
|
||||
|
||||
const tryAddresses = !!addresses.length;
|
||||
const webrtcLastFailedKey = 'webrtcLastFailed';
|
||||
const canUseWebrtc = !!globalThis.RTCPeerConnection;
|
||||
let tryWebrtc = canUseWebrtc && options.webrtc;
|
||||
// try webrtc by default on scrypted cloud.
|
||||
// but webrtc takes a while to fail, so backoff if it fails to prevent continual slow starts.
|
||||
if (scryptedCloud && canUseWebrtc && globalThis.localStorage && options.webrtc === undefined) {
|
||||
tryWebrtc = true;
|
||||
const webrtcLastFailed = parseFloat(localStorage.getItem(webrtcLastFailedKey));
|
||||
// if webrtc has failed in the past day, dont attempt to use it.
|
||||
const now = Date.now();
|
||||
if (webrtcLastFailed < now && webrtcLastFailed > now - 1 * 24 * 60 * 60 * 1000) {
|
||||
tryWebrtc = false;
|
||||
console.warn('WebRTC API connection recently failed. Skipping.')
|
||||
}
|
||||
}
|
||||
|
||||
console.log({
|
||||
tryLocalAddressess: tryAddresses,
|
||||
tryWebrtc,
|
||||
});
|
||||
|
||||
const localEioOptions: Partial<SocketOptions> = {
|
||||
const localEioOptions: eio.SocketOptions = {
|
||||
...eioOptions,
|
||||
extraHeaders: {
|
||||
...eioOptions.extraHeaders,
|
||||
@@ -484,145 +514,11 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
}
|
||||
}
|
||||
|
||||
if (tryWebrtc) {
|
||||
console.log('trying webrtc');
|
||||
const webrtcEioOptions: Partial<SocketOptions> = {
|
||||
path: '/endpoint/@scrypted/webrtc/engine.io/',
|
||||
query: {
|
||||
cacehBust,
|
||||
},
|
||||
withCredentials: true,
|
||||
extraHeaders,
|
||||
rejectUnauthorized: false,
|
||||
transports: options?.transports,
|
||||
};
|
||||
const check = new eio.Socket(explicitBaseUrl, webrtcEioOptions) as IOClientSocket;
|
||||
sockets.push(check);
|
||||
promises.push((async () => {
|
||||
await once(check, 'open');
|
||||
|
||||
const connectionManagementId = `connectionManagement-${Math.random()}`;
|
||||
const updateSessionId = `updateSessionId-${Math.random()}`;
|
||||
check.send(JSON.stringify({
|
||||
pluginId,
|
||||
updateSessionId,
|
||||
connectionManagementId,
|
||||
}));
|
||||
const dcDeferred = new Deferred<RTCDataChannel>();
|
||||
const session = new BrowserSignalingSession();
|
||||
const droppedMessages: any[] = [];
|
||||
session.onPeerConnection = async pc => {
|
||||
pc.ondatachannel = e => {
|
||||
e.channel.onmessage = message => droppedMessages.push(message);
|
||||
e.channel.binaryType = 'arraybuffer';
|
||||
dcDeferred.resolve(e.channel)
|
||||
};
|
||||
}
|
||||
const pcPromise = session.pcDeferred.promise;
|
||||
|
||||
const serializer = createRpcSerializer({
|
||||
sendMessageBuffer: buffer => check.send(buffer),
|
||||
sendMessageFinish: message => check.send(JSON.stringify(message)),
|
||||
});
|
||||
const upgradingPeer = new RpcPeer(clientName || 'webrtc-upgrade', "api", (message, reject, serializationContext) => {
|
||||
try {
|
||||
serializer.sendMessage(message, reject, serializationContext);
|
||||
}
|
||||
catch (e) {
|
||||
reject?.(e as Error);
|
||||
}
|
||||
});
|
||||
|
||||
check.on('message', data => {
|
||||
if (data.constructor === Buffer || data.constructor === ArrayBuffer) {
|
||||
serializer.onMessageBuffer(Buffer.from(data));
|
||||
}
|
||||
else {
|
||||
serializer.onMessageFinish(JSON.parse(data as string));
|
||||
}
|
||||
});
|
||||
serializer.setupRpcPeer(upgradingPeer);
|
||||
|
||||
// is this an issue?
|
||||
// const readyClose = new Promise<RpcPeer>((resolve, reject) => {
|
||||
// check.on('close', () => reject(new Error('closed')))
|
||||
// })
|
||||
|
||||
upgradingPeer.params['session'] = session;
|
||||
|
||||
const pc = await pcPromise;
|
||||
console.log('peer connection received');
|
||||
|
||||
await waitPeerConnectionIceConnected(pc);
|
||||
console.log('waiting for data channel');
|
||||
|
||||
const dc = await dcDeferred.promise;
|
||||
console.log('datachannel received', Date.now() - start);
|
||||
|
||||
const debouncer = new DataChannelDebouncer(dc, e => {
|
||||
console.error('datachannel send error', e);
|
||||
rpcPeer.kill('datachannel send error');
|
||||
});
|
||||
const dcSerializer = createRpcDuplexSerializer({
|
||||
write: (data) => debouncer.send(data),
|
||||
});
|
||||
|
||||
while (droppedMessages.length) {
|
||||
const message = droppedMessages.shift();
|
||||
dc.dispatchEvent(message);
|
||||
}
|
||||
|
||||
const rpcPeer = new RpcPeer('webrtc-client', "api", (message, reject, serializationContext) => {
|
||||
try {
|
||||
dcSerializer.sendMessage(message, reject, serializationContext);
|
||||
}
|
||||
catch (e) {
|
||||
reject?.(e as Error);
|
||||
pc.close();
|
||||
}
|
||||
});
|
||||
dcSerializer.setupRpcPeer(rpcPeer);
|
||||
|
||||
rpcPeer.params['connectionManagementId'] = connectionManagementId;
|
||||
rpcPeer.params['updateSessionId'] = updateSessionId;
|
||||
rpcPeer.params['browserSignalingSession'] = session;
|
||||
|
||||
waitPeerIceConnectionClosed(pc).then(() => check.close());
|
||||
check.on('close', () => {
|
||||
console.log('datachannel upgrade cancelled/closed');
|
||||
pc.close()
|
||||
});
|
||||
|
||||
await new Promise(resolve => {
|
||||
let buffers: Buffer[] = [];
|
||||
dc.onmessage = message => {
|
||||
buffers.push(Buffer.from(message.data));
|
||||
resolve(undefined);
|
||||
|
||||
flush.promise.finally(() => {
|
||||
if (buffers) {
|
||||
for (const buffer of buffers) {
|
||||
dcSerializer.onData(Buffer.from(buffer));
|
||||
}
|
||||
buffers = undefined;
|
||||
}
|
||||
dc.onmessage = message => dcSerializer.onData(Buffer.from(message.data));
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
ready: check,
|
||||
connectionType: 'webrtc',
|
||||
rpcPeer,
|
||||
};
|
||||
})());
|
||||
}
|
||||
|
||||
const p2pPromises = [...promises];
|
||||
|
||||
promises.push((async () => {
|
||||
const waitDuration = tryWebrtc ? 10000 : (tryAddresses ? 1000 : 0);
|
||||
const waitDuration = tryAddresses ? 1000 : 0;
|
||||
console.log('waiting', waitDuration);
|
||||
if (waitDuration) {
|
||||
// give the peer to peers a second, but then try connecting directly.
|
||||
@@ -643,16 +539,13 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
return {
|
||||
ready: check,
|
||||
address: explicitBaseUrl,
|
||||
connectionType: 'http',
|
||||
connectionType: scryptedCloud ? 'http-cloud' : 'http',
|
||||
};
|
||||
})());
|
||||
|
||||
const any = Promise.any(promises);
|
||||
let { ready, connectionType, address, rpcPeer } = await any;
|
||||
|
||||
if (tryWebrtc && connectionType !== 'webrtc')
|
||||
localStorage.setItem(webrtcLastFailedKey, Date.now().toString());
|
||||
|
||||
console.log('connected', connectionType, address)
|
||||
|
||||
socket = ready;
|
||||
@@ -683,7 +576,7 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
});
|
||||
socket.on('message', data => {
|
||||
if (data.constructor === Buffer || data.constructor === ArrayBuffer) {
|
||||
serializer.onMessageBuffer(Buffer.from(data));
|
||||
serializer.onMessageBuffer(Buffer.from(data as string));
|
||||
}
|
||||
else {
|
||||
serializer.onMessageFinish(JSON.parse(data as string));
|
||||
@@ -692,7 +585,6 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
serializer.setupRpcPeer(rpcPeer);
|
||||
}
|
||||
|
||||
setTimeout(() => flush.resolve(undefined), 0);
|
||||
const scrypted = await attachPluginRemote(rpcPeer, undefined);
|
||||
const {
|
||||
serverVersion,
|
||||
@@ -701,6 +593,8 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
endpointManager,
|
||||
mediaManager,
|
||||
clusterManager,
|
||||
pluginHostAPI,
|
||||
pluginRemoteAPI,
|
||||
} = scrypted;
|
||||
console.log('api attached', Date.now() - start);
|
||||
|
||||
@@ -708,20 +602,7 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
return new MediaObject(mimeType, data, options) as any;
|
||||
}
|
||||
|
||||
const { browserSignalingSession, connectionManagementId, updateSessionId } = rpcPeer.params;
|
||||
if (updateSessionId && browserSignalingSession) {
|
||||
systemManager.getComponent('plugins').then(async plugins => {
|
||||
const updateSession: (session: RTCSignalingSession) => Promise<void> = await plugins.getHostParam('@scrypted/webrtc', updateSessionId);
|
||||
if (!updateSession)
|
||||
return;
|
||||
await updateSession(browserSignalingSession);
|
||||
console.log('signaling channel upgraded.');
|
||||
socket.removeAllListeners();
|
||||
socket.close();
|
||||
});
|
||||
}
|
||||
|
||||
const [admin, rtcConnectionManagement] = await Promise.all([
|
||||
const [admin] = await Promise.all([
|
||||
(async () => {
|
||||
try {
|
||||
// info is
|
||||
@@ -732,18 +613,6 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
}
|
||||
return false;
|
||||
})(),
|
||||
(async () => {
|
||||
let rtcConnectionManagement: RTCConnectionManagement;
|
||||
if (connectionManagementId) {
|
||||
try {
|
||||
const plugins = await systemManager.getComponent('plugins');
|
||||
rtcConnectionManagement = await plugins.getHostParam('@scrypted/webrtc', connectionManagementId);
|
||||
return rtcConnectionManagement;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
})(),
|
||||
]);
|
||||
|
||||
console.log('api initialized', Date.now() - start);
|
||||
@@ -752,110 +621,131 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
.map(id => systemManager.getDeviceById(id))
|
||||
.find(device => device.pluginId === '@scrypted/core' && device.nativeId === `user:${username}`);
|
||||
|
||||
const clusterPeers = new Map<number, Promise<RpcPeer>>();
|
||||
const ensureClusterPeer = (clusterObject: ClusterObject) => {
|
||||
let clusterPeerPromise = clusterPeers.get(clusterObject.port);
|
||||
if (!clusterPeerPromise) {
|
||||
clusterPeerPromise = (async () => {
|
||||
const eioPath = 'engine.io/connectRPCObject';
|
||||
const eioEndpoint = baseUrl ? new URL(eioPath, baseUrl).pathname : '/' + eioPath;
|
||||
const clusterPeerOptions = {
|
||||
path: eioEndpoint,
|
||||
query: {
|
||||
cacehBust,
|
||||
clusterObject: JSON.stringify(clusterObject),
|
||||
},
|
||||
withCredentials: true,
|
||||
extraHeaders,
|
||||
rejectUnauthorized: false,
|
||||
transports: options?.transports,
|
||||
};
|
||||
const connectRPCObject = clusterSetup(address, connectionType, queryToken, extraHeaders, options?.transports, sourcePeerId, clientName);
|
||||
|
||||
const clusterPeerSocket = new eio.Socket(explicitBaseUrl, clusterPeerOptions);
|
||||
let peerReady = false;
|
||||
clusterPeerSocket.on('close', () => {
|
||||
clusterPeers.delete(clusterObject.port);
|
||||
if (!peerReady) {
|
||||
throw new Error("peer disconnected before setup completed");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await once(clusterPeerSocket, 'open');
|
||||
|
||||
const serializer = createRpcDuplexSerializer({
|
||||
write: data => clusterPeerSocket.send(data),
|
||||
});
|
||||
clusterPeerSocket.on('message', data => serializer.onData(Buffer.from(data)));
|
||||
|
||||
const clusterPeer = new RpcPeer(clientName || 'engine.io-client', "cluster-proxy", (message, reject, serializationContext) => {
|
||||
try {
|
||||
serializer.sendMessage(message, reject, serializationContext);
|
||||
}
|
||||
catch (e) {
|
||||
reject?.(e as Error);
|
||||
}
|
||||
});
|
||||
serializer.setupRpcPeer(clusterPeer);
|
||||
clusterPeer.tags.localPort = sourcePeerId;
|
||||
peerReady = true;
|
||||
return clusterPeer;
|
||||
}
|
||||
catch (e) {
|
||||
console.error('failure ipc connect', e);
|
||||
clusterPeerSocket.close();
|
||||
throw e;
|
||||
}
|
||||
})();
|
||||
clusterPeers.set(clusterObject.port, clusterPeerPromise);
|
||||
}
|
||||
return clusterPeerPromise;
|
||||
const loginResult: ScryptedClientLoginResult = {
|
||||
username,
|
||||
token,
|
||||
directAddress,
|
||||
localAddresses,
|
||||
externalAddresses,
|
||||
scryptedCloud,
|
||||
queryToken,
|
||||
authorization,
|
||||
cloudAddress,
|
||||
hostname,
|
||||
serverId,
|
||||
};
|
||||
|
||||
const resolveObject = async (proxyId: string, sourcePeerPort: number) => {
|
||||
const sourcePeer = await clusterPeers.get(sourcePeerPort);
|
||||
if (sourcePeer?.remoteWeakProxies) {
|
||||
return Object.values(sourcePeer.remoteWeakProxies).find(
|
||||
v => v.deref()?.__cluster?.proxyId == proxyId
|
||||
)?.deref();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
type ForkType = ScryptedClientStatic['fork'];
|
||||
const fork: ForkType = (forkOptions) => {
|
||||
const { worker } = forkOptions;
|
||||
|
||||
const connectRPCObject = async (value: any) => {
|
||||
const clusterObject: ClusterObject = value?.__cluster;
|
||||
if (!clusterObject) {
|
||||
return value;
|
||||
}
|
||||
const serializer = createRpcSerializer({
|
||||
sendMessageBuffer: buffer => worker.postMessage(buffer),
|
||||
sendMessageFinish: message => worker.postMessage(JSON.stringify(message)),
|
||||
});
|
||||
|
||||
const { port, proxyId } = clusterObject;
|
||||
const threadPeer = new RpcPeer("main-client", 'thread', (message, reject, serializationContext) => {
|
||||
try {
|
||||
serializer.sendMessage(message, reject, serializationContext);
|
||||
}
|
||||
catch (e) {
|
||||
reject?.(e as Error);
|
||||
}
|
||||
});
|
||||
|
||||
// check if object is already connected
|
||||
const resolved = await resolveObject(proxyId, port);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
rpcPeer.killed.finally(() => threadPeer.kill('main rpc peer killed'));
|
||||
|
||||
try {
|
||||
const clusterPeerPromise = ensureClusterPeer(clusterObject);
|
||||
const clusterPeer = await clusterPeerPromise;
|
||||
const connectRPCObject: ConnectRPCObject = await clusterPeer.getParam('connectRPCObject');
|
||||
const newValue = await connectRPCObject(clusterObject);
|
||||
if (!newValue)
|
||||
throw new Error('ipc object not found?');
|
||||
return newValue;
|
||||
}
|
||||
catch (e) {
|
||||
console.error('failure ipc', e);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
worker.addEventListener('message', async event => {
|
||||
if (event.data instanceof Uint8Array) {
|
||||
serializer.onMessageBuffer(Buffer.from(event.data));
|
||||
}
|
||||
else {
|
||||
serializer.onMessageFinish(JSON.parse(event.data));
|
||||
}
|
||||
});
|
||||
|
||||
serializer.setupRpcPeer(threadPeer);
|
||||
|
||||
// there is no worker close event?
|
||||
const forkApi = new PluginAPIProxy(pluginHostAPI, mediaManager);
|
||||
threadPeer.killed.finally(() => {
|
||||
forkApi.removeListeners();
|
||||
worker.terminate();
|
||||
});
|
||||
|
||||
const internalFork: InternalFork = {
|
||||
loginResult,
|
||||
username,
|
||||
address,
|
||||
connectionType,
|
||||
extraHeaders,
|
||||
transports: options?.transports,
|
||||
clientName,
|
||||
admin,
|
||||
};
|
||||
|
||||
threadPeer.params['client'] = internalFork;
|
||||
|
||||
const result = (async () => {
|
||||
const getRemote = await threadPeer.getParam('getRemote');
|
||||
const remote = await getRemote(forkApi, pluginId, {
|
||||
serverVersion
|
||||
}) as PluginRemote;
|
||||
|
||||
await remote.setSystemState(systemManager.getSystemState());
|
||||
forkApi.listen((id, eventDetails, eventData) => {
|
||||
// ScryptedDevice events will be handled specially and repropagated by the remote.
|
||||
if (eventDetails.eventInterface === ScryptedInterface.ScryptedDevice) {
|
||||
if (eventDetails.property === ScryptedInterfaceProperty.id) {
|
||||
// a change on the id property means device was deleted
|
||||
remote.updateDeviceState(eventData, undefined);
|
||||
}
|
||||
else {
|
||||
// a change on anything else is a descriptor update
|
||||
remote.updateDeviceState(id, systemManager.getSystemState()[id]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventDetails.property && !eventDetails.mixinId) {
|
||||
remote.notify(id, eventDetails, systemManager.getSystemState()[id]?.[eventDetails.property]).catch(() => { });
|
||||
}
|
||||
else {
|
||||
remote.notify(id, eventDetails, eventData).catch(() => { });
|
||||
}
|
||||
});
|
||||
|
||||
const fork = await threadPeer.getParam('fork');
|
||||
return fork;
|
||||
})();
|
||||
|
||||
result.catch(() => {
|
||||
threadPeer.kill('fork setup failed');
|
||||
worker.terminate();
|
||||
});
|
||||
|
||||
return {
|
||||
[Symbol.dispose]() {
|
||||
worker.terminate();
|
||||
threadPeer.kill('disposed');
|
||||
},
|
||||
result,
|
||||
worker: {
|
||||
terminate() {
|
||||
worker.terminate();
|
||||
},
|
||||
nativeWorker: worker,
|
||||
} as any as ForkWorker,
|
||||
};
|
||||
};
|
||||
|
||||
const ret: ScryptedClientStatic = {
|
||||
userId: userDevice?.id,
|
||||
serverVersion,
|
||||
username,
|
||||
pluginRemoteAPI: undefined,
|
||||
pluginRemoteAPI,
|
||||
address,
|
||||
connectionType,
|
||||
admin,
|
||||
@@ -867,25 +757,11 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
disconnect() {
|
||||
rpcPeer.kill('disconnect requested');
|
||||
},
|
||||
pluginHostAPI: undefined,
|
||||
rtcConnectionManagement,
|
||||
browserSignalingSession,
|
||||
pluginHostAPI,
|
||||
rpcPeer,
|
||||
loginResult: {
|
||||
username,
|
||||
token,
|
||||
directAddress,
|
||||
localAddresses,
|
||||
externalAddresses,
|
||||
scryptedCloud,
|
||||
queryToken,
|
||||
authorization,
|
||||
cloudAddress,
|
||||
hostname,
|
||||
serverId,
|
||||
},
|
||||
loginResult,
|
||||
connectRPCObject,
|
||||
fork: undefined,
|
||||
fork,
|
||||
connect: undefined,
|
||||
}
|
||||
|
||||
@@ -905,3 +781,308 @@ export async function connectScryptedClient(options: ScryptedClientOptions): Pro
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function clusterSetup(address: string, connectionType: ScryptedClientConnectionType, queryToken: any, extraHeaders: { [header: string]: string }, transports: string[] | undefined, sourcePeerId: string, clientName?: string) {
|
||||
const clusterPeers = new Map<number, Promise<RpcPeer>>();
|
||||
const finalizationRegistry = new FinalizationRegistry((clusterPeer: RpcPeer) => {
|
||||
clusterPeer.kill('object finalized');
|
||||
});
|
||||
const ensureClusterPeer = (clusterObject: ClusterObject, connectRPCObjectOptions?: ConnectRPCObjectOptions) => {
|
||||
// If dedicatedTransport is true, don't reuse existing cluster peers
|
||||
if (!connectRPCObjectOptions?.dedicatedTransport) {
|
||||
let clusterPeerPromise = clusterPeers.get(clusterObject.port);
|
||||
if (clusterPeerPromise)
|
||||
return clusterPeerPromise;
|
||||
}
|
||||
|
||||
const clusterPeerPromise = (async () => {
|
||||
const eioPath = 'engine.io/connectRPCObject';
|
||||
const eioEndpoint = new URL(eioPath, address).pathname;
|
||||
const eioQueryToken = connectionType === 'http' ? undefined : queryToken;
|
||||
const clusterPeerOptions: eio.SocketOptions = {
|
||||
path: eioEndpoint,
|
||||
query: {
|
||||
cacheBust: Math.random().toString(36).substring(3, 10),
|
||||
clusterObject: JSON.stringify(clusterObject),
|
||||
...eioQueryToken,
|
||||
},
|
||||
withCredentials: true,
|
||||
extraHeaders,
|
||||
rejectUnauthorized: false,
|
||||
transports,
|
||||
};
|
||||
|
||||
const clusterPeerSocket = new eio.Socket(address, clusterPeerOptions);
|
||||
let peerReady = false;
|
||||
|
||||
// Timeout handling for dedicated transports
|
||||
let receiveTimeout: NodeJS.Timeout | undefined;
|
||||
let sendTimeout: NodeJS.Timeout | undefined;
|
||||
let clusterPeer: RpcPeer | undefined;
|
||||
|
||||
const clearTimers = () => {
|
||||
if (receiveTimeout) {
|
||||
clearTimeout(receiveTimeout);
|
||||
receiveTimeout = undefined;
|
||||
}
|
||||
if (sendTimeout) {
|
||||
clearTimeout(sendTimeout);
|
||||
sendTimeout = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const resetReceiveTimeout = connectRPCObjectOptions?.dedicatedTransport?.receiveTimeout ? () => {
|
||||
if (receiveTimeout) {
|
||||
clearTimeout(receiveTimeout);
|
||||
}
|
||||
receiveTimeout = setTimeout(() => {
|
||||
if (clusterPeer) {
|
||||
clusterPeer.kill('receive timeout');
|
||||
}
|
||||
}, connectRPCObjectOptions.dedicatedTransport.receiveTimeout);
|
||||
} : undefined;
|
||||
|
||||
const resetSendTimeout = connectRPCObjectOptions?.dedicatedTransport?.sendTimeout ? () => {
|
||||
if (sendTimeout) {
|
||||
clearTimeout(sendTimeout);
|
||||
}
|
||||
sendTimeout = setTimeout(() => {
|
||||
if (clusterPeer) {
|
||||
clusterPeer.kill('send timeout');
|
||||
}
|
||||
}, connectRPCObjectOptions.dedicatedTransport.sendTimeout);
|
||||
} : undefined;
|
||||
|
||||
clusterPeerSocket.on('close', () => {
|
||||
clusterPeer?.kill('socket closed');
|
||||
// Only remove from clusterPeers if it's not a dedicated transport
|
||||
if (!connectRPCObjectOptions?.dedicatedTransport) {
|
||||
clusterPeers.delete(clusterObject.port);
|
||||
}
|
||||
if (!peerReady) {
|
||||
throw new Error("peer disconnected before setup completed");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await once(clusterPeerSocket, 'open');
|
||||
|
||||
const serializer = createRpcDuplexSerializer({
|
||||
write: data => {
|
||||
resetSendTimeout?.();
|
||||
clusterPeerSocket.send(data);
|
||||
},
|
||||
});
|
||||
|
||||
clusterPeerSocket.on('message', data => {
|
||||
resetReceiveTimeout?.();
|
||||
serializer.onData(Buffer.from(data));
|
||||
});
|
||||
|
||||
clusterPeer = new RpcPeer(clientName || 'engine.io-client', "cluster-proxy", (message, reject, serializationContext) => {
|
||||
try {
|
||||
resetSendTimeout?.();
|
||||
serializer.sendMessage(message, reject, serializationContext);
|
||||
}
|
||||
catch (e) {
|
||||
reject?.(e as Error);
|
||||
}
|
||||
});
|
||||
clusterPeer.killedSafe.finally(() => {
|
||||
clearTimers();
|
||||
clusterPeerSocket.close();
|
||||
});
|
||||
serializer.setupRpcPeer(clusterPeer);
|
||||
clusterPeer.tags.localPort = sourcePeerId;
|
||||
peerReady = true;
|
||||
|
||||
// Initialize timeouts if configured
|
||||
resetReceiveTimeout?.();
|
||||
resetSendTimeout?.();
|
||||
|
||||
return clusterPeer;
|
||||
}
|
||||
catch (e) {
|
||||
clearTimers();
|
||||
console.error('failure ipc connect', e);
|
||||
clusterPeerSocket.close();
|
||||
throw e;
|
||||
}
|
||||
})();
|
||||
|
||||
// Only store in clusterPeers if it's not a dedicated transport
|
||||
if (!connectRPCObjectOptions?.dedicatedTransport) {
|
||||
clusterPeers.set(clusterObject.port, clusterPeerPromise);
|
||||
}
|
||||
|
||||
return clusterPeerPromise;
|
||||
};
|
||||
|
||||
const resolveObject = async (proxyId: string, sourcePeerPort: number) => {
|
||||
const sourcePeer = await clusterPeers.get(sourcePeerPort);
|
||||
if (sourcePeer?.remoteWeakProxies) {
|
||||
return Object.values(sourcePeer.remoteWeakProxies).find(
|
||||
v => v.deref()?.__cluster?.proxyId == proxyId
|
||||
)?.deref();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const connectRPCObject = async (value: any, options?: ConnectRPCObjectOptions) => {
|
||||
const clusterObject: ClusterObject = value?.__cluster;
|
||||
if (!clusterObject) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const { port, proxyId } = clusterObject;
|
||||
|
||||
// check if object is already connected
|
||||
const resolved = await resolveObject(proxyId, port);
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
try {
|
||||
const clusterPeerPromise = ensureClusterPeer(clusterObject, options);
|
||||
const clusterPeer = await clusterPeerPromise;
|
||||
const connectRPCObject: ConnectRPCObject = await clusterPeer.getParam('connectRPCObject');
|
||||
try {
|
||||
const newValue = await connectRPCObject(clusterObject);
|
||||
if (!newValue)
|
||||
throw new Error('ipc object not found?');
|
||||
|
||||
// If dedicatedTransport is true, register the object for cleanup
|
||||
if (options?.dedicatedTransport) {
|
||||
finalizationRegistry.register(newValue, clusterPeer);
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
catch (e) {
|
||||
// If we have a clusterPeer and this is a dedicated transport, kill the connection
|
||||
// to prevent resource leaks when connectRPCObject fails
|
||||
if (options?.dedicatedTransport) {
|
||||
clusterPeer.kill('connectRPCObject failed');
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.error('failure ipc', e);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return connectRPCObject;
|
||||
}
|
||||
|
||||
export async function connectScryptedClientFork(forkMain: (client: ScryptedClientStatic) => Promise<any>) {
|
||||
const start = Date.now();
|
||||
|
||||
try {
|
||||
|
||||
const serializer = createRpcSerializer({
|
||||
sendMessageBuffer: buffer => self.postMessage(buffer),
|
||||
sendMessageFinish: message => self.postMessage(JSON.stringify(message)),
|
||||
});
|
||||
|
||||
const rpcPeer = new RpcPeer('thread', "main-client", (message, reject, serializationContext) => {
|
||||
try {
|
||||
serializer.sendMessage(message, reject, serializationContext);
|
||||
}
|
||||
catch (e) {
|
||||
reject?.(e as Error);
|
||||
}
|
||||
});
|
||||
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data instanceof Uint8Array) {
|
||||
serializer.onMessageBuffer(Buffer.from(event.data));
|
||||
}
|
||||
else {
|
||||
serializer.onMessageFinish(JSON.parse(event.data));
|
||||
}
|
||||
});
|
||||
|
||||
serializer.setupRpcPeer(rpcPeer);
|
||||
|
||||
|
||||
const scrypted = await attachPluginRemote(rpcPeer, undefined);
|
||||
const {
|
||||
serverVersion,
|
||||
systemManager,
|
||||
deviceManager,
|
||||
endpointManager,
|
||||
mediaManager,
|
||||
clusterManager,
|
||||
pluginHostAPI,
|
||||
pluginRemoteAPI,
|
||||
} = scrypted;
|
||||
console.log('api attached', Date.now() - start);
|
||||
|
||||
mediaManager.createMediaObject = async<T extends MediaObjectCreateOptions>(data: any, mimeType: string, options: T) => {
|
||||
return new MediaObject(mimeType, data, options) as any;
|
||||
}
|
||||
console.log('api initialized', Date.now() - start);
|
||||
|
||||
const {
|
||||
loginResult,
|
||||
username,
|
||||
address,
|
||||
connectionType,
|
||||
extraHeaders,
|
||||
transports,
|
||||
clientName,
|
||||
admin,
|
||||
} = await rpcPeer.getParam('client') as InternalFork;
|
||||
|
||||
const { queryToken } = loginResult;
|
||||
|
||||
const userDevice = Object.keys(systemManager.getSystemState())
|
||||
.map(id => systemManager.getDeviceById(id))
|
||||
.find(device => device.pluginId === '@scrypted/core' && device.nativeId === `user:${username}`);
|
||||
|
||||
const connectRPCObject = clusterSetup(address, connectionType, queryToken, extraHeaders, transports, sourcePeerId, clientName);
|
||||
|
||||
type ForkType = ScryptedClientStatic['fork'];
|
||||
const fork: ForkType = (forkOptions) => {
|
||||
throw new Error('not implemented');
|
||||
};
|
||||
|
||||
const ret: ScryptedClientStatic = {
|
||||
userId: userDevice?.id,
|
||||
serverVersion,
|
||||
username,
|
||||
pluginRemoteAPI,
|
||||
address,
|
||||
connectionType,
|
||||
admin,
|
||||
systemManager,
|
||||
clusterManager,
|
||||
deviceManager,
|
||||
endpointManager,
|
||||
mediaManager,
|
||||
disconnect() {
|
||||
rpcPeer.kill('disconnect requested');
|
||||
},
|
||||
pluginHostAPI,
|
||||
rpcPeer,
|
||||
loginResult,
|
||||
connectRPCObject,
|
||||
fork,
|
||||
connect: undefined,
|
||||
}
|
||||
rpcPeer.killed.finally(() => {
|
||||
self.close();
|
||||
ret.onClose?.();
|
||||
});
|
||||
|
||||
const forked = await forkMain(ret);
|
||||
|
||||
rpcPeer.params['fork'] = forked;
|
||||
}
|
||||
catch (e) {
|
||||
self.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
947
packages/deferred/package-lock.json
generated
947
packages/deferred/package-lock.json
generated
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "@scrypted/rpc",
|
||||
"version": "0.0.5",
|
||||
"name": "@scrypted/deferred",
|
||||
"version": "0.0.8",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/rpc",
|
||||
"version": "0.0.5",
|
||||
"name": "@scrypted/deferred",
|
||||
"version": "0.0.8",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"rimraf": "^4.1.1",
|
||||
"typescript": "^4.7.4"
|
||||
"@types/node": "^24.0.10",
|
||||
"rimraf": "^6.0.1",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
@@ -43,19 +43,141 @@
|
||||
"../sdk/types": {
|
||||
"extraneous": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.1.1.tgz",
|
||||
"integrity": "sha512-Z4Y81w8atcvaJuJuBB88VpADRH66okZAuEm+Jtaufa+s7rZmIz+Hik2G53kGaNytE7lsfXyWktTmfVz0H9xuDg==",
|
||||
"node_modules/@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"rimraf": "dist/cjs/src/bin.js"
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/brace-expansion": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@isaacs/balanced-match": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
"strip-ansi": "^7.0.1",
|
||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "24.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
|
||||
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.6",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
@@ -64,38 +186,793 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"node_modules/glob": {
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
|
||||
"integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.3.1",
|
||||
"jackspeak": "^4.1.1",
|
||||
"minimatch": "^10.0.3",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
|
||||
"integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
|
||||
"integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
|
||||
"integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/package-json-from-dist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
|
||||
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz",
|
||||
"integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"glob": "^11.0.0",
|
||||
"package-json-from-dist": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "dist/esm/bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs": {
|
||||
"name": "string-width",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs": {
|
||||
"name": "strip-ansi",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs": {
|
||||
"name": "wrap-ansi",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "18.11.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
|
||||
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
|
||||
"@isaacs/balanced-match": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
|
||||
"integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@isaacs/brace-expansion": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
|
||||
"integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@isaacs/balanced-match": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
"strip-ansi": "^7.0.1",
|
||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||
}
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "24.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.10.tgz",
|
||||
"integrity": "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"undici-types": "~7.8.0"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
||||
"dev": true
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"dev": true
|
||||
},
|
||||
"foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^7.0.6",
|
||||
"signal-exit": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "11.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
|
||||
"integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"foreground-child": "^3.3.1",
|
||||
"jackspeak": "^4.1.1",
|
||||
"minimatch": "^10.0.3",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true
|
||||
},
|
||||
"isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"jackspeak": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
|
||||
"integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
}
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
|
||||
"integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==",
|
||||
"dev": true
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "10.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
|
||||
"integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@isaacs/brace-expansion": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"dev": true
|
||||
},
|
||||
"package-json-from-dist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"path-scurry": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
|
||||
"integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^11.0.0",
|
||||
"minipass": "^7.1.2"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.1.1.tgz",
|
||||
"integrity": "sha512-Z4Y81w8atcvaJuJuBB88VpADRH66okZAuEm+Jtaufa+s7rZmIz+Hik2G53kGaNytE7lsfXyWktTmfVz0H9xuDg==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz",
|
||||
"integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^11.0.0",
|
||||
"package-json-from-dist": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.7.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
|
||||
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
|
||||
"signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"string-width-cjs": {
|
||||
"version": "npm:string-width@4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi-cjs": {
|
||||
"version": "npm:strip-ansi@6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "5.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"undici-types": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
||||
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isexe": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"wrap-ansi-cjs": {
|
||||
"version": "npm:wrap-ansi@7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/deferred",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.8",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
@@ -12,8 +12,8 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"rimraf": "^4.1.1",
|
||||
"typescript": "^4.7.4"
|
||||
"@types/node": "^24.0.10",
|
||||
"rimraf": "^6.0.1",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
||||
4
packages/rpc/package-lock.json
generated
4
packages/rpc/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/rpc",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.8",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/rpc",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.8",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/rpc",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.8",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
|
||||
2
plugins/alexa/.vscode/settings.json
vendored
2
plugins/alexa/.vscode/settings.json
vendored
@@ -1,4 +1,4 @@
|
||||
|
||||
{
|
||||
"scrypted.debugHost": "127.0.0.1",
|
||||
"scrypted.debugHost": "scrypted-server",
|
||||
}
|
||||
@@ -1,6 +1,21 @@
|
||||
<details>
|
||||
<summary>Changelog</summary>
|
||||
|
||||
### 0.3.6
|
||||
|
||||
alexa: maybe fix alexa when no detection types are available
|
||||
|
||||
|
||||
### 0.3.4
|
||||
|
||||
Alexa: add option to not auto enable devices (#1615)
|
||||
|
||||
|
||||
### 0.3.3
|
||||
|
||||
google-home/alexa: republish with new sdk for media converter
|
||||
|
||||
|
||||
### 0.3.2
|
||||
|
||||
alexa: fix syncedDevices being undefined
|
||||
|
||||
70
plugins/alexa/package-lock.json
generated
70
plugins/alexa/package-lock.json
generated
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.3.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.3.4",
|
||||
"version": "0.3.7",
|
||||
"dependencies": {
|
||||
"axios": "^1.3.4",
|
||||
"uuid": "^9.0.0"
|
||||
@@ -14,7 +15,7 @@
|
||||
"@scrypted/common": "../../common",
|
||||
"@scrypted/sdk": "../../sdk",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.4.2"
|
||||
"@types/node": "^22.13.11"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
@@ -24,35 +25,41 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@scrypted/server": "file:../server",
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.5",
|
||||
"version": "0.5.10",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^1.6.5",
|
||||
"babel-loader": "^9.1.0",
|
||||
"babel-plugin-const-enum": "^1.1.0",
|
||||
"esbuild": "^0.15.9",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-typescript": "^12.1.1",
|
||||
"@rollup/plugin-virtual": "^3.0.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.7.8",
|
||||
"babel-loader": "^9.2.1",
|
||||
"babel-plugin-const-enum": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"tmp": "^0.2.1",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^4.9.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0"
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.27.4",
|
||||
"tmp": "^0.2.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.96.1",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
},
|
||||
"bin": {
|
||||
"scrypted-changelog": "bin/scrypted-changelog.js",
|
||||
@@ -64,11 +71,9 @@
|
||||
"scrypted-webpack": "bin/scrypted-webpack.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/stringify-object": "^4.0.0",
|
||||
"stringify-object": "^3.3.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.23.21"
|
||||
"@types/node": "^22.10.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typedoc": "^0.26.11"
|
||||
}
|
||||
},
|
||||
"../common": {
|
||||
@@ -98,10 +103,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.2.tgz",
|
||||
"integrity": "sha512-1uEQxww3DaghA0RxqHx0O0ppVlo43pJhepY51OxuQIKHpjbnYLA7vcdwioNPzIqmC2u3I/dmylcqjlh0e7AyUA==",
|
||||
"dev": true
|
||||
"version": "22.13.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.11.tgz",
|
||||
"integrity": "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
@@ -193,6 +202,13 @@
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
@@ -202,4 +218,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/alexa",
|
||||
"version": "0.3.4",
|
||||
"version": "0.3.7",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
@@ -42,6 +42,6 @@
|
||||
"@scrypted/common": "../../common",
|
||||
"@scrypted/sdk": "../../sdk",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^18.4.2"
|
||||
"@types/node": "^22.13.11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,17 +660,26 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
|
||||
|
||||
const deviceHandler = alexaDeviceHandlers.get(mapName);
|
||||
|
||||
if (deviceHandler) {
|
||||
const getDevice = () => {
|
||||
const device = systemManager.getDeviceById(directive.endpoint.endpointId);
|
||||
if (!device) {
|
||||
response.send(deviceErrorResponse("NO_SUCH_ENDPOINT", "The device doesn't exist in Scrypted", directive));
|
||||
if (!device || !device.mixins.includes(this.id)) {
|
||||
response.send(deviceErrorResponse("NO_SUCH_ENDPOINT", "The device doesn't exist in Scrypted or was removed from the Alexa Plugin", directive));
|
||||
this.deleteEndpoints(directive.endpoint.endpointId).catch(() => { });
|
||||
return;
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
if (deviceHandler) {
|
||||
const device = getDevice();
|
||||
if (!device)
|
||||
return;
|
||||
await deviceHandler.apply(this, [request, response, directive, device]);
|
||||
return;
|
||||
} else {
|
||||
this.console.error(`no handler for: ${mapName}`);
|
||||
if (!getDevice())
|
||||
return;
|
||||
}
|
||||
|
||||
// it is better to send a non-specific response than an error, as the API might get rate throttled
|
||||
@@ -718,6 +727,9 @@ class HttpResponseLoggingImpl implements AlexaHttpResponse {
|
||||
sendSocket(socket: any, options: HttpResponseOptions): void {
|
||||
this.response.sendSocket(socket, options);
|
||||
}
|
||||
sendStream(stream: AsyncGenerator<Buffer, void>, options?: HttpResponseOptions): void {
|
||||
this.response.sendStream(stream, options);
|
||||
}
|
||||
}
|
||||
|
||||
export default AlexaPlugin;
|
||||
|
||||
@@ -126,68 +126,69 @@ export async function getCameraCapabilities(device: ScryptedDevice): Promise<Dis
|
||||
];
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.ObjectDetector)) {
|
||||
const detectionTypes = await (device as any as ObjectDetector).getObjectTypes();
|
||||
const classNames = detectionTypes.classes.filter(t => t !== 'ring' && t !== 'motion').map(type => type.toLowerCase());
|
||||
|
||||
capabilities.push(
|
||||
{
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.SmartVision.ObjectDetectionSensor",
|
||||
"version": "1.0",
|
||||
"properties": {
|
||||
"supported": [{
|
||||
"name": "objectDetectionClasses"
|
||||
}],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
},
|
||||
"configuration": {
|
||||
"objectDetectionConfiguration": classNames.map(type => ({
|
||||
"imageNetClass": type
|
||||
}))
|
||||
}
|
||||
} as DiscoveryCapability
|
||||
);
|
||||
|
||||
capabilities.push(
|
||||
{
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.DataController",
|
||||
"instance": "Camera.SmartVisionData",
|
||||
"version": "1.0",
|
||||
"properties": undefined,
|
||||
"configuration": {
|
||||
"targetCapability": {
|
||||
"name": "Alexa.SmartVision.ObjectDetectionSensor",
|
||||
"version": "1.0"
|
||||
const detectionTypes = await (device as any as ObjectDetector).getObjectTypes().catch(() => {}) || undefined;
|
||||
const classNames = detectionTypes?.classes?.filter(t => t !== 'ring' && t !== 'motion').map(type => type.toLowerCase()).filter(c => !!c);
|
||||
if (classNames?.length) {
|
||||
capabilities.push(
|
||||
{
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.SmartVision.ObjectDetectionSensor",
|
||||
"version": "1.0",
|
||||
"properties": {
|
||||
"supported": [{
|
||||
"name": "objectDetectionClasses"
|
||||
}],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
},
|
||||
"dataRetrievalSchema": {
|
||||
"type": "JSON",
|
||||
"schema": "SmartVisionData"
|
||||
},
|
||||
"supportedAccess": ["BY_IDENTIFIER", "BY_TIMESTAMP_RANGE"]
|
||||
}
|
||||
} as DiscoveryCapability
|
||||
);
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.MotionSensor)) {
|
||||
capabilities.push(
|
||||
{
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.MotionSensor",
|
||||
"version": "3",
|
||||
"properties": {
|
||||
"supported": [
|
||||
{
|
||||
"name": "detectionState"
|
||||
}
|
||||
],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
}
|
||||
} as DiscoveryCapability
|
||||
);
|
||||
"configuration": {
|
||||
"objectDetectionConfiguration": classNames.map(type => ({
|
||||
"imageNetClass": type
|
||||
}))
|
||||
}
|
||||
} as DiscoveryCapability
|
||||
);
|
||||
|
||||
capabilities.push(
|
||||
{
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.DataController",
|
||||
"instance": "Camera.SmartVisionData",
|
||||
"version": "1.0",
|
||||
"properties": undefined,
|
||||
"configuration": {
|
||||
"targetCapability": {
|
||||
"name": "Alexa.SmartVision.ObjectDetectionSensor",
|
||||
"version": "1.0"
|
||||
},
|
||||
"dataRetrievalSchema": {
|
||||
"type": "JSON",
|
||||
"schema": "SmartVisionData"
|
||||
},
|
||||
"supportedAccess": ["BY_IDENTIFIER", "BY_TIMESTAMP_RANGE"]
|
||||
}
|
||||
} as DiscoveryCapability
|
||||
);
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.MotionSensor)) {
|
||||
capabilities.push(
|
||||
{
|
||||
"type": "AlexaInterface",
|
||||
"interface": "Alexa.MotionSensor",
|
||||
"version": "3",
|
||||
"properties": {
|
||||
"supported": [
|
||||
{
|
||||
"name": "detectionState"
|
||||
}
|
||||
],
|
||||
"proactivelyReported": true,
|
||||
"retrievable": true
|
||||
}
|
||||
} as DiscoveryCapability
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return capabilities;
|
||||
|
||||
@@ -8,7 +8,7 @@ export interface SupportedType {
|
||||
setState?(device: ScryptedDevice, payload: any): Promise<Partial<Report>>;
|
||||
}
|
||||
|
||||
export const supportedTypes = new Map<ScryptedDeviceType, SupportedType>();
|
||||
export const supportedTypes = new Map<ScryptedDeviceType | string, SupportedType>();
|
||||
|
||||
import '../handlers';
|
||||
import './camera';
|
||||
|
||||
2
plugins/amcrest/.vscode/settings.json
vendored
2
plugins/amcrest/.vscode/settings.json
vendored
@@ -1,4 +1,4 @@
|
||||
|
||||
{
|
||||
"scrypted.debugHost": "127.0.0.1",
|
||||
"scrypted.debugHost": "scrypted-nvr",
|
||||
}
|
||||
115
plugins/amcrest/package-lock.json
generated
115
plugins/amcrest/package-lock.json
generated
@@ -1,21 +1,23 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.164",
|
||||
"version": "0.0.168",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.164",
|
||||
"version": "0.0.168",
|
||||
"license": "Apache",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"content-type": "^1.0.5"
|
||||
"content-type": "^1.0.5",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/content-type": "^1.1.8",
|
||||
"@types/node": "^20.11.30"
|
||||
"@types/node": "^22.19.3",
|
||||
"@types/xml2js": "^0.4.14"
|
||||
}
|
||||
},
|
||||
"../../common": {
|
||||
@@ -24,34 +26,43 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@scrypted/server": "file:../server",
|
||||
"@scrypted/types": "^0.5.27",
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"@types/node": "^20.19.11",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.29",
|
||||
"version": "0.5.55",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^1.6.5",
|
||||
"babel-loader": "^9.1.0",
|
||||
"babel-plugin-const-enum": "^1.1.0",
|
||||
"esbuild": "^0.15.9",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@rollup/plugin-commonjs": "^28.0.9",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-typescript": "^12.3.0",
|
||||
"@rollup/plugin-virtual": "^3.0.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.10.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-const-enum": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"openai": "^6.1.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"tmp": "^0.2.1",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^4.9.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0"
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.52.5",
|
||||
"tmp": "^0.2.3",
|
||||
"ts-loader": "^9.5.4",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.9.3",
|
||||
"webpack": "^5.99.9",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
},
|
||||
"bin": {
|
||||
"scrypted-changelog": "bin/scrypted-changelog.js",
|
||||
@@ -63,11 +74,9 @@
|
||||
"scrypted-webpack": "bin/scrypted-webpack.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/stringify-object": "^4.0.0",
|
||||
"stringify-object": "^3.3.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.23.21"
|
||||
"@types/node": "^24.9.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typedoc": "^0.28.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/common": {
|
||||
@@ -85,12 +94,23 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.11.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz",
|
||||
"integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==",
|
||||
"version": "22.19.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz",
|
||||
"integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~5.26.4"
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/xml2js": {
|
||||
"version": "0.4.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
|
||||
"integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
@@ -101,11 +121,40 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/sax": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||
"dev": true
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/xml2js": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sax": ">=0.6.0",
|
||||
"xmlbuilder": "~11.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "11.0.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/amcrest",
|
||||
"version": "0.0.164",
|
||||
"version": "0.0.168",
|
||||
"description": "Amcrest Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
@@ -39,10 +39,12 @@
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"content-type": "^1.0.5"
|
||||
"content-type": "^1.0.5",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/content-type": "^1.1.8",
|
||||
"@types/node": "^20.11.30"
|
||||
"@types/node": "^22.19.3",
|
||||
"@types/xml2js": "^0.4.14"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { AuthFetchCredentialState, HttpFetchOptions, authHttpFetch } from '@scrypted/common/src/http-auth-fetch';
|
||||
import { AuthFetchCredentialState, authHttpFetch, HttpFetchOptions } from '@scrypted/common/src/http-auth-fetch';
|
||||
import { readLine } from '@scrypted/common/src/read-stream';
|
||||
import { parseHeaders, readBody } from '@scrypted/common/src/rtsp-server';
|
||||
import { MediaStreamConfiguration, Point } from '@scrypted/sdk';
|
||||
import contentType from 'content-type';
|
||||
import { IncomingMessage } from 'http';
|
||||
import { EventEmitter, Readable } from 'stream';
|
||||
import { createRtspMediaStreamOptions, Destroyable, UrlMediaStreamOptions } from '../../rtsp/src/rtsp';
|
||||
import { getDeviceInfo } from './probe';
|
||||
import { MediaStreamConfiguration, MediaStreamOptions, Point } from '@scrypted/sdk';
|
||||
|
||||
export interface AmcrestObjectDetails {
|
||||
Action: string;
|
||||
@@ -81,8 +81,11 @@ async function readAmcrestMessage(client: Readable): Promise<string[]> {
|
||||
}
|
||||
}
|
||||
|
||||
function findValue(blob: string, prefix: string, key: string) {
|
||||
const lines = blob.split('\n');
|
||||
function getLines(blob: string) {
|
||||
return blob.split(/\r?\n/).filter(line => line);
|
||||
}
|
||||
|
||||
function findValue(lines: string[], prefix: string, key: string) {
|
||||
const value = lines.find(line => line.startsWith(`${prefix}.${key}`));
|
||||
if (!value)
|
||||
return;
|
||||
@@ -124,7 +127,7 @@ const amcrestResolutions = {
|
||||
"720P": [1280, 720],
|
||||
"D1": [704, 480],
|
||||
"HD1": [352, 480],
|
||||
"BCIF": [704, 240],
|
||||
"BCIF": [528, 240],
|
||||
"2CIF": [704, 240],
|
||||
"CIF": [352, 240],
|
||||
"QCIF": [176, 120],
|
||||
@@ -133,7 +136,21 @@ const amcrestResolutions = {
|
||||
"QVGA": [320, 240]
|
||||
};
|
||||
|
||||
function fromAmcrestResolution(resolution: string) {
|
||||
const palAmcrestResolutions = {
|
||||
"D1": [704, 576],
|
||||
"HD1": [352, 576],
|
||||
"BCIF": [528, 288],
|
||||
"2CIF": [704, 288],
|
||||
"CIF": [352, 288],
|
||||
"QCIF": [176, 144],
|
||||
};
|
||||
|
||||
function fromAmcrestResolution(resolution: string, videoStandard: string) {
|
||||
if (videoStandard === 'PAL') {
|
||||
const named = palAmcrestResolutions[resolution];
|
||||
if (named)
|
||||
return named;
|
||||
}
|
||||
const named = amcrestResolutions[resolution];
|
||||
if (named)
|
||||
return named;
|
||||
@@ -251,8 +268,8 @@ export class AmcrestCameraClient {
|
||||
continue;
|
||||
if (ignore === boundaryEnd)
|
||||
continue;
|
||||
// dahua bugs out and sends this.
|
||||
if (ignore === 'HTTP/1.1 200 OK') {
|
||||
// dahua bugs out and sends this (handle both HTTP/1.0 and HTTP/1.1).
|
||||
if (ignore === 'HTTP/1.1 200 OK' || ignore === 'HTTP/1.0 200 OK') {
|
||||
const message = await readAmcrestMessage(stream);
|
||||
this.console.log('ignoring dahua http message', message);
|
||||
message.unshift('');
|
||||
@@ -438,6 +455,12 @@ export class AmcrestCameraClient {
|
||||
|
||||
this.console.log(capsResponse.body);
|
||||
|
||||
const videoStandardResponse = await this.request({
|
||||
url: `http://${this.ip}/cgi-bin/configManager.cgi?action=getConfig&name=VideoStandard`,
|
||||
responseType: 'text',
|
||||
});
|
||||
this.console.log(videoStandardResponse.body);
|
||||
|
||||
const formatNumber = Math.max(0, parseInt(options.id?.substring('channel'.length)) - 1);
|
||||
const format = options.id === 'channel0' ? 'MainFormat' : 'ExtraFormat';
|
||||
const encode = `Encode[${cameraNumber - 1}].${format}[${formatNumber}]`;
|
||||
@@ -493,17 +516,19 @@ export class AmcrestCameraClient {
|
||||
|
||||
const caps = `caps[${cameraNumber - 1}].${format}[${formatNumber}]`;
|
||||
const singleCaps = `caps.${format}[${formatNumber}]`;
|
||||
const capsLines = getLines(capsResponse.body);
|
||||
const videoStandard = findValue(getLines(videoStandardResponse.body), 'table', 'VideoStandard');
|
||||
|
||||
const findCaps = (key: string) => {
|
||||
const found = findValue(capsResponse.body, caps, key);
|
||||
const found = findValue(capsLines, caps, key);
|
||||
if (found)
|
||||
return found;
|
||||
// ad410 doesnt return a camera number if accessed directly
|
||||
if (cameraNumber - 1 === 0)
|
||||
return findValue(capsResponse.body, singleCaps, key);
|
||||
return findValue(capsLines, singleCaps, key);
|
||||
}
|
||||
|
||||
const resolutions = findCaps('Video.ResolutionTypes').split(',').map(fromAmcrestResolution);
|
||||
const resolutions = findCaps('Video.ResolutionTypes').split(',').map(r => fromAmcrestResolution(r, videoStandard));
|
||||
const bitrates = findCaps('Video.BitRateOptions').split(',').map(s => parseInt(s) * 1000);
|
||||
const fpsMax = parseInt(findCaps('Video.FPSMax'));
|
||||
const vso: MediaStreamConfiguration = {
|
||||
@@ -533,6 +558,7 @@ export class AmcrestCameraClient {
|
||||
responseType: 'text',
|
||||
});
|
||||
this.console.log(encodeResponse.body);
|
||||
const encodeLines = getLines(encodeResponse.body);
|
||||
|
||||
for (let i = 0; i < vsos.length; i++) {
|
||||
const vso = vsos[i];
|
||||
@@ -544,27 +570,27 @@ export class AmcrestCameraClient {
|
||||
encName = `table.Encode[${cameraNumber - 1}].ExtraFormat[${i - 1}]`;
|
||||
}
|
||||
|
||||
const videoCodec = fromAmcrestVideoCodec(findValue(encodeResponse.body, encName, 'Video.Compression'));
|
||||
const audioCodec = fromAmcrestAudioCodec(findValue(encodeResponse.body, encName, 'Audio.Compression'));
|
||||
const videoCodec = fromAmcrestVideoCodec(findValue(encodeLines, encName, 'Video.Compression'));
|
||||
const audioCodec = fromAmcrestAudioCodec(findValue(encodeLines, encName, 'Audio.Compression'));
|
||||
|
||||
if (vso.audio)
|
||||
vso.audio.codec = audioCodec;
|
||||
vso.video.codec = videoCodec;
|
||||
|
||||
const width = findValue(encodeResponse.body, encName, 'Video.Width');
|
||||
const height = findValue(encodeResponse.body, encName, 'Video.Height');
|
||||
const width = findValue(encodeLines, encName, 'Video.Width');
|
||||
const height = findValue(encodeLines, encName, 'Video.Height');
|
||||
if (width && height) {
|
||||
vso.video.width = parseInt(width);
|
||||
vso.video.height = parseInt(height);
|
||||
}
|
||||
|
||||
const videoEnable = findValue(encodeResponse.body, encName, 'VideoEnable');
|
||||
const videoEnable = findValue(encodeLines, encName, 'VideoEnable');
|
||||
if (videoEnable?.trim() === 'false') {
|
||||
this.console.warn('Video stream is disabled and should likely be enabled:', encName);
|
||||
continue;
|
||||
}
|
||||
|
||||
const encodeOptions = findValue(encodeResponse.body, encName, 'Video.BitRate');
|
||||
const encodeOptions = findValue(encodeLines, encName, 'Video.BitRate');
|
||||
if (!encodeOptions)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { automaticallyConfigureSettings, checkPluginNeedsAutoConfigure } from "@scrypted/common/src/autoconfigure-codecs";
|
||||
import { ffmpegLogInitialOutput } from '@scrypted/common/src/media-helpers';
|
||||
import { readLength } from "@scrypted/common/src/read-stream";
|
||||
import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, Lock, MediaObject, MediaStreamOptions, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, Reboot, RequestPictureOptions, RequestRecordingStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, ScryptedNativeId, Setting, VideoCameraConfiguration, VideoRecorder } from "@scrypted/sdk";
|
||||
import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, Lock, MediaObject, MediaStreamOptions, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, Reboot, RequestPictureOptions, RequestRecordingStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, ScryptedNativeId, Setting, VideoCameraConfiguration, VideoRecorder, VideoTextOverlay, VideoTextOverlays } from "@scrypted/sdk";
|
||||
import child_process, { ChildProcess } from 'child_process';
|
||||
import { PassThrough, Readable, Stream } from "stream";
|
||||
import { OnvifIntercom } from "../../onvif/src/onvif-intercom";
|
||||
import { createRtspMediaStreamOptions, RtspProvider, RtspSmartCamera, UrlMediaStreamOptions } from "../../rtsp/src/rtsp";
|
||||
import { AmcrestCameraClient, AmcrestEvent, AmcrestEventData } from "./amcrest-api";
|
||||
import { amcrestAutoConfigureSettings, autoconfigureSettings } from "./amcrest-configure";
|
||||
import { group } from "console";
|
||||
|
||||
const { mediaManager } = sdk;
|
||||
|
||||
@@ -23,7 +22,7 @@ const rtspChannelSetting: Setting = {
|
||||
placeholder: '1',
|
||||
};
|
||||
|
||||
class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, Camera, Intercom, Lock, VideoRecorder, Reboot, ObjectDetector {
|
||||
class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, Camera, Intercom, Lock, VideoRecorder, Reboot, ObjectDetector, VideoTextOverlays {
|
||||
eventStream: Stream;
|
||||
cp: ChildProcess;
|
||||
client: AmcrestCameraClient;
|
||||
@@ -43,6 +42,92 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration,
|
||||
this.updateDeviceInfo();
|
||||
}
|
||||
|
||||
async getVideoTextOverlays(): Promise<Record<string, VideoTextOverlay>> {
|
||||
const client = this.getClient();
|
||||
const response = await client.request({
|
||||
method: "GET",
|
||||
url: `http://${this.getHttpAddress()}/cgi-bin/configManager.cgi?action=getConfig&name=VideoWidget`,
|
||||
responseType: "text",
|
||||
headers: {
|
||||
"Content-Type": "application/xml",
|
||||
},
|
||||
});
|
||||
const body: string = response.body;
|
||||
if (!body.startsWith("<")) {
|
||||
const encodeBlend = '.EncodeBlend';
|
||||
const config: Record<string, VideoTextOverlay> = {};
|
||||
|
||||
for (const line of body.split(/\r?\n/).filter(l => l.includes(encodeBlend + '='))) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
const splitIndex = trimmed.indexOf("=");
|
||||
if (splitIndex === -1) continue;
|
||||
// remove encodeBlend
|
||||
let key = trimmed.substring(0, splitIndex);
|
||||
key = key.substring(0, key.length - encodeBlend.length);
|
||||
config[key] = {
|
||||
readonly: true,
|
||||
};
|
||||
}
|
||||
|
||||
const textValue = '.Text';
|
||||
|
||||
for (const line of body.split(/\r?\n/).filter(l => l.includes(textValue + '='))) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
const splitIndex = trimmed.indexOf("=");
|
||||
if (splitIndex === -1) continue;
|
||||
// remove encodeBlend
|
||||
let key = trimmed.substring(0, splitIndex);
|
||||
key = key.substring(0, key.length - textValue.length);
|
||||
const text = trimmed.substring(splitIndex + 1).trim();
|
||||
const c = config[key];
|
||||
if (!c)
|
||||
continue;
|
||||
delete c.readonly;
|
||||
c.text = text;
|
||||
}
|
||||
|
||||
return config;
|
||||
} else {
|
||||
throw new Error('invalid response');
|
||||
// const json = await xml2js.parseStringPromise(body);
|
||||
// return { json, xml: body };
|
||||
}
|
||||
}
|
||||
|
||||
async setVideoTextOverlay(id: string, value: VideoTextOverlay): Promise<void> {
|
||||
// trim the table. off id
|
||||
if (id.startsWith('table.'))
|
||||
id = id.substring('table.'.length);
|
||||
const client = this.getClient();
|
||||
if (value.text) {
|
||||
const enableUrl = `http://${this.getHttpAddress()}/cgi-bin/configManager.cgi?action=setConfig&${id}.EncodeBlend=true&${id}.PreviewBlend=true`;
|
||||
await client.request({
|
||||
method: "GET",
|
||||
url: enableUrl,
|
||||
responseType: "text",
|
||||
});
|
||||
|
||||
const textUrl = `http://${this.getHttpAddress()}/cgi-bin/configManager.cgi?action=setConfig&${id}.Text=${encodeURIComponent(
|
||||
value.text
|
||||
)}`;
|
||||
await client.request({
|
||||
method: "GET",
|
||||
url: textUrl,
|
||||
responseType: "text",
|
||||
});
|
||||
}
|
||||
else {
|
||||
const disableUrl = `http://${this.getHttpAddress()}/cgi-bin/configManager.cgi?action=setConfig&${id}.EncodeBlend=false&${id}.PreviewBlend=false`;
|
||||
await client.request({
|
||||
method: "GET",
|
||||
url: disableUrl,
|
||||
responseType: "text",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async reboot() {
|
||||
const client = this.getClient();
|
||||
await client.reboot();
|
||||
@@ -631,6 +716,7 @@ class AmcrestProvider extends RtspProvider {
|
||||
ScryptedInterface.Camera,
|
||||
ScryptedInterface.AudioSensor,
|
||||
ScryptedInterface.MotionSensor,
|
||||
ScryptedInterface.VideoTextOverlays,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
4
plugins/cloud/package-lock.json
generated
4
plugins/cloud/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/cloud",
|
||||
"version": "0.2.48",
|
||||
"version": "0.2.51",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/cloud",
|
||||
"version": "0.2.48",
|
||||
"version": "0.2.51",
|
||||
"dependencies": {
|
||||
"@eneris/push-receiver": "^4.3.0",
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -52,5 +52,5 @@
|
||||
"@types/node": "^22.10.1",
|
||||
"ts-node": "^10.9.2"
|
||||
},
|
||||
"version": "0.2.48"
|
||||
"version": "0.2.51"
|
||||
}
|
||||
|
||||
@@ -240,8 +240,10 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
upnpClient = upnp.createClient();
|
||||
upnpStatus = 'Starting';
|
||||
randomBytes = crypto.randomBytes(16).toString('base64');
|
||||
healthCheckToken = crypto.randomBytes(16).toString('hex');
|
||||
reverseConnections = new Set<Duplex>();
|
||||
cloudflaredLoginController?: AbortController;
|
||||
healthCheckInterval?: NodeJS.Timeout;
|
||||
|
||||
get portForwardingDisabled() {
|
||||
return this.storageSettings.values.forwardingMode === 'Disabled' || this.storageSettings.values.forwardingMode === 'Default';
|
||||
@@ -852,6 +854,12 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
else if (url.pathname === '/_punch/cloudflared_callback') {
|
||||
res.writeHead(200);
|
||||
res.write(this.healthCheckToken);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
else if (url.pathname === '/web/') {
|
||||
const validDomain = this.getSSLHostname();
|
||||
if (validDomain) {
|
||||
@@ -1046,12 +1054,16 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
args['--url'] = tunnelUrl;
|
||||
}
|
||||
|
||||
// if error messages are detected after 10 minutes from tunnel attempt start,
|
||||
// kill the tunnel.
|
||||
const tenMinutesMs = 10 * 60 * 1000;
|
||||
const tunnelStart = Date.now();
|
||||
const deferred = new Deferred<string>();
|
||||
|
||||
const cloudflareTunnel = cloudflared.tunnel(args);
|
||||
deferred.resolvePromise(cloudflareTunnel.url);
|
||||
|
||||
const processData = (string: string) => {
|
||||
this.console.error(string);
|
||||
|
||||
const lines = string.split('\n');
|
||||
for (const line of lines) {
|
||||
if ((line.includes('Unregistered tunnel connection')
|
||||
@@ -1059,12 +1071,10 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
|| line.includes('Register tunnel error')
|
||||
|| line.includes('Failed to serve tunnel')
|
||||
|| line.includes('Failed to get tunnel'))
|
||||
&& deferred.finished) {
|
||||
this.console.warn('Cloudflare registration failed after tunnel started. The old tunnel may be invalid. Terminating.');
|
||||
&& (deferred.finished || Date.now() - tunnelStart > tenMinutesMs)) {
|
||||
this.console.warn('Cloudflare registration failure detected. Terminating.');
|
||||
cloudflareTunnel.child.kill();
|
||||
}
|
||||
if (line.includes('hostname'))
|
||||
this.console.log(line);
|
||||
const match = /config=(".*?}")/gm.exec(line)
|
||||
if (match) {
|
||||
const json = match[1];
|
||||
@@ -1109,7 +1119,10 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
throw e;
|
||||
}
|
||||
this.console.log(`cloudflare url mapped ${this.cloudflareTunnel} to ${tunnelUrl}`);
|
||||
return cloudflareTunnel;
|
||||
return {
|
||||
url: deferred.promise,
|
||||
child: cloudflareTunnel.child,
|
||||
};
|
||||
}, {
|
||||
startingDelay: 60000,
|
||||
timeMultiple: 1.2,
|
||||
@@ -1117,6 +1130,9 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
maxDelay: 300000,
|
||||
});
|
||||
|
||||
// Start health check after cloudflared is successfully started
|
||||
this.startHealthCheck();
|
||||
|
||||
await once(this.cloudflared.child, 'exit').catch(() => { });
|
||||
// the successfully started cloudflared process may exit at some point, loop and allow it to restart.
|
||||
this.console.error('cloudflared exited');
|
||||
@@ -1126,6 +1142,8 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
this.console.error('cloudflared error', e);
|
||||
}
|
||||
finally {
|
||||
clearInterval(this.healthCheckInterval);
|
||||
this.healthCheckInterval = undefined;
|
||||
this.cloudflared = undefined;
|
||||
this.cloudflareTunnel = undefined;
|
||||
this.updateExternalAddresses();
|
||||
@@ -1133,6 +1151,59 @@ class ScryptedCloud extends ScryptedDeviceBase implements OauthClient, Settings,
|
||||
}
|
||||
}
|
||||
|
||||
async startHealthCheck() {
|
||||
// Clear any existing health check interval
|
||||
if (this.healthCheckInterval) {
|
||||
clearInterval(this.healthCheckInterval);
|
||||
}
|
||||
|
||||
// Local failure counter - only accessible within this method
|
||||
let failureCount = 0;
|
||||
const maxFailuresBeforeRestart = 3;
|
||||
const alertTitle = 'Cloudflared health check failed 3 times consecutively. Restarting cloudflared process.';
|
||||
|
||||
const check = async () => {
|
||||
// Only perform health check if cloudflare is enabled and we have a tunnel URL
|
||||
if (!this.storageSettings.values.cloudflareEnabled || !this.cloudflareTunnel) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const healthCheckUrl = `${this.cloudflareTunnel}/_punch/cloudflared_callback`;
|
||||
this.console.log(`Performing health check: ${healthCheckUrl}`);
|
||||
|
||||
const response = await httpFetch({
|
||||
url: healthCheckUrl,
|
||||
responseType: 'text',
|
||||
timeout: 30000, // 30 second timeout
|
||||
});
|
||||
|
||||
this.log.clearAlert(alertTitle);
|
||||
|
||||
if (response.body !== this.healthCheckToken) {
|
||||
throw new Error(`Health check failed: Expected token ${this.healthCheckToken}, got ${response.body}`);
|
||||
}
|
||||
|
||||
failureCount = 0;
|
||||
this.console.log('Cloudflared health check passed');
|
||||
} catch (error) {
|
||||
failureCount++;
|
||||
this.console.error(`Cloudflared health check failed (${failureCount}/${maxFailuresBeforeRestart}):`, error);
|
||||
|
||||
if (failureCount >= maxFailuresBeforeRestart) {
|
||||
this.console.warn('3 consecutive health check failures detected. Restarting cloudflared process.');
|
||||
this.log.a(alertTitle);
|
||||
this.cloudflared?.child?.kill();
|
||||
|
||||
failureCount = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Start a new health check interval (every 2 minutes)
|
||||
this.healthCheckInterval = setInterval(check, 2 * 60 * 1000); // Run every 2 minutes
|
||||
}
|
||||
|
||||
get serverIdentifier() {
|
||||
const serverIdentifier = `${this.storageSettings.values.registrationSecret}@${this.storageSettings.values.serverId}`;
|
||||
return serverIdentifier;
|
||||
|
||||
58
plugins/core/package-lock.json
generated
58
plugins/core/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.3.102",
|
||||
"version": "0.3.146",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.3.102",
|
||||
"version": "0.3.146",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
@@ -77,6 +77,7 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@scrypted/types": "^0.5.27",
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
@@ -88,28 +89,29 @@
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.100",
|
||||
"version": "0.5.33",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.1",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@rollup/plugin-commonjs": "^28.0.5",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-typescript": "^12.1.1",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@rollup/plugin-virtual": "^3.0.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.7.8",
|
||||
"babel-loader": "^9.2.1",
|
||||
"axios": "^1.10.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-const-enum": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"openai": "^5.3.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.27.4",
|
||||
"rollup": "^4.43.0",
|
||||
"tmp": "^0.2.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-loader": "^9.5.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.96.1",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.9",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
},
|
||||
"bin": {
|
||||
@@ -122,9 +124,9 @@
|
||||
"scrypted-webpack": "bin/scrypted-webpack.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.10.1",
|
||||
"@types/node": "^24.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typedoc": "^0.26.11"
|
||||
"typedoc": "^0.28.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/common": {
|
||||
@@ -276,6 +278,7 @@
|
||||
"version": "file:../../common",
|
||||
"requires": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@scrypted/types": "^0.5.27",
|
||||
"@types/node": "^20.11.0",
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"monaco-editor": "^0.50.0",
|
||||
@@ -286,28 +289,29 @@
|
||||
"@scrypted/sdk": {
|
||||
"version": "file:../../sdk",
|
||||
"requires": {
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.1",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@rollup/plugin-commonjs": "^28.0.5",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-typescript": "^12.1.1",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@rollup/plugin-virtual": "^3.0.2",
|
||||
"@types/node": "^22.10.1",
|
||||
"@types/node": "^24.0.1",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.7.8",
|
||||
"babel-loader": "^9.2.1",
|
||||
"axios": "^1.10.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-const-enum": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"openai": "^5.3.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.27.4",
|
||||
"rollup": "^4.43.0",
|
||||
"tmp": "^0.2.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-loader": "^9.5.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typedoc": "^0.26.11",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.96.1",
|
||||
"typedoc": "^0.28.5",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.9",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.3.102",
|
||||
"version": "0.3.146",
|
||||
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
@@ -33,10 +33,6 @@
|
||||
"ScryptedSettings",
|
||||
"SystemSettings",
|
||||
"Settings"
|
||||
],
|
||||
"pluginDependencies": [
|
||||
"@scrypted/snapshot",
|
||||
"@scrypted/webrtc"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -72,7 +72,6 @@ function createVideoCamera(devices: VideoCamera[], console: Console): VideoCamer
|
||||
container: 'rawvideo',
|
||||
mediaStreamOptions: (await createVideoStreamOptions())?.[0],
|
||||
inputArguments: [],
|
||||
h264FilterArguments: [],
|
||||
};
|
||||
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
@@ -102,7 +101,7 @@ function createVideoCamera(devices: VideoCamera[], console: Console): VideoCamer
|
||||
let i = dim * dim - 1;
|
||||
filter.push(`[${prev}][pos${i}] overlay=shortest=1:x=${curx % 1920}:y=${cury % 1080}`);
|
||||
|
||||
ffmpegInput.h264FilterArguments.push(
|
||||
ffmpegInput.inputArguments.push(
|
||||
'-filter_complex',
|
||||
filter.join(' '),
|
||||
);
|
||||
|
||||
@@ -155,6 +155,8 @@ export class ClusterCore extends ScryptedDeviceBase implements Settings, Readme,
|
||||
}
|
||||
|
||||
async getReadmeMarkdown(): Promise<string> {
|
||||
return `Manage Scrypted's cluster mode. Run storage devices and compute services on separate servers.`;
|
||||
return `Manage Scrypted's cluster mode. Run storage devices and compute services on separate servers.
|
||||
|
||||
[Read Documentation](https://docs.scrypted.app/maintenance/cluster.html).`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DeviceState, MixinProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk";
|
||||
import { typeToIcon } from "../../../../manage.scrypted.app/src/device-icons";
|
||||
import { typeToIcon } from "../../../../manage.scrypted.app/src/util/device-icons";
|
||||
|
||||
export class LauncherMixin extends ScryptedDeviceBase implements MixinProvider, Readme {
|
||||
async getReadmeMarkdown(): Promise<string> {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { readFileAsString, tsCompile } from '@scrypted/common/src/eval/scrypted-eval';
|
||||
import { sleep } from '@scrypted/common/src/sleep';
|
||||
import sdk, { DeviceProvider, HttpRequest, HttpRequestHandler, HttpResponse, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting, SettingValue, Settings } from '@scrypted/sdk';
|
||||
import { StorageSettings } from "@scrypted/sdk/storage-settings";
|
||||
import { writeFileSync } from 'fs';
|
||||
@@ -8,14 +9,14 @@ import yaml from 'yaml';
|
||||
import { getUsableNetworkAddresses } from '../../../server/src/ip';
|
||||
import { AggregateCore, AggregateCoreNativeId } from './aggregate-core';
|
||||
import { AutomationCore, AutomationCoreNativeId } from './automations-core';
|
||||
import { ClusterCore, ClusterCoreNativeId } from './cluster';
|
||||
import { LauncherMixin } from './launcher-mixin';
|
||||
import { MediaCore } from './media-core';
|
||||
import { checkLegacyLxc, checkLxc } from './platform/lxc';
|
||||
import { checkLegacyLxc, checkLxc, checkLxcVersionUpdateNeeded } from './platform/lxc';
|
||||
import { ConsoleServiceNativeId, PluginSocketService, ReplServiceNativeId } from './plugin-socket-service';
|
||||
import { ScriptCore, ScriptCoreNativeId, newScript } from './script-core';
|
||||
import { TerminalService, TerminalServiceNativeId } from './terminal-service';
|
||||
import { TerminalService, TerminalServiceNativeId, newTerminalService } from './terminal-service';
|
||||
import { UsersCore, UsersNativeId } from './user';
|
||||
import { ClusterCore, ClusterCoreNativeId } from './cluster';
|
||||
|
||||
const { deviceManager, endpointManager } = sdk;
|
||||
|
||||
@@ -63,7 +64,10 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
'Default',
|
||||
'latest',
|
||||
'beta',
|
||||
`v${sdk.serverVersion}-jammy-full`,
|
||||
'intel',
|
||||
'amd',
|
||||
'nvidia',
|
||||
`v${sdk.serverVersion}-noble-full`,
|
||||
],
|
||||
combobox: true,
|
||||
onPut: (ov, nv) => {
|
||||
@@ -108,7 +112,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
(async () => {
|
||||
await deviceManager.onDeviceDiscovered(
|
||||
{
|
||||
name: 'Cluster',
|
||||
name: 'Cluster Manager',
|
||||
nativeId: ClusterCoreNativeId,
|
||||
interfaces: [ScryptedInterface.Settings, ScryptedInterface.Readme, ScryptedInterface.ScryptedSettings],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
@@ -121,7 +125,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
name: 'Media Core',
|
||||
nativeId: 'mediacore',
|
||||
interfaces: [ScryptedInterface.DeviceProvider, ScryptedInterface.BufferConverter, ScryptedInterface.HttpRequestHandler],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
type: ScryptedDeviceType.Internal,
|
||||
},
|
||||
);
|
||||
})();
|
||||
@@ -131,7 +135,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
name: 'Scripts',
|
||||
nativeId: ScriptCoreNativeId,
|
||||
interfaces: [ScryptedInterface.ScryptedSystemDevice, ScryptedInterface.ScryptedDeviceCreator, ScryptedInterface.DeviceProvider, ScryptedInterface.DeviceCreator, ScryptedInterface.Readme],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
type: ScryptedDeviceType.Internal,
|
||||
},
|
||||
);
|
||||
})();
|
||||
@@ -140,8 +144,8 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
{
|
||||
name: 'Terminal Service',
|
||||
nativeId: TerminalServiceNativeId,
|
||||
interfaces: [ScryptedInterface.StreamService, ScryptedInterface.TTY],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
interfaces: [ScryptedInterface.StreamService, ScryptedInterface.TTY, ScryptedInterface.ClusterForkInterface],
|
||||
type: ScryptedDeviceType.Internal,
|
||||
},
|
||||
);
|
||||
})();
|
||||
@@ -151,7 +155,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
name: 'REPL Service',
|
||||
nativeId: ReplServiceNativeId,
|
||||
interfaces: [ScryptedInterface.StreamService],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
type: ScryptedDeviceType.Internal,
|
||||
},
|
||||
);
|
||||
})();
|
||||
@@ -161,7 +165,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
name: 'Console Service',
|
||||
nativeId: ConsoleServiceNativeId,
|
||||
interfaces: [ScryptedInterface.StreamService],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
type: ScryptedDeviceType.Internal,
|
||||
},
|
||||
);
|
||||
})();
|
||||
@@ -172,7 +176,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
name: 'Automations',
|
||||
nativeId: AutomationCoreNativeId,
|
||||
interfaces: [ScryptedInterface.ScryptedSystemDevice, ScryptedInterface.ScryptedDeviceCreator, ScryptedInterface.DeviceProvider, ScryptedInterface.DeviceCreator, ScryptedInterface.Readme],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
type: ScryptedDeviceType.Internal,
|
||||
},
|
||||
);
|
||||
})();
|
||||
@@ -185,7 +189,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
ScryptedInterface.MixinProvider,
|
||||
ScryptedInterface.Readme,
|
||||
],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
type: ScryptedDeviceType.Internal,
|
||||
});
|
||||
|
||||
(async () => {
|
||||
@@ -206,10 +210,73 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
name: 'Scrypted Users',
|
||||
nativeId: UsersNativeId,
|
||||
interfaces: [ScryptedInterface.ScryptedSystemDevice, ScryptedInterface.ScryptedDeviceCreator, ScryptedInterface.DeviceProvider, ScryptedInterface.DeviceCreator, ScryptedInterface.Readme],
|
||||
type: ScryptedDeviceType.Builtin,
|
||||
type: ScryptedDeviceType.Internal,
|
||||
},
|
||||
);
|
||||
})();
|
||||
|
||||
// check on workers immediately and once an hour.
|
||||
this.updateWorkers();
|
||||
setInterval(() => this.updateWorkers(), 60 * 1000 * 60);
|
||||
|
||||
// check on worker images once an hour.
|
||||
// checking immediately is problematic as a failed update may cause a restart loop on startup.
|
||||
// images are also pruned 1 minute after startup, so avoid that.
|
||||
setInterval(() => this.updateWorkerImages(), 60 * 1000 * 60);
|
||||
}
|
||||
|
||||
async updateWorkers() {
|
||||
const workers = await sdk.clusterManager?.getClusterWorkers();
|
||||
if (!workers)
|
||||
return;
|
||||
for (const [id, worker] of Object.entries(workers)) {
|
||||
const forked = sdk.fork<ReturnType<typeof fork>>({
|
||||
clusterWorkerId: id,
|
||||
runtime: 'node',
|
||||
});
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const result = await forked.result;
|
||||
result.checkLxc();
|
||||
}
|
||||
catch (e) {
|
||||
forked.worker.terminate();
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
async updateWorkerImages() {
|
||||
const workers = await sdk.clusterManager?.getClusterWorkers();
|
||||
if (!workers)
|
||||
return;
|
||||
for (const [id, worker] of Object.entries(workers)) {
|
||||
const forked = sdk.fork<ReturnType<typeof fork>>({
|
||||
clusterWorkerId: id,
|
||||
runtime: 'node',
|
||||
});
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const result = await forked.result;
|
||||
if (!await result.checkLxcVersionUpdateNeeded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// restart the worker to pick up the new image.
|
||||
const clusterFork = await sdk.systemManager.getComponent('cluster-fork');
|
||||
const serviceControl = await clusterFork.getServiceControl(worker.id);
|
||||
await serviceControl.restart().catch(() => { });
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
finally {
|
||||
await sleep(1000);
|
||||
forked.worker.terminate();
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
async getSettings(): Promise<Setting[]> {
|
||||
@@ -242,7 +309,7 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
if (nativeId === UsersNativeId)
|
||||
return this.users ||= new UsersCore();
|
||||
if (nativeId === TerminalServiceNativeId)
|
||||
return this.terminalService ||= new TerminalService();
|
||||
return this.terminalService ||= new TerminalService(TerminalServiceNativeId, false);
|
||||
if (nativeId === ReplServiceNativeId)
|
||||
return this.replService ||= new PluginSocketService(ReplServiceNativeId, 'repl');
|
||||
if (nativeId === ConsoleServiceNativeId)
|
||||
@@ -315,7 +382,6 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Dev
|
||||
const dockerCompose = yaml.parseDocument(readFileAsString('/root/.scrypted/docker-compose.yml'));
|
||||
// @ts-ignore
|
||||
dockerCompose.contents.get('services').get('scrypted').set('image', `ghcr.io/koush/scrypted${releaseChannel}`);
|
||||
yaml.stringify(dockerCompose);
|
||||
writeFileSync('/root/.scrypted/docker-compose.yml', yaml.stringify(dockerCompose));
|
||||
this.setPullImage();
|
||||
|
||||
@@ -331,5 +397,17 @@ export async function fork() {
|
||||
return {
|
||||
tsCompile,
|
||||
newScript,
|
||||
newTerminalService,
|
||||
checkLxcVersionUpdateNeeded,
|
||||
checkLxc: async () => {
|
||||
try {
|
||||
// console.warn('Checking for LXC installation...');
|
||||
await checkLxc();
|
||||
}
|
||||
finally {
|
||||
await sleep(1000);
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import fs from 'fs';
|
||||
import { Deferred } from '@scrypted/common/src/deferred';
|
||||
import { readFileAsString } from '@scrypted/common/src/eval/scrypted-eval';
|
||||
import sdk from '@scrypted/sdk';
|
||||
import fs, { writeFileSync } from 'fs';
|
||||
import http from 'http';
|
||||
import yaml from 'yaml';
|
||||
|
||||
export const SCRYPTED_INSTALL_ENVIRONMENT_LXC = 'lxc';
|
||||
export const SCRYPTED_INSTALL_ENVIRONMENT_LXC_DOCKER = 'lxc-docker';
|
||||
@@ -8,7 +12,7 @@ export async function checkLegacyLxc() {
|
||||
if (process.env.SCRYPTED_INSTALL_ENVIRONMENT !== SCRYPTED_INSTALL_ENVIRONMENT_LXC)
|
||||
return;
|
||||
|
||||
sdk.log.a('This system is currently running the legacy LXC installation method and must be migrated to the new LXC manually: https://docs.scrypted.app/installation.html#proxmox-ve-container-reset');
|
||||
sdk.log.a('This system is currently running the legacy LXC installation method and must be migrated to the new LXC manually: https://docs.scrypted.app/install/proxmox-ve.html#proxmox-ve-container-reset');
|
||||
}
|
||||
|
||||
const DOCKER_COMPOSE_SH_PATH = '/root/.scrypted/docker-compose.sh';
|
||||
@@ -18,6 +22,119 @@ export async function checkLxc() {
|
||||
if (process.env.SCRYPTED_INSTALL_ENVIRONMENT !== SCRYPTED_INSTALL_ENVIRONMENT_LXC_DOCKER)
|
||||
return;
|
||||
|
||||
await checkLxcCompose();
|
||||
await checkLxcScript();
|
||||
}
|
||||
|
||||
|
||||
async function dockerRequest(options: http.RequestOptions, body?: string) {
|
||||
const deferred = new Deferred<string>();
|
||||
|
||||
const req = http.request({
|
||||
socketPath: '/var/run/docker.sock',
|
||||
method: options.method,
|
||||
path: options.path,
|
||||
headers: {
|
||||
'Host': 'localhost',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
req.on('response', (res) => {
|
||||
let data = '';
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
res.on('end', () => {
|
||||
deferred.resolve(data);
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (err) => {
|
||||
deferred.reject(err);
|
||||
});
|
||||
|
||||
if (body) {
|
||||
req.write(body);
|
||||
}
|
||||
|
||||
req.end();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
async function dockerPullScryptedTag(tag: string) {
|
||||
return dockerRequest({
|
||||
method: 'POST',
|
||||
path: `/v1.41/images/create?fromImage=ghcr.io%2Fkoush%2Fscrypted&tag=${tag}`,
|
||||
});
|
||||
}
|
||||
|
||||
async function dockerImageLsScryptedTag(tag: string) {
|
||||
// List all images and find the specific one
|
||||
const data = await dockerRequest({
|
||||
method: 'GET',
|
||||
path: '/v1.41/images/json'
|
||||
});
|
||||
const images = JSON.parse(data);
|
||||
// Filter for your specific image
|
||||
const targetImage = images.find(image => {
|
||||
return image.RepoTags && image.RepoTags.some(t =>
|
||||
t === `ghcr.io/koush/scrypted:${tag}`
|
||||
);
|
||||
});
|
||||
if (!targetImage) {
|
||||
throw new Error('Image not found');
|
||||
}
|
||||
|
||||
return targetImage.Id;
|
||||
}
|
||||
|
||||
async function dockerGetScryptedContainerImageId() {
|
||||
// List running containers filtered by name
|
||||
const data = await dockerRequest({
|
||||
method: 'GET',
|
||||
path: '/v1.41/containers/json?filters={"name":["scrypted"],"status":["running"]}'
|
||||
});
|
||||
const containers = JSON.parse(data);
|
||||
if (!containers.length)
|
||||
throw new Error('No running container named "scrypted" found');
|
||||
const container = containers[0];
|
||||
return container.ImageID;
|
||||
}
|
||||
|
||||
export async function checkLxcVersionUpdateNeeded() {
|
||||
if (process.env.SCRYPTED_INSTALL_ENVIRONMENT !== SCRYPTED_INSTALL_ENVIRONMENT_LXC_DOCKER)
|
||||
return;
|
||||
|
||||
const dockerCompose = yaml.parseDocument(readFileAsString('/root/.scrypted/docker-compose.yml'));
|
||||
// @ts-ignore
|
||||
const image: string = dockerCompose.contents.get('services').get('scrypted').get('image');
|
||||
const label = image.split(':')[1] || 'latest';
|
||||
|
||||
await dockerPullScryptedTag(label);
|
||||
const imageId = await dockerImageLsScryptedTag(label);
|
||||
const containerImageId = await dockerGetScryptedContainerImageId();
|
||||
console.warn('LXC Scrypted latest image ID:', imageId);
|
||||
console.warn('LXC Scrypted running image ID:', containerImageId);
|
||||
return containerImageId !== imageId;
|
||||
}
|
||||
|
||||
async function checkLxcCompose() {
|
||||
// the lxc-docker used watchtower for automatic updates but watchtower started crashing in the lxc environment
|
||||
// after a docker update.
|
||||
// watchtower was removed from the lxc as a result.
|
||||
// however existing installations may still have watchtower in their docker-compose.yml and need it removed.
|
||||
const dockerCompose = yaml.parseDocument(readFileAsString('/root/.scrypted/docker-compose.yml'));
|
||||
// @ts-ignore
|
||||
const watchtower = dockerCompose.contents.get('services').get('watchtower');
|
||||
if (watchtower.get('profiles'))
|
||||
return;
|
||||
watchtower.set('profiles', ['disabled']);
|
||||
writeFileSync('/root/.scrypted/docker-compose.yml', yaml.stringify(dockerCompose));
|
||||
}
|
||||
|
||||
async function checkLxcScript() {
|
||||
const foundDockerComposeSh = await fs.promises.readFile(DOCKER_COMPOSE_SH_PATH, 'utf8');
|
||||
const dockerComposeSh = await fs.promises.readFile(LXC_DOCKER_COMPOSE_SH_PATH, 'utf8');
|
||||
|
||||
@@ -30,6 +147,8 @@ export async function checkLxc() {
|
||||
return;
|
||||
}
|
||||
|
||||
// console.warn('lxc needs updating', sdk.clusterManager.getClusterWorkerId());
|
||||
// console.warn(foundDockerComposeSh);
|
||||
await fs.promises.copyFile(LXC_DOCKER_COMPOSE_SH_PATH, DOCKER_COMPOSE_SH_PATH);
|
||||
await fs.promises.chmod(DOCKER_COMPOSE_SH_PATH, 0o755);
|
||||
}
|
||||
}
|
||||
@@ -93,7 +93,7 @@ export class ScriptCore extends ScryptedDeviceBase implements DeviceProvider, De
|
||||
let script = new Script(nativeId);
|
||||
let worker: ForkWorker;
|
||||
|
||||
const triggerDeviceDiscover = async (name: string, type: ScryptedDeviceType, interfaces: string[]) => {
|
||||
const triggerDeviceDiscover = async (name: string, type: ScryptedDeviceType | string, interfaces: string[]) => {
|
||||
const e = this.scripts.get(nativeId);
|
||||
if (e?.script == script)
|
||||
e.script = undefined;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ScriptCoreNativeId } from "./script-core";
|
||||
const { deviceManager } = sdk;
|
||||
|
||||
export class Script extends ScryptedDeviceBase implements Scriptable, Program, ScriptDeviceImpl {
|
||||
constructor(nativeId: string, public triggerDeviceDiscover?: (name: string, type: ScryptedDeviceType, interfaces: string[]) => Promise<string>) {
|
||||
constructor(nativeId: string, public triggerDeviceDiscover?: (name: string, type: ScryptedDeviceType | string, interfaces: string[]) => Promise<string>) {
|
||||
super(nativeId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import sdk, { ScryptedDeviceBase, ScryptedInterface, ScryptedNativeId, StreamService, TTYSettings } from "@scrypted/sdk";
|
||||
import type { IPty, spawn as ptySpawn } from 'node-pty';
|
||||
import { createAsyncQueue } from '@scrypted/common/src/async-queue'
|
||||
import { createAsyncQueue } from '@scrypted/common/src/async-queue';
|
||||
import sdk, { ClusterForkInterface, ClusterForkInterfaceOptions, ScryptedDeviceBase, ScryptedInterface, ScryptedNativeId, StreamService, TTY, TTYSettings } from "@scrypted/sdk";
|
||||
import { ChildProcess, spawn as childSpawn } from "child_process";
|
||||
import type { IPty, spawn as ptySpawn } from 'node-pty';
|
||||
import path from 'path';
|
||||
|
||||
export const TerminalServiceNativeId = 'terminalservice';
|
||||
@@ -19,12 +19,24 @@ function toSpawnPathEnv(paths: string[]): string {
|
||||
class InteractiveTerminal {
|
||||
cp: IPty
|
||||
|
||||
constructor(cmd: string[], paths: string[], spawn: typeof ptySpawn) {
|
||||
constructor(cmd: string[], paths: string[], spawn: typeof ptySpawn, cwd?: string) {
|
||||
const spawnPath = toSpawnPathEnv(paths);
|
||||
if (cmd?.length) {
|
||||
this.cp = spawn(cmd[0], cmd.slice(1), { env: { ...process.env, PATH: spawnPath } });
|
||||
this.cp = spawn(cmd[0], cmd.slice(1), {
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: spawnPath,
|
||||
},
|
||||
cwd,
|
||||
});
|
||||
} else {
|
||||
this.cp = spawn(process.env.SHELL as string, [], { env: { ...process.env, PATH: spawnPath } });
|
||||
this.cp = spawn(process.env.SHELL as string, [], {
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: spawnPath,
|
||||
},
|
||||
cwd,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +123,11 @@ class NoninteractiveTerminal {
|
||||
}
|
||||
|
||||
|
||||
export class TerminalService extends ScryptedDeviceBase implements StreamService<Buffer | string, Buffer> {
|
||||
constructor(nativeId?: ScryptedNativeId) {
|
||||
export class TerminalService extends ScryptedDeviceBase implements StreamService<Buffer | string, Buffer>, ClusterForkInterface, TTY {
|
||||
private forks: { [clusterWorkerId: string]: TerminalService } = {};
|
||||
private forkClients: 0;
|
||||
|
||||
constructor(nativeId?: ScryptedNativeId, private isFork: boolean = false) {
|
||||
super(nativeId);
|
||||
}
|
||||
|
||||
@@ -134,6 +149,42 @@ export class TerminalService extends ScryptedDeviceBase implements StreamService
|
||||
return extraPaths;
|
||||
}
|
||||
|
||||
async forkInterface<StreamService>(forkInterface: ScryptedInterface, options?: ClusterForkInterfaceOptions): Promise<StreamService> {
|
||||
if (forkInterface !== ScryptedInterface.StreamService) {
|
||||
throw new Error('can only fork StreamService');
|
||||
}
|
||||
|
||||
if (!options?.clusterWorkerId) {
|
||||
throw new Error('clusterWorkerId required');
|
||||
}
|
||||
|
||||
if (this.isFork) {
|
||||
throw new Error('cannot fork a fork');
|
||||
}
|
||||
|
||||
const clusterWorkerId = options.clusterWorkerId;
|
||||
if (this.forks[clusterWorkerId]) {
|
||||
return this.forks[clusterWorkerId] as StreamService;
|
||||
}
|
||||
|
||||
const fork = sdk.fork<{
|
||||
newTerminalService: typeof newTerminalService,
|
||||
}>({ clusterWorkerId });
|
||||
try {
|
||||
const result = await fork.result;
|
||||
const terminalService = await result.newTerminalService();
|
||||
this.forks[clusterWorkerId] = terminalService;
|
||||
fork.worker.on('exit', () => {
|
||||
delete this.forks[clusterWorkerId];
|
||||
});
|
||||
return terminalService as StreamService;
|
||||
}
|
||||
catch (e) {
|
||||
fork.worker.terminate();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The input to this stream can send buffers for normal terminal data and strings
|
||||
* for control messages. Control messages are JSON-formatted.
|
||||
@@ -147,7 +198,20 @@ export class TerminalService extends ScryptedDeviceBase implements StreamService
|
||||
async connectStream(input: AsyncGenerator<Buffer | string, void>, options?: any): Promise<AsyncGenerator<Buffer, void>> {
|
||||
let cp: InteractiveTerminal | NoninteractiveTerminal = null;
|
||||
const queue = createAsyncQueue<Buffer>();
|
||||
const extraPaths = await this.getExtraPaths();
|
||||
const extraPaths = [...options?.env?.PATH?.split(path.delimiter) || [], ...await this.getExtraPaths()];
|
||||
|
||||
if (this.isFork) {
|
||||
this.forkClients++;
|
||||
}
|
||||
|
||||
queue.endPromise.then(() => {
|
||||
if (this.isFork) {
|
||||
this.forkClients--;
|
||||
if (this.forkClients === 0) {
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function registerChildListeners() {
|
||||
cp.onExit(() => queue.end());
|
||||
@@ -207,7 +271,7 @@ export class TerminalService extends ScryptedDeviceBase implements StreamService
|
||||
let spawn: typeof ptySpawn;
|
||||
try {
|
||||
spawn = require('@scrypted/node-pty').spawn as typeof ptySpawn;
|
||||
cp = new InteractiveTerminal(cmd, extraPaths, spawn);
|
||||
cp = new InteractiveTerminal(cmd, extraPaths, spawn, options?.cwd);
|
||||
}
|
||||
catch (e) {
|
||||
this.console.error('Error starting pty', e);
|
||||
@@ -226,10 +290,16 @@ export class TerminalService extends ScryptedDeviceBase implements StreamService
|
||||
}
|
||||
catch (e) {
|
||||
this.console.log(e);
|
||||
}
|
||||
finally {
|
||||
cp?.kill();
|
||||
}
|
||||
})();
|
||||
|
||||
return generator();
|
||||
}
|
||||
}
|
||||
|
||||
export async function newTerminalService(): Promise<TerminalService> {
|
||||
return new TerminalService(TerminalServiceNativeId, true);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import sdk, { DeviceCreator, DeviceCreatorSettings, DeviceProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedUser, ScryptedUserAccessControl, Setting, Settings, SettingValue } from "@scrypted/sdk";
|
||||
import sdk, { DeviceCreator, DeviceCreatorSettings, DeviceManifest, DeviceProvider, Readme, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedUser, ScryptedUserAccessControl, Setting, Settings, SettingValue } from "@scrypted/sdk";
|
||||
import { addAccessControlsForInterface } from "@scrypted/sdk/acl";
|
||||
import { StorageSettings } from "@scrypted/sdk/storage-settings";
|
||||
export const UsersNativeId = 'users';
|
||||
@@ -111,7 +111,7 @@ export class User extends ScryptedDeviceBase implements Settings, ScryptedUser {
|
||||
const { username, admin } = user;
|
||||
const nativeId = `user:${username}`;
|
||||
const aclId = await sdk.deviceManager.onDeviceDiscovered({
|
||||
providerNativeId: this.nativeId,
|
||||
providerNativeId: UsersNativeId,
|
||||
name: username.toString(),
|
||||
nativeId,
|
||||
interfaces: [
|
||||
@@ -132,7 +132,13 @@ export class UsersCore extends ScryptedDeviceBase implements Readme, DeviceProvi
|
||||
deviceCreator: 'Scrypted User',
|
||||
};
|
||||
|
||||
this.syncUsers();
|
||||
this.syncUsers()
|
||||
.then(length => {
|
||||
if (!length) {
|
||||
this.console.log('no users found, looping for first user');
|
||||
setInterval(() => this.syncUsers(), 60 * 1000);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async getDevice(nativeId: string): Promise<any> {
|
||||
@@ -192,7 +198,7 @@ export class UsersCore extends ScryptedDeviceBase implements Readme, DeviceProvi
|
||||
async syncUsers() {
|
||||
const usersService = await sdk.systemManager.getComponent('users');
|
||||
const users: DBUser[] = await usersService.getAllUsers();
|
||||
await sdk.deviceManager.onDevicesChanged({
|
||||
const manifest: DeviceManifest = {
|
||||
providerNativeId: this.nativeId,
|
||||
devices: users.map(user => ({
|
||||
name: user.username,
|
||||
@@ -203,6 +209,16 @@ export class UsersCore extends ScryptedDeviceBase implements Readme, DeviceProvi
|
||||
],
|
||||
type: ScryptedDeviceType.Person,
|
||||
})),
|
||||
})
|
||||
};
|
||||
const nativeIds = new Set(manifest.devices.map(d => d.nativeId));
|
||||
for (const nativeId of sdk.deviceManager.getNativeIds()) {
|
||||
nativeIds.delete(nativeId);
|
||||
}
|
||||
if (nativeIds.size) {
|
||||
// add any missing users.
|
||||
await sdk.deviceManager.onDevicesChanged(manifest);
|
||||
}
|
||||
|
||||
return manifest.devices.length;
|
||||
}
|
||||
}
|
||||
|
||||
58
plugins/coreml/package-lock.json
generated
58
plugins/coreml/package-lock.json
generated
@@ -1,34 +1,42 @@
|
||||
{
|
||||
"name": "@scrypted/coreml",
|
||||
"version": "0.1.76",
|
||||
"version": "0.1.89",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/coreml",
|
||||
"version": "0.1.76",
|
||||
"version": "0.1.89",
|
||||
"devDependencies": {
|
||||
"@scrypted/sdk": "file:../../sdk"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.77",
|
||||
"version": "0.5.22",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@rollup/plugin-commonjs": "^28.0.5",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@rollup/plugin-virtual": "^3.0.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.7.7",
|
||||
"babel-loader": "^9.2.1",
|
||||
"axios": "^1.10.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-const-enum": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"openai": "^5.3.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.43.0",
|
||||
"tmp": "^0.2.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.5.4",
|
||||
"webpack": "^5.95.0",
|
||||
"ts-loader": "^9.5.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.9",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
},
|
||||
"bin": {
|
||||
@@ -41,11 +49,9 @@
|
||||
"scrypted-webpack": "bin/scrypted-webpack.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.8.1",
|
||||
"@types/stringify-object": "^4.0.5",
|
||||
"stringify-object": "^3.3.0",
|
||||
"@types/node": "^24.0.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typedoc": "^0.26.10"
|
||||
"typedoc": "^0.28.5"
|
||||
}
|
||||
},
|
||||
"../sdk": {
|
||||
@@ -60,23 +66,29 @@
|
||||
"@scrypted/sdk": {
|
||||
"version": "file:../../sdk",
|
||||
"requires": {
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@types/node": "^22.8.1",
|
||||
"@types/stringify-object": "^4.0.5",
|
||||
"@babel/preset-typescript": "^7.27.1",
|
||||
"@rollup/plugin-commonjs": "^28.0.5",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@rollup/plugin-virtual": "^3.0.2",
|
||||
"@types/node": "^24.0.1",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.7.7",
|
||||
"babel-loader": "^9.2.1",
|
||||
"axios": "^1.10.0",
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-const-enum": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"openai": "^5.3.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^6.0.1",
|
||||
"stringify-object": "^3.3.0",
|
||||
"rollup": "^4.43.0",
|
||||
"tmp": "^0.2.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"ts-loader": "^9.5.2",
|
||||
"ts-node": "^10.9.2",
|
||||
"typedoc": "^0.26.10",
|
||||
"typescript": "^5.5.4",
|
||||
"webpack": "^5.95.0",
|
||||
"tslib": "^2.8.1",
|
||||
"typedoc": "^0.28.5",
|
||||
"typescript": "^5.8.3",
|
||||
"webpack": "^5.99.9",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user