mirror of
https://github.com/hanzoai/dev.git
synced 2026-10-25 03:32:32 +00:00
Our history is just-every's with author emails rewritten, so the graphs are
disjoint and git finds no merge base. Supplied one by hand: d1f1f51a41, the
just-every commit matching our Oct-2025 fork point (22 files apart). 9618
upstream commits, 7004 files, +1.81M/-159k, 37 conflicts.
Resolution:
- Upstream code where our side was the Oct-2025 prototype of a feature they
have since reimplemented 3-5x larger (agent_runs, browser_sessions,
history_cell/{agent,browser}, the vt100 harness and its snapshots).
- Upstream's config.rs module split (builder/defaults/sources/validation)
kept; zen5-coder and the provider-follows-model rule re-applied on top.
- Ours where the file IS the identity: README, package.json, postinstall,
release notes pipeline, integration-zed.
- Hybrid where upstream improved a file we had branded: updates.rs (their
release-repo migration, our hanzoai/dev), release.yml (their fail-fast npm
auth check, our package name), chatwidget.rs (their turn handling, our
HANZO_USER_KEY hint), exec human output (their 237 lines, our banner).
Superseded and dropped deliberately: our resume_command_name() coder-alias
hack — upstream's derive_resume_command_name() reads the invocation basename
generically, which yields "dev" for us.
LLM.md now records the real topology and the sync point; AGENTS.md and
CLAUDE.md are symlinks to it again. Single parent on purpose: adopting
jecode/main as a second parent would re-import 13,742 commits carrying the
upstream author emails a prior rewrite deliberately removed.
Version 0.2.188 -> 0.2.189.
Co-authored-by: Hanzo Dev <[email protected]>
125 lines
3.9 KiB
Rust
125 lines
3.9 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::path::PathBuf;
|
|
use std::process::Command;
|
|
use std::process::Stdio;
|
|
|
|
use serde_json::Value;
|
|
use serde_json::json;
|
|
|
|
fn app_server_bin() -> PathBuf {
|
|
PathBuf::from(assert_cmd::cargo::cargo_bin!("code-app-server"))
|
|
}
|
|
|
|
fn run_jsonrpc_script(requests: &[Value]) -> BTreeMap<i64, Value> {
|
|
let mut child = Command::new(app_server_bin())
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.expect("failed to spawn code-app-server");
|
|
|
|
let mut stdin = child.stdin.take().expect("child stdin is not piped");
|
|
for request in requests {
|
|
let line = serde_json::to_string(request).expect("request must be valid JSON");
|
|
use std::io::Write as _;
|
|
writeln!(stdin, "{line}").expect("failed to write JSON-RPC request line");
|
|
}
|
|
drop(stdin);
|
|
|
|
let output = child
|
|
.wait_with_output()
|
|
.expect("failed waiting for code-app-server output");
|
|
|
|
assert!(
|
|
output.status.success(),
|
|
"code-app-server exited with {status}; stderr:\n{stderr}",
|
|
status = output.status,
|
|
stderr = String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
|
|
String::from_utf8_lossy(&output.stdout)
|
|
.lines()
|
|
.map(|line| {
|
|
let message: Value = serde_json::from_str(line)
|
|
.unwrap_or_else(|e| panic!("invalid JSON-RPC line `{line}`: {e}"));
|
|
let id = message
|
|
.get("id")
|
|
.and_then(Value::as_i64)
|
|
.unwrap_or_else(|| panic!("JSON-RPC message missing numeric id: {message}"));
|
|
(id, message)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[test]
|
|
fn binary_smoke_requires_init_and_executes_command() {
|
|
let marker = "hello-from-app-server-binary-smoke";
|
|
let requests = vec![
|
|
json!({"jsonrpc":"2.0","id":1,"method":"getUserAgent"}),
|
|
json!({
|
|
"jsonrpc":"2.0",
|
|
"id":2,
|
|
"method":"initialize",
|
|
"params":{
|
|
"clientInfo":{
|
|
"name":"app-server-binary-smoke",
|
|
"version":"0.1.0"
|
|
}
|
|
}
|
|
}),
|
|
json!({"jsonrpc":"2.0","id":3,"method":"getUserAgent"}),
|
|
json!({
|
|
"jsonrpc":"2.0",
|
|
"id":4,
|
|
"method":"execOneOffCommand",
|
|
"params":{
|
|
"command":["bash","-lc", format!("echo {marker}")],
|
|
"timeoutMs":5000
|
|
}
|
|
}),
|
|
];
|
|
|
|
let responses = run_jsonrpc_script(&requests);
|
|
|
|
let pre_init_error = responses
|
|
.get(&1)
|
|
.and_then(|v| v.get("error"))
|
|
.and_then(|v| v.get("message"))
|
|
.and_then(Value::as_str)
|
|
.expect("expected error response for pre-initialize getUserAgent");
|
|
assert!(
|
|
pre_init_error.contains("Not initialized"),
|
|
"unexpected pre-init error message: {pre_init_error}"
|
|
);
|
|
|
|
let user_agent = responses
|
|
.get(&3)
|
|
.and_then(|v| v.get("result"))
|
|
.and_then(|v| v.get("userAgent"))
|
|
.and_then(Value::as_str)
|
|
.expect("expected getUserAgent response after initialize");
|
|
assert!(
|
|
user_agent.contains("(app-server-binary-smoke; 0.1.0)"),
|
|
"user agent did not include initialize client info: {user_agent}"
|
|
);
|
|
|
|
let exec_result = responses
|
|
.get(&4)
|
|
.and_then(|v| v.get("result"))
|
|
.expect("expected execOneOffCommand response");
|
|
let exit_code = exec_result
|
|
.get("exitCode")
|
|
.and_then(Value::as_i64)
|
|
.expect("execOneOffCommand result missing exitCode");
|
|
let stdout = exec_result
|
|
.get("stdout")
|
|
.and_then(Value::as_str)
|
|
.expect("execOneOffCommand result missing stdout");
|
|
|
|
assert_eq!(exit_code, 0, "execOneOffCommand returned non-zero exit");
|
|
assert!(
|
|
stdout.contains(marker),
|
|
"execOneOffCommand stdout missing marker. stdout was: {stdout}"
|
|
);
|
|
}
|