Agent Sandbox Registry

awesome-templates

registry/index.html

无英文版

registry/index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Agent Sandbox Registry</title>
  <style>
    :root {
      --bg: #f3efe4;
      --ink: #1c1915;
      --muted: #5c564c;
      --line: #d9d0c1;
      --card: #fffdf8;
      --accent: #9a3412;
      --ok: #1f6b45;
      --no: #8f2d2d;
    }
    * { box-sizing: border-box; }
    body {
      margin: 0;
      background: var(--bg);
      color: var(--ink);
      font: 16px/1.5 "Iowan Old Style", Palatino, "Palatino Linotype", serif;
    }
    header, main { width: min(1100px, calc(100% - 32px)); margin: 0 auto; }
    header { padding: 40px 0 12px; }
    h1 { font-size: 40px; line-height: 1.1; margin: 0 0 8px; letter-spacing: -0.03em; }
    h2 { font-size: 22px; margin: 36px 0 10px; }
    p.lead { max-width: 68ch; color: var(--muted); margin: 0; }
    a { color: var(--accent); }
    .bar { display: flex; gap: 8px; flex-wrap: wrap; margin: 18px 0; }
    input, button {
      font: inherit;
      border: 1px solid var(--line);
      background: var(--card);
      color: var(--ink);
      border-radius: 999px;
      padding: 8px 14px;
    }
    input { min-width: min(100%, 280px); }
    button[aria-pressed="true"] { background: var(--ink); color: var(--bg); }
    table { width: 100%; border-collapse: collapse; background: var(--card); }
    th, td { text-align: left; vertical-align: top; padding: 10px 12px; border-bottom: 1px solid var(--line); }
    th { font-size: 13px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
    .tag { display: inline-block; margin: 0 4px 4px 0; padding: 1px 7px; border: 1px solid var(--line); border-radius: 999px; font-size: 13px; }
    .ok { color: var(--ok); }
    .no { color: var(--no); }
    .cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; }
    article { background: var(--card); border: 1px solid var(--line); padding: 14px 16px; }
    article h3 { margin: 0 0 6px; font-size: 18px; }
    article p { margin: 0 0 8px; }
    code { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; font-size: 0.92em; }
    footer { width: min(1100px, calc(100% - 32px)); margin: 28px auto 48px; color: var(--muted); }
    @media (max-width: 720px) {
      h1 { font-size: 32px; }
      table { display: block; overflow-x: auto; }
    }
  </style>
</head>
<body>
  <header>
    <h1>Agent Sandbox Registry</h1>
    <p class="lead">这里列出当前 SDK 的五个 provider,以及 awesome-templates 里能被搜索到的模板。能安装、能创建、只是一个名字,这三件事是分开的。域名 registry.agent-sandbox.cn 还没有接到这页上。</p>
    <div class="bar">
      <input id="q" type="search" placeholder="搜索模板或 provider" aria-label="搜索" />
      <button type="button" data-filter="all" aria-pressed="true">全部模板</button>
      <button type="button" data-filter="platform">平台模板</button>
      <button type="button" data-filter="docker">可构建镜像</button>
    </div>
  </header>
  <main>
    <h2>Provider</h2>
    <div id="providers" class="cards"></div>
    <h2>模板</h2>
    <table>
      <thead>
        <tr>
          <th>名称</th>
          <th>说明</th>
          <th>极速版</th>
          <th>容器版</th>
          <th>Cube</th>
          <th>E2B</th>
          <th>OpenSandbox</th>
        </tr>
      </thead>
      <tbody id="rows"></tbody>
    </table>
  </main>
  <footer>
    数据来自本仓库的 <code>awesome-templates.yaml</code>。<code>providers</code> 字段当前 CLI 会忽略,<code>schema_version</code> 仍是 1。CLI 只能对极速版执行 <code>abx template install</code>。
  </footer>
  <script>
    const labels = {
      supported: "可安装",
      builtin: "内置 id",
      "preexisting-template-id": "已有 id 才行",
      "push-image-then-pass-uri": "先推镜像",
      "unsupported-needs-pty": "不支持终端",
      unsupported: "不支持",
      "image-python-3.12-slim": "映射到 slim",
      "use-opensandbox-code-interpreter": "用另一镜像",
      "image-opensandbox-code-interpreter": "镜像 URI"
    };
    const order = ["alicloud-express", "alicloud-container", "cubesandbox", "e2b", "opensandbox"];
    let catalog = { providers: [], templates: [] };
    let filter = "all";

    function cell(value) {
      const text = labels[value] || value || "—";
      const cls = value === "supported" || value === "builtin" ? "ok" : (String(value).startsWith("unsupported") ? "no" : "");
      return `<td class="${cls}">${text}</td>`;
    }

    function draw() {
      const q = document.getElementById("q").value.trim().toLowerCase();
      const rows = catalog.templates.filter((row) => {
        const platform = ["base", "code-interpreter-v1", "desktop", "opensandbox-code-interpreter"].includes(row.name);
        if (filter === "platform" && !platform) return false;
        if (filter === "docker" && platform) return false;
        const blob = `${row.name} ${row.description} ${(row.tags || []).join(" ")}`.toLowerCase();
        return !q || blob.includes(q);
      });
      document.getElementById("rows").innerHTML = rows.map((row) => {
        const p = row.providers || {};
        return `<tr><td><code>${row.name}</code></td><td>${row.description}</td>${order.map((id) => cell(p[id])).join("")}</tr>`;
      }).join("");
      const pq = q;
      document.getElementById("providers").innerHTML = catalog.providers.filter((p) => {
        return !pq || `${p.id} ${p.name} ${p.templates} ${p.limits}`.toLowerCase().includes(pq);
      }).map((p) => `
        <article>
          <h3>${p.name}</h3>
          <p><code>${p.id}</code></p>
          <p>${p.cli ? "CLI 可加载" : "仅 Python SDK,CLI 返回 E8001"}</p>
          <p>${p.templates}</p>
          <p>${p.limits}</p>
        </article>
      `).join("");
    }

    document.getElementById("q").addEventListener("input", draw);
    for (const button of document.querySelectorAll("button[data-filter]")) {
      button.addEventListener("click", () => {
        filter = button.dataset.filter;
        for (const other of document.querySelectorAll("button[data-filter]")) {
          other.setAttribute("aria-pressed", other === button ? "true" : "false");
        }
        draw();
      });
    }
    fetch("catalog.json").then((res) => res.json()).then((data) => { catalog = data; draw(); });
  </script>
</body>
</html>