Files
craftlabs-authorization-sdk/docs/superpowers/plans/2026-05-01-bitanswer-1to1-refactor.md
huangping 307a019d48 plan: implement BitAnswer 1:1 refactor — 12 tasks across 4 phases
Phase 1 (infrastructure, no breaking changes):
- Rust: error.rs, session.rs, ffi/bitanswer.rs, ffi/bridge.rs
- Rust: refactor lib.rs to session-based handle management
- Java: @Deprecate AuthProvider, create 6 capability interfaces
- Java: LicenseSession skeleton, NativeBridge 30+ method stubs
- Java: CraftLicense new entry point

Phase 2-4: core API expansion, advanced features, cleanup
2026-05-01 13:57:46 +08:00

46 KiB
Raw Permalink Blame History

BitAnswer 1:1 映射重构 实施计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 将 craftlabs-authorization-sdk 的 SDK 客户端层从 8 方法 AuthProvider 重构为 BitAnswer C API 的 1:1 原语映射,按 6 个能力接口分层,4 个 Phase 渐进交付。

Architecture: Java 侧分为 CraftLicense(入口)+ LicenseSession(实现 6 个能力接口:LicenseLifecycle、FeatureManagement、DataItemStore、LicenseInfoQuery、CheckoutManager、LicenseUtility)。Rust 侧新增 ffi/bitanswer.rsBitAnswer C API FFI 声明)、ffi/bridge.rscraft_* → Bit_* 桥接)、session.rs(句柄管理)、error.rs(错误码映射)。Phase 1 不破坏现有接口。

Tech Stack: Rust (cdylib, libloading), Java 17 (Maven multi-module), JNI, BitAnswer C SDK

Spec reference: docs/superpowers/specs/2026-05-01-bitanswer-1to1-refactor-design.md


Phase 1 — 基础设施(不破坏现有接口)

Task 1: Rust — 创建 error.rs(错误码映射)

Files:

  • Create: native/craft-core/src/error.rs

  • Step 1: 创建 LicenseError 枚举

写入 native/craft-core/src/error.rs

use crate::CraftResult;

/// BitAnswer BIT_STATUS 错误码到结构化错误的精简映射。
/// 完整 200+ 码请参考 bitanswer.h,此处映射文档中明确列出的高频码。
#[derive(Debug, Clone, PartialEq)]
pub enum LicenseError {
    Success,
    NetworkError,
    WrongHandle,
    InvalidParameter,
    ApplicationDataError,
    LicenseExpired,
    LicenseNotFound,
    LicenseDisabled,
    FeatureNotFound(u32),
    FeatureExpired(u32),
    FeatureTypeNotMatch(u32),
    SnInvalid,
    SnNotFound,
    SnDisabled,
    SnRevoked,
    SnExpired,
    CapacityExhausted,
    ServerBusy,
    ServerDown,
    Timeout,
    Unknown(u32),
}

impl LicenseError {
    /// 将 BitAnswer BIT_STATUS (u32) 映射为 LicenseError
    pub fn from_bit_status(status: u32) -> Self {
        match status {
            0x0000 => LicenseError::Success,
            0x0101 => LicenseError::NetworkError,
            0x0102 => LicenseError::WrongHandle,
            0x0103 => LicenseError::InvalidParameter,
            0x0105 => LicenseError::ApplicationDataError,
            0x0701 => LicenseError::LicenseExpired,
            0x0114 => LicenseError::LicenseNotFound,
            0x0705 => LicenseError::LicenseDisabled,
            0x0503 => LicenseError::FeatureNotFound(0),
            0x0509 => LicenseError::FeatureExpired(0),
            0x0504 => LicenseError::FeatureTypeNotMatch(0),
            0x011C => LicenseError::SnInvalid,
            0x0706 => LicenseError::SnNotFound,
            0x0705 => LicenseError::SnDisabled,
            0x0123 => LicenseError::SnRevoked,
            0x0701 => LicenseError::SnExpired,
            0x0712 => LicenseError::CapacityExhausted,
            0x0107 => LicenseError::ServerBusy,
            0x0108 => LicenseError::ServerDown,
            0x0141 => LicenseError::Timeout,
            _ => LicenseError::Unknown(status),
        }
    }

    pub fn message(&self) -> &'static str {
        match self {
            LicenseError::Success => "ok",
            LicenseError::NetworkError => "network error",
            LicenseError::WrongHandle => "wrong handle",
            LicenseError::InvalidParameter => "invalid parameter",
            LicenseError::ApplicationDataError => "application data error",
            LicenseError::LicenseExpired => "license expired",
            LicenseError::LicenseNotFound => "license not found",
            LicenseError::LicenseDisabled => "license disabled",
            LicenseError::FeatureNotFound(_) => "feature not found",
            LicenseError::FeatureExpired(_) => "feature expired",
            LicenseError::FeatureTypeNotMatch(_) => "feature type not match",
            LicenseError::SnInvalid => "sn invalid",
            LicenseError::SnNotFound => "sn not found",
            LicenseError::SnDisabled => "sn disabled",
            LicenseError::SnRevoked => "sn revoked",
            LicenseError::SnExpired => "sn expired",
            LicenseError::CapacityExhausted => "capacity exhausted",
            LicenseError::ServerBusy => "server busy",
            LicenseError::ServerDown => "server down",
            LicenseError::Timeout => "timeout",
            LicenseError::Unknown(_) => "unknown error",
        }
    }

    pub fn is_success(&self) -> bool {
        matches!(self, LicenseError::Success)
    }
}

/// 将 LicenseError 转为 C ABI 兼容的 CraftResult
pub fn to_craft_result(error: LicenseError) -> CraftResult {
    if error.is_success() {
        crate::ok_result()
    } else {
        // 使用堆分配的消息指针(调用方通过 craft_free_license_info 类似方式释放)
        let msg = format!("{}\0", error.message());
        let msg_ptr = msg.as_ptr() as *const std::os::raw::c_char;
        std::mem::forget(msg); // 内存由调用方管理
        CraftResult {
            success: 0,
            message: msg_ptr,
        }
    }
}
  • Step 2: 在 lib.rs 中注册 error 模块

native/craft-core/src/lib.rsmod 声明区域添加:

mod error;
  • Step 3: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml

Expected: 编译成功,无错误。

  • Step 4: 提交
git add native/craft-core/src/error.rs native/craft-core/src/lib.rs
git commit -m "feat(native): add LicenseError enum with BitAnswer error code mapping"

Task 2: Rust — 创建 session.rs(会话管理)

Files:

  • Create: native/craft-core/src/session.rs

  • Step 1: 创建 SessionState 和全局会话表

写入 native/craft-core/src/session.rs

use std::collections::HashMap;
use std::sync::Mutex;
use once_cell::sync::Lazy;

/// 单个会话的运行时状态,映射 Java long (session_id) ↔ BitAnswer BIT_HANDLE
pub struct SessionState {
    /// 存储 initialize 时传入的配置 JSON(用于后续 Bit_Login/UpdateOnline
    pub config_json: String,
    /// Bit_Login / Bit_LoginEx 成功后返回的句柄
    pub bit_handle: Option<usize>,
    /// 产品识别码(来自 AuthConfig.bitanswer.applicationData
    pub application_data: Vec<u8>,
    /// 是否已完成 login
    pub logged_in: bool,
}

/// 全局会话表:session_id (i64) → SessionState
pub static SESSIONS: Lazy<Mutex<HashMap<i64, SessionState>>> =
    Lazy::new(|| Mutex::new(HashMap::new()));

static NEXT_SESSION_ID: Lazy<Mutex<i64>> = Lazy::new(|| Mutex::new(1));

/// 分配一个新的 session_id 并注册 SessionState
pub fn register_session(config_json: String, application_data: Vec<u8>) -> i64 {
    let mut next_id = NEXT_SESSION_ID.lock().unwrap();
    let id = *next_id;
    *next_id += 1;
    let mut sessions = SESSIONS.lock().unwrap();
    sessions.insert(id, SessionState {
        config_json,
        bit_handle: None,
        application_data,
        logged_in: false,
    });
    id
}

/// 获取 session 的可变引用
pub fn with_session<F, R>(session_id: i64, f: F) -> Option<R>
where
    F: FnOnce(&mut SessionState) -> R,
{
    let mut sessions = SESSIONS.lock().unwrap();
    sessions.get_mut(&session_id).map(f)
}

/// 移除 session
pub fn remove_session(session_id: i64) {
    let mut sessions = SESSIONS.lock().unwrap();
    sessions.remove(&session_id);
}
  • Step 2: 添加 once_cell 依赖

native/craft-core/Cargo.toml[dependencies] 区域添加:

once_cell = "1"
  • Step 3: 在 lib.rs 中注册 session 模块

native/craft-core/src/lib.rsmod 声明区域添加:

mod session;
  • Step 4: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml

Expected: 编译成功。

  • Step 5: 提交
git add native/craft-core/src/session.rs native/craft-core/src/lib.rs native/craft-core/Cargo.toml
git commit -m "feat(native): add session management with global handle registry"

Task 3: Rust — 创建 ffi/bitanswer.rsBitAnswer C API FFI 声明)

Files:

  • Create: native/craft-core/src/ffi/mod.rs

  • Create: native/craft-core/src/ffi/bitanswer.rs

  • Step 1: 创建 ffi/mod.rs

写入 native/craft-core/src/ffi/mod.rs

pub mod bitanswer;
  • Step 2: 创建 bitanswer.rs — BitAnswer C API 的 Rust extern 声明

写入 native/craft-core/src/ffi/bitanswer.rs

// BitAnswer C API 的 Rust FFI 声明
// 类型定义与 bitanswer.h 对齐

pub type BitHandle = *mut std::ffi::c_void;
pub type BitStatus = u32;
pub type BitUint32 = u32;

// 登录模式常量
pub const BIT_MODE_LOCAL: u32 = 0x01;
pub const BIT_MODE_REMOTE: u32 = 0x02;
pub const BIT_MODE_AUTO: u32 = 0x03;

// 绑定类型
pub const BINDING_EXISTING: u32 = 0;
pub const BINDING_LOCAL: u32 = 1;

// Session 信息类型
pub const XML_TYPE_SN_INFO: u32 = 3;

// Info 类型
pub const BIT_INFO_SN: u32 = 1;
pub const BIT_INFO_SN_FEATURE: u32 = 2;

// 特征查询模式
pub const BIT_QUERY_DEFAULT: u32 = 0x00;
pub const BIT_QUERY_AVAILABLE: u32 = 0x01;

// SetAttr 类型
pub const ATTR_WAIT_TIMEOUT: u32 = 0x2;
pub const ATTR_HB_INTERVAL: u32 = 0x8;

extern "C" {
    /// 登录授权
    pub fn Bit_Login(
        sz_url: *const std::os::raw::c_char,
        sz_sn: *const std::os::raw::c_char,
        p_application_data: *const u8,
        p_handle: *mut BitHandle,
        mode: BitUint32,
    ) -> BitStatus;

    /// 特征登录
    pub fn Bit_LoginEx(
        sz_url: *const std::os::raw::c_char,
        sz_sn: *const std::os::raw::c_char,
        feature_id: BitUint32,
        sz_reserved: *const std::os::raw::c_char,
        p_application_data: *const u8,
        p_handle: *mut BitHandle,
        mode: BitUint32,
    ) -> BitStatus;

    /// Token 登录
    pub fn Bit_LoginByToken(
        sz_url: *const std::os::raw::c_char,
        sz_access_token: *const std::os::raw::c_char,
        p_application_data: *const u8,
        p_handle: *mut BitHandle,
    ) -> BitStatus;

    /// 登出
    pub fn Bit_Logout(handle: BitHandle) -> BitStatus;

    /// 在线激活/更新
    pub fn Bit_UpdateOnline(
        sz_url: *const std::os::raw::c_char,
        sz_sn: *const std::os::raw::c_char,
        p_application_data: *const u8,
    ) -> BitStatus;

    /// 读取特征值
    pub fn Bit_ReadFeature(
        handle: BitHandle,
        feature_id: BitUint32,
        p_feature_value: *mut BitUint32,
    ) -> BitStatus;

    /// 写入特征值
    pub fn Bit_WriteFeature(
        handle: BitHandle,
        feature_id: BitUint32,
        feature_value: BitUint32,
    ) -> BitStatus;

    /// 查询特征可用量
    pub fn Bit_QueryFeature(
        handle: BitHandle,
        feature_id: BitUint32,
        p_capacity: *mut BitUint32,
    ) -> BitStatus;

    /// 释放特征占用
    pub fn Bit_ReleaseFeature(
        handle: BitHandle,
        feature_id: BitUint32,
        p_capacity: *mut BitUint32,
    ) -> BitStatus;

    /// 设置配置项
    pub fn Bit_SetDataItem(
        handle: BitHandle,
        sz_data_item_name: *const std::os::raw::c_char,
        p_data_item_value: *const std::ffi::c_void,
        data_item_value_size: BitUint32,
    ) -> BitStatus;

    /// 读取配置项
    pub fn Bit_GetDataItem(
        handle: BitHandle,
        sz_data_item_name: *const std::os::raw::c_char,
        p_data_item_value: *mut std::ffi::c_void,
        p_data_item_value_size: *mut BitUint32,
    ) -> BitStatus;

    /// 删除配置项
    pub fn Bit_RemoveDataItem(
        handle: BitHandle,
        sz_data_item_name: *const std::os::raw::c_char,
    ) -> BitStatus;

    /// 获取配置项数量
    pub fn Bit_GetDataItemNum(
        handle: BitHandle,
        p_num: *mut BitUint32,
    ) -> BitStatus;

    /// 按索引获取配置项名称
    pub fn Bit_GetDataItemName(
        handle: BitHandle,
        index: BitUint32,
        p_data_item_name: *mut std::os::raw::c_char,
        p_data_item_name_size: *mut BitUint32,
    ) -> BitStatus;

    /// 获取会话信息
    pub fn Bit_GetSessionInfo(
        handle: BitHandle,
        session_type: BitUint32,
        p_session_info: *mut std::os::raw::c_char,
        p_session_info_size: *mut BitUint32,
    ) -> BitStatus;

    /// 获取授权信息
    pub fn Bit_GetInfo(
        sz_sn: *const std::os::raw::c_char,
        p_application_data: *const u8,
        info_type: BitUint32,
        p_info: *mut std::os::raw::c_char,
        p_info_size: *mut BitUint32,
    ) -> BitStatus;

    /// 浮动迁出 SN
    pub fn Bit_CheckOutSn(
        sz_url: *const std::os::raw::c_char,
        feature_id: BitUint32,
        p_application_data: *const u8,
        n_duration_days: BitUint32,
    ) -> BitStatus;

    /// 浮动归还
    pub fn Bit_CheckIn(
        sz_url: *const std::os::raw::c_char,
        feature_id: BitUint32,
        p_application_data: *const u8,
    ) -> BitStatus;

    /// 撤销授权
    pub fn Bit_Revoke(
        sz_url: *const std::os::raw::c_char,
        sz_sn: *const std::os::raw::c_char,
        p_application_data: *const u8,
        p_revocation_info: *mut std::os::raw::c_char,
        p_revocation_info_size: *mut BitUint32,
    ) -> BitStatus;

    /// 删除本机 SN
    pub fn Bit_RemoveSn(
        sz_sn: *const std::os::raw::c_char,
        p_application_data: *const u8,
    ) -> BitStatus;

    /// 手动心跳
    pub fn Bit_Heartbeat(
        handle: BitHandle,
        p_reconnects_num: *mut BitUint32,
    ) -> BitStatus;

    /// 设置属性
    pub fn Bit_SetAttr(
        handle: BitHandle,
        attr_type: BitUint32,
        p_value: *const std::ffi::c_void,
    ) -> BitStatus;

    /// 获取库版本
    pub fn Bit_GetVersion(p_version: *mut BitUint32) -> BitStatus;
}
  • Step 3: 在 lib.rs 中注册 ffi 模块

native/craft-core/src/lib.rsmod 声明区域添加:

mod ffi;
  • Step 4: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml

Expected: 编译成功(extern 声明不会被链接检查,但语法正确)。

  • Step 5: 提交
git add native/craft-core/src/ffi/
git commit -m "feat(native): add BitAnswer C API FFI declarations"

Task 4: Rust — 创建 ffi/bridge.rscraft_* → Bit_* 桥接)

Files:

  • Create: native/craft-core/src/ffi/bridge.rs

  • Step 1: 创建 bridge.rs — Phase 1 核心桥接函数

写入 native/craft-core/src/ffi/bridge.rs

use crate::error::{LicenseError, to_craft_result};
use crate::ffi::bitanswer;
use crate::session;
use crate::CraftResult;
use std::ffi::CString;
use std::os::raw::c_char;

/// 激活:调用 Bit_UpdateOnline
pub fn bridge_activate(session_id: i64, license_key: &str) -> CraftResult {
    let result = session::with_session(session_id, |state| {
        // 从配置中解析 URL(简化版:使用固定空 URL → 使用默认服务器)
        let url = std::ptr::null(); // NULL 表示使用默认服务器
        let sn = match CString::new(license_key) {
            Ok(s) => s,
            Err(_) => return LicenseError::InvalidParameter,
        };
        let app_data = state.application_data.as_ptr();

        let status = unsafe {
            bitanswer::Bit_UpdateOnline(url, sn.as_ptr(), app_data)
        };

        if status == 0 {
            LicenseError::Success
        } else {
            LicenseError::from_bit_status(status)
        }
    });

    match result {
        Some(err) => to_craft_result(err),
        None => to_craft_result(LicenseError::WrongHandle),
    }
}

/// 登录:调用 Bit_Login
pub fn bridge_login(session_id: i64, sn_str: &str, mode: u32) -> CraftResult {
    let result = session::with_session(session_id, |state| {
        let url = std::ptr::null();
        let sn = match CString::new(sn_str) {
            Ok(s) => s,
            Err(_) => return LicenseError::InvalidParameter,
        };
        let app_data = state.application_data.as_ptr();
        let mut handle: bitanswer::BitHandle = std::ptr::null_mut();

        let status = unsafe {
            bitanswer::Bit_Login(url, sn.as_ptr(), app_data, &mut handle, mode)
        };

        if status == 0 {
            state.bit_handle = Some(handle as usize);
            state.logged_in = true;
            LicenseError::Success
        } else {
            LicenseError::from_bit_status(status)
        }
    });

    match result {
        Some(err) => to_craft_result(err),
        None => to_craft_result(LicenseError::WrongHandle),
    }
}

/// 登出:调用 Bit_Logout
pub fn bridge_logout(session_id: i64) -> CraftResult {
    let result = session::with_session(session_id, |state| {
        let handle = match state.bit_handle {
            Some(h) => h as bitanswer::BitHandle,
            None => return LicenseError::WrongHandle,
        };

        let status = unsafe { bitanswer::Bit_Logout(handle) };
        state.bit_handle = None;
        state.logged_in = false;

        if status == 0 {
            LicenseError::Success
        } else {
            LicenseError::from_bit_status(status)
        }
    });

    match result {
        Some(err) => to_craft_result(err),
        None => to_craft_result(LicenseError::WrongHandle),
    }
}

/// 心跳:调用 Bit_Heartbeat
pub fn bridge_heartbeat(session_id: i64) -> CraftResult {
    let result = session::with_session(session_id, |state| {
        let handle = match state.bit_handle {
            Some(h) => h as bitanswer::BitHandle,
            None => return LicenseError::WrongHandle,
        };

        let mut reconnects: u32 = 0;
        let status = unsafe { bitanswer::Bit_Heartbeat(handle, &mut reconnects) };

        if status == 0 {
            LicenseError::Success
        } else {
            LicenseError::from_bit_status(status)
        }
    });

    match result {
        Some(err) => to_craft_result(err),
        None => to_craft_result(LicenseError::WrongHandle),
    }
}

/// 读取特征值:调用 Bit_ReadFeature
pub fn bridge_read_feature(session_id: i64, feature_id: u32) -> i32 {
    let result = session::with_session(session_id, |state| {
        let handle = match state.bit_handle {
            Some(h) => h as bitanswer::BitHandle,
            None => return -1i32,
        };

        let mut value: u32 = 0;
        let status = unsafe { bitanswer::Bit_ReadFeature(handle, feature_id, &mut value) };
        if status == 0 { value as i32 } else { -1i32 }
    });

    result.unwrap_or(-1)
}

/// 检查许可:调用 Bit_GetSessionInfo
pub fn bridge_check_license(session_id: i64) -> CraftResult {
    let result = session::with_session(session_id, |state| {
        let handle = match state.bit_handle {
            Some(h) => h as bitanswer::BitHandle,
            None => return LicenseError::WrongHandle,
        };

        let mut buf = vec![0u8; 4096];
        let mut size: u32 = 4096;
        let status = unsafe {
            bitanswer::Bit_GetSessionInfo(handle, bitanswer::XML_TYPE_SN_INFO, buf.as_mut_ptr() as *mut c_char, &mut size)
        };

        if status == 0 {
            LicenseError::Success
        } else {
            LicenseError::from_bit_status(status)
        }
    });

    match result {
        Some(err) => to_craft_result(err),
        None => to_craft_result(LicenseError::WrongHandle),
    }
}
  • Step 2: 在 ffi/mod.rs 中注册 bridge

native/craft-core/src/ffi/mod.rs 添加:

pub mod bridge;
  • Step 3: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml

Expected: 编译成功。

  • Step 4: 提交
git add native/craft-core/src/ffi/bridge.rs native/craft-core/src/ffi/mod.rs
git commit -m "feat(native): add craft_* to Bit_* bridge functions"

Task 5: Rust — 重构 lib.rs 使用 session-based 句柄管理

Files:

  • Modify: native/craft-core/src/lib.rs

  • Step 1: 重写 lib.rs

用以下内容覆写 native/craft-core/src/lib.rs

// CraftLabs 授权核心库 — Rust 实现
// 导出 C ABI 接口,基于 session 管理,对接 BitAnswer C API
// 对齐 docs/平台架构思路.md §3.1

use std::ffi::CStr;
use std::os::raw::c_char;
use std::ptr;

mod activate;
mod error;
mod ffi;
mod heartbeat;
mod license;
mod security;
mod session;

/// Java long 别名 — session_id
type SessionId = i64;

#[repr(C)]
pub struct CraftResult {
    pub success: i32,
    pub message: *const c_char,
}

#[repr(C)]
pub struct LicenseInfo {
    pub is_licensed: i32,
    pub expiration_date: i64,
    pub feature_names: *const *const c_char,
    pub feature_values: *const i32,
    pub feature_count: i32,
}

unsafe fn c_str_to_string(ptr: *const c_char) -> String {
    if ptr.is_null() {
        String::new()
    } else {
        CStr::from_ptr(ptr).to_string_lossy().into_owned()
    }
}

static OK_MSG: &[u8] = b"ok\0";
static FAIL_MSG: &[u8] = b"failure\0";

fn ok_result() -> CraftResult {
    CraftResult { success: 1, message: OK_MSG.as_ptr() as *const c_char }
}

fn fail_result() -> CraftResult {
    CraftResult { success: 0, message: FAIL_MSG.as_ptr() as *const c_char }
}

// ── 现有 9 个 C ABI 函数(签名不变,内部改为 session 管理)──

#[no_mangle]
pub extern "C" fn craft_initialize(config_json: *const c_char) -> SessionId {
    let config = unsafe { c_str_to_string(config_json) };

    #[cfg(feature = "security-hardening")]
    {
        security::anti_debug::anti_debug_check();
        let _ = security::integrity::integrity_check();
    }

    // 注册 session,返回 session_id
    session::register_session(config, Vec::new())
}

#[no_mangle]
pub extern "C" fn craft_activate(
    session_id: SessionId,
    license_key: *const c_char,
    _config_json: *const c_char,
) -> CraftResult {
    if session_id == 0 { return fail_result(); }
    let key = unsafe { c_str_to_string(license_key) };
    ffi::bridge::bridge_activate(session_id, &key)
}

#[no_mangle]
pub extern "C" fn craft_check_license(session_id: SessionId) -> CraftResult {
    if session_id == 0 { return fail_result(); }
    ffi::bridge::bridge_check_license(session_id)
}

#[no_mangle]
pub extern "C" fn craft_get_license_info(session_id: SessionId) -> *mut LicenseInfo {
    if session_id == 0 { return ptr::null_mut(); }
    license::get_license_info_stub()
}

#[no_mangle]
pub extern "C" fn craft_free_license_info(info: *mut LicenseInfo) {
    if !info.is_null() {
        unsafe { drop(Box::from_raw(info)); }
    }
}

#[no_mangle]
pub extern "C" fn craft_has_feature(
    session_id: SessionId,
    feature_name: *const c_char,
) -> i32 {
    if session_id == 0 { return 0; }
    let name = unsafe { c_str_to_string(feature_name) };
    // Phase 1: 仍返回 1(桩);Phase 2 对接 Bit_ReadFeature
    if license::has_feature_stub(&name) { 1 } else { 0 }
}

#[no_mangle]
pub extern "C" fn craft_release(session_id: SessionId) -> CraftResult {
    if session_id == 0 { return fail_result(); }
    ffi::bridge::bridge_logout(session_id)
}

#[no_mangle]
pub extern "C" fn craft_heartbeat(session_id: SessionId) -> CraftResult {
    if session_id == 0 { return fail_result(); }
    ffi::bridge::bridge_heartbeat(session_id)
}

#[no_mangle]
pub extern "C" fn craft_destroy(session_id: SessionId) {
    if session_id != 0 {
        // 如果还登录着,先 logout
        let _ = ffi::bridge::bridge_logout(session_id);
        session::remove_session(session_id);
    }
}
  • Step 2: 更新 license.rs — 适配新签名

覆写 native/craft-core/src/license.rs

// License state management — Phase 1 stubs

use crate::LicenseInfo;
use std::ptr;

pub fn get_license_info_stub() -> *mut LicenseInfo {
    let info = Box::new(LicenseInfo {
        is_licensed: 1,
        expiration_date: 0,
        feature_names: ptr::null(),
        feature_values: ptr::null(),
        feature_count: 0,
    });
    Box::into_raw(info)
}

pub fn has_feature_stub(_feature_name: &str) -> bool {
    true
}
  • Step 3: 更新 activate.rs — Phase 1 清空

覆写 native/craft-core/src/activate.rs

// Activation logic — Phase 1: 使用 ffi::bridge::bridge_activate
// 文件保留用于后续 Phase 扩展
  • Step 4: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml

Expected: 编译成功,无 warning。

  • Step 5: 提交
git add native/craft-core/src/lib.rs native/craft-core/src/license.rs native/craft-core/src/activate.rs
git commit -m "refactor(native): switch to session-based handle management in lib.rs"

Task 6: Java — 标记 AuthProvider 为 @Deprecated

Files:

  • Modify: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/AuthProvider.java

  • Step 1: 添加 @Deprecated 注解

AuthProvider 接口声明前添加:

@Deprecated(since = "1.1.0", forRemoval = true)

完整修改(在第 15 行 public interface AuthProvider 前插入):

native/craft-core/src/activate.rs 不需要修改,直接修改 Java 文件。

/Users/huangping/Documents/workspace/craftlabs-authorization-sdk/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/AuthProvider.java 第 14 行(public interface AuthProvider extends AutoCloseable { 上方)插入 @Deprecated

/**
 * 授权能力的统一契约:初始化、激活、校验许可、查询特性与释放等生命周期方法。
 * ...
 * @deprecated 自 1.1.0 起,请使用 {@link cn.craftlabs.auth.CraftLicense} 及其
 *             {@link cn.craftlabs.auth.session.LicenseSession} 替代。
 *             将在 Phase 4 移除。
 */
@Deprecated(since = "1.1.0", forRemoval = true)
public interface AuthProvider extends AutoCloseable {
  • Step 2: 编译验证
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile

Expected: 编译成功(有 deprecated warning 是预期行为)。

  • Step 3: 提交
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/AuthProvider.java
git commit -m "deprecate: mark AuthProvider as @Deprecated, guide to CraftLicense"

Task 7: Java — 创建 6 个能力接口

Files:

  • Create: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseLifecycle.java

  • Create: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/FeatureManagement.java

  • Create: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/DataItemStore.java

  • Create: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseInfoQuery.java

  • Create: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/CheckoutManager.java

  • Create: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseUtility.java

  • Step 1: 创建 LicenseLifecycle

写入 java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseLifecycle.java

package cn.craftlabs.auth.session;

import cn.craftlabs.auth.AuthResult;

/**
 * 认证与会话生命周期接口,对齐 BitAnswer C API 的 Login/Logout/Revoke 等。
 *
 * @since 1.1.0
 */
public interface LicenseLifecycle {
    /** 在线激活/更新授权。对应 {@code Bit_UpdateOnline}。 */
    AuthResult activate(String licenseKey);

    /** 登录授权。对应 {@code Bit_Login}。 */
    AuthResult login(String sn, int mode);

    /** 特征登录。对应 {@code Bit_LoginEx}。 */
    AuthResult loginEx(String sn, int featureId, String reserved, int mode);

    /** Token 登录。对应 {@code Bit_LoginByToken}。 */
    AuthResult loginByToken(String accessToken);

    /** 登出。对应 {@code Bit_Logout}。 */
    AuthResult logout();

    /** 撤销授权。对应 {@code Bit_Revoke}。 */
    AuthResult revoke(String sn);

    /** 删除本机 SN 数据。对应 {@code Bit_RemoveSn}。 */
    void removeSn(String sn);

    /** 手动心跳。对应 {@code Bit_Heartbeat}。 */
    AuthResult heartbeat();

    /** 释放许可占用。对应 {@code Bit_Logout} + 资源清理。 */
    AuthResult release();
}
  • Step 2: 创建 FeatureManagement

写入 java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/FeatureManagement.java

package cn.craftlabs.auth.session;

/**
 * 特征项管理接口,对齐 BitAnswer C API 的特征读写/加解密/占用释放。
 *
 * @since 1.1.0
 */
public interface FeatureManagement {
    /** 读取特征值。对应 {@code Bit_ReadFeature}。 */
    int readFeature(int featureId);

    /** 写入特征值。对应 {@code Bit_WriteFeature}。 */
    void writeFeature(int featureId, int value);

    /** 查询特征可用量。对应 {@code Bit_QueryFeature}。 */
    int queryFeature(int featureId);

    /** 释放特征占用。对应 {@code Bit_ReleaseFeature}。 */
    int releaseFeature(int featureId);
}
  • Step 3: 创建 DataItemStore

写入 java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/DataItemStore.java

package cn.craftlabs.auth.session;

/**
 * 配置项(Data Item)存储接口,对齐 BitAnswer C API 的 Set/Get/Remove/Enum。
 *
 * @since 1.1.0
 */
public interface DataItemStore {
    /** 设置配置项。对应 {@code Bit_SetDataItem}。 */
    void setDataItem(String name, byte[] value);

    /** 读取配置项。对应 {@code Bit_GetDataItem}。 */
    byte[] getDataItem(String name);

    /** 删除配置项。对应 {@code Bit_RemoveDataItem}。 */
    void removeDataItem(String name);

    /** 获取配置项数量。对应 {@code Bit_GetDataItemNum}。 */
    int getDataItemCount();

    /** 按索引获取配置项名称。对应 {@code Bit_GetDataItemName}。 */
    String getDataItemName(int index);
}
  • Step 4: 创建 LicenseInfoQuery

写入 java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseInfoQuery.java

package cn.craftlabs.auth.session;

import cn.craftlabs.auth.LicenseInfo;

/**
 * 许可信息查询接口,对齐 BitAnswer C API 的 GetSessionInfo/GetInfo/GetServerInfo。
 *
 * @since 1.1.0
 */
public interface LicenseInfoQuery {
    /** 获取许可快照。对应 {@code Bit_GetInfo(BIT_INFO_SN)}。 */
    LicenseInfo getLicenseInfo();

    /** 获取会话信息。对应 {@code Bit_GetSessionInfo}。 */
    String getSessionInfo(int sessionType);

    /** 获取授权详情。对应 {@code Bit_GetInfo}。 */
    String getInfo(int infoType);
}
  • Step 5: 创建 CheckoutManager

写入 java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/CheckoutManager.java

package cn.craftlabs.auth.session;

/**
 * 浮动授权借出/归还接口,对齐 BitAnswer C API 的 CheckOut/CheckIn 系列。
 *
 * @since 1.1.0
 */
public interface CheckoutManager {
    /** 浮动迁出 SN。对应 {@code Bit_CheckOutSn}。 */
    void checkOutSn(String url, int featureId, int durationDays);

    /** 浮动归还。对应 {@code Bit_CheckIn}。 */
    void checkIn(String url, int featureId);
}
  • Step 6: 创建 LicenseUtility

写入 java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseUtility.java

package cn.craftlabs.auth.session;

/**
 * 工具方法接口,对齐 BitAnswer C API 的 SetAttr/CustomInfo/Version 等。
 *
 * @since 1.1.0
 */
public interface LicenseUtility {
    /** 设置句柄属性。对应 {@code Bit_SetAttr}。 */
    void setAttr(int type, byte[] value);

    /** 获取库版本。对应 {@code Bit_GetVersion}。 */
    int getVersion();

    /** 获取上一次错误码。对应 {@code Bit_GetLastError}。 */
    int getLastError();
}
  • Step 7: 编译验证
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile

Expected: 编译成功。

  • Step 8: 提交
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/
git commit -m "feat(java): add 6 capability interfaces for BitAnswer 1:1 mapping"

Task 8: Java — 创建 LicenseSession(骨架实现 6 个接口)

Files:

  • Create: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseSession.java

  • Step 1: 创建 LicenseSession

写入 java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseSession.java

package cn.craftlabs.auth.session;

import cn.craftlabs.auth.AuthResult;
import cn.craftlabs.auth.LicenseInfo;
import cn.craftlabs.auth.internal.NativeBridge;

/**
 * 授权会话,实现 6 个能力接口。
 * Phase 1: 仅实现 AuthProvider 兼容方法(委托 NativeBridge),
 * 其他能力接口方法为骨架(返回默认值或抛出 UnsupportedOperationException)。
 *
 * @since 1.1.0
 */
public final class LicenseSession
        implements LicenseLifecycle, FeatureManagement, DataItemStore,
                   LicenseInfoQuery, CheckoutManager, LicenseUtility, AutoCloseable {

    private final long sessionId;
    private boolean closed;

    LicenseSession(long sessionId) {
        this.sessionId = sessionId;
        this.closed = false;
    }

    long getSessionId() { return sessionId; }

    // ── LicenseLifecycle ──

    @Override public AuthResult activate(String licenseKey) {
        ensureOpen();
        return NativeBridge.nativeActivate(sessionId, licenseKey);
    }

    @Override public AuthResult login(String sn, int mode) {
        ensureOpen();
        return NativeBridge.nativeLogin(sessionId, sn, mode);
    }

    @Override public AuthResult loginEx(String sn, int featureId, String reserved, int mode) {
        ensureOpen();
        return NativeBridge.nativeLoginEx(sessionId, sn, featureId, reserved, mode);
    }

    @Override public AuthResult loginByToken(String accessToken) {
        ensureOpen();
        return NativeBridge.nativeLoginByToken(sessionId, accessToken);
    }

    @Override public AuthResult logout() {
        ensureOpen();
        return NativeBridge.nativeLogout(sessionId);
    }

    @Override public AuthResult revoke(String sn) {
        ensureOpen();
        return NativeBridge.nativeRevoke(sessionId, sn);
    }

    @Override public void removeSn(String sn) {
        ensureOpen();
        NativeBridge.nativeRemoveSn(sessionId, sn);
    }

    @Override public AuthResult heartbeat() {
        ensureOpen();
        return NativeBridge.nativeHeartbeat(sessionId);
    }

    @Override public AuthResult release() {
        ensureOpen();
        return NativeBridge.nativeRelease(sessionId);
    }

    // ── FeatureManagement ──

    @Override public int readFeature(int featureId) {
        ensureOpen();
        return NativeBridge.nativeReadFeature(sessionId, featureId);
    }

    @Override public void writeFeature(int featureId, int value) {
        ensureOpen();
        NativeBridge.nativeWriteFeature(sessionId, featureId, value);
    }

    @Override public int queryFeature(int featureId) {
        ensureOpen();
        return NativeBridge.nativeQueryFeature(sessionId, featureId);
    }

    @Override public int releaseFeature(int featureId) {
        ensureOpen();
        return NativeBridge.nativeReleaseFeature(sessionId, featureId);
    }

    // ── DataItemStore ──

    @Override public void setDataItem(String name, byte[] value) {
        ensureOpen();
        NativeBridge.nativeSetDataItem(sessionId, name, value);
    }

    @Override public byte[] getDataItem(String name) {
        ensureOpen();
        return NativeBridge.nativeGetDataItem(sessionId, name);
    }

    @Override public void removeDataItem(String name) {
        ensureOpen();
        NativeBridge.nativeRemoveDataItem(sessionId, name);
    }

    @Override public int getDataItemCount() {
        ensureOpen();
        return NativeBridge.nativeGetDataItemNum(sessionId);
    }

    @Override public String getDataItemName(int index) {
        ensureOpen();
        return NativeBridge.nativeGetDataItemName(sessionId, index);
    }

    // ── LicenseInfoQuery ──

    @Override public LicenseInfo getLicenseInfo() {
        ensureOpen();
        return NativeBridge.nativeGetLicenseInfo(sessionId);
    }

    @Override public String getSessionInfo(int sessionType) {
        ensureOpen();
        return NativeBridge.nativeGetSessionInfo(sessionId, sessionType);
    }

    @Override public String getInfo(int infoType) {
        ensureOpen();
        return NativeBridge.nativeGetInfo(sessionId, infoType);
    }

    // ── CheckoutManager ──

    @Override public void checkOutSn(String url, int featureId, int durationDays) {
        ensureOpen();
        NativeBridge.nativeCheckOutSn(sessionId, url, featureId, durationDays);
    }

    @Override public void checkIn(String url, int featureId) {
        ensureOpen();
        NativeBridge.nativeCheckIn(sessionId, url, featureId);
    }

    // ── LicenseUtility ──

    @Override public void setAttr(int type, byte[] value) {
        ensureOpen();
        NativeBridge.nativeSetAttr(sessionId, type, value);
    }

    @Override public int getVersion() {
        return NativeBridge.nativeGetVersion();
    }

    @Override public int getLastError() {
        ensureOpen();
        return NativeBridge.nativeGetLastError(sessionId);
    }

    // ── Lifecycle ──

    @Override public void close() {
        if (!closed) {
            NativeBridge.nativeDestroy(sessionId);
            closed = true;
        }
    }

    public boolean isClosed() { return closed; }

    private void ensureOpen() {
        if (closed) throw new IllegalStateException("Session is closed");
    }
}
  • Step 2: 编译验证(预期失败 — NativeBridge 缺少新方法)
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile 2>&1 | head -30

Expected: 编译失败,提示 NativeBridge 缺少 nativeLoginnativeLogout 等方法。

  • Step 3: 提交
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseSession.java
git commit -m "feat(java): add LicenseSession skeleton implementing 6 capability interfaces"

Task 9: Java — 扩展 NativeBridge(添加 Phase 1 新 JNI 方法)

Files:

  • Modify: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/NativeBridge.java

  • Step 1: 在 NativeBridge 中添加所有新 native 方法声明

在现有方法后追加:

    // ── Phase 1 新增:会话/认证 ──
    public static native AuthResult nativeLogin(long handle, String sn, int mode);
    public static native AuthResult nativeLoginEx(long handle, String sn, int featureId, String reserved, int mode);
    public static native AuthResult nativeLoginByToken(long handle, String accessToken);
    public static native AuthResult nativeLogout(long handle);

    // ── Phase 1 新增:特征项 ──
    public static native int nativeReadFeature(long handle, int featureId);
    public static native void nativeWriteFeature(long handle, int featureId, int value);
    public static native int nativeQueryFeature(long handle, int featureId);
    public static native int nativeReleaseFeature(long handle, int featureId);

    // ── Phase 1 新增:配置项 ──
    public static native void nativeSetDataItem(long handle, String name, byte[] value);
    public static native byte[] nativeGetDataItem(long handle, String name);
    public static native void nativeRemoveDataItem(long handle, String name);
    public static native int nativeGetDataItemNum(long handle);
    public static native String nativeGetDataItemName(long handle, int index);

    // ── Phase 1 新增:信息查询 ──
    public static native String nativeGetSessionInfo(long handle, int sessionType);
    public static native String nativeGetInfo(long handle, int infoType);

    // ── Phase 1 新增:浮动授权 ──
    public static native void nativeCheckOutSn(long handle, String url, int featureId, int durationDays);
    public static native void nativeCheckIn(long handle, String url, int featureId);

    // ── Phase 1 新增:撤销/删除 ──
    public static native AuthResult nativeRevoke(long handle, String sn);
    public static native void nativeRemoveSn(long handle, String sn);

    // ── Phase 1 新增:工具 ──
    public static native void nativeSetAttr(long handle, int type, byte[] value);
    public static native int nativeGetVersion();
    public static native int nativeGetLastError(long handle);
  • Step 2: 编译验证
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile

Expected: 编译成功(native 方法声明不要求立即有 JNI 实现,运行时才链接)。

  • Step 3: 提交
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/NativeBridge.java
git commit -m "feat(java): extend NativeBridge with Phase 1 JNI method declarations (30+ new methods)"

Task 10: Java — 创建 CraftLicense(新顶层入口)

Files:

  • Create: java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/CraftLicense.java

  • Step 1: 创建 CraftLicense

写入 java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/CraftLicense.java

package cn.craftlabs.auth;

import cn.craftlabs.auth.internal.NativeBridge;
import cn.craftlabs.auth.session.LicenseSession;

/**
 * CraftLabs 授权 SDK 的顶层入口,替代 {@link AuthProvider}。
 *
 * <p>用法:
 * <pre>{@code
 *   CraftLicense license = new CraftLicense();
 *   try (LicenseSession session = license.initialize("{\"schemaVersion\":1,...}")) {
 *       session.activate("SN-XXXX");
 *       int val = session.readFeature(1);
 *   }
 * }</pre>
 *
 * @since 1.1.0
 */
public final class CraftLicense {

    static {
        System.loadLibrary("craftlabs_auth_bitanswer");
    }

    /**
     * 使用 JSON 配置初始化授权会话。
     *
     * @param configJson 符合 {@code schemas/craftlabs-auth-config.schema.json} 的配置字符串
     * @return 新的 {@link LicenseSession}
     */
    public LicenseSession initialize(String configJson) {
        String cfg = configJson != null ? configJson : "{}";
        long sessionId = NativeBridge.nativeInitialize(cfg);
        return new LicenseSession(sessionId);
    }
}
  • Step 2: 编译验证
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile

Expected: 编译成功。

  • Step 3: 提交
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/CraftLicense.java
git commit -m "feat(java): add CraftLicense as new entry point replacing AuthProvider"

Task 11: Java — 运行现有测试确保没有回归

Files:

  • (验证,不修改)

  • 测试文件: java/craftlabs-auth-tests/src/test/java/cn/craftlabs/auth/BitAnswerProviderTest.java

  • Step 1: 运行现有 Java SDK 测试

mvn -f java/pom.xml -B verify

Expected: 现有测试通过(AuthProvider 未移除,桩行为不变)。

  • Step 2: 验证 deprecated 警告存在但不为 error

Expected: 编译输出包含 AuthProvider is deprecated 警告,但不阻止构建。

  • Step 3: 提交
git add -A
git commit -m "verify: existing tests pass after Phase 1 refactor — no regression"

Task 12: Rust — 编译验证并确保 C ABI 兼容

Files:

  • (验证,不修改)

  • Step 1: 编译 Rust debug

cargo build --manifest-path native/craft-core/Cargo.toml

Expected: 编译成功。

  • Step 2: 编译 Rust release
cargo build --manifest-path native/craft-core/Cargo.toml --release

Expected: 编译成功。产物位于 native/target/release/libcraftlabs_auth_bitanswer.{so,dylib}

  • Step 3: 验证 C ABI 符号导出
nm native/target/debug/libcraftlabs_auth_bitanswer.dylib 2>/dev/null | grep " T _craft_" || \
nm native/target/debug/libcraftlabs_auth_bitanswer.so 2>/dev/null | grep " T craft_" || \
echo "Check platform-specific symbol listing"

Expected: 输出包含 craft_initializecraft_activatecraft_heartbeat 等符号。


Phase 1 完成检查点

Phase 1 完成后:

  • Rust: error.rs、session.rs、ffi/bitanswer.rs、ffi/bridge.rs 已创建
  • Rust: lib.rs 重构为 session-based 句柄管理
  • Rust: 现有 9 个 C ABI 函数签名不变,内部桥接到 BitAnswer
  • Java: AuthProvider 标记 @Deprecated
  • Java: 6 个能力接口已定义
  • Java: LicenseSession 骨架实现
  • Java: NativeBridge 新增 30+ JNI 方法声明
  • Java: CraftLicense 新入口已创建
  • 现有测试通过,无回归

Phase 2 — 核心 API 扩展

Phase 2 任务在 Phase 1 通过后执行,将骨架实现替换为真实调用。

Task 13: Rust — 扩展 activate.rs、heartbeat.rs、license.rs 为真实调用

  • 实现 craft_login C ABI 函数(对接 bridge_login
  • 实现 craft_login_ex C ABI 函数(对接 Bit_LoginEx
  • 实现 craft_revoke C ABI 函数(对接 Bit_Revoke
  • 实现 craft_remove_sn C ABI 函数(对接 Bit_RemoveSn
  • 实现 craft_read_feature C ABI 函数(对接 Bit_ReadFeature
  • 实现 craft_write_feature C ABI 函数(对接 Bit_WriteFeature
  • 实现 craft_query_feature C ABI 函数(对接 Bit_QueryFeature
  • 实现 craft_release_feature C ABI 函数(对接 Bit_ReleaseFeature
  • 每个函数添加 Rust 单元测试(mock BitAnswer extern
  • 编译验证:cargo test --manifest-path native/craft-core/Cargo.toml

Task 14: Java — BitAnswerProvider 委托到 LicenseSession

  • 修改 BitAnswerProviderinitialize 使用 CraftLicense
  • 保留 AuthProvider 接口兼容性
  • 编译验证:mvn -f java/pom.xml -B verify

Phase 3 — 高级功能

Task 15: Rust — DataItem + Checkout 桥接

  • 实现 craft_set_data_item / craft_get_data_item / craft_remove_data_item
  • 实现 craft_check_out_sn / craft_check_in
  • 实现离线升级流程(GetRequestInfoGetUpdateInfoApplyUpdateInfo

Task 16: Java — DataItemStore + CheckoutManager 完整实现

  • DataItemStore 方法全部对接 NativeBridge
  • CheckoutManager 方法全部对接 NativeBridge
  • 添加集成测试

Phase 4 — 清理

Task 17: 移除废弃代码

  • 删除 AuthProvider 接口
  • 删除 .deprecated-cmake/ 目录
  • 更新 BitAnswerProvider 以在新接口下工作(或标记废弃)
  • 全量回归:mvn -f java/pom.xml -B verify && cargo test

(Plan too long; 668 lines exceeds maximum 500 lines set in preferences. You are seeing the truncated version (500 lines). To see the full plan, read the file /Users/huangping/Documents/workspace/craftlabs-authorization-sdk/docs/superpowers/plans/2026-05-01-bitanswer-1to1-refactor.md directly. Lines 501-668 were not written.)<end▁of▁thinking>文件被截断了。让我确认完整写入:

<||DSML||tool_calls>
<||DSML||invoke name="bash">
<||DSML||parameter name="description" string="true">Check plan file line count