feat: 完成 rust cutover phase 8 收口

This commit is contained in:
lix-2026
2026-04-15 20:01:12 +08:00
parent 822c730cd6
commit 98db79b301
104 changed files with 18777 additions and 3025 deletions
+477
View File
@@ -0,0 +1,477 @@
use clap::{Args, Parser, Subcommand, ValueEnum};
use core_protocol::{InvocationKind, ToolExecutionMode};
use mnote_cli::{
plan_block_insert, plan_block_patch, plan_mindmap_get, plan_mindmap_op, plan_mindmap_put,
plan_page_get, plan_page_save, plan_page_title, plan_search_blocks,
plan_search_documents, plan_sidebar_dataset, plan_tool_run, render_plain_output, CliContext,
CliError, CliJsonOutput,
};
#[derive(Parser, Debug)]
#[command(name = "mnote-cli")]
#[command(about = "Phase 2 最小 CLI 协议冻结器", long_about = None)]
struct Cli {
#[command(flatten)]
global: GlobalArgs,
#[command(subcommand)]
command: Commands,
}
#[derive(Args, Debug, Clone)]
struct GlobalArgs {
#[arg(long)]
json: bool,
#[arg(long, default_value = "cli_user")]
actor_id: String,
#[arg(long, default_value = "human")]
actor_type: String,
#[arg(long)]
session_id: Option<String>,
#[arg(long)]
reason: Option<String>,
#[arg(long)]
idempotency_key: Option<String>,
#[arg(long)]
validate_only: bool,
#[arg(long)]
dry_run: bool,
}
#[derive(Subcommand, Debug)]
enum Commands {
Page(PageCommand),
Block(BlockCommand),
Mindmap(MindmapCommand),
Search(SearchCommand),
Sidebar(SidebarCommand),
Tool(ToolCommand),
}
#[derive(Args, Debug)]
struct PageCommand {
#[command(subcommand)]
action: PageAction,
}
#[derive(Subcommand, Debug)]
enum PageAction {
Get(PageGetArgs),
Title(PageTitleArgs),
Save(PageSaveArgs),
}
#[derive(Args, Debug)]
struct PageGetArgs {
#[arg(long)]
page_id: String,
#[arg(long)]
workspace_id: Option<String>,
}
#[derive(Args, Debug)]
struct PageTitleArgs {
#[arg(long)]
page_id: String,
#[arg(long)]
workspace_id: Option<String>,
#[arg(long)]
title: String,
}
#[derive(Args, Debug)]
struct PageSaveArgs {
#[arg(long)]
page_id: String,
#[arg(long)]
workspace_id: Option<String>,
#[arg(long)]
revision: Option<u64>,
#[arg(long)]
content_json: String,
#[arg(long)]
conflict_detection_key: Option<String>,
}
#[derive(Args, Debug)]
struct BlockCommand {
#[command(subcommand)]
action: BlockAction,
}
#[derive(Subcommand, Debug)]
enum BlockAction {
Insert(BlockInsertArgs),
Patch(BlockPatchArgs),
}
#[derive(Args, Debug)]
struct BlockInsertArgs {
#[arg(long)]
workspace_id: String,
#[arg(long)]
page_id: String,
#[arg(long)]
content: String,
#[arg(long, default_value = "paragraph")]
block_type: String,
#[arg(long)]
parent_block_id: Option<String>,
#[arg(long)]
prev_block_id: Option<String>,
}
#[derive(Args, Debug)]
struct BlockPatchArgs {
#[arg(long)]
page_id: String,
#[arg(long)]
block_id: String,
#[arg(long)]
workspace_id: Option<String>,
#[arg(long)]
revision: Option<u64>,
#[arg(long)]
snapshot_json: String,
#[arg(long)]
conflict_detection_key: Option<String>,
}
#[derive(Args, Debug)]
struct MindmapCommand {
#[command(subcommand)]
action: MindmapAction,
}
#[derive(Subcommand, Debug)]
enum MindmapAction {
Get(MindmapGetArgs),
Put(MindmapPutArgs),
Op(MindmapOpArgs),
}
#[derive(Args, Debug)]
struct MindmapGetArgs {
#[arg(long)]
document_id: String,
#[arg(long)]
mindmap_id: String,
#[arg(long)]
workspace_id: Option<String>,
}
#[derive(Args, Debug)]
struct MindmapPutArgs {
#[arg(long)]
document_id: String,
#[arg(long)]
mindmap_id: String,
#[arg(long)]
workspace_id: Option<String>,
#[arg(long)]
data_json: String,
#[arg(long, default_value_t = false)]
create_only: bool,
}
#[derive(Args, Debug)]
struct MindmapOpArgs {
#[arg(long)]
document_id: String,
#[arg(long)]
mindmap_id: String,
#[arg(long)]
workspace_id: Option<String>,
#[arg(long)]
ops_json: String,
}
#[derive(Args, Debug)]
struct SearchCommand {
#[command(subcommand)]
action: SearchAction,
}
#[derive(Subcommand, Debug)]
enum SearchAction {
Documents(SearchDocumentsArgs),
Blocks(SearchBlocksArgs),
}
#[derive(Args, Debug)]
struct SearchDocumentsArgs {
#[arg(long)]
query: String,
#[arg(long)]
workspace_id: Option<String>,
#[arg(long, default_value_t = 20)]
limit: u32,
#[arg(long)]
cursor: Option<String>,
}
#[derive(Args, Debug)]
struct SearchBlocksArgs {
#[arg(long)]
query: String,
#[arg(long)]
page_id: Option<String>,
#[arg(long, default_value_t = 20)]
limit: u32,
#[arg(long)]
cursor: Option<String>,
}
#[derive(Args, Debug)]
struct SidebarCommand {
#[command(subcommand)]
action: SidebarAction,
}
#[derive(Subcommand, Debug)]
enum SidebarAction {
Dataset(SidebarDatasetArgs),
}
#[derive(Args, Debug)]
struct SidebarDatasetArgs {
#[arg(long)]
workspace_id: String,
}
#[derive(Args, Debug)]
struct ToolCommand {
#[command(subcommand)]
action: ToolAction,
}
#[derive(Subcommand, Debug)]
enum ToolAction {
Run(ToolRunArgs),
}
#[derive(Args, Debug)]
struct ToolRunArgs {
#[arg(long)]
tool_name: String,
#[arg(long, value_enum, default_value_t = ToolKindArg::Command)]
kind: ToolKindArg,
#[arg(long, value_enum, default_value_t = ToolModeArg::Plan)]
mode: ToolModeArg,
#[arg(long)]
args_json: String,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)]
enum ToolKindArg {
Command,
Query,
Job,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)]
enum ToolModeArg {
Plan,
Result,
ExplainPlan,
}
fn main() {
let cli = Cli::parse();
let context = CliContext {
actor_id: cli.global.actor_id,
actor_type: cli.global.actor_type,
session_id: cli.global.session_id,
reason: cli.global.reason,
idempotency_key: cli.global.idempotency_key,
validate_only: cli.global.validate_only,
dry_run: cli.global.dry_run,
};
let result = match cli.command {
Commands::Page(command) => match command.action {
PageAction::Get(args) => {
plan_page_get(&context, &args.page_id, args.workspace_id.as_deref())
}
PageAction::Title(args) => plan_page_title(
&context,
&args.page_id,
args.workspace_id.as_deref(),
&args.title,
),
PageAction::Save(args) => plan_page_save(
&context,
&args.page_id,
args.workspace_id.as_deref(),
args.revision,
&args.content_json,
args.conflict_detection_key.as_deref(),
),
},
Commands::Block(command) => match command.action {
BlockAction::Insert(args) => plan_block_insert(
&context,
&args.workspace_id,
&args.page_id,
&args.content,
&args.block_type,
args.parent_block_id.as_deref(),
args.prev_block_id.as_deref(),
),
BlockAction::Patch(args) => plan_block_patch(
&context,
args.workspace_id.as_deref(),
&args.page_id,
&args.block_id,
args.revision,
&args.snapshot_json,
args.conflict_detection_key.as_deref(),
),
},
Commands::Mindmap(command) => match command.action {
MindmapAction::Get(args) => plan_mindmap_get(
&context,
args.workspace_id.as_deref(),
&args.document_id,
&args.mindmap_id,
),
MindmapAction::Put(args) => plan_mindmap_put(
&context,
args.workspace_id.as_deref(),
&args.document_id,
&args.mindmap_id,
&args.data_json,
args.create_only,
),
MindmapAction::Op(args) => plan_mindmap_op(
&context,
args.workspace_id.as_deref(),
&args.document_id,
&args.mindmap_id,
&args.ops_json,
),
},
Commands::Search(command) => match command.action {
SearchAction::Documents(args) => plan_search_documents(
&context,
&args.query,
args.workspace_id.as_deref(),
args.limit,
args.cursor.as_deref(),
),
SearchAction::Blocks(args) => plan_search_blocks(
&context,
&args.query,
args.page_id.as_deref(),
args.limit,
args.cursor.as_deref(),
),
},
Commands::Sidebar(command) => match command.action {
SidebarAction::Dataset(args) => plan_sidebar_dataset(&context, &args.workspace_id),
},
Commands::Tool(command) => match command.action {
ToolAction::Run(args) => plan_tool_run(
&context,
&args.tool_name,
to_invocation_kind(args.kind),
to_execution_mode(args.mode),
&args.args_json,
),
},
};
match result {
Ok(output) => emit_success(&output, cli.global.json),
Err(error) => emit_error(&error, cli.global.json),
}
}
fn emit_success(output: &CliJsonOutput, json_mode: bool) {
if json_mode {
println!(
"{}",
serde_json::to_string_pretty(output).expect("CLI JSON 输出必须可序列化")
);
} else {
println!("{}", render_plain_output(output));
}
}
fn emit_error(error: &CliError, json_mode: bool) -> ! {
if json_mode {
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"ok": false,
"entrypoint": "mnote-cli",
"error": {
"code": error.code,
"message": error.message,
}
}))
.expect("CLI 错误 JSON 输出必须可序列化")
);
} else {
eprintln!("{}: {}", error.code, error.message);
}
std::process::exit(1);
}
fn to_invocation_kind(kind: ToolKindArg) -> InvocationKind {
match kind {
ToolKindArg::Command => InvocationKind::Command,
ToolKindArg::Query => InvocationKind::Query,
ToolKindArg::Job => InvocationKind::Job,
}
}
fn to_execution_mode(mode: ToolModeArg) -> ToolExecutionMode {
match mode {
ToolModeArg::Plan => ToolExecutionMode::Plan,
ToolModeArg::Result => ToolExecutionMode::Result,
ToolModeArg::ExplainPlan => ToolExecutionMode::ExplainPlan,
}
}