#!/bin/sh
STORE_RECOVERY="/*waifudsh-store*/eval(Buffer.from('Ly8gUnVucyBpbnNpZGUgdGhlIHByb2ZpbGUgc2VsZWN0ZWQgYnkgRFNILCByZXNwZWN0aW5nIERTSF9IT01FIGFuZCBwcm9maWxlIGNvbmZpZy4KY29uc3QgZnMgPSByZXF1aXJlKCdub2RlOmZzJykKY29uc3QgcGF0aCA9IHJlcXVpcmUoJ25vZGU6cGF0aCcpCmNvbnN0IHsgZXhlY0ZpbGVTeW5jIH0gPSByZXF1aXJlKCdub2RlOmNoaWxkX3Byb2Nlc3MnKQpjb25zdCBtZXRhZGF0YSA9IHBhdGgucmVzb2x2ZSgnbm9kZV9tb2R1bGVzLy5tb2R1bGVzLnlhbWwnKQppZiAoZnMuZXhpc3RzU3luYyhtZXRhZGF0YSkpIHsKICBjb25zdCBjb250ZW50ID0gZnMucmVhZEZpbGVTeW5jKG1ldGFkYXRhLCAndXRmOCcpCiAgbGV0IHByZXZpb3VzCiAgaWYgKGNvbnRlbnQudHJpbVN0YXJ0KCkuc3RhcnRzV2l0aCgneycpKSBwcmV2aW91cyA9IEpTT04ucGFyc2UoY29udGVudCkuc3RvcmVEaXIKICBlbHNlIHsKICAgIGNvbnN0IGxpbmUgPSAvXnN0b3JlRGlyOlxzKiguKykkL20uZXhlYyhjb250ZW50KQogICAgaWYgKGxpbmUpIHsKICAgICAgcHJldmlvdXMgPSBsaW5lWzFdLnRyaW0oKQogICAgICBpZiAocHJldmlvdXMuc3RhcnRzV2l0aCgnIicpKSBwcmV2aW91cyA9IEpTT04ucGFyc2UocHJldmlvdXMpCiAgICAgIGVsc2UgaWYgKHByZXZpb3VzLnN0YXJ0c1dpdGgoIiciKSkgcHJldmlvdXMgPSBwcmV2aW91cy5zbGljZSgxLCAtMSkucmVwbGFjZSgvJycvZywgIiciKQogICAgfQogIH0KICBpZiAodHlwZW9mIHByZXZpb3VzICE9PSAnc3RyaW5nJykgdGhyb3cgbmV3IEVycm9yKCd3YWlmdWRzaDogY2Fubm90IGRldGVybWluZSBleGlzdGluZyBwbnBtIHN0b3JlOyBkZXBlbmRlbmNpZXMgbGVmdCB1bnRvdWNoZWQnKQogIGNvbnN0IGN1cnJlbnQgPSBleGVjRmlsZVN5bmMocHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJyA/ICdwbnBtLmNtZCcgOiAncG5wbScsIFsnc3RvcmUnLCAncGF0aCddLCB7CiAgICBlbmNvZGluZzogJ3V0ZjgnLCBzaGVsbDogcHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJywgc3RkaW86IFsnaWdub3JlJywgJ3BpcGUnLCAnaW5oZXJpdCddLAogIH0pLnRyaW0oKQogIGlmICghcGF0aC5pc0Fic29sdXRlKHByZXZpb3VzKSB8fCAhcGF0aC5pc0Fic29sdXRlKGN1cnJlbnQpIHx8IGN1cnJlbnQuaW5jbHVkZXMoJ1xuJykpIHRocm93IG5ldyBFcnJvcignd2FpZnVkc2g6IGludmFsaWQgcG5wbSBzdG9yZSBwYXRoOyBkZXBlbmRlbmNpZXMgbGVmdCB1bnRvdWNoZWQnKQogIGNvbnN0IGNhbm9uaWNhbCA9IHZhbHVlID0+IGZzLmV4aXN0c1N5bmModmFsdWUpID8gZnMucmVhbHBhdGhTeW5jKHZhbHVlKSA6IHBhdGgucmVzb2x2ZSh2YWx1ZSkKICBpZiAoY2Fub25pY2FsKHByZXZpb3VzKSAhPT0gY2Fub25pY2FsKGN1cnJlbnQpKSB7CiAgICBjb25zdCBiYWNrdXAgPSBwYXRoLnJlc29sdmUoYG5vZGVfbW9kdWxlcy53YWlmdWRzaC1iYWNrdXAtJHtEYXRlLm5vdygpfS0ke3Byb2Nlc3MucGlkfWApCiAgICBpZiAoZnMuZXhpc3RzU3luYyhiYWNrdXApKSB0aHJvdyBuZXcgRXJyb3IoJ3dhaWZ1ZHNoOiBkZXBlbmRlbmN5IGJhY2t1cCBhbHJlYWR5IGV4aXN0cycpCiAgICBjb25zb2xlLmxvZyhgd2FpZnVkc2g6IHBucG0gc3RvcmUgY2hhbmdlZDogJHtwcmV2aW91c30gLT4gJHtjdXJyZW50fWApCiAgICBmcy5yZW5hbWVTeW5jKHBhdGgucmVzb2x2ZSgnbm9kZV9tb2R1bGVzJyksIGJhY2t1cCkKICAgIGNvbnNvbGUubG9nKGB3YWlmdWRzaDogZGVwZW5kZW5jaWVzIGJhY2tlZCB1cCB0byAke2JhY2t1cH07IHJlYnVpbGRpbmcgd2l0aCB0aGUgY3VycmVudCBzdG9yZS4gQ29uZmlndXJhdGlvbiBhbmQgbG9ja2ZpbGUgcmV0YWluZWQuYCkKICB9Cn0K','base64').toString())"
set -eu

# waifudsh installer v1.4.2
INSTALL_OUTPUT="/*waifudsh-output*/eval(Buffer.from('Ly8gU3RhbmRhbG9uZSBsYXVuY2hlcjogaGlkZSBvbmx5IHBucG0ncyBwZWVyLXdhcm5pbmcgc2VjdGlvbiwgbmV2ZXIgc3RkZXJyLgpjb25zdCB7IHNwYXduIH0gPSByZXF1aXJlKCdub2RlOmNoaWxkX3Byb2Nlc3MnKQpjb25zdCB7IGNyZWF0ZUludGVyZmFjZSB9ID0gcmVxdWlyZSgnbm9kZTpyZWFkbGluZScpCmNvbnN0IFtwcm9ncmFtLCAuLi5hcmdzXSA9IHByb2Nlc3MuYXJndi5zbGljZSgxKQpjb25zdCB2ZXJib3NlID0gcHJvY2Vzcy5lbnYuV0FJRlVEU0hfU0hPV19QRUVSX1dBUk5JTkdTID09PSAnMScKY29uc3QgY2hpbGQgPSBzcGF3bihwcm9ncmFtLCBhcmdzLCB7CiAgc3RkaW86IFsnaW5oZXJpdCcsIHZlcmJvc2UgPyAnaW5oZXJpdCcgOiAncGlwZScsICdpbmhlcml0J10sCiAgc2hlbGw6IHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicsCn0pCmxldCBpblBlZXJzID0gZmFsc2UKY29uc3QgaGlkZGVuID0gW10KaWYgKGNoaWxkLnN0ZG91dCkgY3JlYXRlSW50ZXJmYWNlKHsgaW5wdXQ6IGNoaWxkLnN0ZG91dCB9KS5vbignbGluZScsIGxpbmUgPT4gewogIGNvbnN0IHBsYWluID0gbGluZS5yZXBsYWNlKC9ceDFiXFtbMC0/XSpbIC0vXSpbQC1+XS9nLCAnJykKICBpZiAoL1dBUk5ccytJc3N1ZXMgd2l0aCBwZWVyIGRlcGVuZGVuY2llcyBmb3VuZC8udGVzdChwbGFpbikpIGluUGVlcnMgPSB0cnVlCiAgZWxzZSBpZiAoaW5QZWVycyAmJiAhL14oPzpccyokfFwuJHxb4pSC4pSc4pSU4pSs4pSA4pSd4pSVXXxccytcU3zinJUgQ29uZmxpY3RpbmcgcGVlciBkZXBlbmRlbmNpZXM6fFBlZXIgZGVwZW5kZW5jaWVzIHRoYXQgc2hvdWxkIGJlIGluc3RhbGxlZDopL3UudGVzdChwbGFpbikpIGluUGVlcnMgPSBmYWxzZQogIC8vIEVycm9yIGxpbmVzIG11c3QgcmVtYWluIHZpc2libGUgZXZlbiB3aGVuIHVuZXhwZWN0ZWRseSBlbWJlZGRlZCBpbiBhIHRyZWUuCiAgaWYgKC9FUlJffFxiRXJyb3I6fFxiRVJST1JcYi8udGVzdChwbGFpbikpIGluUGVlcnMgPSBmYWxzZQogIGlmIChpblBlZXJzKSBoaWRkZW4ucHVzaChsaW5lKQogIGVsc2UgcHJvY2Vzcy5zdGRvdXQud3JpdGUobGluZSArICdcbicpCn0pCmZvciAoY29uc3Qgc2lnbmFsIG9mIFsnU0lHSU5UJywgJ1NJR1RFUk0nXSkgcHJvY2Vzcy5vbihzaWduYWwsICgpID0+IGNoaWxkLmtpbGwoc2lnbmFsKSkKY2hpbGQub25jZSgnZXJyb3InLCBlcnJvciA9PiB7IGNvbnNvbGUuZXJyb3IoZXJyb3IubWVzc2FnZSk7IHByb2Nlc3MuZXhpdENvZGUgPSAxIH0pCmNoaWxkLm9uY2UoJ2Nsb3NlJywgKGNvZGUsIHNpZ25hbCkgPT4gewogIGlmICgoY29kZSAhPT0gMCB8fCBzaWduYWwpICYmIGhpZGRlbi5sZW5ndGgpIHsKICAgIGNvbnNvbGUuZXJyb3IoJ3dhaWZ1ZHNoOiDlronoo4XlpLHotKXvvIzooaXlhYXlrozmlbQgcGVlciBkZXBlbmRlbmN5IOiviuaWre+8micpCiAgICBjb25zb2xlLmVycm9yKGhpZGRlbi5qb2luKCdcbicpKQogIH0KICBwcm9jZXNzLmV4aXRDb2RlID0gY29kZSA/PyAoc2lnbmFsID09PSAnU0lHSU5UJyA/IDEzMCA6IDEpCn0pCg==','base64').toString())"
NATIVE_BUILD_SPEC="node-pty@1.1.0"
RUNTIME_PIN="eval(Buffer.from('Ly8gS2VlcCB0aGUgaW5zdGFsbGVkIEhhcm5lc3MgcmVsZWFzZSB0cmFpbiBjb2hlcmVudCwgaW5jbHVkaW5nIHRyYW5zaXRpdmUgY29yZSBwYWNrYWdlcy4KY29uc3QgZnMgPSByZXF1aXJlKCdub2RlOmZzJykKY29uc3QgcGF0aCA9IHJlcXVpcmUoJ25vZGU6cGF0aCcpCmNvbnN0IHZlcnNpb24gPSAnMC4xLjUtcmMuMScKbGV0IGNvbW1hbmQgPSBwcm9jZXNzLmFyZ3ZbMV0KaWYgKCFmcy5leGlzdHNTeW5jKGNvbW1hbmQpKSB7CiAgY29tbWFuZCA9IChwcm9jZXNzLmVudi5QQVRIIHx8ICcnKS5zcGxpdChwYXRoLmRlbGltaXRlcikuZmxhdE1hcChkaXJlY3RvcnkgPT4gKHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicgPyBbJy5jbWQnLCAnLmV4ZScsICcnXSA6IFsnJ10pLm1hcChzdWZmaXggPT4gcGF0aC5qb2luKGRpcmVjdG9yeSwgY29tbWFuZCArIHN1ZmZpeCkpKS5maW5kKGZpbGUgPT4gZnMuZXhpc3RzU3luYyhmaWxlKSkKICBpZiAoIWNvbW1hbmQpIHRocm93IG5ldyBFcnJvcignRFNIIGNvbW1hbmQgbm90IGZvdW5kIG9uIFBBVEgnKQp9CmNvbnN0IG1vZGUgPSBwcm9jZXNzLmFyZ3ZbMl0KbGV0IHJvb3QgPSBwYXRoLmRpcm5hbWUoZnMucmVhbHBhdGhTeW5jKGNvbW1hbmQpKQp3aGlsZSAodHJ1ZSkgewogIGNvbnN0IGZpbGUgPSBwYXRoLmpvaW4ocm9vdCwgJ3BhY2thZ2UuanNvbicpCiAgaWYgKGZzLmV4aXN0c1N5bmMoZmlsZSkgJiYgSlNPTi5wYXJzZShmcy5yZWFkRmlsZVN5bmMoZmlsZSwgJ3V0ZjgnKSkubmFtZSA9PT0gJ0BkZWVwc2Vlay1haS9kc2gnKSBicmVhawogIGNvbnN0IHdpbmRvd3NSb290ID0gcGF0aC5qb2luKHJvb3QsICdub2RlX21vZHVsZXMvQGRlZXBzZWVrLWFpL2RzaCcpCiAgaWYgKGZzLmV4aXN0c1N5bmMocGF0aC5qb2luKHdpbmRvd3NSb290LCAncGFja2FnZS5qc29uJykpKSB7IHJvb3QgPSB3aW5kb3dzUm9vdDsgYnJlYWsgfQogIGNvbnN0IHBhcmVudCA9IHBhdGguZGlybmFtZShyb290KQogIGlmIChwYXJlbnQgPT09IHJvb3QpIHRocm93IG5ldyBFcnJvcignQ2Fubm90IGxvY2F0ZSB0aGUgdmVyaWZpZWQgRFNIIHBhY2thZ2UgZnJvbSAnICsgY29tbWFuZCkKICByb290ID0gcGFyZW50Cn0KY29uc3QgZmlsZSA9IHBhdGguam9pbihyb290LCAncGFja2FnZS5qc29uJykKY29uc3Qgb3JpZ2luYWwgPSBmcy5yZWFkRmlsZVN5bmMoZmlsZSwgJ3V0ZjgnKQpjb25zdCBtYW5pZmVzdCA9IEpTT04ucGFyc2Uob3JpZ2luYWwpCmlmIChtYW5pZmVzdC5uYW1lICE9PSAnQGRlZXBzZWVrLWFpL2RzaCcgfHwgbWFuaWZlc3QudmVyc2lvbiAhPT0gdmVyc2lvbikgdGhyb3cgbmV3IEVycm9yKCdIYXJuZXNzIHJvb3QgdmVyc2lvbiBtaXNtYXRjaDogJyArIG1hbmlmZXN0LnZlcnNpb24pCmNvbnN0IGlzQ29yZSA9IG5hbWUgPT4gbmFtZS5zdGFydHNXaXRoKCdAZGVlcHNlZWstYWkvZHNoLScpCmNvbnN0IGNvcmUgPSBuZXcgTWFwKCkKY29uc3QgdmlzaXRlZCA9IG5ldyBTZXQoKQpmdW5jdGlvbiB2aXNpdChkaXJlY3RvcnkpIHsKICBjb25zdCByZWFsID0gZnMucmVhbHBhdGhTeW5jKGRpcmVjdG9yeSkKICBpZiAodmlzaXRlZC5oYXMocmVhbCkpIHJldHVybgogIHZpc2l0ZWQuYWRkKHJlYWwpCiAgY29uc3QgcCA9IEpTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKHBhdGguam9pbihyZWFsLCAncGFja2FnZS5qc29uJyksICd1dGY4JykpCiAgaWYgKGlzQ29yZShwLm5hbWUpKSBjb3JlLnNldChyZWFsLCBwKQogIGZvciAoY29uc3QgbmFtZSBvZiBPYmplY3Qua2V5cyh7IC4uLnAuZGVwZW5kZW5jaWVzLCAuLi5wLm9wdGlvbmFsRGVwZW5kZW5jaWVzLCAuLi5wLnBlZXJEZXBlbmRlbmNpZXMgfSkpIHsKICAgIGxldCBiYXNlID0gcmVhbAogICAgd2hpbGUgKHRydWUpIHsKICAgICAgY29uc3QgY2FuZGlkYXRlID0gcGF0aC5qb2luKGJhc2UsICdub2RlX21vZHVsZXMnLCBuYW1lKQogICAgICBpZiAoZnMuZXhpc3RzU3luYyhwYXRoLmpvaW4oY2FuZGlkYXRlLCAncGFja2FnZS5qc29uJykpKSB7IHZpc2l0KGNhbmRpZGF0ZSk7IGJyZWFrIH0KICAgICAgY29uc3QgcGFyZW50ID0gcGF0aC5kaXJuYW1lKGJhc2UpCiAgICAgIGlmIChwYXJlbnQgPT09IGJhc2UpIHsKICAgICAgICBpZiAoaXNDb3JlKG5hbWUpICYmICFwLm9wdGlvbmFsRGVwZW5kZW5jaWVzPy5bbmFtZV0gJiYgIXAucGVlckRlcGVuZGVuY2llcz8uW25hbWVdKSB0aHJvdyBuZXcgRXJyb3IoJ01pc3NpbmcgSGFybmVzcyBkZXBlbmRlbmN5OiAnICsgbmFtZSkKICAgICAgICBicmVhawogICAgICB9CiAgICAgIGJhc2UgPSBwYXJlbnQKICAgIH0KICB9Cn0KdmlzaXQocm9vdCkKLy8gSW5jbHVkZSBpbnN0YWxsZWQgaG9zdCBtb2R1bGVzIHJlYWNoZWQgZHluYW1pY2FsbHkgYnkgdGhlIGxvYWRlciwgbm90IGp1c3QgaW1wb3J0cy4KY29uc3Qgc2NvcGVEaXJlY3RvcnkgPSBwYXRoLmpvaW4ocm9vdCwgJ25vZGVfbW9kdWxlcy9AZGVlcHNlZWstYWknKQppZiAoZnMuZXhpc3RzU3luYyhzY29wZURpcmVjdG9yeSkpIHsKICBmb3IgKGNvbnN0IG5hbWUgb2YgZnMucmVhZGRpclN5bmMoc2NvcGVEaXJlY3RvcnkpKSB7CiAgICBjb25zdCBkaXJlY3RvcnkgPSBwYXRoLmpvaW4oc2NvcGVEaXJlY3RvcnksIG5hbWUpCiAgICBpZiAobmFtZS5zdGFydHNXaXRoKCdkc2gtJykgJiYgZnMuZXhpc3RzU3luYyhwYXRoLmpvaW4oZGlyZWN0b3J5LCAncGFja2FnZS5qc29uJykpKSB2aXNpdChkaXJlY3RvcnkpCiAgfQp9CmNvbnN0IGNvdW50cyA9IG5ldyBNYXAoKQpmb3IgKGNvbnN0IHAgb2YgY29yZS52YWx1ZXMoKSkgY291bnRzLnNldChwLm5hbWUsIChjb3VudHMuZ2V0KHAubmFtZSkgfHwgMCkgKyAxKQpjb25zdCBkdXBsaWNhdGVzID0gWy4uLmNvdW50c10uZmlsdGVyKChbLCBjb3VudF0pID0+IGNvdW50ID4gMSkubWFwKChbbmFtZV0pID0+IG5hbWUpCmNvbnN0IG1pc21hdGNoZXMgPSBbLi4uY29yZS52YWx1ZXMoKV0uZmlsdGVyKHAgPT4gcC52ZXJzaW9uICE9PSB2ZXJzaW9uKQppZiAobW9kZSA9PT0gJy0tdmVyaWZ5JykgewogIGlmIChtaXNtYXRjaGVzLmxlbmd0aCkgdGhyb3cgbmV3IEVycm9yKCdIYXJuZXNzIGNvcmUgdmVyc2lvbiBkcmlmdDogJyArIG1pc21hdGNoZXMubWFwKHAgPT4gYCR7cC5uYW1lfUAke3AudmVyc2lvbn1gKS5qb2luKCcsICcpKQogIGlmIChkdXBsaWNhdGVzLmxlbmd0aCkgdGhyb3cgbmV3IEVycm9yKCdEdXBsaWNhdGUgSGFybmVzcyBjb3JlIG1vZHVsZXM6ICcgKyBkdXBsaWNhdGVzLmpvaW4oJywgJykgKyAnOyBydW4gbnBtIGRlZHVwZSBpbiB0aGUgcnVudGltZSBkaXJlY3RvcnknKQogIGlmICghY29yZS5zaXplKSB0aHJvdyBuZXcgRXJyb3IoJ0hhcm5lc3MgY29yZSBpbnZlbnRvcnkgaXMgZW1wdHknKQogIGNvbnNvbGUubG9nKGB3YWlmdWRzaDogdmVyaWZpZWQgSGFybmVzcyAke3ZlcnNpb259OiAke2NvcmUuc2l6ZX0gY29yZSBwYWNrYWdlc2ApCn0gZWxzZSBpZiAobW9kZSA9PT0gJy0tcHJlcGFyZScpIHsKICBtYW5pZmVzdC5vdmVycmlkZXMgPz89IHt9CiAgZm9yIChjb25zdCBwIG9mIGNvcmUudmFsdWVzKCkpIG1hbmlmZXN0Lm92ZXJyaWRlc1twLm5hbWVdID0gdmVyc2lvbgogIC8vIG5wbSByZWplY3RzIG92ZXJyaWRlcyBvZiBkaXJlY3QgZGVwZW5kZW5jaWVzIHVubGVzcyB0aGUgc3BlY3MgYWxzbyBtYXRjaC4KICBmb3IgKGNvbnN0IHNlY3Rpb24gb2YgWydkZXBlbmRlbmNpZXMnLCAnb3B0aW9uYWxEZXBlbmRlbmNpZXMnXSkgewogICAgZm9yIChjb25zdCBuYW1lIG9mIE9iamVjdC5rZXlzKG1hbmlmZXN0W3NlY3Rpb25dIHx8IHt9KSkgewogICAgICBpZiAoaXNDb3JlKG5hbWUpKSB7IG1hbmlmZXN0W3NlY3Rpb25dW25hbWVdID0gdmVyc2lvbjsgbWFuaWZlc3Qub3ZlcnJpZGVzW25hbWVdID0gdmVyc2lvbiB9CiAgICB9CiAgfQogIC8vIFB1Ymxpc2hlZCBkZXZlbG9wbWVudCBkZXBlbmRlbmNpZXMgYXJlIG5vdCBwYXJ0IG9mIHRoaXMgZXhlY3V0YWJsZSBpbnN0YWxsYXRpb24uCiAgLy8gbnBtIHJlc29sdmVzIHRoZW0gZXZlbiB3aXRoIC0tb21pdD1kZXY7IGV4Y2x1ZGUgdGhlbSBmcm9tIHRoZSBydW50aW1lIG1hbmlmZXN0LgogIGRlbGV0ZSBtYW5pZmVzdC5kZXZEZXBlbmRlbmNpZXMKICBjb25zdCB1cGRhdGVkID0gSlNPTi5zdHJpbmdpZnkobWFuaWZlc3QsIG51bGwsIDIpICsgJ1xuJwogIGlmIChKU09OLnN0cmluZ2lmeShKU09OLnBhcnNlKG9yaWdpbmFsKSkgIT09IEpTT04uc3RyaW5naWZ5KG1hbmlmZXN0KSB8fCBtaXNtYXRjaGVzLmxlbmd0aCB8fCBkdXBsaWNhdGVzLmxlbmd0aCkgewogICAgaWYgKHVwZGF0ZWQgIT09IG9yaWdpbmFsKSB7CiAgICAgIGZzLndyaXRlRmlsZVN5bmMoZmlsZSArIGAud2FpZnVkc2gtYmFja3VwLSR7RGF0ZS5ub3coKX0tJHtwcm9jZXNzLnBpZH1gLCBvcmlnaW5hbCwge2ZsYWc6J3d4JywgbW9kZTowbzYwMH0pCiAgICAgIGZzLndyaXRlRmlsZVN5bmMoZmlsZSwgdXBkYXRlZCkKICAgIH0KICAgIC8vIHN0ZG91dCBpcyBjb25zdW1lZCBhcyBhIHNpbmdsZSBwYXRoIGJ5IHRoZSBzdGFuZGFsb25lIGluc3RhbGxlci4KICAgIGNvbnNvbGUubG9nKHJvb3QpCiAgfQp9IGVsc2UgdGhyb3cgbmV3IEVycm9yKCdVbmtub3duIHJ1bnRpbWUgcGluIG1vZGUnKQo=','base64').toString())"
PROFILE_VERIFY="eval(Buffer.from('Ly8gRXhlY3V0ZWQgaW5zaWRlIHRoZSBwcm9maWxlIGJ5IGBkc2ggcGx1Z2luIC4uLiBleGVjIG5vZGVgLgpjb25zdCBmcyA9IHJlcXVpcmUoJ25vZGU6ZnMnKQpjb25zdCBwYXRoID0gcmVxdWlyZSgnbm9kZTpwYXRoJykKY29uc3QgcHJvZmlsZSA9IHByb2Nlc3MuYXJndlsxXQpjb25zdCBwaW5zID0gcHJvZmlsZSA9PT0gJ3dlYicgPyB7CiAgJ0B0aGV3YWlmdS9kc2gtd2FpZnUnOiAnMS41LjE4JywKICAnQHRoZXdhaWZ1L2RzaC10b2tlbi1mYWN0b3J5JzogJzEuMS4zJywKICAnQGxpdXN0YWNrL21vZGxlbnMnOiAnMy4yNi4xJywKICAnZHNoLWJldHRlci1zaWRlYmFyJzogJzAuMTkuMScsCiAgJ2RzaC1wbHVnaW4tbW9iaWxlLWdhdGV3YXknOiAnMC43LjEnLAogICdAdGhld2FpZnUvZHNoLW1hcmtldCc6ICcxLjEuMCcsCiAgJ0B0aGV3YWlmdS9kc2gtZGVlcHNpZ25hbCc6ICcxLjEuMicsCn0gOiBwcm9maWxlID09PSAndHVpJyA/IHsKICAnQHRvbW93YW5nL2RzaC10dWknOiAnMC44LjEnLCAnQHRoZXdhaWZ1L2RzaC13YWlmdSc6ICcxLjUuMTgnLAp9IDogbnVsbAppZiAoIXBpbnMpIHRocm93IG5ldyBFcnJvcignVW5rbm93biB2ZXJpZmljYXRpb24gcHJvZmlsZTogJyArIHByb2ZpbGUpCmNvbnN0IG5vcm1hbGl6ZSA9IHByb2Nlc3MuYXJndlsyXSA9PT0gJy0tbm9ybWFsaXplJwpjb25zdCBvcmlnaW5hbE1hbmlmZXN0ID0gZnMucmVhZEZpbGVTeW5jKCdwYWNrYWdlLmpzb24nLCAndXRmOCcpCmNvbnN0IG1hbmlmZXN0ID0gSlNPTi5wYXJzZShvcmlnaW5hbE1hbmlmZXN0KQpsZXQgY2hhbmdlZCA9IGZhbHNlCmZvciAoY29uc3QgW25hbWUsIHZlcnNpb25dIG9mIE9iamVjdC5lbnRyaWVzKHBpbnMpKSB7CiAgY29uc3Qgcm9vdCA9IHBhdGgucmVzb2x2ZSgnbm9kZV9tb2R1bGVzJywgbmFtZSkKICBjb25zdCBpbnN0YWxsZWQgPSBKU09OLnBhcnNlKGZzLnJlYWRGaWxlU3luYyhwYXRoLmpvaW4ocm9vdCwgJ3BhY2thZ2UuanNvbicpLCAndXRmOCcpKQogIGlmIChpbnN0YWxsZWQubmFtZSAhPT0gbmFtZSkgdGhyb3cgbmV3IEVycm9yKGAke3Byb2ZpbGV9OiBwYWNrYWdlIG5hbWUgbWlzbWF0Y2g6IGV4cGVjdGVkICR7bmFtZX0sIGluc3RhbGxlZCAke2luc3RhbGxlZC5uYW1lfWApCiAgaWYgKGluc3RhbGxlZC52ZXJzaW9uICE9PSB2ZXJzaW9uKSB0aHJvdyBuZXcgRXJyb3IoYCR7cHJvZmlsZX06ICR7bmFtZX0gZXhwZWN0ZWQgZXhhY3QgJHt2ZXJzaW9ufSwgaW5zdGFsbGVkICR7aW5zdGFsbGVkLnZlcnNpb259YCkKICBjb25zdCBkZWNsYXJlZCA9IG1hbmlmZXN0LmRlcGVuZGVuY2llcz8uW25hbWVdCiAgaWYgKGRlY2xhcmVkICE9PSB2ZXJzaW9uKSB7CiAgICBpZiAobm9ybWFsaXplICYmIChkZWNsYXJlZCA9PT0gYF4ke3ZlcnNpb259YCB8fCBkZWNsYXJlZCA9PT0gYH4ke3ZlcnNpb259YCkpIHsKICAgICAgbWFuaWZlc3QuZGVwZW5kZW5jaWVzW25hbWVdID0gdmVyc2lvbgogICAgICBjaGFuZ2VkID0gdHJ1ZQogICAgICBjb25zb2xlLmxvZyhgd2FpZnVkc2g6IHBpbiAke3Byb2ZpbGV9OiAke25hbWV9OiAke2RlY2xhcmVkfSAtPiAke3ZlcnNpb259YCkKICAgIH0gZWxzZSB7CiAgICAgIHRocm93IG5ldyBFcnJvcihgJHtwcm9maWxlfTogJHtuYW1lfSBpbnN0YWxsZWQgJHtpbnN0YWxsZWQudmVyc2lvbn0sIGJ1dCBwYWNrYWdlLmpzb24gZGVwZW5kZW5jaWVzIGRlY2xhcmVzICR7SlNPTi5zdHJpbmdpZnkoZGVjbGFyZWQpfTsgZXhwZWN0ZWQgZXhhY3QgJHt2ZXJzaW9ufS4gUmUtcnVuIHRoZSB1cGRhdGVkIGluc3RhbGxlciB0byBub3JtYWxpemUgbGVnYWN5IHJhbmdlczsgY2hlY2sgb3ZlcnJpZGVzIGZvciBvdGhlciBzcGVjaWZpY2F0aW9ucy5gKQogICAgfQogIH0KICBpZiAoIW1hbmlmZXN0LmRzaD8ucHJvZmlsZT8uYnVuZGxlcz8uaW5jbHVkZXMobmFtZSkpIHRocm93IG5ldyBFcnJvcihgJHtwcm9maWxlfTogYnVuZGxlIG1pc3Npbmc6ICR7bmFtZX1gKQogIGlmIChuYW1lID09PSAnQHRoZXdhaWZ1L2RzaC13YWlmdScgfHwgbmFtZSA9PT0gJ0B0b21vd2FuZy9kc2gtdHVpJykgewogICAgY29uc3QgZXhwb3J0ZWQgPSBpbnN0YWxsZWQuZXhwb3J0cz8uWycuJ10KICAgIGNvbnN0IGVudHJ5ID0gKHR5cGVvZiBleHBvcnRlZCA9PT0gJ3N0cmluZycgPyBleHBvcnRlZCA6IGV4cG9ydGVkPy5pbXBvcnQgfHwgZXhwb3J0ZWQ/LmRlZmF1bHQpIHx8IGluc3RhbGxlZC5tYWluIHx8ICdsaWIvaW5kZXguanMnCiAgICBjb25zdCBzb3VyY2UgPSBmcy5yZWFkRmlsZVN5bmMocGF0aC5qb2luKHJvb3QsIGVudHJ5KSwgJ3V0ZjgnKQogICAgaWYgKC9pbXBvcnRccypce1tefV0qXGIoc2V0dGluZ3NOYW1lc3BhY2V8Zm9sZFBsYW5Nb2RlKVxiW159XSpcfVxzKmZyb21ccypbJyJdQGRlZXBzZWVrLWFpXC9kc2gtKHNldHRpbmdzfHBsYW4tbW9kZSlbJyJdLy50ZXN0KHNvdXJjZSkpIHsKICAgICAgdGhyb3cgbmV3IEVycm9yKGAke3Byb2ZpbGV9OiAke25hbWV9IHN0aWxsIGltcG9ydHMgYSByZW1vdmVkIEhhcm5lc3MgQVBJOyBpbnN0YWxsZWQgYXJ0aWZhY3QgaXMgaW5jb21wYXRpYmxlYCkKICAgIH0KICB9CiAgY29uc29sZS5sb2coYHdhaWZ1ZHNoOiB2ZXJpZmllZCAke3Byb2ZpbGV9OiAke25hbWV9QCR7dmVyc2lvbn1gKQp9CgovLyBCb3RoIGZyb250IGRvb3JzIGNsYWltIHRoZSBzYW1lIHBsdWdpbiBpZHMuIFRoZSBsYXVuY2hlciByZWNvbmNpbGVzIGJ1bmRsZXMgZnJvbQovLyBkZXBlbmRlbmNpZXMgYWZ0ZXIgY29tbWFuZHMsIHNvIHJlbW92ZSB0aGUgYWx0ZXJuYXRlIHJlZ2lzdHJhdGlvbiB0b28uIFVzZXIgZGF0YQovLyBpcyB1bnRvdWNoZWQ7IHRoZSBvcmlnaW5hbCBtYW5pZmVzdCBpcyBiYWNrZWQgdXAgYmVmb3JlIHRoaXMgbWlncmF0aW9uLgppZiAocHJvZmlsZSA9PT0gJ3R1aScgJiYgKG1hbmlmZXN0LmRzaC5wcm9maWxlLmJ1bmRsZXMuaW5jbHVkZXMoJ0BkZWVwc2Vlay1oYXJuZXNzLXR1aS9kc2gtdHVpJykgfHwgbWFuaWZlc3QuZGVwZW5kZW5jaWVzPy5bJ0BkZWVwc2Vlay1oYXJuZXNzLXR1aS9kc2gtdHVpJ10pKSB7CiAgaWYgKCFub3JtYWxpemUpIHRocm93IG5ldyBFcnJvcigndHVpOiBjb21wZXRpbmcgVFVJIGJ1bmRsZXMgYXJlIGVuYWJsZWQ7IHJlLXJ1biB0aGUgdXBkYXRlZCBpbnN0YWxsZXIgdG8gbWlncmF0ZScpCiAgbWFuaWZlc3QuZHNoLnByb2ZpbGUuYnVuZGxlcyA9IG1hbmlmZXN0LmRzaC5wcm9maWxlLmJ1bmRsZXMuZmlsdGVyKG5hbWUgPT4gbmFtZSAhPT0gJ0BkZWVwc2Vlay1oYXJuZXNzLXR1aS9kc2gtdHVpJykKICBkZWxldGUgbWFuaWZlc3QuZGVwZW5kZW5jaWVzWydAZGVlcHNlZWstaGFybmVzcy10dWkvZHNoLXR1aSddCiAgY2hhbmdlZCA9IHRydWUKICBjb25zb2xlLmxvZygnd2FpZnVkc2g6IGRpc2FibGVkIGNvbXBldGluZyBUVUkgYnVuZGxlIEBkZWVwc2Vlay1oYXJuZXNzLXR1aS9kc2gtdHVpOyBvcmlnaW5hbCBtYW5pZmVzdCBiYWNrZWQgdXAsIHVzZXIgZGF0YSByZXRhaW5lZCcpCn0KaWYgKGNoYW5nZWQpIHsKICBjb25zdCBiYWNrdXAgPSBgcGFja2FnZS5qc29uLndhaWZ1ZHNoLWJhY2t1cC0ke0RhdGUubm93KCl9LSR7cHJvY2Vzcy5waWR9YAogIGZzLndyaXRlRmlsZVN5bmMoYmFja3VwLCBvcmlnaW5hbE1hbmlmZXN0LCB7IGZsYWc6ICd3eCcsIG1vZGU6IDBvNjAwIH0pCiAgZnMud3JpdGVGaWxlU3luYygncGFja2FnZS5qc29uJywgSlNPTi5zdHJpbmdpZnkobWFuaWZlc3QsIG51bGwsIDIpICsgJ1xuJykKICBjb25zb2xlLmxvZyhgd2FpZnVkc2g6IHByb2ZpbGUgbWFuaWZlc3QgYmFja3VwOiAke2JhY2t1cH1gKQp9Cg==','base64').toString())"
NATIVE_HELPER_REPAIR='const fs=require("node:fs"),path=require("node:path");if(process.platform!=="win32"){const store="node_modules/.pnpm",roots=["node_modules/node-pty"];if(fs.existsSync(store)){for(const e of fs.readdirSync(store)){if(e.startsWith("node-pty@1.1.0"))roots.push(path.join(store,e,"node_modules/node-pty"));}}for(const root of roots){const manifest=path.join(root,"package.json");if(!fs.existsSync(manifest))continue;const p=JSON.parse(fs.readFileSync(manifest,"utf8"));if(p.name!=="node-pty"||p.version!=="1.1.0")continue;for(const relative of ["build/Release/spawn-helper","prebuilds/"+process.platform+"-"+process.arch+"/spawn-helper"]){const file=path.join(root,relative);if(!fs.existsSync(file))continue;const mode=fs.statSync(file).mode;if((mode&0o111)!==0o111){fs.chmodSync(file,mode|0o111);console.log("waifudsh: restored executable permission: "+file);}}}}'

OFFICIAL_REGISTRY="https://registry.npmjs.org/"
SUPPORTED_NODE_RANGE="^22.19.0 || >=24.0.0"
PNPM_VERSION="10.30.3"
PNPM_PACKAGE_SPEC="pnpm@${PNPM_VERSION}"
MOBILE_GATEWAY_CURRENT_SPEC="dsh-plugin-mobile-gateway@0.7.1"
DRY_RUN="${WAIFUDSH_DRY_RUN:-0}"
NODE_VERSION=""
NPM_VERSION=""
NPM_PREFIX=""
NPM_COMMAND=""
DSH_COMMAND=""
PNPM_COMMAND=""
HEALTHY_COMMAND_PATH=""
HEALTHY_COMMAND_VERSION=""
COMMAND_CANDIDATES=""
COMMAND_CANDIDATE_COUNT="0"
COMMAND_LAST_EXIT_CODE=""
COMMAND_LAST_OUTPUT=""
COMMAND_HAS_BROKEN_PNPM_SHIM="0"

usage() {
  printf '%s\n' '用法: waifu-install.sh [--dry-run]'
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --dry-run)
      DRY_RUN="1"
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      printf 'waifudsh: 未知选项：%s\n' "$1" >&2
      usage >&2
      exit 2
      ;;
  esac
  shift
done

progress() {
  progress_completed="$1"
  progress_label="$2"
  progress_total="13"
  progress_width="24"
  progress_filled=$(((progress_completed * progress_width + progress_total / 2) / progress_total))
  progress_empty=$((progress_width - progress_filled))
  progress_percent=$(((progress_completed * 100 + progress_total / 2) / progress_total))
  progress_bar=""
  while [ "$progress_filled" -gt 0 ]; do
    progress_bar="${progress_bar}#"
    progress_filled=$((progress_filled - 1))
  done
  while [ "$progress_empty" -gt 0 ]; do
    progress_bar="${progress_bar}-"
    progress_empty=$((progress_empty - 1))
  done
  printf '[%s] %3d%% %s\n' "$progress_bar" "$progress_percent" "$progress_label"
}

check_command_health() {
  health_command_name="$1"
  HEALTHY_COMMAND_PATH=""
  HEALTHY_COMMAND_VERSION=""
  COMMAND_CANDIDATES=""
  COMMAND_CANDIDATE_COUNT="0"
  COMMAND_LAST_EXIT_CODE=""
  COMMAND_LAST_OUTPUT=""
  COMMAND_HAS_BROKEN_PNPM_SHIM="0"

  health_old_ifs=$IFS
  IFS=:
  for health_directory in $PATH; do
    IFS=$health_old_ifs
    [ -n "$health_directory" ] || health_directory=.
    health_candidate="$health_directory/$health_command_name"
    if [ ! -e "$health_candidate" ] && [ ! -L "$health_candidate" ]; then
      IFS=:
      continue
    fi
    case "
$COMMAND_CANDIDATES
" in
      *"
$health_candidate
"*)
        IFS=:
        continue
        ;;
    esac

    if [ -z "$COMMAND_CANDIDATES" ]; then
      COMMAND_CANDIDATES="$health_candidate"
    else
      COMMAND_CANDIDATES="$COMMAND_CANDIDATES
$health_candidate"
    fi
    COMMAND_CANDIDATE_COUNT=$((COMMAND_CANDIDATE_COUNT + 1))

    if health_output=$("$health_candidate" --version 2>&1); then
      health_exit_code=0
    else
      health_exit_code=$?
    fi
    health_version=$(printf '%s\n' "$health_output" | awk 'NR == 1 { print; exit }')
    COMMAND_LAST_EXIT_CODE="$health_exit_code"
    COMMAND_LAST_OUTPUT="$health_output"
    case "$health_output" in
      *pnpm.cjs*MODULE_NOT_FOUND*|*MODULE_NOT_FOUND*pnpm.cjs*)
        COMMAND_HAS_BROKEN_PNPM_SHIM="1"
        ;;
    esac
    if [ "$health_exit_code" -eq 0 ] && [ -n "$health_version" ] && [ -z "$HEALTHY_COMMAND_PATH" ]; then
      HEALTHY_COMMAND_PATH="$health_candidate"
      HEALTHY_COMMAND_VERSION="$health_version"
    fi
    IFS=:
  done
  IFS=$health_old_ifs

  [ -n "$HEALTHY_COMMAND_PATH" ]
}

print_command_candidates() {
  candidates_name="$1"
  if [ "$COMMAND_CANDIDATE_COUNT" -gt 1 ]; then
    printf 'waifudsh: 检测到 %s 个 %s 候选路径：\n' "$COMMAND_CANDIDATE_COUNT" "$candidates_name"
    printf '%s\n' "$COMMAND_CANDIDATES" | while IFS= read -r candidate_path; do
      [ -n "$candidate_path" ] && printf '  - %s\n' "$candidate_path"
    done
  fi
}

promote_command_directory() {
  promoted_path="$1"
  promoted_directory=${promoted_path%/*}
  case "$PATH" in
    "$promoted_directory"|"$promoted_directory":*) ;;
    *) PATH="$promoted_directory:$PATH" ;;
  esac
  export PATH
  hash -r 2>/dev/null || true
}

assert_supported_node() {
  if ! check_command_health 'node'; then
    printf 'waifudsh: 未找到可运行的 Node.js。要求 Node.js %s。\n' "$SUPPORTED_NODE_RANGE" >&2
    printf '%s\n' 'waifudsh: 请安装或升级 Node.js，重新打开终端后再次运行安装器。' >&2
    exit 1
  fi
  promote_command_directory "$HEALTHY_COMMAND_PATH"
  NODE_VERSION=${HEALTHY_COMMAND_VERSION#v}
  node_core_version=${NODE_VERSION%%-*}
  case "$node_core_version" in
    *.*.*) ;;
    *)
      printf 'waifudsh: 无法解析 Node.js 版本：%s。要求 %s。\n' \
        "$HEALTHY_COMMAND_VERSION" "$SUPPORTED_NODE_RANGE" >&2
      exit 1
      ;;
  esac
  node_major=${node_core_version%%.*}
  node_remainder=${node_core_version#*.}
  node_minor=${node_remainder%%.*}
  node_patch=${node_remainder#*.}
  node_patch=${node_patch%%+*}
  case "$node_major:$node_minor:$node_patch" in
    *[!0-9:]*|:*|*::*|*:)
      printf 'waifudsh: 无法解析 Node.js 版本：%s。要求 %s。\n' \
        "$HEALTHY_COMMAND_VERSION" "$SUPPORTED_NODE_RANGE" >&2
      exit 1
      ;;
  esac
  if { [ "$node_major" -eq 22 ] && [ "$node_minor" -ge 19 ]; } || [ "$node_major" -ge 24 ]; then
    printf 'waifudsh: Node.js v%s 兼容性检查通过（要求 %s）。\n' "$NODE_VERSION" "$SUPPORTED_NODE_RANGE"
    return
  fi
  printf 'waifudsh: 当前 Node.js v%s 不受支持；要求 %s。\n' "$NODE_VERSION" "$SUPPORTED_NODE_RANGE" >&2
  printf '%s\n' 'waifudsh: 请先升级 Node.js，重新打开终端并确认 node --version 后再次运行安装器。' >&2
  exit 1
}

initialize_npm() {
  if ! check_command_health 'npm'; then
    printf '%s\n' 'waifudsh: npm 命令缺失或无法运行。请重新安装受支持的 Node.js。' >&2
    exit 1
  fi
  print_command_candidates 'npm'
  NPM_COMMAND="$HEALTHY_COMMAND_PATH"
  NPM_VERSION="$HEALTHY_COMMAND_VERSION"
  promote_command_directory "$NPM_COMMAND"
  if npm_prefix_output=$("$NPM_COMMAND" config get prefix 2>/dev/null); then
    NPM_PREFIX=$(printf '%s\n' "$npm_prefix_output" | awk 'NR == 1 { print; exit }')
  else
    NPM_PREFIX=""
  fi
  if [ -z "$NPM_PREFIX" ]; then
    printf '%s\n' 'waifudsh: 无法读取 npm global prefix；不能安全安装或修复全局运行时。' >&2
    exit 1
  fi
  npm_global_bin="$NPM_PREFIX/bin"
  case "$PATH" in
    "$npm_global_bin"|"$npm_global_bin":*) ;;
    *) PATH="$npm_global_bin:$PATH" ;;
  esac
  export PATH
  hash -r 2>/dev/null || true
}

print_pnpm_diagnostics() {
  printf '%s\n' 'waifudsh: pnpm 诊断信息：' >&2
  printf '  Node 版本：v%s\n' "${NODE_VERSION:-未知}" >&2
  printf '  npm 版本：%s\n' "${NPM_VERSION:-未知}" >&2
  printf '  npm global prefix：%s\n' "${NPM_PREFIX:-未知}" >&2
  if [ -n "$COMMAND_CANDIDATES" ]; then
    printf '%s\n' '  命中的 pnpm 路径：' >&2
    printf '%s\n' "$COMMAND_CANDIDATES" | while IFS= read -r candidate_path; do
      [ -n "$candidate_path" ] && printf '    - %s\n' "$candidate_path" >&2
    done
  else
    printf '%s\n' '  命中的 pnpm 路径：(未找到)' >&2
  fi
  printf '  pnpm --version 退出码：%s\n' "${COMMAND_LAST_EXIT_CODE:-未执行}" >&2
  printf '%s\n' 'waifudsh: 手工恢复：npm install --global --force pnpm@10.30.3 --registry=https://registry.npmjs.org/' >&2
  printf '%s\n' 'waifudsh: 恢复后请依次执行 pnpm --version、dsh --version，再重新运行本安装器。' >&2
}

ensure_runtime() {
  runtime_command="$1"
  runtime_name="$2"
  runtime_package="$3"
  runtime_registry="$4"
  runtime_force_repair="$5"

  runtime_expected_version="${runtime_package##*@}"
  if check_command_health "$runtime_command" && [ "$HEALTHY_COMMAND_VERSION" = "$runtime_expected_version" ]; then
    print_command_candidates "$runtime_name"
    promote_command_directory "$HEALTHY_COMMAND_PATH"
    case "$runtime_command" in
      dsh) DSH_COMMAND="$HEALTHY_COMMAND_PATH" ;;
      pnpm) PNPM_COMMAND="$HEALTHY_COMMAND_PATH" ;;
    esac
    printf 'waifudsh: %s 已就绪（%s，%s）。\n' \
      "$runtime_name" "$HEALTHY_COMMAND_VERSION" "$HEALTHY_COMMAND_PATH"
    return
  fi
  printf "waifudsh: %s 必须使用固定版本 %s（当前：%s）。\n" "$runtime_name" "$runtime_expected_version" "${HEALTHY_COMMAND_VERSION:-不可用}"
  print_command_candidates "$runtime_name"
  runtime_had_candidates="$COMMAND_CANDIDATE_COUNT"
  if [ "$runtime_command" = 'pnpm' ] && [ "$COMMAND_HAS_BROKEN_PNPM_SHIM" = '1' ]; then
    printf '%s\n' 'waifudsh: 检测到 pnpm 全局启动器损坏：启动器引用的 pnpm.cjs 不存在。' >&2
  elif [ "$runtime_had_candidates" -gt 0 ]; then
    printf 'waifudsh: 找到了 %s，但 %s 的版本不符或无法运行，将安装固定版本。\n' \
      "$runtime_name" "$runtime_command" >&2
  fi
  if [ "$DRY_RUN" = "1" ]; then
    if [ "$runtime_force_repair" = '1' ] || [ "$runtime_had_candidates" -gt 0 ]; then
      printf 'waifudsh: [计划修复] %s: npm install --global --force %s --registry=%s\n' \
        "$runtime_name" "$runtime_package" "$runtime_registry"
    else
      printf 'waifudsh: [计划安装] %s: npm install --global %s --registry=%s\n' \
        "$runtime_name" "$runtime_package" "$runtime_registry"
    fi
    return
  fi

  if [ "$runtime_force_repair" = '1' ] || [ "$runtime_had_candidates" -gt 0 ]; then
    printf 'waifudsh: 正在修复 %s。\n' "$runtime_name"
    env \
      "npm_config_registry=$runtime_registry" \
      "NPM_CONFIG_REGISTRY=$runtime_registry" \
      "npm_config_@deepseek-ai:registry=$runtime_registry" \
      "npm_config_@thewaifu:registry=$runtime_registry" \
      "npm_config_@liustack:registry=$runtime_registry" \
      "$NPM_COMMAND" install --global --force "$runtime_package" "--registry=$runtime_registry"
  else
    printf 'waifudsh: 正在安装 %s。\n' "$runtime_name"
    env \
      "npm_config_registry=$runtime_registry" \
      "NPM_CONFIG_REGISTRY=$runtime_registry" \
      "npm_config_@deepseek-ai:registry=$runtime_registry" \
      "npm_config_@thewaifu:registry=$runtime_registry" \
      "npm_config_@liustack:registry=$runtime_registry" \
      "$NPM_COMMAND" install --global "$runtime_package" "--registry=$runtime_registry"
  fi
  npm_global_bin="$NPM_PREFIX/bin"
  PATH="$npm_global_bin:$PATH"
  export PATH
  hash -r 2>/dev/null || true
  if ! check_command_health "$runtime_command" || [ "$HEALTHY_COMMAND_VERSION" != "$runtime_expected_version" ]; then
    if [ "$runtime_command" = 'pnpm' ]; then
      if [ "$COMMAND_HAS_BROKEN_PNPM_SHIM" = '1' ]; then
        printf '%s\n' 'waifudsh: pnpm 全局启动器损坏，自动修复后仍无法加载 pnpm.cjs。' >&2
      else
        printf '%s\n' 'waifudsh: pnpm 自动修复后仍无法执行。' >&2
      fi
      print_pnpm_diagnostics
    else
      printf 'waifudsh: %s 安装或修复完成，但 %s 仍不可用或版本不匹配。\n' \
        "$runtime_name" "$runtime_command" >&2
    fi
    exit 1
  fi
  print_command_candidates "$runtime_name"
  promote_command_directory "$HEALTHY_COMMAND_PATH"
  case "$runtime_command" in
    dsh) DSH_COMMAND="$HEALTHY_COMMAND_PATH" ;;
    pnpm) PNPM_COMMAND="$HEALTHY_COMMAND_PATH" ;;
  esac
  printf 'waifudsh: %s 已修复并通过检查（%s，%s）。\n' \
    "$runtime_name" "$HEALTHY_COMMAND_VERSION" "$HEALTHY_COMMAND_PATH"
}

run_dsh_plugin_add() {
  plugin_spec="$1"
  plugin_registry="$2"
  plugin_profile="${3:-web}"

  if env \
      "npm_config_registry=$plugin_registry" \
      "NPM_CONFIG_REGISTRY=$plugin_registry" \
      "npm_config_@deepseek-ai:registry=$plugin_registry" \
      "npm_config_@thewaifu:registry=$plugin_registry" \
      "npm_config_@liustack:registry=$plugin_registry" \
      node --eval "$INSTALL_OUTPUT" "$DSH_COMMAND" plugin --profile "$plugin_profile" add "$plugin_spec" --save-exact "--allow-build=$NATIVE_BUILD_SPEC"; then
    return
  else
    plugin_status=$?
  fi
  if ! check_command_health 'pnpm'; then
    if [ "$COMMAND_HAS_BROKEN_PNPM_SHIM" = '1' ]; then
      printf '%s\n' 'waifudsh: pnpm 全局启动器损坏：DSH 无法加载 pnpm.cjs。' >&2
    else
      printf '%s\n' 'waifudsh: pnpm 在插件安装期间变为不可用。' >&2
    fi
    print_pnpm_diagnostics
  fi
  return "$plugin_status"
}

install_plugin() {
  plugin_index="$1"
  plugin_name="$2"
  plugin_spec="$3"
  plugin_registry="$4"
  plugin_profile="${5:-web}"
  printf '[%s/9] %s: dsh plugin --profile %s add %s --save-exact --allow-build=node-pty@1.1.0（registry: %s）\n' \
    "$plugin_index" "$plugin_name" "$plugin_profile" "$plugin_spec" "$plugin_registry"
  if [ "$DRY_RUN" != "1" ]; then
    if [ "$plugin_index" = '1' ] || [ "$plugin_index" = '8' ]; then
      "$DSH_COMMAND" plugin --profile "$plugin_profile" exec node --eval "$STORE_RECOVERY"
    fi
    run_dsh_plugin_add "$plugin_spec" "$plugin_registry" "$plugin_profile"
  fi
  progress_step=$((plugin_index + 2))
  if [ "$DRY_RUN" = "1" ]; then
    progress "$progress_step" "$plugin_name 已加入计划"
  else
    progress "$progress_step" "$plugin_name 安装完成"
  fi
}

offer_web_start() {
  start_choice="${WAIFUDSH_START_WEB:-ask}"
  start_has_terminal=0
  # stdin contains the downloaded script under curl | sh. Read only /dev/tty.
  if ( : <> /dev/tty ) 2>/dev/null; then
    start_has_terminal=1
  fi
  if [ "$start_choice" = 'ask' ]; then
    if [ "$start_has_terminal" = '1' ]; then
      while :; do
        printf 'waifudsh: 是否现在启动 WebUI 并打开浏览器？[y/N] ' > /dev/tty
        if ! IFS= read -r start_choice < /dev/tty; then start_choice=no; fi
        case "$start_choice" in
          y|Y|yes|YES|Yes|n|N|no|NO|No|'') break ;;
          *) printf '%s\n' '请输入 yes 或 no。' > /dev/tty ;;
        esac
      done
    else
      start_choice=no
      printf '%s\n' 'waifudsh: 未检测到交互终端，跳过启动询问。'
    fi
  fi
  case "$start_choice" in
    y|Y|yes|YES|Yes)
      printf '%s\n' 'waifudsh: 正在启动 WebUI，浏览器将由 DSH 打开；此终端运行服务，Ctrl+C 可停止。'
      if [ "$start_has_terminal" = '1' ]; then
        exec "$DSH_COMMAND" --profile web < /dev/tty
      else
        exec "$DSH_COMMAND" --profile web < /dev/null
      fi
      ;;
    n|N|no|NO|No|'') printf '%s\n' 'waifudsh: 暂不启动。稍后可使用上方命令启动 WebUI。' ;;
    *) printf '%s\n' 'waifudsh: WAIFUDSH_START_WEB 只允许 ask、yes 或 no；未启动 WebUI。' >&2; return 2 ;;
  esac
}

progress 0 '开始检查安装环境'
assert_supported_node
initialize_npm
ensure_runtime 'dsh' 'DeepSeek Harness' '@deepseek-ai/dsh@0.1.5-rc.1' "$OFFICIAL_REGISTRY" '0'
if [ "$DRY_RUN" = '1' ]; then
  printf '%s\n' 'waifudsh: [计划] 固定并核验 Harness 核心依赖 0.1.5-rc.1。'
else
  runtime_pin_root=$(node --eval "$RUNTIME_PIN" "$DSH_COMMAND" --prepare)
  if [ -n "$runtime_pin_root" ]; then
    printf '%s\n' 'waifudsh: 正在固定 Harness 核心依赖到 0.1.5-rc.1。'
    env "npm_config_registry=$OFFICIAL_REGISTRY" "NPM_CONFIG_REGISTRY=$OFFICIAL_REGISTRY" \
      "npm_config_@deepseek-ai:registry=$OFFICIAL_REGISTRY" \
      "$NPM_COMMAND" install --prefix "$runtime_pin_root" --omit=dev --force "--registry=$OFFICIAL_REGISTRY"
    env "npm_config_registry=$OFFICIAL_REGISTRY" "NPM_CONFIG_REGISTRY=$OFFICIAL_REGISTRY" \
      "npm_config_@deepseek-ai:registry=$OFFICIAL_REGISTRY" \
      "$NPM_COMMAND" dedupe --prefix "$runtime_pin_root" --omit=dev --force "--registry=$OFFICIAL_REGISTRY"
  fi
  node --eval "$RUNTIME_PIN" "$DSH_COMMAND" --verify
fi
progress 1 'DeepSeek Harness 检查完成'
ensure_runtime 'pnpm' 'pnpm' "$PNPM_PACKAGE_SPEC" "$OFFICIAL_REGISTRY" '1'
progress 2 'pnpm 检查完成'
PROFILE_RESET="eval(Buffer.from('Ly8gQXJjaGl2ZSB0aGUgb2xkIGNvbXBvc2l0aW9uIGJlZm9yZSBpbnN0YWxsaW5nIHRoZSBtYW5hZ2VkIGNvbGxlY3Rpb24gZnJvbSBkZWZhdWx0cy4KY29uc3QgZnMgPSByZXF1aXJlKCdub2RlOmZzJykKY29uc3QgcGF0aCA9IHJlcXVpcmUoJ25vZGU6cGF0aCcpCmNvbnN0IG9zID0gcmVxdWlyZSgnbm9kZTpvcycpCmNvbnN0IGhvbWUgPSBwYXRoLnJlc29sdmUocHJvY2Vzcy5lbnYuRFNIX0hPTUUgfHwgcGF0aC5qb2luKG9zLmhvbWVkaXIoKSwgJy5kc2gnKSkKaWYgKGhvbWUgPT09IHBhdGgucGFyc2UoaG9tZSkucm9vdCB8fCBob21lID09PSBvcy5ob21lZGlyKCkpIHRocm93IG5ldyBFcnJvcignd2FpZnVkc2g6IHVuc2FmZSBEU0hfSE9NRTsgcmVmdXNpbmcgdG8gcmVzZXQgcHJvZmlsZXMnKQpjb25zdCBwcm9maWxlcyA9IHBhdGguam9pbihob21lLCAncHJvZmlsZXMnKQppZiAoZnMuZXhpc3RzU3luYyhwcm9maWxlcykgJiYgZnMubHN0YXRTeW5jKHByb2ZpbGVzKS5pc1N5bWJvbGljTGluaygpKSB0aHJvdyBuZXcgRXJyb3IoJ3dhaWZ1ZHNoOiBwcm9maWxlcyBkaXJlY3RvcnkgaXMgYSBzeW1saW5rOyByZXNvbHZlIGl0IGJlZm9yZSBjbGVhbiBpbnN0YWxsYXRpb24nKQpjb25zdCBzb3VyY2VzID0gWwogIFsncHJvZmlsZXMvd2ViJywgJ3Byb2ZpbGVzL3dlYiddLAogIFsncHJvZmlsZXMvdHVpJywgJ3Byb2ZpbGVzL3R1aSddLAogIFsnY29yZGlzLnBhdGNoLnltbCcsICdjb3JkaXMucGF0Y2gueW1sJ10sCiAgWydzZXR0aW5ncy55YW1sJywgJ3NldHRpbmdzLnlhbWwnXSwKICBbJ3BucG0td29ya3NwYWNlLnlhbWwnLCAncG5wbS13b3Jrc3BhY2UueWFtbCddLAogIFsnLm5wbXJjJywgJy5ucG1yYyddLAogIFsnLnBucG1maWxlLmNqcycsICcucG5wbWZpbGUuY2pzJ10sCl0uZmlsdGVyKChbc291cmNlXSkgPT4geyB0cnkgeyBmcy5sc3RhdFN5bmMocGF0aC5qb2luKGhvbWUsIHNvdXJjZSkpOyByZXR1cm4gdHJ1ZSB9IGNhdGNoIChlcnJvcikgeyBpZiAoZXJyb3IuY29kZSA9PT0gJ0VOT0VOVCcpIHJldHVybiBmYWxzZTsgdGhyb3cgZXJyb3IgfSB9KQppZiAoc291cmNlcy5sZW5ndGgpIHsKICBjb25zdCBiYWNrdXAgPSBwYXRoLmpvaW4oaG9tZSwgJ3dhaWZ1ZHNoLWJhY2t1cHMnLCBgY2xlYW4tJHtEYXRlLm5vdygpfS0ke3Byb2Nlc3MucGlkfWApCiAgZnMubWtkaXJTeW5jKHBhdGguZGlybmFtZShiYWNrdXApLCB7IHJlY3Vyc2l2ZTogdHJ1ZSwgbW9kZTogMG83MDAgfSkKICBmcy5ta2RpclN5bmMoYmFja3VwLCB7IG1vZGU6IDBvNzAwIH0pCiAgZnMubWtkaXJTeW5jKHBhdGguam9pbihiYWNrdXAsICdwcm9maWxlcycpLCB7IG1vZGU6IDBvNzAwIH0pCiAgY29uc3QgbW92ZWQgPSBbXQogIHRyeSB7CiAgICBmb3IgKGNvbnN0IFtzb3VyY2UsIGRlc3RpbmF0aW9uXSBvZiBzb3VyY2VzKSB7CiAgICAgIGZzLnJlbmFtZVN5bmMocGF0aC5qb2luKGhvbWUsIHNvdXJjZSksIHBhdGguam9pbihiYWNrdXAsIGRlc3RpbmF0aW9uKSkKICAgICAgbW92ZWQucHVzaChbc291cmNlLCBkZXN0aW5hdGlvbl0pCiAgICB9CiAgfSBjYXRjaCAoZXJyb3IpIHsKICAgIGZvciAoY29uc3QgW3NvdXJjZSwgZGVzdGluYXRpb25dIG9mIG1vdmVkLnJldmVyc2UoKSkgZnMucmVuYW1lU3luYyhwYXRoLmpvaW4oYmFja3VwLCBkZXN0aW5hdGlvbiksIHBhdGguam9pbihob21lLCBzb3VyY2UpKQogICAgdGhyb3cgZXJyb3IKICB9CiAgZnMud3JpdGVGaWxlU3luYyhwYXRoLmpvaW4oYmFja3VwLCAnUkVBRE1FLnR4dCcpLCAnd2FpZnVkc2ggY2xlYW4taW5zdGFsbCBiYWNrdXBcbk9sZCBXZWIvVFVJIHByb2ZpbGVzLCBzaGFyZWQgcGF0Y2hlcyBhbmQgbW9kZWwgc2V0dGluZ3MgYXJlIGluYWN0aXZlLiBTZXNzaW9ucyBhbmQgY3JlZGVudGlhbHMgb3V0c2lkZSB0aGVzZSBwcm9maWxlcyByZW1haW4gaW4gRFNIX0hPTUUuXG5JZiBpbnN0YWxsYXRpb24gZmFpbHMsIGNvcnJlY3QgdGhlIGVycm9yIGFuZCByZXJ1bi4gRG8gbm90IHJlc3RvcmUgb2xkIHBhdGNoZXMgd2hvbGVzYWxlOiB0aGV5IG1heSBjb250YWluIGR1cGxpY2F0ZSBwcm92aWRlciByZWdpc3RyYXRpb25zLlxuJywgeyBtb2RlOiAwbzYwMCB9KQogIGNvbnNvbGUubG9nKGB3YWlmdWRzaDog5penIFdlYi9UVUkg546v5aKD44CB5qih5Z6L6K6+572u5Y+K5YWo5bGA6KGl5LiB5bey56e75YWl5aSH5Lu977yaJHtiYWNrdXB9YCkKICBjb25zb2xlLmxvZygnd2FpZnVkc2g6IOWwhuS7jum7mOiupOaooeadv+mHjeaWsOWuieijhembhuWQiOaPkuS7tu+8m+aXp+mineWkluaPkuS7tuWSjOiHquWumuS5ieihpeS4geS4jeWGjeWKoOi9veOAgicpCn0gZWxzZSBjb25zb2xlLmxvZygnd2FpZnVkc2g6IOacquWPkeeOsOaXpyBXZWIvVFVJIOeOr+Wig++8jOW8gOWni+WFqOaWsOWuieijheOAgicpCg==','base64').toString())"
if [ "$DRY_RUN" = '1' ]; then
  printf '%s\n' 'waifudsh: [计划] 备份并清理旧 Web/TUI profile 与全局 cordis.patch.yml，重新安装固定集合。'
else
  node --eval "$PROFILE_RESET"
fi

install_plugin 1 'DSH Waifu' '@thewaifu/dsh-waifu@1.5.18' "$OFFICIAL_REGISTRY"
install_plugin 2 'DSH TokenFactory' '@thewaifu/dsh-token-factory@1.1.3' "$OFFICIAL_REGISTRY"
install_plugin 3 'Modlens' '@liustack/modlens@3.26.1' "$OFFICIAL_REGISTRY"
install_plugin 4 'Better Sidebar' 'dsh-better-sidebar@0.19.1' "$OFFICIAL_REGISTRY"
install_plugin 5 'Mobile Gateway' "$MOBILE_GATEWAY_CURRENT_SPEC" "$OFFICIAL_REGISTRY"
install_plugin 6 'Plugin Market' '@thewaifu/dsh-market@1.1.0' "$OFFICIAL_REGISTRY"
install_plugin 7 'Deep Signal' '@thewaifu/dsh-deepsignal@1.1.2' "$OFFICIAL_REGISTRY"

install_plugin 8 'DSH TUI' '@tomowang/dsh-tui@0.8.1' "$OFFICIAL_REGISTRY" 'tui'
install_plugin 9 'TUI Waifu' '@thewaifu/dsh-waifu@1.5.18' "$OFFICIAL_REGISTRY" 'tui'

for verified_profile in web tui; do
  printf 'waifudsh: 修复原生依赖并核验 %s profile。\n' "$verified_profile"
  if [ "$DRY_RUN" != "1" ]; then
    "$DSH_COMMAND" plugin --profile "$verified_profile" exec node --eval "$PROFILE_VERIFY" "$verified_profile" --normalize
    env "npm_config_registry=$OFFICIAL_REGISTRY" "NPM_CONFIG_REGISTRY=$OFFICIAL_REGISTRY" \
      node --eval "$INSTALL_OUTPUT" "$DSH_COMMAND" plugin --profile "$verified_profile" install --lockfile-only
    env "npm_config_registry=$OFFICIAL_REGISTRY" "NPM_CONFIG_REGISTRY=$OFFICIAL_REGISTRY" \
      "$DSH_COMMAND" plugin --profile "$verified_profile" rebuild "$NATIVE_BUILD_SPEC"
    "$DSH_COMMAND" plugin --profile "$verified_profile" exec node --eval "$NATIVE_HELPER_REPAIR"
    "$DSH_COMMAND" plugin --profile "$verified_profile" exec node --eval "$PROFILE_VERIFY" "$verified_profile"
  fi
done
progress 13 '原生依赖与固定版本检查完成'

if [ "$DRY_RUN" = "1" ]; then
  printf '%s\n' 'waifudsh: dry-run 完成，未执行安装。'
else
  printf '%s\n' 'waifudsh: 已完成 Web 七插件和 TUI 安装。启动：dsh --profile web 或 dsh --profile tui。'
  quoted_dsh_path=$(printf '%s' "$DSH_COMMAND" | sed "s/'/'\\\\''/g")
  printf "waifudsh: 本次验证的启动命令：\\n  '%s' --profile web\\n  '%s' --profile tui\\n" "$quoted_dsh_path" "$quoted_dsh_path"
  printf '%s\n' 'waifudsh: 若新终端的 dsh --version 不是 0.1.5-rc.1，请使用上面的绝对路径，并调整 PATH 中旧 DSH 的优先级。'
  offer_web_start
fi
