يشغّل هذا الدليل صندوق رمل من JavaScript أو TypeScript. ستبدأ آلة microVM معزولة من Firecracker، وتنفّذ الشيفرة بثلاث لغات، وتنقل الملفات، وتتابع مخرجات مهمة طويلة. كل خطوة مقطع شيفرة قصير.
تحتاج إلى Node.js 18+ (أو Deno أو Bun) وحساب في EQVPS.
1. احصل على رمز وصول
تصادق الحزمة برمز وصول حساب عادي. تشرح صفحة ربط الحساب كيفية الحصول عليه من لوحة التحكم أو الـ API أو خادم MCP.
export EQVPS_API_KEY="your-token"
يحصل الحساب الجديد الذي لا رصيد له على 1 دولار لصناديق الرمل عند إنشاء أول صندوق رمل. هذا يكفي لهذا الدليل مرات كثيرة.
2. ثبّت الحزمة
npm i @eqvps/sdk
تستخدم الأمثلة وحدات ES وawait على المستوى الأعلى. احفظها كملفات .mjs، أو كملفات .ts وشغّلها بـ npx tsx.
3. شغّل صندوق رمل ونفّذ الشيفرة
import { Sandbox } from "@eqvps/sdk";
await Sandbox.with({ tariff: "small" }, async (sb) => {
console.log(sb.id);
const r = await sb.run("import platform; print(platform.python_version())");
console.log(r.exit_code, r.stdout);
});
المخرج هو معرّف صندوق الرمل (sb_ يليه 24 حرفًا)، ثم 0 3.12.x. تحذف Sandbox.with صندوق الرمل عند انتهاء الدالة الممرّرة، حتى لو رمت خطأً.
اللغة الافتراضية Python، واللغتان الأخريان على بُعد خيار واحد:
await Sandbox.with({}, async (sb) => {
const js = await sb.run("console.log(process.version)", { language: "node" });
const sh = await sb.run("uname -r && nproc", { language: "bash" });
console.log(js.stdout, sh.stdout);
});
السكربت الفاشل لا يرمي شيئًا. تحقق من r.ok، وهي صحيحة عندما يكون exit_code صفرًا ولم يبلغ الأمر مهلته.
4. أوامر shell والحزم
يقبل exec نص shell أو مصفوفة argv. لصندوق الرمل اتصال بالإنترنت، لذا تعمل عمليات التثبيت عبر npm وpip:
await Sandbox.with({ tariff: "small" }, async (sb) => {
await sb.exec("mkdir -p /root/app && cd /root/app && npm init -y && npm i lodash", { timeout: 55 });
const r = await sb.exec(["node", "-e", "console.log(require('/root/app/node_modules/lodash').VERSION)"]);
console.log(r.stdout);
});
يدوم الاستدعاء المتزامن 55 ثانية على الأكثر. ما هو أطول يذهب إلى مهمة في الخلفية (الخطوة 6).
5. الملفات
await Sandbox.with({}, async (sb) => {
await sb.upload("/root/input.json", JSON.stringify({ values: [3, 5, 8] }));
await sb.run("import json; d = json.load(open('/root/input.json')); open('/root/out.txt', 'w').write(str(sum(d['values'])))");
console.log(await sb.downloadText("/root/out.txt")); // 16
});
تقبل upload نصًا أو Uint8Array. تُرجع download قيمة Uint8Array، وdownloadText نصًا. النقل الواحد محدود بـ 5 MB.
6. مهام طويلة بمخرجات مباشرة
await Sandbox.with({ tariff: "standard" }, async (sb) => {
const task = await sb.exec("for i in 1 2 3 4 5; do echo step $i; sleep 20; done", { background: true });
const res = await task.wait({ onOutput: (out) => process.stdout.write(out) });
console.log(res.state, res.exit_code); // done 0
});
تعود المهمة فورًا وليس لها حد الـ 55 ثانية. تتحقق wait افتراضيًا كل ثانيتين (ويغيّر ذلك pollIntervalMs). توقفها task.kill()، وتعيد sb.task(id) الاتصال بها من عملية أخرى.
الأخطاء
import { Sandbox, InsufficientBalanceError, RateLimitError } from "@eqvps/sdk";
try {
await Sandbox.with({}, async (sb) => console.log((await sb.run("print(1)")).stdout));
} catch (e) {
if (e instanceof InsufficientBalanceError) console.log("Top up the balance");
else if (e instanceof RateLimitError) console.log("Busy, retry in", e.retryAfter);
else throw e;
}
تعيد الحزمة بنفسها محاولة 429 و503 حتى ثلاث مرات عندما يرسل الخادم Retry-After. أكثر حالات 429 شيوعًا سببها تشغيل أكثر من أمرين في الوقت نفسه على حساب واحد. كل الفئات موجودة في مرجع الحزمة.
تكلفة هذا الدليل
عاش كل مثال بضع ثوانٍ وحُسب بالحد الأدنى البالغ 60 ثانية: 0.00055 دولار على small، و0.0011 دولار على standard. عملت مهمة الخطوة 6 نحو 100 ثانية على standard، أي نحو 0.002 دولار. المجموع أقل بكثير من سنت واحد. القائمة الكاملة للأسعار في حدود صناديق الرمل وفوترتها.
الخطوات التالية
- الأمر نفسه بلغة Python: حزمة Python لصناديق الرمل في 5 دقائق.
- اربط نموذجًا ودعه يشغّل شيفرته بنفسه: أول مهمة لوكيل في صندوق رمل.
- الخطط والتجربة بدولار واحد: صفحة صناديق الرمل.
التعليقات
لا تعليقات بعد. كن الأول.