SSD Nodes Learn 🎉 VPS من $5.50/شهر
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-08-21

تشغيل dsh دون واجهة على VPS باستخدام systemd

شغّل dsh كخدمة systemd على VPS مع مستخدم مخصص وإصدار مثبت وقواعد Restart، وتعلّم قراءة السجلات عبر journalctl وفتح الواجهة عبر نفق SSH.

تشغيل dsh دون واجهة تفاعلية على VPS، وليس داخل طرفية

يتطلب تشغيل dsh دون واجهة تفاعلية على VPS ملف وحدة systemd واحداً، بالإضافة إلى مستخدم مخصص لامتلاكه. يُعد dsh مشغّل سطر الأوامر لـDeepSeek Harness، وهي بيئة تشغيل الوكلاء من DeepSeek، وقد نُشرت بموجب ترخيص MIT في إصدار معاينة للمطورين في August 2026. يطلب منك دليل البدء السريع كتابة npx @deepseek-ai/dsh web، وهذا صحيح، لكن العملية تتوقف أيضاً بمجرد إغلاق جلسة SSH (shell آمنة).

يحل ملف الوحدة أربع مشكلات دفعة واحدة. تعود الخدمة إلى العمل بعد إعادة التشغيل. ويُرسَل خرجها إلى journal بدلاً من مروره أمامك أثناء التمرير. وتعمل الخدمة باستخدام حساب ليس root. كما أن الإصدار الذي تشغله هو الإصدار الذي اخترته. وهذا مهم هنا أكثر من المعتاد، لأن المشروع يذكر ذلك بأحرف كبيرة:

DeepSeek Harness متاح حالياً في معاينة للمطورين، ويتغير بسرعة. ستحدث تغييرات تكسر التوافق.

يفترض هذا الدليل أن dsh يعمل لديك يدوياً بالفعل. إذا لم يكن كذلك، فابدأ بـتثبيت DeepSeek Harness على VPS، ثم عد بعد أن يقدّم npx @deepseek-ai/dsh web صفحة.

Node أولاً، لأن npm لن يحذّرك

node -v

حزمة 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 لأن تمرير script بعيد مباشرةً إلى 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 أن web profile يستمع على 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 الإصدار الدقيق. وهذا هو ما تحتاج إليه بعد ستة أسابيع عندما يتغير السلوك ولا تتذكر الإصدار الذي ثبّتَّه.

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. 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 web

Set 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 في shell، لذلك يزيل المسار المطلق هذا التخمين.

يحدد 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 دون خطأ، ولا يظهر الفشل إلا في السجل. مع 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 لن تصل أبداً إلى خمس مرات بدء خلال نافذة مدتها عشر ثوانٍ. لذلك تعيد الوحدة التي تتعطل عند بدء التشغيل تشغيل نفسها إلى ما لا نهاية، ولا يعرف ذلك إلا journal. يعني 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 المجرّد بعد إعادة التشغيل التالية، كما أن تحديثات kernel تتطلب إعادة التشغيل.

يجب أن يعرض systemctl status dsh السطر Active: active (running)، وسطر Main PID، وسطر Memory:. ثم تحقّق من عنوان الاستماع:

sudo ss -lntp | grep 3080

يجب أن ترى 127.0.0.1:3080. إذا رأيت 0.0.0.0:3080، فهذا يعني أن شيئاً ما غيّر عنوان الربط، وأن agent يستمع على الإنترنت العام. اسم العملية في ذلك الناتج هو node، وليس dsh، لأن الملف الثنائي dsh هو Node script، ولذلك لا يعثر 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، وستحذفه كل عملية إعادة تشغيل. أنشئ الدليل وأعد تشغيل daemon:

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

هذا ليس قيداً ينبغي التحايل عليه. تقود واجهة الويب API (واجهة برمجة التطبيقات) الوكيل، وينفّذ الوكيل أوامر shell، لذلك فإن المنفذ الذي يمكن الوصول إليه يتيح shell على VPS الخاص بك لأي شخص يعثر عليه. يذكر المشرفون أن عدم تنفيذ المصادقة عن بُعد هو سبب تثبيت الربط على loopback. بدلاً من ذلك، مرّر المنفذ من جهازك:

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 الخاص بك بصرامة أكبر من المعتاد.

لا تضع المفتاح في ملف الوحدة. تُطبع قيم Environment= بواسطة systemctl show dsh -p Environment، ويمكن لأي مستخدم على الخادم تشغيله. إذا احتاجت إضافة تثبّتها إلى مفتاح في البيئة، فضعه في /etc/dsh.env مع ضبط mode على 600 وامتلاكه من root، ثم أشر إليه باستخدام EnvironmentFile=/etc/dsh.env. يقرأ systemd هذا الملف بامتيازات root وقت exec، ولا يطبع systemctl show محتوياته.

تكلفة التشغيل

تحدث عملية الاستدلال عبر API الخاص بـDeepSeek، وليس على VPS الخاص بك. يدفع خادمك مقابل عملية Node، وواجهة المستخدم التي يقدّمها، وكل أمر يقرّر الوكيل تشغيله. أول عنصرين ثابتان واستهلاكهما منخفض. أما العنصر الثالث فلا يقيّده شيء في ملف الوحدة هذا.

قِس الحد الأدنى على خادمك بدلاً من الاعتماد على رقم مأخوذ من خادم شخص آخر:

systemctl show dsh -p MemoryCurrent
systemd-cgtop -1 --depth 2

تُقاس قيمة MemoryCurrent بالبايت. راقبها أثناء عمل الوكيل، لا أثناء خمولِه.

تكون استدعاءات الأدوات عمليات فرعية للخدمة، ولذلك تقع في مجموعة التحكم نفسها وتُحتسب ضمن الحدود نفسها. يمكن لوكيل يشغّل npm install أو مجموعة اختبارات داخل مساحة العمل أن يستخدم ذاكرة تتجاوز ذاكرة بيئة التشغيل نفسها بفارق كبير. على 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 إلى الأداة التي تستخدمها لمراقبة القرص.

إذا كان هدفك وكيلاً تفاعلياً تتصل به وتفصل عنه، فهذه الخدمة ليست الشكل المناسب، ويكون تشغيل وكيل في جلسة 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 نفسه باستخدام الإصدار القديم، ثم استعادة ملف tarball ذاك. ولا ينجح ذلك إلا إذا كنت قد أنشأت النسخة الاحتياطية. يُعد runtime للوكيل في مرحلة المعاينة تحديداً نوع البرنامج الذي قد تعيد ترقيته كتابة تنسيق الإعدادات من دون علمك.

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 من دون تحديد إصدار يحل الحزمة وقت بدء التشغيل، ولذلك قد تنقلك إعادة التشغيل غير المراقبة بصمت إلى بنية ذات تنسيق إعداد مختلف.