@ easybits. cloud/ sdk
用于AI代理的EasyBits云的被打入HTTP客户端——沙盒,网络,文件,SQL数据库,文档和应用程序托管.
EasyBits是云AI代理已经知道如何使用:在被隔离的"鞭炮"微VM中运行代码,搜索并读取网络,存储并服务文件,查询一个SQL数据库,生成文档并部署应用——通过SDK,MCP和REST API. 价格为MXN.
安装
npm install @easybits.cloud/sdk
快速启动
import { EasybitsClient } from "@easybits.cloud/sdk";
const eb = new EasybitsClient({ apiKey: "eb_sk_live_..." });
// Upload a file
const { file, putUrl } = await eb.uploadFile({
fileName: "photo.jpg",
contentType: "image/jpeg",
size: 1024000,
});
// Upload bytes to the presigned URL
await fetch(putUrl, { method: "PUT", body: fileBuffer });
// Confirm upload
await eb.updateFile(file.id, { status: "DONE" });
认证
从中获取您的 API 密钥 开发者 Dashboard。 。 。 .
const eb = new EasybitsClient({ apiKey: process.env.EASYBITS_API_KEY! });
或使用自动分辨率从 ~/.easybitsrc 或 env vars : (中文(简体) )
import { createClientFromEnv } from "@easybits.cloud/sdk";
const eb = await createClientFromEnv();
方法
文件
| 方法 | 说明 |
|---|---|
listFiles(params?) | 列出您的文件( 已插入) |
getFile(fileId) | 获取文件细节 + 下载 URL |
uploadFile(params) | 创建文件记录 + 获得上传 URL |
updateFile(fileId, params) | 更新名称、访问、元数据或状态 |
deleteFile(fileId) | 软删除文件( 保留了 7天) |
restoreFile(fileId) | 恢复软删除文件 |
listDeletedFiles(params?) | 列出删除的文件, 日期为清除日期 |
searchFiles(query) | AI 驱动的自然语言文件搜索 |
bulkUploadFiles(items) | 创建最多 20 个文件记录 + 上传 URL |
bulkDeleteFiles(fileIds) | 软删除最多100个文件 |
duplicateFile(fileId, name?) | 复制已存在的文件( 新存储对象) |
listPermissions(fileId) | 列出文件共享权限 |
图像
| 方法 | 说明 |
|---|---|
optimizeImage(params) | 转换为 WebP/ AVIF( 创建新文件) |
transformImage(params) | 调整大小、裁剪、旋转、翻转、灰度 |
共享
| 方法 | 说明 |
|---|---|
shareFile(params) | 通过电子邮件与另一个用户共享文件 |
generateShareToken(fileId, expiresIn?) | 生成临时下载 URL |
listShareTokens(params?) | 列出共享令牌( 已贴上) |
网络呼号
| 方法 | 说明 |
|---|---|
listWebhooks() | 列出您已配置的网点 |
createWebhook(params) | 创建 Webhook( 返回秘密, 一次显示) |
getWebhook(webhookId) | 获取网络浏览细节 |
updateWebhook(webhookId, params) | 更新 URL、事件或状态 |
deleteWebhook(webhookId) | 永久删除网签 |
克鲁斯
您账户中已排定的任务: 调用一个公开的 https URL( 选择以一个金库密钥作为标题) 或发送一个转机给您的车队代理 。 最多5分钟; IANA 时区( 默认) America/Mexico_City);每个计划的限制(Byte 3,Mega 20,Tera 100).
| 方法 | 说明 |
|---|---|
listCrons({ includeAgents? }) | 你的膝盖includeAgents 增加你的特工作为转弯目标) |
createCron(params) | { name, cron, timezone?, enabled?, action } |
getCron(id) 编号 : updateCron(id, params) 编号 : deleteCron(id) | updateCron(id, { enabled: false }) 暂停 |
runCron(id) | 现在在背景中运行 |
listCronRuns(id, { limit? }) | 最新跑道,最新第一 |
await eb.createCron({
name: "Study reminder",
cron: "30 8 * * *",
action: { type: "http", url: "https://www.ghosty.studio/api/v2/me/push", method: "POST", body: { title: "Study time" }, secretName: "GHOSTY_PUSH_TOKEN" },
});
秘密(账户金库)
| 方法 | 说明 |
|---|---|
listSecrets() | 在您的金库中的名字( C){ items:[{id,name,createdAt,lastUsedAt}] }; 从未返回值 |
setSecret(name, value) | 存储或替换一个秘密( E)[A-Z_][A-Z0-9_]*; 名称为 $secret:NAME |
deleteSecret(name) | 删除它;无论它停止解决任何引用 |
网站
| 方法 | 说明 |
|---|---|
listSites() | 列出您的站点建设站点(/dash/sitios):静态和网络应用程序,并带有引擎和URL |
listWebsites() | 列出您的静态网站 |
createWebsite(name, { slug? }) | 创建新网站( 可选自定义 swit) |
getWebsite(websiteId) | 获取网站细节 |
updateWebsite(websiteId, params) | 更新网站名称, swit或状态(旧 swit 301s to the new switter) |
deleteWebsite(websiteId) | 删除网站及其文件 |
工作空间
指定位置, 限定配额的文件容器 。 每个租户创建一个, 薄荷一个工作空间范围密钥, 而该密钥只能触摸它自己的工作空间的文件.
| 方法 | 说明 |
|---|---|
listWorkspaces(params?) | 列出工作空间( cursor pagized) |
createWorkspace({ name, slug?, quotaBytes? }) | 创建工作空间 |
getWorkspace(workspaceId) | 获取工作空间细节 |
updateWorkspace(workspaceId, params) | 更新名称/状态/配额 |
deleteWorkspace(workspaceId) | 删除工作空间及其文件 |
getWorkspaceUsage(workspaceId) | 获取 { usedBytes, quotaBytes, fileCount } |
createWorkspaceKey(workspaceId, params?) | Mint 工作空间扫描 API 密钥( 画回一次) |
文档
| 方法 | 说明 |
|---|---|
listDocuments() | 列出文档 |
getDocument(id) | 获取所有页面的文档 |
createDocument(params) | 创建文档 |
updateDocument(id, params) | 更新文档元数据( 名称、 主题、 颜色) |
deleteDocument(id) | 删除文档 |
deployDocument(id) | 作为现场网站发布 |
unpublishDocument(id) | 撤消文档 |
generateDocument(id, params) | AI-遗传页面(并行,流出) |
refineDocument(id, params) | 外科AI编辑到一页 |
regenerateDocumentPage(id, params) | 重新设计保存内容的页面 |
enhanceDocumentPrompt(name, prompt?) | 自动描述或增强提示 |
getDocumentDirections(prompt, opts?) | 获得4个设计方向(指针、颜色、心情) |
账户
| 方法 | 说明 |
|---|---|
getUsageStats() | 已使用的存储/限制、文件清点、计划信息 |
listProviders() | 列表存储提供者 |
listKeys() | 列出您的 API 密钥 |
网络呼号
轻点 当事件发生时,比特将 POST 请求发送到您的 URL 。 有效载荷由HMAC SHA-256签署.
// Create a webhook
const webhook = await eb.createWebhook({
url: "https://your-server.com/webhooks/easybits",
events: ["file.created", "file.deleted"],
});
// Save the secret — it's only shown once
console.log(webhook.secret); // whsec_...
活动
| 活动 | 触发 |
|---|---|
file.created | 文件已上传或复制 |
file.updated | 文件名称、访问或元数据已更改 |
file.deleted | 文件被软删除 |
file.restored | 从回收站恢复文件 |
website.created | 创建网站 |
website.updated | 网站名称或弹片已更改 |
website.deleted | 网站已删除 |
workspace.created | 创建工作空间 |
workspace.deleted | 工作空间已删除 |
验证签名
import { createHmac } from "crypto";
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = `sha256=${createHmac("sha256", secret).update(body).digest("hex")}`;
return signature === expected;
}
// In your webhook handler:
const signature = req.headers["x-easybits-signature"];
const event = req.headers["x-easybits-event"];
const isValid = verifyWebhook(rawBody, signature, webhook.secret);
有效载荷格式
{
"event": "file.created",
"timestamp": "2026-02-26T12:00:00.000Z",
"data": {
"id": "abc123",
"name": "photo.jpg",
"size": 1024000,
"contentType": "image/jpeg",
"access": "private"
}
}
自动暂停
Webhooks在连续5次发送失败后自动被暂停. 以 :
await eb.updateWebhook(webhookId, { status: "ACTIVE" });
实例
批量上传文件
const { items } = await eb.bulkUploadFiles([
{ fileName: "a.pdf", contentType: "application/pdf", size: 50000 },
{ fileName: "b.png", contentType: "image/png", size: 120000 },
]);
for (const { file, putUrl } of items) {
await fetch(putUrl, { method: "PUT", body: buffers[file.name] });
await eb.updateFile(file.id, { status: "DONE" });
}
检查账户使用情况
const stats = await eb.getUsageStats();
console.log(`${stats.storage.usedGB}/${stats.storage.maxGB} GB used`);
console.log(`${stats.counts.files} files, ${stats.counts.webhooks} webhooks`);
复制文件
const copy = await eb.duplicateFile("abc123", "photo-backup.jpg");
倾斜
每个被标注的列表返回一个信封: { items, nextCursor, hasMore } (部分)
还包括: total) (中文(简体) ). 何时 hasMore 实值 true,通过 nextCursor 恢复为 cursor
获取下一页 :
let cursor: string | undefined;
do {
const { items, nextCursor, hasMore } = await eb.listFiles({ limit: 50, cursor });
process(items);
cursor = nextCursor ?? undefined;
if (!hasMore) break;
} while (cursor);
处理错误
所有出错都有一个形状: JSON 躯体 { "error": "message" },有时与
额外字段类 code 或者说 status。 。 。 .
import { EasybitsError } from "@easybits.cloud/sdk";
try {
await eb.getFile("nonexistent");
} catch (err) {
if (err instanceof EasybitsError) {
console.log(err.status); // 404
console.log(err.body); // '{"error":"File not found"}'
}
}
沙盒
在孤立的鞭炮微VMs中运行代码——用持久执行代码 Jupyter 内核,管理文件,运行背景进程,并曝光端口为 公共 HTTPS URLs. (原始内容存档于2018-09-29).
import { EasybitsClient } from "@easybits.cloud/sdk";
const eb = new EasybitsClient({ apiKey: process.env.EASYBITS_API_KEY });
// Create a sandbox (waits until it's running)
const sbx = await eb.sandboxes.create({ template: "code-interpreter" });
// Persistent Python kernel — state survives between calls
await sbx.runCell("import pandas as pd; df = pd.read_csv('sales.csv')");
const out = await sbx.runCell("df.groupby('month').total.sum()");
console.log(out.stdout);
// matplotlib charts come back as image/png in results[]
const chart = await sbx.runCell("df.plot(); plt.show()");
const png = chart.results.find((r) => r.type === "image/png")?.data; // base64
// Run a server and get a public URL. Write the command as `exec <program>`:
// the shell is REPLACED by your process instead of staying as its parent, so
// signals and logs go straight to it. (Do NOT try to background something with
// nohup or & inside exec(): that call is synchronous and its shell dies with
// the response, taking the child with it. That is what execBackground is for.)
const { execId } = await sbx.execBackground("exec python3 -m http.server 3000");
const { url } = await sbx.exposePort(3000);
console.log(url); // https://sb-...-3000.sandboxes.easybits.cloud
// Poll it, list them, kill it. bgStatus returns the ACCUMULATED stdout, so
// live logs are just `st.stdout.slice(seen)` — no websockets needed.
const st = await sbx.bgStatus(execId); // { status, exitCode?, stdout, stderr }
const { processes } = await sbx.bgList(); // lost the execId? it is in here
await sbx.bgKill(execId, { graceSeconds: 2 });
// bgKill signals the whole process GROUP (SIGTERM, then SIGKILL after the
// grace), so a dev server's forked children die too instead of surviving and
// holding the port. Killing an already-finished process succeeds and reports
// alreadyExited.
// exposePort is HTTP-only (22/23/25/445/3389 are rejected). For a raw L4 port:
const fwd = await sbx.exposeRawPort(22, "tcp");
console.log(fwd.endpoint); // cname.sandboxes.easybits.cloud:49123 — dial this
// hostPort comes from a pool, differs per box and is released on destroy:
// read it back, never hardcode it. Close it with unexposeRawPort(22, "tcp").
// Only templates that declare the port work; a 403 is permanent, not transient.
// SSH: injects the key, restarts the box sshd and opens 22, in one call.
// The box sshd is fail-closed, so the key has to go in before the port opens.
const ssh = await sbx.enableSsh(["ssh-ed25519 AAAA... me@laptop"]);
console.log(ssh.command); // ssh -p 49002 root@<host of THAT box>
// Key-only, as root. disableSsh() closes the port but does NOT revoke the key.
// PREFER THE TUNNEL over that command. A high port does not survive office
// networks or corporate VPNs, and that failure reaches you as an
// unreproducible "it won't connect". The tunnel rides the same 443 as the web:
//
// npm i -g @easybits.cloud/cli && easybits login # or: easybits login - < key.txt
// easybits ssh-key # your public key; created once, never leaves the box
//
// # ~/.ssh/config
// Host *.ghosty
// ProxyCommand easybits ssh-proxy %h
// User root
//
// ssh my-box.ghosty # the `name` you gave it at create time
// ssh sb_abc123.ghosty # the id works too
// ssh my-box.ghosty "cd /data/work && ghosty serve --acp" # remote ACP
//
// enableSsh() is still needed once, to inject the key (the box sshd is
// fail-closed). The tunnel moves opaque bytes and does NOT authenticate: the
// SSH session authenticates end-to-end against the box's sshd, so a bug in the
// tunnel cannot let anyone in. sshTicket() mints the short-lived signed ticket
// the proxy uses — you rarely call it yourself.
// Files
await sbx.files.write("/tmp/data.json", JSON.stringify({ ok: true }));
const { content } = await sbx.files.read("/tmp/data.json");
// Lifecycle
await sbx.extend(600); // add 10 min to the TTL
await sbx.destroy();
快照叉( 复制- 写入克隆) (F)
别动a 运行 框,然后启动 无子女 从它 - 每个独立沙盒都有自己的IP。 一次准备环境( 选项 ) 安装、项目设置、快照和叉子并列尝试 N 变体 不重复设置.
// Base box: install deps once
const base = await eb.sandboxes.create({ template: "node" });
await base.exec("npm i -g cowsay");
// Snapshot the ready state — the box keeps running
const snap = await base.snapshot("deps-ready");
// Fork into 3 children that run in parallel (each inherits the disk)
const kids = await base.fork({ count: 3 });
for (const k of kids) {
await k.waitUntilReady();
console.log(k.sandboxId, (await k.exec("cowsay hi")).stdout);
}
// Reuse the snapshot later, without the base box
const more = await eb.sandboxes.forkFromSnapshot(snap.snapshotId, { count: 2 });
// Catalog + cleanup
await eb.sandboxes.snapshots.list();
await eb.sandboxes.snapshots.delete(snap.snapshotId);
衍生模板( 模板- snapshot)
准备 一个 框内(npm ci种子,技能,在内容下捕捉一次
键 (key, hash),每个框 创建与该对 与生俱来
靴子绑好了 ——没有分叉,没有每个孩子的复制;主机只保留磁盘三角洲
(12-22 MB) (中文(简体) ). 测量:捕捉~0.5-1 s,取自衍生模板~0.6 s
- ~2个靴子. 这就是
@easybits.cloud/eve-sandbox用于夜之夜prewarm。 。 。 .
const base = await eb.sandboxes.create({ template: "node" });
await base.exec("cd /workspace && npm ci");
// Idempotent per (key, hash): a second call returns reused: true
const dt = await base.templateSnapshot({ key: "agent", hash: "3f9c" }); // { derivedId, reused, sizeBytes }
// Children never inherit the source env (secrets are scrubbed): pass their own
const child = await eb.sandboxes.create({ templateKey: "agent", templateHash: "3f9c", env: { FOO: "bar" } });
await eb.sandboxes.templateSnapshots.get({ key: "agent", hash: "3f9c" }); // 404 DerivedTemplateNotProvisioned → capture first
await eb.sandboxes.templateSnapshots.list();
await eb.sandboxes.templateSnapshots.delete(dt.derivedId); // 409 DerivedTemplateInUse while children live
未使用30天,一个衍生模板会自删除. 如果基准模板是
重温, create 答题 409 DerivedTemplateStale:又捉来.
网络政策(每箱 Egress)
await sbx.setNetworkPolicy("deny-all");
await sbx.setNetworkPolicy({ allow: { "api.github.com": [], "registry.npmjs.org": [] } }); // no wildcards
await sbx.setNetworkPolicy("allow-all");
由主机决定IP( DNS 刷新), 坚持使用框并重新应用
简历. transform (头部注射)不支持.
睡觉/醒来(静眠)
无 suspendOnIdle,这个盒子是 已销毁 何时 timeoutSeconds (默认)
300 s)发作. 以后要谈的,你先睡吧
挂起的框是一个鞭炮快照,在~1 s 中恢复并保存磁盘和内存.
// At creation
const sbx = await eb.sandboxes.create({ template: "node", timeoutSeconds: 600, suspendOnIdle: true });
// Or on an existing box (e.g. one you created without it)
await sbx.setIdlePolicy({ suspendOnIdle: true, idleTtlSeconds: 600, hardTtlSeconds: 7 * 24 * 3600 });
await sbx.suspend(); // sleep now; TTL is paused
await sbx.resume(); // wake; the remaining lifetime is restored
模板
使用 eb.listTemplates() 用于有需要的内存的活目录。 类型 : base
(运行代码), agent (已准备好的代理人), service (平台框开始于
service_start, (中文(简体) ), internal (平台创建的员工).
| 模板 | 仁爱 | 说明 |
|---|---|---|
ubuntu | 基础 | 满地取出. 安装软件包,编译,运行服务器. |
python | 基础 | Python 运行时间;每个运行代码是一个新鲜的过程. |
node | 基础 | 节点24+打字,tsx,pnpm,git和python3;每个跑码是一个新鲜的过程. |
bun | 基础 | Bun 运行时间. |
dev-box | 基础 | 清洁工作箱(基特、卷曲、基本建设、节点22);为SSH推荐的工作箱. |
code-interpreter | 基础 | Python + 持久性 Jupyter 内核(sandbox run cell):在细胞间存活的变量和图表. |
eve-nitro | 基础 | 自发托管的夜(Vercel)服务器:节点24,pnpm,前夕CLI 0.65;持久/数据,端口3000. |
node-agent | 代理人 | 节点 + Claude Agent SDK 预烤(代理运行). |
claude-code | 代理人 | Claude探员SDK回路,每笔账单. |
goose | 代理人 | 鹅 (AAIF),与本地非加太公司的编码代理. |
ghostyclaw | 代理人 | 常上"鬼"守护进程(WhatsApp,Slack,Telegram)与多克和admin-api. |
ghosty-lite | 代理人 | 轻量级 Rust ACP 代理, 多供应商; 您的 Easy 位键可以是它的大脑. |
open-ghosty | 代理人 | 开放式模型上的"鬼",SSE网络聊天. |
lang-ghosty | 代理人 | (原始内容存档于2012-10-12). Ghosty on LangChain, SSE web talk. |
rust-ghosty | 代理人 | DeepSeek-First Ghosty (CodeWhale/Rust) 有SSE网络聊天和WhatsApp. |
ghosty-gc | 代理人 | Ghosty for teams (GTeams):线条,文物,合作编辑. |
ghosty-chat | 代理人 | 持续幽灵聊天(Express + SSE). |
cagent-ghosty | 代理人 | Cagent上的幽灵 (Docker),SSE网络聊天. |
openclaw | 代理人 | OpenClaw,总是个人AI. |
chat-openai | 代理人 | 在 OpenAI 上持续快递+SSE 聊天; 使用代理创建 create. |
chat-anthropic | 代理人 | 在 Anthropic 上持续快递+ SSE 聊天; 用代理 创建它 . |
ghosty-studio | 代理人 | Ghosty Studio:一个盒子里面的特工控制平面. |
desktop-ghosty | 代理人 | Linux 有 Ghosty (noVNC) 的桌面. |
computer-ghosty | 代理人 | 用XFCE桌面+公用NoVNC的计算机使用. |
computer-ghosty-gemini | 代理人 | 电脑对双子座的使用. |
livekit-svc | 服务 | 视频呼叫室+HD录音 (Studio). |
whisper-svc | 服务 | 低声STT; 语音框的一部分. |
kokoro-svc | 服务 | kokoro TTS; 语音盒的一部分. |
voice-svc | 服务 | 为舰队配音(STT + TTS; service start ('voice')). |
render-svc | 服务 | PDF/PNG/审计的铬; service start('nder'). |
collab-svc | 服务 | GTeams 协作编辑器 (Yjs). |
hyperframes-svc | 服务 | HyperFrames 视频渲染. |
claude-worker | 内部 | 舰队工人 (克洛德). 由平台创建. |
codex-worker | 内部 | 舰队工人 (Codex). 由平台创建. |
gemini-worker | 内部 | 舰队工人 (格米尼). 由平台创建. |
对于没有持续状态的一次性片段,使用 sbx.runCode(code, { lang })。 。 。 .
建筑代理人 夜 间? 吗? @easybits.cloud/eve-sandbox 转弯
这些盒子进入了夜之夜 SandboxBackend。 。 。 .
舰队特工
弹性舰队特工与麻风工人的对话(WhatsApp, WABA,
页:1 用客户端证书创建/列表/删除认证( 用户 OAuth JWT)
与 WRITE 范围); 每一个配置和信件的调用都使用每个代理 token
返回时间 create() ——坚持不懈 { id, token } 并重用符号作为
第二次辩论 一个客户端实例可以配置许多代理.
// Lifecycle — auth = client credential (pass the user JWT as apiKey)
const eb = new EasybitsClient({ apiKey: userJwt });
const { fleetAgent } = await eb.fleet.create({ name: "Tania", systemPrompt: "...", model: "claude-sonnet-5" });
const { id, token } = fleetAgent; // persist BOTH
// Pick a non-default engine (DeepSeek/Codex/…). Some engines need a provider
// secret — set it right after with setSecret (e.g. DEEPSEEK_API_KEY).
const ds = await eb.fleet.create({ engine: "deepseek", name: "Vendedor", systemPrompt: "..." });
await eb.fleet.setSecret(ds.fleetAgent.id, ds.fleetAgent.token, { name: "DEEPSEEK_API_KEY", value: "sk-..." });
await eb.fleet.list(); // { pools: [...] }
await eb.fleet.delete(id);
// Read config — auth = per-agent token
const caps = await eb.fleet.getCapabilities(id, token);
caps.agent.model; // "claude-sonnet-5"
caps.agent.effort; // "medium"
caps.skills; // [{ id, name, enabled, ... }]
// Agent-level config
await eb.fleet.setName(id, token, "Tania");
await eb.fleet.setAgentPrompt(id, token, "New base instructions...");
await eb.fleet.setModel(id, token, "claude-opus-4-8");
await eb.fleet.setEffort(id, token, "high"); // low|medium|high|xhigh|max
await eb.fleet.toggleOwnNumber(id, token, true);
await eb.fleet.addMcp(id, token, { name: "stripe", pkg: "@stripe/mcp", requiredSecret: "STRIPE_KEY" });
await eb.fleet.toggleSkill(id, token, { skillId: "abc", on: true });
// Per-channel config (groupId; "*" = the agent's default)
await eb.fleet.setGroupPrompt(id, token, "*", "Extra per-channel instructions");
await eb.fleet.setToolGroup(id, token, "*", { buckets: ["documentos", "db", "db-write"] });
await eb.fleet.setCapLevel(id, token, "*", { cap: "denik", level: "write" });
// Messaging
const { reply } = await eb.fleet.message(id, token, { groupId: "web-123", text: "Hola" });
// WhatsApp (Baileys) connection — auth = client credential (owner), NOT the per-agent token.
// Link a PERSONAL number (never a Business/WABA number). Omit pairingPhone → QR; pass it → code.
await eb.fleet.connect(id); // then poll:
const { baileys } = await eb.fleet.connectionState(id); // { status, qr?, pairingCode?, pairBlockedUntil? }
// status: qr_pending | pairing | connecting | connected | failed | disconnected. Poll ~2.5s.
const { groups } = await eb.fleet.listGroups(id); // [{ groupId, subject, enabled, isMain }] (on-demand)
await eb.fleet.toggleGroup(id, groups[0].groupId, true); // answer here
await eb.fleet.setMain(id, groups[0].groupId); // admin/main channel
await eb.fleet.disconnect(id);
getCapabilities 返回完整目录 + 当前状态(已建构,容量,
桶头, bucketTools,模型,技能,每组配置. 见类型
FleetCapabilities 为了形状.
MCP 一体化
对于 AI探员,同样的沙盒作为MCP工具可用(代理调用) 它们本身——不需要代码:
npx -y @easybits.cloud/mcp
许可证
麻省理工学院