From 845bd1aeb147e336cbe2e8543c932dda84d1edb4 Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Mon, 22 Dec 2025 21:25:06 +0100 Subject: [PATCH] Make /v1/ui a concept editor --- README.md | 8 +- registry/amduatd-api-contract.v1.json | 2 +- registry/api-contract.jsonl | 2 +- src/amduatd.c | 296 ++++++++++++++------------ 4 files changed, 163 insertions(+), 145 deletions(-) diff --git a/README.md b/README.md index 9ca28fd..87b4283 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Browser UI (use `socat` to forward the Unix socket): socat TCP-LISTEN:8080,fork,reuseaddr UNIX-CONNECT:amduatd.sock ``` -Then open `http://localhost:8080/v1/ui`. +Then open `http://localhost:8080/v1/ui` (concept editor). Discover the store-backed API contract ref: @@ -129,6 +129,12 @@ curl --unix-socket amduatd.sock http://localhost/v1/concepts curl --unix-socket amduatd.sock http://localhost/v1/concepts/hello ``` +Artifact info (length + type tag): + +```sh +curl --unix-socket amduatd.sock 'http://localhost/v1/artifacts/?format=info' +``` + ## Current endpoints - `GET /v1/meta` → `{store_id, encoding_profile_id, hash_id, api_contract_ref}` diff --git a/registry/amduatd-api-contract.v1.json b/registry/amduatd-api-contract.v1.json index adfd37e..dd2dfcc 100644 --- a/registry/amduatd-api-contract.v1.json +++ b/registry/amduatd-api-contract.v1.json @@ -1 +1 @@ -{"contract":"AMDUATD/API/1","base_path":"/v1","endpoints":[{"method":"GET","path":"/v1/ui"},{"method":"GET","path":"/v1/meta"},{"method":"HEAD","path":"/v1/meta"},{"method":"GET","path":"/v1/contract"},{"method":"POST","path":"/v1/concepts"},{"method":"GET","path":"/v1/concepts"},{"method":"GET","path":"/v1/concepts/{name}"},{"method":"POST","path":"/v1/concepts/{name}/publish"},{"method":"GET","path":"/v1/resolve/{name}"},{"method":"POST","path":"/v1/artifacts"},{"method":"GET","path":"/v1/artifacts/{ref}"},{"method":"HEAD","path":"/v1/artifacts/{ref}"},{"method":"POST","path":"/v1/pel/run"},{"method":"POST","path":"/v1/pel/programs"}],"schemas":{"pel_run_request":{"type":"object","required":["program_ref","input_refs"],"properties":{"program_ref":{"type":"string","description":"hex ref or concept name"},"input_refs":{"type":"array","items":{"type":"string","description":"hex ref or concept name"}},"params_ref":{"type":"string","description":"hex ref or concept name"},"scheme_ref":{"type":"string","description":"hex ref or 'dag'"}}},"pel_run_response":{"type":"object","required":["result_ref","output_refs","status"],"properties":{"result_ref":{"type":"string","description":"hex ref"},"trace_ref":{"type":"string","description":"hex ref"},"output_refs":{"type":"array","items":{"type":"string","description":"hex ref"}},"status":{"type":"string"}}},"pel_program_author_request":{"type":"object","required":["nodes","roots"],"properties":{"nodes":{"type":"array"},"roots":{"type":"array"}}},"concept_create_request":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"ref":{"type":"string","description":"hex ref"}}}}} +{"contract":"AMDUATD/API/1","base_path":"/v1","endpoints":[{"method":"GET","path":"/v1/ui"},{"method":"GET","path":"/v1/meta"},{"method":"HEAD","path":"/v1/meta"},{"method":"GET","path":"/v1/contract"},{"method":"POST","path":"/v1/concepts"},{"method":"GET","path":"/v1/concepts"},{"method":"GET","path":"/v1/concepts/{name}"},{"method":"POST","path":"/v1/concepts/{name}/publish"},{"method":"GET","path":"/v1/resolve/{name}"},{"method":"POST","path":"/v1/artifacts"},{"method":"GET","path":"/v1/artifacts/{ref}"},{"method":"HEAD","path":"/v1/artifacts/{ref}"},{"method":"GET","path":"/v1/artifacts/{ref}?format=info"},{"method":"POST","path":"/v1/pel/run"},{"method":"POST","path":"/v1/pel/programs"}],"schemas":{"pel_run_request":{"type":"object","required":["program_ref","input_refs"],"properties":{"program_ref":{"type":"string","description":"hex ref or concept name"},"input_refs":{"type":"array","items":{"type":"string","description":"hex ref or concept name"}},"params_ref":{"type":"string","description":"hex ref or concept name"},"scheme_ref":{"type":"string","description":"hex ref or 'dag'"}}},"pel_run_response":{"type":"object","required":["result_ref","output_refs","status"],"properties":{"result_ref":{"type":"string","description":"hex ref"},"trace_ref":{"type":"string","description":"hex ref"},"output_refs":{"type":"array","items":{"type":"string","description":"hex ref"}},"status":{"type":"string"}}},"pel_program_author_request":{"type":"object","required":["nodes","roots"],"properties":{"nodes":{"type":"array"},"roots":{"type":"array"}}},"concept_create_request":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"ref":{"type":"string","description":"hex ref"}}},"artifact_info_response":{"type":"object","required":["len","has_type_tag","type_tag"],"properties":{"len":{"type":"integer"},"has_type_tag":{"type":"boolean"},"type_tag":{"type":"string"}}}}} diff --git a/registry/api-contract.jsonl b/registry/api-contract.jsonl index 8117f1b..64f4b71 100644 --- a/registry/api-contract.jsonl +++ b/registry/api-contract.jsonl @@ -1 +1 @@ -{"registry":"AMDUATD/API","contract":"AMDUATD/API/1","handle":"amduat.api.amduatd.contract.v1@1","media_type":"application/json","status":"active","bytes_sha256":"3c8ef7489b0499ebb6e8025981c6f43962568d554d63d6994d75e43812e17feb","notes":"Seeded into the ASL store at amduatd startup; ref is advertised via /v1/meta."} +{"registry":"AMDUATD/API","contract":"AMDUATD/API/1","handle":"amduat.api.amduatd.contract.v1@1","media_type":"application/json","status":"active","bytes_sha256":"0072ad1a308bfa52c7578a1ff4fbfc85b662b41f37a839f4390bdb4c24ecef0c","notes":"Seeded into the ASL store at amduatd startup; ref is advertised via /v1/meta."} diff --git a/src/amduatd.c b/src/amduatd.c index 9fc9f71..be61cd5 100644 --- a/src/amduatd.c +++ b/src/amduatd.c @@ -143,6 +143,7 @@ static const char k_amduatd_contract_v1_json[] = "{\"method\":\"POST\",\"path\":\"/v1/artifacts\"}," "{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}\"}," "{\"method\":\"HEAD\",\"path\":\"/v1/artifacts/{ref}\"}," + "{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}?format=info\"}," "{\"method\":\"POST\",\"path\":\"/v1/pel/run\"}," "{\"method\":\"POST\",\"path\":\"/v1/pel/programs\"}" "]," @@ -184,82 +185,76 @@ static const char k_amduatd_ui_html[] = "\n" " \n" " \n" - " amduatd — PEL Program Builder\n" + " amduatd — Concept editor\n" " \n" "\n" "\n" "
\n" - "

amduatd — PEL/PROGRAM-DAG/1 builder

\n" - "
POSTs to /v1/pel/programs and /v1/pel/run on this daemon.
\n" + "

amduatd — Concept editor

\n" + "
Load shows the latest materialized bytes; Save uploads a new artifact and publishes it as a new version.
\n" "
\n" "
\n" "
\n" "
\n" - "
\n" - "
Program authoring JSON
\n" - "
\n" - " \n" - " \n" - "
\n" + "
\n" + " \n" + " \n" + " \n" + " \n" + "
\n" + "
\n" + " \n" + " \n" + " \n" + "
\n" + " \n" + "
\n" + " \n" + " \n" "
\n" - " \n" "
\n" "\n" "
\n" - "
Run
\n" - "
program_ref
\n" - " \" />\n" - "
input_refs (comma-separated hex refs)
\n" - " ,,...\" />\n" - "
params_ref (optional)
\n" - " \" />\n" - "
scheme_ref (optional, default dag)
\n" - " \n" - "
Concept
\n" - "
name (lowercase, [a-z0-9_./-])
\n" - " \n" - "
\n" - " \n" - " \n" - " \n" - "
\n" - "
Upload bytes (sets program_ref)
\n" + "
Upload bytes (sets program_ref)
\n" "
\n" " \n" - " \n" + " \n" "
\n" + "
\n" + "
Run
\n" + " \n" + "
input_refs (comma-separated hex refs or names)
\n" + " \n" + "
params_ref (optional)
\n" + " \n" + "
scheme_ref (optional, default dag)
\n" + " \n" "
\n" - " \n" + " \n" " /v1/contract\n" " /v1/meta\n" "
\n" @@ -272,98 +267,87 @@ static const char k_amduatd_ui_html[] = " \n" "\n" "\n"; @@ -2190,6 +2166,7 @@ static bool amduatd_handle_get_artifact(int fd, amduat_artifact_t artifact; amduat_asl_store_error_t err; bool want_artifact = false; + bool want_info = false; memset(&ref, 0, sizeof(ref)); memset(&artifact, 0, sizeof(artifact)); @@ -2214,8 +2191,13 @@ static bool amduatd_handle_get_artifact(int fd, if (amduatd_query_param(path, "format", format, sizeof(format)) != NULL) { if (strcmp(format, "artifact") == 0) { want_artifact = true; + want_info = false; + } else if (strcmp(format, "info") == 0) { + want_artifact = false; + want_info = true; } else if (strcmp(format, "raw") == 0 || format[0] == '\0') { want_artifact = false; + want_info = false; } else { free((void *)ref.digest.data); return amduatd_http_send_text(fd, 400, "Bad Request", @@ -2238,6 +2220,36 @@ static bool amduatd_handle_get_artifact(int fd, } if (!want_artifact) { + if (want_info) { + char json[256]; + int n; + if (artifact.has_type_tag) { + n = snprintf(json, + sizeof(json), + "{" + "\"len\":%zu," + "\"has_type_tag\":true," + "\"type_tag\":\"0x%08x\"" + "}\n", + artifact.bytes.len, + (unsigned int)artifact.type_tag.tag_id); + } else { + n = snprintf(json, + sizeof(json), + "{" + "\"len\":%zu," + "\"has_type_tag\":false," + "\"type_tag\":null" + "}\n", + artifact.bytes.len); + } + amduat_asl_artifact_free(&artifact); + if (n <= 0 || (size_t)n >= sizeof(json)) { + return amduatd_http_send_text(fd, 500, "Internal Server Error", + "error\n", head_only); + } + return amduatd_http_send_json(fd, 200, "OK", json, head_only); + } bool ok = amduatd_http_send_status( fd, 200,