summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-02-10 23:29:43 +0000
committersoryu <soryu@soryu.co>2026-02-10 23:29:43 +0000
commita1ecbc23f1f289ba312e7a8d8c6e92c9d1e81ecf (patch)
tree32857278937db5cab13bceea86acaac5f0fe743d
parent339c1769379a851c4126021132573bd4b7994cf2 (diff)
downloadsoryu-makima/makima--add-an-optional-memory-system-for-directiv-e123bc35.tar.gz
soryu-makima/makima--add-an-optional-memory-system-for-directiv-e123bc35.zip
-rw-r--r--makima/src/daemon/api/directive.rs117
1 files changed, 117 insertions, 0 deletions
diff --git a/makima/src/daemon/api/directive.rs b/makima/src/daemon/api/directive.rs
index cd21692..1ca099b 100644
--- a/makima/src/daemon/api/directive.rs
+++ b/makima/src/daemon/api/directive.rs
@@ -30,6 +30,47 @@ pub struct UpdateStepDepsRequest {
pub depends_on: Vec<Uuid>,
}
+/// Percent-encode a string for use as a URL path segment.
+///
+/// Encodes all characters except unreserved characters (alphanumeric, `-`, `.`, `_`, `~`).
+fn percent_encode_path(s: &str) -> String {
+ let mut encoded = String::with_capacity(s.len());
+ for byte in s.bytes() {
+ match byte {
+ b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
+ encoded.push(byte as char);
+ }
+ _ => {
+ encoded.push_str(&format!("%{:02X}", byte));
+ }
+ }
+ }
+ encoded
+}
+
+/// Request body for setting a single memory entry.
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SetMemoryRequest {
+ pub key: String,
+ pub value: String,
+}
+
+/// A single entry within a batch set request.
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BatchMemoryEntry {
+ pub key: String,
+ pub value: String,
+}
+
+/// Request body for setting multiple memory entries at once.
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BatchSetMemoryRequest {
+ pub entries: Vec<BatchMemoryEntry>,
+}
+
impl ApiClient {
/// List all directives.
pub async fn list_directives(&self) -> Result<JsonValue, ApiError> {
@@ -134,4 +175,80 @@ impl ApiClient {
let req = UpdateGoalRequest { goal: goal.to_string() };
self.put(&format!("/api/v1/directives/{}/goal", directive_id), &req).await
}
+
+ // ── Directive Memory ──────────────────────────────────────────────
+
+ /// List all memory entries for a directive.
+ pub async fn list_memories(&self, directive_id: Uuid) -> Result<JsonValue, ApiError> {
+ self.get(&format!("/api/v1/directives/{}/memory", directive_id))
+ .await
+ }
+
+ /// Get a single memory entry by key.
+ pub async fn get_memory(
+ &self,
+ directive_id: Uuid,
+ key: &str,
+ ) -> Result<JsonValue, ApiError> {
+ self.get(&format!(
+ "/api/v1/directives/{}/memory/{}",
+ directive_id,
+ percent_encode_path(key)
+ ))
+ .await
+ }
+
+ /// Set (create or update) a single memory entry.
+ pub async fn set_memory(
+ &self,
+ directive_id: Uuid,
+ key: &str,
+ value: &str,
+ ) -> Result<JsonValue, ApiError> {
+ let req = SetMemoryRequest {
+ key: key.to_string(),
+ value: value.to_string(),
+ };
+ self.put(&format!("/api/v1/directives/{}/memory", directive_id), &req)
+ .await
+ }
+
+ /// Set multiple memory entries in a single request.
+ pub async fn batch_set_memories(
+ &self,
+ directive_id: Uuid,
+ entries: Vec<(String, String)>,
+ ) -> Result<JsonValue, ApiError> {
+ let req = BatchSetMemoryRequest {
+ entries: entries
+ .into_iter()
+ .map(|(key, value)| BatchMemoryEntry { key, value })
+ .collect(),
+ };
+ self.post(
+ &format!("/api/v1/directives/{}/memory/batch", directive_id),
+ &req,
+ )
+ .await
+ }
+
+ /// Delete a single memory entry by key.
+ pub async fn delete_memory(
+ &self,
+ directive_id: Uuid,
+ key: &str,
+ ) -> Result<(), ApiError> {
+ self.delete(&format!(
+ "/api/v1/directives/{}/memory/{}",
+ directive_id,
+ percent_encode_path(key)
+ ))
+ .await
+ }
+
+ /// Clear all memory entries for a directive.
+ pub async fn clear_memories(&self, directive_id: Uuid) -> Result<(), ApiError> {
+ self.delete(&format!("/api/v1/directives/{}/memory", directive_id))
+ .await
+ }
}