figma-generate-personal-token

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

figma-generate-personal-token

figma-generate-personal-token

Manages the complete lifecycle of
FIGMA_TOKEN
: detection, validation, automated generation via browser, and persistence to the project
.env
.
Supports auto-login if
FIGMA_USERNAME
and
FIGMA_PASSWORD
are available in the environment or
.env
files.

管理
FIGMA_TOKEN
的完整生命周期:检测、验证、通过浏览器自动生成,以及持久化到项目的
.env
文件中。
若环境或
.env
文件中存在
FIGMA_USERNAME
FIGMA_PASSWORD
,则支持自动登录。

Workflow (follow this exact order)

工作流程(严格遵循以下顺序)

Step 1 — Run the main script

步骤1 — 运行主脚本

bash
SCRIPT_OUTPUT=$(python3 .claude/skills/figma-generate-personal-token/scripts/manage-token.py)
EXIT_CODE=$?
echo "$SCRIPT_OUTPUT"
echo "EXIT_CODE=$EXIT_CODE"
The script returns:
  • exit 0
    → token valid, nothing to do
  • exit 3
    FIGMA_USERNAME
    /
    FIGMA_PASSWORD
    found → auto-login (see Step 2)
  • exit 2
    → no token/expired or no credentials → manual login (see Step 3)
  • exit 1
    → unexpected error

bash
SCRIPT_OUTPUT=$(python3 .claude/skills/figma-generate-personal-token/scripts/manage-token.py)
EXIT_CODE=$?
echo "$SCRIPT_OUTPUT"
echo "EXIT_CODE=$EXIT_CODE"
脚本返回值说明:
  • exit 0
    → 令牌有效,无需操作
  • exit 3
    → 检测到
    FIGMA_USERNAME
    /
    FIGMA_PASSWORD
    自动登录(见步骤2)
  • exit 2
    → 无令牌/令牌过期 无凭据 → 手动登录(见步骤3)
  • exit 1
    → 意外错误

Step 2 — Auto-login with credentials (exit 3)

步骤2 — 使用凭据自动登录(exit 3)

The script found
FIGMA_USERNAME
and
FIGMA_PASSWORD
. Its stdout contains two lines with the values to use — note:
FIGMA_AUTO_LOGIN_*
is just the script's output format, not the name of the source env vars. Extract them from
$SCRIPT_OUTPUT
:
bash
USERNAME=$(echo "$SCRIPT_OUTPUT" | grep "^FIGMA_AUTO_LOGIN_USERNAME=" | cut -d= -f2-)
PASSWORD=$(echo "$SCRIPT_OUTPUT" | grep "^FIGMA_AUTO_LOGIN_PASSWORD=" | cut -d= -f2-)
Open the browser, fill the form and submit in one chain:
bash
agent-browser open "https://www.figma.com/login" && agent-browser wait --load networkidle && agent-browser snapshot -i
Fill the form (refs
@eX
come from the snapshot):
bash
agent-browser fill @eX "$USERNAME" && agent-browser fill @eY "$PASSWORD" && agent-browser click @eZ
Wait for post-login redirect:
bash
agent-browser wait --load networkidle --timeout 30000 && agent-browser get url
If the URL still contains
/login
, Figma may require 2FA — wait for the user to complete the flow:
bash
agent-browser wait --fn "!window.location.href.includes('/login')" --timeout 120000
Then continue to Step 4.

脚本检测到
FIGMA_USERNAME
FIGMA_PASSWORD
。其标准输出包含两行需使用的值——注意:
FIGMA_AUTO_LOGIN_*
仅为脚本输出格式,并非源环境变量的名称。从
$SCRIPT_OUTPUT
中提取:
bash
USERNAME=$(echo "$SCRIPT_OUTPUT" | grep "^FIGMA_AUTO_LOGIN_USERNAME=" | cut -d= -f2-)
PASSWORD=$(echo "$SCRIPT_OUTPUT" | grep "^FIGMA_AUTO_LOGIN_PASSWORD=" | cut -d= -f2-)
打开浏览器,一次性完成表单填写与提交:
bash
agent-browser open "https://www.figma.com/login" && agent-browser wait --load networkidle && agent-browser snapshot -i
填写表单(
@eX
等引用来自快照):
bash
agent-browser fill @eX "$USERNAME" && agent-browser fill @eY "$PASSWORD" && agent-browser click @eZ
等待登录后重定向:
bash
agent-browser wait --load networkidle --timeout 30000 && agent-browser get url
若URL仍包含
/login
,则Figma可能需要二次验证(2FA)——等待用户完成验证流程:
bash
agent-browser wait --fn "!window.location.href.includes('/login')" --timeout 120000
随后继续步骤4。

Step 3 — Manual login (exit 2)

步骤3 — 手动登录(exit 2)

No credentials available. Navigate to settings and immediately check the resulting URL:
bash
agent-browser open "https://www.figma.com/settings" && agent-browser wait --load networkidle && agent-browser get url
  • If the URL does not contain
    /login
    → already logged in, skip to Step 4 immediately
  • If the URL contains
    /login
    → inform the user, then wait up to 2 minutes:
bash
agent-browser wait --fn "!window.location.href.includes('/login')" --timeout 120000
ℹ️ A browser session from an earlier step in the same conversation may already be authenticated — always check the URL before waiting.
Then continue to Step 4.

无可用凭据。导航至设置页面并立即检查生成的URL:
bash
agent-browser open "https://www.figma.com/settings" && agent-browser wait --load networkidle && agent-browser get url
  • 若URL包含
    /login
    → 已登录,立即跳至步骤4
  • 若URL包含
    /login
    → 告知用户,然后等待最多2分钟:
bash
agent-browser wait --fn "!window.location.href.includes('/login')" --timeout 120000
ℹ️ 同一会话中之前步骤的浏览器会话可能已完成认证——等待前务必检查URL。
随后继续步骤4。

Step 4 — Open Settings and click the Security tab

步骤4 — 打开设置并点击“安全”标签页

After login, open settings. Figma redirects to the files page but opens the settings dialog automatically:
bash
agent-browser open "https://www.figma.com/settings" && agent-browser wait --load networkidle
Take ONE snapshot, grep the Security tab ref, then click it directly:
bash
agent-browser snapshot -i | grep -E "tab.*(Séc|Secur)"
agent-browser click @eREF
⚠️ Never use
agent-browser scroll
inside a Figma modal — it can close the dialog. ⚠️ Do NOT use JS eval with escaped quotes to find the Security tab — it returns
null
unreliably. Use snapshot refs instead.

登录后,打开设置页面。Figma会重定向到文件页面,但会自动打开设置对话框
bash
agent-browser open "https://www.figma.com/settings" && agent-browser wait --load networkidle
拍摄一张快照,提取“安全”标签页的引用,然后直接点击:
bash
agent-browser snapshot -i | grep -E "tab.*(Séc|Secur)"
agent-browser click @eREF
⚠️ 切勿在Figma模态框内使用
agent-browser scroll
——这会关闭对话框。 ⚠️ 不要使用带有转义引号的JS eval来查找“安全”标签页——它会不可靠地返回
null
。请使用快照引用。

Step 5 — Create the token

步骤5 — 创建令牌

Click "Generate new token" via JS with
--stdin
(avoids all shell escaping issues):
bash
agent-browser eval --stdin <<'EVALEOF'
(() => {
  const btn = Array.from(document.querySelectorAll("button"))
    .find(b => b.textContent.includes("Générer un nouveau token") || b.textContent.includes("Create new token") || b.textContent.includes("Generate new token"));
  if (btn) { btn.scrollIntoView({ block: "center", behavior: "instant" }); btn.click(); return "clicked: " + btn.textContent.trim(); }
  return "not found";
})()
EVALEOF
agent-browser wait 500
ONE snapshot to get the name input and expiry combobox refs:
bash
agent-browser snapshot -i | grep -E "(textbox|combobox)"
Fill the form in a single sequence:
bash
undefined
通过带
--stdin
参数的JS点击“Generate new token”按钮(避免所有Shell转义问题):
bash
agent-browser eval --stdin <<'EVALEOF'
(() => {
  const btn = Array.from(document.querySelectorAll("button"))
    .find(b => b.textContent.includes("Générer un nouveau token") || b.textContent.includes("Create new token") || b.textContent.includes("Generate new token"));
  if (btn) { btn.scrollIntoView({ block: "center", behavior: "instant" }); btn.click(); return "clicked: " + btn.textContent.trim(); }
  return "not found";
})()
EVALEOF
agent-browser wait 500
拍摄一张快照以获取名称输入框和到期时间下拉框的引用:
bash
agent-browser snapshot -i | grep -E "(textbox|combobox)"
一次性完成表单填写:
bash
undefined

1. Token name — auto-detect project name from cwd

1. 令牌名称——从当前工作目录自动检测项目名称

PROJECT_NAME=$(basename $(pwd)) agent-browser fill @eNAME_REF "Claude Code - $PROJECT_NAME"
PROJECT_NAME=$(basename $(pwd)) agent-browser fill @eNAME_REF "Claude Code - $PROJECT_NAME"

2. Longest expiry — the combobox is CUSTOM (not a <select>), use click + JS

2. 最长有效期——下拉框为自定义组件(非<select>),使用点击+JS实现

Click the combobox ref to open it:

点击下拉框引用以展开选项:

agent-browser click @eEXPIRY_REF
agent-browser click @eEXPIRY_REF

Then click the last [role="option"] via JS (longest duration):

然后通过JS点击最后一个[role="option"](最长有效期):

agent-browser eval --stdin <<'EVALEOF' (() => { const options = Array.from(document.querySelectorAll('[role="option"]')); const last = options[options.length - 1]; if (last) { last.click(); return "selected: " + last.textContent.trim(); } return "no options found"; })() EVALEOF
agent-browser eval --stdin <<'EVALEOF' (() => { const options = Array.from(document.querySelectorAll('[role="option"]')); const last = options[options.length - 1]; if (last) { last.click(); return "selected: " + last.textContent.trim(); } return "no options found"; })() EVALEOF

3. Check ALL permissions in one JS call — always use --stdin

3. 一次性勾选所有权限——务必使用--stdin参数

agent-browser eval --stdin <<'EVALEOF' document.querySelectorAll('input[type="checkbox"]').forEach(cb => { if (!cb.checked) cb.click(); }); document.querySelectorAll('input[type="checkbox"]:checked').length + " checked" EVALEOF

> ⚠️ **Never use `agent-browser select` for the expiry field** — Figma's expiry combobox is a custom component, not a native `<select>`. It will always fail with "Element is not a \<select\> element". Always use click + JS `[role="option"]`.

Click Generate via JS directly — **no snapshot needed**:

```bash
agent-browser eval --stdin <<'EVALEOF'
(() => {
  const btn = Array.from(document.querySelectorAll("button"))
    .find(b => b.textContent.trim() === "Générer un token" || b.textContent.trim() === "Generate token");
  if (btn && !btn.disabled) { btn.click(); return "clicked"; }
  return "not found or disabled: " + Array.from(document.querySelectorAll("button")).map(b => b.textContent.trim()).filter(Boolean).join(", ");
})()
EVALEOF
agent-browser wait 1000

agent-browser eval --stdin <<'EVALEOF' document.querySelectorAll('input[type="checkbox"]').forEach(cb => { if (!cb.checked) cb.click(); }); document.querySelectorAll('input[type="checkbox"]:checked').length + " checked" EVALEOF

> ⚠️ **切勿对到期时间字段使用`agent-browser select`**——Figma的到期时间下拉框是自定义组件,而非原生`<select>`。使用该命令会始终报错“Element is not a \<select\> element”。请始终使用点击+JS `[role="option"]`的方式。

直接通过JS点击“Generate”按钮——**无需快照**:

```bash
agent-browser eval --stdin <<'EVALEOF'
(() => {
  const btn = Array.from(document.querySelectorAll("button"))
    .find(b => b.textContent.trim() === "Générer un token" || b.textContent.trim() === "Generate token");
  if (btn && !btn.disabled) { btn.click(); return "clicked"; }
  return "not found or disabled: " + Array.from(document.querySelectorAll("button")).map(b => b.textContent.trim()).filter(Boolean).join(", ");
})()
EVALEOF
agent-browser wait 1000

Step 6 — Retrieve and persist the token

步骤6 — 获取并持久化令牌

Click the Copy button and read via
pbpaste
(primary method — most reliable):
bash
agent-browser eval --stdin <<'EVALEOF'
(() => {
  const btn = Array.from(document.querySelectorAll("button"))
    .find(b => b.title?.match(/Copi/i) || b.getAttribute("aria-label")?.match(/Copi/i) || b.textContent?.match(/Copi/i));
  if (btn) { btn.click(); return "clicked"; }
  return "not found";
})()
EVALEOF
agent-browser wait 300
TOKEN_VALUE=$(pbpaste)
echo "Token retrieved: ${TOKEN_VALUE:0:8}..."
If
pbpaste
returns empty or not a Figma token, try reading from the DOM as a last resort:
bash
TOKEN_VALUE=$(agent-browser eval --stdin <<'EVALEOF'
Array.from(document.querySelectorAll("input")).find(i => i.value?.startsWith("figd_"))?.value || ""
EVALEOF
)
⚠️ Do not use DOM read as primary — Figma's token display field may use internal React state rather than the DOM
.value
property, causing the read to return empty even when the token is visible on screen. The clipboard approach is always reliable.
If
TOKEN_VALUE
is still empty after both attempts, stop and report the error — do not call
--save
:
bash
if [ -z "$TOKEN_VALUE" ] || [[ "$TOKEN_VALUE" != figd_* ]]; then
  echo "❌ Could not retrieve token — check the Figma dialog is still open and the token is visible."
  exit 1
fi
Then save:
bash
python3 .claude/skills/figma-generate-personal-token/scripts/manage-token.py --save "$TOKEN_VALUE"
点击复制按钮并通过
pbpaste
读取(首选方法——最可靠):
bash
agent-browser eval --stdin <<'EVALEOF'
(() => {
  const btn = Array.from(document.querySelectorAll("button"))
    .find(b => b.title?.match(/Copi/i) || b.getAttribute("aria-label")?.match(/Copi/i) || b.textContent?.match(/Copi/i));
  if (btn) { btn.click(); return "clicked"; }
  return "not found";
})()
EVALEOF
agent-browser wait 300
TOKEN_VALUE=$(pbpaste)
echo "Token retrieved: ${TOKEN_VALUE:0:8}..."
pbpaste
返回空或非Figma令牌,则尝试从DOM读取作为最后手段:
bash
TOKEN_VALUE=$(agent-browser eval --stdin <<'EVALEOF'
Array.from(document.querySelectorAll("input")).find(i => i.value?.startsWith("figd_"))?.value || ""
EVALEOF
)
⚠️ 不要将DOM读取作为首选方法——Figma的令牌显示字段可能使用内部React状态而非DOM的
.value
属性,导致即使令牌在屏幕上可见,读取结果仍为空。剪贴板方法始终可靠。
若经过两种尝试后
TOKEN_VALUE
仍为空,则停止操作并报告错误——不要调用
--save
bash
if [ -z "$TOKEN_VALUE" ] || [[ "$TOKEN_VALUE" != figd_* ]]; then
  echo "❌ 无法获取令牌——请检查Figma对话框是否仍处于打开状态且令牌可见。"
  exit 1
fi
然后保存令牌:
bash
python3 .claude/skills/figma-generate-personal-token/scripts/manage-token.py --save "$TOKEN_VALUE"

Step 7 — Close the browser

步骤7 — 关闭浏览器

Always close the browser session after the token has been saved:
bash
agent-browser close

令牌保存完成后,务必关闭浏览器会话:
bash
agent-browser close

Supported
.env
files (priority order)

支持的
.env
文件(优先级顺序)

The script searches and updates in this order:
FileUsagePriority
.env.local
Vite/Next.js — gitignored by default1st (read)
.env
Standard — write target2nd (read) / 1st (write)
.env.development.local
Local dev3rd
.env.development
Dev only4th
The token is always written to
.env
. If
.env
is not gitignored, a warning is shown.
脚本按以下顺序搜索并更新文件:
文件用途优先级
.env.local
Vite/Next.js——默认被Git忽略1st (读取)
.env
标准文件——写入目标2nd (读取) / 1st (写入)
.env.development.local
本地开发环境3rd
.env.development
仅开发环境4th
令牌始终写入
.env
文件
。若
.env
未被Git忽略,脚本会显示警告。

Variables read (credentials)

读取的变量(凭据)

dotenv
FIGMA_USERNAME=your@email.com   # or FIGMA_EMAIL
FIGMA_PASSWORD=your_password
dotenv
FIGMA_USERNAME=your@email.com   # 或 FIGMA_EMAIL
FIGMA_PASSWORD=your_password

Variables written (token)

写入的变量(令牌)

dotenv
FIGMA_TOKEN=figd_xxxxx

dotenv
FIGMA_TOKEN=figd_xxxxx

Security

安全说明

  • Token is never logged in plaintext (masked:
    figd_W86...zjyM
    )
  • Password is masked in human-readable output (
    ********
    ); the structured exit-3 output prints it in plaintext so the agent can extract it — do not persist
    $SCRIPT_OUTPUT
  • If
    .env
    is not gitignored, the script shows a warning
  • Never commit
    FIGMA_USERNAME
    or
    FIGMA_PASSWORD
    in a non-gitignored
    .env
    file

  • 令牌永远不会以明文形式记录(掩码显示:
    figd_W86...zjyM
  • 密码在人类可读输出中被掩码(
    ********
    );结构化的exit-3输出会以明文形式打印密码以便Agent提取——请勿持久化
    $SCRIPT_OUTPUT
  • .env
    未被Git忽略,脚本会显示警告
  • 切勿在未被Git忽略的
    .env
    文件中提交
    FIGMA_USERNAME
    FIGMA_PASSWORD

Troubleshooting

故障排除

"The generation dialog closes unexpectedly" → Always use JS to click the "Generate new token" button — never use a snapshot ref click. The JS approach calls
scrollIntoView
first to ensure the element is in view before clicking.
"Checkboxes fail partway through" → Do not use a shell
for
loop over refs. Use exclusively the JS command in Step 5 which checks everything in a single evaluation.
"
agent-browser clipboard read
returns empty"
→ Use
pbpaste
directly on macOS. It is more reliable and synchronous.
"Figma keeps redirecting without showing tokens" → Figma may require 2FA re-authentication. Let the user complete the flow in the visible browser.
"Permission denied on .env"
chmod 644 .env
then retry.
"Token generated but immediately invalid" → Figma may have a propagation delay of a few seconds. The script retries automatically 3 times with a 2s interval.
"Auto-login fails despite valid credentials" → Verify
FIGMA_USERNAME
and
FIGMA_PASSWORD
are correct via manual login. → If 2FA is enabled, auto-login will stop at the 2FA step — the user must complete it manually.
"Expiry select fails with 'Element is not a <select> element'" → Figma's expiry field is a custom combobox, not a native
<select>
. Do not use
agent-browser select
. Instead: click the combobox ref to open it, then click the last
[role="option"]
via JS (see Step 5).
"DOM input read returns empty even though the token is visible on screen" → Figma's token display input uses internal React state;
.value
may not reflect the rendered text. Always use the Copy button +
pbpaste
as the primary retrieval method (see Step 6).
“生成对话框意外关闭” → 始终使用JS点击“Generate new token”按钮——切勿使用快照引用点击。JS方法会先调用
scrollIntoView
确保元素可见后再点击。
“勾选复选框中途失败” → 不要对引用使用Shell
for
循环。请完全使用步骤5中的JS命令,它会在单次求值中完成所有勾选操作。
agent-browser clipboard read
返回空”
→ 在macOS上直接使用
pbpaste
。它更可靠且同步。
“Figma持续重定向而不显示令牌” → Figma可能需要重新进行二次验证(2FA)。让用户在可见的浏览器中完成验证流程。
.env
文件权限被拒绝”
→ 执行
chmod 644 .env
后重试。
“令牌已生成但立即无效” → Figma可能存在几秒的传播延迟。脚本会自动重试3次,每次间隔2秒。
“凭据有效但自动登录失败” → 通过手动登录验证
FIGMA_USERNAME
FIGMA_PASSWORD
是否正确。 → 若启用了2FA,自动登录会在2FA步骤停止——用户必须手动完成验证。
“到期时间选择失败,报错'Element is not a <select> element'” → Figma的到期时间字段是自定义下拉框,而非原生
<select>
。请勿使用
agent-browser select
。正确做法:点击下拉框引用展开选项,然后通过JS点击最后一个
[role="option"]
(见步骤5)。
“DOM输入框读取返回空,但令牌在屏幕上可见” → Figma的令牌显示输入框使用内部React状态;
.value
可能无法反映渲染的文本。请始终使用复制按钮+
pbpaste
作为首选获取方法(见步骤6)。