@easybits.cloud/sdk

中文文档
0.44.0发布于 2026/10/23MITTypeScriptESM + CJS零依赖近 30 天下载 2217

Typed HTTP client for the EasyBits cloud for AI agents — sandboxes, web, files, SQL databases, documents and app hosting

原始使用文档
文档目录34 节
  1. Install
  2. Quick Start
  3. Authentication
  4. Methods
  5. Files
  6. Images
  7. Sharing
  8. Webhooks
  9. Crons
  10. Secrets (account vault)
  11. Websites
  12. Workspaces
  13. Documents
  14. Account
  15. Webhooks
  16. Events
  17. Verifying signatures
  18. Payload format
  19. Auto-pause
  20. Examples
  21. Bulk upload files
  22. Check account usage
  23. Duplicate a file
  24. Pagination
  25. Error Handling
  26. Sandboxes
  27. Snapshot & fork (copy-on-write clone)
  28. Derived templates (template-snapshot)
  29. Network policy (per-box egress)
  30. Sleep / wake (survive quiet periods)
  31. Templates
  32. Fleet Agents
  33. MCP Integration
  34. License

@easybits.cloud/sdk

The typed HTTP client for the EasyBits cloud for AI agents — sandboxes, web, files, SQL databases, documents and app hosting.

EasyBits is the cloud AI agents already know how to use: run code in an isolated Firecracker microVM, search and read the web, store and serve files, query a SQL database, generate documents and deploy apps — via SDK, MCP and REST API. Priced in MXN.

Install

npm install @easybits.cloud/sdk

Quick Start

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" });

Authentication

Get your API key from the Developer Dashboard.

const eb = new EasybitsClient({ apiKey: process.env.EASYBITS_API_KEY! });

Or use automatic resolution from ~/.easybitsrc or env vars:

import { createClientFromEnv } from "@easybits.cloud/sdk";
const eb = await createClientFromEnv();

Methods

Files

MethodDescription
listFiles(params?)List your files (paginated)
getFile(fileId)Get file details + download URL
uploadFile(params)Create file record + get upload URL
updateFile(fileId, params)Update name, access, metadata, or status
deleteFile(fileId)Soft-delete a file (7-day retention)
restoreFile(fileId)Restore a soft-deleted file
listDeletedFiles(params?)List deleted files with days until purge
searchFiles(query)AI-powered natural language file search
bulkUploadFiles(items)Create up to 20 file records + upload URLs
bulkDeleteFiles(fileIds)Soft-delete up to 100 files at once
duplicateFile(fileId, name?)Copy an existing file (new storage object)
listPermissions(fileId)List sharing permissions for a file

Images

MethodDescription
optimizeImage(params)Convert to WebP/AVIF (creates new file)
transformImage(params)Resize, crop, rotate, flip, grayscale

Sharing

MethodDescription
shareFile(params)Share a file with another user by email
generateShareToken(fileId, expiresIn?)Generate a temporary download URL
listShareTokens(params?)List share tokens (paginated)

Webhooks

MethodDescription
listWebhooks()List your configured webhooks
createWebhook(params)Create a webhook (returns secret, shown once)
getWebhook(webhookId)Get webhook details
updateWebhook(webhookId, params)Update URL, events, or status
deleteWebhook(webhookId)Permanently delete a webhook

Crons

Scheduled jobs of your account: call a public https URL (optionally with a vault secret as a header) or send a turn to one of your fleet agents. At most every 5 minutes; IANA time zone (default America/Mexico_City); limit per plan (Byte 3, Mega 20, Tera 100).

MethodDescription
listCrons({ includeAgents? })Your crons (includeAgents adds your agents as turn targets)
createCron(params){ name, cron, timezone?, enabled?, action }
getCron(id) / updateCron(id, params) / deleteCron(id)updateCron(id, { enabled: false }) pauses it
runCron(id)Run now in the background
listCronRuns(id, { limit? })Latest runs, newest first
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" },
});

Secrets (account vault)

MethodDescription
listSecrets()Names in your vault ({ items:[{id,name,createdAt,lastUsedAt}] }); values are never returned
setSecret(name, value)Store or replace a secret ([A-Z_][A-Z0-9_]*); used by name as $secret:NAME
deleteSecret(name)Delete it; whatever references it stops resolving

Websites

MethodDescription
listSites()List your site-builder sites (/dash/sitios): static and web apps, with engine and URL
listWebsites()List your static websites
createWebsite(name, { slug? })Create a new website (optional custom slug)
getWebsite(websiteId)Get website details
updateWebsite(websiteId, params)Update website name, slug or status (old slug 301s to the new one)
deleteWebsite(websiteId)Delete website and its files

Workspaces

Namespaced, quota-bounded containers of files. Create one per tenant, mint a workspace-scoped key, and that key can only ever touch its own workspace's files.

MethodDescription
listWorkspaces(params?)List workspaces (cursor paginated)
createWorkspace({ name, slug?, quotaBytes? })Create a workspace
getWorkspace(workspaceId)Get workspace details
updateWorkspace(workspaceId, params)Update name/status/quota
deleteWorkspace(workspaceId)Delete workspace and its files
getWorkspaceUsage(workspaceId)Get { usedBytes, quotaBytes, fileCount }
createWorkspaceKey(workspaceId, params?)Mint a workspace-scoped API key (raw returned once)

Documents

MethodDescription
listDocuments()List your documents
getDocument(id)Get document with all pages
createDocument(params)Create a document
updateDocument(id, params)Update document metadata (name, theme, colors)
deleteDocument(id)Delete a document
deployDocument(id)Publish as live website
unpublishDocument(id)Unpublish document
generateDocument(id, params)AI-generate pages (parallel, streaming)
refineDocument(id, params)Surgical AI edits to a page
regenerateDocumentPage(id, params)Redesign a page keeping content
enhanceDocumentPrompt(name, prompt?)Auto-describe or enhance a prompt
getDocumentDirections(prompt, opts?)Get 4 design directions (fonts, colors, mood)

Account

MethodDescription
getUsageStats()Storage used/limit, file counts, plan info
listProviders()List storage providers
listKeys()List your API keys

Webhooks

EasyBits sends POST requests to your URL when events occur. Payloads are signed with 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_...

Events

EventTrigger
file.createdFile uploaded or duplicated
file.updatedFile name, access, or metadata changed
file.deletedFile soft-deleted
file.restoredFile restored from trash
website.createdWebsite created
website.updatedWebsite name or slug changed
website.deletedWebsite deleted
workspace.createdWorkspace created
workspace.deletedWorkspace deleted

Verifying signatures

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);

Payload format

{
  "event": "file.created",
  "timestamp": "2026-02-26T12:00:00.000Z",
  "data": {
    "id": "abc123",
    "name": "photo.jpg",
    "size": 1024000,
    "contentType": "image/jpeg",
    "access": "private"
  }
}

Auto-pause

Webhooks are automatically paused after 5 consecutive delivery failures. Reactivate with:

await eb.updateWebhook(webhookId, { status: "ACTIVE" });

Examples

Bulk upload files

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" });
}

Check account usage

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`);

Duplicate a file

const copy = await eb.duplicateFile("abc123", "photo-backup.jpg");

Pagination

Every paginated list returns one envelope: { items, nextCursor, hasMore } (some also include total). When hasMore is true, pass nextCursor back as cursor to fetch the next page:

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);

Error Handling

All errors share one shape: a JSON body { "error": "message" }, sometimes with extra fields like code or 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"}'
  }
}

Sandboxes

Run code in isolated Firecracker microVMs — execute code with a persistent Jupyter kernel, manage files, run background processes, and expose ports as public HTTPS URLs.

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();

Snapshot & fork (copy-on-write clone)

Freeze a running box into a named image, then boot N children from it — each an independent sandbox with its own IP. Prep the environment once (deps installed, project set up), snapshot it, and fork in parallel to try N variants without repeating the setup.

// 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);

Derived templates (template-snapshot)

Prepare one box (npm ci, seeds, skills), capture it once under a content key (key, hash), and every box created with that pair is born with the bootstrap done — no fork, no per-child copy; the host keeps only the disk delta (12-22 MB). Measured: capture ~0.5-1 s, create from the derived template ~0.6 s

  • ~2 s boot. This is what @easybits.cloud/eve-sandbox uses for eve's 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

Unused for 30 days, a derived template deletes itself. If the base template is rebaked, create answers 409 DerivedTemplateStale: capture again.

Network policy (per-box egress)

await sbx.setNetworkPolicy("deny-all");
await sbx.setNetworkPolicy({ allow: { "api.github.com": [], "registry.npmjs.org": [] } }); // no wildcards
await sbx.setNetworkPolicy("allow-all");

Resolved to IPs by the host (DNS refresh), persisted with the box and re-applied on resume. transform (header injection) is not supported.

Sleep / wake (survive quiet periods)

Without suspendOnIdle, the box is destroyed when timeoutSeconds (default 300 s) elapses. For anything you will talk to later, sleep it instead: a suspended box is a Firecracker snapshot, resumes in ~1 s and keeps disk and memory.

// 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

Templates

Use eb.listTemplates() for the live catalog with required env. Kinds: base (run code), agent (ready-made agent), service (platform boxes started with service_start), internal (fleet workers created by the platform).

TemplateKindDescription
ubuntubaseFull Linux. Install packages, compile, run servers.
pythonbasePython runtime; each run-code is a fresh process.
nodebaseNode 24 + typescript, tsx, pnpm, git and python3; each run-code is a fresh process.
bunbaseBun runtime.
dev-boxbaseClean work box (git, curl, build-essential, Node 22); the recommended one for SSH.
code-interpreterbasePython + persistent Jupyter kernel (sandbox_run_cell): variables and charts survive between cells.
eve-nitrobaseSelf-hosted eve (Vercel) server: Node 24, pnpm, eve CLI 0.65; persistent /data, port 3000.
node-agentagentNode + Claude Agent SDK pre-baked (agent_run).
claude-codeagentClaude Agent SDK loop; per-token billing.
gooseagentgoose (AAIF), coding agent with native ACP.
ghostyclawagentAlways-on Ghosty daemon (WhatsApp, Slack, Telegram) with Docker and admin-api.
ghosty-liteagentLightweight Rust ACP agent, multi-provider; your EasyBits key can be its brain.
open-ghostyagentGhosty on open models, SSE web chat.
lang-ghostyagentGhosty on LangChain, SSE web chat.
rust-ghostyagentDeepSeek-first Ghosty (CodeWhale/Rust) with SSE web chat and WhatsApp.
ghosty-gcagentGhosty for teams (GTeams): threads, artifacts, collaborative editor.
ghosty-chatagentPersistent Ghosty chat (Express + SSE).
cagent-ghostyagentGhosty on cagent (Docker), SSE web chat.
openclawagentOpenClaw, always-on personal AI.
chat-openaiagentPersistent Express+SSE chat on OpenAI; create it with agent_create.
chat-anthropicagentPersistent Express+SSE chat on Anthropic; create it with agent_create.
ghosty-studioagentGhosty Studio: agent control plane inside a box.
desktop-ghostyagentLinux desktop with Ghosty (noVNC).
computer-ghostyagentComputer-use with XFCE desktop + public noVNC.
computer-ghosty-geminiagentComputer-use on Gemini.
livekit-svcserviceVideo call room + HD recording (Studio).
whisper-svcservicewhisper STT; part of the voice box.
kokoro-svcservicekokoro TTS; part of the voice box.
voice-svcserviceVoice (STT + TTS) for the fleet; service_start('voice').
render-svcserviceChromium for PDF/PNG/audits; service_start('render').
collab-svcserviceGTeams collaborative editor (Yjs).
hyperframes-svcserviceHyperFrames video rendering.
claude-workerinternalFleet worker (Claude). Created by the platform.
codex-workerinternalFleet worker (Codex). Created by the platform.
gemini-workerinternalFleet worker (Gemini). Created by the platform.

For one-off snippets without persistent state, use sbx.runCode(code, { lang }). Building agents with eve? @easybits.cloud/eve-sandbox turns these boxes into eve's SandboxBackend.

Fleet Agents

Elastic fleet agents route conversations to ephemeral workers (WhatsApp, WABA, web). Create/list/delete authenticate with the client credential (a user OAuth JWT with WRITE scope); every config and message call takes the per-agent token returned by create() — persist { id, token } and reuse the token as the second argument. One client instance can configure many agents.

// 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 returns the full catalog + current state (builtins, capabilities, buckets, bucketTools, models, skills, per-group config). See the type FleetCapabilities for the shape.

MCP Integration

For AI agents, the same sandboxes are available as MCP tools (the agent calls them itself — no code needed):

npx -y @easybits.cloud/mcp

License

MIT

版本与发布记录

@easybits.cloud/sdk 共发布 59 个版本,首次发布于 2026/02/26;过去 12 个月发布了 59 个版本。

版本发布日期距今
0.44.0latest2026/10/2321 天前
0.43.02026/10/2321 天前
0.42.02026/10/2321 天前
0.41.02026/10/2321 天前
0.40.12026/10/1925 天前
0.40.02026/10/1925 天前

关于 @easybits.cloud/sdk 的常见问题

@easybits.cloud/sdk 是什么?

Typed HTTP client for the EasyBits cloud for AI agents — sandboxes, web, files, SQL databases, documents and app hosting

如何安装 @easybits.cloud/sdk?

在项目目录运行 npm install @easybits.cloud/sdk,或使用 pnpm add / yarn add / bun add。

@easybits.cloud/sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@easybits.cloud/sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@easybits.cloud/sdk 可以商用吗?

@easybits.cloud/sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@easybits.cloud/sdk 的下载量有多少?

根据 npm 官方下载统计,@easybits.cloud/sdk 在 2026/10/13 – 2026/11/11 这 30 天内被下载 2,217 次,日均约 74 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@easybits.cloud/sdk 还在维护吗?

最新版本 0.44.0 发布于 2026/10/23(21 天前);至今共发布 59 个版本,其中过去 12 个月发布了 59 个;首次发布于 2026/02/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。