Windmill DAP 调试模块完全指南:在 Monaco 编辑器中断点调试 Python 与 TypeScript/Bun 脚本
2026/9/13 16:56:08 网站建设 项目流程

Windmill DAP 调试模块完全指南:在 Monaco 编辑器中断点调试 Python 与 TypeScript/Bun 脚本

【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill

导读

Windmill 在仓库的 debugger/ 目录中内置了一套完整的 DAP(Debug Adapter Protocol,调试适配器协议)实现,让你无需离开 Web 编辑器就能对 Python 与 TypeScript/Bun 脚本进行逐行调试——包括设置断点、单步执行、变量检查、堆栈追踪与表达式求值。本文以 debugger/README.md 为骨架,结合 debugger/dap_debug_service.ts、debugger/dap_websocket_server.py、debugger/dap_websocket_server_bun.ts 等源码实现,完整讲解调试服务如何工作、如何启动配置、依赖如何自动安装、注册表/CA 凭据如何安全传递、nsjail 沙箱如何隔离会话,以及如何在前端集成与测试。读完本文,你可以独立部署、调优并扩展 Windmill 的浏览器内调试能力。

模块概览:一条 WebSocket,两套语言调试后端

调试模块的核心定位是:为 Windmill 的 Monaco 编辑器提供逐步调试能力,支持断点、变量检查与堆栈追踪。前端编辑器与语言专属的调试后端之间,通过 WebSocket 承载 DAP 协议消息进行通信。

模块支持两种语言的调试,各有独立的实现路线:

  • Python:基于 Python 标准库bdb的调试器,实现在 dap_websocket_server.py(服务进程内还会用到debugpy作为 DAP 网关,见下文架构说明);
  • TypeScript/Bun:通过 Bun 的WebKit Inspector 协议(而非 V8/Chrome DevTools 协议)调试,实现在 dap_websocket_server_bun.ts。

README 给出的架构图清晰地展示了三层结构:

┌─────────────────────┐ WebSocket ┌──────────────────────────┐ │ Monaco Editor │◄──────────────────►│ DAP Debug Service │ │ (dapClient.ts) │ DAP Protocol │ (dap_debug_service.ts) │ └─────────────────────┘ └──────────┬───────────────┘ │ ┌──────────┴───────────┐ │ │ ┌──────▼──────┐ ┌───────▼───────┐ │ Python │ │ Bun/TS │ │ Debugger │ │ Debugger │ └─────────────┘ └───────────────┘

其中 dap_debug_service.ts 是统一 WebSocket 服务,根据连接路径把请求路由到 Python 或 Bun 调试器,是本文的主角。

文件清单

文件描述
dap_debug_service.ts统一 WebSocket 服务,按端点路由到 Python 或 Bun 调试器
dap_websocket_server.pyPython 调试后端(基于 bdb)
dap_websocket_server_bun.tsBun/TypeScript 调试后端(基于 WebKit Inspector 协议)
registry_config.ts从 Windmill 后端拉取实例注册表设置的客户端
env_passthrough.ts向调试会话转发代理与 CA 信任根的环境变量白名单
nsjail.debug.config.protonsjail 沙箱配置文件
Dockerfile容器化调试服务的镜像构建文件
test_dap_server.py / test_dap_server_bun.ts / test_debug_service.ts三个调试器的测试脚本

启动与配置调试服务

命令行启动

最简单的启动方式(见 README "Usage"):

bun run debug/dap_debug_service.ts

服务支持以下命令行选项,均可在 dap_debug_service.ts 的parseConfig()中找到对应解析逻辑:

选项说明默认值
--port PORT服务端口3003
--host HOST服务绑定地址0.0.0.0
--python-path PATHPython 解释器路径python3
--bun-path PATHBun 二进制路径bun
--nsjail启用 nsjail 沙箱化关闭
--nsjail-config PATHnsjail 配置文件路径-
--nsjail-path PATHnsjail 二进制路径nsjail
--windmill PATHwindmill CLI 路径,用于依赖自动安装-
--debug开启调试日志关闭

启动后,服务提供以下 WebSocket 端点:

  • /python—— Python 调试;
  • /typescript—— TypeScript/Bun 调试;
  • /bun——/typescript的别名。

另外还有两个 HTTP 端点:根路径返回端点说明文本,/health(以及/ws_debug/health)返回 JSON 健康检查信息(statusserviceendpointsnsjail是否启用)。WebSocket 握手时若路径以/ws_debug/开头,服务会自动剥离此前缀,以兼容反代不剥前缀的部署方式;/ping路径则用于连通性测试,收到后立即回pong并关闭连接。

环境变量配置

README 给出了完整的服务端环境变量清单,与命令行选项一一对应:

变量描述默认值
DAP_PORT服务端口3003
DAP_HOST服务绑定地址0.0.0.0
DAP_PYTHON_PATHPython 二进制路径python3
DAP_BUN_PATHBun 二进制路径bun
DAP_NSJAIL_ENABLED是否启用 nsjail 沙箱false
DAP_NSJAIL_PATHnsjail 二进制路径nsjail
DAP_NSJAIL_CONFIGnsjail 配置文件路径-
DAP_WINDMILL_PATHwindmill CLI 路径(依赖自动安装)-
DAP_DEBUG是否开启调试日志false
DAP_PREPARE_DEPS_TIMEOUT_MS依赖安装超时120000
DAP_REGISTRY_CONFIG_TIMEOUT_MS拉取注册表设置的等待时间10000
WINDMILL_BASE_URL/BASE_INTERNAL_URLWindmill 后端地址(用于 JWKS 与注册表配置)-
REQUIRE_SIGNED_DEBUG_REQUESTS是否强制要求签名调试令牌true
DEBUG_ALLOWED_ORIGINS允许的浏览器 Origin(逗号分隔,跨源防护)
LOG_LEVEL日志级别(DEBUG/INFO等)INFO

安全与鉴权机制

调试服务不是裸奔的:任何launch请求都必须携带由后端签发的调试令牌。从源码看,verifyDebugToken 的校验链路包括:

  1. ${WINDMILL_BASE_URL}/api/debug/jwks拉取并缓存Ed25519 公钥getPublicKey()支持并发去重,仅请求一次);
  2. crypto.subtle.verify校验 JWT 签名;
  3. 校验exp过期时间;
  4. 校验code_hash:对提交的内联代码做 SHA-256 并取前 16 字节十六进制,与令牌中的声明比对,防止代码在签名后被篡改

两条额外的防线值得注意:

  • 拒绝 program 模式启动:后端只为内联code签名,因此服务直接拒绝携带program(服务端任意文件路径)的 launch,避免绕过令牌校验执行任意服务端文件;
  • 可选的跨源防护(CSWSH):当设置了DEBUG_ALLOWED_ORIGINS(逗号分隔)时,握手阶段会拒绝携带非白名单Origin头的浏览器请求(非浏览器客户端不带 Origin,不受影响)。

另外,JWT 的languageworkspace_idemailjob_id等声明会被记录进日志,便于审计每次会话由谁在哪个工作区发起。

容器化部署

仓库提供了 debugger/Dockerfile:它从 windmill 官方 EE 镜像拷贝nsjailwindmill二进制,基于oven/bun:1.4.0构建运行环境,安装 Python 3、websocketsdebugpy依赖,创建 UID/GID 1000 的非 root 用户windmill,暴露端口 5679,并以bun run dap_debug_service.ts --windmill /usr/local/bin/windmill作为入口(自动开启依赖安装),还内置了/health的 Docker HEALTHCHECK。构建与运行示例:

docker build -t windmill-debugger . docker run -p 5679:5679 windmill-debugger # 启用 nsjail 沙箱需要 --privileged docker run -p 5679:5679 --privileged windmill-debugger --nsjail

依赖自动安装:prepare-deps 与注册表配置

为什么在服务侧安装依赖

调试一个脚本前,需要先安装其 import 的依赖。README 明确说明:安装通过windmill prepare-deps完成,该命令无需数据库连接,Python 走uv、TypeScript 走bun install。关键在于安装必须发生在服务进程中、而不是 Python/Bun 会话进程里——因为注册表配置通常内嵌凭据(如npm_config_registry里的:_authToken=),而调试服务器会在自身进程内执行被提交的脚本,脚本能读到进程里的一切。因此 Python 服务器只被交给安装产出的 venv(通过--venv-path),Bun 会话只拿到安装产出的node_modules,凭据永远不会进入会执行用户代码的进程。

这一设计在 dap_debug_service.ts 的prepareDependencies()中有完整实现:服务 spawnwindmill prepare-deps,通过 stdin 传入{ code, language, python_path, registry }JSON,读取 stdout 最后一行 JSON 解析出venv_path/node_modules_path,再在启动调试子进程时注入。值得注意的细节:

  • 安装受DAP_PREPARE_DEPS_TIMEOUT_MS限制(默认 120000ms),超时后会话照常启动,只是没有依赖;
  • 安装失败时 CLI 返回success: false,stderr 同时出现在errorinstall_stderr字段;服务会将其作为output事件报告给客户端——于是"镜像不可达、证书不受信、包不存在"这类真实原因会直接呈现在用户眼前,而不是一个干巴巴的ModuleNotFoundError
  • 安装以detached: true独立进程组运行,配合killProcessTree()(见下文)才能确保超时或断开时把uv/bun及其子孙进程一并 SIGKILL;
  • Python 的 venv 必须用实际运行脚本的解释器构建(python_path传入 CLI),否则site-packages不会进入该解释器的sys.path,编译型扩展会静默无法导入。

注册表配置的获取与作用域

因为prepare-deps没有数据库,服务从实例设置读取注册表配置:向${WINDMILL_BASE_URL}/api/debug/registry_config发起GET,用正在启动的会话的 launch token做 Bearer 授权(见 registry_config.ts)。拉取带 10 秒超时(DAP_REGISTRY_CONFIG_TIMEOUT_MS),失败时绝不阻塞启动——会话回落到公共注册表安装,同时通过message字段把原因告诉用户,而不是留下一句莫名其妙的"包找不到"。

授权粒度同样严格:401/403/404属预期应答,会话直接回落公共注册表。令牌只服务于该会话自己的安装器,因此一个 TypeScript 会话的令牌无法用来读取 Python 索引凭据。令牌也到达了浏览器,所以它能拉到的就是工作区成员能拉到的;操作员(operator)发起的会话会被直接拒绝(操作员本来也无法运行预览任务)。对能运行预览的成员而言,npm 设置其实已被预览暴露(worker 会把同样的.npmrc/bunfig.toml留在预览脚本的运行目录),而 Python 索引 URL 原本只出现在uv的 argv 里,现在可读了——这是一处有意的、被明确记录的行为边界。

注册表设置项如下(README 原表):

设置项作用对象
npm_config_registrybun install的注册表及其:_authToken=
npmrc原样写入.npmrc,优先级高于npm_config_registry
bunfig_install_scopes生成bunfig.toml中的[install.scopes]
pip_index_urluv --index-url
pip_extra_index_urluv --extra-index-url(逗号分隔)

限制与前提:

  • 这些设置仅 Enterprise 版本提供(与任务作业一致);CE 实例会在会话输出中报告这一点,而不是应用设置;
  • uv_index_strategy与 worker 一样,任何版本都会下发;
  • EPHEMERAL_TOKEN占位符的索引 URL不会下发——只有 worker 能运行执行替换的命令;
  • 携带凭据的文件(.npmrcbunfig.toml)写入/var/tmp/windmill-debug-registry,而非安装目录,并在安装结束时删除。原因有二:会话会把node_modules的符号链接解析回该目录,且 nsjail.debug.config.proto 把整个/tmpbind-mount 进每个会话——留在那里会被并发会话读到。而/var/tmp在该配置里是每 jail 一份的 tmpfs:会话看到的是空的,被杀死(SIGKILL)的 jail 中的凭据也随 jail 一起消失。未 jail 的安装则写宿主机/var/tmp,残留目录由下一次安装清理;未 jail 的会话本来就无约束,能看到整个文件系统。

环境变量形式的回退与扩展

注册表配置的其余部分没有实例设置,直接从调试服务进程的环境变量读取(README 原表;两个名字同时列出时前者优先,worker 读取的是同样的名字):

变量描述默认值
PY_TRUSTED_HOST/PIP_TRUSTED_HOST受信主机,空白分隔(--trusted-host-
PY_INDEX_CERT/PIP_INDEX_CERT索引 CA 包,作为SSL_CERT_FILE传给 uv。回退顺序SSL_CERT_FILEREQUESTS_CA_BUNDLECURL_CA_BUNDLE注意它"替换"uv 自带的信任根而非追加,只含私有 CA 的包会让所有公共索引不受信;bun install拿到同样的包作为NODE_EXTRA_CA_CERTS(Bun 唯一认的拼写)-
SSL_CERT_DIR证书目录,原样传给 uv,同样替换信任根-
PY_NATIVE_CERT/UV_NATIVE_TLStrue时同时信任平台证书库(--native-tlsfalse
UV_HTTP_TIMEOUTuv HTTP 请求超时(秒)uv 自身默认
DAP_REGISTRY_CONFIG_TIMEOUT_MS设置拉取等待时间10000

此外,PY_INDEX_URL/PIP_INDEX_URLPY_EXTRA_INDEX_URL/PIP_EXTRA_INDEX_URLUV_INDEX_STRATEGY仍然从同一环境读取,当拉取不到索引时生效(实例未设置 / CE 实例 / 会话无权限)。这是实例级的决定(为所有会话从该索引装 Python 依赖),与谁打开了会话无关;不设置它们,让实例设置单独决定即可。npm 设置没有这样的回退——实例设置是唯一来源。

会话环境:代理、信任根与 CA 注册

会话环境白名单

调试会话的环境由白名单构建而非全量继承,这与 worker 给任务脚本的环境保持一致(见 env_passthrough.ts)。代理变量(HTTP_PROXY/HTTPS_PROXY/NO_PROXY及小写拼写)从服务转发进每个会话——被调试脚本自己发起的出站调用需要它们,和任务脚本在 worker 上一样。当设置了代理但没有旁路列表时,NO_PROXY默认补上localhost,127.0.0.1,保证对BASE_INTERNAL_URL的调用不被代理。

信任根也一并转发:SSL_CERT_FILESSL_CERT_DIRREQUESTS_CA_BUNDLECURL_CA_BUNDLENODE_EXTRA_CA_CERTS。在 TLS 拦截代理后面,这些正是让被调试脚本自身的 HTTPS 调用通过校验的关键——只把 CA 装进容器系统库不够,因为requests自带证书包,Node 只读NODE_EXTRA_CA_CERTS注册表设置则刻意不转发:它们带凭据,只有服务需要。

系统 CA 注册

把 CA 注册进容器系统库有独立机制:把它挂载到/usr/local/share/ca-certificates/且命名为*.crtupdate-ca-certificates只认这个扩展名),windmill_extra会在启动任何服务前运行它。另有:

  • RUN_UPDATE_CA_CERTIFICATE_AT_START=true强制同样行为(无论是否挂载了证书);
  • RUN_UPDATE_CA_CERTIFICATE_PATH覆盖该工具路径(与服务端、worker 一致);
  • 两者都是尽力而为:无法写/etc/ssl/certs的 UID 只记警告,容器照常启动;
  • 更复杂的需求用INIT_SCRIPT挂钩,它失败时会中止启动(与 CA 更新不同)。

README 特别提醒系统库覆盖不到的范围——而这恰是调试会话安装的大部分东西:uv 信任自带根(除非PY_NATIVE_CERT/UV_NATIVE_TLS为 true),Bun 与 Node 只读NODE_EXTRA_CA_CERTSrequests自带 certifi。注册 CA 只修好 Python 标准库sslcurlgit,其余仍需要上面的变量。

沙箱边界与进程隔离

把设置排除在会话环境之外,只是限制了被调试脚本能读到什么。未沙箱的会话与服务同用户运行,仍可通过/proc读服务环境——就像未沙箱 worker 上任务能读 worker 一样。真正把会话与服务隔开的是--nsjail --nsjail-config nsjail.debug.config.proto:起作用的是配置里的PID 命名空间和mount_proc,而不只是那个 flag。

从 nsjail.debug.config.proto 可以看到关键设置:

  • mode: ONCEmount_proc: trueclone_newuser: true(用户命名空间)、keep_env: true
  • /bin/lib/lib64/usr/etc/sys/fs以只读 bind-mount 挂入;/debugger挂入调试器脚本目录;
  • /tmp从宿主机 bind-mount(读写)——任务目录创建于此;
  • /var/tmp每 jail 一份的 tmpfs(私有暂存,前面凭据清理逻辑的前提);
  • /root是 tmpfs(供 bun 缓存等使用);/dev/null/dev/random/dev/urandom单独挂载;
  • envar设定HOME=/rootTMPDIR=/tmp与 PATH。

安装器与调试目标以同样的条件被 jail:uv pip install会构建源码发行版、bun install会跑 postinstall 脚本,包自身代码同样会在 jail 内执行。配置的keep_env让安装器跨沙箱边界保留服务环境(注册表与 CA 变量正是这样到达它的)——若改为白名单,就必须显式携带这些变量。安装器还运行在独立进程组中,因为uv/bun是孙进程:只给子进程发信号会把它们重新挂到 init 下继续下载,让超时与断开即取消变成半吊子措施。这正是 dap_websocket_server_bun.ts 中killProcessTree()存在的意义——它从/proc/<pid>/stat读回组 ID 确认自己拥有该进程组后,才对整个组发SIGKILL,避免误杀容器内其他服务。

前端集成

前端通过 Svelte 组件与调试服务交互。README 给出的集成示例:

<script> import { MonacoDebugger } from './debug' let editor // Monaco editor instance let code = 'print("Hello")' </script> <MonacoDebugger {editor} {code} language="python3" />

配套的还有DebugToolbar.svelte(步进、继续等控制按钮)、DebugPanel.svelte(变量与堆栈显示面板)、dapClient.ts(带 Svelte store 的客户端 DAP WebSocket 客户端),以及index.ts(模块导出)。读者可以在 frontend/src 中检索MonacoDebugger相关的编辑器集成实现。

测试

仓库为三层组件各提供了测试脚本(README "Testing" 一节,运行方式略有出入:测试脚本与调试服务同目录):

# 先启动服务 bun run debug/dap_debug_service.ts # 测试 Python 调试器 bun run debug/test_dap_server.py # 测试 Bun 调试器 bun run debug/test_dap_server_bun.ts # 测试统一服务 bun run debug/test_debug_service.ts

test_debug_service.ts 的头部注释说明了更多测试模式:服务加--nsjail --nsjail-config后,测试脚本加--nsjail即可测沙箱路径;服务加--windmill /path/to/windmill后,测试脚本加--test-autoinstall即可测依赖自动安装。测试代码覆盖了 TS/Python 的main()调用与结果捕获、环境变量(WM_WORKSPACE/WM_TOKEN)透传、对象 console 输出、变量面板中的对象展示等场景。

调试器实现要点:两套后端如何落地

Python:bdb 驱动的调试线程

dap_websocket_server.py 用asyncio承载 WebSocket 服务,核心是继承bdb.BdbWindmillDebugger类:

  • 非步进模式下默认不停:重写stop_here(),只有_step_mode非空时才在每行停下,断点则由break_here()判定;
  • 单步模型do_step_overset_next(frame)do_step_inset_step()do_step_outset_return(frame),配合_wait_for_continue事件实现"暂停—等待用户指令—继续";
  • 堆栈与变量get_stack_frames()沿f_back回溯直到<module>(避免把调试器/线程内部栈帧暴露给用户);get_locals()/get_globals()从帧对象取变量字典;
  • main() 调用launch时若callMain为真,把代码拼上自动生成的main(**args)调用与__WINDMILL_RESULT__结果输出——脚本结果正是通过截获这个特殊前缀的输出事件回传客户端,而不是转发给用户界面;
  • 脚本执行在独立线程:阻塞的debugger.run()放到threading.Thread,事件循环线程负责 WebSocket;脚本的stdout/stderr被包装成流式输出对象,逐行以output事件实时推给客户端;
  • 依赖准备有进度提示:安装放在asyncio.to_thread中执行,每 5 秒发一次 "Still preparing dependencies...",避免冷缓存下的安装让界面看起来像死机(120 秒超时)。

统一服务(dap_debug_service.ts 的PythonDebugSession)则扮演了网关角色:它用debugpy作为实际 DAP 后端(服务内随机选取 10000–60000 端口,spawndap_websocket_server.py后轮询连接就绪,再以 WebSocket 直连),把initialize/setBreakpoints/launch/continue/next/stepIn/stepOut/evaluate等命令转发给 debugpy,并把stopped/continued/terminated/output事件翻译回客户端。依赖安装(prepareDependencies)在服务侧完成,--venv-path只把 venv 交给 Python 服务器。launch命令的 DAP 超时被放宽到 180 秒,以容纳最长 120 秒的依赖安装。

TypeScript/Bun:WebKit Inspector 协议桥

dap_websocket_server_bun.ts 是 DAP 与 Bun 调试能力之间的桥。注释明确列出了它与 V8/Node 调试的关键差异:

  • 使用WebKit Inspector 协议(类似 Safari DevTools),需要Inspector.enableDebugger.setPauseOnDebuggerStatementsInspector.initialized
  • 断点必须用Debugger.setBreakpointsActive激活;
  • 控制台输出走Console.messageAdded而非Runtime.consoleAPICalled(两种事件处理器都实现了)。

启动流程值得展开:

  1. 在 launch 参数中收到代码后,先做prepareDependencies(同样经fetchRegistryConfig取注册表设置,bun install安装,产出node_modules路径);
  2. removePinnedImports()剥离 import 里的版本号(如lodash@4lodash,与后端remove_pinned_imports行为一致)——注意必须在 prepare-deps 之后、执行之前做,因为安装需要版本号而 Bun 不认@version语法;
  3. 在代码头部注入debugger;语句制造初始暂停点,保证脚本解析完成后能先设置断点再继续;
  4. callMain模式下通过parseMainFunctionParams()解析main函数签名(支持export async function mainexport const main = async (...) =>等写法),按参数名顺序生成调用参数(处理了 JSON 序列化剥离undefined的场景);
  5. 把代码写入临时目录script.ts,若有node_modules符号链接进临时目录(失败则回退NODE_PATH);
  6. bun --inspect-wait=<随机 9229 起的端口>启动,从 stderr 用正则ws://[\d.]+:\d+\/\S+(?=\s)解析出 inspector WebSocket URL(只在空白字符后确认 URL 完整才连接,防止截断路径 404),连接后依次Inspector.enableConsole.enableDebugger.enableRuntime.enableDebugger.setBreakpointsActiveDebugger.setPauseOnDebuggerStatementsDebugger.setPauseOnExceptions(uncaught)→ 设置断点 →Inspector.initialized(没有它,--inspect-wait会一直等下去)才真正开始执行。

断点与源码映射是 Bun 调试的核心难点:Bun 会转译 TypeScript,可能删掉空行与注释,行号会偏移。parseSourceMapLineMapping()解析 source map 的 VLQ 编码,构建双向行映射:

  • 原行 → 转译行(设置断点):每个原行只存第一次出现,确保断点落在最早包含该代码的转译行;
  • 转译行 → 原行(报告位置):总是存最高的原行——Bun 会把多行函数签名压缩到一行,停在转译行 3 时用户应当看到第 7 行(实际的 return 语句)而不是第 6 行(更早的 console.log)。

初始暂停(第 0 行注入的debugger)被识别后自动恢复,随后重新按 URL 设置断点,再真正执行用户代码。堆栈帧只保留来自用户脚本的帧(过滤 Bun/loader 内部帧),变量面板通过Runtime.getProperties拉取属性并做了大量过滤(跳过__内部属性、native 函数、内建对象名、PascalCase 类名),用对象的preview数据展示对象/数组内容而不是干巴巴的 "Object"。evaluate支持可选的表达式令牌校验(用于审计日志,不阻断执行),并能从 Promise 的 preview 里提取已 settled 的结果(Runtime.awaitPromise在 Bun inspector 里不可靠)。步进期间的控制台输出会被缓冲,待stopped事件之后再冲刷,保证输出顺序正确。__WINDMILL_RESULT__前缀的结果同样在服务端截获并立即发送terminated事件。

总结

Windmill 的调试模块是一个"小而完整"的 DAP 实现:统一服务 + 双语言后端、JWT 签名令牌鉴权、服务侧依赖安装与注册表凭据隔离、nsjail 沙箱化执行、代理与 CA 环境白名单透传、源码映射行号矫正——每一环都有明确的边界与取舍。无论是想为自托管 Windmill 启用脚本调试,还是希望在自己的产品里复刻这套"浏览器内调试"架构,debugger 目录下的源码、nsjail.debug.config.proto 沙箱配置与三个测试脚本都是可以直接研读与复用的参考资料。

【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询