Make /v1/ui a concept editor
This commit is contained in:
parent
ccfee235a9
commit
845bd1aeb1
|
|
@ -49,7 +49,7 @@ Browser UI (use `socat` to forward the Unix socket):
|
||||||
socat TCP-LISTEN:8080,fork,reuseaddr UNIX-CONNECT:amduatd.sock
|
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:
|
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
|
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/<ref>?format=info'
|
||||||
|
```
|
||||||
|
|
||||||
## Current endpoints
|
## Current endpoints
|
||||||
|
|
||||||
- `GET /v1/meta` → `{store_id, encoding_profile_id, hash_id, api_contract_ref}`
|
- `GET /v1/meta` → `{store_id, encoding_profile_id, hash_id, api_contract_ref}`
|
||||||
|
|
|
||||||
|
|
@ -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"}}}}}
|
||||||
|
|
|
||||||
|
|
@ -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."}
|
||||||
|
|
|
||||||
290
src/amduatd.c
290
src/amduatd.c
|
|
@ -143,6 +143,7 @@ static const char k_amduatd_contract_v1_json[] =
|
||||||
"{\"method\":\"POST\",\"path\":\"/v1/artifacts\"},"
|
"{\"method\":\"POST\",\"path\":\"/v1/artifacts\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}\"},"
|
||||||
"{\"method\":\"HEAD\",\"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/run\"},"
|
||||||
"{\"method\":\"POST\",\"path\":\"/v1/pel/programs\"}"
|
"{\"method\":\"POST\",\"path\":\"/v1/pel/programs\"}"
|
||||||
"],"
|
"],"
|
||||||
|
|
@ -184,82 +185,76 @@ static const char k_amduatd_ui_html[] =
|
||||||
"<head>\n"
|
"<head>\n"
|
||||||
" <meta charset=\"utf-8\" />\n"
|
" <meta charset=\"utf-8\" />\n"
|
||||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
|
||||||
" <title>amduatd — PEL Program Builder</title>\n"
|
" <title>amduatd — Concept editor</title>\n"
|
||||||
" <style>\n"
|
" <style>\n"
|
||||||
" :root{color-scheme:light dark;}\n"
|
" :root{color-scheme:light dark;}\n"
|
||||||
" body{font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif;"
|
" body{font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif;margin:0;line-height:1.35;}\n"
|
||||||
" margin:0;line-height:1.35;}\n"
|
" header,main{max-width:1100px;margin:0 auto;padding:18px;}\n"
|
||||||
" header{padding:18px 18px 8px;max-width:1100px;margin:0 auto;}\n"
|
" h1{margin:0 0 6px;font-size:18px;}\n"
|
||||||
" main{padding:0 18px 24px;max-width:1100px;margin:0 auto;}\n"
|
|
||||||
" h1{margin:0 0 4px;font-size:18px;}\n"
|
|
||||||
" .muted{opacity:.75;font-size:13px;}\n"
|
" .muted{opacity:.75;font-size:13px;}\n"
|
||||||
" .grid{display:grid;grid-template-columns:1fr;gap:14px;}\n"
|
" .grid{display:grid;grid-template-columns:1fr;gap:14px;}\n"
|
||||||
" @media (min-width: 980px){.grid{grid-template-columns: 1.2fr .8fr;}}\n"
|
" @media (min-width: 980px){.grid{grid-template-columns:1.2fr .8fr;}}\n"
|
||||||
" .card{border:1px solid rgba(127,127,127,.35);border-radius:14px;padding:14px;"
|
" .card{border:1px solid rgba(127,127,127,.35);border-radius:14px;padding:14px;background:rgba(127,127,127,.06);}\n"
|
||||||
" background:rgba(127,127,127,.06);}\n"
|
" textarea,input,select{width:100%;box-sizing:border-box;border-radius:10px;padding:10px;border:1px solid rgba(127,127,127,.35);background:transparent;"
|
||||||
" textarea{width:100%;min-height:380px;resize:vertical;box-sizing:border-box;"
|
" font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:12.5px;}\n"
|
||||||
" font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
" textarea{min-height:420px;resize:vertical;}\n"
|
||||||
" font-size:12.5px;line-height:1.35;border-radius:10px;padding:10px;"
|
" button{border-radius:10px;padding:10px 12px;border:1px solid rgba(127,127,127,.35);background:rgba(127,127,127,.12);cursor:pointer;}\n"
|
||||||
" border:1px solid rgba(127,127,127,.35);background:transparent;}\n"
|
|
||||||
" input{width:100%;box-sizing:border-box;border-radius:10px;padding:10px;"
|
|
||||||
" border:1px solid rgba(127,127,127,.35);background:transparent;"
|
|
||||||
" font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
|
||||||
" font-size:12.5px;}\n"
|
|
||||||
" button{border-radius:10px;padding:10px 12px;border:1px solid rgba(127,127,127,.35);"
|
|
||||||
" background:rgba(127,127,127,.12);cursor:pointer;}\n"
|
|
||||||
" button:hover{background:rgba(127,127,127,.18);}\n"
|
" button:hover{background:rgba(127,127,127,.18);}\n"
|
||||||
" .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}\n"
|
" .row{display:flex;gap:10px;flex-wrap:wrap;align-items:center;}\n"
|
||||||
" .row button{flex:0 0 auto;}\n"
|
" .row > *{flex:1 1 auto;}\n"
|
||||||
" pre{white-space:pre-wrap;word-break:break-word;margin:0;padding:10px;border-radius:10px;"
|
" .row .btn{flex:0 0 auto;}\n"
|
||||||
" border:1px solid rgba(127,127,127,.35);background:rgba(0,0,0,.08);"
|
" pre{white-space:pre-wrap;word-break:break-word;margin:0;padding:10px;border-radius:10px;border:1px solid rgba(127,127,127,.35);background:rgba(0,0,0,.08);"
|
||||||
" font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
" font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;font-size:12.5px;min-height:120px;}\n"
|
||||||
" font-size:12.5px;min-height:120px;}\n"
|
|
||||||
" a{color:inherit;}\n"
|
" a{color:inherit;}\n"
|
||||||
" </style>\n"
|
" </style>\n"
|
||||||
"</head>\n"
|
"</head>\n"
|
||||||
"<body>\n"
|
"<body>\n"
|
||||||
" <header>\n"
|
" <header>\n"
|
||||||
" <h1>amduatd — PEL/PROGRAM-DAG/1 builder</h1>\n"
|
" <h1>amduatd — Concept editor</h1>\n"
|
||||||
" <div class=\"muted\">POSTs to <code>/v1/pel/programs</code> and <code>/v1/pel/run</code> on this daemon.</div>\n"
|
" <div class=\"muted\">Load shows the latest materialized bytes; Save uploads a new artifact and publishes it as a new version.</div>\n"
|
||||||
" </header>\n"
|
" </header>\n"
|
||||||
" <main>\n"
|
" <main>\n"
|
||||||
" <div class=\"grid\">\n"
|
" <div class=\"grid\">\n"
|
||||||
" <div class=\"card\">\n"
|
" <div class=\"card\">\n"
|
||||||
" <div class=\"row\" style=\"justify-content:space-between;\">\n"
|
|
||||||
" <div class=\"muted\">Program authoring JSON</div>\n"
|
|
||||||
" <div class=\"row\">\n"
|
" <div class=\"row\">\n"
|
||||||
" <button id=\"btnTemplate\" type=\"button\">Insert template</button>\n"
|
" <input id=\"conceptName\" placeholder=\"concept name (e.g. hello)\" />\n"
|
||||||
" <button id=\"btnCreate\" type=\"button\">Create program</button>\n"
|
" <button class=\"btn\" id=\"btnConceptCreate\" type=\"button\">Create</button>\n"
|
||||||
|
" <button class=\"btn\" id=\"btnLoad\" type=\"button\">Load</button>\n"
|
||||||
|
" <button class=\"btn\" id=\"btnSave\" type=\"button\">Save</button>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
|
" <div class=\"row\" style=\"margin-top:10px;\">\n"
|
||||||
|
" <select id=\"mode\">\n"
|
||||||
|
" <option value=\"text\">text (utf-8)</option>\n"
|
||||||
|
" <option value=\"base64\">base64</option>\n"
|
||||||
|
" <option value=\"hex\">hex</option>\n"
|
||||||
|
" </select>\n"
|
||||||
|
" <input id=\"typeTag\" placeholder=\"X-Amduat-Type-Tag (optional, e.g. 0x00000101)\" />\n"
|
||||||
|
" <input id=\"latestRef\" placeholder=\"latest_ref\" readonly />\n"
|
||||||
|
" </div>\n"
|
||||||
|
" <textarea id=\"editor\" spellcheck=\"false\" placeholder=\"(concept bytes)\"></textarea>\n"
|
||||||
|
" <div class=\"row\" style=\"margin-top:10px;\">\n"
|
||||||
|
" <input id=\"publishRef\" placeholder=\"publish existing ref\" />\n"
|
||||||
|
" <button class=\"btn\" id=\"btnPublishRef\" type=\"button\">Publish ref</button>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
" <textarea id=\"program\" spellcheck=\"false\"></textarea>\n"
|
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
"\n"
|
"\n"
|
||||||
" <div class=\"card\">\n"
|
" <div class=\"card\">\n"
|
||||||
" <div class=\"muted\" style=\"margin-bottom:8px;\">Run</div>\n"
|
" <div class=\"muted\" style=\"margin-bottom:8px;\">Upload bytes (sets program_ref)</div>\n"
|
||||||
" <div style=\"margin-bottom:8px;\" class=\"muted\">program_ref</div>\n"
|
|
||||||
" <input id=\"programRef\" placeholder=\"<program_ref>\" />\n"
|
|
||||||
" <div style=\"margin:10px 0 8px;\" class=\"muted\">input_refs (comma-separated hex refs)</div>\n"
|
|
||||||
" <input id=\"inputRefs\" placeholder=\"<ref0>,<ref1>,...\" />\n"
|
|
||||||
" <div style=\"margin:10px 0 8px;\" class=\"muted\">params_ref (optional)</div>\n"
|
|
||||||
" <input id=\"paramsRef\" placeholder=\"<params_ref>\" />\n"
|
|
||||||
" <div style=\"margin:10px 0 8px;\" class=\"muted\">scheme_ref (optional, default dag)</div>\n"
|
|
||||||
" <input id=\"schemeRef\" placeholder=\"dag\" />\n"
|
|
||||||
" <div class=\"muted\" style=\"margin:14px 0 8px;\">Concept</div>\n"
|
|
||||||
" <div style=\"margin-bottom:8px;\" class=\"muted\">name (lowercase, [a-z0-9_./-])</div>\n"
|
|
||||||
" <input id=\"conceptName\" placeholder=\"hello\" />\n"
|
|
||||||
" <div class=\"row\" style=\"margin-top:10px;\">\n"
|
|
||||||
" <button id=\"btnConceptCreate\" type=\"button\">Create concept</button>\n"
|
|
||||||
" <button id=\"btnConceptPublish\" type=\"button\">Publish program_ref</button>\n"
|
|
||||||
" <button id=\"btnConceptLoad\" type=\"button\">Load</button>\n"
|
|
||||||
" </div>\n"
|
|
||||||
" <div style=\"margin:10px 0 8px;\" class=\"muted\">Upload bytes (sets program_ref)</div>\n"
|
|
||||||
" <div class=\"row\">\n"
|
" <div class=\"row\">\n"
|
||||||
" <input id=\"uploadFile\" type=\"file\" />\n"
|
" <input id=\"uploadFile\" type=\"file\" />\n"
|
||||||
" <button id=\"btnUpload\" type=\"button\">Upload</button>\n"
|
" <button class=\"btn\" id=\"btnUpload\" type=\"button\">Upload</button>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
|
" <hr style=\"border:none;border-top:1px solid rgba(127,127,127,.35);margin:14px 0;\" />\n"
|
||||||
|
" <div class=\"muted\" style=\"margin-bottom:8px;\">Run</div>\n"
|
||||||
|
" <input id=\"programRef\" placeholder=\"program_ref (hex ref or concept name)\" />\n"
|
||||||
|
" <div class=\"muted\" style=\"margin:10px 0 8px;\">input_refs (comma-separated hex refs or names)</div>\n"
|
||||||
|
" <input id=\"inputRefs\" placeholder=\"in0,in1,...\" />\n"
|
||||||
|
" <div class=\"muted\" style=\"margin:10px 0 8px;\">params_ref (optional)</div>\n"
|
||||||
|
" <input id=\"paramsRef\" placeholder=\"params\" />\n"
|
||||||
|
" <div class=\"muted\" style=\"margin:10px 0 8px;\">scheme_ref (optional, default dag)</div>\n"
|
||||||
|
" <input id=\"schemeRef\" placeholder=\"dag\" />\n"
|
||||||
" <div class=\"row\" style=\"margin-top:12px;\">\n"
|
" <div class=\"row\" style=\"margin-top:12px;\">\n"
|
||||||
" <button id=\"btnRun\" type=\"button\">Run program</button>\n"
|
" <button class=\"btn\" id=\"btnRun\" type=\"button\">Run</button>\n"
|
||||||
" <a class=\"muted\" href=\"/v1/contract\">/v1/contract</a>\n"
|
" <a class=\"muted\" href=\"/v1/contract\">/v1/contract</a>\n"
|
||||||
" <a class=\"muted\" href=\"/v1/meta\">/v1/meta</a>\n"
|
" <a class=\"muted\" href=\"/v1/meta\">/v1/meta</a>\n"
|
||||||
" </div>\n"
|
" </div>\n"
|
||||||
|
|
@ -272,98 +267,87 @@ static const char k_amduatd_ui_html[] =
|
||||||
" <script>\n"
|
" <script>\n"
|
||||||
" const el = (id) => document.getElementById(id);\n"
|
" const el = (id) => document.getElementById(id);\n"
|
||||||
" const out = (v) => { el('out').textContent = typeof v === 'string' ? v : JSON.stringify(v, null, 2); };\n"
|
" const out = (v) => { el('out').textContent = typeof v === 'string' ? v : JSON.stringify(v, null, 2); };\n"
|
||||||
" const template = {\n"
|
" const td = new TextDecoder('utf-8');\n"
|
||||||
" nodes: [\n"
|
" const te = new TextEncoder();\n"
|
||||||
" {\n"
|
" const toHex = (u8) => Array.from(u8).map(b => b.toString(16).padStart(2,'0')).join('');\n"
|
||||||
" id: 1,\n"
|
" const fromHex = (s) => { const t=(s||'').trim(); if(t.length%2) throw new Error('hex length must be even'); const o=new Uint8Array(t.length/2); for(let i=0;i<o.length;i++){o[i]=parseInt(t.slice(i*2,i*2+2),16);} return o; };\n"
|
||||||
" op: { name: 'pel.bytes.concat', version: 1 },\n"
|
" const toB64 = (u8) => { let bin=''; for(let i=0;i<u8.length;i++) bin += String.fromCharCode(u8[i]); return btoa(bin); };\n"
|
||||||
" inputs: [ { external: { input_index: 0 } }, { external: { input_index: 1 } } ],\n"
|
" const fromB64 = (s) => { const bin=atob((s||'').trim()); const o=new Uint8Array(bin.length); for(let i=0;i<bin.length;i++) o[i]=bin.charCodeAt(i); return o; };\n"
|
||||||
" params_hex: ''\n"
|
|
||||||
" }\n"
|
|
||||||
" ],\n"
|
|
||||||
" roots: [ { node_id: 1, output_index: 0 } ]\n"
|
|
||||||
" };\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" el('btnTemplate').addEventListener('click', () => {\n"
|
" async function ensureConcept(name){\n"
|
||||||
" el('program').value = JSON.stringify(template, null, 2);\n"
|
" const resp = await fetch('/v1/concepts',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name})});\n"
|
||||||
" });\n"
|
" if(!resp.ok) throw new Error(await resp.text());\n"
|
||||||
"\n"
|
|
||||||
" el('btnCreate').addEventListener('click', async () => {\n"
|
|
||||||
" try {\n"
|
|
||||||
" const body = JSON.parse(el('program').value || '{}');\n"
|
|
||||||
" const resp = await fetch('/v1/pel/programs', {\n"
|
|
||||||
" method: 'POST',\n"
|
|
||||||
" headers: { 'Content-Type': 'application/json' },\n"
|
|
||||||
" body: JSON.stringify(body)\n"
|
|
||||||
" });\n"
|
|
||||||
" const text = await resp.text();\n"
|
|
||||||
" out(text);\n"
|
|
||||||
" if (resp.ok) {\n"
|
|
||||||
" const j = JSON.parse(text);\n"
|
|
||||||
" if (j && j.program_ref) el('programRef').value = j.program_ref;\n"
|
|
||||||
" }\n"
|
" }\n"
|
||||||
" } catch (e) {\n"
|
|
||||||
" out(String(e));\n"
|
|
||||||
" }\n"
|
|
||||||
" });\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" el('btnConceptCreate').addEventListener('click', async () => {\n"
|
" async function loadConcept(){\n"
|
||||||
" try {\n"
|
|
||||||
" const name = el('conceptName').value.trim();\n"
|
|
||||||
" const resp = await fetch('/v1/concepts', {\n"
|
|
||||||
" method: 'POST',\n"
|
|
||||||
" headers: { 'Content-Type': 'application/json' },\n"
|
|
||||||
" body: JSON.stringify({ name })\n"
|
|
||||||
" });\n"
|
|
||||||
" out(await resp.text());\n"
|
|
||||||
" } catch (e) {\n"
|
|
||||||
" out(String(e));\n"
|
|
||||||
" }\n"
|
|
||||||
" });\n"
|
|
||||||
"\n"
|
|
||||||
" el('btnConceptPublish').addEventListener('click', async () => {\n"
|
|
||||||
" try {\n"
|
|
||||||
" const name = el('conceptName').value.trim();\n"
|
|
||||||
" const ref = el('programRef').value.trim();\n"
|
|
||||||
" const resp = await fetch(`/v1/concepts/${encodeURIComponent(name)}/publish`, {\n"
|
|
||||||
" method: 'POST',\n"
|
|
||||||
" headers: { 'Content-Type': 'application/json' },\n"
|
|
||||||
" body: JSON.stringify({ ref })\n"
|
|
||||||
" });\n"
|
|
||||||
" out(await resp.text());\n"
|
|
||||||
" } catch (e) {\n"
|
|
||||||
" out(String(e));\n"
|
|
||||||
" }\n"
|
|
||||||
" });\n"
|
|
||||||
"\n"
|
|
||||||
" el('btnConceptLoad').addEventListener('click', async () => {\n"
|
|
||||||
" try {\n"
|
|
||||||
" const name = el('conceptName').value.trim();\n"
|
" const name = el('conceptName').value.trim();\n"
|
||||||
|
" if(!name){ out('missing concept name'); return; }\n"
|
||||||
" const resp = await fetch(`/v1/concepts/${encodeURIComponent(name)}`);\n"
|
" const resp = await fetch(`/v1/concepts/${encodeURIComponent(name)}`);\n"
|
||||||
" out(await resp.text());\n"
|
" const text = await resp.text();\n"
|
||||||
" } catch (e) {\n"
|
" if(!resp.ok){ out(text); return; }\n"
|
||||||
" out(String(e));\n"
|
" const j = JSON.parse(text);\n"
|
||||||
|
" el('latestRef').value = j.latest_ref || '';\n"
|
||||||
|
" el('programRef').value = name;\n"
|
||||||
|
" if(!j.latest_ref){ el('editor').value=''; out(text); return; }\n"
|
||||||
|
" const infoResp = await fetch(`/v1/artifacts/${j.latest_ref}?format=info`);\n"
|
||||||
|
" if(infoResp.ok){ const info = JSON.parse(await infoResp.text()); el('typeTag').value = info.has_type_tag ? info.type_tag : ''; }\n"
|
||||||
|
" const aResp = await fetch(`/v1/artifacts/${j.latest_ref}`);\n"
|
||||||
|
" if(!aResp.ok){ out(await aResp.text()); return; }\n"
|
||||||
|
" const u8 = new Uint8Array(await aResp.arrayBuffer());\n"
|
||||||
|
" const mode = el('mode').value;\n"
|
||||||
|
" if(mode==='hex') el('editor').value = toHex(u8);\n"
|
||||||
|
" else if(mode==='base64') el('editor').value = toB64(u8);\n"
|
||||||
|
" else el('editor').value = td.decode(u8);\n"
|
||||||
|
" out(text);\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
"\n"
|
||||||
|
" async function saveConcept(){\n"
|
||||||
|
" const name = el('conceptName').value.trim();\n"
|
||||||
|
" if(!name){ out('missing concept name'); return; }\n"
|
||||||
|
" await ensureConcept(name);\n"
|
||||||
|
" const mode = el('mode').value;\n"
|
||||||
|
" let u8;\n"
|
||||||
|
" if(mode==='hex') u8 = fromHex(el('editor').value);\n"
|
||||||
|
" else if(mode==='base64') u8 = fromB64(el('editor').value);\n"
|
||||||
|
" else u8 = te.encode(el('editor').value);\n"
|
||||||
|
" const headers = {'Content-Type':'application/octet-stream'};\n"
|
||||||
|
" const typeTag = el('typeTag').value.trim();\n"
|
||||||
|
" if(typeTag) headers['X-Amduat-Type-Tag'] = typeTag;\n"
|
||||||
|
" const putResp = await fetch('/v1/artifacts',{method:'POST',headers,body:u8});\n"
|
||||||
|
" const putText = await putResp.text();\n"
|
||||||
|
" if(!putResp.ok){ out(putText); return; }\n"
|
||||||
|
" const put = JSON.parse(putText);\n"
|
||||||
|
" const pubResp = await fetch(`/v1/concepts/${encodeURIComponent(name)}/publish`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ref:put.ref})});\n"
|
||||||
|
" const pubText = await pubResp.text();\n"
|
||||||
|
" out(pubText);\n"
|
||||||
|
" if(pubResp.ok) await loadConcept();\n"
|
||||||
|
" }\n"
|
||||||
|
"\n"
|
||||||
|
" el('btnConceptCreate').addEventListener('click', async () => { try{ await ensureConcept(el('conceptName').value.trim()); out('{\"ok\":true}\\n'); }catch(e){ out(String(e)); } });\n"
|
||||||
|
" el('btnLoad').addEventListener('click', () => loadConcept().catch(e => out(String(e))));\n"
|
||||||
|
" el('btnSave').addEventListener('click', () => saveConcept().catch(e => out(String(e))));\n"
|
||||||
|
" el('mode').addEventListener('change', () => loadConcept().catch(() => {}));\n"
|
||||||
|
"\n"
|
||||||
|
" el('btnPublishRef').addEventListener('click', async () => {\n"
|
||||||
|
" try{\n"
|
||||||
|
" const name = el('conceptName').value.trim();\n"
|
||||||
|
" const ref = el('publishRef').value.trim();\n"
|
||||||
|
" if(!name||!ref){ out('missing name/ref'); return; }\n"
|
||||||
|
" await ensureConcept(name);\n"
|
||||||
|
" const resp = await fetch(`/v1/concepts/${encodeURIComponent(name)}/publish`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({ref})});\n"
|
||||||
|
" out(await resp.text());\n"
|
||||||
|
" }catch(e){ out(String(e)); }\n"
|
||||||
" });\n"
|
" });\n"
|
||||||
"\n"
|
"\n"
|
||||||
" el('btnUpload').addEventListener('click', async () => {\n"
|
" el('btnUpload').addEventListener('click', async () => {\n"
|
||||||
" try {\n"
|
" try {\n"
|
||||||
" const file = el('uploadFile').files && el('uploadFile').files[0];\n"
|
" const file = el('uploadFile').files && el('uploadFile').files[0];\n"
|
||||||
" if (!file) { out('no file selected'); return; }\n"
|
" if (!file) { out('no file selected'); return; }\n"
|
||||||
" const resp = await fetch('/v1/artifacts', {\n"
|
" const resp = await fetch('/v1/artifacts', { method:'POST', headers:{'Content-Type':'application/octet-stream'}, body:file });\n"
|
||||||
" method: 'POST',\n"
|
|
||||||
" headers: { 'Content-Type': 'application/octet-stream' },\n"
|
|
||||||
" body: file\n"
|
|
||||||
" });\n"
|
|
||||||
" const text = await resp.text();\n"
|
" const text = await resp.text();\n"
|
||||||
" out(text);\n"
|
" out(text);\n"
|
||||||
" if (resp.ok) {\n"
|
" if (resp.ok) { const j = JSON.parse(text); if (j && j.ref) el('programRef').value = j.ref; }\n"
|
||||||
" const j = JSON.parse(text);\n"
|
" } catch (e) { out(String(e)); }\n"
|
||||||
" if (j && j.ref) el('programRef').value = j.ref;\n"
|
|
||||||
" }\n"
|
|
||||||
" } catch (e) {\n"
|
|
||||||
" out(String(e));\n"
|
|
||||||
" }\n"
|
|
||||||
" });\n"
|
" });\n"
|
||||||
"\n"
|
"\n"
|
||||||
" el('btnRun').addEventListener('click', async () => {\n"
|
" el('btnRun').addEventListener('click', async () => {\n"
|
||||||
|
|
@ -375,18 +359,10 @@ static const char k_amduatd_ui_html[] =
|
||||||
" const body = { program_ref, input_refs };\n"
|
" const body = { program_ref, input_refs };\n"
|
||||||
" if (params_ref) body.params_ref = params_ref;\n"
|
" if (params_ref) body.params_ref = params_ref;\n"
|
||||||
" if (scheme_ref) body.scheme_ref = scheme_ref;\n"
|
" if (scheme_ref) body.scheme_ref = scheme_ref;\n"
|
||||||
" const resp = await fetch('/v1/pel/run', {\n"
|
" const resp = await fetch('/v1/pel/run', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body) });\n"
|
||||||
" method: 'POST',\n"
|
|
||||||
" headers: { 'Content-Type': 'application/json' },\n"
|
|
||||||
" body: JSON.stringify(body)\n"
|
|
||||||
" });\n"
|
|
||||||
" out(await resp.text());\n"
|
" out(await resp.text());\n"
|
||||||
" } catch (e) {\n"
|
" } catch (e) { out(String(e)); }\n"
|
||||||
" out(String(e));\n"
|
|
||||||
" }\n"
|
|
||||||
" });\n"
|
" });\n"
|
||||||
"\n"
|
|
||||||
" el('btnTemplate').click();\n"
|
|
||||||
" </script>\n"
|
" </script>\n"
|
||||||
"</body>\n"
|
"</body>\n"
|
||||||
"</html>\n";
|
"</html>\n";
|
||||||
|
|
@ -2190,6 +2166,7 @@ static bool amduatd_handle_get_artifact(int fd,
|
||||||
amduat_artifact_t artifact;
|
amduat_artifact_t artifact;
|
||||||
amduat_asl_store_error_t err;
|
amduat_asl_store_error_t err;
|
||||||
bool want_artifact = false;
|
bool want_artifact = false;
|
||||||
|
bool want_info = false;
|
||||||
|
|
||||||
memset(&ref, 0, sizeof(ref));
|
memset(&ref, 0, sizeof(ref));
|
||||||
memset(&artifact, 0, sizeof(artifact));
|
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 (amduatd_query_param(path, "format", format, sizeof(format)) != NULL) {
|
||||||
if (strcmp(format, "artifact") == 0) {
|
if (strcmp(format, "artifact") == 0) {
|
||||||
want_artifact = true;
|
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') {
|
} else if (strcmp(format, "raw") == 0 || format[0] == '\0') {
|
||||||
want_artifact = false;
|
want_artifact = false;
|
||||||
|
want_info = false;
|
||||||
} else {
|
} else {
|
||||||
free((void *)ref.digest.data);
|
free((void *)ref.digest.data);
|
||||||
return amduatd_http_send_text(fd, 400, "Bad Request",
|
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_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(
|
bool ok = amduatd_http_send_status(
|
||||||
fd,
|
fd,
|
||||||
200,
|
200,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue