needhelp
← Back to blog

DeepSeek Harness: یک فریم‌ورک اَیجنت منبع‌باز که عملاً همه‌چیز یک پلاگین است

by needhelp
DeepSeek
AI Agents
Open Source
Cordis
Plugin Architecture

یک استک اَیجنت کامل، در GitHub منتشر شد

DeepSeek در ۱۳ اوت ۲۰۲۶ پیش‌نمایش توسعه‌دهنده‌ای از DeepSeek Harness (dsh) را منتشر کرد. مخزن در آدرس deepseek-ai/deepseek-harness قرار دارد. تا پایان روز ۱.۶k ستاره، ۱۲,۲۹۳ کامیت و ۱۹ مشارکت‌کننده داشت — عدد‌هایی که نشان می‌دهد این یک پروژه اتفاقی آخر هفته نیست. پایگاه کد ۹۷.۱٪ TypeScript، ۱.۶٪ CSS و ۰.۷٪ Python است. نسخه پکیج در زمان انتشار: 0.1.0-rc.5

صفحه لندینگ در deepseek.com/harness و مستندات توسعه‌دهنده در deepseek-harness.github.io/deepseek-harness قرار دارد.

تحت مجوز MIT منتشر شده است

شعار یک‌خطی

از README: «اَیجنت = مدل + Harness»

مدل روح است. Harness به یک اَیجنت اجازه می‌دهد محیط خود را درک کند، از ابزارها استفاده کند و در محیط‌های واقعی به کار خود ادامه دهد. دیدگاه DeepSeek: هر قابلیت — مدل‌ها، ابزارها، مهارت‌ها، سشن‌ها، سندباکس‌ها، ذخیره‌سازی، حلقه‌ها، زمان‌بندی و رابط کاربری — یک پلاگین قابل تعویض است

این فقط شعار نیست. این توسط معماری اجباری شده

Cordis: هسته که زیر همه‌چیز قرار دارد

DeepSeek Harness بر پایه Cordis ساخته شده است، یک فریم‌ورک پلاگین توکار. طراحی Cordis در مقاله‌ای با نام A Programming Paradigm for Spatiotemporal Composability توضیح داده شده است

کل فریم‌ورک به پنج ایده کاهش می‌یابد:

۱. پلاگین شیئی است که Service را پیاده‌سازی می‌کند. می‌تواند یک تابع با inject و apply(ctx) باشد، یا یک زیرکلاس Service ۲. کانتکست یک مخزن از سرویس‌ها است. یک پلاگین یک کلید پایدار مانند ctx.tools، ctx.llm یا ctx.sessions را ادعا می‌کند. سایر پلاگین‌ها به جای وارد کردن پیاده‌سازی‌های مشخص، سرویس‌ها را با کلید پیدا می‌کنند ۳. وابستگی سرویس را از طریق inject اعلام کنید. پلاگینی که سرویس‌های مورد نیاز را نام می‌برد تا زمانی که آن سرویس‌ها وجود نداشته باشند منتظر می‌ماند. هیچ ترتیب بوت دستی لازم نیست ۴. ایونت‌های تایپ‌شده برای ارتباط. چهار حالت ارسال: emit (ارسال و فراموشی)، waterfall (سبک میدل‌ور با next()parallel (همه شنونده‌ها به صورت همزمان اجرا می‌شوند) و serial (شنونده‌ها به ترتیب با مقادیر برگشتی اجرا می‌شوند) ۵. ثبت‌ها اثرات قابل بازگشت هستند. بخش‌های پرامپت، اسکیما ابزار، آداپتورها، شنونده‌ها — همه چیز از طریق ctx.effect() نصب می‌شود بنابراین بارگذاری مجدد و تخریب آن‌ها را به صورت قابل پیش‌بینی باز می‌کنند

مدل waterfall Cordis یک میدل‌ور محیطی است. یک شنونده (...args, next) را دریافت می‌کند. برای واگذاری به سرویس بعدی next() را فراخوانی کنید؛ برای قطع زنجیره بدون next() برگردید. برای ایونت‌های تک‌تصمیمی، قطع زنجیره همان طراحی است — یک شنونده سیاست می‌تواند به طور کامل مالک یک تصمیم باشد

همه‌چیز یک پلاگین است (جدیداً)

لیست پلاگین‌ها در deepseek.com/harness کل سطح قابلیت را بیان می‌کند:

  • Models — بک‌اندهای LLM (DeepSeek، OpenAI، Anthropic، Bedrock، Vertex، Azure، Codex به همراه هر اندپوینت سازگار با OpenAI)
  • Tools — آنچه مدل می‌تواند فراخوانی کند
  • Skills — باندل‌های پرامپت و ابزار قابل استفاده مجدد
  • Sessions — مدیریت حالت گفتگو
  • Sandboxes — جایی که کد اجرا می‌شود (PTY bash، بک‌اند محلی danger-full-access، سندباکس بومی مبتنی بر landlock)
  • Storage — تداوم سشن
  • Loops — منطق نوبت اَیجنت
  • Scheduling — توزیع ساب‌اَیجنت و وظیفه
  • UI — رابط کاربری وب که به طور پیش‌فرض در پورت ۳۰۸۰ سرویس‌دهی می‌شود

توسعه‌دهنده می‌تواند هر کدام از این موارد را از طریق پیکربندی انتخاب، تعویض یا گسترش دهد — بدون تغییر در کد منبع خود DeepSeek Harness. کاتالوگ کانفیگ پلاگین به طور خودکار از منبع (scripts/gen-config-catalog.ts) تولید می‌شود و توسط CI به صورت تازه تأیید می‌شود، بنابراین هر فیلدی در بلوک config: یک cordis.yml دقیقاً با نوع کانفیگ اعلام‌شده پلاگین مطابقت دارد

چهار حالت رانتایم

مستندات چهار پیش‌تنظیم ارائه می‌دهند. هر کدام یک مورد استفاده متفاوت را هدف قرار می‌دهند

Standard Mode — اَیجنت کدنویسی کامل: ویرایش فایل، شل، جستجوی فایل و وب، مهارت‌ها، برنامه‌ریزی، اهداف، ساب‌اَیجنت‌ها و گردش‌های کاری. این تجربه پیش‌فرض رابط وب است

Code Mode — همه چیز در Standard، اما ابزارها از طریق Code Mode SDK در معرض دید قرار می‌گیرند بنابراین مدل می‌تواند عملیات چندمرحله‌ای را در یک برنامه TypeScript ترکیب کند. یک برنامه تولیدشده توسط مدل جایگزین چندین دور فراخوانی ابزار می‌شود

Minimal Mode — فقط دو ابزار: یک شل bash دائمی و str_replace_editor. این برای بنچمارک مدل‌ها در یک محیط ساده‌شده است. بدون مهارت، بدون برنامه‌ریزی، بدون ساب‌اَیجنت — قابلیت خام مدل در برابر یک پایگاه کد واقعی

Creator Mode — برای نوشتن پیش‌تنظیمات اَیجنت سفارشی ساخته شده است. شامل تمام قابلیت‌های حالت Standard به همراه بازرسی رانتایم، آزمایش‌های پلاگین Cordis در حافظه، و راهنمایی ایجاد پیش‌تنظیم. این همان روشی است که شما حالت پنجم، ششم و هفتم را می‌سازید

هر اجرا قابل ردیابی است

اینجا است که Harness خود را از اکثر ابزارهای اَیجنت متمایز می‌کند. همه‌چیزی که مدل می‌بیند در یک لاگ سشن فقط-الحاقی ثبت می‌شود:

  • پرامپت‌های سیستم
  • خروجی استدلال
  • فراخوانی‌های ابزار و نتایج آن‌ها
  • تصمیمات زمان‌بندی ساب‌اَیجنت
  • هر تزریق کانتکست

رابط کاربری وب یک نمای Trajectory دارد که در آن می‌توانید این رکوردها را با فیلتر بر اساس منبع بررسی کنید. ادامه دادن، انشعاب (fork)، جستجو و پخش مجدد — هر چهار عملیات با یک جریان ایونت یکسان کار می‌کنند. هیچ دیتابیس ردیابی جداگانه‌ای وجود ندارد، هیچ مرحله صادراتی نیست. JSONL سشن خودش منبع حقیقت است

برای Python SDK، دایرکتوری سشن JSONL فشرده‌نشده‌ای را ذخیره می‌کند که شامل درخواست‌های مدل مونتاژ شده و فراخوانی‌های ابزار است. مثال در examples/jsonrpc-agent/minimal.py این را از ابتدا تا انتها نشان می‌دهد

پیکربندی مدل: DeepSeek + هر چیزی

صفحه پیکربندی مدل (Configure models) سه لایه دارد

DeepSeek Official. Settings → Models را باز کنید، یک کلید API DeepSeek را جای‌گذاری کنید، ذخیره کنید. کلید فقط قابل نوشتن است. پس از ذخیره، رابط کاربری یک توصیفگر پنهان‌شده دریافت می‌کند، هرگز متن ساده را نه. کلیدها در \$DSH_HOME/.credentials.yaml ذخیره می‌شوند؛ صفحه تنظیمات فقط یک ارجاع اعتبارنامه نگه می‌دارد

Catalog providers. روی «Add provider» کلیک کنید، Anthropic یا OpenAI را انتخاب کنید، کلید را جای‌گذاری کنید. ارائه‌دهندگانی که از احراز هویت بومی استفاده می‌کنند (Bedrock از طریق AWS creds + region، Vertex از طریق ADC project، Azure از طریق api-version، Codex از طریق OAuth) فقط با یک فیلد کلید API کار نمی‌کنند — هر کدام به مسیر احراز هویت خود نیاز دارند

Custom providers. برای یک دروازه شرکتی، سرور خودمیزبان، یا هر ارائه‌دهنده‌ای که در کاتالوگ نیست. یک Provider ID حروف کوچک تنظیم کنید (دائمی — درخواست‌ها، سشن‌های ذخیره‌شده، پیش‌فرض‌های مدل و ارجاعات اعتبارنامه همه از آن استفاده می‌کنند)، URL پایه، پروتکل API، اعتبارنامه‌ها و حداقل یک مدل

مدل‌های بصری به یک مرحله اضافی نیاز دارند. از آنجا که یک اندپوینت سفارشی راهی برای تبلیغ روش‌های پشتیبانی‌شده خود ندارد، فرم نمی‌تواند پشتیبانی از بینایی را به صورت خودکار تشخیص دهد. شما input: [text, image] را به مدل در \$DSH_HOME/settings.yaml اضافه می‌کنید. اگر تمام مدل‌های سفارشی شما تصاویر را می‌پذیرند، به جای اینکه به ازای هر مدل انجام دهید، یک بار defaultInput: [text, image] را در سطح ارائه‌دهنده تنظیم کنید. فیلد input یک ادعا است، نه یک بررسی — اگر ادعا کنید مدلی بینایی انجام می‌دهد اما اندپوینت در واقع چنین کاری نمی‌کند، ارائه‌دهنده درخواست را رد می‌کند نه Harness

رفع مشکلات مستقیماً در مستندات بیان شده است:

  • MISSING_CREDENTIAL → کلید ارائه‌دهنده را از طریق صفحه Models ذخیره کنید یا متغیر محیطی ارجاع‌شده را صادر کنید
  • UNKNOWN_MODEL → یک مدل پیکربندی‌شده انتخاب کنید، یا مدل گمشده را به ارائه‌دهنده سفارشی اضافه کنید
  • «Get available models 401 برمی‌گرداند» → کلید را بررسی کنید. کشف مدل اندپوینت GET /models سازگار با OpenAI را فراخوانی می‌کند. اگر سرویس شما آن را نمایش نمی‌دهد، مدل‌ها را به صورت دستی وارد کنید
  • «تصویر قبل از ارسال رد شد» → مدل روش image را اعلام نکرده است. input: [text, image] را اضافه کنید
  • «ارائه‌دهنده درخواستی با تصویر را رد می‌کند» → مدل قابلیت بینایی را ادعا کرده که اندپوینت آن را واقعاً ندارد. image را از لیست حذف کنید و یک سشن جدید شروع کنید (تصویر قدیمی در لاگ سشن باقی می‌ماند و همان درخواست را تکرار می‌کند)

شروع کار: سه مسیر

مسیر ۱: npx @deepseek-ai/dsh web

Node.js را نصب کنید، یک دستور اجرا کنید. رابط وب در http://127.0.0.1:3080 شروع می‌شود. همین. هیچ کلون، هیچ بیلد، هیچ pnpm

Terminal window
npx @deepseek-ai/dsh web

سپس: Settings → Models → کلید API DeepSeek را جای‌گذاری کنید. فضای کاری را انتخاب کنید (دایرکتوری‌ای که dsh از آن فراخوانی شده کار می‌کند). یک سشن شروع کنید:

این مخزن را خلاصه کنید و پکیج‌های اصلی آن را شناسایی کنید

اَیجنت فایل‌های فضای کاری را می‌خواند و ویرایش می‌کند، دستورات را اجرا می‌کند، کار را واگذار می‌کند و یک برنامه را حفظ می‌کند. هر عملیاتی که بر اساس سیاست مجوز فعال به تأیید نیاز دارد، قبل از اجرا یک دیالوگ در رابط وب نمایش می‌دهد

مسیر ۲: کلون و بیلد از منبع

Terminal window
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web

مسیر ۳: Python SDK

نیازمندی‌ها: Python 3.10+، Linux x64 / Linux arm64 / macOS 14+ روی arm64، یک اندپوینت سازگار با DeepSeek

Terminal window
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
python -m venv .venv
. .venv/bin/activate
python -m pip install deepseek-harness-sdk

اعتبارنامه‌ها را تنظیم کنید:

8000/v1
export DEEPSEEK_API_KEY=sk-your-key-here
# Optional:
# export DSH_MODEL=deepseek-v4-flash
# export DSH_SYSTEM_PROMPT='You are a helpful software engineer assistant.'

یک وظیفه را در برابر یک فضای کاری جدا شده اجرا کنید:

Terminal window
python examples/jsonrpc-agent/minimal.py \
--workspace /absolute/path/to/workspace \
--session-root /absolute/path/to/sessions \
--session-id example-001 \
"مخزن را بررسی کنید و تست‌های شکست‌خورده را تعمیر کنید."

برای کد خودتان، نقطه ورود SDK DeepSeekHarness به عنوان یک کانتکست منیجر است:

from pathlib import Path
from deepseek_harness import DeepSeekHarness
config = Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()
workspace = Path("/absolute/path/to/workspace").resolve()
sessions = Path("/absolute/path/to/sessions").resolve()
with DeepSeekHarness(
provider="deepseek-official",
model="deepseek-v4-flash",
max_tokens=49_152,
cwd=str(workspace),
session_root=str(sessions),
cordis=str(config),
) as harness:
result = harness.run(
"مخزن را بررسی کنید و تست‌های شکست‌خورده را تعمیر کنید.",
session_id="example-001",
)
print(result.final_response)

DeepSeekHarness به صورت تنبل رانتایم همراه را شروع می‌کند و تا خروج از بلوک with از آن مجدداً استفاده می‌کند. برای حفظ فرآیند Bash (دایرکتوری کاری، متغیرهای محیطی، توابع شل) از همان ID سشن مجدداً استفاده کنید. برای یک وظیفه مستقل از یک ID سشن تازه استفاده کنید

ترکیب حداقلی jsonrpc-agent عمداً کم‌مصرف است: فقط bash دائمی و str_replace_editor به عنوان ابزارهای روبروی مدل. تایم‌اوت Bash ۳۰۰ ثانیه. محدودیت خروجی ویرایشگر ۱۶,۰۰۰ کاراکتر. فشرده‌سازی کانتکست غیرفعال است. سیستم فایل از بک‌اند محلی ساده استفاده می‌کند — مسیرهای ویرایشگر می‌توانند به هر چیزی که فرآیند رانتایم می‌بیند آدرس دهند. مستندات به طور صریح هشدار می‌دهند: «آن را فقط داخل یک چک‌اوت یا کانتینر یکبارمصرف اجرا کنید.» بک‌اند PTY دائمی همچنین به یک لایه ترمینال POSIX نیاز دارد — پشتیبانی از Windows برای این ترکیب وجود ندارد

اولین پلاگین خود را بنویسید

آموزش: اولین پلاگین شما. یک پلاگین یک ماژول TypeScript است که یک تابع apply را صادر می‌کند:

import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
console.log('[hello-plugin] plugin loaded!')
}

آن را در یک پچ cordis.yml ثبت کنید:

- insert:
- id: hello
name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'

با اورلای بوت کنید:

Terminal window
pnpm dsh web --patch ./scratch-plugin/cordis.yml

پاک‌سازی خودکار ویژگی کلیدی است. هر چیزی که از طریق ctx ثبت می‌شود — شنونده‌های ایونت، ابزارها، تایمرها — هنگام بارگذاری مجدد پلاگین پاک می‌شوند. هیچ removeListener یا clearInterval دستی لازم نیست. برای پاک‌سازی صریح (اتصالات شبکه)، یک دیسپوزر را از ctx.effect() برگردانید

وابستگی‌ها با inject اعلام می‌شوند:

export const name = 'my-tool-plugin'
export const inject = ['tools']
export function apply(ctx: Context) {
// ctx.tools is ready here
}

Cordis قبل از بارگذاری پلاگین منتظر هر سرویس مورد نیاز می‌ماند

سه شکل پلاگین وجود دارد: تابع (بالا)، آبجکت با apply، و کلاس گسترش‌یافته از Service. از فرم کلاس استفاده کنید وقتی که پلاگین خودش یک سرویس برای سایر پلاگین‌ها برای مصرف فراهم می‌کند

اولین ابزار خود را بنویسید

آموزش: ساخت یک ابزار. از defineTool از @deepseek-ai/dsh-tools استفاده کنید:

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: {
type: 'string',
required: true,
description: 'The name to greet',
},
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `Hello, ${args.name}!`
},
}))
}

defineTool args را از parameters استنتاج و اعتبارسنجی می‌کند. execute مقدار کانونیکال اعلام‌شده توسط output.schema را برمی‌گرداند. output.render آن مقدار کانونیکال را به محتوای روبروی مدل تبدیل می‌کند. پس از راه‌اندازی مجدد با پچ، از رابط وب بپرسید: «از ابزار greet برای سلام‌کردن به Ada استفاده کن.» مدل greet را فراخوانی می‌کند و Hello, Ada! را دریافت می‌کند

مراحل بعدی از آموزش عبارتند از پیکربندی پلاگین، مرجع ایجاد ابزار (اسکیماهای تودرتو، مقادیر کانونیکال، کار پس‌زمینه، هوک‌های سیاست، Code Mode، کارت‌های UI) و لایه‌بندی قابلیت (تقسیم پکیج Service Definition → Service Provider → Consumer)

حالت‌های ورودی CLI

دستور @deepseek-ai/dsh لانچر محصول است. چهار نقطه ورودی:

دستور هدف
dsh --profile <name> پروفایل نام‌گذاری‌شده را تحت \$DSH_HOME/profiles/<name> بوت کنید
dsh --profile headless "job" یک سشن ذخیره‌شده تازه را اجرا کنید، پاسخ نهایی را چاپ کنید، خارج شوید
dsh web مستعار --profile web
dsh plugin --profile <name> <pnpm args> پلاگین‌های یک پروفایل را با ارسال به pnpm مدیریت کنید

دایرکتوری فراخوانی ریشه فضای کاری پیش‌فرض است. پروفایل‌های web و headless در اولین استفاده از قالب‌های ارائه‌شده به صورت خودکار مقداردهی اولیه می‌شوند. هر پروفایل دیگری باید از طریق dsh plugin ایجاد شود

پرچم‌های لانچر اول می‌آیند. اولین توکنی که لانچر تشخیص نمی‌دهد آرگومان‌های برنامه می‌شود. مثال: dsh --profile web --port 8080 --port 8080 را به اپلیکیشن وب می‌دهد، نه لانچر

یک دایرکتوری پروفایل حاوی یک package.json (وابستگی‌های پلاگین خارج از درخت، به همراه مانیفست پروفایل dsh.profile با لیست مرتب‌شده bundles آن) و یک cordis.patch.yml (لایه پچ خود کاربر) است. ترتیب ترکیب روی یک ریشه خالی به این صورت است: پچ هر باندل به ترتیب dsh.profile.bundlescordis.patch.yml پروفایل → \$DSH_HOME/cordis.patch.yml در سطح خانه → اورلای‌های --patch. برای بررسی درخت ترکیب‌شده بدون بوت کردن از --dump-default-config و --dump-config استفاده کنید

پلاگین‌های جامعه و اکوسیستم

برای قابل کشف بودن، مخزن پلاگین خود را در GitHub با برچسب dsh-plugin تگ کنید. سایت رسمی مستقیماً به آن صفحه تاپیک لینک می‌دهد. همچنین یک جامعه Discord DeepSeek Harness برای بحث وجود دارد

پروژه از GitHub Discussions برای بازخورد و گزارش اشکال استفاده می‌کند. مستندات به CONTRIBUTING.md برای گردش کار توسعه، به architecture.md برای طراحی سیستم، و به AGENTS.md برای قراردادهای کدنویسی مخصوص اَیجنت لینک می‌دهد

پیش‌نمایش توسعه‌دهنده — بله، این می‌شکند

README این را با حروف بزرگ نوشته است: «تغییرات ناسازگاری ایجاد‌کننده وجود خواهد داشت.»

پلاگین‌های هسته و API هنوز در حال تکامل هستند. صفحه لندینگ این را مستقیماً می‌گوید: «DeepSeek Harness در پیش‌نمایش توسعه‌دهنده باقی می‌ماند و هنوز توسط توسعه‌دهندگانی که هارنس اَیجنت می‌سازند، در حال تست است»

اگر شما روی آن ساختن می‌کنید، یک هش کامیت را پین کنید، پلاگین‌های خود را نسبت به کاتالوگ کانفیگ نگاه دارید، و انتظار داشته باشید در هر rc bump دوباره تست کنید. پاک‌سازی خودکار پلاگین و تزریق وابستگی تست مجدد را نسبت به فریم‌ورک‌های یکپارچه کم‌دردسرتر می‌کند، اما «پیش‌نمایش توسعه‌دهنده» دقیقاً همان چیزی است که می‌گوید

چیزی که این را متفاوت می‌کند

اکثر فریم‌ورک‌های اَیجنت امروزی با یک حلقه نوبت شروع می‌شوند و گسترش‌پذیری را به عنوان یک افزوده به آن چسبانند. Harness این را برعکس می‌کند: گسترش‌پذیری خود فریم‌ورک است، و حلقه نوبت فقط یک پلاگین دیگر است. نتیجه قابل مشاهده سه چیز است که معمولاً در یک پکیج واحد به دست نمی‌آورید:

  • هر چیزی را بدون فورک تعویض کنید. از سیستم ابزار داخلی خوشتان نمی‌آید؟ آن را جایگزین کنید. یک لایه مسیریابی LLM متفاوت می‌خواهید؟ ارائه‌دهنده را تعویض کنید. همه چیز از طریق کلیدهای سرویس Cordis حل می‌شود
  • قابلیت ردیابی به صورت پیش‌فرض. لاگ فقط-الحاقی یک افزوده قابلیت مشاهده نیست. این خود نحوه عملکرد سشن‌ها است. ادامه دادن، انشعاب، جستجو و پخش مجدد همگی از یک جریان یکسان استفاده می‌کنند
  • پیش‌تنظیمات قابل ترکیب، نه پرچم‌های ویژگی. چهار حالت (Standard / Code / Minimal / Creator) فقط لایه‌های پچ باندل پلاگین مرتب‌شده هستند. می‌توانید پیش‌تنظیمات خود را با YAML به جای کد روی آن‌ها لایه‌بندی کنید

این رویکرد بر سر SDKهای یکپارچه پیروز می‌شود یا نه، به این بستگی دارد که آیا اکوسیستم پلاگین به اندازه کافی ابزار و ارائه‌دهنده شخص ثالث تولید می‌کند که داستان تعویض/بازترکیب را واقعی کند. با ۱.۶k ستاره و ۱۲k کامیت در روز اول، حرکت به وضوح وجود دارد

منابع

Share this page