)
GitHub Copilot SDK for Java 原生运行时打包策略解析per-platform classifier JAR 分发架构ADR-007【免费下载链接】copilot-sdkMulti-platform SDK for integrating GitHub Copilot Agent into apps and services项目地址: https://gitcode.com/GitHub_Trending/co/copilot-sdk本篇技术指南围绕github/copilot-sdk仓库中 Java SDK 的架构决策记录 ADR-007Native runtime bundling strategy 展开系统讲解 Copilot SDK for Java 如何在进程内in-process加载 Copilot Agent 的原生运行时runtime.node以及为什么选择 Maven per-platform classifier JAR 作为跨平台分发的核心方案。读完本文你将掌握运行时 C ABI 的五入口设计、8 个平台分类器的构建矩阵、运行时解析顺序、JNA vs FFM 的绑定选型依据以及消费者如何通过 Maven/Gradle 与 uber-JAR 组装插件正确引入对应平台的 native 依赖。背景与问题陈述为什么需要打包原生运行时Copilot SDK for Javajava/sdk模块pom.xml支持一种实验性的 in-process 连接方式把 Copilot Agent 运行时作为原生共享库直接加载进 Java 进程通过 FFI 双向通信。而 stdio、TCP、URI 三种子进程连接依然是默认行为只有用户显式选择 in-process 连接时才启用。于是产生了一个架构问题这个动辄几十 MB 的原生运行时二进制应该以什么形式发布、打包、解析、加载ADR-007 就是对这个问题的完整决策记录。从仓库源码看Java SDK 的连接抽象集中在 RuntimeConnection其中RuntimeConnection.forInProcess()返回 in-process 连接对象RuntimeConnection.java并与StdioRuntimeConnection、TcpRuntimeConnection、UriRuntimeConnection并列构成 CopilotClient 的传输层选项。被嵌入的运行时构件runtime.node要被嵌入的构件是runtime.node—— 由github/copilot-agent-runtime仓库的src/runtimecrate 基于 napi-rs 工具链构建的 Rustcdylib。尽管扩展名是.nodenapi-rs 的命名惯例它实际上就是普通的平台相关共享库Linux 上是.somacOS 上是.dylibWindows 上是.dll。它对外暴露两个前门共用同一个内部引擎napi 前门由 Node.js 进程作为原生 addon 加载服务于当前 CLI 路径C ABI 前门一组固定的 5 个extern C生命周期与传输入口任何语言都可以通过 FFIJava 用 JNA、Python 用 cffi、C# 用DllImport、Go 用 purego在进程内调用。所有 API 方法都以 JSON-RPC 数据的形式走这个固定传输通道导出列表不随方法集增长而变化。五个入口点的完整 C 签名如下入口点C 签名用途copilot_runtime_host_start(const uint8_t* argv_json, size_t argv_json_len, const uint8_t* env_json, size_t env_json_len) → uint32_t启动运行时宿主argv_json是 JSON 数组如[copilot,--embedded-host]env_json是可选的 JSON 对象环境覆盖。返回 server 句柄0 表示失败。copilot_runtime_host_shutdown(uint32_t server_id) → bool关闭server_id标识的运行时宿主。copilot_runtime_connection_open(uint32_t server_id, void(*on_outbound)(void* user_data, const uint8_t* data, size_t len), void* user_data, const uint8_t* ext_source, size_t ext_source_len, const uint8_t* ext_name, size_t ext_name_len, const uint8_t* conn_token, size_t conn_token_len) → uint32_t在 server 上打开双向连接注册on_outbound回调用于运行时→SDK 的数据投递。ext_source、ext_name、conn_token是可空元数据缓冲区。返回 connection 句柄0 表示失败。copilot_runtime_connection_write(uint32_t connection_id, const uint8_t* data, size_t len) → bool从 SDK 向运行时写入 JSON-RPC 帧。native 侧在返回前同步拷贝缓冲区。copilot_runtime_connection_close(uint32_t connection_id) → bool关闭连接。出站回调签名为void on_outbound(void* user_data, const uint8_t* data, size_t len)—— 由 native 代码可能在 native 线程上调用把 JSON-RPC 响应和通知回传给 SDK。这套 C ABI 在 Java 侧的映射可以在源码中找到直接对应内部接口 NativeBinding 定义了hostStart、hostShutdown、connectionOpen、connectionWrite、connectionClose五个方法其 Javadoc 明确说明所有帧使用与 stdio 传输一致的 LSPContent-Length头帧格式FFI 边界无需特殊编解码JNA 实现 JnaNativeBinding 则通过内部接口CopilotRuntimeLibrary extends Library将这五个copilot_runtime_*导出逐一对齐。需要注意cli-native.node—— 提供 ICU4X 文本分段、Win32 API 包装和终端 UI 辅助的独立小型 addon —— 是 CLI 专用构件Ink/React 终端界面使用Java SDK 不需要它。活跃的 Rust 迁移期说明截至 2026-08runtime.node二进制正处于把 TypeScript 运行时代码逐步移植为 Rust 的过程中它是在增长而非缩减。embedded_host.rs模块目前通过启动子进程copilot --embedded-host来服务尚未移植到 Rust 的方法体。因此迁移期的 classifier JAR 包含一组版本匹配的搭档runtime.node通过 JNA 加载进 Java 进程copilot或copilot.exe由copilot_runtime_host_start内部启动platform.propertiesclassifier 与运行时版本元数据。Java SDK 不会独立为 JSON-RPC 传输 spawn 这个子进程过渡期的 embedded-host 进程由 native 运行时自己持有。Rust 迁移完成后打包 CLI 的需求消失而 C ABI 与 Java 加载机制保持稳定。平台维度8 个 Rust target triple运行时必须针对 OS × CPU 架构 ×Linux 上的C 运行时变体的每一种组合构建。github/copilot-agent-runtime的构建系统产出 8 个 Rust target triple平台标签Rust triple约束linux-x64x86_64-unknown-linux-gnuglibc ≥ 2.28Debian 10、Ubuntu 20.04、RHEL 8linux-arm64aarch64-unknown-linux-gnuglibc ≥ 2.28linuxmusl-x64x86_64-unknown-linux-musl动态链接 musl libcAlpine Linuxlinuxmusl-arm64aarch64-unknown-linux-musl动态链接 musl libcdarwin-x64x86_64-apple-darwinmacOSInteldarwin-arm64aarch64-apple-darwinmacOSApple Siliconwin32-x64x86_64-pc-windows-msvcMSVC CRT 静态链接crt-staticwin32-arm64aarch64-pc-windows-msvcMSVC CRT 静态链接crt-static要点补充GNU/Linux 的 glibc 2.28 下限在构建时通过 Microsoft/vscode-linux-build-agent sysroot 强制并在构建后由script/linux/verify-glibc-requirements.sh验证musl 二进制不是完全静态链接构建时显式设置-C target-feature-crt-static动态链接 musl libc常见情况Windows × 2 macOS × 2 GNU/Linux × 2需要6 个二进制支持 Alpine Linux 需额外 2 个 musl 二进制共8 个。这 8 个 classifier 与 Java 侧 PlatformDetector 中的SUPPORTED_CLASSIFIERS常量集合linux-x64、linux-arm64、linuxmusl-x64、linuxmusl-arm64、darwin-x64、darwin-arm64、win32-x64、win32-arm64一一对应CLASSIFIER_BY_KEY映射表PlatformDetector.java则把(os, arch, libc)三元组映射到最终 classifier 字符串。平台选择纯 Java/OS API 的运行时探测加载器运行时使用标准 Java 与 OS API 选择 classifier三步走OSSystem.getProperty(os.name)区分 Windows、macOS、Linux架构System.getProperty(os.arch)把amd64、x86_64、x64归一到x64把aarch64、arm64归一到arm64Linux libc 变体读取/proc/self/exe的前 2 KB解析 ELFPT_INTERP段。解释器路径包含/ld-musl-判定为 musl包含/ld-linux-判定为 glibc。如果 Linux 可执行文件无法读取或解释器无法识别实现回退到检测架构对应的 GNU/Linux classifier不支持的 OS 和架构直接抛IllegalStateException。这段逻辑在 PlatformDetector.detectLinuxLibc(Path) 中落地readPrefix读取前 2048 字节readElfPtInterp手工解析 ELF 头、program header 表并定位PT_INTERP段中的解释器路径字符串。之所以在运行时探测而不是执行子进程是因为解析 ELF 元数据不依赖 fork/exec更可靠也更快ADR 参考表将其列为最可靠的在运行时区分 glibc/musl 且不启动子进程的方式。体积基线以下数据来自github/copilot-agent-runtime的 releasecli-1.0.69-22026-07-06平台runtime.node未压缩压缩后约 40% deflatelinux-x6464.7 MB~25.9 MBlinux-arm6455.5 MB~22.2 MBlinuxmusl-x6464.4 MB~25.8 MBlinuxmusl-arm6455.3 MB~22.1 MBdarwin-x6457.3 MB~22.9 MBdarwin-arm6448.1 MB~19.2 MBwin32-x6455.9 MB~22.4 MBwin32-arm6448.4 MB~19.4 MB对比参照当前已发布的 Java SDK JARcopilot-sdk-java-1.0.6-preview.1.jar只有1.53 MB。未来仅含 6 个常见平台 native 二进制的 runtime-only 单体 JAR 压缩后约为132 MB含全部 8 个含 musl约180 MB。需要强调的是这些 runtime-only 估算并不描述当前迁移期构件当前开发版linux-x64classifier JAR 还包含版本匹配的 CLI 可执行文件压缩后约152 MB其暂存内容在 JAR 压缩前约133 MBruntime.node170 MBcopilot。运行时内的所有 native 依赖TLS 用的rustls/aws-lc-rs、SQLite 用的rusqlitebundledfeature、压缩用的zlib-rs都已静态编译进二进制不依赖系统 OpenSSL、libgit2 或 libz。候选方案评估三种分发形态的取舍Option 1包含全部平台二进制的单体 JAR把 6 个或 8 个平台构件集全部塞进一个单体 artifact运行时 SDK 提取并加载匹配当前平台的那一个其余 5–7 个被默默携带。优点pom.xml中只有一个dependency用户零额外配置模式成熟ONNX Runtimeonnxruntime-1.21.0.jar130 MB全平台证明这在 Java ML 生态是被接受的惯例。缺点每个用户都要下载所有平台无论其目标平台是什么。Apple Silicon 上的开发者要下载 105 MB 永远不会用到的 Linux/Windows 二进制构建工具薄 Docker 层、增量 CI 缓存、制品仓库对大 JAR 惩罚明显任何一个平台的二进制变更都会使整个 132–180 MB JAR 的缓存全部失效Maven 依赖解析没有机制自动提供平台合适的变体平台选择必须完全发生在 JAR 内的运行时与Maven 构件应当可复现且最小化的原则冲突。Option 2per-platform classifier JAR最终选型发布一个小的纯 Java 协调构件copilot-sdk-java约 1.5 MB旁边按 Maven classifier 区分发布各平台 native 构件com.github:copilot-sdk-java-runtime:VERSION:linux-x64 com.github:copilot-sdk-java-runtime:VERSION:linux-arm64 com.github:copilot-sdk-java-runtime:VERSION:linuxmusl-x64 com.github:copilot-sdk-java-runtime:VERSION:linuxmusl-arm64 com.github:copilot-sdk-java-runtime:VERSION:darwin-x64 com.github:copilot-sdk-java-runtime:VERSION:darwin-arm64 com.github:copilot-sdk-java-runtime:VERSION:win32-x64 com.github:copilot-sdk-java-runtime:VERSION:win32-arm64每个 classifier JAR 包含runtime.node、platform.properties以及在活跃 Rust 迁移期内的版本匹配copilot/copilot.exeembedded-host 可执行文件。协调构件在用户选择 in-process 连接时选择并加载匹配的 native。这与 DJL 的 PyTorch native 构件pytorch-native-cpu-2.5.1-linux-x86_64.jar、pytorch-native-cpu-2.5.1-osx-aarch64.jar等、Netty 的netty-tcnative-boringssl-staticper-platform JAR 等是同一模式。构建工具可以配置为自动解析正确的 classifierMaven通过 os-maven-plugin 使用classifier${os.detected.classifier}/classifierGradle基于 attribute matching 的 variant-aware 依赖解析Uber-jar 构建包含所有 classifier由协调构件在运行时挑选正确的那一个。优点长期 runtime-only 下载量 协调构件 一个平台 JAR而不是所有平台的二进制每个平台 JAR 独立变更未变更平台的 CI 缓存和 Docker 层跨 release 保留面向单一已知平台的用户绝大多数生产部署只付出该平台的成本遵循成熟的 Maven 生态惯例标准工具链os-maven-plugin、Gradle variant 解析可处理 classifier 选择与 DJL 分发大型 native ML 运行时的成熟策略一致。缺点每个 release 需要多发布 6–8 个 Maven 构件构建可移植 uber-JAR 的用户必须显式包含想支持的所有 classifier需要跨平台打包的用户pom.xml/build.gradle稍复杂。Option 3按需下载SDK 携带一个最小占位构件运行时检测当前平台首次使用时从分发端点GitHub Releases 或 CDN下载正确的runtime.node并本地缓存如~/.copilot/runtime-cache/。优点任何已发布的 Maven 构件都不含 native 二进制内容mvn install的总下载量可忽略下载期间与当前外部提供运行时模型的用户体验一致多数 CLI 用户已接受。缺点首次运行需要联网。离线环境隔离内网企业、无出站 HTTP 的 CI会静默失败或需要手动预置在纯库构件中引入网络依赖违反 Maven Central 对可复现构建的期望增加运维关注点分发端点可用性、CDN 成本、跨版本 URL 稳定性使 JVM 启动延迟不确定首次运行下载 20–26 MB依赖管理工具无法预热不存在可类比mvn dependency:resolve的运行时下载机制。决策结果Option 2 为主Option 1 可由消费者自行组装最终选择Option 2per-platform classifier JAR同时通过消费者自建单体 JAR 保留 Option 1 的可能。消费者可以使用maven-assembly-plugin合并自己需要的平台 classifier。决策理由原文 7 条用户下载成本与实际需求匹配多数用户只跑一个 OS 架构Option 2 避免下载全部平台构件。迁移期内每个 classifier 还携带 embedded-host 可执行文件因此比 runtime-only 目标更大已被验证的生态模式DJL、Netty 等已确立 per-classifier 是 Maven 处理大型 native 二进制的正确惯用法构建工具链天然支持Spring Boot、Quarkus、Micronaut 等框架集成的用户对此熟悉缓存效率单个平台 JAR 只在该平台二进制变更时才变化未变更的平台 JAR 永远不会被 CI 或开发者机器重新下载/重新缓存无运维依赖与 Option 3 不同运行时不需要外部下载服务。一旦被 Maven/Gradle 解析构件即自包含分发模型在构件体积变化时依然成立当前过渡期 classifier 因同时包含 runtime 与 CLI 而很大但 classifier 模型仍能阻止用户下载无关平台构件且当 embedded-host 可执行文件不再需要时体积会下降Option 3 仍可组合按需下载回退可以叠加在 Option 2 之上而不改变主分发模型——协调构件可以先尝试 classpath 查找若无匹配的 classifier JAR 再回退到缓存下载详见下文如何同时支持 classifier 与单体 JAR。传输选择与失败行为严格语义添加 classifier JAR不会自动改变客户端的连接方式用户必须显式选择 in-processCopilotClientOptions options new CopilotClientOptions() .setConnection(RuntimeConnection.forInProcess());COPILOT_SDK_DEFAULT_CONNECTIONinprocess环境变量也能在没有显式或遗留子进程选项覆盖时选择 in-process 连接。从 CopilotClient.resolveDefaultConnection 的实现看环境变量接受inprocess或stdio不区分大小写其他值抛IllegalArgumentException且显式的子进程选项cliUrl/cliPath/port 等优先于环境变量默认值。选定的连接是严格的用户选择 in-process 且 native 解析或启动失败 →CopilotClient.start()直接失败SDK不会静默降级重试 stdio 或 TCP用户未选择 in-process → classifier JAR 被忽略既有 stdio、TCP、URI 行为完全不变。这与 CopilotClient 中对InProcessRuntimeConnection的分支处理一致同时validateEnvironmentOptionsCopilotClient.java会拒绝那些与 in-process 运行时共享宿主的选项组合。运行时解析顺序选择 in-process 连接后Java 加载器按以下顺序解析runtime.nodeCOPILOT_CLI_PATH接受扁平兄弟文件runtime.node或 npmprebuilds/classifier/runtime.node布局Classpath 资源从 classifier 或单体 JAR 中把native/classifier/runtime.node与捆绑 CLI 提取到以 SDK 版本、native 包版本和 classifier 为键的缓存PATH 兼容回退在PATH上找到copilot并接受扁平兄弟文件runtime.node。三者都失败则启动失败。PATH 回退不承诺支持所有 npm 或 Homebrew 安装布局。这段顺序在 NativeRuntimeLoader.resolve() 中有完整实现resolveFromCliPathNativeRuntimeLoader.java先检查扁平布局再检查prebuilds/classifier布局classpath 路径通过extractRuntimeToCache完成定位资源 → 建目录 → 写同目录唯一临时文件 →FileChannel.force(true)刷盘 → 原子 move 发布的完整序列并发场景下DEFAULT_PUBLISHERNativeRuntimeLoader.java对 Windows 的FileAlreadyExistsException/AccessDeniedException做了竞态兜底PATH 回退由 findRuntimeOnPath 负责Windows 上还识别copilot.exe/copilot.cmd/copilot.bat。当前平台范围与发布工作流平台探测器识别 ADR 中列出的 8 个 classifier。Maven 构建只为主机匹配已实现 classifier 的情况绑定 native 打包Linux x64 与 ARM64 glibc 主机可通过copilot.native.libcglibc选择加入Windows x64、Windows ARM64、Apple Silicon macOS 主机自动打包win32-x64、win32-arm64、darwin-arm64inprocess测试 profile 在五种主机上自动选择匹配的已实现 classifier每个路径在下载或打包 native 文件之前都先验证主机Linux musl 及其他不支持的主机只构建 OS 中立的占位 JAR、sources 与 Javadoc 构件除非显式请求 in-process 测试会在主机验证阶段失败额外的 classifier 构件留作后续工作。在 java/copilot-native/pom.xml 中可以看到这套机制的完整落地native-linux-x64激活条件os.nameLinuxos.archamd64 属性copilot.native.libcglibc、native-linux-arm64、native-win32-x64、native-win32-arm64、native-darwin-arm64五个 host profile 绑定validate-native-hostvalidate 阶段、fetch-nativegenerate-resources 阶段、jar-nativepackage 阶段、verify-native-jarspackage 阶段四个执行另有attach-external-*系列 profile 用于让 Ubuntu x64 发布机挂载从 Linux ARM64 / Windows x64 / Windows ARM64 / Apple Silicon macOS 构建机上传递过来的 classifier以及skip-native-download-Dcopilot.native.skip.downloadtrue用于离线只构建占位 JAR。Maven Central release 与 snapshot 工作流从同一不可变源码在各匹配 native 主机上构建各 classifierLinux ARM64、Windows x64、Windows ARM64、macOS 任务各自只上传验证过的 classifier 与校验和清单Ubuntu x64 任务验证并挂载全部四个外部 classifier用 glibc opt-in 构建linux-x64并执行唯一一次Maven 部署。因此 release 签名在一次部署中覆盖中立构件和全部五个 classifier。fetch-native.mjsscripts/fetch-native.mjs实现了下载与校验的细节从 nodejs/package.json 的copilotCliVersion读取固定版本构造github-copilot-version-classifier.tgz资产名下载SHA256SUMS.txt与 tarball用 SHA-256 严格校验校验失败即退出再把 tarball 内prebuilds/classifier目录扁平化为native/classifier/下的 staging 树写出runtime-assets.list清单每行八进制权限\t相对路径与platform.properties最后用基于目录树内容的 sha512 digest 写.versionstamp 实现幂等跳过。validate-native-host.mjsscripts/validate-native-host.mjs则在打包前核对主机 platform/arch/glibc 是否匹配 classifier。绑定技术决策JNA over Panama FFMADR 范围内还有一个次级决策正确加载runtime.node后协调构件如何调用 C ABI 入口。候选有两个JNA 与 FFMForeign Function Memory APIProject Panama 的产物Java 22 通过 JEP 454 转正。最终选择JNA。FFM 被认真考虑但刻意推迟理由如下Java 基线SDK 支持 Java 17而 FFM 直到 Java 22 才转正。基于 JNA 的绑定无论如何都是必需的今天采用 FFM 意味着维护两套并行绑定实现而非替换消费者侧配置负担在 JDK integrity-by-default 方向JEP 472下FFM downcall/upcall 是受限操作。基于 FFM 的 SDK 要求每个消费者显式授权 native 访问——启动器加--enable-native-accessmoduleclasspath 应用用ALL-UNNAMED或 manifest 里的Enable-Native-Access属性。JNA 今天零消费者侧配置。对 SDK 而言这个 flag 会成为下游每个应用的负担和可预见的支持问题源头JNA 最终也会走上同一强制执行轨道因为其内部使用 JNI这只买时间不免疫没有可实现的性能收益FFM 相对 JNA 的主要优势是消除每次调用的反射式 marshalling 开销。这里的 C ABI 面是固定 5 个入口、携带 JSON-RPC 字节JSON 序列化/反序列化成本主导调用路径调用频率受 agent 交互速率限制而非紧循环。JNA 与 FFM 的延迟差异在端到端 SDK 使用中预计不可测量。只有当传输演进为高频或共享内存帧模型时这个权衡才会改变Upcall 生命周期复杂度传输是双向的——运行时从 native 线程把 JSON-RPC 响应和 server 发起的请求送回 Java。JNA 的Callback机制处理外来线程附着有成熟语义FFM upcall stub 需要显式Arena生命周期管理stub 的 arena 在 Rust 侧仍持有函数指针时被关闭会导致 JVM 崩溃。这把 JNA 封装掉的生命周期推理责任转移到绑定层GraalVM native-image 成熟度JNA 在 GraalVM native-image 下的行为成熟可达性元数据完备native-image 对 FFM尤其 upcall的支持更新且随 GraalVM 版本变化。SDK 的合理消费者如 Quarkus/Micronaut 系 CLI 工具会编译成 native 镜像这是不应未经验证就动摇的兼容面FFM 的安全优势在此 ABI 形态上不适用FFM 的MemorySegment边界与生命周期检查在 Java 代码对 native 内存做结构化操作时才有价值。这里的 surface 只是把字符串穿过固定传输几乎没有需要保护的结构性内存工作。保留 FFM 迁移路径FFM 被视为将来可能的绑定技术JEP 472 的终局同样对 JNA 施加强制压力而 5 函数稳定 C ABI 使未来迁移成本很低。为以低成本保留该路径绑定层被抽象在一个小型内部接口native load downcall upcall 注册之后未来可以引入 FFM 实现——例如通过 multi-release JAR 在 Java 22 上选择 FFM——而无需改动传输层或 API 层。这正是 NativeBinding 的职责其 Javadoc 明确写着未来的 FFM 实现可通过 multi-release JAR 机制替换调用方不变JnaNativeBinding 也注明 GraalVM native-image 下 JNA upcall 不受支持、in-process 传输在 native-image 可执行文件中不可用应改用子进程传输决策应在以下二者中先到者到来时重新评估(a) SDK 最低 Java 基线越过 17(b) JDK release 开始默认强制--illegal-native-accessdeny。从 java/sdk/pom.xml 也能看到配套工程细节JNA 版本固定为5.19.1注释明确JNA 升级必须重跑 callback spike且 JNA 依赖声明为optionaltrue/optionalpom.xml——只有选择 in-process 传输的消费者才需要 JNA保持默认子进程连接的消费者不会被传递引入 native 依赖。如何同时支持 classifier 与单体 JARClasspath 资源约定与平台检测每个 classifier JAR 使用众所周知的资源路径每个 per-platform JAR 把构件放在确定性路径下native/darwin-arm64/runtime.node native/darwin-arm64/platform.properties native/darwin-arm64/copilotWindows classifier 使用copilot.exe。在运行时需要过渡期 embedded host 期间CLI 入口点保留在 classifier 中。当maven-assembly-plugin创建 uber-JAR 时它会解包所有依赖并合并。最终 uber-JAR 包含所选平台com/github/copilot/sdk/... (Java classes) native/linux-x64/runtime.node native/linux-x64/copilot native/linux-arm64/runtime.node native/linux-arm64/copilot native/linuxmusl-x64/runtime.node native/linuxmusl-x64/copilot native/linuxmusl-arm64/runtime.node native/linuxmusl-arm64/copilot native/darwin-x64/runtime.node native/darwin-x64/copilot native/darwin-arm64/runtime.node native/darwin-arm64/copilot native/win32-x64/runtime.node native/win32-x64/copilot.exe native/win32-arm64/runtime.node native/win32-arm64/copilot.exe协调构件通过 classloader 在运行时选择NativeRuntimeLoader检测当前 classifier向 classloader 请求native/classifier/runtime.node、native/classifier/platform.properties与native/classifier/copilot。它以platform.properties中的 native 包版本作为缓存身份的一部分把每个可执行构件写入同目录唯一临时文件强制刷盘然后原子发布到~/.copilot/runtime-cache/sdk-version/native-version/classifier/NativeRuntimeLoader.java。非 Windows 平台上加载器在原子发布前把临时 CLI 可执行文件置为可执行并验证其可执行状态已缓存但非空却不可执行的 CLI 会被修复而非当作有效接受extractCliToCache。此外classifier JAR 中的runtime-assets.list清单允许把任意附加运行时资产如 tree-sitter 语法文件等一并原子提取到缓存并对路径逃逸绝对路径、..做了安全校验extractRuntimeAssetsToCache。JNA 从提取后的路径加载一旦提取到已知文件系统路径JNA 直接加载CopilotRuntimeLibrary runtime Native.load(extractedPath.toString(), CopilotRuntimeLibrary.class);同一段代码在两种模式下都工作classloader 资源查找在以下两种情况下行为完全一致native 构件位于 classpath 上独立的 classifier JAR 中构件已被maven-assembly-plugin合并进 uber-JAR。classloader 搜索整个 classpath因此两种消费模型下 Java 加载代码零改动。消费者侧 assembly 插件配置构建可移植 uber-jar 的消费者应配置plugin artifactIdmaven-assembly-plugin/artifactId configuration descriptorRefs descriptorRefjar-with-dependencies/descriptorRef /descriptorRefs /configuration /plugin并把所需 classifier JAR 声明为依赖dependencies dependency groupIdcom.github/groupId artifactIdcopilot-sdk-java/artifactId version${copilot.version}/version /dependency !-- Include the platforms you need -- dependency groupIdcom.github/groupId artifactIdcopilot-sdk-java-runtime/artifactId version${copilot.version}/version classifierlinux-x64/classifier /dependency dependency groupIdcom.github/groupId artifactIdcopilot-sdk-java-runtime/artifactId version${copilot.version}/version classifierdarwin-arm64/classifier /dependency !-- Repeat for each target platform -- /dependenciesMaven 用户若只想跟随当前主机自动选择 classifier可在单平台场景使用 os-maven-plugin 暴露的${os.detected.classifier}填充classifier。为什么这套方案干净利落关注点如何处理无资源路径冲突每个平台有独立子目录native/classifier/只提取一次缓存到~/.copilot/runtime-cache/sdk-version/native-version/classifier/无 uber-JAR 也能工作classloader 在独立 classifier JAR 中也能找到同一资源子集选择消费者只声明需要的 classifier缺失平台在运行时给出明确错误JNA 加载提取后Native.load(path, interface)从绝对文件系统路径加载整体模式跟随 DJL 的LibUtils.loadLibrary()思路检测平台 → 构造资源路径 → 需要时提取 → 从绝对路径加载。后果与影响copilot-sdk-java-runtimeMaven 模块持有 per-platform classifier JAR用户为每个打算运行的平台添加对应 classifier选择 in-process 模式的用户还要添加 JNA协调构件不会对保持默认子进程连接的用户强加 native 依赖对应 java/sdk/pom.xml 的optionaltrue/optional声明协调构件包含平台检测与 native 加载代码按序完成四步① 确定性检测 OS、架构与 Linux libc 变体② 在 classpath 上定位匹配的runtime.node通过 classifier JAR 的getResourceAsStream③ 若有效缓存文件不存在把runtime.node与过渡期 CLI 入口提取到~/.copilot/runtime-cache/④ 通过 JNA 使用 C ABI 入口加载JNA 特定代码被隔离在内部绑定接口之后以保留未来 FFM 迁移路径NativeBinding.java经验证的支持主机 profile 从固定的github/copilot-clirelease 抓取匹配平台 tarball、校验其 release SHA-256并打包版本匹配的运行时文件fetch-native.mjs当前 release 发布linux-x64、linux-arm64、win32-x64、win32-arm64、darwin-arm64五个 classifier计划中的 classifier 集合将扩展到其余检测到的平台新增一个已实现平台需要验证过的主机激活、提供 classifier 与平台 CLI 文件名的 profile、以及共享的主机验证、抓取、脚本测试、打包、验证各执行的 lifecycle 绑定对应 copilot-native/pom.xml 中各 host profile 的固定结构cli-native.node不打包其终端 UI 特性与 Java SDK 的程序化 API surface 无关。参考资源决策正文ADR-007: Native runtime bundling strategy加载器实现NativeRuntimeLoader.java、PlatformDetector.java绑定实现NativeBinding.java、JnaNativeBinding.java、FfiRuntimeHost.java连接选择RuntimeConnection.java、CopilotClient.java构建与发布java/copilot-native/pom.xml、java/sdk/pom.xml、fetch-native.mjs、validate-native-host.mjs相关架构决策ADR-001、ADR-002、ADR-004【免费下载链接】copilot-sdkMulti-platform SDK for integrating GitHub Copilot Agent into apps and services项目地址: https://gitcode.com/GitHub_Trending/co/copilot-sdk创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考