docs(AGENTS): add agent guidelines for Motr Enclave

This commit is contained in:
2026-01-07 16:46:31 -05:00
parent d1ebfa5fc6
commit 8073405b7f
4 changed files with 555 additions and 0 deletions

168
example/index.html Normal file
View File

@@ -0,0 +1,168 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Motr Enclave Test</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
h1 {
color: #333;
}
.card {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.card h2 {
margin-top: 0;
color: #555;
font-size: 1.2rem;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #666;
}
input, textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: monospace;
font-size: 14px;
margin-bottom: 10px;
}
textarea {
min-height: 100px;
resize: vertical;
}
button {
background: #4a90d9;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
margin-right: 10px;
margin-bottom: 10px;
}
button:hover {
background: #357abd;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
.output {
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 13px;
white-space: pre-wrap;
word-break: break-all;
max-height: 300px;
overflow-y: auto;
}
.status {
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
}
.status.success {
background: #d4edda;
color: #155724;
}
.status.error {
background: #f8d7da;
color: #721c24;
}
.status.loading {
background: #fff3cd;
color: #856404;
}
.btn-group {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
</style>
</head>
<body>
<h1>Motr Enclave Plugin Test</h1>
<div class="card">
<h2>Plugin Status</h2>
<div id="status" class="status loading">Loading plugin...</div>
<button id="loadPluginBtn" onclick="loadPlugin()">Reload Plugin</button>
</div>
<div class="card">
<h2>generate()</h2>
<p>Initialize database with WebAuthn credential</p>
<label for="credentialInput">Credential (Base64):</label>
<input type="text" id="credentialInput" value="dGVzdC1jcmVkZW50aWFsLWRhdGEtZm9yLXRlc3Rpbmc=" placeholder="Base64-encoded PublicKeyCredential">
<button onclick="testGenerate()">Run generate()</button>
<div id="generateOutput" class="output"></div>
</div>
<div class="card">
<h2>load()</h2>
<p>Load database from serialized buffer</p>
<label for="databaseInput">Database Buffer (Base64):</label>
<input type="text" id="databaseInput" placeholder="Base64-encoded database buffer">
<button onclick="testLoad()">Run load()</button>
<button onclick="useGeneratedDb()">Use Generated DB</button>
<div id="loadOutput" class="output"></div>
</div>
<div class="card">
<h2>exec()</h2>
<p>Execute action with GitHub-style filter syntax</p>
<label for="filterInput">Filter:</label>
<input type="text" id="filterInput" value="resource:accounts action:list" placeholder="resource:accounts action:sign subject:did:sonr:abc">
<label for="tokenInput">UCAN Token (optional):</label>
<input type="text" id="tokenInput" placeholder="UCAN token for authorization">
<div class="btn-group">
<button onclick="testExec()">Run exec()</button>
<button onclick="setFilter('resource:accounts action:list')">List Accounts</button>
<button onclick="setFilter('resource:credentials action:list')">List Credentials</button>
<button onclick="setFilter('resource:sessions action:list')">List Sessions</button>
<button onclick="setFilter('resource:accounts action:sign')">Sign</button>
</div>
<div id="execOutput" class="output"></div>
</div>
<div class="card">
<h2>query()</h2>
<p>Resolve DID to document with resources</p>
<label for="didInput">DID:</label>
<input type="text" id="didInput" placeholder="did:sonr:abc123 (leave empty for current DID)">
<button onclick="testQuery()">Run query()</button>
<div id="queryOutput" class="output"></div>
</div>
<div class="card">
<h2>Console Log</h2>
<button onclick="clearLog()">Clear</button>
<div id="consoleLog" class="output"></div>
</div>
<script type="module" src="test.js"></script>
</body>
</html>

198
example/test.js Normal file
View File

@@ -0,0 +1,198 @@
import createPlugin from 'https://esm.sh/@extism/extism@1.0.0/dist/browser/mod.js';
let plugin = null;
let generatedDatabase = null;
function log(message, type = 'info') {
const consoleLog = document.getElementById('consoleLog');
const timestamp = new Date().toISOString().split('T')[1].split('.')[0];
const prefix = type === 'error' ? '[ERROR]' : type === 'success' ? '[OK]' : '[INFO]';
consoleLog.textContent += `${timestamp} ${prefix} ${message}\n`;
consoleLog.scrollTop = consoleLog.scrollHeight;
console[type === 'error' ? 'error' : 'log'](message);
}
function setStatus(message, type) {
const status = document.getElementById('status');
status.textContent = message;
status.className = `status ${type}`;
}
function formatOutput(data) {
try {
if (typeof data === 'string') {
const parsed = JSON.parse(data);
return JSON.stringify(parsed, null, 2);
}
return JSON.stringify(data, null, 2);
} catch {
return String(data);
}
}
async function loadPlugin() {
setStatus('Loading plugin...', 'loading');
log('Loading enclave.wasm...');
try {
plugin = await createPlugin('../build/enclave.wasm', {
useWasi: true,
logger: console
});
setStatus('Plugin loaded successfully', 'success');
log('Plugin loaded successfully', 'success');
} catch (error) {
setStatus(`Failed to load plugin: ${error.message}`, 'error');
log(`Failed to load plugin: ${error.message}`, 'error');
}
}
async function testGenerate() {
if (!plugin) {
log('Plugin not loaded', 'error');
return;
}
const output = document.getElementById('generateOutput');
const credential = document.getElementById('credentialInput').value;
log(`Calling generate() with credential: ${credential.substring(0, 20)}...`);
output.textContent = 'Running...';
try {
const input = JSON.stringify({ credential });
const result = await plugin.call('generate', input);
const data = result.json();
output.textContent = formatOutput(data);
log(`generate() completed. DID: ${data.did}`, 'success');
if (data.database) {
generatedDatabase = data.database;
log('Database buffer stored for load() test');
}
} catch (error) {
output.textContent = `Error: ${error.message}`;
log(`generate() failed: ${error.message}`, 'error');
}
}
async function testLoad() {
if (!plugin) {
log('Plugin not loaded', 'error');
return;
}
const output = document.getElementById('loadOutput');
const databaseInput = document.getElementById('databaseInput').value;
if (!databaseInput) {
output.textContent = 'Error: Database buffer is required';
log('load() requires database buffer', 'error');
return;
}
log('Calling load()...');
output.textContent = 'Running...';
try {
const input = JSON.stringify({
database: Array.from(atob(databaseInput), c => c.charCodeAt(0))
});
const result = await plugin.call('load', input);
const data = result.json();
output.textContent = formatOutput(data);
log(`load() completed. Success: ${data.success}`, data.success ? 'success' : 'error');
} catch (error) {
output.textContent = `Error: ${error.message}`;
log(`load() failed: ${error.message}`, 'error');
}
}
function useGeneratedDb() {
if (generatedDatabase) {
const base64 = btoa(String.fromCharCode(...generatedDatabase));
document.getElementById('databaseInput').value = base64;
log('Populated database input with generated database');
} else {
log('No generated database available. Run generate() first.', 'error');
}
}
async function testExec() {
if (!plugin) {
log('Plugin not loaded', 'error');
return;
}
const output = document.getElementById('execOutput');
const filter = document.getElementById('filterInput').value;
const token = document.getElementById('tokenInput').value;
if (!filter) {
output.textContent = 'Error: Filter is required';
log('exec() requires filter', 'error');
return;
}
log(`Calling exec() with filter: ${filter}`);
output.textContent = 'Running...';
try {
const input = JSON.stringify({ filter, token: token || undefined });
const result = await plugin.call('exec', input);
const data = result.json();
output.textContent = formatOutput(data);
log(`exec() completed. Success: ${data.success}`, data.success ? 'success' : 'error');
} catch (error) {
output.textContent = `Error: ${error.message}`;
log(`exec() failed: ${error.message}`, 'error');
}
}
function setFilter(filter) {
document.getElementById('filterInput').value = filter;
}
async function testQuery() {
if (!plugin) {
log('Plugin not loaded', 'error');
return;
}
const output = document.getElementById('queryOutput');
const did = document.getElementById('didInput').value;
log(`Calling query() with DID: ${did || '(current)'}`);
output.textContent = 'Running...';
try {
const input = JSON.stringify({ did: did || '' });
const result = await plugin.call('query', input);
const data = result.json();
output.textContent = formatOutput(data);
log(`query() completed. DID: ${data.did}`, 'success');
} catch (error) {
output.textContent = `Error: ${error.message}`;
log(`query() failed: ${error.message}`, 'error');
}
}
function clearLog() {
document.getElementById('consoleLog').textContent = '';
}
window.loadPlugin = loadPlugin;
window.testGenerate = testGenerate;
window.testLoad = testLoad;
window.useGeneratedDb = useGeneratedDb;
window.testExec = testExec;
window.setFilter = setFilter;
window.testQuery = testQuery;
window.clearLog = clearLog;
loadPlugin();