Convex 组件
运行 代顿a 来自您的 Convex 后端的沙盒: 创建孤立的沙盒, 执行 shell 命令和代码, 读取/ 写入文件, 并获得实时预览 URL —— 在您可以订阅的 被动 Convex 表格中跟踪沙盒和执行状态 .
// convex/agent.ts
import { Daytona } from "@daytona/convex";
import { v } from "convex/values";
import { components } from "./_generated/api";
import { action } from "./_generated/server";
const daytona = new Daytona(components.daytona);
export const codeInterpreter = action({
args: { code: v.string() },
handler: async (ctx, args) => {
const { sandboxId } = await daytona.createSandbox(ctx, {
autoStopInterval: 15,
});
const { result, exitCode } = await daytona.runCode(ctx, {
sandboxId,
code: args.code,
language: "python",
});
return { result, exitCode };
},
});
为什么一个组件不直接呼叫代托纳API?
- 反应状态 ——每个沙盒和执行都记录在成份表中. 驱动代理UIs("running...",出站代码,输出历史)来自现场Convex查询而不是投票.
- 单独记账 ——组件将表格保存在自己的命名空间;无法触摸到您的应用程序的数据,您的应用程序通过一个打入的客户端来管理.
- 没有SDK捆绑 - 与代托纳REST API的会谈
fetch在 Convex 默认运行时间中: 否"use node"没有重的依赖性, 快速的寒冷开始.
安装
npm install @daytona/convex
设置您部署的代托纳 API 密钥( 在 app.daytona.io/dashboard/键盘:
npx convex env set DAYTONA_API_KEY dtn_...
然后创建或更新 convex/convex.config.ts 在你的应用程序。 组件不能读取您的应用程序的env vars,所以将密钥传递到组件:
// convex/convex.config.ts
import { defineApp } from "convex/server";
import { v } from "convex/values";
import daytona from "@daytona/convex/convex.config.js";
const app = defineApp({
env: { DAYTONA_API_KEY: v.string() },
});
app.use(daytona, {
env: { DAYTONA_API_KEY: app.env.DAYTONA_API_KEY },
});
export default app;
通过引用传递,意味着组件总是看到当前值,因此旋转键不需要再调用.
可选 : DAYTONA_API_URL (自主办实例;默认为 https://app.daytona.io/api) (中文(简体) ). 设相同相. 这不是一个秘密,所以它不需要通过:客户端通过每通电话转发.
使用量
用安装的组件为客户端提供证据 。 从您的部署环境变量中获取的证书决心, 或明确通过这些变量:
import { Daytona } from "@daytona/convex";
import { components } from "./_generated/api";
const daytona = new Daytona(components.daytona);
// or: new Daytona(components.daytona, { apiKey: "dtn_..." })
runBackground 是例外: 它总是使用传递到组件的密钥 convex.config.ts。 。 。 .
沙盒生命周期
export const create = action({
args: {},
handler: async (ctx) => {
return await daytona.createSandbox(ctx, {
snapshot: "my-snapshot", // optional — org default when omitted
autoStopInterval: 15, // pause after 15 idle minutes (fs preserved)
autoDeleteInterval: -1, // never auto-delete
userKey: "user_123", // scope sandboxes to a user/agent (see below)
});
// → { sandboxId: "…", state: "started" }
},
});
从一个 简介 (你的,或兽类默认) 或者说 由 Docker 创建 图像 ——通过一个或另一个. cpu编号 :memory编号 :disk 仅适用于基于图像的创建; 快照定义了自己的资源 :
await daytona.createSandbox(ctx, { image: "python:3.12", cpu: 2, memory: 4 });
要跨越沙盒共享或粘接文件, 请挂起 代托纳卷。只有创建了沙盒,才能将音量挂起;用代托纳SDK、CLI或仪表板创建音量本身,并等待它 ready数字 :
await daytona.createSandbox(ctx, {
volumes: [
{ volumeId: "shared-models", mountPath: "/home/daytona/models" }, // volume ID or name
{ volumeId: "tenants", mountPath: "/home/daytona/data", subpath: "users/alice" }, // only this prefix
],
});
还提供: startSandbox, (中文(简体) ), stopSandbox, (中文(简体) ), deleteSandbox,以及 refreshSandbox (将远程状态重新同步到组件的表格;返回) null 如果沙盒已经不复存在).
运行命令和代码
const { executionId, exitCode, result } = await daytona.run(ctx, {
sandboxId,
command: "python train.py",
cwd: "/home/daytona/project",
timeoutSeconds: 300,
});
const py = await daytona.runCode(ctx, {
sandboxId,
code: "print(sum(range(10)))",
language: "python",
});
一个已停止或存档的沙盒首先透明地重新启动( 无法使用) autoStart: false) (中文(简体) ). 每一个电话记录一个行刑行(running → completed编号 :failed)带有被截断的输出,因此历史和状态是可以查询的.
对于长期运行的命令, runBackground 在沙盒会话中启动命令并立即返回 —— 启动行动没有在命令的持续时间内开放(而是定期进行轻量级调度器驱动的计票员检查), 命令不受10分钟行动上限的约束 :
const { executionId } = await daytona.runBackground(ctx, {
sandboxId,
command: "python train.py",
cwd: "/home/daytona/project",
});
// Watch it reactively: a scheduler-driven poller streams logs into the row
// while it runs and records the exit code when it finishes.
export const training = query({
// Component table IDs cross the boundary as plain strings.
args: { executionId: v.string() },
handler: async (ctx, args) => daytona.getExecution(ctx, args),
});
当命令完成时(而不是投票时),让你的后端作出反应,通过一个 onComplete 突变——组件在每个终端状态上引用(completed, (中文(简体) ), failed,或 cancelled结果和可选 context 您提供 :
const { executionId } = await daytona.runBackground(ctx, {
sandboxId,
command: "cargo build",
onComplete: internal.build.buildFinished, // (ctx, { executionId, status, exitCode, result, error, context })
onCompleteContext: { appId },
minPollMs: 500, // first poll + backoff floor (default 1000, min 250)
maxPollMs: 5000, // backoff ceiling (default 10000, max 120000)
});
取消正在运行的背景执行 daytona.cancelExecution(ctx, { executionId }) 它停止命令( 杀死它的会话) , 标记行 cancelled,并发射 onComplete 处理者.
运行的背景命令不会重置沙盒 闲置自动停止计时器 - 为长工创建沙盒 autoStopInterval: 0。 。 。 .
runBackground 需要按 安装它的传粉器从排程器,在你的任何电话之外, 所以它读到 DAYTONA_API_KEY 而不是用预定的函数参数来表示.
反应状态
他们阅读了组件的表格
export const sandboxes = query({
args: {},
handler: async (ctx) => daytona.listSandboxes(ctx, { userKey: "user_123" }),
});
export const executions = query({
args: { sandboxId: v.string() },
handler: async (ctx, args) => daytona.listExecutions(ctx, args),
});
还: getSandbox, (中文(简体) ), getExecution。 。 。 .
文件
await daytona.writeFile(ctx, { sandboxId, path: "/home/daytona/app.py", content });
const text = await daytona.readFile(ctx, { sandboxId, path: "/home/daytona/app.py" });
const entries = await daytona.listFiles(ctx, { sandboxId, path: "/home/daytona" });
await daytona.deleteFile(ctx, { sandboxId, path: "/home/daytona/tmp", recursive: true });
readFile编号 :writeFile 将内容移动为 UTF-8 字符串——代码、配置和日志的正确默认值。 对于二进制文件(图像、PDF、档案),使用字节变体,通过生 ArrayBuffer穿过未触及的:
const png = await daytona.readFileBytes(ctx, { sandboxId, path: "/home/daytona/chart.png" });
await ctx.storage.store(new Blob([png], { type: "image/png" })); // e.g. into Convex file storage
await daytona.writeFileBytes(ctx, { sandboxId, path: "/home/daytona/data.zip", content: zipBuffer });
无论哪种方式,文件内容都会跨越 Convex 函数边界,所以将单个文件保留在 Convex 的 16 MiB 参数/返回限制下.
预览 URL
const { url } = await daytona.getPreviewUrl(ctx, { sandboxId, port: 3000 });
已签名的 URL( 默认) 嵌入一个短命符; pass signed: false 用于标准 URL + 符号对.
授权
组件看不到你的应用程序 ctx.auth - 认证在自己的功能和瞄准镜沙箱中的呼叫者 userKey数字 :
export const mySandboxes = query({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Unauthorized");
return await daytona.listSandboxes(ctx, { userKey: identity.subject });
},
});
保持沙盒状态同步( webhooks)
Sandbox记录反映了组件最后观察到的内容. 当代托纳独自改变一个沙盒(自动停止,自动存档,自动删除)时,记录会延后到呼叫 refreshSandbox保留 sandboxes 列表在实时同步中连接一个代顿纳 Webhook。 选入,并分出三个步骤:
1. 挂载组件的网路并传递秘密 内 convex/convex.config.ts数字 :
const app = defineApp({
env: {
DAYTONA_API_KEY: v.string(),
DAYTONA_WEBHOOK_SECRET: v.optional(v.string()),
},
});
app.use(daytona, {
httpPrefix: "/daytona/",
env: {
DAYTONA_API_KEY: app.env.DAYTONA_API_KEY,
DAYTONA_WEBHOOK_SECRET: app.env.DAYTONA_WEBHOOK_SECRET,
},
});
2. 在 Daytona Dashboard 中创建一个网点 (单位:千美元)如何创建终点:
- 端点 URL数字 :
https://<your-deployment>.convex.site/daytona/webhook(您的部署中的 HTTP 动作 URL, 加上上面的前缀) - 活动: 订阅
sandbox.state.updated。该组件忽略了所有其他事件,因此订阅其他任何信息只会产生额外交付.
在 戴托纳组织和你 DAYTONA_API_KEY:webhooks是每个组织,所以另一个org中的一个终点从未收到你的沙盒事件.
3. 将端点签名的秘密复制到您的部署中. Daytona 生成签名密钥( 开始于 whsec_对于每一个端点,你创建 并签注每一次送货。 在代托纳板 网络呼号 页面,单击端点表格中的端点;其细节显示签名秘密,准备复制。 设定为您的 Convex 部署 :
npx convex env set DAYTONA_WEBHOOK_SECRET whsec_...
从那时起 代托纳的每个州都出现了 getSandbox编号 :listSandboxes 事情就是这样 譬如说, listSandboxes(ctx, { state: "started" }) 成为准确的活计。 每一次送货的签名都经过验证(超过5分钟的货被拒),尽可能重复播放. 重复交付和订单外交付自动处理。 沙盒事件 这个部署没有被确认和忽略, 因为一个端点接收到你组织的每一个沙盒。 如果秘密没有设定的话,路线会拒绝所有的交货.
限制长期工作( L)
- 10分钟后对流动作暂停,所以同步
run编号 :runCode命令始终被限制在该上限以下:timeoutSeconds默认值为540,上限为570。 用于较长的工作runBackground它没有期限,也没有采取行动. - 沙盒和行刑行作为审计历史保存. 他们从不清理自己
daytona.purgeSandboxes(ctx, { olderThanMs })财务报告和审定财务报表daytona.purgeExecutions(ctx, { olderThanMs })(每个呼叫返回)hasMore;只有终端行,远程沙盒没有被移动).listSandboxes接受一个state过滤器(例如)."started")和listSandboxesPaginated提供光标页. - 存储的执行输出被截取( 64 KB); 动作的返回值载出最多 4 MB.
- 运行时沙盒成本: 设置
autoStopInterval,并删除你用完的沙盒.refreshSandbox核对索要的记录; 网络用户 所有记录自动同步.
见 实例/会议/实例 一个完整的实例应用程序.
找到窃听器了吗? 特质请求? 放在这。 。 。 .
发展
这个包裹住在 daytona/integrations 单重 packages/convex 并且自成一体的 package.json,锁定文件,没有工作空间工具.
git clone https://github.com/daytona/integrations
cd integrations/packages/convex
npm install
组件 src/component/_generated 代码被承诺; 在改变组件函数后重生 :
npx convex init # one-time: bootstrap a local anonymous deployment
npm run codegen # component codegen (src/component/_generated)
npx convex codegen # example app codegen (example/convex/_generated)
检查 :
npm run build # compile to dist/
npm run typecheck # package sources
npm run typecheck:example # example app (needs codegen above)
npm test # offline unit tests (mocked Daytona API)
npm run test:live # live E2E: local Convex deployment + real Daytona (needs DAYTONA_API_KEY)
npm run dev 启动 Convex dev 部署服务, 为示例应用服务 —— set DAYTONA_API_KEY 用来对抗真正的沙盒.
出版
释放是自动的 请放行:将这个软件包的"放出PR"标签合并,并从repo的放出工作流程发布到npm(公开,有出处).
许可证
阿帕奇 - 2.0