updated docker to include janusstack/xrfragments repos + added tests + updated hooks

This commit is contained in:
Leon van Kammen 2025-12-09 18:11:17 +01:00
parent fcc6a4111e
commit cdeb836d88
24 changed files with 1770 additions and 50 deletions

View file

@ -53,6 +53,7 @@ $ docker run -t xrforge docker.io/coderofsalvation/xrforge:latest -v ./config:/c
| `APPNAME` | `XRForge` | manyfold instance name |
| `HOMEPAGE` | `/models` | show '/models' URL as homepage (use `/` for manyfold default) |
| `THEME` | `default` | bootstrap theme |
| 'JANUSXR' | `` | run local JanusXR stack (janus-server, janus-gateway, janusweb) |
| `AFRAME_VERSION` | `1.7.0` | AFRAME version |
| `GODOT_VERSION` | `4.4.1-stable`| godot editor version |
| `GODOT_TEMPLATE_ZIP` | `` | godot template zip URL or file (default is empty godot project) |
@ -188,3 +189,32 @@ For a quick dev-environment run:
$ mkdir /dev
$ manyfold/cli/manyfold.sh run -e DEV=1
```
# JanusXR
When running xrforge with the `JANUSXR=1` env-flag, the opensource [JanusXR](https://janusxr.org) stack will be installed and started:
* [janus-server](https://github.com/janusvr/janus-server) for chat + syncing avatar positions
* [janus-gateway](https://github.com/meetecho/janus-gateway) for video/audio
* [janusweb](https://github.com/meetecho/janus-gateway) the viewer using the above services
> NOTE: consider this a fingers-crossed 'rolling release' installation, as this is not officially part of XRForge (just a helper for intranets).
Note that janus-server exposes a http websocket at port 5566, so you need to configure your reverse proxy as following:
* https://presence.foo.bar.com => 5566
> This assumes environment-var `FEDERATE_DRIVE_HOST` is set to `https://foo.bar.com` (`presence` subdomain is automatically prefixed by the installer)
#### persist JanusXR stack
When running the container run the following cmds to speed up the boot-time:
```
$ docker cp xrforge:/mnt/janusweb .
$ docker cp xrforge:/root/janus-server .
```
then add the following flags to your docker cmd: `-v ./janusweb:/mnt/janusweb -v ./janus-server:/root/janus-server`
> That way JanusXR does not have to be installed every time during boot

View file

@ -28,7 +28,8 @@ run(){
#-e NO_DEFAULTDB=true \
#-e PUBLIC_HOSTNAME=localhost \
#-e PUBLIC_PORT=80 \
echo ${oci} run "$@" -p 8790:3214 -p 8791:3215 --name xrforge \
#-e JANUSXR=1 \
echo ${oci} run "$@" -p 8790:3214 -p 8791:3215 -p 5566:5566 -p 5577:5577 --name xrforge \
-e SECRET_KEY_BASE=lkjwljlkwejrlkjek34k234l \
-e DATABASE_ADAPTER=sqlite3 \
-e FEDERATE_DRIVE_HOST=http://localhost:8791 \
@ -49,6 +50,7 @@ overlayfs(){
echocolor "[$APPNAME]" "applying filesystem overlay"
cd /manyfold
rsync -rvzi * /.
#apply_patches
}
# cron-like function using sleep (./manifold.sh infinite 3600 zip -r /backup.zip /)
@ -99,6 +101,20 @@ start_hook_daemon(){
#find /mnt | grep datapackage | xargs -n1 $0 hook inotify_MODIFY
}
apply_patches(){
echocolor "[$APPNAME]" "applying patches"
for patch_file in /manyfold/patches/*.patch; do
if patch -p1 -N --forward < "$patch_file"; then
echo "✅ Successfully applied **$(basename "$patch_file")**"
else
echo "🛑 Failed to apply **$(basename "$patch_file")**"
echo "Aborting script. Please inspect the failed patch and resolve conflicts."
# Use 'exit' to stop the script on the first failure
exit 1
fi
done
}
db(){
default(){
@ -133,7 +149,7 @@ set_upload_path(){
}
mount_dir(){
find /mnt -type d -mindepth 1 -maxdepth 1 | while read dir; do
find /mnt -type d -mindepth 1 -maxdepth 1 | grep -v janusweb | while read dir; do
echocolor "[$APPNAME]" "mounting $dir as library"
add_lib_to_db "$dir"
done
@ -235,21 +251,49 @@ force_public(){
infinite 60 rails_query 'Model.find_each { |it| it.grant_permission_to("view", nil) }' &
}
get_xrfragment_assets(){
import_assets(){
test -n "$NO_ASSETS" && return 0 # nothing to do here
test -d /mnt/asset || {
echocolor "fetching XR Fragments asset & templates"
mkdir -p /mnt/asset/xrfragments /mnt/templates/xrfragments
cd /tmp
timeout 50 wget "https://codeberg.org/coderofsalvation/xrfragment/archive/main.zip"
unzip main.zip
cp -r xrfragment/assets/library /mnt/asset/xrfragments/\#1
find xrfragment/assets/template -maxdepth 1 -mindepth 1 -type d | awk '{ system("cp -r "$0" /mnt/templates/xrfragments/#"(NR+1)) }'
}
add_lib_to_db /mnt/asset
add_lib_to_db /mnt/templates
}
janusxr(){
start_server(){
while sleep 2s; do
flock -n "$@"
echocolor "[$1]" "'$2 $3 $4' exited (why?)...restarting"
done
}
cd /root/corsanywhere
npm install
PORT=5577 start_server ~/.run-corsanywhere node server.js &
cd /root
chmod +x janus_server-linux
PORT=5566 start_server ~/.run-janus-server ./janus_server-linux &
## we should do this in nix/docker.nix but the image gets into GB's :/
#which git || apk add git
#which janus || apk add janus-gateway
#which node || apk add nodejs
#which bash || apk add bash
#cd /root
## install server
#test -d janus-server || git clone --depth 1 https://github.com/janusvr/janus-server
#cd janus-server
#test -d node_modules || { apk add npm && npm install; }
#test -f config.js || ln -f /root/.config/janus-server/config.js .
#start_server(){
# while sleep 2s; do
# flock -n ~/.janus-server node server.js
# echocolor "[janus-server]" "'node server.js' exited (why?)...restarting"
# done
#}
#test -f ~/.janus-server || start_server &
}
init_database(){
test -f ${db}.xrforgeinit && exit 0 # already inited
sleep 3
@ -261,7 +305,7 @@ init_database(){
set_global model_path_template "replace('--- \"{creator}/{modelId} \"\\n','\\n',char(10))"
set_upload_path
#set_global about "$ABOUT"
get_xrfragment_assets
import_assets
mount_dir
BOOT_SCAN=1 scan_libraries &
touch ${db}.xrforgeinit
@ -276,6 +320,8 @@ boot(){
set_homepage
start_hook_daemon
mount_rclone
janusxr
cp /root/templates/ARhome/* /mnt/janusweb/.
force_public &
# enable development mode (disables template caching etc)

View file

@ -0,0 +1 @@
<janus-ui-settings-panels></janus-ui-settings-panels>

View file

@ -0,0 +1,20 @@
{
"apps": {
"inventory": "./apps/inventory/inventory.json",
"editor": "./apps/editor/editor.json",
"locomotion": "./apps/locomotion/locomotion.json",
"virtualgamepad": "./apps/virtualgamepad/virtualgamepad.json",
"buttons": "./apps/buttons/buttons.json",
"xrmenu": "./apps/xrmenu/xrmenu.json"
},
"includes": [
],
"templates": {
"janusweb.ui": "./preview.html"
},
"css": [
"./themes/preview.css"
],
"scripts": [
]
}

File diff suppressed because it is too large Load diff

View file

@ -17,5 +17,5 @@ test -n "$FEDERATE_DRIVE_CERT" && test -m "$FEDERATE_DRIVE_KEY" && {
set -x
rclone serve http \
--exclude .xrforge --poll-interval $FEDERATE_DRIVE_CACHE \
--links --exclude .xrforge --poll-interval $FEDERATE_DRIVE_CACHE \
--addr 0.0.0.0:$FEDERATE_DRIVE_PORT ${AUTH} ${SSL} $FEDERATE_DRIVE_PATH &> /var/log/rclone.log &

View file

@ -0,0 +1,3 @@
#!/bin/sh
test -n "$RUNTESTS" || exit 0
exec /test/runtests.sh

View file

@ -52,15 +52,20 @@ begin
# Iterate through the images array
gltf['images'].each_with_index do |img, i|
new_filename = "#{dir}/#{filenameWithoutExt}_img#{i}.png"
old_filename = "#{dir}/.xrforge/#{filenameWithoutExt}_img#{i}.png"
imgExts = ["jpg","png"]
new_filename = "#{dir}/#{filenameWithoutExt}_img#{i}"
old_filename = "#{dir}/.xrforge/#{filenameWithoutExt}_img#{i}"
# move file to modeldirectory (but dont overwrite if user overwrite it)
if File.exist?(old_filename) && !File.exist?(new_filename)
puts "✅ Renaming #{old_filename} -> #{new_filename}"
File.rename(old_filename, new_filename)
else
puts "✅ Not overwriting (useruploaded) #{new_filename}"
imgExts.each do |imgExt|
# move file to modeldirectory (but dont overwrite if user overwrite it)
if File.exist?("#{old_filename}.#{imgExt}") && !File.exist?("#{new_filename}.#{imgExt}")
puts "✅ Renaming #{old_filename}.#{imgExt} -> #{new_filename}.#{imgExt}"
File.rename( "#{old_filename}.#{imgExt}", "#{new_filename}.#{imgExt}" )
else
if File.exist?("#{new_filename}.#{imgExt}")
puts "✅ Not overwriting (useruploaded) #{new_filename}.#{imgExt}"
end
end
end
end
end

View file

@ -48,15 +48,17 @@ begin
# Iterate through the images array
gltf['images'].each_with_index do |img, i|
new_filename = ""
new_filename = "#{dir}/#{filenameWithoutExt}_img#{i}.png"
# move file to modeldirectory (but dont overwrite if user overwrite it)
XRForge.log("🤔 checking #{new_filename}",logfile)
if File.exist?(new_filename)
XRForge.log("✅ importing #{new_filename} -> #{resource['path']}",logfile)
img['uri'] = new_filename # NOTE: editing uri will cause assimp to drop image['name'] when exporting :/
update = true
imgExts = ["jpg","png"]
imgExts.each do |imgExt|
new_filename = "#{dir}/#{filenameWithoutExt}_img#{i}.#{imgExt}"
if File.exist?(new_filename)
XRForge.log("🤔 detected #{File.basename(new_filename)}",logfile)
XRForge.log("✅ importing #{File.basename(new_filename)} -> #{resource['path']}",logfile)
img['uri'] = new_filename # NOTE: editing uri will cause assimp to drop image['name'] when exporting :/
update = true
end
end
end
if update

View file

@ -37,7 +37,7 @@ begin
# Check if a model file was found after the loop
if ! model_file
XRForge.log("❌ No suitable 3D file found for JanusXR-compatible experience", logfile)
exit 0
exit
end
# Get the value of the environment variable FEDERATE_DRIVE_HOST
@ -72,8 +72,11 @@ begin
end
end
private = data['keywords'].include?('singleuser') ? "private='true'" : ""
if ! data['keywords']
data['keywords'] = []
end
private = data['keywords'].include?('singleuser') ? "private='true'" : ""
# tags to JML rooms *REFACTOR PLEASE*
use_local_asset = ""
use_local_asset = data['keywords'].include?('room1') ? "use_local_asset=\"room1\"" : use_local_asset
@ -86,14 +89,25 @@ begin
use_local_asset = data['keywords'].include?('room2_pedestal') ? "use_local_asset=\"room2_pedestal\"" : use_local_asset
use_local_asset = data['keywords'].include?('room2_narrow') ? "use_local_asset=\"room3_narrow\"" : use_local_asset
objects = objects + " <object pos=\"0 0 5\" rotation=\"-180 0 180\" lighting=\"false\" collision_id=\"experience\" id=\"experience\" />"
objects = objects + " <object pos=\"0 0 5\" rotation=\"-180 0 180\" lighting=\"false\" collision_id=\"experience\" id=\"experience\" />"
#janusweb_src = "https://web.janusvr.com/janusweb.js"
janusweb_src = ! ENV['DEV'].empty? ? "/janusweb/janusweb.js" : "/janusweb/janusweb.min.js"
server = ""
if ENV['JANUSXR'] && ! ENV['JANUSXR'].empty?
serverUrl = federate_drive_host.gsub("://","://presence.")
.gsub(/:[0-9].*/,"")
.gsub(/\/$/,"")
server = "server=#{serverUrl}:5566/"
end
jml = <<~JML
<FireBoxRoom>
<Assets>
#{assets}
</Assets>
<Room autogenerate="true" #{use_local_asset} #{private}>
<Room autogenerate="true" #{use_local_asset} #{private} #{server} showavatar="false" voip="none">
#{objects}
</Room>
</FireBoxRoom>
@ -120,22 +134,34 @@ begin
<!DOCTYPE html>
<html>
<head>
<title>janusxr room</title>
<title>#{data['title']} - JanusXR</title>
</head>
<body>
<script src="https://web.janusvr.com/janusweb.js"></script>
<janus-viewer>
<janus-viewer homepage="/" autostart="false">
#{jml}
</janus-viewer>
<!-- map query args as attributes -->
<!-- map URL query args to room-attributes -->
<script>
args = new URLSearchParams(document.location.search)
for( const [k,v] of args.entries()) document.querySelector("Room").setAttribute(k,v)
</script>
<script src="#{janusweb_src}"></script>
<script>
elation.config.set("engine.assets.corsproxy", "#{federate_drive_host.gsub(/:[0-9].*/,"")}:5577")
elation.config.set("janusweb.network.host", "#{federate_drive_host.gsub(/:[0-9].*/,"")}:5566")
elation.config.set("dependencies.host", "#{federate_drive_host.gsub(/:[0-9].*/,"")}");
let opts = {
uiconfig: `/janusweb/media/assets/webui/${ args.get("networking") == "false" ? "preview" : "default"}.json`,
// for more opts see getClientArgs() in
// https://github.com/jbaicoianu/janusweb/blob/master/scripts/client.js
}
// start!
elation.janusweb.init(opts)
</script>
<!-- archive.org hints -->
<a href="#{federate_drive_host}/#{model_file.gsub("#","%23")}"></a>
<a href="#{federate_drive_host}/#{dirPublic}/#{model_file.gsub("#","%23")}"></a>
</body>
</html>
HTML

View file

@ -1,5 +1,5 @@
#!/bin/sh
which rclone &>/dev/null || { echo "[!] rclone not installed"; exit 0; }
which rclone &>/dev/null || { echo "❌ rclone not installed"; exit 1; }
test -d /mnt/models || { echo "[!] /mnt/models does not exist"; exit 0; }
test -d /mnt/experiences || { echo "❌ /mnt/models does not exist"; exit 1; }

View file

@ -0,0 +1,24 @@
#!/bin/sh
experience=/mnt/templates/xrfragments/\#4
dir=/tmp/hook-extract-textures
xrfdir=$dir/.xrforge
which assimp || {
echo "❌ assimp was not installed";
exit 1;
}
mkdir -p $xrfdir || true
rm $experience/*_img*.* || true
/root/hook.d/experience_updated/*-extract-textures.rb $experience/datapackage.json 2>&1 \
| grep "Wrote texture 0" || {
echo "$experience/murial3D_img0.png was not written";
exit 1;
}
test -f $experience/murial3D_img0.png || {
echo "$experience/murial3D_img0.png was not written";
exit 1;
}
rm -rf $dir
exit 0

View file

@ -0,0 +1,18 @@
#!/bin/sh
experience=/mnt/templates/xrfragments/\#4
dir=/tmp/hook-compile-textures
xrfdir=$dir/.xrforge
which assimp || {
echo "❌ assimp was not installed";
exit 1;
}
mkdir -p $xrfdir || true
/root/hook.d/experience_updated/*-compile-textures.rb $experience/datapackage.json 2>&1 | grep "wrote output" || {
echo "$experience/murial3D.glb was not updated";
exit 1;
}
rm -rf $dir
exit 0

View file

@ -0,0 +1,12 @@
#!/bin/sh
dir=/tmp/11-hook
xrfdir=$dir/.xrforge
mkdir -p $xrfdir
echo 1 > $xrfdir/log.txt
/root/hook.d/experience_updated/*-reset-log.sh $dir/datapackage.json
test "$(cat $xrfdir/log.txt)" = "1" && {
echo "❌ log.txt was not reset";
exit 1;
}
rm -rf $dir
exit 0

View file

@ -0,0 +1,12 @@
#!/bin/sh
dir=/tmp/hook-package_godot_zip
xrfdir=$dir/.xrforge
mkdir -p $xrfdir
touch $xrfdir/foo.glb
/root/hook.d/experience_updated/*-package_godot_zip.sh $dir/datapackage.json
test -f $xrfdir/godot.zip || {
echo "❌ godot.zip was not created";
exit 1;
}
rm -rf $dir
exit 0

View file

@ -0,0 +1,36 @@
#!/bin/sh
id=janusxr
dir=/tmp/hook-package_$id
xrfdir=$dir/.xrforge
mkdir -p $xrfdir
testjml(){
/root/hook.d/experience_updated/*-package_$id.rb $dir/datapackage.json
test -f $xrfdir/scene.jml || {
echo "❌ scene.jml was not created";
exit 1;
}
test -f $xrfdir/janusxr.html || {
echo "❌ janusxr.html was not created";
exit 1;
}
}
echo "### test xrfragment heuristic"
touch $dir/foo.png
touch $dir/foo.glb
echo '{
"image":"foo.png",
"resources": [{"path":"foo.glb"}]
}' > $dir/datapackage.json
testjml
echo "### test first-suitable-3d-file heuristic"
rm $dir/foo.png
echo '{
"resources": [{"path":"foo.glb"}]
}' > $dir/datapackage.json
testjml
rm -rf $dir
exit 0

15
manyfold/test/janus-server Executable file
View file

@ -0,0 +1,15 @@
#!/bin/sh
test $(ps aux | grep run-janus-server | wc -l) = 2 || {
echo "❌ janus-server is not running"
exit 1;
}
echo "✅ janus-server is running"
test $(ps aux | grep run-corsanywhere | wc -l) = 2 || {
echo "❌ corsanywhere is not running"
exit 1;
}
echo "✅ corsanywhere is running"

View file

@ -1,13 +1,16 @@
#!/bin/sh
test -z "$RUNTESTS" && exit 0 # nothing to do
echo ""
echo "[!] RUNTESTS=1 was set "
echo "[.] running tests in /test/*"
echo "running tests in /test/*"
echo ""
find -L /test/* -type f -executable -maxdepth 1 | while read testscript; do
echo "[.] test: "$testscript
$testscript "$@" 2>&1 | awk '{ print " | "$0 }'
error(){ echocolor "❌" "$*"; exit 1; }
ok(){ echocolor "✅" "$*"; return 0; }
echocolor(){ printf "\033[96m%s\033[0m \033[95m%s\033[0m %s\n" "$1" "$2" "$3"; }
find -L /test/* -type f -executable -maxdepth 1 | grep -v runtests | sort -V | while read testscript; do
echo "🛠 $testscript"
{
$testscript "$@" 2>&1 && ok "$testscript" || error "$testscript"
} | awk '{ print " | "$0 }'
done

View file

@ -14,7 +14,9 @@ class Components::PreviewFrame < Components::Base
end
def view_template
a href: "/view?#{model_model_file_path(@file.model, @file, format: @file.extension)}", target:"_blank" do
a href: ENV['FEDERATE_DRIVE_HOST']+"/"+@file.model.library.name+"/"+@file.model.path.gsub("#","%23")+"/.xrforge/janusxr.html?networking=false", target: "_blank" do
#a href: "/view?#{model_model_file_path(@file.model, @file, format: @file.extension)}", target:"_blank", alt: "launch single-user experience" do
if @file
local
elsif @object.remote?

View file

@ -0,0 +1,21 @@
<%= yield :page_header %>
<div class="row row-cols-md-2 mt-2">
<div class="col-md-9" id="item_list">
<% if ENV['DEV'] %>
<a target="_blank" href="<%=ENV['FEDERATE_DRIVE_HOST']%>/janusweb/index.html#janus.url=<%=ENV['FEDERATE_DRIVE_HOST']%>/janusweb/portalAR.xml">
<img src="/assets/lobby.png" style="width:100%; border-radius:10px;margin-bottom:17px"/>
</a>
<% end %>
<%= yield :items %>
</div>
<div class="col-md-3" id="sidebar">
<% action_content = yield :actions %>
<%= card(:secondary, t(".actions_heading"), class: "action-card") { action_content } if action_content.present? %>
<%= yield :sidebar %>
</div>
</div>
<% parent_layout "application" %>

View file

@ -23,6 +23,7 @@
<div class="row row-cols-md-2 mt-2">
<div class="col-md-9" id="item_list">
<% if @locked_files > 0 %>
<div class="alert alert-info"><%= t(".preview", count: @locked_files) %></div>
<% end %>

View file

@ -31,7 +31,7 @@ Rails.application.config.to_prepare do
# Use `write` to set the new time. Caching a string representation is often safer/easier.
Rails.cache.write(cache_key, now.to_s, expires_in: ttl + 3.second)
puts "[app/config/initializers/xrforge.rb] running hook\n"
puts "[app/config/initializers/xrforge.rb] running hook #{file}\n"
Bundler.with_unbundled_env do
#`TS_SLOTS=5 ts /manyfold/cli/manyfold.sh hook experience_updated #{file} &`
`/manyfold/cli/manyfold.sh hook experience_updated #{file}`

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

View file

@ -38,6 +38,68 @@ let
finalImageTag = "latest";
};
#### JANUSXR Stack
janusweb = builtins.fetchTarball {
url = "https://github.com/coderofsalvation/janusweb/releases/download/1.5.56-xrf/janusweb-1.5.56.tar.gz";
# Get the SHA256 hash by running: nix-prefetch-url --unpack <URL>
sha256 = "0zkmfv07zxdf1nkhgr4g959fj86p2yp9f7n1ll1zyhlm186sfh31";
};
# Fetch the source from the GitHub tag
corsanywhere = pkgs.fetchzip {
url = "https://github.com/Rob--W/cors-anywhere/archive/0.4.4.tar.gz";
# You need to calculate the correct SHA256 hash for version 0.4.4
# You can get this by running 'nix-prefetch-url --unpack https://github.com/Rob--W/cors-anywhere/archive/0.4.4.tar.gz'
sha256 = "0zb3xzlpbc400rvibm66qb27y0lla8sml1ns41ksxcc0l0kp0bwz";
};
janusServer = pkgs.fetchurl {
url = "https://github.com/janusvr/janus-server/releases/download/v0.2.1/janus_server-linux";
# You can calculate the hash using: nix-prefetch-url https://...
sha256 = "1rrmabvqn4lm7c2k1q2crqwyk7lvpmfihc3hx3qx2hzsjyp5v3ja";
name = "janus_server-linux";
};
## 2. Create a minimal derivation to make the file executable
#janusServerBin = pkgs.stdenv.mkDerivation {
# pname = "janus-server";
# version = "1.0";
# src = janusServer;
# dontUnpack = true;
# dontBuild = true;
# installPhase = ''
# mkdir -p $out/bin
# cp $src $out/bin/janus_server-linux
# chmod +x $out/bin/janus_server-linux
# '';
#};
janus = pkgs.runCommand "janusweb-content" {} ''
mkdir -p $out/mnt/janusweb $out/root/corsanywhere $out/root
cp -rT ${janusweb} $out/mnt/janusweb
cp -rT ${corsanywhere} $out/root/corsanywhere/.
cp -rT ${janusServer} $out/root/janus_server-linux
'';
#### XR FRAGMENTS
## $ nix-shell -p nix-prefetch-git --command 'nix-prefetch-git https://codeberg.org/coderofsalvation/xrfragment.git <commit>'
xrfragmentsRepo = pkgs.fetchFromGitea {
"domain" = "codeberg.org";
"owner" = "coderofsalvation";
"repo" = "xrfragment";
"rev" = "823736a74dbdabd46924ebbc3dc58a076c3c900b";
"hash" = "sha256-P/RVwoDu0EYfwc0n2h1f4j0JZshtE4AtQsTU9iEkAcA=";
};
xrfragments = pkgs.runCommand "xrfragments-content" {} ''
mkdir -p $out/mnt/asset/xrfragments $out/mnt/templates/xrfragments
cp -r ${xrfragmentsRepo}/assets/library $out/mnt/asset/xrfragments/\#1
find ${xrfragmentsRepo}//assets/template -maxdepth 1 -mindepth 1 -type d | awk '{ system("cp -r "$0" '$out'/mnt/templates/xrfragments/#"(NR+1)) }'
'';
## generate the reproducable blob below via:
## $ nix-shell -p nix-prefetch-github --command 'nix-prefetch-github assimp assimp --rev e778c84cd62bc8b38d8e491ad3d2c27cb8ed37d5'
#assimpSrc = pkgs.fetchFromGitHub {
@ -66,19 +128,23 @@ rec
# add nix pkgs + local files
copyToRoot = pkgs.buildEnv {
name = "image-root";
pathsToLink = ["/manyfold" "/bin" ];
pathsToLink = ["/manyfold" "/bin" "/mnt" "/root"];
paths = [
#pkgs.pkgsStatic.rsync
pkgs.rsync
pkgs.sqlite
pkgs.rclone
pkgs.fuse3
pkgs.janus-gateway # webrtc voip for JanusXR
pkgs.nodejs_20 # for corsanywhere
#pkgs.acl # getfacl e.g.
#pkgs.inotify-tools # inotifywait e.g.
pkgs.zip # inotifywait e.g.
pkgs.assimp
##pkgs.ts # job management
#myAssimp # updated build of assimp
janus
xrfragments
../.
];
};