refactor(tools): rename rhema-tools-contract to rhema-claw-tools-contract
The crate binds the claw to its client tools, not tools at large, so it carries the claw's name: package, lib and dep names, the mock's binary, and the conformance suite's use paths. Specs and README follow. The tools/contract directory keeps its name, and no reference to the old name remains.
This commit is contained in:
1 parent
9e132fdf71
commit
d067f4632f
11 files changed
+63
-48
No files matched your search
@@ -49,7 +49,7 @@ rhema-rsd = { path = "../rhema-rsd" }
|
|||||||
# The tool contract: the agent-token allowlist and version the auth
|
# The tool contract: the agent-token allowlist and version the auth
|
||||||
# middleware enforces (rhema-tools spec). The arrow runs claw ->
|
# middleware enforces (rhema-tools spec). The arrow runs claw ->
|
||||||
# contract only — never the reverse.
|
# contract only — never the reverse.
|
||||||
rhema-tools-contract = { path = "../../tools/contract" }
|
rhema-claw-tools-contract = { path = "../../tools/contract" }
|
||||||
# The resident tier's durable memory: its tools join the resolver chain
|
# The resident tier's durable memory: its tools join the resolver chain
|
||||||
rhema-memory = { path = "../rhema-memory" }
|
rhema-memory = { path = "../rhema-memory" }
|
||||||
# The shared ModelCard the backend registry's listings return
|
# The shared ModelCard the backend registry's listings return
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ use axum::routing::put;
|
|||||||
use axum::routing::{delete, get, post};
|
use axum::routing::{delete, get, post};
|
||||||
use axum::{Router, middleware};
|
use axum::{Router, middleware};
|
||||||
use rhema_autonomous_agent::Driver;
|
use rhema_autonomous_agent::Driver;
|
||||||
|
use rhema_claw_tools_contract::Auth;
|
||||||
|
use rhema_claw_tools_contract::CONTRACT_HEADER;
|
||||||
|
use rhema_claw_tools_contract::CONTRACT_VERSION;
|
||||||
use rhema_core::ImageMediaType;
|
use rhema_core::ImageMediaType;
|
||||||
use rhema_rsd::{
|
use rhema_rsd::{
|
||||||
DispatchError, Dispatcher, IngestResult, KeyRecord, KeyState, ResourceKey, SqliteStore, Store,
|
DispatchError, Dispatcher, IngestResult, KeyRecord, KeyState, ResourceKey, SqliteStore, Store,
|
||||||
};
|
};
|
||||||
use rhema_tools_contract::Auth;
|
|
||||||
use rhema_tools_contract::CONTRACT_HEADER;
|
|
||||||
use rhema_tools_contract::CONTRACT_VERSION;
|
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use tokio::sync::broadcast::error::RecvError;
|
use tokio::sync::broadcast::error::RecvError;
|
||||||
@@ -401,8 +401,10 @@ async fn require_auth(State(state): State<ClawState>, request: Request, next: Ne
|
|||||||
)
|
)
|
||||||
.into_response();
|
.into_response();
|
||||||
}
|
}
|
||||||
if rhema_tools_contract::agent_allowed(request.method().as_str(), request.uri().path())
|
if rhema_claw_tools_contract::agent_allowed(
|
||||||
{
|
request.method().as_str(),
|
||||||
|
request.uri().path(),
|
||||||
|
) {
|
||||||
next.run(request).await
|
next.run(request).await
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
@@ -427,8 +429,10 @@ async fn require_auth(State(state): State<ClawState>, request: Request, next: Ne
|
|||||||
)
|
)
|
||||||
.into_response();
|
.into_response();
|
||||||
}
|
}
|
||||||
if rhema_tools_contract::ingest_allowed(request.method().as_str(), request.uri().path())
|
if rhema_claw_tools_contract::ingest_allowed(
|
||||||
{
|
request.method().as_str(),
|
||||||
|
request.uri().path(),
|
||||||
|
) {
|
||||||
next.run(request).await
|
next.run(request).await
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -1483,7 +1483,8 @@ async fn the_surface_speaks_the_tool_contract() {
|
|||||||
.expect("the daemon assembles");
|
.expect("the daemon assembles");
|
||||||
let base = serve(&daemon).await;
|
let base = serve(&daemon).await;
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let result = rhema_tools_contract::conformance(|request: rhema_tools_contract::CallRequest| {
|
let result = rhema_claw_tools_contract::conformance(
|
||||||
|
|request: rhema_claw_tools_contract::CallRequest| {
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
let base = base.clone();
|
let base = base.clone();
|
||||||
async move {
|
async move {
|
||||||
@@ -1491,17 +1492,17 @@ async fn the_surface_speaks_the_tool_contract() {
|
|||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let builder = client.request(method, format!("{base}{}", request.path));
|
let builder = client.request(method, format!("{base}{}", request.path));
|
||||||
let builder = match request.auth {
|
let builder = match request.auth {
|
||||||
rhema_tools_contract::Auth::None => builder,
|
rhema_claw_tools_contract::Auth::None => builder,
|
||||||
rhema_tools_contract::Auth::Human => {
|
rhema_claw_tools_contract::Auth::Human => {
|
||||||
builder.header(reqwest::header::AUTHORIZATION, "Bearer human-token")
|
builder.header(reqwest::header::AUTHORIZATION, "Bearer human-token")
|
||||||
}
|
}
|
||||||
rhema_tools_contract::Auth::Agent => {
|
rhema_claw_tools_contract::Auth::Agent => {
|
||||||
builder.header(reqwest::header::AUTHORIZATION, "Bearer agent-token")
|
builder.header(reqwest::header::AUTHORIZATION, "Bearer agent-token")
|
||||||
}
|
}
|
||||||
rhema_tools_contract::Auth::Invalid => {
|
rhema_claw_tools_contract::Auth::Invalid => {
|
||||||
builder.header(reqwest::header::AUTHORIZATION, "Bearer a-stranger")
|
builder.header(reqwest::header::AUTHORIZATION, "Bearer a-stranger")
|
||||||
}
|
}
|
||||||
rhema_tools_contract::Auth::Ingest => {
|
rhema_claw_tools_contract::Auth::Ingest => {
|
||||||
builder.header(reqwest::header::AUTHORIZATION, "Bearer ingest-token")
|
builder.header(reqwest::header::AUTHORIZATION, "Bearer ingest-token")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1516,9 +1517,10 @@ async fn the_surface_speaks_the_tool_contract() {
|
|||||||
let response = builder.send().await.map_err(|e| e.to_string())?;
|
let response = builder.send().await.map_err(|e| e.to_string())?;
|
||||||
let status = response.status().as_u16();
|
let status = response.status().as_u16();
|
||||||
let body = response.json().await.unwrap_or(serde_json::json!({}));
|
let body = response.json().await.unwrap_or(serde_json::json!({}));
|
||||||
Ok(rhema_tools_contract::CallResponse { status, body })
|
Ok(rhema_claw_tools_contract::CallResponse { status, body })
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.await;
|
.await;
|
||||||
assert_eq!(result, Ok(()), "the daemon speaks the tool contract");
|
assert_eq!(result, Ok(()), "the daemon speaks the tool contract");
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+13
-13
@@ -466,8 +466,8 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"clap",
|
"clap",
|
||||||
|
"rhema-claw-tools-contract",
|
||||||
"rhema-tools-common",
|
"rhema-tools-common",
|
||||||
"rhema-tools-contract",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -525,18 +525,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rhema-tools-common"
|
name = "rhema-claw-tools-contract"
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"rhema-tools-contract",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
|
||||||
"ureq",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rhema-tools-contract"
|
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
@@ -544,6 +533,17 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rhema-tools-common"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rhema-claw-tools-contract",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"ureq",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ repository.workspace = true
|
|||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rhema-tools-contract = { path = "../contract" }
|
rhema-claw-tools-contract = { path = "../contract" }
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use rhema_tools_contract::{ApiError, CONTRACT_HEADER, CONTRACT_VERSION};
|
use rhema_claw_tools_contract::{ApiError, CONTRACT_HEADER, CONTRACT_VERSION};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rhema-tools-contract"
|
name = "rhema-claw-tools-contract"
|
||||||
description = "The wire contract between the rhema claw daemon and its thin client tools: routes, shapes, the agent-token allowlist, and the conformance suite"
|
description = "The wire contract between the rhema claw daemon and its thin client tools: routes, shapes, the agent-token allowlist, and the conformance suite"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//! The daemon's real router runs the same suite in its test tree —
|
//! The daemon's real router runs the same suite in its test tree —
|
||||||
//! same results is the definition of implementing the contract.
|
//! same results is the definition of implementing the contract.
|
||||||
|
|
||||||
use rhema_tools_contract::{CallRequest, MockClaw, conformance};
|
use rhema_claw_tools_contract::{CallRequest, MockClaw, conformance};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn the_suite_passes_against_the_mock_router() {
|
async fn the_suite_passes_against_the_mock_router() {
|
||||||
@@ -28,31 +28,40 @@ async fn an_open_mock_fails_the_suite() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn the_agent_allowlist_reads_the_documented_routes() {
|
async fn the_agent_allowlist_reads_the_documented_routes() {
|
||||||
assert!(rhema_tools_contract::agent_allowed("GET", "/v1/watches"));
|
assert!(rhema_claw_tools_contract::agent_allowed(
|
||||||
assert!(rhema_tools_contract::agent_allowed(
|
"GET",
|
||||||
|
"/v1/watches"
|
||||||
|
));
|
||||||
|
assert!(rhema_claw_tools_contract::agent_allowed(
|
||||||
"DELETE",
|
"DELETE",
|
||||||
"/v1/watches/github/acme/widgets"
|
"/v1/watches/github/acme/widgets"
|
||||||
));
|
));
|
||||||
assert!(rhema_tools_contract::agent_allowed(
|
assert!(rhema_claw_tools_contract::agent_allowed(
|
||||||
"POST",
|
"POST",
|
||||||
"/v1/notifications"
|
"/v1/notifications"
|
||||||
));
|
));
|
||||||
assert!(rhema_tools_contract::agent_allowed(
|
assert!(rhema_claw_tools_contract::agent_allowed(
|
||||||
"DELETE",
|
"DELETE",
|
||||||
"/v1/tasks/12"
|
"/v1/tasks/12"
|
||||||
));
|
));
|
||||||
assert!(!rhema_tools_contract::agent_allowed("POST", "/v1/ingest"));
|
assert!(!rhema_claw_tools_contract::agent_allowed(
|
||||||
assert!(!rhema_tools_contract::agent_allowed(
|
"POST",
|
||||||
|
"/v1/ingest"
|
||||||
|
));
|
||||||
|
assert!(!rhema_claw_tools_contract::agent_allowed(
|
||||||
"POST",
|
"POST",
|
||||||
"/v1/consents/c-1"
|
"/v1/consents/c-1"
|
||||||
));
|
));
|
||||||
assert!(!rhema_tools_contract::agent_allowed("GET", "/v1/consents"));
|
assert!(!rhema_claw_tools_contract::agent_allowed(
|
||||||
assert!(!rhema_tools_contract::agent_allowed(
|
"GET",
|
||||||
|
"/v1/consents"
|
||||||
|
));
|
||||||
|
assert!(!rhema_claw_tools_contract::agent_allowed(
|
||||||
"DELETE",
|
"DELETE",
|
||||||
"/v1/phones/p1"
|
"/v1/phones/p1"
|
||||||
));
|
));
|
||||||
assert!(
|
assert!(
|
||||||
!rhema_tools_contract::agent_allowed("GET", "/v1/watches/github/acme/widgets"),
|
!rhema_claw_tools_contract::agent_allowed("GET", "/v1/watches/github/acme/widgets"),
|
||||||
"only DELETE reaches subpaths of /v1/watches"
|
"only DELETE reaches subpaths of /v1/watches"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ path = "src/main.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rhema-tools-common = { path = "../common" }
|
rhema-tools-common = { path = "../common" }
|
||||||
rhema-tools-contract = { path = "../contract" }
|
rhema-claw-tools-contract = { path = "../contract" }
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use rhema_tools_common::{Client, Config, fail, print};
|
use rhema_tools_common::{Client, Config, fail, print};
|
||||||
use rhema_tools_contract::NotificationPush;
|
use rhema_claw_tools_contract::NotificationPush;
|
||||||
|
|
||||||
/// Push a notification to the user's devices.
|
/// Push a notification to the user's devices.
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
@@ -49,7 +49,7 @@ fn main() {
|
|||||||
body,
|
body,
|
||||||
device,
|
device,
|
||||||
};
|
};
|
||||||
match client.call("POST", rhema_tools_contract::NOTIFICATIONS, Some(&push)) {
|
match client.call("POST", rhema_claw_tools_contract::NOTIFICATIONS, Some(&push)) {
|
||||||
Ok(record) => print(&record),
|
Ok(record) => print(&record),
|
||||||
Err(error) => fail(error),
|
Err(error) => fail(error),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use axum::extract::State;
|
|||||||
use axum::http::{HeaderMap, StatusCode};
|
use axum::http::{HeaderMap, StatusCode};
|
||||||
use axum::routing::get;
|
use axum::routing::get;
|
||||||
use rhema_tools_common::{Client, Config, ToolError};
|
use rhema_tools_common::{Client, Config, ToolError};
|
||||||
use rhema_tools_contract::NotificationPush;
|
use rhema_claw_tools_contract::NotificationPush;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
|
|
||||||
/// What the fake daemon recorded, for the test to assert on.
|
/// What the fake daemon recorded, for the test to assert on.
|
||||||
@@ -82,7 +82,7 @@ async fn a_push_rides_the_contract_and_prints_the_row() {
|
|||||||
let row = client
|
let row = client
|
||||||
.call(
|
.call(
|
||||||
"POST",
|
"POST",
|
||||||
rhema_tools_contract::NOTIFICATIONS,
|
rhema_claw_tools_contract::NOTIFICATIONS,
|
||||||
Some(&NotificationPush {
|
Some(&NotificationPush {
|
||||||
title: "CI is red".into(),
|
title: "CI is red".into(),
|
||||||
body: "main failed".into(),
|
body: "main failed".into(),
|
||||||
@@ -111,7 +111,7 @@ async fn a_refused_call_maps_to_denied() {
|
|||||||
let error = client
|
let error = client
|
||||||
.call(
|
.call(
|
||||||
"POST",
|
"POST",
|
||||||
rhema_tools_contract::NOTIFICATIONS,
|
rhema_claw_tools_contract::NOTIFICATIONS,
|
||||||
Some(&NotificationPush {
|
Some(&NotificationPush {
|
||||||
title: "x".into(),
|
title: "x".into(),
|
||||||
body: "y".into(),
|
body: "y".into(),
|
||||||
|
|||||||
Reference in new issue
Block a user