2026-06-30 20:19:56 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html lang="en">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="utf-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
|
<title>Tutorial Execution Report</title>
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
|
|
|
<style>
|
|
|
|
|
:root {
|
|
|
|
|
--bg: #0d1117; --panel: #161b22; --border: #30363d;
|
|
|
|
|
--text: #c9d1d9; --muted: #8b949e; --accent: #58a6ff;
|
|
|
|
|
--pass: #2ea043; --fail: #f85149; --info: #d29922;
|
|
|
|
|
--code-bg: #0b0f14;
|
|
|
|
|
}
|
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
|
body {
|
|
|
|
|
margin: 0; background: var(--bg); color: var(--text);
|
|
|
|
|
font: 14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
|
|
|
}
|
|
|
|
|
header {
|
|
|
|
|
position: sticky; top: 0; z-index: 10; background: var(--panel);
|
|
|
|
|
border-bottom: 1px solid var(--border); padding: 12px 20px;
|
|
|
|
|
display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
header h1 { font-size: 16px; margin: 0; font-weight: 600; }
|
|
|
|
|
.pill { padding: 2px 10px; border-radius: 999px; font-size: 12px; font-weight: 600; }
|
|
|
|
|
.pill.pass { background: rgba(46,160,67,.15); color: var(--pass); }
|
|
|
|
|
.pill.fail { background: rgba(248,81,73,.15); color: var(--fail); }
|
|
|
|
|
.pill.skip { background: rgba(210,153,34,.15); color: var(--info); }
|
|
|
|
|
select { background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 4px 8px; }
|
|
|
|
|
main { max-width: 1600px; margin: 0 auto; padding: 16px; }
|
|
|
|
|
.tutorial-title { font-size: 20px; font-weight: 700; margin: 24px 0 8px; color: #fff; }
|
|
|
|
|
.row {
|
|
|
|
|
display: grid; grid-template-columns: 1fr 1fr; gap: 0;
|
|
|
|
|
border: 1px solid var(--border); border-radius: 8px; margin: 10px 0; overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.row.preamble, .row.section { grid-template-columns: 1fr; }
|
|
|
|
|
.col { padding: 14px 18px; min-width: 0; }
|
|
|
|
|
.col.left { border-right: 1px solid var(--border); }
|
|
|
|
|
.row.preamble .col.left, .row.section .col.left { border-right: none; }
|
|
|
|
|
.col.right { background: var(--panel); }
|
|
|
|
|
/* A row containing any failed step is tinted red end-to-end so it's
|
|
|
|
|
impossible to miss when scanning the report. */
|
|
|
|
|
.row.has-fail { border-color: var(--fail); box-shadow: 0 0 0 1px var(--fail); }
|
|
|
|
|
.row.has-fail .col.left { background: rgba(248,81,73,.07); border-right-color: var(--fail); }
|
|
|
|
|
.row.has-fail .col.right { background: rgba(248,81,73,.12); }
|
|
|
|
|
.col-label { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: 8px; }
|
|
|
|
|
.md :first-child { margin-top: 0; }
|
|
|
|
|
.md h1 { font-size: 22px; } .md h2 { font-size: 18px; } .md h3 { font-size: 15px; }
|
|
|
|
|
.md pre { background: var(--code-bg); border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; }
|
|
|
|
|
.md code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; }
|
|
|
|
|
.md :not(pre) > code { background: rgba(110,118,129,.2); padding: .15em .4em; border-radius: 4px; }
|
|
|
|
|
.md table { border-collapse: collapse; margin: 8px 0; }
|
|
|
|
|
.md th, .md td { border: 1px solid var(--border); padding: 5px 10px; }
|
|
|
|
|
.md blockquote { border-left: 3px solid var(--border); margin: 8px 0; padding: 2px 12px; color: var(--muted); }
|
|
|
|
|
.md img { width: 100%; height: auto; border: 1px solid var(--border); border-radius: 6px; }
|
|
|
|
|
.exec { margin-bottom: 14px; }
|
|
|
|
|
.exec:last-child { margin-bottom: 0; }
|
|
|
|
|
.exec-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
|
|
|
|
.badge { font-size: 11px; font-weight: 700; padding: 1px 8px; border-radius: 4px; }
|
|
|
|
|
.badge.pass { background: rgba(46,160,67,.15); color: var(--pass); }
|
|
|
|
|
.badge.fail { background: rgba(248,81,73,.15); color: var(--fail); }
|
|
|
|
|
.badge.info { background: rgba(210,153,34,.15); color: var(--info); }
|
|
|
|
|
.kind { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
|
|
|
|
|
.note { color: var(--muted); font-size: 12.5px; margin-bottom: 6px; }
|
|
|
|
|
pre.cmd { background: var(--code-bg); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 6px; padding: 10px 12px; margin: 0 0 6px; overflow-x: auto; white-space: pre-wrap; word-break: break-word; }
|
|
|
|
|
pre.output { background: #010409; border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; margin: 0; overflow-x: auto; max-height: 360px; overflow-y: auto; color: #b9c1ca; }
|
|
|
|
|
pre.cmd, pre.output { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.45; }
|
|
|
|
|
.empty { color: var(--muted); font-style: italic; font-size: 12.5px; }
|
|
|
|
|
details summary { cursor: pointer; color: var(--accent); font-size: 12px; margin-bottom: 4px; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<header>
|
|
|
|
|
<h1>Tutorial Execution Report</h1>
|
|
|
|
|
<span id="summary"></span>
|
|
|
|
|
<label style="margin-left:auto; font-size:12px; color:var(--muted)">Tutorial
|
|
|
|
|
<select id="picker"></select>
|
|
|
|
|
</label>
|
|
|
|
|
</header>
|
|
|
|
|
<main id="main"></main>
|
|
|
|
|
|
2026-06-30 21:58:15 +00:00
|
|
|
<script id="report-data" type="application/json">{"generated_platform": "Linux", "summary": {"passed": 23, "failed": 0, "skipped": 0}, "tutorials": [{"meta": {"name": "Running This Execution-Zone Module Against logoscore", "platform": "Linux (ext=so)", "release": "(none)"}, "rows": [{"md": "# Running This Execution-Zone Module Against logoscore\n\n`logos-execution-zone` is a Logos `core` module that wraps the\n[Logos execution-zone wallet library](https://github.com/logos-blockchain/lssa)\n(`wallet_ffi`) to provide wallet lifecycle, account management, balance and\nblock queries, transfers, pinata claiming, and account-id encoding. This\ndoc-test exercises **this** execution-zone-module commit end-to-end through the\nheadless `logoscore` runtime:\n\n1. Build the `logoscore` CLI and the `lgpm` local package manager from their\n published flakes. `logoscore` is the headless frontend for `logos-liblogos`,\n so building it brings in the whole module-runtime stack (`logos_host`,\n `liblogos_core`, the IPC layer).\n2. Build **this** execution-zone module as an installable `.lgx` package\n straight from its own flake's `#lgx` output, **pinned to the commit under\n test** \u2014 so the module you run is built from exactly what is checked out\n here, not the latest published release.\n3. Install the `.lgx` into a `./modules` directory with `lgpm`.\n4. Start `logoscore` in daemon mode (`-D`), load `logos_execution_zone`,\n introspect it with `module-info`, and call its wallet-free methods \u2014\n verifying the module actually runs and round-trips real values through\n `wallet_ffi`.\n\nThe methods exercised here \u2014 `name`/`version` and the base58 account-id codec\n(`account_id_to_base58` / `account_id_from_base58`) \u2014 are the module's\ndeterministic, **offline** surface: they need neither an open wallet nor a live\nsequencer, so a green run is reproducible in CI. The stateful operations\n(creating accounts, transfers, sync, pinata claims) require a running\nsequencer and network, and are covered by the module's unit tests (mocked\n`wallet_ffi`) and integration tests (real `wallet_ffi`) instead.\n\nBecause the module is built from the commit under test and then loaded and\ncalled through a real `logoscore` daemon, a green run is real evidence that\nthis change keeps the execution-zone module loadable and callable.\n\n**What you'll build:** This `logos_execution_zone` module, packaged as `.lgx`, installed with `lgpm`, and called through a `logoscore` daemon.\n\n**What you'll learn:**\n\n- How to build the `logoscore` runtime and the `lgpm` package manager from their flakes\n- How a module's flake exposes a ready-to-install `.lgx` via its `#lgx` output\n- How to install an `.lgx` into a modules directory with `lgpm`\n- How to start the `logoscore` daemon, load a module, introspect it, and call its methods\n- How to shut the daemon down and confirm it has exited\n\n## Prerequisites\n\n- **Nix** with flakes enabled. Install from [nixos.org](https://nixos.org/download.html), then enable flakes:\n\n```bash\nmkdir -p ~/.config/nix\necho 'experimental-features = nix-command flakes' >> ~/.config/nix/nix.conf\n```\n\nVerify: `nix flake --help >/dev/null 2>&1 && echo \"Flakes enabled\"`\n\n- **A Linux or macOS machine.**", "execs": []}, {"md": "## Step 1: Build logoscore\n\nBuild the `logoscore` CLI from its published flake. The result is symlinked to\n`./logos/`. `logoscore` is the headless frontend for `logos-liblogos`, so this\none build brings in the whole module-runtime stack the daemon needs.", "execs": []}, {"md": "### 1.1 Build the CLI\n\n```bash\nnix build 'github:logos-co/logos-logoscore-cli' --out-link ./logos\n```\n\nThe build produces `logos/bin/logoscore` plus bundled runtime libraries\nand a `logos/modules/` directory containing the built-in\n`capability_module` (required for the auth handshake when loading\nmodules).", "execs": [{"kind": "run", "cmd": "nix build 'github:logos-co/logos-logoscore-cli' --out-link ./logos", "status": "pass", "exit_code": 0, "output": "unpacking 'github:logos-co/logos-log
|
2026-06-30 20:19:56 +00:00
|
|
|
<script>
|
|
|
|
|
const DATA = JSON.parse(document.getElementById("report-data").textContent);
|
|
|
|
|
if (window.marked) { marked.setOptions({ gfm: true, breaks: false }); }
|
|
|
|
|
const mdToHtml = (s) => window.marked ? marked.parse(s || "") : ("<pre>" + (s || "") + "</pre>");
|
|
|
|
|
|
|
|
|
|
const sum = DATA.summary;
|
|
|
|
|
document.getElementById("summary").innerHTML =
|
|
|
|
|
`<span class="pill pass">${sum.passed} passed</span> ` +
|
|
|
|
|
(sum.failed ? `<span class="pill fail">${sum.failed} failed</span> ` : "") +
|
|
|
|
|
(sum.skipped ? `<span class="pill skip">${sum.skipped} skipped</span>` : "");
|
|
|
|
|
|
|
|
|
|
const picker = document.getElementById("picker");
|
|
|
|
|
DATA.tutorials.forEach((t, i) => {
|
|
|
|
|
const o = document.createElement("option");
|
|
|
|
|
o.value = i; o.textContent = t.meta.name; picker.appendChild(o);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function execHtml(e) {
|
|
|
|
|
const badge = `<span class="badge ${e.status}">${e.status.toUpperCase()}</span>`;
|
|
|
|
|
const kind = `<span class="kind">${e.kind}${e.exit_code !== undefined ? " · exit " + e.exit_code : ""}</span>`;
|
|
|
|
|
let h = `<div class="exec"><div class="exec-head">${badge}${kind}</div>`;
|
|
|
|
|
if (e.note) h += `<div class="note">${escapeHtml(e.note)}</div>`;
|
|
|
|
|
if (e.cmd) h += `<pre class="cmd">${escapeHtml(e.cmd)}</pre>`;
|
|
|
|
|
const out = (e.output || "").trim();
|
|
|
|
|
if (out) {
|
|
|
|
|
const long = out.split("\n").length > 12;
|
|
|
|
|
if (long) {
|
|
|
|
|
h += `<details><summary>output (${out.split("\n").length} lines)</summary><pre class="output">${escapeHtml(out)}</pre></details>`;
|
|
|
|
|
} else {
|
|
|
|
|
h += `<pre class="output">${escapeHtml(out)}</pre>`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
h += `</div>`;
|
|
|
|
|
return h;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function escapeHtml(s) {
|
|
|
|
|
return String(s).replace(/[&<>]/g, c => ({ "&": "&", "<": "<", ">": ">" }[c]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function render(idx) {
|
|
|
|
|
const t = DATA.tutorials[idx];
|
|
|
|
|
const main = document.getElementById("main");
|
|
|
|
|
main.innerHTML = "";
|
|
|
|
|
const h = document.createElement("div");
|
|
|
|
|
h.className = "tutorial-title";
|
|
|
|
|
h.textContent = t.meta.name + " — " + t.meta.platform;
|
|
|
|
|
main.appendChild(h);
|
|
|
|
|
|
|
|
|
|
t.rows.forEach(row => {
|
|
|
|
|
const isStructural = row.execs.length === 0 &&
|
|
|
|
|
(/^#\s|^##\s/.test(row.md.trim()));
|
|
|
|
|
const div = document.createElement("div");
|
|
|
|
|
const hasExec = row.execs.length > 0;
|
|
|
|
|
const hasFail = row.execs.some(e => e.status === "fail");
|
|
|
|
|
div.className = "row" + (hasExec ? "" : (isStructural ? " section" : " preamble")) +
|
|
|
|
|
(hasFail ? " has-fail" : "");
|
|
|
|
|
|
|
|
|
|
const left = document.createElement("div");
|
|
|
|
|
left.className = "col left";
|
|
|
|
|
left.innerHTML = `<div class="md">${mdToHtml(row.md)}</div>`;
|
|
|
|
|
div.appendChild(left);
|
|
|
|
|
|
|
|
|
|
if (hasExec || !isStructural) {
|
|
|
|
|
const right = document.createElement("div");
|
|
|
|
|
right.className = "col right";
|
|
|
|
|
if (hasExec) {
|
|
|
|
|
right.innerHTML = `<div class="col-label">Executed</div>` +
|
|
|
|
|
row.execs.map(execHtml).join("");
|
|
|
|
|
} else {
|
|
|
|
|
right.innerHTML = `<div class="col-label">Executed</div>` +
|
|
|
|
|
`<div class="empty">No commands run for this step.</div>`;
|
|
|
|
|
}
|
|
|
|
|
div.appendChild(right);
|
|
|
|
|
}
|
|
|
|
|
main.appendChild(div);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function slug(s) {
|
|
|
|
|
return String(s).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
|
|
|
}
|
|
|
|
|
function indexFromHash() {
|
|
|
|
|
const raw = decodeURIComponent(location.hash.replace(/^#/, ""));
|
|
|
|
|
if (!raw) return 0;
|
|
|
|
|
if (/^\d+$/.test(raw) && +raw < DATA.tutorials.length) return +raw;
|
|
|
|
|
const i = DATA.tutorials.findIndex(
|
|
|
|
|
t => slug(t.meta.name) === raw || t.meta.name === raw
|
|
|
|
|
);
|
|
|
|
|
return i >= 0 ? i : 0;
|
|
|
|
|
}
|
|
|
|
|
function show(idx) { picker.value = idx; render(idx); }
|
|
|
|
|
picker.addEventListener("change", () => {
|
|
|
|
|
location.hash = slug(DATA.tutorials[+picker.value].meta.name);
|
|
|
|
|
render(+picker.value);
|
|
|
|
|
});
|
|
|
|
|
window.addEventListener("hashchange", () => show(indexFromHash()));
|
|
|
|
|
show(indexFromHash());
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|