380 lines
30 KiB
HTML
380 lines
30 KiB
HTML
<!doctype html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Add to magpie · magpie 文档</title>
|
||
<meta name="description" content="导入链接:给用户一个链接,你的模型就出现在他们用的每个 Agent 里。链接格式、参数、链接生成器和 Add to magpie 按钮。">
|
||
<meta name="color-scheme" content="light dark">
|
||
<meta name="theme-color" content="#fafafa">
|
||
<script>
|
||
(() => {
|
||
let mode = "system";
|
||
try { mode = localStorage.getItem("theme") || "system"; } catch {}
|
||
const q = matchMedia("(prefers-color-scheme: dark)");
|
||
const apply = () => {
|
||
const t = mode === "system" ? (q.matches ? "dark" : "light") : mode;
|
||
document.documentElement.dataset.theme = t;
|
||
document.querySelector('meta[name="theme-color"]').content = t === "dark" ? "#0b0b0c" : "#fafafa";
|
||
document.querySelectorAll("button.theme").forEach((b) => { b.dataset.mode = mode; b.title = "Theme: " + mode; });
|
||
};
|
||
q.addEventListener("change", () => mode === "system" && apply());
|
||
window.setTheme = (m) => { mode = m; try { m === "system" ? localStorage.removeItem("theme") : localStorage.setItem("theme", m); } catch {} apply(); };
|
||
window.themeMode = () => mode;
|
||
apply();
|
||
})();
|
||
</script>
|
||
<link rel="icon" href="/favicon.png" type="image/png">
|
||
<link rel="apple-touch-icon" href="/img/icon-256.png">
|
||
<link rel="alternate" hreflang="en" href="https://usemagpie.ai/docs/import">
|
||
<link rel="alternate" hreflang="zh" href="https://usemagpie.ai/docs/zh/import">
|
||
<link rel="stylesheet" href="/docs.css">
|
||
<meta property="og:title" content="Add to magpie">
|
||
<meta property="og:description" content="一个链接,把你的模型送进用户的每个 Agent。">
|
||
<meta property="og:image" content="https://usemagpie.ai/img/og.png">
|
||
<meta property="og:url" content="https://usemagpie.ai/docs/zh/import">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
</head>
|
||
<body>
|
||
|
||
<nav><div class="wrap">
|
||
<a class="brand" href="/"><img class="bird" src="/img/magpie-small.svg" alt="">magpie <small>文档</small></a>
|
||
<div class="links">
|
||
<a href="/">首页</a>
|
||
<a href="/docs/zh/start">快速上手</a>
|
||
<a href="/docs/zh/intent">意图路由</a>
|
||
<a href="/docs/zh/import">Add to magpie</a>
|
||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||
</div>
|
||
<a class="icon-btn lang" href="/docs/import" hreflang="en" lang="en" title="English version">EN</a>
|
||
<a class="icon-btn" href="https://discord.gg/vGSnD3ZKQF" aria-label="magpie on Discord" title="Join us on Discord"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></svg></a>
|
||
<a class="icon-btn" href="https://github.com/yetone/magpie" aria-label="magpie on GitHub" title="Source on GitHub"><svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg></a>
|
||
<button class="icon-btn theme" type="button" aria-label="Theme" title="Theme: system"><svg class="i-system" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="1.75" y="2.75" width="12.5" height="8.5" rx="1.5"/><path d="M5.5 14h5M8 11.25V14"/></svg><svg class="i-light" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="8" cy="8" r="3"/><path d="M8 1v1.5M8 13.5V15M1 8h1.5M13.5 8H15M3 3l1 1M12 12l1 1M3 13l1-1M12 4l1-1"/></svg><svg class="i-dark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"><path d="M13.5 9.5A5.5 5.5 0 0 1 6.5 2.5a5.5 5.5 0 1 0 7 7Z"/></svg></button>
|
||
<a class="btn small" href="/#get">下载</a>
|
||
</div></nav>
|
||
|
||
<main class="wrap doc">
|
||
<aside class="toc">
|
||
<p>Add to magpie</p>
|
||
<a href="#overview">概览</a>
|
||
<a href="#quick-start">快速开始</a>
|
||
<a href="#link">链接格式</a>
|
||
<a href="#parameters">参数</a>
|
||
<a href="#builder">链接生成器</a>
|
||
<a href="#button">按钮</a>
|
||
<a href="#generate">在后端生成</a>
|
||
<a href="#security">安全</a>
|
||
<a href="#platforms">平台支持</a>
|
||
</aside>
|
||
|
||
<article>
|
||
<p class="eyebrow" id="overview">文档 · 接入</p>
|
||
<h1>Add to magpie</h1>
|
||
<p class="lede">给用户一个链接,你的模型就会出现在他们用的每个 Agent 里:Claude Code、Codex、OpenCode、Gemini CLI、Pi、Crush。不用改配置文件,不用设环境变量,也不用手动复制 base URL。</p>
|
||
|
||
<figure class="figure">
|
||
<img class="for-light" src="/img/import-zh-light.png" width="1280" height="1120" alt="magpie 询问是否添加 Acme Relay,列出链接里的地址、密钥和模型">
|
||
<img class="for-dark" src="/img/import-zh-dark.png" width="1280" height="1120" alt="magpie 询问是否添加 Acme Relay,列出链接里的地址、密钥和模型">
|
||
<figcaption>用户看到的界面。在点「添加」之前,什么都不会保存。</figcaption>
|
||
</figure>
|
||
|
||
<h2 id="quick-start"><a class="anchor" href="#quick-start">快速开始</a></h2>
|
||
<ol class="steps">
|
||
<li><b>生成链接</b>用 URL 参数描述你的服务:名称、你支持的每种 API 的 base URL,还可以带上用户的密钥和要提供的模型。</li>
|
||
<li><b>放到按钮后面</b>放在控制台里、刚生成的密钥旁边:<em>Add to magpie</em>。</li>
|
||
<li><b>用户确认</b>magpie 打开后,会清楚列出提示词和密钥将发往哪里,用户同意后才添加这个供应商。之后每个 Agent 都能直接用。</li>
|
||
</ol>
|
||
|
||
<div class="code" style="margin-top:20px">
|
||
<span class="lang">链接</span>
|
||
<button class="copy" data-copy="ex1">复制</button>
|
||
<pre id="ex1">https://usemagpie.ai/import#name=Acme%20Relay&chat=https://api.acme.example/v1&anthropic=https://api.acme.example&key=sk-…&models=gpt-5.5,claude-sonnet-5</pre>
|
||
</div>
|
||
|
||
<h2 id="link"><a class="anchor" href="#link">链接格式</a></h2>
|
||
<p>magpie 会向系统注册 <code>magpie://</code> 协议。导入链接就是这个协议,加上 <code>import</code> 和一串查询参数:</p>
|
||
<div class="url"><span class="n">magpie://import</span><span class="p">?</span><span class="n">name</span><span class="p">=</span><span class="v">Acme%20Relay</span><span class="p">&</span><span class="n">chat</span><span class="p">=</span><span class="v">https://api.acme.example/v1</span><span class="p">&</span><span class="n">key</span><span class="p">=</span><span class="v">sk-…</span></div>
|
||
<p>在网页上,请改用 <code>usemagpie.ai/import</code>,把同样的参数放在 <code>#</code> 后面:</p>
|
||
<div class="url"><span class="n">https://usemagpie.ai/import</span><span class="p">#</span><span class="n">name</span><span class="p">=</span><span class="v">Acme%20Relay</span><span class="p">&</span><span class="n">chat</span><span class="p">=</span><span class="v">https://api.acme.example/v1</span><span class="p">&</span><span class="n">key</span><span class="p">=</span><span class="v">sk-…</span></div>
|
||
<div class="cards" style="margin-top:20px">
|
||
<div><h3>为什么用 https 链接</h3><p>普通链接能用的地方它都能用:GitHub README、聊天软件和邮件客户端常常会去掉或拦截自定义协议。它会打开 magpie;还没装 magpie 时,会引导下载。</p></div>
|
||
<div><h3>为什么放在 <code>#</code> 后面</h3><p>浏览器从不把 # 后面的部分发给服务器。密钥从你的页面直接到用户电脑上,不经过 usemagpie.ai,连日志里也不会出现。</p></div>
|
||
</div>
|
||
<p>每个值都要做 URL 编码,<code>URLSearchParams</code> 或 <code>urlencode</code> 的结果即可。空格写成 <code>%20</code> 或 <code>+</code> 都行。</p>
|
||
|
||
<h2 id="parameters"><a class="anchor" href="#parameters">参数</a></h2>
|
||
<p>链接要么指定 magpie 的某个<a href="#presets">预设</a>(预设已经知道该厂商的地址),要么从头描述一个供应商:一个名称,加至少一个 base URL。</p>
|
||
<div class="table-wrap"><table>
|
||
<tr><th>参数</th><th>含义</th></tr>
|
||
<tr><td><code>name</code><span class="req">或 preset</span></td><td>供应商在 magpie 里的名称,最多 80 个字符。</td></tr>
|
||
<tr><td><code>chat</code></td><td>OpenAI Chat Completions 的 base URL,以 <code>/v1</code> 结尾(magpie 会自动拼上 <code>/chat/completions</code>)。</td></tr>
|
||
<tr><td><code>responses</code></td><td>OpenAI Responses 的 base URL,以 <code>/v1</code> 结尾。Codex 只支持这个 API;你不支持时,magpie 会替它转换。</td></tr>
|
||
<tr><td><code>anthropic</code></td><td>Anthropic Messages 的 base URL:根地址,不带 <code>/v1</code>。</td></tr>
|
||
<tr><td><code>key</code></td><td>用户的 API 密钥。不填的话,用户在对话框里自己粘贴。</td></tr>
|
||
<tr><td><code>models</code></td><td>提供给 Agent 的模型 id,用逗号分隔。不填时,magpie 会列出你的 <code>/v1/models</code> 返回的模型。</td></tr>
|
||
<tr><td><code>id</code></td><td>Agent 在 <code>id/model</code> 里用的 id。默认取名称的小写加连字符形式(<code>Acme Relay</code> → <code>acme-relay</code>)。</td></tr>
|
||
<tr><td><code>catalog</code></td><td>一个 <a href="https://models.dev">models.dev</a> 供应商 id,用它的元数据,比如转发 OpenAI 模型的服务可以填 <code>openai</code>。它提供:显示名、上下文长度(会写进支持该设置的 agent)、推理档位和是否支持图片;供应商自己的 <code>/models</code> 读不到时的模型列表;以及每 token 价格,用量页据此估算美元费用。可以用逗号填多个 id:它们的模型合在一起,同一个模型以排在前面的为准。</td></tr>
|
||
<tr><td><code>website</code></td><td>你的网站(https),显示在供应商页面上。</td></tr>
|
||
<tr><td><code>keys</code></td><td>用户创建密钥的页面(https)。链接里没有密钥时,对话框会链接到这里。</td></tr>
|
||
<tr><td><code>icon</code></td><td>你自己的图标(https):PNG、JPEG、GIF、WebP、ICO 或 SVG,最大 1 MB。用户确认导入后,magpie 只下载一次,存在供应商旁边。不填时,magpie 显示 <code>catalog</code> 对应厂商的 logo,或一个默认图标。</td></tr>
|
||
<tr><td><code>preset</code><span class="req">或 name</span></td><td>预设 id;使用它的地址、目录和页面,可以用 <code>name</code> 改名。</td></tr>
|
||
<tr><td><code>region</code></td><td>预设有多个区域时,指定用哪个。</td></tr>
|
||
</table></div>
|
||
<p>不用预设时,<code>chat</code>、<code>responses</code>、<code>anthropic</code> 至少要填一个;你支持几种 API 就填几个,每个 Agent 会用它原生支持的那一种。Base URL 必须是 <b>https</b>,只有指向 <code>localhost</code>、回环地址、内网地址和 <code>*.local</code> 时可以用 http,因为本地模型服务通常不配 TLS。地址里不能带查询参数、# 片段或账号密码。</p>
|
||
|
||
<h3 id="presets">预设</h3>
|
||
<p>magpie 已经内置的厂商。<code>magpie presets</code> 会打印当前的列表。</p>
|
||
<div class="chips">
|
||
<span class="chip"><code>anthropic</code></span><span class="chip"><code>openai</code></span><span class="chip"><code>google</code></span><span class="chip"><code>deepseek</code></span><span class="chip"><code>xai</code></span><span class="chip"><code>moonshot</code><span>Kimi</span></span><span class="chip"><code>moonshot-cn</code></span><span class="chip"><code>zhipu</code><span>GLM</span></span><span class="chip"><code>zai</code></span><span class="chip"><code>minimax</code></span><span class="chip"><code>minimax-cn</code></span><span class="chip"><code>qwen</code></span><span class="chip"><code>qwen-cn</code></span><span class="chip"><code>mistral</code></span><span class="chip"><code>groq</code></span><span class="chip"><code>ollama-cloud</code></span><span class="chip"><code>openrouter</code></span><span class="chip"><code>opencode-go</code></span><span class="chip"><code>opencode-zen</code></span><span class="chip"><code>together</code></span><span class="chip"><code>fireworks</code></span><span class="chip"><code>siliconflow</code></span><span class="chip"><code>aihubmix</code></span><span class="chip"><code>302ai</code></span><span class="chip"><code>yylx</code><span>auto · global · cn</span></span><span class="chip"><code>ollama</code></span><span class="chip"><code>lmstudio</code></span>
|
||
</div>
|
||
<p>有预设的厂商只需带上密钥:<code>magpie://import?preset=deepseek&key=sk-…</code>。想加入列表?<a href="https://github.com/yetone/magpie/issues">提个 issue</a>。</p>
|
||
|
||
<h2 id="builder"><a class="anchor" href="#builder">链接生成器</a></h2>
|
||
<p>填上你的服务信息,链接、按钮代码和 Markdown 会随输入实时生成。所有内容都只在这个页面里,不会发送出去。</p>
|
||
<div class="builder">
|
||
<form id="b" autocomplete="off" onsubmit="return false">
|
||
<div class="full"><div class="seg" role="tablist"><button type="button" data-mode="custom" class="on">自己的服务</button><button type="button" data-mode="preset">预设</button></div></div>
|
||
<label class="m-preset hide">预设<select name="preset"></select></label>
|
||
<label class="m-preset hide" id="region-l">区域<select name="region"></select></label>
|
||
<label class="m-custom">名称<input name="name" placeholder="Acme Relay" value="Acme Relay"></label>
|
||
<label class="m-custom">Id <i>可选</i><input name="id" placeholder="acme-relay"></label>
|
||
<label class="m-custom">OpenAI Chat base URL<input name="chat" class="mono" placeholder="https://api.acme.example/v1" value="https://api.acme.example/v1"></label>
|
||
<label class="m-custom">Anthropic base URL<input name="anthropic" class="mono" placeholder="https://api.acme.example" value="https://api.acme.example"></label>
|
||
<label class="m-custom">OpenAI Responses base URL <i>可选</i><input name="responses" class="mono" placeholder="https://api.acme.example/v1"></label>
|
||
<label class="m-custom">models.dev 目录 <i>可选</i><input name="catalog" placeholder="openai"></label>
|
||
<label>API 密钥 <i>可选</i><input name="key" class="mono" placeholder="sk-…" spellcheck="false"></label>
|
||
<label>模型 <i>逗号分隔</i><input name="models" class="mono" placeholder="gpt-5.5, claude-sonnet-5" value="gpt-5.5, claude-sonnet-5"></label>
|
||
<label class="m-custom">网站 <i>可选</i><input name="website" placeholder="https://acme.example"></label>
|
||
<label class="m-custom">密钥页面 <i>可选</i><input name="keys" placeholder="https://acme.example/keys"></label>
|
||
<label class="m-custom">图标 <i>可选,https</i><input name="icon" placeholder="https://acme.example/logo.svg"></label>
|
||
</form>
|
||
<div class="out">
|
||
<div class="row"><span>网页链接</span><code id="o-web"></code><button class="copy" data-copy="o-web">复制</button></div>
|
||
<div class="row"><span>应用链接</span><code id="o-app"></code><button class="copy" data-copy="o-app">复制</button></div>
|
||
<div class="row"><span>HTML</span><code id="o-html"></code><button class="copy" data-copy="o-html">复制</button></div>
|
||
<div class="row"><span>Markdown</span><code id="o-md"></code><button class="copy" data-copy="o-md">复制</button></div>
|
||
<div class="msg hide" id="o-msg"></div>
|
||
<div class="try"><a class="btn small" id="o-try" href="#" target="_blank" rel="noopener"><img class="bird-inv" src="/img/magpie-small.svg" alt="">试一下</a><small>会打开 magpie;你确认之前不会添加任何东西。</small></div>
|
||
</div>
|
||
</div>
|
||
|
||
<h2 id="button"><a class="anchor" href="#button">按钮</a></h2>
|
||
<p>两个现成的徽章,一个用于浅色页面,一个用于深色页面。把它链接到你的导入链接即可;也可以自己画按钮,<em>Add to magpie</em> 字样和这只鸟都可以随意使用。</p>
|
||
<div class="badges">
|
||
<div class="on-light"><img src="/img/add-to-magpie.svg" alt="Add to magpie" width="176" height="40"></div>
|
||
<div class="on-dark"><img src="/img/add-to-magpie-light.svg" alt="Add to magpie" width="176" height="40"></div>
|
||
</div>
|
||
<div class="code">
|
||
<span class="lang">HTML</span>
|
||
<button class="copy" data-copy="ex-html">复制</button>
|
||
<pre id="ex-html"><a href="https://usemagpie.ai/import#preset=deepseek&key=sk-…">
|
||
<img src="https://usemagpie.ai/img/add-to-magpie.svg" alt="Add to magpie" width="176" height="40">
|
||
</a></pre>
|
||
</div>
|
||
<div class="code">
|
||
<span class="lang">Markdown</span>
|
||
<button class="copy" data-copy="ex-md">复制</button>
|
||
<pre id="ex-md">[](https://usemagpie.ai/import#preset=deepseek)</pre>
|
||
</div>
|
||
<p>深色页面请用 <code>add-to-magpie-light.svg</code>。README 这类公开页面绝不能带密钥:去掉 <code>key</code>,让用户粘贴自己的。</p>
|
||
|
||
<h2 id="generate"><a class="anchor" href="#generate">在后端生成</a></h2>
|
||
<p>按钮最适合放在展示新密钥的那个页面。在拿到密钥的地方生成链接:</p>
|
||
<div class="code">
|
||
<span class="lang">JavaScript</span>
|
||
<button class="copy" data-copy="ex-js">复制</button>
|
||
<pre id="ex-js"><span class="k">const</span> params = <span class="k">new</span> URLSearchParams({
|
||
name: <span class="s">"Acme Relay"</span>,
|
||
chat: <span class="s">"https://api.acme.example/v1"</span>,
|
||
anthropic: <span class="s">"https://api.acme.example"</span>,
|
||
key: apiKey,
|
||
models: [<span class="s">"gpt-5.5"</span>, <span class="s">"claude-sonnet-5"</span>].join(<span class="s">","</span>),
|
||
icon: <span class="s">"https://acme.example/logo.svg"</span>,
|
||
});
|
||
<span class="k">const</span> href = <span class="s">"https://usemagpie.ai/import#"</span> + params;</pre>
|
||
</div>
|
||
<div class="code">
|
||
<span class="lang">Python</span>
|
||
<button class="copy" data-copy="ex-py">复制</button>
|
||
<pre id="ex-py"><span class="k">from</span> urllib.parse <span class="k">import</span> urlencode
|
||
|
||
href = <span class="s">"https://usemagpie.ai/import#"</span> + urlencode({
|
||
<span class="s">"name"</span>: <span class="s">"Acme Relay"</span>,
|
||
<span class="s">"chat"</span>: <span class="s">"https://api.acme.example/v1"</span>,
|
||
<span class="s">"anthropic"</span>: <span class="s">"https://api.acme.example"</span>,
|
||
<span class="s">"key"</span>: api_key,
|
||
<span class="s">"models"</span>: <span class="s">"gpt-5.5,claude-sonnet-5"</span>,
|
||
<span class="s">"icon"</span>: <span class="s">"https://acme.example/logo.svg"</span>,
|
||
})</pre>
|
||
</div>
|
||
<div class="code">
|
||
<span class="lang">终端</span>
|
||
<button class="copy" data-copy="ex-sh">复制</button>
|
||
<pre id="ex-sh">magpie import <span class="s">'magpie://import?preset=deepseek&key=sk-…'</span> <span class="c"># 会先询问;加 -y 跳过</span></pre>
|
||
</div>
|
||
|
||
<h2 id="security"><a class="anchor" href="#security">安全</a></h2>
|
||
<p>导入链接只是一个建议,决定权在用户手里。</p>
|
||
<ul>
|
||
<li><b>总是要确认。</b>magpie 会显示名称、提示词和密钥将发往的每个地址,以及模型列表;用户点「添加」之前什么都不保存。用户还可以先改名或换掉密钥。</li>
|
||
<li><b>不会悄悄覆盖。</b>id 已被占用时,对话框会说明,按钮也会变成「替换」。</li>
|
||
<li><b>只接受 https。</b>除了本机和局域网,一律拒绝 http;<code>file:</code>、URL 里带账号密码以及其他协议直接拒绝。</li>
|
||
<li><b>只读一次。</b>在应用里,链接内容只交给窗口一次;刷新后对话框不会再出现。</li>
|
||
<li><b>图标是下载的,不是嵌入的。</b><code>icon=</code> 地址由 magpie 自己下载,只在用户确认导入之后,只走 https,只存进它自己的图标目录,最大 1 MB,并校验类型。域名在连接时解析,落到回环、内网、链路本地或保留地址时 magpie 拒绝连接,所以指回用户本机的域名(或 DNS 重绑定)都会被拒绝。页面和对话框从不直接加载远程图片。</li>
|
||
<li><b>密钥不经过服务器。</b>网页链接把参数放在 # 片段里。密钥最终只存在 <code>~/.config/magpie/providers.json</code>,只有用户本人可读。</li>
|
||
</ul>
|
||
<div class="note"><b>链接里的密钥就是机密。</b>只在该用户本人能看到的页面上把密钥放进链接,最好是专门给 magpie 生成、可以随时吊销的密钥。绝不要把共享密钥放进公开链接。</div>
|
||
|
||
<h2 id="platforms"><a class="anchor" href="#platforms">平台支持</a></h2>
|
||
<div class="table-wrap"><table>
|
||
<tr><th>系统</th><th><code>magpie://</code> 如何注册</th></tr>
|
||
<tr><td>macOS</td><td>由应用自己注册,magpie.app 放进「应用程序」即可。</td></tr>
|
||
<tr><td>Windows</td><td>magpie 首次运行时为当前用户注册。</td></tr>
|
||
<tr><td>Linux</td><td>通过桌面文件(<code>x-scheme-handler/magpie</code>)注册,安装器写一次,首次运行时再写一次。</td></tr>
|
||
<tr><td>终端</td><td><code>magpie import <链接></code> 显示同样的摘要,添加前会询问。</td></tr>
|
||
</table></div>
|
||
<p>导入链接需要 magpie 0.1.8 或更新版本。magpie 已在运行时,链接会交给那个实例,并把它的窗口调到前台。</p>
|
||
</article>
|
||
</main>
|
||
|
||
<footer><div class="wrap">
|
||
<span>© 2026 magpie · MIT</span>
|
||
<div class="links">
|
||
<a href="/">首页</a>
|
||
<a href="/docs/zh/start">快速上手</a>
|
||
<a href="/docs/zh/intent">意图路由</a>
|
||
<a href="https://github.com/yetone/magpie">GitHub</a>
|
||
<a href="https://discord.gg/vGSnD3ZKQF">Discord</a>
|
||
<a href="https://github.com/yetone/magpie-releases/releases">版本发布</a>
|
||
</div>
|
||
</div></footer>
|
||
|
||
<script>
|
||
(() => {
|
||
const next = { system: "light", light: "dark", dark: "system" };
|
||
document.querySelectorAll("button.theme").forEach((b) => {
|
||
b.dataset.mode = themeMode();
|
||
b.title = "Theme: " + themeMode();
|
||
b.addEventListener("click", () => setTheme(next[themeMode()]));
|
||
});
|
||
|
||
document.querySelectorAll(".copy").forEach((b) => b.addEventListener("click", async () => {
|
||
try {
|
||
await navigator.clipboard.writeText(document.getElementById(b.dataset.copy).textContent);
|
||
b.textContent = "已复制"; setTimeout(() => (b.textContent = "复制"), 1400);
|
||
} catch {}
|
||
}));
|
||
|
||
// The table of contents follows the reading position.
|
||
const links = [...document.querySelectorAll(".toc a")];
|
||
const heads = links.map((a) => document.getElementById(a.hash.slice(1)));
|
||
const spy = () => {
|
||
let i = 0;
|
||
heads.forEach((h, j) => { if (h.getBoundingClientRect().top < 140) i = j; });
|
||
if (innerHeight + scrollY >= document.body.scrollHeight - 4) i = heads.length - 1;
|
||
links.forEach((a, j) => a.classList.toggle("on", j === i));
|
||
};
|
||
addEventListener("scroll", spy, { passive: true }); spy();
|
||
|
||
// Link builder. It mirrors the checks magpie makes, so a link that
|
||
// builds here imports there.
|
||
const PRESETS = [
|
||
["anthropic", "Anthropic"], ["openai", "OpenAI"], ["google", "Google Gemini"], ["deepseek", "DeepSeek"],
|
||
["xai", "xAI"], ["moonshot", "Kimi"], ["moonshot-cn", "Kimi(中国)"], ["zhipu", "智谱 GLM"], ["zai", "Z.ai"],
|
||
["minimax", "MiniMax"], ["minimax-cn", "MiniMax(中国)"], ["qwen", "通义千问"], ["qwen-cn", "Qwen(中国)"],
|
||
["mistral", "Mistral"], ["groq", "Groq"], ["ollama-cloud", "Ollama Cloud"], ["openrouter", "OpenRouter"], ["opencode-go", "OpenCode Go"],
|
||
["opencode-zen", "OpenCode Zen"], ["together", "Together AI"], ["fireworks", "Fireworks"],
|
||
["siliconflow", "硅基流动"], ["aihubmix", "AiHubMix"], ["302ai", "302.AI"], ["yylx", "鱼鱼连线"],
|
||
["ollama", "Ollama"], ["lmstudio", "LM Studio"],
|
||
];
|
||
const REGIONS = { yylx: [["", "默认"], ["auto", "自动"], ["global", "海外"], ["cn", "中国大陆"]] };
|
||
const f = document.getElementById("b");
|
||
const el = (n) => f.elements[n];
|
||
el("preset").innerHTML = PRESETS.map(([id, n]) => `<option value="${id}">${n} · ${id}</option>`).join("");
|
||
el("preset").value = "deepseek";
|
||
let mode = "custom";
|
||
|
||
const local = (h) => h === "localhost" || h.endsWith(".local") || /^127\.|^10\.|^192\.168\.|^172\.(1[6-9]|2\d|3[01])\.|^\[?::1\]?$/.test(h);
|
||
const base = (v) => {
|
||
try {
|
||
const u = new URL(v);
|
||
if (u.username || u.password || u.search || u.hash) return false;
|
||
return u.protocol === "https:" || (u.protocol === "http:" && local(u.hostname));
|
||
} catch { return false; }
|
||
};
|
||
const esc = (s) => s.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
||
|
||
const build = () => {
|
||
document.querySelectorAll(".m-custom").forEach((e) => e.classList.toggle("hide", mode !== "custom"));
|
||
document.querySelectorAll(".m-preset").forEach((e) => e.classList.toggle("hide", mode !== "preset"));
|
||
const regions = REGIONS[el("preset").value];
|
||
document.getElementById("region-l").classList.toggle("hide", mode !== "preset" || !regions);
|
||
if (regions && el("region").dataset.for !== el("preset").value) {
|
||
el("region").innerHTML = regions.map(([id, n]) => `<option value="${id}">${n}</option>`).join("");
|
||
el("region").dataset.for = el("preset").value;
|
||
}
|
||
|
||
const q = new URLSearchParams();
|
||
const errs = [];
|
||
const put = (k) => { const v = el(k).value.trim(); if (v) q.set(k, v); return v; };
|
||
if (mode === "preset") {
|
||
q.set("preset", el("preset").value);
|
||
if (regions) put("region");
|
||
} else {
|
||
if (!put("name")) errs.push("名称必填。");
|
||
put("id");
|
||
let any = false;
|
||
for (const k of ["chat", "responses", "anthropic"]) {
|
||
const v = put(k);
|
||
el(k).classList.toggle("bad", !!v && !base(v));
|
||
if (v) { any = true; if (!base(v)) errs.push(`${k}= 必须是 https 的 base URL,且不带查询参数。`); }
|
||
}
|
||
if (!any) errs.push("至少填一个 base URL。");
|
||
put("catalog");
|
||
for (const k of ["website", "keys"]) {
|
||
const v = put(k);
|
||
const ok = !v || /^https:\/\/[^/@\s]+/.test(v);
|
||
el(k).classList.toggle("bad", !ok);
|
||
if (!ok) errs.push(`${k}= 必须是 https 页面,否则 magpie 会忽略它。`);
|
||
}
|
||
{
|
||
const v = put("icon");
|
||
if (v && !/^https:\/\/[^/@\s?#]+\/[^\s#]*$/.test(v)) {
|
||
el("icon").classList.add("bad");
|
||
errs.push("icon= 必须是 https 的图片地址,否则 magpie 会忽略它。");
|
||
}
|
||
}
|
||
}
|
||
put("key");
|
||
const models = el("models").value.split(",").map((s) => s.trim()).filter(Boolean).join(",");
|
||
if (models) q.set("models", models);
|
||
|
||
// Readable, and what magpie parses: only what must be escaped is.
|
||
const enc = (v) => encodeURIComponent(v).replace(/%3A/gi, ":").replace(/%2F/gi, "/").replace(/%2C/gi, ",");
|
||
const s = [...q].map(([k, v]) => k + "=" + enc(v)).join("&");
|
||
const web = "https://usemagpie.ai/import#" + s;
|
||
const app = "magpie://import?" + s;
|
||
document.getElementById("o-web").textContent = web;
|
||
document.getElementById("o-app").textContent = app;
|
||
document.getElementById("o-html").textContent = `<a href="${esc(web)}"><img src="https://usemagpie.ai/img/add-to-magpie.svg" alt="Add to magpie" width="176" height="40"></a>`;
|
||
document.getElementById("o-md").textContent = `[](${web})`;
|
||
document.getElementById("o-try").href = "/import#" + s;
|
||
const msg = document.getElementById("o-msg");
|
||
msg.textContent = errs.join(" ");
|
||
msg.classList.toggle("hide", !errs.length);
|
||
};
|
||
|
||
document.querySelectorAll(".seg button").forEach((b) => b.addEventListener("click", () => {
|
||
mode = b.dataset.mode;
|
||
document.querySelectorAll(".seg button").forEach((x) => x.classList.toggle("on", x === b));
|
||
if (mode === "preset" && el("models").value === "gpt-5.5, claude-sonnet-5") el("models").value = "";
|
||
build();
|
||
}));
|
||
f.addEventListener("input", build);
|
||
f.addEventListener("change", build);
|
||
build();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|