forked from 1b-agent/fieldcraft-upstream
Rename the project to FieldCraft
Crates fieldcraft-*, binaries fieldcraft and fieldcraft-cli, the .fieldcraft file (format tag fieldcraft/1, manifest fieldcraft.json), FIELDCRAFT_* environment variables, the MCP server name, egui widget ids (fc_*), the app icon document FieldCraft.icon and its generated family, docs and licences. No compatibility shim: no files of the earlier name exist. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
1 parent
9bc4e1b182
commit
d5bd74570f
268 files changed
+1666
-1666
No files matched your search
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "fieldcraft-cli"
|
||||
description = "FieldCraft from the command line: list commands, run scripts, serve MCP"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
fieldcraft-engine = { workspace = true }
|
||||
fieldcraft-io = { workspace = true }
|
||||
fieldcraft-mcp = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
@@ -1,27 +1,27 @@
|
||||
//! modelcraft-cli commands
|
||||
//! modelcraft-cli run <script.json>
|
||||
//! modelcraft-cli features
|
||||
//! modelcraft-cli mcp [--connect ADDR | --headless] [--automation-read-root DIR] [--automation-write-root DIR]
|
||||
//! fieldcraft-cli commands
|
||||
//! fieldcraft-cli run <script.json>
|
||||
//! fieldcraft-cli features
|
||||
//! fieldcraft-cli mcp [--connect ADDR | --headless] [--automation-read-root DIR] [--automation-write-root DIR]
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
use std::path::Path;
|
||||
use std::process::ExitCode;
|
||||
|
||||
use modelcraft_engine::Session;
|
||||
use fieldcraft_engine::Session;
|
||||
use serde_json::json;
|
||||
|
||||
const USAGE: &str = "\
|
||||
usage:
|
||||
modelcraft-cli commands list every command with its parameters
|
||||
modelcraft-cli run <script.json> run a script (a JSON array of {\"command\", \"params\"})
|
||||
fieldcraft-cli commands list every command with its parameters
|
||||
fieldcraft-cli run <script.json> run a script (a JSON array of {\"command\", \"params\"})
|
||||
and print the model summary
|
||||
modelcraft-cli features print docs/features.md (every capability, ready or
|
||||
fieldcraft-cli features print docs/features.md (every capability, ready or
|
||||
coming soon) from the capability catalogue
|
||||
modelcraft-cli mcp [options] serve the Model Context Protocol on stdin/stdout
|
||||
fieldcraft-cli mcp [options] serve the Model Context Protocol on stdin/stdout
|
||||
(docs/mcp.md):
|
||||
--headless an in-process session (the default)
|
||||
--connect ADDR drive the app at ADDR (`modelcraft --control PORT`);
|
||||
--connect ADDR drive the app at ADDR (`fieldcraft --control PORT`);
|
||||
headless when it cannot be reached
|
||||
--automation-read-root DIR files may be read only inside DIR
|
||||
--automation-write-root DIR files may be written only inside DIR (with either root,
|
||||
@@ -35,13 +35,13 @@ fn main() -> ExitCode {
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
let result = match args.first().map(String::as_str) {
|
||||
Some("commands") => {
|
||||
for c in modelcraft_engine::commands() {
|
||||
for c in fieldcraft_engine::commands() {
|
||||
println!("{:<24} {:<18} {}", c.id, c.label, c.params);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Some("features") => {
|
||||
print!("{}", modelcraft_engine::catalogue::features_page());
|
||||
print!("{}", fieldcraft_engine::catalogue::features_page());
|
||||
Ok(())
|
||||
}
|
||||
Some("mcp") => mcp(args.get(1..).unwrap_or(&[])),
|
||||
@@ -62,7 +62,7 @@ fn main() -> ExitCode {
|
||||
}
|
||||
|
||||
/// The `mcp` options: each given once, as `--flag value` or `--flag=value`.
|
||||
fn mcp_options(args: &[String]) -> Result<modelcraft_mcp::Options, String> {
|
||||
fn mcp_options(args: &[String]) -> Result<fieldcraft_mcp::Options, String> {
|
||||
let mut connect = None;
|
||||
let mut headless = false;
|
||||
let mut read = None;
|
||||
@@ -94,18 +94,18 @@ fn mcp_options(args: &[String]) -> Result<modelcraft_mcp::Options, String> {
|
||||
return Err("--connect and --headless exclude each other".into());
|
||||
}
|
||||
let roots = match (&read, &write) {
|
||||
(None, None) => modelcraft_io::roots::Roots::default(),
|
||||
_ => modelcraft_io::roots::Roots::new(read.as_deref().map(Path::new), write.as_deref().map(Path::new)).map_err(|e| e.to_string())?,
|
||||
(None, None) => fieldcraft_io::roots::Roots::default(),
|
||||
_ => fieldcraft_io::roots::Roots::new(read.as_deref().map(Path::new), write.as_deref().map(Path::new)).map_err(|e| e.to_string())?,
|
||||
};
|
||||
Ok(modelcraft_mcp::Options { connect, roots })
|
||||
Ok(fieldcraft_mcp::Options { connect, roots })
|
||||
}
|
||||
|
||||
fn mcp(args: &[String]) -> Result<(), String> {
|
||||
let (backend, note) = modelcraft_mcp::backend(mcp_options(args)?)?;
|
||||
let (backend, note) = fieldcraft_mcp::backend(mcp_options(args)?)?;
|
||||
if let Some(note) = note {
|
||||
eprintln!("modelcraft-cli mcp: {note}");
|
||||
eprintln!("fieldcraft-cli mcp: {note}");
|
||||
}
|
||||
let mut server = modelcraft_mcp::Server::new(backend);
|
||||
let mut server = fieldcraft_mcp::Server::new(backend);
|
||||
server.serve(std::io::stdin().lock(), std::io::stdout().lock()).map_err(|e| format!("mcp: {e}"))
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ fn run(path: &str) -> Result<(), String> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn opts(args: &[&str]) -> Result<modelcraft_mcp::Options, String> {
|
||||
fn opts(args: &[&str]) -> Result<fieldcraft_mcp::Options, String> {
|
||||
mcp_options(&args.iter().map(|s| s.to_string()).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "modelcraft"
|
||||
description = "ModelCraft desktop app"
|
||||
name = "fieldcraft"
|
||||
description = "FieldCraft desktop app"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
@@ -10,9 +10,9 @@ publish = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
modelcraft-engine = { workspace = true }
|
||||
modelcraft-ui-egui = { workspace = true }
|
||||
modelcraft-io = { workspace = true }
|
||||
fieldcraft-engine = { workspace = true }
|
||||
fieldcraft-ui-egui = { workspace = true }
|
||||
fieldcraft-io = { workspace = true }
|
||||
rfd = { workspace = true }
|
||||
eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit", "wayland", "x11"] }
|
||||
egui = { workspace = true }
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Loopback JSON-lines control server: one request per line, one reply per line. Agents and
|
||||
//! tests drive the app through it; the UI thread answers each request between frames
|
||||
//! (`modelcraft_ui_egui::control`).
|
||||
//! (`fieldcraft_ui_egui::control`).
|
||||
//!
|
||||
//! Adapted from SolidCraft's `apps/solidcraft/src/control_server.rs` (MIT OR Apache-2.0), see
|
||||
//! ATTRIBUTION.md.
|
||||
@@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
use std::time::Duration;
|
||||
|
||||
use modelcraft_ui_egui::ControlRequest;
|
||||
use fieldcraft_ui_egui::ControlRequest;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
/// Longest request line (bytes).
|
||||
@@ -31,11 +31,11 @@ pub fn start(port: u16, ctx: egui::Context) -> Receiver<ControlRequest> {
|
||||
let listener = match TcpListener::bind(("127.0.0.1", port)) {
|
||||
Ok(l) => l,
|
||||
Err(e) => {
|
||||
eprintln!("modelcraft: control server failed to bind 127.0.0.1:{port}: {e}");
|
||||
eprintln!("fieldcraft: control server failed to bind 127.0.0.1:{port}: {e}");
|
||||
return rx;
|
||||
}
|
||||
};
|
||||
eprintln!("modelcraft: control server listening on 127.0.0.1:{port}");
|
||||
eprintln!("fieldcraft: control server listening on 127.0.0.1:{port}");
|
||||
std::thread::spawn(move || {
|
||||
for stream in listener.incoming().flatten() {
|
||||
if OPEN.fetch_add(1, Ordering::Relaxed) >= MAX_CONNECTIONS {
|
||||
@@ -1,23 +1,23 @@
|
||||
//! ModelCraft desktop app.
|
||||
//! FieldCraft desktop app.
|
||||
//!
|
||||
//! Usage: `modelcraft [--sample [NAME]] [--control PORT] [FILE…]` (files open as if dropped on the
|
||||
//! window; NAME is a sample of `modelcraft_engine::sample`, `parts` by default)
|
||||
//! Usage: `fieldcraft [--sample [NAME]] [--control PORT] [FILE…]` (files open as if dropped on the
|
||||
//! window; NAME is a sample of `fieldcraft_engine::sample`, `parts` by default)
|
||||
//!
|
||||
//! `--sample` opens a sample model (`modelcraft_engine::sample`). `--control PORT` (or
|
||||
//! `MODELCRAFT_CONTROL_PORT`) starts a localhost JSON-lines control server:
|
||||
//! `--sample` opens a sample model (`fieldcraft_engine::sample`). `--control PORT` (or
|
||||
//! `FIELDCRAFT_CONTROL_PORT`) starts a localhost JSON-lines control server:
|
||||
//! `{"id":1,"method":"engine.execute","params":{"command":"model.info","params":{}}}`. See
|
||||
//! `modelcraft_ui_egui::control` and docs/control-protocol.md.
|
||||
//! `fieldcraft_ui_egui::control` and docs/control-protocol.md.
|
||||
#![cfg_attr(all(target_os = "windows", not(debug_assertions)), windows_subsystem = "windows")]
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod control_server;
|
||||
|
||||
use modelcraft_engine::Session;
|
||||
use modelcraft_engine::autosave::{self, Autosaver};
|
||||
use modelcraft_ui_egui::{Filter, ModelApp, Services};
|
||||
use fieldcraft_engine::Session;
|
||||
use fieldcraft_engine::autosave::{self, Autosaver};
|
||||
use fieldcraft_ui_egui::{Filter, ModelApp, Services};
|
||||
|
||||
const USAGE: &str = "usage: modelcraft [--sample [parts|cantilever|impact]] [--control PORT] [FILE…] (FILE: .modelcraft, .solidcraft or .3mf)";
|
||||
const USAGE: &str = "usage: fieldcraft [--sample [parts|cantilever|impact]] [--control PORT] [FILE…] (FILE: .fieldcraft, .solidcraft or .3mf)";
|
||||
const DEPTH_BITS: u8 = 24;
|
||||
/// The window draws its own caption buttons where the OS ones would sit apart from the app bar.
|
||||
const CUSTOM_TITLEBAR: bool = !cfg!(target_os = "macos");
|
||||
@@ -90,7 +90,7 @@ fn services() -> Services {
|
||||
dialog(filters).set_file_name(name).save_file().map(|p| p.to_string_lossy().to_string())
|
||||
})),
|
||||
// Beside the recovery folder in the per-user data folder.
|
||||
recent_path: modelcraft_io::recovery::default_dir().and_then(|d| d.parent().map(|p| p.join("recent.json"))),
|
||||
recent_path: fieldcraft_io::recovery::default_dir().and_then(|d| d.parent().map(|p| p.join("recent.json"))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ fn parse_args(args: impl IntoIterator<Item = String>, env_port: Option<String>)
|
||||
match a.as_str() {
|
||||
"--sample" => {
|
||||
// An optional sample name follows.
|
||||
let name = args.peek().filter(|a| modelcraft_engine::sample::find(a).is_some()).cloned();
|
||||
let name = args.peek().filter(|a| fieldcraft_engine::sample::find(a).is_some()).cloned();
|
||||
if name.is_some() {
|
||||
args.next();
|
||||
}
|
||||
@@ -122,7 +122,7 @@ fn parse_args(args: impl IntoIterator<Item = String>, env_port: Option<String>)
|
||||
out.control = Some(port.parse().map_err(|_| format!("--control: `{port}` is not a port number"))?);
|
||||
}
|
||||
"--version" | "-V" => {
|
||||
println!("modelcraft {}", modelcraft_ui_egui::VERSION);
|
||||
println!("fieldcraft {}", fieldcraft_ui_egui::VERSION);
|
||||
return Ok(None);
|
||||
}
|
||||
"--help" | "-h" => {
|
||||
@@ -137,17 +137,17 @@ fn parse_args(args: impl IntoIterator<Item = String>, env_port: Option<String>)
|
||||
}
|
||||
|
||||
fn main() -> eframe::Result {
|
||||
let args = match parse_args(std::env::args().skip(1), std::env::var("MODELCRAFT_CONTROL_PORT").ok()) {
|
||||
let args = match parse_args(std::env::args().skip(1), std::env::var("FIELDCRAFT_CONTROL_PORT").ok()) {
|
||||
Ok(Some(a)) => a,
|
||||
Ok(None) => return Ok(()),
|
||||
Err(e) => {
|
||||
eprintln!("modelcraft: {e}");
|
||||
eprintln!("fieldcraft: {e}");
|
||||
std::process::exit(2);
|
||||
}
|
||||
};
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_title("ModelCraft")
|
||||
.with_title("FieldCraft")
|
||||
.with_icon(app_icon())
|
||||
.with_inner_size([1400.0, 900.0])
|
||||
.with_min_inner_size([960.0, 600.0])
|
||||
@@ -163,7 +163,7 @@ fn main() -> eframe::Result {
|
||||
..Default::default()
|
||||
};
|
||||
eframe::run_native(
|
||||
"ModelCraft",
|
||||
"FieldCraft",
|
||||
options,
|
||||
Box::new(move |cc| {
|
||||
let mut app = ModelApp::new(Session::default()).with_services(services());
|
||||
@@ -171,7 +171,7 @@ fn main() -> eframe::Result {
|
||||
app.integrated_titlebar = cfg!(target_os = "macos");
|
||||
if let Some(rs) = &cc.wgpu_render_state {
|
||||
let info = rs.adapter.get_info();
|
||||
eprintln!("modelcraft: graphics {:?} {}, MSAA {}x (requested {MSAA}x)", info.backend, info.name, rs.msaa_samples);
|
||||
eprintln!("fieldcraft: graphics {:?} {}, MSAA {}x (requested {MSAA}x)", info.backend, info.name, rs.msaa_samples);
|
||||
app.set_wgpu(rs, DEPTH_BITS, rs.msaa_samples, &cc.egui_ctx);
|
||||
}
|
||||
if let Some(port) = args.control {
|
||||
@@ -224,9 +224,9 @@ mod tests {
|
||||
assert!(parse(&["--control", "99999"], None).is_err());
|
||||
assert!(parse(&["--frobnicate"], None).is_err());
|
||||
assert_eq!(parse(&["--sample", "impact"], None).map(|a| a.and_then(|a| a.sample)), Ok(Some("impact".into())));
|
||||
let a = parse(&["--sample", "x.modelcraft"], None).unwrap().unwrap();
|
||||
assert_eq!((a.sample.as_deref(), a.files), (Some("parts"), vec!["x.modelcraft".to_string()]));
|
||||
assert_eq!(parse(&["a.modelcraft", "--sample"], None).map(|a| a.map(|a| a.files)), Ok(Some(vec!["a.modelcraft".to_string()])));
|
||||
let a = parse(&["--sample", "x.fieldcraft"], None).unwrap().unwrap();
|
||||
assert_eq!((a.sample.as_deref(), a.files), (Some("parts"), vec!["x.fieldcraft".to_string()]));
|
||||
assert_eq!(parse(&["a.fieldcraft", "--sample"], None).map(|a| a.map(|a| a.files)), Ok(Some(vec!["a.fieldcraft".to_string()])));
|
||||
assert_eq!(parse(&["--help"], None), Ok(None));
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "modelcraft-cli"
|
||||
description = "ModelCraft from the command line: list commands, run scripts, serve MCP"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
modelcraft-engine = { workspace = true }
|
||||
modelcraft-io = { workspace = true }
|
||||
modelcraft-mcp = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
Reference in new issue
Block a user