تشغيل dsh دون واجهة عبر systemd على VPS
شغّل dsh كخدمة systemd على VPS بحساب مخصص وإصدار ثابت، واضبط Restart وjournalctl، ثم اتصل بالواجهة عبر نفق SSH بعد إغلاق جلسة SSH.
تشغيل dsh دون واجهة تفاعلية على VPS، وليس داخل طرفية
يتطلب تشغيل dsh دون واجهة تفاعلية على VPS ملف وحدة systemd واحداً ومستخدماً مخصصاً لامتلاكه. dsh هو مشغّل سطر الأوامر لـDeepSeek Harness، وهو بيئة تشغيل الوكلاء من DeepSeek، وقد نُشر بموجب ترخيص MIT في إصدار معاينة للمطورين في August 2026. الـharness هو البرنامج المحيط بالنموذج، وليس النموذج نفسه. لذلك، ما تضعه تحت إدارة systemd هو الحلقة والأدوات والصلاحيات، وليس عملية الاستدلال الخاصة بـDeepSeek. يطلب منك دليل البدء السريع كتابة npx @deepseek-ai/dsh web، وهذا صحيح، لكن العملية تتوقف فور إغلاق جلسة SSH (الصدفة الآمنة) أيضاً.
يعالج ملف الوحدة أربعة أمور دفعة واحدة. تعود الخدمة إلى العمل بعد إعادة التشغيل. يذهب خرجها إلى journal بدلاً من مروره على الشاشة. تعمل الخدمة بحساب ليس root. كما تعمل بالإصدار الذي اخترته. وهذا مهم هنا أكثر من المعتاد، لأن المشروع الأساسي يذكر ذلك بأحرف كبيرة:
DeepSeek Harness حالياً في معاينة للمطورين ويتغير بسرعة. ستحدث تغييرات تكسر التوافق.
يفترض هذا الدليل أن dsh يعمل لديك يدوياً بالفعل. إذا لم يكن كذلك، فابدأ بـتثبيت DeepSeek Harness على VPS، ثم ارجع بعد أن يقدّم npx @deepseek-ai/dsh web صفحة.
ابدأ بـNode، لأن npm لن يحذّرك
node -vإصدار Node المضمّن في حزمة Ubuntu 24.04 هو Node 18 (الإصدار 18.19.1 حتى August 2026)، وهو قديم بالنسبة إلى حزمة نُشرت هذا العام. لا تنشر @deepseek-ai/dsh أي حقل engines، لذلك لا يعرض npm تحذير EBADENGINE عندما يكون إصدار Node لديك قديماً جداً. يظهر الفشل بدلاً من ذلك وقت التشغيل، على شكل خطأ في الصياغة أو مكوّن مضمّن مفقود. وهذا أسوأ بكثير لاكتشاف المشكلة فيه. ثبّت إصداراً حالياً من إصدارات الدعم طويل الأمد (LTS) عبر NodeSource:
curl -fsSL https://deb.nodesource.com/setup_22.x -o /tmp/nodesource_setup.sh
less /tmp/nodesource_setup.sh
sudo -E bash /tmp/nodesource_setup.sh
sudo apt install -y nodejs
node -vيجب أن يعرض node -v الآن إصدار v22. يوجد السطر less لأن تمرير نص برمجي عن بُعد مباشرةً إلى bash يؤدي إلى تشغيل تعليمات برمجية لم تقرأها.
تحقّق من تشغيله قبل كتابة unit
npx @deepseek-ai/dsh@0.1.0-rc.7 webاتركه قيد التشغيل. افتح جلسة SSH ثانية:
curl -fsS http://127.0.0.1:3080/ -o /dev/null && echo upيعني up أن ملف تعريف الويب يستمع على loopback، وهو العنوان الذي يرتبط به افتراضياً. ويعني curl: (7) Failed to connect to 127.0.0.1 port 3080: Connection refused أنه لا يستمع عليه، وأن الطرفية الأولى توضح السبب. أوقف التشغيل اليدوي باستخدام Ctrl+C قبل المتابعة: تفشل unit التي تحاول الارتباط بمنفذ تشغله عملية أخرى بالفعل مع Error: listen EADDRINUSE: address already in use 127.0.0.1:3080.
كان 0.1.0-rc.7 هو الإصدار المنشور في 18 August 2026. تحقّق من الإصدار الحالي باستخدام npm view @deepseek-ai/dsh version، ثم ثبّت الإصدار الذي تقرر تشغيله.
ثبّت الإصدار الذي حدّدته على مستوى النظام
استخدام npx داخل ملف الوحدة هو الخيار الخاطئ. فهو يحل إصدار الحزمة عند بدء العملية، لذلك قد تؤدي إعادة التشغيل بعد ثلاثة أشهر إلى تشغيل إصدار مختلف من وكيل في مرحلة المعاينة، من دون إجراء أي تغيير من جانبك. كما يحتاج إلى إمكانية الوصول إلى سجل npm أثناء الإقلاع، ما يحوّل جهازاً عاملاً إلى وحدة فاشلة في اليوم الذي يصبح فيه السجل بطيئاً. ثبّت البرنامج مرة واحدة بإصدار دوّنته:
sudo npm install -g @deepseek-ai/dsh@0.1.0-rc.7
command -v dsh
npm ls -g --depth=0 @deepseek-ai/dshيطبع command -v dsh القيمة /usr/bin/dsh عندما يكون npm قد جاء من NodeSource، ويطبع /usr/local/bin/dsh عندما يكون قد جاء من حزمة Ubuntu الخاصة. استخدم المسار الذي طبعه فعلياً في ملف الوحدة. يطبع npm ls -g الإصدار الدقيق. وهذا هو ما تحتاج إليه بعد ستة أسابيع عندما يتغير السلوك ولا تعود تتذكر ما ثبّتَّه. إذا فشل التثبيت، أو لم يطبع command -v dsh شيئاً بعد ذلك، أو لم يكن الإصدار الناتج هو الإصدار الذي طلبته، فاتبع خطوات معالجة أخطاء تثبيت dsh وإصدارها المعتادة قبل كتابة ملف الوحدة.
A user that owns the service and nothing else
The agent runs shell commands. That is its job. Running it as root makes every tool call a root tool call, so give it its own account with no login shell.
sudo useradd --system --create-home --home-dir /var/lib/dsh --shell /usr/sbin/nologin dsh
sudo install -d -o dsh -g dsh -m 750 /var/lib/dsh/harness /var/lib/dsh/workspace
id dsh/var/lib/dsh/harness becomes DSH_HOME, the directory dsh keeps profiles in. A profile is a named stack of plugin bundles with your own patch layer on top, and the web and headless profiles build themselves from shipped templates the first time you boot them. Anything you add to that stack later runs as this user with the agent's own file and shell access, so vetting a plugin before you install it belongs to the same job as creating the account. That first boot writes files and may fetch bundles, so do it by hand where you can watch it.
sudo -u dsh env HOME=/var/lib/dsh DSH_HOME=/var/lib/dsh/harness /usr/bin/dsh --profile webSet HOME explicitly rather than trusting what sudo does with it, because whether sudo rewrites HOME for a non-login command depends on the set_home setting in /etc/sudoers. Get it wrong and the first run drops cache directories into your home directory owned by dsh, and the service later cannot find its own state. Stop it with Ctrl+C once the curl check returns up.
ملف الوحدة
اكتب /etc/systemd/system/dsh.service:
[Unit]
Description=DeepSeek Harness (dsh) web profile
Documentation=https://github.com/deepseek-ai/deepseek-harness
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Type=exec
User=dsh
Group=dsh
WorkingDirectory=/var/lib/dsh/workspace
Environment=HOME=/var/lib/dsh
Environment=DSH_HOME=/var/lib/dsh/harness
ExecStart=/usr/bin/dsh --profile web
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
SyslogIdentifier=dsh
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.targetيأخذ ExecStart= المسار المطلق الذي حصلت عليه من command -v dsh. يبحث systemd في قائمة ثابتة من المسارات عن اسم أمر مجرد، لكن هذه القائمة ليست PATH الخاصة بالصدفة، لذلك يزيل المسار المطلق الحاجة إلى التخمين.
يحدد WorkingDirectory= المكان الذي تُحل فيه المسارات النسبية، وهو المكان الذي تبدأ منه استدعاءات الأدوات التي تشغّل ls من دون وسيطات. وجّه هذا المتغير إلى مساحة العمل التي تمنحها للوكيل. إذا كان الدليل مفقوداً أو تعذر على مستخدم الخدمة الدخول إليه، تفشل الوحدة مع status=200/CHDIR قبل تشغيل dsh أصلاً.
يعزل ProtectHome=true كلاً من /home و/root عن العملية. وهذا آمن هنا لأن كل ما تصل إليه الخدمة موجود ضمن /var/lib/dsh. إذا وجّهت مساحة العمل إلى مسار ضمن /home، فسيبلغ الوكيل بأن الدليل غير موجود. وقد يبدو ذلك مربكاً إلى أن تتذكر هذا السطر. يجعل ProtectSystem=full كلاً من /usr و/boot و/etc للقراءة فقط، مع أن الخدمة لا تحتاج إلى الكتابة إليها.
قد يبدو الانتقال إلى مستوى أبعد خياراً مناسباً، لكنه يكون خاطئاً عادةً. يجعل ProtectSystem=strict نظام الملفات بأكمله للقراءة فقط، باستثناء أنظمة الملفات الوهمية الخاصة بالنواة، لذلك يفشل أول استدعاء أداة يحاول كتابة ملف مع EROFS: read-only file system. إذا أردت هذا المستوى من العزل، فأضف ReadWritePaths=/var/lib/dsh في التعديل نفسه.
ما قيمة Type= التي تنتمي إلى هذا الموضع؟
Type=exec، لأن dsh يعمل في الواجهة الأمامية ولا ينشئ عملية فرعية مطلقاً. وما يقدّمه ذلك مقارنةً بالقيمة الافتراضية هو رسالة خطأ فعلية. عند استخدام Type=simple، يعتبر systemd بدء التشغيل ناجحاً بمجرد أن ينشئ العملية الفرعية، وقبل أن يعرف حتى ما إذا كان الملف التنفيذي موجوداً. لذلك يعيد systemctl start dsh النتيجة بنجاح، ولا يظهر الفشل إلا في journal. عند استخدام Type=exec، ينتظر systemd نجاح execve()، ولذلك يؤدي الخطأ المطبعي في ExecStart= إلى فشل الأمر الذي كتبته للتو، وسترى الخطأ فيه.
الإجابتان الخاطئتان تؤديان كلتاهما إلى تعليق العملية. تخبر Type=forking systemd بالانتظار حتى تنتهي العملية الأب، لكن dsh لا ينتهي مطلقاً. لذلك يتوقف بدء التشغيل حتى تنقضي مهلة TimeoutStartSec، وهي 90 ثانية افتراضياً، ثم يعرض Job for dsh.service failed because a timeout was exceeded.. أما Type=notify فتنتظر رسالة READY=1 عبر sd_notify، وتتعطل بالطريقة نفسها إذا لم ترسلها عملية Node لا تنتهي. يغطي المقارنة الكاملة بين أنواع خدمات systemd بقية التفاصيل، بما في ذلك الحالات التي يستحق فيها إعداد notify الجهد.
قواعد إعادة التشغيل التي تفشل بوضوح
Restart=on-failure يعيد التشغيل عند الخروج برمز غير صفري أو عند تلقي إشارة قاتلة، ويترك الوحدة دون تغيير بعد الخروج الطبيعي. هذا هو السلوك المطلوب من إصدار للمعاينة. إذا خرج dsh في أي وقت بالرمز 0 لأنه قرأ إعداداً لم يعجبه، تتوقف الوحدة وتبقى متوقفة، ويعرض systemctl status dsh القيمة inactive (dead) حيث يمكنك رؤيتها. يحوّل Restart=always الحدث نفسه إلى حلقة إعادة تشغيل تبدو سليمة عند المراقبة من بعيد.
حد معدل إعادة التشغيل هو الجزء الذي يغفله الناس. الإعدادات الافتراضية في systemd هي خمس عمليات بدء خلال عشر ثوانٍ، ومع RestartSec=5s لن تصل أبداً إلى خمس عمليات بدء ضمن نافذة مدتها عشر ثوانٍ، ولذلك تستمر وحدة تتعطل عند بدء التشغيل في إعادة التشغيل إلى الأبد، ولا يعرف ذلك إلا السجل. يعني StartLimitIntervalSec=300 مع StartLimitBurst=5 أن خمس حالات فشل خلال خمس دقائق تكفي: تتوقف systemd عن المحاولة وتضع الوحدة في حالة failed، مع تسجيل Start request repeated too quickly.. امسح هذه الحالة باستخدام sudo systemctl reset-failed dsh بعد إصلاح السبب. يجب وضع كلا الإعدادين في [Unit]، وليس في [Service]، وتتجاهلهما systemd بصمت إذا وُضعا في القسم الخطأ.
شغّله، ثم تحقّق منه
sudo systemctl daemon-reload
sudo systemctl enable --now dsh
systemctl status dshينفّذ enable --now مهمتين. يعيد enable الخدمة بعد إعادة التشغيل، ويشغّلها --now في جلسة الإقلاع الحالية. يختفي systemctl start المجرد بعد إعادة التشغيل التالية، وتؤدي تحديثات النواة إلى إعادة التشغيل.
يجب أن يعرض systemctl status dsh القيمة Active: active (running)، وسطر Main PID، وسطر Memory:. ثم تحقّق من عنوان الاستماع:
sudo ss -lntp | grep 3080تريد أن ترى 127.0.0.1:3080. إذا رأيت 0.0.0.0:3080، فقد عدّل شيء ما عنوان الربط، وأصبحت الوكيلة متاحة على الإنترنت العام. اسم العملية في ذلك الناتج هو node، وليس dsh، لأن الملف الثنائي dsh هو برنامج Node، ولذلك لا يعثر pgrep -x dsh على شيء. استخدم systemctl show -p MainPID dsh بدلاً منه.
أعد التشغيل مرة واحدة. الخدمة التي لم تنجح قط في العمل بعد إعادة التشغيل ليست خدمة مكتملة بعد.
sudo rebootأعد الاتصال ونفّذ systemctl is-active dsh. سيطبع active.
قراءة السجلات باستخدام journalctl
كل ما يكتبه dsh إلى stdout وstderr يظهر في journal ضمن اسم الوحدة.
journalctl -u dsh -f
journalctl -u dsh -n 200 --no-pager
journalctl -u dsh --since "10 min ago" -p errيتابع -f الأسطر الجديدة، ويعرض -n آخر N أسطر، ويصفّي -p err النتائج حسب الأولوية. يحدد SyslogIdentifier=dsh في الوحدة سبب وسم تلك الأسطر بالوسم dsh بدلاً من node، وهذا مهم عند قراءة مخرجات journal غير المصفّاة حسب الوحدة لأول مرة.
تحقق من بقاء journal بعد عمليات إعادة التشغيل قبل أن تحتاج إليه:
journalctl -u dsh -b -1إذا أظهر ذلك Specifying boot ID or boot offset has no effect, no persistent journal was found، فإن journal موجود في /run، وتُحذف محتوياته عند كل إعادة تشغيل. أنشئ الدليل وأعد تشغيل البرنامج الخفي:
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journaldالوصول إلى واجهة المستخدم عبر نفق SSH، وليس عبر منفذ عام
يخدم dsh واجهة الويب في 127.0.0.1:3080، ويرفض خدمتها في أي مكان آخر. عند طلب --host 0.0.0.0، يتوقف ويعرض ما يلي:
error: --host 0.0.0.0 is intentionally not supported yet for safety: it would expose remote code execution to the network; use 127.0.0.1 insteadهذا ليس قيداً ينبغي الالتفاف عليه. تقود واجهة الويب البرمجية الوكيل، وينفذ الوكيل أوامر shell، لذلك فإن المنفذ القابل للوصول يوفر shell على VPS الخاص بك لأي شخص يعثر عليه. يذكر المشرفون أن عدم بناء المصادقة عن بُعد هو سبب تثبيت الربط على loopback. تستحق معرفة ما يعنيه السطر 127.0.0.1:3080 فعلياً في ناتج بدء التشغيل القراءة قبل محاولة نقله. أعد توجيه المنفذ من جهازك:
ssh -N -L 3080:127.0.0.1:3080 you@203.0.113.10يفتح -L 3080:127.0.0.1:3080 المنفذ 3080 على حاسوبك المحمول، ويرسل كل ما يصل إليه إلى 127.0.0.1:3080 بعد حله على VPS. يعني -N عدم تشغيل أي أمر عن بُعد، لذلك تبقي الجلسة النفق مفتوحاً فقط. اتركه قيد التشغيل وافتح http://127.0.0.1:3080/ في المتصفح. أدخل مفتاح DeepSeek API هناك، ضمن Settings ثم Models، واختر دليل مساحة العمل. وجّه مساحة العمل إلى /var/lib/dsh/workspace، وهو الدليل الذي يملكه مستخدم الخدمة، وإلا تفشل أدوات الملفات في الوكيل مع EACCES: permission denied.
إذا كان المنفذ 3080 مستخدماً على حاسوبك المحمول، يعرض ssh ما يلي:
bind [127.0.0.1]:3080: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3080اختر منفذاً محلياً مختلفاً باستخدام ssh -N -L 3081:127.0.0.1:3080 you@203.0.113.10، ثم انتقل إلى http://127.0.0.1:3081/ في المتصفح. احفظ الأمر المكتوب في ~/.ssh/config على جهازك:
Host dsh-vps
HostName 203.0.113.10
User you
LocalForward 3080 127.0.0.1:3080بعد ذلك، يصبح ssh -N dsh-vps هو الأمر الكامل. هذا النفق هو الآن الوسيلة الوحيدة للوصول إلى وكيلك، ولذلك فإن SSH daemon هو ما يحميه: استخدم المفاتيح فقط، وعطّل مصادقة كلمات المرور، وطبّق بقية تأمين SSH على VPS الخاص بك بدرجة أكبر من المعتاد. إذا تطور VPS إلى شبكة خاصة صغيرة بحد ذاته، تضم قاعدة بيانات أو خادماً للاختبار المرحلي، فإن الإعلان عن هذه العناوين لشبكة tailnet لديك باستخدام موجّه شبكات فرعية يلغي الحاجة إلى إعادة توجيه منفصل لكل خدمة، مع أن ربط dsh على loopback يعني أن واجهة المستخدم نفسها تظل تصل عبر نفق.
لا تضع المفتاح في ملف الوحدة. تُطبع قيم Environment= بواسطة systemctl show dsh -p Environment، ويمكن لأي مستخدم على الجهاز تشغيله. إذا احتاجت إضافة تثبّتها إلى مفتاح في البيئة، فضعه في /etc/dsh.env، واضبط وضعه على 600 واجعل root مالكه، ثم أشر إليه باستخدام EnvironmentFile=/etc/dsh.env. يقرأ systemd هذا الملف بصفته root عند وقت التنفيذ، ولا يطبع systemctl show محتوياته. أما الملف الموجود على القرص الذي تُحفظ فيه كل إعدادات فعلياً، وما يغادر جهازك عند توجيه dsh إلى نقطة نهاية Ollama محلية بدلاً من API الخاص بـDeepSeek، فهما موضوع إعداد مفاتيح dsh ونماذجه ونقاط نهايته.
تكلفة التشغيل
تحدث عملية الاستدلال عبر API الخاص بـDeepSeek، وليس على VPS لديك. يستهلك خادمك موارد عملية Node، وواجهة المستخدم التي يقدّمها، وكل أمر يقرر الوكيل تشغيله. الاستهلاك الأول والثاني ثابت ومنخفض. أما الاستهلاك الثالث فلا يحدّه أي شيء في ملف الوحدة هذا.
قِس الحد الأدنى على خادمك بدلاً من الاعتماد على رقم مأخوذ من خادم شخص آخر:
systemctl show dsh -p MemoryCurrent
systemd-cgtop -1 --depth 2تظهر قيمة MemoryCurrent بالبايت. راقبها أثناء عمل الوكيل، لا أثناء بقائه خاملاً.
تكون استدعاءات الأدوات عمليات فرعية للخدمة، لذلك تقع في مجموعة التحكم نفسها وتخضع للحدود نفسها. يمكن لوكيل يشغّل npm install أو مجموعة اختبارات داخل مساحة العمل أن يستهلك ذاكرة تفوق ذاكرة harness نفسه بفارق كبير. على VPS بسعة 1 GB، تحدث الأعطال هنا: تختار النواة عملية وتقتلها، ويعرض journalctl -k | grep -i "out of memory" السطر Out of memory: Killed process الذي يذكر العملية التي اختارتها. وغالباً لا تكون هذه العملية هي التي سببت المشكلة.
الحل هو تحديد حدّ تتعمد وضعه. تحافظ MemoryMax= وCPUQuota= في القسم [Service] على الضرر داخل الوحدة، ولذلك يُقتل البناء المنفلت بدلاً من توقف الخادم بالكامل. يشرح تحديد الذاكرة ووحدة المعالجة المركزية باستخدام systemd القيم وسلوك الفشل. يزداد استخدام القرص أيضاً بسبب سجل الجلسات ضمن DSH_HOME وبسبب ما يكتبه الوكيل في مساحة العمل، لذلك أضف du -sh /var/lib/dsh إلى الأداة التي تستخدمها أصلاً لمراقبة القرص.
إذا كنت تريد وكيلاً تفاعلياً تتصل به وتنفصل عنه، فخدمة systemd ليست الشكل المناسب، وتناسبك أكثر تشغيل وكيل داخل جلسة tmux مستمرة. شغّل dsh كوحدة عندما تريده متاحاً دائماً ويمكن الوصول إليه عبر نفق.
أوضاع الفشل والرسائل التي ستظهر لك
status=203/EXEC. تعذّر على systemd تشغيل الملف، وتظهر السجلات Failed to locate executable /usr/local/bin/dsh: No such file or directory. المسار في ExecStart= لا يطابق ما عرضه command -v dsh. هذا هو الفشل الذي يبلّغ عنه Type=exec في وقت systemctl start بدلاً من إخفائه.
status=217/USER. الحساب المذكور في User= غير موجود. تحقّق من ذلك باستخدام id dsh.
status=200/CHDIR. WorkingDirectory= مفقود، أو أن مستخدم الخدمة لا يستطيع الدخول إليه. يعيد sudo -u dsh ls /var/lib/dsh/workspace إنتاج المشكلة مباشرة.
Error: listen EADDRINUSE: address already in use 127.0.0.1:3080. هناك برنامج آخر يستخدم المنفذ، وغالباً ما تكون جلسة npx التي تُركت مفتوحة في طرفية أخرى هي السبب. يحدّد sudo ss -lntp | grep 3080 العملية.
EACCES: permission denied متبوعاً بمسار. ملكية الملفات ضمن /var/lib/dsh غير صحيحة، ويحدث ذلك عادةً لأن التشغيل الأول تم بصفة root أو باستخدام HOME غير صحيح. يُصلح sudo chown -R dsh:dsh /var/lib/dsh ذلك.
Start request repeated too quickly. تجاوزت الوحدة حد معدل بدء التشغيل وتوقفت عن المحاولة. يوجد الخطأ الفعلي في الأسطر السابقة. شغّل sudo systemctl reset-failed dsh قبل المحاولة مجدداً.
الوحدة active (running) لكن المتصفح لا يعرض شيئاً. شغّل الاختبار على VPS: إذا طبع curl -fsS http://127.0.0.1:3080/ -o /dev/null && echo up القيمة up هناك، فالخدمة تعمل بصورة سليمة، والمشكلة في إعادة توجيه المنفذ.
الترقية عن قصد
يعني تثبيت الإصدار أن الترقية عملية تنفّذها أنت، وليست أمراً يحدث لك. اقرأ ملاحظات الإصدار أولاً، لأن تحذير المشروع الأصلي من التغييرات التي تكسر التوافق هو السبب الكامل لتثبيت الإصدار. انسخ دليل الحالة احتياطياً، ثم بدّل الإصدار:
sudo systemctl stop dsh
sudo tar czf /root/dsh-home-$(date +%F).tgz -C /var/lib/dsh harness
sudo npm install -g @deepseek-ai/dsh@0.1.0-rc.7
sudo systemctl start dsh
journalctl -u dsh -n 50 --no-pagerتتم استعادة الإصدار السابق باستخدام npm install -g نفسه مع الإصدار القديم، إضافةً إلى استعادة ذلك الأرشيف، ولا ينجح ذلك إلا إذا أنشأت نسخة منه. ويُعد وقت تشغيل الوكيل في مرحلة المعاينة تحديداً من البرمجيات التي قد تعيد كتابة تنسيق الإعدادات أثناء الترقية.
FAQ
لماذا تتوقف dsh عند إغلاق جلسة SSH؟
لأن npx @deepseek-ai/dsh web عملية تعمل في المقدمة وتملكها جلسة تسجيل الدخول، ولذلك تُنهى عند انتهاء الجلسة. أما وحدة systemd فيملكها نظام init، ولهذا تستمر في العمل بعد قطع الاتصال وتبدأ مجدداً بعد إعادة التشغيل. يمثّل sudo systemctl enable --now dsh مجموعة الخطوتين التي تمنحك الأمرين معاً: enable لإعادة التشغيل، و--now للإقلاع الحالي.
هل أستخدم Type=simple أم Type=exec مع dsh؟
Type=exec. تعمل dsh في المقدمة ولا تنشئ عملية فرعية، ولذلك يعمل الخياران، لكن Type=exec يجعل systemd ينتظر نجاح execve() قبل أن يعتبر بدء الخدمة ناجحاً. يؤدي وجود مسار خاطئ في ExecStart= إلى فشل systemctl start مع ظهور status=203/EXEC أمامك. أما مع Type=simple فتعيد المشكلة نفسها حالة نجاح وتختفي في السجل. الخياران Type=forking وType=notify غير صحيحين هنا، وكلاهما يظل معلقاً حتى انتهاء TimeoutStartSec بعد 90 ثانية.
كيف أفتح واجهة dsh على الويب من حاسوبي المحمول؟
أعد توجيه المنفذ عبر SSH: ssh -N -L 3080:127.0.0.1:3080 you@your-vps، ثم افتح http://127.0.0.1:3080/ في المتصفح. لا تحاول ربط الخدمة بعنوان عام. ترفض dsh --host 0.0.0.0 مع error: --host 0.0.0.0 is intentionally not supported yet for safety: it would expose remote code execution to the network; use 127.0.0.1 instead، لأن واجهة الويب البرمجية يمكنها جعل الوكيل ينفذ أوامر shell، ولا توجد مصادقة عن بُعد أمامها.
هل يمكنني تشغيل dsh بصلاحيات root لتبسيط الصلاحيات؟
لا. صُممت هذه البنية لتشغيل الأوامر وكتابة الملفات، ولذلك فإن أي صلاحيات تملكها الخدمة ستكون متاحة للوكيل. أنشئ حساب نظام باستخدام useradd --system --shell /usr/sbin/nologin dsh، واجعل /var/lib/dsh مملوكاً له، وأضف NoNewPrivileges=true إلى الوحدة. إذا واجهت EACCES: permission denied بعد ذلك، فالسبب المعتاد هو أن تشغيلًا سابقًا بصلاحيات root خلّف ملفات مملوكة لـroot، ويزيل sudo chown -R dsh:dsh /var/lib/dsh هذه المشكلة.
ما الإصدار الذي ينبغي تثبيته من dsh في الوحدة؟
استخدم الإصدار الذي يعرضه npm view @deepseek-ai/dsh version عند إعداد الخدمة، وثبّته باستخدام npm install -g @deepseek-ai/dsh@<that version> وسجّله في مكان يمكنك العثور عليه. كان 0.1.0-rc.7 هو الإصدار الحالي في 18 August 2026. لا تكمن المسألة في الرقم، بل في أن npx من دون تحديد إصدار يحل الحزمة عند بدء التشغيل، ولذلك قد تنقلك إعادة التشغيل غير التفاعلية بصمت إلى إصدار مبني بتنسيق إعدادات مختلف.