Web Service Onboarding

Polar Sponsor
爱发电 赞助
.NET 9.0

自动为外部网络服务注册——浏览器自动化、邮件验证、API密钥生成以及在1Password中安全存储。在被要求c...时使用。

Web 服务登机技能

功能概述

Web 服务登机技能是一项面向实际任务的技能,主要用于使用单个未断开的浏览器会话进行;绝不关闭步间浏览器;

核心要点

  • 关键规则( 已打开硬路- Twitchey, 2026-03- 25).。
  • 它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。
  • 使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。

使用与执行

该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;

结果检查与注意事项

若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。

Web Service 入驻技能

核心流程

发送注册邮件 → 验证邮箱 → 完成注册 → 
生成 API 密钥 → 安全存入 1Password → 注入 .env 文件

请在单个不间断的浏览器会话中完成全部步骤。步骤之间切勿关闭浏览器。

关键规则(血泪教训 — Turnkey,2026-03-25)

1. 单一会话,严禁中断

  • 必须在同一浏览器会话内完成注册、API 密钥创建及密钥安全存储
  • 仅当凭证已成功保存至 1Password 后,方可关闭浏览器
  • 若在提取凭证前关闭浏览器,则永久丢失访问权限——通行密钥(passkey)/会话已失效

2. 邮箱别名陷阱

  • Proton Mail(及多数服务商)将 user+alias@domain.com 视为与 user@domain.com 同一用户
  • 若该服务已存在 user@domain.com 账户,所有别名邮件均路由至该已有账户
  • 使用别名注册新账户前,务必确认该服务是否解析邮箱别名
  • 应使用完全独立的邮箱(不同域名、不同服务商)以确保账户彻底隔离

3. WebAuthn 虚拟认证器为临时性资源

  • Playwright 的 WebAuthn.addVirtualAuthenticator 创建的是内存级凭据存储
  • 所注册的通行密钥仅对该浏览器进程有效
  • 关闭并重新打开浏览器后,该凭据将永久丢失
  • 复用唯一方式是:关闭前导出凭据,下次运行时再重新导入
  • 注册完成后立即导出:
    const creds = await cdp.send('WebAuthn.getCredentials', { authenticatorId });
    fs.writeFileSync('/tmp/webauthn-creds.json', JSON.stringify(creds));
    
  • 下次会话中重新导入:
    const saved = JSON.parse(fs.readFileSync('/tmp/webauthn-creds.json'));
    for (const cred of saved.credentials) {
      await cdp.send('WebAuthn.addCredential', { authenticatorId, credential: cred });
    }
    

4. 邮箱验证基于链接,而非总是 OTP

  • 切勿预设存在 OTP 输入框——须先检查实际邮件正文内容
  • Turnkey、Vercel、Railway、Render 均发送魔法链接(magic links),而非一次性验证码
  • 提取 URL 前,需对邮件正文执行 quoted-printable 解码
  • 注意 QP 编码邮件中的软换行符(=n)

5. 会话 Cookie 与认证器强绑定

  • 若在上下文 A 中完成注册,却尝试在上下文 B 中复用该会话,则操作失败
  • Cookie 与通行密钥凭据必须保留在同一浏览器上下文中

6. 内部 API 不等同于公开 API

  • app.service.com/internal/api/* 接口依赖会话 Cookie 认证
  • api.service.com/public/v1/* 接口要求 API 密钥签名(X-Stamp)
  • 尚未获取 API 密钥时,无法调用公开 API 进行初始化
  • 仅内部 API(Cookie 认证)可从已认证的浏览器会话中直接调用

工作流

阶段 A:发送注册邮件(干净上下文 — 无 Cookie)

const ctxClean = await browser.newContext({ storageState: undefined });
const page = await ctxClean.newPage();
await page.goto('https://service.com/signup');
// 填写邮箱,点击继续
// 提交后立即关闭 ctxClean
await ctxClean.close();

为何分离? 防止既有会话 Cookie 干扰注册流程。

阶段 B:完成注册 + 保存 API 密钥(全程保持同一上下文)

const ctx = await browser.newContext({ storageState: undefined });
const page = await ctx.newPage();
const cdp = await ctx.newCDPSession(page);

// 在导航前配置虚拟认证器
await cdp.send('WebAuthn.enable', { enableUI: false });
const { authenticatorId } = await cdp.send('WebAuthn.addVirtualAuthenticator', {
  options: {
    protocol: 'ctap2', transport: 'internal',
    hasResidentKey: true, hasUserVerification: true,
    isUserVerified: true, automaticPresenceSimulation: true,
  }
});

// 导航至验证链接
await page.goto(verifyUrl);

// 完成注册后续步骤...

// 通行密钥注册完成后立即导出
const creds = await cdp.send('WebAuthn.getCredentials', { authenticatorId });
fs.writeFileSync('/tmp/webauthn-creds.json', JSON.stringify(creds));
console.log('凭据已备份:', creds.credentials?.length);

// 在同一会话中继续完成 API 密钥与钱包创建
// ...保存 API 密钥...

// 仅当所有凭证均已存入 1Password 后,才关闭浏览器

通过 Proton Bridge IMAP 获取邮件

function fetchLatestTurnkeyLink(host='127.0.0.1', port=1143, user, pass) {
  return new Promise((resolve) => {
    const socket = net.connect(port, host);
    let buf='', tls2=null, step=0, body=[], inBody=false;
    const t = setTimeout(() => { try{(tls2||socket).destroy()}catch(e){}; resolve(null); }, 22000);
    function send(cmd) { (tls2||socket).write(cmd+'\r\n'); }
    function onData(data) {
      buf += data.toString();
      const lines = buf.split('\r\n'); buf = lines.pop();
      for (const l of lines) {
        if (inBody) body.push(l);
        if (step===0 && l.includes('OK')) { step=1; send('a1 STARTTLS'); }
        else if (step===1 && l.includes('a1 OK')) { tls2=tls.connect({socket,rejectUnauthorized:false}); tls2.on('data',onData); step=2; send(`a2 LOGIN "${user}" "${pass}"`); }
        else if (step===2 && l.includes('a2 OK')) { step=3; send('a3 SELECT INBOX'); }
        else if (step===3 && l.includes('a3 OK')) { step=4; send('a4 SEARCH ALL'); }
        else if (step===4 && l.startsWith('* SEARCH')) {
          const nums = l.replace('* SEARCH','').trim().split(' ').filter(Boolean);
          step=5; inBody=true; send(`a5 FETCH ${nums[nums.length-1]} (BODY[TEXT])`);
        }
        else if (step===5 && l.includes('a5 OK')) {
          clearTimeout(t); (tls2||socket).end();
          // 解码 quoted-printable
          const decoded = body.join('\n')
            .replace(/=\r?\n/g, '')
            .replace(/=([0-9A-Fa-f]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
          // 提取重定向 URL
          const urls = [...decoded.matchAll(/https:\/\/service\.com\/redirect\?token=[^\s"<>)]+/g)].map(m=>m[0]);
          resolve(urls[0] || null);
        }
      }
    }
    socket.on('data', onData);
    socket.on('error', () => { clearTimeout(t); resolve(null); });
  });
}

要点: 应匹配服务所属域名的重定向 URL,而非泛化 URL 模式。

Proton Mail 配置

  • IMAP 主机:127.0.0.1,端口:1143,启用 STARTTLS
  • 1Password 中凭证路径:op://OpenClaw/Proton Bridge - Monk Fenix/...
  • Bridge 必须处于运行状态:ps aux | grep -i bridge

表单输入填充 — 使用原生 value 设置器

标准 element.fill() 在 React 输入框上偶有失效。请改用以下方式:

await page.evaluate((value) => {
  const input = document.querySelector('input');
  Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')
    .set.call(input, value);
  input.dispatchEvent(new Event('input', { bubbles: true }));
  input.dispatchEvent(new Event('change', { bubbles: true }));
}, value);

按钮点击 — 先滚动至视口内

位于视口外的按钮点击会报错 element is outside of the viewport。务必先滚动:

await page.evaluate((text) => {
  const btn = [...document.querySelectorAll('button')]
    .find(b => b.textContent?.toLowerCase().includes(text) && !b.disabled);
  if (btn) { btn.scrollIntoView(); btn.click(); }
}, buttonText);

认证成功后 — 保存 API 密钥

针对提供内部浏览器 API 的服务:

// 从页面上下文调用已认证的内部 API
const data = await page.evaluate(async () => {
  const r = await fetch('/internal/api/v1/whoami');
  return r.json();
});
// data.organizationId, data.userId 等字段

通过内部 API 创建 API 密钥 / 资源

完成认证(Cookie 已存在)后,可在页面上下文中调用内部接口:

const result = await page.evaluate(async ({ orgId, publicKey }) => {
  const r = await fetch('/tkhq/api/v1/activities', {  // 根据具体服务调整路径
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      type: 'ACTIVITY_TYPE_CREATE_API_KEYS',
      organizationId: orgId,
      parameters: { apiKeys: [{ apiKeyName: 'my-key', publicKey, curveType: 'API_KEY_CURVE_P256' }] }
    })
  });
  return { status: r.status, body: await r.text() };
}, { orgId, publicKey });

重要提示: 各服务内部接口路径各不相同。创建资源前,请捕获网络请求以确认真实接口:

page.on('request', req => {
  if (req.method() !== 'GET' && req.url().includes(serviceDomain))
    console.log(req.method(), req.url());
});

保存至 1Password

op item create 
  --vault OpenClaw 
  --title "Turnkey API Credentials — Reddi Agent Protocol" 
  --category "API Credential" 
  "org_id[text]=$ORG_ID" 
  "user_id[text]=$USER_ID" 
  "api_public_key[text]=$API_PUB" 
  "api_private_key[password]=$API_PRIV" 
  "wallet_id[text]=$WALLET_ID" 
  "wallet_address[text]=$WALLET_ADDR"

注册登记表(强制要求)

启动任何注册流程前,必须向 Notion 注册登记表添加一条记录:

  • 数据库 ID:322eb552-581a-81dc-adbc-fabb7af1d311
  • 字段:服务名称、所用邮箱、日期、用途
  • 此为 POLICIES.md 明确规定的不可协商条款

各服务专项说明

Turnkey(app.turnkey.com)

  • 邮箱验证:基于链接(魔法链接,非 OTP)
  • 认证方式:WebAuthn 通行密钥(虚拟认证器可用)
  • 注册后 API:/tkhq/api/v1/activities(内部接口,Cookie 认证)
  • 公开 API:api.turnkey.com/public/v1/ 需 X-Stamp(已签名请求)
  • 邮箱别名(+tag)映射至同一 Turnkey 账户——如需独立组织,请更换邮箱服务商
  • 单次运行创建的组织 ID:b7378687-cf82-45ab-a46c-7dda9239001d(Reddi Agent Protocol)

通用模式

  • Vercel:邮箱 OTP 或 GitHub OAuth
  • Railway:GitHub OAuth(不支持邮箱注册)
  • Supabase:邮箱 + 密码,随后在控制台获取 API 密钥
  • Fly.io:邮箱 + 信用卡,推荐优先使用 CLI 初始化

起飞前检查清单

启动任意注册流程前,请确认以下事项:

  • 已添加至 Notion 注册登记表
  • 已确认邮箱可用(未被该服务占用)
  • 邮箱别名:该服务是否会合并别名?(建议先行测试)
  • 当前邮箱服务商的 IMAP 可读取
  • 1Password 保险库可正常访问
  • Proton Bridge 正在运行(如使用 Proton Mail)
  • 已确认付费套餐预算充足(如适用)——请先咨询 Nissan

相关专题

更多
LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

2026.09.30

0

10

LLVM RISC-V参数配置教程
LLVM RISC-V参数配置教程

本专题介绍LLVM对RISC-V基础ISA和扩展的支持方式,涵盖RV32、RV64、标准扩展、实验性扩展、厂商扩展、-menable-experimental-extensions和版本差异。

2026.09.30

0

14

LLVM IR中间表示入门指南
LLVM IR中间表示入门指南

本专题整理LLVM IR的核心概念,包括中间表示作用、模块结构、函数、基本块、SSA形式、类型系统和常见语法,帮助新手理解LLVM编译流程中的关键层。

2026.09.30

0

12

PDF转图片方法
PDF转图片方法

需要把 PDF 页面用于上传、预览、分享或图片归档时,PDF 转图片方法专题整理 JPG/PNG 格式选择、逐页导出、清晰度设置、批量下载和结果检查等流程,帮助用户稳定完成 PDF 图片化处理。

2026.09.30

0

26

PixTV AI视频生成与无限画布创作
PixTV AI视频生成与无限画布创作

PixTV专题整理AI视频与视觉内容创作相关功能使用教程,涵盖AI生图、视频生成、无限画布、多模型创作、素材管理、声音音乐及视频剪辑等功能,帮助用户快速掌握PixTV从创意到成片的完整制作方法。

2026.09.29

0

15

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

2026.09.23

0

15

Buffalo框架路由与请求处理实操指南
Buffalo框架路由与请求处理实操指南

本专题讲解Buffalo框架路由与请求处理机制,涵盖路由注册与分组、资源路由、Handler编写规范、Context上下文方法、参数绑定、中间件编写挂载、Session与Cookie读写、Flash消息及错误页面定制方法。

2026.09.23

0

15

Buffalo框架零基础入门教程
Buffalo框架零基础入门教程

本专题整理Buffalo框架入门内容,涵盖Go环境准备、buffalo CLI安装、新项目生成、目录结构说明、dev热加载启动、数据库连接配置与常见报错排查,帮助新手按约定优于配置的思路跑通第一个Buffalo框架应用。

2026.09.23

0

15

Conan创建软件包配方指南
Conan创建软件包配方指南

本专题介绍通过conanfile.py创建软件包的方法,讲解包名、版本、依赖和构建设置等基础信息,以及source、build、package、package_info等常用方法的作用及编写思路。

2026.09.22

0

12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习