diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..6b43e5d4c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,44 @@ +name: ci + +# Every push to main and every PR: install, secret scan, i18n parity, type check, +# lint, unit tests, production build. The build needs no database — every page that +# touches Prisma is force-dynamic, and the sitemap degrades when the DB is absent. +on: + pull_request: + push: + branches: [main] + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + checks: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: npm + - run: npm ci + - name: Secret scan + run: npm run check:secrets + - name: i18n parity + run: npm run check:i18n + - name: Skill assets in sync + run: npm run check:skill-assets + - name: Prisma client + run: npx prisma generate + - name: Type check + run: npm run typecheck + - name: Lint + run: npm run lint + - name: Unit tests + run: npm test + - name: Build + run: npm run build + env: + NEXTAUTH_SECRET: ci-build-only + NEXTAUTH_URL: http://localhost:3000 diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml deleted file mode 100644 index ecdc3ba16..000000000 --- a/.github/workflows/i18n.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: i18n - -# Fail the build if any messages/.json drifts from en.json's keys or -# contains invalid ICU. Keeps the 15 locale catalogs in lockstep. -on: - pull_request: - push: - branches: [main] - -jobs: - check-keys: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: "20" - cache: npm - - run: npm ci - - run: npm run check:i18n diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..209e3ef4b --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 diff --git a/Dockerfile.sync b/Dockerfile.sync index 2048c9c9f..735d00f9e 100644 --- a/Dockerfile.sync +++ b/Dockerfile.sync @@ -29,6 +29,10 @@ WORKDIR /app COPY package*.json ./ COPY prisma ./prisma/ COPY scripts/daily-sync.ts ./scripts/ +# daily-sync.ts imports ../src/lib/ingestion-log for its run-summary telemetry. +# The module is dependency-free, so this one file is the whole subtree it needs; +# without it `npx tsx scripts/daily-sync.ts` dies at MODULE_NOT_FOUND before main(). +COPY src/lib/ingestion-log.ts ./src/lib/ # tsx is not in package.json deps; install it into the image so the # runtime `npx tsx` never has to fetch it. prisma generate runs against diff --git a/README.md b/README.md index 6dc781d9d..c2d805c18 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,25 @@ npx tsx prisma/seed.ts npm run dev ``` -Open [http://localhost:3000](http://localhost:3000). The dev server uses SWC and skips type-checking, so run `npx tsc --noEmit` before shipping. +Open [http://localhost:3000](http://localhost:3000). + +### Checks + +The dev server skips type-checking, so run the same checks CI does before pushing: + +```bash +npm run typecheck # tsc --noEmit +npm run lint +npm test # vitest +npm run check:i18n # every locale matches messages/en.json, ICU is valid +npm run check:secrets # no credential-shaped values in tracked files +npm run check:skill-assets # the generated skill copies match takoapi_skill/SKILL.md +npm run build +``` + +The skill body has one source of truth (`takoapi_skill/SKILL.md`) and five generated +copies — the plugin, the three npx installer assets, and the heredoc inside +`public/install.sh`. Edit the source, then run `npm run build:skill-assets`. ### Environment Variables @@ -149,32 +167,42 @@ Open [http://localhost:3000](http://localhost:3000). The dev server uses SWC and ## API Reference -### Agent registry +Full reference with request and response examples: ****. + +### Agent registry (no auth) ``` -GET /api/registry # Markdown directory of all agents (default) +GET /api/registry # Markdown directory (default) — drop straight into an LLM GET /api/registry?format=json # Structured JSON +GET /api/agents/{slug} # One agent: capabilities, protocols, pricing, health ``` -Filters: `q` (search), `category`, `protocol` (`A2A` | `OPENAI_COMPAT` | `MCP`), `limit`. +Filters: `q` (search), `category`, `protocol` (`A2A` | `OPENAI_COMPAT` | `MCP`), `kind` +(`HOSTED` | `PROJECT`), `sort` (`stars` | `calls` | `rating`), `limit`. + +Only `HOSTED` agents are callable; `PROJECT` entries are open-source repositories +listed for discovery. Responses report the totals per kind, so a client can tell +when it is holding a subset. -### Gateway (hosted agents) +### Gateway (hosted agents — requires an API key) ``` -POST /v1/agents/{slug}/message # A2A passthrough -GET /v1/agents/{slug}/stream # Server-sent events -POST /v1/chat/completions # OpenAI-compatible shim +POST /v1/agents/{slug}/message # A2A passthrough; accepts {"text": …} or a full A2A message +POST /v1/agents/{slug}/stream # Same, relayed as Server-Sent Events +POST /v1/chat/completions # OpenAI-compatible; model = agent slug, stream supported ``` -All gateway routes require an API key: `Authorization: Bearer $TAKO_KEY`. +`Authorization: Bearer $TAKO_KEY`. Default allowance 120 requests / 60 s per key. +Free agents cost nothing; priced agents debit prepaid credit, and a call that fails +— or a stream that does not run to completion — is not charged. -### Skills +### Skills (no auth) ``` -GET /api/agent # Curated skills as Markdown (md) or JSON, for AI agents -GET /api/skills # List skills (paginated, sortable) -GET /api/skills/search?q= # Full-text search -GET /api/skills/:id # Skill detail (by ID or slug) +GET /api/agent # Curated skills as Markdown (default) or JSON, for AI agents +GET /api/skills # List skills (paginated, sortable, filterable) +GET /api/skills/search?q= # Search by name, description or author +GET /api/skills/{idOrSlug} # Skill detail POST /api/skills/submit # Submit a new skill (auth required) ``` @@ -184,28 +212,45 @@ POST /api/skills/submit # Submit a new skill (auth required) POST /mcp # Streamable-HTTP MCP endpoint (JSON-RPC 2.0) ``` -Tools: `search_agents`, `get_agent`, `search_skills` (anonymous), and `invoke_agent` (send your key as `Authorization: Bearer $TAKO_KEY`). Register in any MCP client, e.g. `claude mcp add --transport http takoapi https://takoapi.com/mcp`. +Tools: `search_agents`, `get_agent`, `search_skills` (anonymous), and `invoke_agent` +(send your key as `Authorization: Bearer $TAKO_KEY`). Register in any MCP client, e.g. +`claude mcp add --transport http takoapi https://takoapi.com/mcp`. ## Project Structure ``` src/ app/ - page.tsx # Home - agents/ # Agent marketplace + detail pages - skills/ # Skills browse + detail pages - trending/ # Trending leaderboard - dashboard/ # Developer console (API keys, usage) - admin/ # Moderation - api/ # REST API routes - v1/ # Gateway routes (A2A / OpenAI-compatible) - sitemap.ts, robots.ts # SEO + [locale]/ # Every page, under a locale segment (en unprefixed, 14 more) + page.tsx # Home + agents/ # Agent marketplace + detail pages + scenarios/ # Use-case landing pages + skills/, trending/ # Coding-agent skills catalog + leaderboard + blog/, install/, badge/ + dashboard/ # Developer console (API keys, usage, credits) + profile/, bookmarks/ # My listings, saved agents + submit-agent/, submit/ # Publish an agent / a skill + admin/ # Moderation + auth/ # Sign in / up / password reset + api/ # REST API routes (public catalog, user, admin, cron, billing) + v1/ # Gateway routes (A2A passthrough, SSE, OpenAI-compatible) + mcp/ # Hosted MCP server (Streamable HTTP) + sitemap.xml/, sitemap/ # Sitemap index + chunked children + robots.ts, opengraph-image.tsx + proxy.ts # Locale routing (Next 16 request interceptor) components/ # UI + layout - lib/ # auth, prisma, seo, helpers + i18n/ # next-intl routing + request config + lib/ # auth, prisma, billing, gateway, seo, helpers + hooks/ # client hooks +messages/ # 15 locale catalogs (checked by `npm run check:i18n`) prisma/ schema.prisma # Database schema + migrations/ # Applied manually in production (see docs/agent-marketplace/HANDOFF.md §5) seed.ts # Database seeder -scripts/ # Scrapers + maintenance scripts +scripts/ # Scrapers, sync jobs, CI checks (secrets, skill assets) +packages/takoapi-install/ # `npx takoapi-install` +plugins/takoapi/ # Claude Code plugin +takoapi_skill/SKILL.md # Source of truth for the skill body (run `npm run build:skill-assets`) ``` ## Deployment diff --git a/docs/06-cron-schedule.md b/docs/06-cron-schedule.md index 91dea8ece..9a5c52e53 100644 --- a/docs/06-cron-schedule.md +++ b/docs/06-cron-schedule.md @@ -1,39 +1,41 @@ # Cron 调度 — 权威清单与幂等建置 -> 三个自动摄取端点全靠 **Cloud Scheduler** 触发,但调度配置此前不在代码库里 -> (无 `vercel.json`,`cloudbuild.yaml` 是 stale 模板)。本文件是它们的**唯一权威来源**: -> 频率、参数、鉴权,以及可复现的 `gcloud` 建置命令。改调度请改这里并同步执行。 -> 关联:`docs/05-automation-ingestion-review.md` §7。 +> 所有自动摄取与维护端点都靠 **Cloud Scheduler** 触发,调度配置本身不在代码库里 +> (无 `vercel.json`)。本文件是它们的**唯一权威来源**:频率、参数、鉴权,以及可复现的 +> `gcloud` 建置命令。改调度请改这里并同步执行。 +> 关联:`docs/05-automation-ingestion-review.md` §7、`docs/08-tech-review-2026-09-05.md` §2.9。 ## 端点一览 | Job | 端点 | 建议频率 | 参数 | 落库策略 | |-----|------|----------|------|----------| -| `tako-scrape-agents` | `GET /api/cron/scrape-agents` | 每日 04:00 UTC | `?pages=1&minStars=200&max=2000` | PROJECT 直接 APPROVED;admin REJECT/DISABLE 的 slug 会被跳过 | -| `tako-import-hosted` | `GET /api/cron/import-hosted` | 每日 04:30 UTC | `?max=0`(不限) | HOSTED 落 PENDING,待 `/admin/agents` 审核;跨域变更也降级 PENDING | -| `tako-health` | `GET /api/cron/health` | 每 6 小时 | —— | 更新 `healthStatus`/`healthCheckedAt` | +| `tako-scrape-agents` | `GET /api/cron/scrape-agents` | 每日 04:00 UTC | `?pages=1&minStars=200&max=2000` | PROJECT 直接 APPROVED;admin REJECT/DISABLE 的 slug 会被跳过 | +| `tako-import-hosted` | `GET /api/cron/import-hosted` | 每日 04:30 UTC | `?max=0`(不限) | HOSTED 落 PENDING,待 `/admin/agents` 审核;跨域变更也降级 PENDING | +| `tako-check-github` | `GET /api/cron/check-github` | 每周一 05:00 UTC | —— | 探测 skill 的 GitHub 链接是否 404,写 `ghStatus`/`ghCheckedAt` | +| `tako-health` | `GET /api/cron/health` | 每 6 小时 | —— | 更新 `healthStatus`/`healthCheckedAt`,状态**翻转**时追加 `AgentHealthCheck` | +| `tako-weekly-digest` | `GET /api/cron/weekly-digest` | 每周一 09:00 UTC | —— | 给已验证订阅者发周报 | +| `tako-retention` | `GET /api/cron/retention` | 每日 02:00 UTC | —— | 删除 90 天前的 `RequestLog`(分批,单次上限 10 万行) | +| `tako-ingestion-health` | `GET /api/cron/ingestion-health` | 每 6 小时 | —— | **只读**。按来源报告目录新鲜度,超过 72 小时无新增即 `stale: true` | +| `takoapi-daily-sync` | Cloud Run **Job**(非 HTTP 端点) | 每日 03:00 UTC | —— | 从 clawskills.sh 增量同步 skills;见 `scripts/deploy-sync-job.sh` | -所有端点鉴权:`Authorization: Bearer `(见 `src/lib/cron-auth.ts`; -`CRON_SECRET` 未配置时端点 fail-closed 返回 401)。响应含 `durationMs` + `ranAt`, -调度日志据此判断是否接近超时 / 被截断。 +所有 HTTP 端点鉴权:`Authorization: Bearer `(见 `src/lib/cron-auth.ts`)。 +**只接受请求头**——`?key=` 形式已移除(Cloud Run 请求日志会记录完整 URL 含查询串,保留 30 天, +等于把密钥抄进一份 `logging.viewer` 就能读的日志)。`CRON_SECRET` 未配置时端点 fail-closed 返回 401。 -## 建置(幂等 create-or-update) +响应含 `durationMs` + `ranAt`;摄取类端点另外向 stdout 打一行 +`{"event":"ingestion_run", …}` 结构化日志(`src/lib/ingestion-log.ts`),供告警使用。 + +## 建置(幂等 create-or-update) ```bash set -euo pipefail PROJECT=takoapi-491505 REGION=us-central1 -LOCATION=us-central1 # Cloud Scheduler location -BASE=https://takoapi.com # 或 run URL: https://takoapi-429522911261.us-central1.run.app +LOCATION=us-central1 # Cloud Scheduler location +BASE=https://takoapi.com # 或 run URL -# CRON_SECRET 来源(二选一,取决于是否启用 Secret Manager — 见 docs/00-infrastructure.md): -# a) Secret Manager: SECRET=$(gcloud secrets versions access latest --secret=tako-cron-secret --project="$PROJECT") -# b) 若 Secret Manager 未启用,从 Cloud Run 服务 env 读取当前值: -# SECRET=$(gcloud run services describe takoapi --region "$REGION" --project="$PROJECT" \ -# --format='value(spec.template.spec.containers[0].env)' | tr ',' '\n' | grep -A1 CRON_SECRET | tail -1) -# 幂等 upsert:create 失败(已存在)即改 update。 upsert_job () { local name="$1" schedule="$2" uri="$3" local verb=create @@ -47,30 +49,53 @@ upsert_job () { --max-retry-attempts=1 } -upsert_job tako-scrape-agents "0 4 * * *" "${BASE}/api/cron/scrape-agents?pages=1&minStars=200&max=2000" -upsert_job tako-import-hosted "30 4 * * *" "${BASE}/api/cron/import-hosted?max=0" -upsert_job tako-health "0 */6 * * *" "${BASE}/api/cron/health" +upsert_job tako-scrape-agents "0 4 * * *" "${BASE}/api/cron/scrape-agents?pages=1&minStars=200&max=2000" +upsert_job tako-import-hosted "30 4 * * *" "${BASE}/api/cron/import-hosted?max=0" +upsert_job tako-check-github "0 5 * * 1" "${BASE}/api/cron/check-github" +upsert_job tako-health "0 */6 * * *" "${BASE}/api/cron/health" +upsert_job tako-weekly-digest "0 9 * * 1" "${BASE}/api/cron/weekly-digest" +upsert_job tako-retention "0 2 * * *" "${BASE}/api/cron/retention" +upsert_job tako-ingestion-health "15 */6 * * *" "${BASE}/api/cron/ingestion-health" ``` -> `--attempt-deadline=320s` 略高于 scrape-agents 的 `maxDuration=300`,以免 Scheduler 先于端点超时。 -> **Cloud Run 服务的 request timeout 必须 ≥ 320s**(否则那才是真正的截断点);核对: +> `--attempt-deadline=320s` 略高于 scrape-agents 的 `maxDuration=300`,以免 Scheduler 先于端点超时。 +> **Cloud Run 服务的 request timeout 必须 ≥ 320s**(否则那才是真正的截断点);核对: > `gcloud run services describe takoapi --region us-central1 --project=takoapi-491505 --format='value(spec.template.spec.timeoutSeconds)'`。 +## 必须配的告警(否则等于没做) + +摄取管道曾经**静默停摆两个月**:`takoapi-daily-sync` 每天 03:00 正常触发、正常退出 0, +但 clawskills.sh 改版后选择器失配,一条都没导入。目录里最新的 skill 停在 2026-07-04, +而没有任何信号提示。现在 job 在"跑了但一条没找到"时会以非 0 退出, +但**只有配了告警才有人知道**。建议在 Cloud Monitoring 建三条: + +| 告警 | 条件 | 为什么 | +|---|---|---| +| 摄取任务失败 | Cloud Scheduler / Cloud Run Job 执行失败 ≥ 1 次 | 抓取失配、鉴权过期 | +| 目录停滞 | `/api/cron/ingestion-health` 返回体里任一来源 `stale: true` | 唯一能抓住"成功但没产出"的信号 | +| 网关 5xx 率 | `jsonPayload.event="gateway_call_rejected"` 或 5xx 超阈值 | 上游批量失效 | + ## 运维 ```bash -# 列出 / 查看 gcloud scheduler jobs list --location=us-central1 --project=takoapi-491505 -# 手动触发一次(冒烟) -gcloud scheduler jobs run tako-health --location=us-central1 --project=takoapi-491505 -# 轮换 CRON_SECRET 后,重跑上面的 upsert_job 即可刷新 header -# 暂停 / 恢复 +gcloud scheduler jobs run tako-ingestion-health --location=us-central1 --project=takoapi-491505 gcloud scheduler jobs pause tako-scrape-agents --location=us-central1 --project=takoapi-491505 gcloud scheduler jobs resume tako-scrape-agents --location=us-central1 --project=takoapi-491505 ``` +## 密钥到期日历(人工提醒,没有代码会替你做) + +| 密钥 | 到期 / 轮换周期 | 到期后的症状 | +|---|---|---| +| `APPLE_CLIENT_SECRET` | **2026-09-25**(JWT,Apple 上限 6 个月) | Apple 登录静默失败,无报错页 | +| `tako-github-token` | GitHub PAT,按创建时设定 | 抓取返回 401;`scrape-agents` 现在会抛错而非静默返回空 | +| `CRON_SECRET` | 建议每 6 个月 | 轮换后必须重跑上面的 `upsert_job`(header 里带的是明文值) | +| Cloud SQL 密码 | 见 `docs/08-tech-review-2026-09-05.md` §1.1 —— **待轮换** | —— | + ## 安全备注 -- header 里带的是明文 bearer secret;`gcloud scheduler jobs describe` 可见,权限同项目其他密钥。 - 若日后启用 OIDC(service account),可改用 `--oidc-service-account-email` 并把 `cron-auth.ts` 换成校验 OIDC token。 -- **别把 `SECRET` 硬编码进本文件或提交历史**;上面的命令都从 Secret Manager / 运行时 env 现取。 +- header 里带的是明文 bearer secret;`gcloud scheduler jobs describe` 可见,权限同项目其他密钥。 + 若日后启用 OIDC(service account),可改用 `--oidc-service-account-email` 并把 `cron-auth.ts` 换成校验 OIDC token。 +- **别把 `SECRET` 硬编码进本文件或提交历史**;上面的命令从 Secret Manager 现取。 +- 仓库根有 `npm run check:secrets`(CI 也跑),会在任何被追踪文件里出现凭据形态的值时让构建失败。 diff --git a/docs/analytics-plan-2026-08-21.md b/docs/analytics-plan-2026-08-21.md new file mode 100644 index 000000000..af864be6d --- /dev/null +++ b/docs/analytics-plan-2026-08-21.md @@ -0,0 +1,1804 @@ +# TakoAPI 打点建设方案(2026-08-21) + +> ## ⚠️ 核实状态(2026-08-21,写在最前面) +> +> **本文初稿是在一个落后 `origin/main` 18 个 commit 的工作树上写的。** +> 已针对 `origin/main` = **`b868319c`**(2026-08-08,"chore(deploy): make cloudbuild safe +> + add ignore files (#51)")逐条复核,并连生产(Cloud Run / Cloud SQL / Cloud Scheduler / +> Cloud Logging,只读)实测。复核结果: +> +> - **一条地基被推翻**:§0.1 原有的「`cloudbuild.yaml` 已与线上漂移、照它跑会把生产密钥 +> 覆盖成占位串」**是错的**,已就地更正(见 §0.1 与 §2.3 第 2 条)。它之所以出现,正是因为 +> 初稿读的是陈旧 checkout —— 那句话描述的是本地文件,不是 `origin/main` 上的文件。 +> - **一条口径被收窄**:§0.1 / §1 的「402/429/401 **无任何记录**」不准确。平台请求日志 +> (timestamp / status / URL,`_Default` bucket 保留 30 天)是有的,缺的是**归因**。 +> `gateway_call_rejected` 仍然该做,但价值主张是「从不可归因到可归因」。 +> - **两条待确认项已结**:`_Default` 保留期 = 30 天;Cloud Run `cpu-throttling` 注解为空 +> (即默认节流态)、无 `minScale`、`maxScale=20`、`containerConcurrency=80`。见 §0.4。 +> - §1043 / §1118 关于「PostgreSQL 的 `DELETE` 没有 `LIMIT`」和「别用 `prisma/sql` TypedSQL」 +> 两条**经核实成立**,且仓里现有代码没有踩这两个坑——它们是**给实现者的约束**,不是待修的 bug。 +> +> **本文所描述的现状,有一部分已被同一个 PR 改掉了**(见 §0.6)。读正文时以 §0.6 为准。 +> +> **方法论来源**:一份内部的可移植打点手册(10 节,下称"蓝图",从另一个产品的生产事故提炼; +> 出处不在本仓,正文中以 `` 指代,需要原文请找维护者)。本文不是那本手册的改名版—— +> **蓝图的参照项目是 web+mobile 的 C 端产品,TakoAPI 是开发者 API 产品**,蓝图里有相当一部分 +> 条目在这里根本不成立。适用的部分照抄并落到本仓具体文件;不适用的部分在**附录 A** 逐条列出 +> 理由与替代做法。 +> +> 所有路径均为**仓库相对路径**(`src/…`、`prisma/…`)。行号会漂移,按符号名搜索为准。 +> +> 赶时间的读法:**§0.3(先补洞,再加事件)→ §1(问题→指标→事件映射)→ §2.4(Cloud Run 的 CPU +> 节流,决定 sink 怎么写)→ §4(chokepoint)→ §7(口径陷阱)→ §10(Phase 0 checklist)**。 +> 附录 A 是本文与蓝图差异最大的部分,建议通读。 + +--- + +## 摘要 + +**一句话**:TakoAPI 的打点是**服务端权威、单一 chokepoint、双 sink(Cloud Logging 结构化日志 +为"仓"+ Postgres 精简热表为"join 面")**的体系,锚点是 `api_key_id` 而不是浏览器会话, +北极星(每周成功完成的 agent 任务数)的唯一权威落点是 `src/lib/billing.ts` 的 `meterInvocation()` +及其新增的 `gateway_call_rejected` 补集。 + +**与蓝图的最大差异(四条)**: + +1. **仓不是 BigQuery,是 Cloud Logging**。理由是成本红线:生产是 512Mi 单实例 + db-f1-micro, + 几乎零收入;而 `cloudbuild.yaml` 已配 `logging: CLOUD_LOGGING_ONLY`,Cloud Run 的 stdout + 本来就免费进 Cloud Logging(_Default bucket 每项目 50 GiB/月免费)。Phase 1 用 + `console.log(JSON.stringify(...))` 当仓,Phase 2 再挂 log sink → BigQuery。这让"仓 sink" + 的成本、依赖、失败模式全部归零——`console.log` 没有"不可用"状态。 +2. **Phase 0 完全没有客户端打点**。蓝图 §3 整节(batch envelope / 时钟 clamp / 防伪造 + allowlist / properties 上限)是为客户端事件入口设计的;TakoAPI 的钱和北极星 100% 在服务端, + 浏览器只承担 SEO 与 dashboard。客户端入口推到 Phase 2,且**必须与 consent gate 同期落地** + (GA4 已在 15 个 locale 跑着却没有任何 consent banner,这是既存债务,不能靠加埋点放大它)。 +3. **采样的结论与蓝图相反**。蓝图 §7.6 的实测结论是"多数体量下答案是不采";TakoAPI 必须采样, + 因为 `/api/registry` 默认 `limit=1000` 一次吐全目录、`/api/badge/[slug]` 每次渲染写一行 + `RequestLog`、MCP 三个读工具全匿名——这三类天生高频、单条价值极低,而热表是 0.6 GB 的 + db-f1-micro。**采样率必须作为事件属性入库**(`sample_rate`),否则还原时无从加权。 +4. **蓝图 §1.1 的"buffer + 定时 flush + SIGTERM drain"范式在这里会静默失效**。它默认进程常驻; + Cloud Run 默认"CPU 仅在处理请求期间分配",**响应发出后未 settle 的 promise 和不 tick 的定时器 + 一样不可靠**。所以本方案的日志 sink 是**请求路径内写出(响应返回之前)**的,DB sink 的 flush 由**流量驱动**, + 并新增一条"日志 ↔ `Invocation` 丢失率"对账(§2.4、§8.3 ④)。这条同时暴露了一个 + **已经存在的资金风险**:今天 `void meterInvocation(...)` 里的 DEBIT 扣款也在这条约束之下。 + +--- + +## §0 前提与假设 + +### 0.1 调研已确认的事实(本方案的地基) + +| 事实 | 依据 | +|---|---| +| 北极星 = 每周经网关**成功完成**的 agent 任务数 | `docs/agent-marketplace/00-vision-and-positioning.md:99` | +| 生产 DB = Cloud SQL PG15 / **db-f1-micro(1 shared CPU、0.6 GB)**、无连接池、无 Redis | `docs/00-infrastructure.md` | +| Cloud Run 512Mi、`--allow-unauthenticated`、`logging: CLOUD_LOGGING_ONLY` | `cloudbuild.yaml` | +| Secret Manager **已就绪**(不是待办):API 已启用、6 个 secret 已建、Cloud Run SA 已授权、revision `takoapi-00043-wcq` 起 env 全走 `secretKeyRef`。只剩「步骤 5 密钥轮换」未勾 | `docs/03-secret-hardening.md`「状态追踪」步骤 1–4 全部 `[x]`(2026-04-22) | +| ~~**`cloudbuild.yaml` 已与线上漂移**:`--set-env-vars` 里是占位串,照它跑一次 = 把生产密钥覆盖成占位值~~ **← 这一条是错的,已推翻,见下方注** | ~~`cloudbuild.yaml`~~ | +| 仓库 MIT 公开;`public/install.sh`、`takoapi_skill/SKILL.md`、`packages/takoapi-install` 是公开分发物 | `LICENSE`、这些文件本身 | +| GA4 `G-PPXV98MJ4Y` 硬编码在 `src/components/Analytics.tsx`,**全仓无一处 `gtag()` 自定义事件**,无 consent banner | 该文件 + grep | +| 网关三条路由与 `/mcp` **完全没有 `RequestLog` 包裹** | `src/app/v1/**`、`src/app/mcp/route.ts` | +| `meterInvocation()` 在 `prisma.$transaction` 里同时写 Invocation + `Agent.callsCount++` + DEBIT ledger + 扣余额,`catch {}` 吞异常 | `src/lib/billing.ts` | +| `Invocation` 表**没有** `source` / `client` / `userAgent` 列;`taskState` 有列但三条网关路由都不传 | `prisma/schema.prisma:553-573` + `src/app/v1/**` | +| MCP `invoke_agent` 内部 `fetch(${TAKOAPI_ORIGIN}/v1/agents/{slug}/message)`,**不带任何来源标识** | `src/lib/mcp/tools.ts` | +| MCP 是无状态的;`initialize` 收到的 `params.clientInfo`(name/version)**被代码直接丢弃** | `src/app/mcp/route.ts` 的 `case "initialize"` | +| `ApiKey` 只存 SHA-256 + 6 位 prefix;`scopes[]` / `rateLimit` / `monthlyQuota` 有列**无强制执行** | `src/lib/apikey.ts`、`prisma/schema.prisma:534-551` | +| 限流写死 60s/120 次,key 为 `gw::`,**不读 `ApiKey.rateLimit`** | `src/app/v1/**` + `src/lib/ratelimit.ts` | +| `RequestLog.ip` 是**明文全量保留**,无 TTL / 无归档 / 无采样 | `src/lib/requestLog.ts` + `prisma/schema.prisma:186-201` | +| 本仓**无测试框架**(`package.json` 无 test script、无 vitest/jest),**无 CI 构建/部署 workflow**(`.github/workflows/` 只有 `i18n.yml`) | `package.json`、`.github/workflows/` | +| `prisma/migrations/` **存在**(11 个目录,含 `0_init` baseline) | `ls prisma/migrations/`——注意 `docs/00-infrastructure.md` 里"无 migrations 目录"的说法已过时 | + +> **注:被划掉的 `cloudbuild.yaml` 那一行为什么是错的(两层都错)** +> +> 1. **它早就修好了。**`origin/main` 的 `cloudbuild.yaml` 在 commit `b868319c` +> (2026-08-08,PR #51)已经重写:用 `--update-env-vars` 传公开值(真值,非占位), +> 并用 `--update-secrets` 绑定 `DATABASE_URL` / `NEXTAUTH_SECRET` / `GOOGLE_CLIENT_SECRET` / +> `APPLE_CLIENT_SECRET` / `RESEND_API_KEY` / `CRON_SECRET`。文件头注释自己写明用 +> `--update-*` 而非 `--set-*`,以保留未列出的既有绑定。占位串只存在于**初稿所读的那个 +> 陈旧本地工作树**里。 +> 2. **即使拿旧文件去跑,破坏机制也不是"覆盖成占位串"。**`--set-env-vars` 里有 4 个名字与 +> 线上的 `secretKeyRef` 撞名。gcloud 的 `_PruneMapping` 只清 literal 类型的 env +> (`filter_func` 要求 `valueFrom is None`),随后 `literals.update()` 命中同名的 +> 非 literal 条目会 `raise KeyError`,被上层转成 +> `ConfigurationError: Cannot update environment variable [DATABASE_URL] to string +> literal because it has already been set with a different type.` +> 这是**客户端校验,发生在调用 API 之前**——deploy step 会红着失败,一个字节都写不到生产。 +> 3. **而且它从来没在真实流水线里跑过。**`gcloud builds triggers list` 返回空(无触发器); +> 最近一次成功构建只有一个 docker build step、无 deploy step、source 是 `storageSource` +> (人工 `gcloud builds submit`)。它的风险形状是「死配置」,不是「定时炸弹」。 +> +> **对本方案的影响**:§2.3 第 2 条原来的整段警告作废(已就地重写);遥测 env 的落地动作 +> **不需要**先"修 `cloudbuild.yaml` 的漂移"这一前置步骤。但「改 `cloudbuild.yaml` 不等于 +> 改了生产」这个**结论仍然成立**,原因换成了更简单的一个:**没有任何触发器在跑它**, +> 部署是人工 `gcloud run deploy`。 + +### 0.2 调研之外、本次实测新增的事实(每条都直接改变方案) + +1. **`/skills/[slug]` 是 client component,`/agents/[slug]` 是 server component。** + `src/app/[locale]/skills/[slug]/page.tsx` 第一行是 `"use client"`,数据靠浏览器 + `fetch('/api/skills/${slug}')` 拿——所以 skill 浏览**会经过 API 层**,`viewsCount++` 就发生在 + `src/app/api/skills/[id]/route.ts` 的 `prisma.skill.update({ data: { viewsCount: { increment: 1 } } })`。 + **注意:这条路由并没有被 `withRequestLog` 包住**——`api/skills/[id]/route.ts` 的 import 里 + 根本没有 `requestLog`(要看当前覆盖面跑 + `grep -rl 'withRequestLog\|logRequest' src/app/api`,**别把数量写进正文**,§3.5 的规则对本文 + 自己生效;本次审校就是在这里抓到一个已经错了的数字)。所以 skill 详情浏览今天**只有一个 + counter,没有任何带时间戳的行**,无法按天/按 locale/按人机切分。 + 而 `src/app/[locale]/agents/[slug]/page.tsx` 直接 `prisma.agent.findFirst()`(含 + `generateMetadata`,且 `export const dynamic = "force-dynamic"` 所以每次请求都真的执行), + **不经过任何 API 路由**。 + **后果:agent 详情页的浏览量目前 100% 不可见**——`RequestLog` 只包部分 `/api/*`,`src/proxy.ts` + 的 matcher 虽然覆盖页面路由但不写任何日志。业务问题 Q4("哪些 agent 只有目录页浏览、零调用") + 现在**完全无法回答**。两个事件因此落点不同:`skill_detail_viewed` 发在 API 路由, + `agent_detail_viewed` 必须发在 server component 的 page 函数里(**不是 `generateMetadata` 里** + ——它与 page 函数各跑一次,发在那里会把每次浏览记成两条)。 + +2. **`RequestLog.path` 的基数语义在 badge 路由上是破的。** + 所有 `withRequestLog(req, "/api/agents/[slug]", …)` 传的都是**模板**(低基数), + 唯独 `src/app/api/badge/[slug]/route.ts:73` 传的是插值后的 + `` `/api/badge/${slug}` ``(高基数,每个被徽章化的 repo 一个值)。这是**刻意的**——徽章采纳 + 数就是这么数出来的——但它意味着:`GROUP BY path` 混了两种语义;`@@index([path, createdAt])` + 会随徽章采纳线性膨胀。方案里必须把徽章从 `RequestLog` 迁到独立事件(见 §6.4)。 + +3. **402 / 429 / 401 的调用根本不产生 `Invocation` 行。** + `checkCreditPreflight` 与 `checkRateLimit` 都在 `meterInvocation()` **之前** return + (`src/app/v1/agents/[slug]/message/route.ts` 的 credit gate 分支、`rateLimitResponse`)。 + 所以"从 Invocation 表算网关错误率"必然错——分母里根本没有被我们自己挡掉的那部分。 + 这是 Q1 的核心陷阱,也是 `gateway_call_rejected` 事件存在的唯一理由。 + +4. **SSE 流式路由在拿到 upstream response header 的那一刻就计量并计费。** + `src/app/v1/agents/[slug]/stream/route.ts` 在 `fetch` 返回后立刻 + `meterInvocation({ status: upstream.status, … })`,然后才把流 pass through。流中途上游报错、 + 客户端断开、120s 超时——**一律不被记录,且已经收过钱**。所以 stream 的成功率天然高估, + 且与 `message` 路由的成功率**不可比、不可相加**(蓝图 §6.2「同名不同物」在本项目的对应物)。 + **最刺眼的一个子情形**:紧跟计量之后的 `if (!upstream.body)` 分支返回 502, + 但那次调用**已经按 `upstream.status`(可能是 200)计量并计费了**——`Invocation` 里躺着一条 + "成功且已收费"的行,用户拿到的是 502。这不是假想边界,是代码里现成的一条路径 + (`stream/route.ts` 的 `!upstream.body` 分支),事件 3 的 `reason` 枚举必须能表达它。 + +5. **Cloud Run 的 CPU 在响应 flush 之后就被节流——`void doAsync()` 不等于"稍后一定跑完"。** + `cloudbuild.yaml` 的 `gcloud run deploy` 既没有 `--no-cpu-throttling` 也没有 `--min-instances`, + `docs/00-infrastructure.md` 记录的服务 spec 里也没有相关注解 → 默认是 + **"CPU 仅在处理请求期间分配"**。含义:**响应发出后仍未 settle 的 promise,要么等到该实例的 + 下一次请求才继续,要么随实例回收永远不跑**;`setInterval` / `setTimeout` 在两次请求之间**不 tick**。 + 这不是新代码才有的问题——**今天的 `void meterInvocation(...)`(内含 DEBIT 扣款 + 余额递减) + 与 `logRequest()` 已经全部暴露在这条约束下**:一次已经返回 200 的收费调用,有可能既没有 + `Invocation` 行也没有扣款。 + 蓝图 §1.1 的核心范式(内存有界 buffer + 定时 flush + SIGTERM drain)**默认进程是常驻的**, + 照抄到这里会得到一个"看起来在跑、实际按流量随机丢"的管线。处置见 §2.4。 + **部署侧实际值待确认**(本次只从仓内文件推断,未连生产): + `gcloud run services describe takoapi --region us-central1 \` + `--format="value(spec.template.metadata.annotations['run.googleapis.com/cpu-throttling'])"` + ——返回空或 `true` 即为节流(默认态)。 + +### 0.3 必须先补的洞(Phase 0 的实际内容) + +打点方案的前半段不是"加事件",是**把已有管线补到能回答问题**。下表这些不补,后面所有事件都是 +在错误的地基上加装饰: + +| # | 洞 | 修在哪 | +|---|---|---| +| H1 | `Invocation` 无 `source` / `client` 维度 → MCP 转调与裸 curl 无法区分 | `prisma/schema.prisma` 加列 + `src/lib/mcp/tools.ts` 注入 `X-Tako-Client` header | +| H2 | 三条网关路由都不传 `taskState` → "成功完成"只能靠 HTTP status 近似 | `src/app/v1/agents/[slug]/message/route.ts` 解析 A2A 响应的 `result.status.state` | +| H3 | 402/429/401 **无 DB 行、无事件、不可归因**(平台请求日志有 timestamp/status/URL,保留 30 天,但没有 userId / apiKeyId / agentId / 拒绝原因,402 与 429 在日志里长得一样,且无法与 `Invocation` join)| 新事件 `gateway_call_rejected` | +| H4 | SSE 提前计量 | 新事件 `gateway_stream_closed` + 口径规则 | +| H5 | `RequestLog.ip` 明文 + 无 TTL,在 0.6 GB 实例上无限增长 | `src/lib/requestLog.ts` 改 `ipHash` + 新增保留期 cron | +| H6 | `/mcp` 零日志 | chokepoint 事件 `mcp_session_initialized` / `mcp_tool_invoked` | +| H7 | **MCP 的四个工具全部走公网 loopback 调自己的 API**(`src/lib/mcp/tools.ts` 的 `getJson()` → `/api/registry`、`/api/agents/{slug}`、`/api/skills/search`、`/api/agent`;`invoke_agent` → `/v1/agents/{slug}/message`)。不处理 = 每一次 MCP 读工具调用都在 API 侧再产生一条事件(双计),且那条 loopback 请求的 IP 是 **Cloud Run 自己的出口 IP**,会把 `client_fp` 污染成一个巨大的热值 | 在 `getJson()` 与 `invoke_agent` 的 fetch **统一注入 `X-Tako-Client`**(§5.4),服务端见到它就把该请求标记 `source='mcp'` 且**不计入匿名指纹**;口径侧按 §7.2 去重 | + +### 0.4 待确认项(**不要当成事实用**) + +| 项 | 为什么重要 | 确认方法 | +|---|---|---| +| 生产 `Invocation` / `RequestLog` 当前行数 | 决定采样率与 TTL 的起点;`docs/00-infrastructure.md`(2026-04-22 快照)没列这两张表 | `cloud-sql-proxy takoapi-491505:us-central1:takoapi-db --port 5434` 后 `SELECT count(*), min("createdAt"), max("createdAt") FROM "Invocation";` 与 `"RequestLog"` 同 | +| ~~Cloud Logging `_Default` bucket 的保留期是否被改过~~ **已实测 = 30 天(未被改过)** | 直接决定"仓"的保留期,也决定 GDPR 的自然删除边界(§9.3) | `gcloud logging buckets describe _Default --location=global --project=takoapi-491505 --format='value(retentionDays)'` → `30` | +| Cloud Run 上 `GOOGLE_SITE_VERIFICATION` 是否真的设了 / GSC 是否已验证 | Q9(15 语言 SEO ROI)的曝光侧数据源 | `gcloud run services describe takoapi --region us-central1 --format=json \| jq '.spec.template.spec.containers[0].env'`;GSC 控制台确认 takoapi.com 属性 | +| GA4 property `G-PPXV98MJ4Y` 的数据保留期设置(默认 2 个月,可改 14 个月)与是否有人在看 | 决定 GA4 是保留、并行还是弃用 | GA4 后台 → 管理 → 数据设置 → 数据保留 | +| `SkillEvent`(生产 1 行)、`KolContact`(1042 行)等 9 张孤立表的写入方 | 若有外部写入方,本方案的新表可能重复造轮子 | `docs/00-infrastructure.md`「待澄清事项」已挂账,本方案**假设它们全是死表**(见 §6.3 为什么不复用 `SkillEvent`) | +| ~~线上 Cloud Run 的 `run.googleapis.com/cpu-throttling` 注解与 `min-instances` 实际值~~ **已实测:注解为空(= 默认节流)、无 `minScale`、`maxScale=20`、`containerConcurrency=80`** → fire-and-forget **确实可能跑不完**,扣款确实可能在丢;因为 concurrency=80,丢失是**概率性**的(只咬"实例转入空闲前的最后一批"与缩容瞬间),不是每次都丢 | 决定 fire-and-forget 到底能不能跑完——**同时决定今天的计费扣款有没有在丢**(§0.2-5、§2.4) | `gcloud run services describe takoapi --region us-central1 --format=json \| jq '{ann:.spec.template.metadata.annotations, scale:.spec.template.metadata.annotations["autoscaling.knative.dev/minScale"]}'` | + +**已从本表移出的三条**(原本挂在这里,本次实测查清,结论直接落进正文——留档是为了让下一个读者 +知道这三条被查过,而不是被忘了): + +1. **注销流程不存在。**`src/` 下零个 `prisma.user.delete` / `deleteUser` / delete-account 路由; + `/api/admin/users/[id]/route.ts` 只导出 `PATCH`;`User` 模型也没有 `status` / `deletedAt`。 + → §9.3 的 GDPR 删除**今天没有触发点**:规则现在就定,实现随注销功能一起做。 +2. **`Invocation.costUsd` 的语义是"上游成本"**(`docs/agent-marketplace/04-data-model.md:120` + 的行内注释原文就是「上游成本」),列已建但 `meterInvocation()` 从不写它。 + → 它不是废列,是**未接线的列**。毛利(`billedUsd − costUsd`)在它被写入前无法计算, + **Q6 的口径里不许出现"毛利"二字**,只能说"收入 / 消耗 / 沉淀"。 +3. **`RESEND_API_KEY` 确认零引用**(`src/` 下 grep `resend` 零命中)。邮件通道不存在 + → 激活漏斗里没有"验证邮件送达/点击"这一段,别在漏斗图上画它。 + +### 0.6 本 PR 已经改掉的现状(读正文时以本节为准) + +本文与一批核实后的修复走的是**同一个 PR**(分支 `fix/verified-findings-2026-08-21`,基于 +`origin/main` = `b868319c`)。以下条目在正文里仍按"修复前"描述,**代码已经变了**: + +| 正文里的说法 | 现在的实际情况 | 对打点方案的影响 | +|---|---|---| +| `meterInvocation()` 一律 `void`,`catch {}` 吞异常 | 拆成 `debitInvocation`(计费,**响应前 await**)与 `meterInvocation`(遥测,可 fire-and-forget);空 catch 换成 `console.error` | §2.4 R1 的结论不变,但"扣款也在 fire-and-forget 里"这个具体例子已经不成立 | +| SSE 拿到 header 就计费 | 改为 `startInvocation`(流开始前建未计价行)+ `settleInvocation`(流真正结束时定价并扣款);新增 errorCode:`NO_STREAM_BODY` / `STREAM_ABORTED` / `CLIENT_DISCONNECTED` | H4「stream 终局不记」**已修**;`gateway_call_completed` 的 stream 分支要对齐这三个 errorCode | +| 限流 key 为 `gw::`,**不读 `ApiKey.rateLimit`** | 网关限流改为 `perIp: false`(key 就是 `gw:`);`max` 改读 `ApiKey.rateLimit`(空则 120) | 「限流可被伪造 XFF 绕过」已修;`rate_limited` 事件的分母口径随之变成"每 key"而非"每 key×IP" | +| `RequestLog.ip` 明文全量保留、无 TTL | 列改为 `ipHash`(盐化 + 按 UTC 日轮换,`TAKO_IP_SALT` 未设则写 null);新增 `/api/cron/retention` 按 90 天分批清理 | §5.3「日轮换指纹」的设计**已经在 `RequestLog` 上落地**,`client_fp` 可以直接复用同一套盐与轮换口径 | +| 402/429/401 无事件 | 三条 `/v1` 路由 + `/mcp` 已在拒绝分支发结构化日志 `gateway_call_rejected`(带 `reason` / `route` 模板 / `apiKeyId` / `userId` / `agentSlug`,402 另带 `requiredUsd` / `balanceUsd`) | §4 的 chokepoint 设计不变,但**事件 2 已有一个 stdout 版本的雏形**;Phase 1 接 `ProductEvent` 时是"接上第二个 sink",不是从零发事件 | +| `Agent.healthStatus` 只有当前值 | 新增 `AgentHealthCheck`(**只写状态翻转**,不是每小时每 agent 一行);`HealthSummary` 加 `transitions` 并整行结构化输出到 Cloud Logging | 「agent 什么时候开始挂的」现在可答;健康度相关指标不必再自己造历史表 | +| PayPal 手续费与正额是两个事务 | 合并为单事务 `applyTopUp`;capture 之后的三条静默 `return "failed"` 全部带上下文告警;webhook 失败时返回 500 让 PayPal 重投 | 影响 §6 的收入口径:`TOPUP` 行现在可以当作"这笔充值已完整入账"的可靠代理 | +| `isAuthorizedCron` 接受 `?key=` | 只接受 `Authorization: Bearer`,比较改 `timingSafeEqual` | 与打点无关,但**任何文档里出现 `?key=` 的调用示例都要改掉** | + +**仍未改、正文描述依然准确的**:`Invocation` 无 `source` / `client` 列;MCP `invoke_agent` 不带来源标识; +MCP `clientInfo` 被丢弃;`ApiKey.scopes` / `monthlyQuota` 仍无强制执行;`userAgent` 仍明文全量存; +`Skill.downloads` 仍是第三方数字(本 PR 只改了文案与 schema 注释,**没有改列名**)。 + +### 0.5 量级假设(阈值全部基于它,量级变了要重调) + +蓝图的规模参照是 **21k events/天**;TakoAPI 的 GTM 文档自己承认日 UV 是 "?"、徽章采纳是 +"0"、GitHub stars 是 1。**差 3–4 个数量级**。所以蓝图里所有具体数字(240 req/min/IP、 +50 条 batch 上限、15% 对账容差、$50 预算、"5 次/小时"地板)**一条都不能直接抄**。 + +本方案按三档设阈值,每个阈值旁标注它属于哪一档: + +- **T0(今天,≈0 流量)**:告警只能是"绝对零"型,比率型监控全部无意义(分母是 0)。 +- **T1(日 1k 事件)**:采样开始有意义,per-agent 看板可读。 +- **T2(日 50k 事件,即北极星量级起来了)**:启用蓝图 §7.4 的 per-event 量级回归、log sink → BigQuery。 + +--- + +## §1 业务问题 → 指标 → 事件 + +调研列出九个业务问题。逐条给"用哪个指标算、指标从哪些事件/表来、今天缺什么"。 + +| # | 业务问题 | 指标(口径) | 数据来源 | 今天缺什么 | +|---|---|---|---|---| +| **Q1** | 北极星是多少?被 502/402/429 挡掉多少? | `weekly_successful_tasks` = `COUNT(gateway_call_completed WHERE outcome='success')`;`rejection_mix` = `gateway_call_rejected` 按 `reason` 分布 | 事件 1、2、3 | 402/429/401 有平台日志但不可归因(H3);`taskState` 不解析(H2);stream 终局不记(H4) | +| **Q2** | 激活漏斗每步流失与中位耗时 | 五步:`install`(代理指标) → `account_registered` → `api_key_created` → `api_key_first_call` → `week2_retained`。每步转化率 + p50 时长 | 事件 5、7、9 + `ApiKey.createdAt` | `api_key_first_call` 不存在;安装侧零遥测(见 §5.4 的替代方案) | +| **Q3** | 哪个入口的开发者最值钱 | 按 `source ∈ {direct, mcp, skill, plugin, openai_shim, unknown}` 切 Q1 与 Q2 的次周留存 | 事件 1 的 `source` / `client` 属性、事件 10、11、12 | `Invocation` 无 source 维度(H1);MCP 转调不可区分 | +| **Q4** | agent 供给是不是虚胖 | `activated_agents` = `COUNT(DISTINCT agent_slug WHERE 成功调用 ≥ 1)`;`call_gini`;`viewed_never_called` = 有 `agent_detail_viewed` 且零成功调用的 agent | 事件 1、14 | agent 详情页浏览完全不可见(§0.2-1) | +| **Q5** | 每个 HOSTED agent 的上游可靠性画像 | per-agent:`p50/p95 latency_ms`、`error_rate`、`down_ratio` = down/degraded 时长占比 | 事件 1、3、4 + `Agent.healthStatus` | 延迟与错误率原料齐(`Invocation.latencyMs`)。但 **`down_ratio` 今天算不出来**:`Agent.healthStatus` 是一列**被每轮 cron 覆写的当前值**,只配一个 `healthCheckedAt`,**没有任何历史表**——"时长占比"必须靠事件 4 的流水从头攒,**上线之日起才有第一天的数据**,历史无法回溯 | +| **Q6** | 预付经济闭环健不健康 | `weekly_topup_usd`、`fee_revenue_usd`、`burn_days` = 充值额 / 日均消耗、`idle_balance_usd`、**`blocked_revenue_usd`** = 因 402 被拒调用的应收 | 事件 20–23 + `LedgerEntry` + `CreditBalance` | `credit_exhausted` 不存在 → 放宽 `TAKO_CREDIT_FLOOR_USD` 的决策今天没有依据 | +| **Q7** | 5,146 条 skill 的转化效率 | `install_intent_rate` = `install_command_copied` / `skill_detail_viewed`(按 category / scenario 切);`zero_result_rate` | 事件 13、15、16、19 | 安装意图事件不存在;`Skill.downloads` 是抓来的外部数字**不能当转化**;`viewsCount` 人机混计(§7.3) | +| **Q8** | 徽章外链飞轮转没转 | `badge_repos_weekly` = `COUNT(DISTINCT agent_slug)` 有渲染的;`badge_renders`(加权还原);转化端**不可归因**(camo 剥 Referer) | 事件 17、18 | 转化端需要替代锚点:徽章链接加 `?ref=badge`(§5.5) | +| **Q9** | 15 语言 SEO fan-out 的投入产出 | 按 `locale` 切 `account_registered` / `api_key_created` / 首次成功调用;分母用 GSC 曝光。**必须分两类页面算**(见右栏) | 事件 5、7、13、14、15 的 `locale` 属性 + GSC | GSC 待确认已接入;locale 解析有陷阱(§7.6)。**最关键的一条**:`src/proxy.ts` 已经对**非 en 的 `/agents/*` 与 `/skills/*` 详情页**打了 `X-Robots-Tag: noindex, follow`(理由:详情页正文是英文抓来的,14 个 locale 只是 UI 壳)。所以"15 语言 SEO"实际只有**列表页 / scenario 页 / 营销页**在被索引——拿全站 locale 汇总去算 ROI 会把一批**故意不索引**的页面算进分母 | + +**映射的反向检查**:§4.3 列出的事件里,没有一个是"因为蓝图里有所以加的"(此处**刻意不写事件 +总数**——§3.5 的规则对本文自己生效;要数跑 `node scripts/audit-analytics-events.mjs --counts`。 +本次审校在这一行原本就抓到过一个会漂移的数字)。每个事件在上表 +至少出现一次。反过来,Q1–Q9 每一条都至少有一个 Phase 0 或 Phase 1 事件覆盖——唯一的例外是 +Q2 的第一段(安装量),它**永远只有代理指标**,理由见 §5.4。 + +--- + +## §2 设计不变量在 TakoAPI 的形态 + +蓝图 §1 的三条不变量全部适用,但每条在本项目有具体的、已经被代码验证过的形态。 + +### 2.1 打点永不阻塞网关(fire-and-forget) + +这条在 TakoAPI 不是新规则——**代码里已经写死了**:`src/lib/billing.ts` 的 `meterInvocation()` +注释原文是 *"metering/billing must never break the gateway path"*,实现是全函数 +`try { … } catch {}`;`src/lib/requestLog.ts` 的 `logRequest()` 是 `void` 签名 + +`.catch(console.error)`。**新增的 `trackEvent()` 必须沿用同一契约,并且要比它们更严一档**: + +```ts +// src/lib/analytics.ts —— 唯一入口,签名不可变 +export function trackEvent( + name: N, + props: TakoEventProps, + ctx?: EventContext, // { req?, apiKeyId?, userId?, clientFp? } +): void; // ← 同步、返回 void、永不 throw、永不 await +``` + +三条本项目特有的补充规则: + +- **不得在 `trackEvent` 内部做任何 `await prisma.*`**。db-f1-micro 没有 PgBouncer + (`docs/agent-marketplace/03-technical-architecture.md` 把连接池列为「#1 扩展性翻车点」), + Cloud Run 每个实例各开连接。同步 DB 写会在流量峰值直接吃掉连接池。DB sink 走**有界 buffer + + 定量/流量驱动的批量 INSERT**(§6.3)——**注意不是"定时"**:定时器在 Cloud Run 上不可靠, + 理由与替代做法见 §2.4 R2。 +- **Cloud Logging sink 是 `console.log`,不允许包装成 Promise**。它把行交给 stdout 流后立刻 + 返回,不 await、不把失败抛给调用方。把它 `await` 化只会引入一个不存在的失败模式。 + **但要说准一件事,否则 §10 的验收会写成一条必然翻车的断言**:在 Linux 上、当 stdout 是 + **pipe**(容器里就是)时,Node 的写入是**异步**的,不是同步 fd 写——行先进 libuv 的缓冲, + 由事件循环 flush。含义有两条:① 它仍然满足 fire-and-forget 契约(调用点零阻塞), + 且因为 flush 发生在**响应还在生成、CPU 还在的时候**,R1 的论证成立; + ② 但"`console.log` 绝不丢"是**假的**——`kill -9` 或实例被强杀时,未 flush 的那一小截会消失。 + §8.3 已经把这条记为日志 sink 唯一的丢法,两处要保持一致,别在这里写成"写 fd、同步返回"。 +- **buffer 溢出丢最旧**(蓝图 §1.1),且溢出本身要发 `tako_analytics_error`——否则丢数据静默。 + `TAKO_ANALYTICS_DB_MAX_BUFFER` 默认 **2000**(蓝图是 10000;这里按 512Mi 内存下调, + 2000 行 × ~400B ≈ 0.8 MB,可接受)。 +- **flush 失败丢弃本批,不重试、不回队**(蓝图 §1.1,本方案先前漏了这一条)。蓝图原话是 + *a retry loop against a failing sink is how analytics turns into an outage amplifier*, + 而在 TakoAPI 这条比在 参照项目 更硬:DB sink 的对面是 **db-f1-micro、无 PgBouncer** 的同一个 + 业务库。一个持续失败的批次如果重试,打点会和网关、限流(`RateLimitBucket`)、计费事务 + 抢同一个连接池——把"丢了几十行事件"放大成"钱路径写不进库"。丢的数据靠 §8.2 的 + `tako_analytics_error` 告警和 §8.3 ④ 的对账被看见,**不靠重试掩盖**。 +- **SIGTERM 时 drain 一次 buffer**(蓝图 §1.1 的第四条契约,本方案先前漏了)。Cloud Run + 缩容/换 revision 时会给实例 SIGTERM 再等约 10s,不 drain = 每次部署都静默丢掉最后一批。 + **本仓有现成形状可抄,但它今天是死代码**:`src/lib/requestLog.ts` 的 `flushRequestLogs()` + (用一个 `inflight` Set 收住所有 in-flight promise,`Promise.allSettled` 等它们落地) + **在 `src/` 下零调用点,仓里也没有任何 `process.on("SIGTERM")`**——它被写出来过,从未运行过。 + 这正是蓝图反复点名的那种病:一条只活在代码里、没人验证过的防线。所以落地动作是 + **新建**一个 SIGTERM handler,把新的 `flushProductEvents()` 与既有的 `flushRequestLogs()` + 一起挂进去(顺手把后者接线,是白捡的收益)。**注意三件事**:① drain 本身要有超时(≤5s), + 不能让打点拖住实例退出;② Cloud Logging sink 是 `console.log`,**没有 buffer 可以 drain**—— + 它的行早在 `trackEvent()` 返回时就已交给 stdout 流(余下的 flush 是 libuv 的事,我们碰不到), + 给它加"drain"只是凭空造一个失败模式;③ **drain 是"优雅退出时少丢一点",不是 + "不丢"**——实例被回收时能否拿到足够 CPU 把 drain 跑完是平台行为(§2.4),把它当保障就等于 + 在设计里默许一个不成立的假设。 + +**踩坑(蓝图 §1.1 的坑在本项目的具体形态)**:绝不要在 `trackEvent` 开头写 +`if (!process.env.TAKO_ANALYTICS_LOG) return`。短路判断必须放在**各 sink 内部**——否则将来 +关掉日志 sink 会把 DB sink 一起带死。这不是假想:`src/components/Analytics.tsx` 已经有一个 +入口级短路(`if (process.env.NODE_ENV !== "production" \|\| !GA_ID) return null`),它只有一个 +sink 所以无害,但那个模式**不能**复制到 `trackEvent`。 + +### 2.2 双 sink 彼此解耦(本项目是单向解耦) + +蓝图 §1.2 假设两个 sink 对称。TakoAPI 不对称:Cloud Logging sink **没有"不可用"状态**—— +无依赖、无凭据、无配额、无连接(它唯一的丢法是进程被强杀时 stdout 未 flush,§2.1)。所以 +解耦的实际含义收窄为**一条单向规则**: + +> **DB sink 的任何失败(连接耗尽、写超时、buffer 溢出、schema drift)不得影响 Log sink 落行。** + +实现上就是 fan-out 里两次独立 try/catch,不共享变量、不共享 buffer。反方向(Log sink 影响 +DB sink)在物理上不可能,不需要防。 + +### 2.3 sink 开关 env 化 + +| env | 默认 | 作用 | +|---|---|---| +| `TAKO_ANALYTICS_LOG` | `"1"`(`NODE_ENV=production` 时)/ `"0"`(其余) | Cloud Logging sink 总开关。本地 `docker-compose.yml` 起的全栈默认静默 | +| `TAKO_ANALYTICS_DB` | 未设 = **关** | Postgres 热表 sink 总开关(`!!process.env.TAKO_ANALYTICS_DB`)。Phase 1 才在生产打开 | +| `TAKO_ANALYTICS_DB_FLUSH_AT` | `20` | 定量 flush | +| `TAKO_ANALYTICS_DB_FLUSH_MS` | `5000` | **buffer 最老行的年龄阈值**,不是"每 5 秒 flush 一次"——flush 由下一次请求触发(§2.4 R2) | +| `TAKO_ANALYTICS_DB_MAX_BUFFER` | `2000` | buffer 上限,溢出丢最旧 | +| `TAKO_ANALYTICS_SALT` | 未设 = **匿名指纹功能整体关闭**(`client_fp` 落 null,不降级成明文 IP) | 匿名指纹的 HMAC 盐,见 §5.3 | +| `TAKO_ANALYTICS_SAMPLE_REGISTRY` | `0.05` | `/api/registry`、`/api/agent` 的采样率 | +| `TAKO_ANALYTICS_SAMPLE_BADGE` | `0.02` | 徽章渲染采样率 | +| `TAKO_ANALYTICS_SAMPLE_CATALOG` | `0.25` | skill/agent/scenario 详情页浏览采样率 | +| `TAKO_REQUESTLOG_RETENTION_DAYS` | `90` | `RequestLog` 保留期(新增 cron) | +| `TAKO_PRODUCTEVENT_RETENTION_DAYS` | `400` | 热表保留期(`365 + 一个月对齐余量`,同蓝图 §5.1) | + +**本项目特有的约束(两条,别把它们混成一条)**: + +1. **`TAKO_ANALYTICS_SALT` 走 Secret Manager,不走 `--set-env-vars`。**好消息是**前置条件早已就绪**: + `docs/03-secret-hardening.md` 的状态追踪显示步骤 1–4 在 2026-04-22 全部完成(API 已启用、 + 6 个 secret 已建、Cloud Run 默认 SA `429522911261-compute@…` 已授 `secretAccessor`、 + revision `takoapi-00043-wcq` 起 env 全走 `secretKeyRef`)。所以这一步不是"先启用 API", + 而是**照既有范式再加一个 secret**:`gcloud secrets create tako-analytics-salt` → 给同一个 SA + 授权 → 部署时 `--set-secrets TAKO_ANALYTICS_SALT=tako-analytics-salt:latest`。 + 在它就绪前,**宁可让 `client_fp` 恒为 null**(不设即整体关闭),也不要退回存明文 IP—— + 那正是 H5 要修的东西。 +2. **⚠️ `cloudbuild.yaml` 不是线上的部署路径,改它不会生效。**(本条已按 `origin/main` + = `b868319c` 重写;初稿在此处的"跑它会把生产密钥覆盖成占位串"**是错的**,理由见 §0.1 下方的注。) + `origin/main` 上的 `cloudbuild.yaml` **已经是安全版本**:PR #51 把它改成了 + `--update-env-vars`(公开值真值)+ `--update-secrets`(6 个 secret 绑定),且刻意用 + `--update-*` 而非 `--set-*`,以保留未列出的既有绑定。所以「照它跑会闯祸」这半句不再成立。 + **但「改它不会生效」这半句仍然成立,而且理由更简单**:`gcloud builds triggers list` 返回空 + ——**没有任何触发器在跑这个文件**;最近一次成功构建只有一个 docker build step、没有 deploy + step,部署是人工敲的 `gcloud run deploy`。 + **后果因此只剩一个方向**:只把遥测 env 加进 `cloudbuild.yaml` 而不改实际的手工 + `gcloud run deploy`,新变量在生产**根本不会出现**,打点会静默 no-op(而"不配置即 no-op" + 正是我们自己设计的行为,所以它不会报错,只会没数据)。 + **所以本方案的遥测 env 落地动作是:以人工 `gcloud run deploy --update-env-vars` 为准, + 并把同样的开关补进 `cloudbuild.yaml` 以免将来接上 CI 时回退。** + 这不再是 Phase 0 的阻塞项——原来把它列为阻塞项,是基于那条已被推翻的判断。 + +**"不配置即 no-op"的验收方式**:`docker-compose up` 起本地全栈,跑一遍 `/v1/agents/x/message`, +确认 Postgres 里 `ProductEvent` 零行、stdout 无 `tako_event` 行、且接口行为与打点上线前完全一致。 + +### 2.4 Cloud Run 的 CPU 节流——本项目最硬的物理约束(蓝图完全没有这一节) + +事实与待确认项见 §0.2-5。一句话:**在请求级 CPU 分配下,"fire-and-forget" 的后半句不成立**—— +它 fire 了,但不保证 forget 之后还会跑。蓝图的三条不变量(§1)仍然全对,但它们的**实现范式** +(buffer + 定时 flush + drain)默认进程常驻,照抄过来会得到一个"看起来在跑、实际按流量随机丢" +的管线。四条规则,按重要性排序: + +**R1 — 日志 sink 必须在响应返回之前、在请求路径内写出。** +`trackEvent()` 的 Cloud Logging 分支只是一次字符串序列化 + 一次 `console.log`,微秒级; +放在 `return NextResponse.json(...)` 之前是安全的。**绝不允许**把它挪进 `after()`、`setTimeout`、 +或某个 `.then()` 尾巴——那等于把"仓"交给一个可能永远不 tick 的时钟。 +**本方案敢把 Cloud Logging 当权威"仓"(§6.1),前提就是这一条**;违反它,§6 的选型论证同时失效。 + +**R2 — DB sink 的 flush 由流量驱动,定时器只能是第二道保险。** +`trackEvent()` 把行推进 buffer 后**立刻**判定:`buffer.length >= TAKO_ANALYTICS_DB_FLUSH_AT` +**或** 最老一行的年龄 > `TAKO_ANALYTICS_DB_FLUSH_MS` → 就在**当前请求的生命周期内**发起 flush。 +落地用 Next 16 的 `after()`(`import { after } from "next/server"`;本仓 `next@16.2.1` 已导出, +`node -e "console.log(typeof require('next/server').after)"` → `function`)。 +`setInterval` 可以留,但它的定位降级为"实例恰好在处理别的请求时顺手清一下",**不能是唯一路径**。 + +> **别把 `after()` 当成 R3 的替代品——它不解决 CPU 节流,只解决"时钟不 tick"。** +> 本仓是 `output: "standalone"` + 裸 `node server.js`(`next.config.ts` / `Dockerfile`), +> **没有任何平台 `waitUntil` 可以延长请求的 CPU 分配窗口**。Next 的 `after()` 按定义是 +> **响应结束之后**才跑回调,所以它面对 Cloud Run 请求级 CPU 时的暴露面**与今天的 +> `void meterInvocation(...)` 完全相同**。它相对 `setInterval` 唯一的、但确实有价值的优势是 +> **调度时机**:回调在响应 flush 的那一刻就已经在事件循环里排好队,比"5 秒后某个定时器 +> 但愿还能 tick"有数量级更高的命中率。 +> **结论**:R2 消掉的是"定时器永远不 tick"这个失败模式,**不是**"CPU 已经被收走"那个。 +> 后者只有 R3 的两条路能真正消掉,丢多少由 R4 度量。**不要因为用了 `after()` 就跳过 R4。** +- **`TAKO_ANALYTICS_DB_FLUSH_MS` 的语义因此变了**:它不再是"每 5 秒 flush 一次",而是 + "**下一次请求到来时**,若 buffer 最老行超过 5 秒就带走它"。这个语义差别必须写进 env 表的注释, + 否则下一个人会拿它当 SLA 用。 +- **验收**:本地把 `FLUSH_AT` 设成 50,发 3 条事件后**停止发请求** 60 秒 → 不落库; + 再发第 4 个无关请求 → 前 3 条随之落库。这正是生产上应有的行为。 + +**R3 — 钱不许依赖"以后再说"。** +今天 `void meterInvocation(...)` 里含 DEBIT 扣款与余额递减(`src/lib/billing.ts`)。 +在请求级 CPU 下,**一次已经返回 200 的收费调用可能既没有 `Invocation` 行、也没扣款**。 +这是**本方案之外、已经存在的资金风险**,打点方案不负责修它,但**负责先把它测出来**(R4)。 +真要修只有两条路,都要显式决策: +① 网关路由里改成 `await meterInvocation(...)`——它按契约 never-throws,`await` 不会把失败传给用户, +代价是几毫秒延迟换确定性; +② 部署加 `--no-cpu-throttling`(CPU 常驻)——绝对成本在当前量级很小,但它**改变计费模型**, +且与"512Mi 单实例、成本红线"这条约束正面相关,要算过账再做。 +**触发条件(写死,别留成"再看看")**:R4 测出丢失率 **> 0.5%** 即必须二选一。 + +**R4 — 用日志 sink 反过来审计 DB 写入路径(本项目独有的对账,T0 就有价值)。** +因为 R1 保证 `gateway_call_completed` 是请求路径内写出(响应返回之前)的,而 `Invocation` 行是 fire-and-forget +落库的,同一时间窗里两者的差就是**计量丢失率**: + +``` +COUNT(jsonPayload.tako_event="gateway_call_completed") -- Cloud Logging,权威分母 +SELECT count(*) FROM "Invocation" WHERE "createdAt" ... -- DB,可能少 +``` + +它抓的形态是 §8.3 的 ①②③ 全都看不见的那一种:**事务根本没开始**(①②③ 比的是已经落了库的行 +之间是否自洽,事务没跑就三边同时缺,比出来全绿)。这条不需要任何流量规模, +**Phase 0 就该开**,落点并进 §8.3 的对账 cron 作为第 ④ 条。 + +--- + +## §3 事件 schema 与类型化治理——本项目该上几层 + +蓝图 §2 是五层。TakoAPI **上四层,第五层并进第四层**。逐层给裁剪决策与理由。 + +### 3.1 第一层:schema 单一真相源 —— **全上** + +新建 `src/lib/analytics-schema.ts`:discriminated union + `EVENT_EMITTERS` 声明表。 + +```ts +export type TakoEvent = + | { name: "gateway_call_completed"; properties: GatewayCallProps } + | { name: "gateway_call_rejected"; properties: GatewayRejectProps } + | …; +export type TakoEventName = TakoEvent["name"]; +export type TakoEventProps = + Extract["properties"]; + +export type EventEmitter = "server" | "client" | "both" | "both_server_authoritative"; +export const EVENT_EMITTERS: Readonly> = { … }; +``` + +`Record` 作用在字符串字面量 union 上时每个成员都是必填 key——加了 union arm +却忘声明 emitter 就是 TS2739 编译错误。**保留全部四个 emitter 值**,即使 Phase 0 只用到 +`server`:删掉再加回来是 breaking change,而多留两个字面量的成本是零。当前 `both` 与 +`both_server_authoritative` 都是空集,用审计脚本 pin 死(见 3.4): + +``` +DUAL_EMIT_EVENTS.size === 0 // 有人加了双端事件时这条断言会红,逼他先读 §7.2 的双计数规则 +``` + +**本项目特有的 emitter 纪律**:TakoAPI 有一整类事件名含金钱 token(`credit_topup_*`、 +`credit_debited`、`credit_exhausted`、`gateway_call_completed` 带 `billed_usd`)。这些 +**永远是 `server`**——理由比 参照项目 更硬:TakoAPI 的客户端是 coding agent,**它本身就是一个 +能构造任意 HTTP 请求的程序**。参照项目 面对的是"用户可能伪造",TakoAPI 面对的是"调用方在设计上 +就是自动化程序"。任何"client 可发"的金钱事件在这里等于把计费口径开放写入。 + +### 3.2 第二层:chokepoint helper —— **裁剪为 1 端(Phase 0)** + +蓝图是三端(web / mobile / server)。TakoAPI: + +| 端 | 文件 | Phase | 说明 | +|---|---|---|---| +| server | `src/lib/analytics.ts` | **0** | 唯一入口,见 §2.1 | +| client(web) | `src/lib/telemetry-client.ts` | **2** | 只服务 3 个事件(16/17/19-web 侧),且必须与 consent gate 同期 | +| mobile | — | **不适用** | 仓库里没有任何 iOS/Android/RN/Expo 代码 | + +`src/lib/analytics.ts` 的职责边界(**只做这四件事,别的都不做**): +① 从 `EventContext` 补齐公共字段(见 §4.2);② 采样裁决并把 `sample_rate` 写进 props; +③ fan-out 到两个 sink;④ 自身失败发 `tako_analytics_error`。 +**不做 enrich 型 DB 查询**(例如"顺手查一下 agent 的 category")——那是把 chokepoint 变成 +关键路径上的额外查询,在 db-f1-micro 上是自杀。需要 join 的维度在查询侧 join,不在写入侧。 + +**这条规则的边界要说清楚,否则会与 §4.3 的事件表打架**:事件表里确实有几个属性需要额外查询—— +`api_key_created.key_index`(该用户第几把 key)、`.minutes_since_registration`(要读 `User.createdAt`)、 +`api_key_revoked.lifetime_invocations` / `.lifetime_billed_usd`(要对 `Invocation` 做聚合)。 +规则不是"这些字段不许要",而是: + +> **enrich 只允许发生在调用点,永远不允许发生在 `trackEvent()` 内部; +> 且只允许出现在低频路径上。** + +判据是**该路径每天跑几次**:key 创建/吊销是**人手动点一次**的动作(`/api/keys` POST/DELETE), +每天个位数,多一次 `count()` 无所谓;而网关 `/v1/*`、`/api/registry`、徽章渲染是**机器高频路径**, +那里的事件属性**只能用手边已有的对象**(`keyRecord`、`agent` 的 select 结果、`upstream.status`), +一次额外查询都不许加。`trackEvent()` 自己不知道调用点的频率,所以它索性一次查询都不做—— +把判断权留在调用点,是这条规则的全部意思。 + +### 3.3 第三层:ESLint 禁裸调 —— **上,但规则形态与蓝图完全不同** + +参照项目 禁的是 `posthog.capture()` / `fetch('/api/telemetry/event')`。TakoAPI 的裸调风险是 +另外两种: + +```js +// eslint.config.mjs —— 追加在现有 defineConfig 数组末尾 +{ + files: ["src/**/*.ts", "src/**/*.tsx"], + ignores: ["src/lib/analytics.ts", "src/lib/analytics-schema.ts"], + rules: { + "no-restricted-syntax": ["error", + { // ① 绕过 chokepoint 直接写热表 + selector: "MemberExpression[object.property.name='productEvent']", + message: "写 ProductEvent 必须经 trackEvent()(src/lib/analytics.ts)。见 docs/analytics-plan-2026-08-21.md §3.3", + }, + { // ② 手搓结构化日志伪装成事件行 + selector: "CallExpression[callee.object.name='console'][callee.property.name='log'] > CallExpression[callee.object.name='JSON'][callee.property.name='stringify']", + message: "结构化事件日志只能由 trackEvent() 产出,否则 Cloud Logging 的 tako_event 计数会被污染。", + }, + ], + }, +} +``` + +**两个本项目特有的踩坑**: + +- 蓝图 §2.3 的核心教训是"flat config 没挂 TS parser → 规则一次都没执行过而 CI 是绿的"。 + 本仓 `eslint.config.mjs` 用的是 `eslint-config-next/typescript`,理论上带 parser—— + 但**本仓根本没有 CI 跑 lint**(`.github/workflows/` 只有 `i18n.yml`)。所以这条规则上线时 + 必须**同时**新建 workflow 跑 `npm run lint`,否则它连"绿的假象"都没有。 +- 上线后**故意写一个 `prisma.productEvent.create()` 验证它真的报红**。蓝图明确要求这个动作, + 本仓没有测试兜底,更需要。 + +### 3.4 第四层:CI schema↔调用点审计 —— **上,且吸收第五层** + +新建 `scripts/audit-analytics-events.mjs`(**用 `.mjs` 而不是 `.ts`**:本仓无 +`tsx` 依赖,`scripts/check-i18n.mjs` 已经是纯 node 脚本的现成范式,零新依赖)。它做四件事: + +1. **missing**:正则扫全仓 `trackEvent("…"` 的字面量事件名,减去 schema 里的 union 成员 + → 非空则 `exit 1`。 +2. **unused**:schema 有、无人发 → 打印(`--strict` 下 exit 1)。 +3. **敏感命名**(吸收蓝图第五层):事件名**按 `_` 分词**后命中 + `{credit, payment, topup, refund, payout, ledger, billed, admin, revoke}` 任一 token 的, + 必须 `EVENT_EMITTERS[name] === "server"`,否则 exit 1;例外表内联在脚本顶部,每条带一句 + 为什么豁免的理由。 + **必须分词而非 substring**:naive 的 `includes("admin")` 会误伤未来的 + `agent_admin_page_viewed` 这类名字;蓝图明确记录了误报把防线做废的机制。 +4. **不变量断言**:`DUAL_EMIT_EVENTS.size === 0`;`serverOnly + clientEmittable === 全量` + (partition 断言,破了 exit 1)。 + +**为什么把第五层并进来而不是独立成单测**:本仓**没有测试框架**。为一条命名检查引入 +vitest + 配置 + CI job,成本远高于在已有的审计脚本里加 20 行。这是本项目量级下的正确裁剪。 +**代价要写明**:并进去之后,这条检查失去了"单测的可读失败输出",所以脚本必须打印 +`事件名 / 命中的 token / 当前 emitter / 应为 server` 四列,别只 exit 1。 + +**Workflow(与脚本同一个 PR 落地,不许进 backlog)**:新建 +`.github/workflows/analytics-audit.yml`,`on: [push, pull_request]`,三步: +`npm ci` → `npm run lint` → `node scripts/audit-analytics-events.mjs`。 +蓝图 §2.4 记录了 参照项目 把这个脚本晾在 backlog 数月、同时文档声称"also runs in CI"的病; +本仓的起点更差(一个 CI job 都没有),所以这条更不能拖。 +**验收方式**:故意在某个路由里写 `trackEvent("not_declared_event", {})`,确认 CI 变红。 + +### 3.5 附则:不把集合大小写进 prose —— **适用** + +本文全文**没有出现**"共 N 个事件"这样的数字断言(§4.3 的事件表是清单不是计数)。要数就跑 +`node scripts/audit-analytics-events.mjs --counts`。 + +**例外**:`docs/00-infrastructure.md` 里的行数快照(Skill 5,146、KolContact 1,042 等)是 +**2026-04-22 的一次性快照**且文中已标注日期——那种带日期的快照是合法的;无日期的 +"目前有 493 个 agent"才是谎言温床。注意 `src/app/api/registry/route.ts` 的注释里就写着 +"493 today"——那行注释迟早会过时,别引用它。 + +--- + +## §4 数据入口:chokepoint 在哪、协议长什么样 + +### 4.1 chokepoint 清单(哪些代码位置调 `trackEvent`) + +TakoAPI 的运气不错:**关键路径上已经有几个天然收口**,不需要新造。 + +| chokepoint | 文件 | 覆盖 | 状态 | +|---|---|---|---| +| `meterInvocation()` | `src/lib/billing.ts` | 三条网关路由的**成功侧** | 已存在,加事件即可 | +| `withRequestLog()` | `src/lib/requestLog.ts` | **一部分** `/api/*` 路由(跑 `grep -rl 'withRequestLog\|logRequest' src/app/api` 看当前覆盖面) | 已存在;需扩到网关与 `/mcp` | +| `withAdmin()` | `src/lib/admin.ts` | 全部 admin 路由 | 已存在,是"一个 wrapper 覆盖一整类路由"的现成范式 | +| `authenticateApiKey()` | `src/lib/apikey.ts` | 机器身份解析 | 已存在;`api_key_first_call` 判据在这里(见下方陷阱) | + +**需要新增的收口**: +- `withGateway()`(新建于 `src/lib/gateway.ts`)——把三条 `/v1/*` 路由的 + 「鉴权 → 限流 → 找 agent → credit preflight」四段共用逻辑抽出来,让 `gateway_call_rejected` + 只有一个发射点。今天这四段在三个文件里各抄了一遍,加事件如果不先收口,就要维护三份。 +- `src/app/mcp/route.ts` 的 `POST` handler 顶部与 `tools/call` 分支——MCP 的两个事件。 + +**`withGateway()` 的两个非对称点(不处理就会把 OpenAI 兼容性做坏)**: + +1. **顺序不同。**`message` 与 `stream` 是「auth → rate limit → **找 agent** → credit → 读 body」; + `chat/completions` 必须**先读 body** 才知道 agent 是谁(slug 来自 `body.model`),所以它是 + 「auth → rate limit → **读 body** → 找 agent → credit」。`withGateway()` 不能把 slug 当参数 + 硬拿,得接受一个 `resolveSlug: (req) => Promise`(前两条直接从 path params 拿, + shim 那条从已解析的 body 拿),否则 shim 那条路要么读两次 body、要么塞不进这个 wrapper。 +2. **错误信封不同。**`message`/`stream` 回的是 `{ error: "Agent not found" }`; + `chat/completions` 回的是 OpenAI 形状的 + `{ error: { message, type: "invalid_request_error" | "insufficient_quota", code } }`, + 402 那条还专门用了 `type/code = "insufficient_quota"` 让 OpenAI SDK 能正确识别。 + **一个"统一"的 `withGateway()` 如果返回统一的错误体,就等于悄悄破坏了 shim 的兼容契约**—— + 而这类破坏没有测试会接住(本仓无测试框架)。所以 `withGateway()` 必须收一个 + `renderError(reason, detail) => NextResponse` 回调,由各路由自己给形状; + wrapper 只负责**判定 reason 并发 `gateway_call_rejected`**,不负责渲染。 + 一句话:**收口的是"判定与打点",不是"响应体"。** + +**实现陷阱(`api_key_first_call` 的判据会被自己踩掉)**: +`authenticateApiKey()` 里有一句 best-effort 的 +`prisma.apiKey.update({ data: { lastUsedAt: new Date() } })`。如果在 `meterInvocation` 里用 +"`lastUsedAt IS NULL` 说明是首调"来判断,**这个判据在鉴权阶段就已经被写坏了**。 +好消息:**今天的代码恰好是对的**——`record` 来自 `findUnique`,那句 `update` 是 +fire-and-forget 且**没有回写 `record`**,所以 `record.lastUsedAt` 拿到的就是更新前的旧值。 +所以这里要做的**不是改 `apikey.ts`,而是别把它改坏**:绝不能"顺手优化"成用 `update` 的返回值 +当返回记录。要落地事件 9,真正要动的是**下游**——`meterInvocation()` 的 `MeterInput` 目前 +只有 `{apiKeyId, userId, agentId, protocol, status, latencyMs, taskState, errorCode, units, billedUsd}`, +**没有任何字段能带过去这个旧值**,三条网关路由也只从 `keyRecord` 里取了 `.id` 和 `.userId`。 + +**`MeterInput` 的完整加宽清单(不只是 `keyLastUsedAt`——这是本方案工作量最容易被低估的一处)。** +`meterInvocation()` 是事件 1、9、22 的共同落点,而 §3.2 定死了"`trackEvent()` 内部不做任何 +enrich 查询"。两条一合,事件 1 的每一个属性都**必须由调用方经 `MeterInput` 传进来**。今天的 +`MeterInput` 只够填其中一半,所以以下字段全部要加,且三条路由都要传: + +| 新字段 | 事件 1 的哪个 prop | 调用点手边有吗 | +|---|---|---| +| `agentSlug: string` | `agent_slug` | 有:`message`/`stream` 从 path params 拿,shim 从 `body.model` 拿。**注意 `agent` 的 `select` 里没有 `slug`**,别去查库,用路由已有的那个变量 | +| `route: "message" \| "stream" \| "chat_completions"` | `route` | 有:每个文件一个常量。**`protocol` 替代不了它**——`message` 与 `stream` 都传 `"A2A"`(只有 shim 是 `"OPENAI_COMPAT"`),§7.5 的"不可相加"在数据上根本切不开 | +| `pricingModel: PricingModel` | `agent_pricing_model` | 有:`agent.pricingModel` 已在三条路由的 `select` 里 | +| `upstreamHost: string \| null` | `upstream_host` | 有:`new URL(agent.endpointUrl).host`。**只取 host,不取全 URL**(§9.1) | +| `promptChars: number` | `prompt_chars_bucket` | 有:`text.length`(原文不传,**分桶在 `trackEvent` 调用点或 schema 侧做**) | +| `source` / `client` | `source` / `client` | 有:从 `X-Tako-Client` 解析(§5.4)。同时也是 H1 要写进 `Invocation.source/client` 两列的值 | +| `keyLastUsedAt?: Date \| null` | 事件 9 的判据 | 有:`keyRecord.lastUsedAt`(上面那个"旧值") | + +**这七条一条不加,事件 1 就只剩 `status/latency/billed` 三个维度,Q3(入口对比)、Q5 +(per-agent 画像)、§7.5(route 不可相加)全部落空**——而它们恰好是本方案最贵的三个产出。 + +### 4.2 事件信封(每行的公共字段) + +无论进哪个 sink,每条事件的公共部分固定为: + +```jsonc +{ + "tako_event": "gateway_call_completed", // ← Cloud Logging 的 logs-based metric 靠这个 key 过滤 + "event_id": "uuid-v4", // 幂等键;Phase 2 进 BigQuery 时当 insertId + "ts": "2026-08-21T09:14:02.113Z",// 服务端时间,客户端不参与(见下) + "env": "production", // NODE_ENV + "rev": "takoapi-00042-abc", // K_REVISION(Cloud Run 注入),定位"哪个版本开始变的" + "identity": { + "user_id": "cuid | null", + "api_key_id": "cuid | null", + "client_fp": "hex16 | null", // 日轮换指纹,见 §5.3 + "grade": "key | session | anon" // 见 §5.2 + }, + "source": "direct | mcp | skill | plugin | openai_shim | web | cron | unknown", + "client": "takoapi-mcp/0.1.0", // X-Tako-Client 原样截断 64 字符,可为 null + "locale": "en | zh | ja | …", // 仅 web 侧事件 + "sample_rate": 1.0, // ← 采样事件必填,还原时 SUM(1/sample_rate) + "props": { … } // 逐事件形状,见 §4.3 +} +``` + +**三条设计决定与理由**: + +- **`ts` 一律用服务端时间,不接受客户端自报**。蓝图 §3.3 的时钟 clamp(`[now−24h, now+60s]`) + 是为移动端离线 outbox 设计的;TakoAPI 没有离线队列,Phase 2 的浏览器事件也是即时发送。 + 接受客户端 ts 只会引入坏时钟风险而换不到任何东西。Phase 2 若真需要,clamp 窗口应是 + **`[now−1h, now+60s]`**(不是 24h),且窗口外**丢弃 ts 用到达时间**,不 clamp 到边界值。 +- **`rev` 必须有**。本仓无 CI 部署、靠手动 `gcloud run deploy`,"什么时候变的"经常只能靠 + revision 反推。`K_REVISION` 是 Cloud Run 自动注入的,白捡。 +- **`props` 里永远不出现**:prompt 原文、agent 响应体、API key 明文或 prefix、 + 邮箱、明文 IP、搜索词原文。硬规则见 §9.1。 + +### 4.3 首批事件清单 + +**Phase 列**:0 = 与补洞同周;1 = 第一批看板需求;2 = 客户端入口 + consent 之后。 +**emitter 列**:`server` / `client` / `both` / `both_server_authoritative`(蓝图 §2.1 的四值)。 + +#### A. 网关与北极星 + +| # | 事件名 | emitter | Phase | 落点 | props | 回答 | +|---|---|---|---|---|---|---| +| 1 | `gateway_call_completed` | server | 0 | `src/lib/billing.ts` `meterInvocation()` | `agent_slug, agent_id, protocol(A2A\|OPENAI_COMPAT), route(message\|stream\|chat_completions), outcome(success\|upstream_error\|upstream_unreachable\|task_failed), status, error_code, task_state, latency_ms, billed_usd, units, prompt_chars_bucket(0\|1-200\|201-1k\|1k-8k\|8k+), upstream_host, agent_pricing_model` | Q1 Q3 Q4 Q5 | +| 2 | `gateway_call_rejected` | server | 0 | 新建 `src/lib/gateway.ts` `withGateway()` | `reason(no_key\|invalid_key\|revoked_key\|rate_limited\|insufficient_credit\|agent_not_found\|not_invokable\|bad_request), route, agent_slug?, required_usd?, balance_usd?, floor_usd?, retry_after_ms?` | Q1 Q6 | +| 3 | `gateway_stream_closed` | server | 0 | `src/app/v1/agents/[slug]/stream/route.ts` 的 `close`/`cancel`/error 分支 **以及 `!upstream.body` 分支** | `agent_slug, invocation_id, reason(complete\|client_cancel\|upstream_error\|timeout\|**no_body**), bytes_out, duration_ms, first_byte_ms, event_count, **billed_usd**` | Q1 Q5(修正 H4 的成功率高估) | +| 4 | `agent_health_checked` | server | 0 | `src/lib/health.ts` 的 `runHealthChecks()`(**不是** cron 路由——路由只是触发器,逐 agent 的结果在这里) | `agent_slug, health_status(ok\|degraded\|down), previous_status, transitioned(bool), latency_ms, http_status, error_code` | Q5 | + +> **1 的 `outcome` 是本方案最重要的一个字段。**它不是 `status < 400` 的同义词——见 §7.1。 +> +> **4 的 props 今天一个都取不到,`src/lib/health.ts` 必须先改(这是 Phase 0 的实际工作量,别低估)**: +> `probeAgentHealth()` 现在的签名是 `(agent) => Promise`——只回一个 +> `"ok"|"degraded"|"down"` 字符串,**延迟没测、HTTP 状态被 `classify()` 吃掉、原始错误被丢弃**; +> `runHealthChecks()` 的 `findMany` 只 `select: { id, cardUrl, endpointUrl }`—— +> **既没有 `slug`,也没有旧的 `healthStatus`**,所以 `agent_slug`、`previous_status`、 +> `transitioned` 三个字段无从谈起。落地动作是两处扩宽: +> ① `probeAgentHealth` 返回 `{ health, latencyMs, httpStatus, errorCode }`; +> ② `runHealthChecks` 的 select 加上 `slug` 与 `healthStatus`(写库前的旧值就是 `previous_status`)。 +> 两处都在同一个文件里,改动小,但**不改就没有事件 4,没有事件 4 就永远没有 `down_ratio`**(§1 Q5)。 + +#### B. 身份与激活漏斗 + +| # | 事件名 | emitter | Phase | 落点 | props | 回答 | +|---|---|---|---|---|---|---| +| 5 | `account_registered` | server | 0 | `src/lib/auth.ts` 的 `events.createUser`(**今天 auth.ts 只有 `callbacks`,没有 `events` 块,要新加**)+ `src/app/api/auth/register/route.ts`(credentials 注册不经 adapter,`createUser` 不会为它触发,必须两处都发) | `user_id, method(google\|apple\|credentials), locale, referrer_host, has_username` | Q2 Q9 | +| 6 | `session_signed_in` | server | 0 | `src/lib/auth.ts` 的 `events.signIn`(同上,`events` 块要新加) | `user_id, method, is_new_user, locale` | Q2 | +| 7 | `api_key_created` | server | 0 | `src/app/api/keys/route.ts` POST | `user_id, api_key_id, key_index(该用户第几把), named(bool), minutes_since_registration` | Q2 Q9 | +| 8 | `api_key_revoked` | server | 0 | `src/app/api/keys/[id]/route.ts` DELETE | `user_id, api_key_id, age_days, lifetime_invocations, lifetime_billed_usd` | Q2 | +| 9 | `api_key_first_call` | server | 0 | `src/lib/billing.ts` 的 `meterInvocation()`,判据是**新增的 `MeterInput.keyLastUsedAt` 为 null**(不是查库!见 §4.1 陷阱) | `user_id, api_key_id, minutes_since_created, agent_slug, source, outcome` | **Q2 的激活时刻** | + +#### C. MCP 与分发入口 + +| # | 事件名 | emitter | Phase | 落点 | props | 回答 | +|---|---|---|---|---|---|---| +| 10 | `mcp_session_initialized` | server | 0 | `src/app/mcp/route.ts` `case "initialize"` | `client_name, client_version, protocol_version, protocol_downgraded(bool), has_token, client_fp` | Q3 | +| 11 | `mcp_tool_invoked` | server | 0 | `src/app/mcp/route.ts` `case "tools/call"` | `tool(search_agents\|get_agent\|search_skills\|invoke_agent), has_token, outcome(ok\|tool_error\|validation_error\|unknown_tool), duration_ms, result_chars, query_chars_bucket, client_fp` | Q3 | +| 12 | `registry_fetched` | server | 1(采样) | `src/app/api/registry/route.ts`、`src/app/api/agent/route.ts` | `endpoint(registry\|agent), format(md\|json), limit, result_count, response_bytes, filtered_by(q\|category\|protocol\|none), ua_class, client_fp, sample_rate, **is_mcp_loopback(bool)**` | Q3 | + +> **10 是唯一能识别 MCP 客户端的机会。**MCP server 是无状态的(`src/app/mcp/route.ts` 头注释 +> 明确"no session id"),`initialize` 之后的每个请求都不带任何客户端标识。今天 +> `case "initialize"` **把 `params.clientInfo` 直接丢弃了**(它只从 `params` 里取了 +> `protocolVersion`)——加上它,"是 Claude Code 还是 Cursor 还是自建脚本在用我们的 MCP" +> 这个问题才有答案。 +> **代价要写明**:无状态意味着 `mcp_session_initialized` 与后续 `mcp_tool_invoked` +> **无法用 session id 串起来**,只能靠同一天的 `client_fp` 做弱关联。这是协议决定的,不是缺陷。 +> +> **11 与 12 之间有一条必须先切断的双计线(H7)。**MCP 不是直接查库的——`src/lib/mcp/tools.ts` +> 的四个工具**全部经公网 loopback 回调自己的 HTTP API**:`search_agents` → `/api/registry`、 +> `get_agent` → `/api/agents/{slug}`、`search_skills` → `/api/skills/search`(无 query 时走 +> `/api/agent`)、`invoke_agent` → `/v1/agents/{slug}/message`。所以一次 MCP 读工具调用 +> **必然同时产生一条 `mcp_tool_invoked` 和一条 API 侧事件**。两个后果: +> ① 计数双计(口径规则见 §7.2,已从"只讲 invoke_agent"扩写成通用规则); +> ② **匿名指纹被污染**——那条 loopback 请求的 `x-forwarded-for` 是 **Cloud Run 自己的出口 IP**, +> `extractClientIp()` 取到的与真实 MCP 客户端毫无关系。若不处理,`registry_fetched.client_fp` +> 会出现一个吃掉大半流量的热值,"日活设备数上界"这个语义直接作废。 +> **处置**:`getJson()` 与 `invoke_agent` 的 fetch 统一带 `X-Tako-Client: takoapi-mcp/` +> (§5.4),服务端见到它就 ① 置 `is_mcp_loopback=true`、② **`client_fp` 落 null 而不是算出来** +> ——宁可少一个维度,也不要一个假的。 + +#### D. 目录与 SEO 转化 + +| # | 事件名 | emitter | Phase | 落点 | props | 回答 | +|---|---|---|---|---|---|---| +| 13 | `skill_detail_viewed` | server | 1(采样) | `src/app/api/skills/[id]/route.ts`(`viewsCount++` 的同一处) | `skill_id, skill_slug, category_slug, locale, referrer_host, ua_class(browser\|llm_agent\|bot\|unknown), client_fp, sample_rate` | Q7 Q9 | +| 14 | `agent_detail_viewed` | server | 1(采样) | **`src/app/[locale]/agents/[slug]/page.tsx` 的 page 函数**(server component,见 §0.2-1) | `agent_slug, agent_id, category_slug, locale, health_status, is_hosted, referrer_host, ua_class, client_fp, sample_rate` | Q4 Q9 | +| 15 | `scenario_page_viewed` | server | 1(采样) | `src/app/[locale]/scenarios/[slug]/page.tsx` | `scenario_slug, locale, item_count, referrer_host, client_fp, sample_rate` | Q7 Q9 | +| 16 | `install_command_copied` | **client** | 2 | **`src/components/ui/InstallTabs.tsx` 的 `CopyButton`**(`/install` 页唯一的复制入口)+ `src/app/[locale]/skills/[slug]/page.tsx` 的复制按钮 | `surface(install_page\|skill_detail), target(claude_native\|codex\|opencode\|curl\|npx\|mcp\|uninstall\|skill_snippet), locale` | **Q7 的转化分子** | +| 17 | `badge_snippet_copied` | **client** | 2 | **`src/components/BadgeSnippet.tsx` 的 `copy()`**(今天唯一带复制按钮的徽章片段,挂在 `/agents/[slug]`) | `agent_slug, locale, format(md\|html)` | Q8 | +| 18 | `badge_rendered` | server | 1(采样) | `src/app/api/badge/[slug]/route.ts`(替换现有 `logRequest`) | `agent_slug(**规范 slug,不是 URL 段**), lookup_key_kind(slug\|id), ua_class(github_camo\|browser\|other), referer_host, value_kind(stars\|listed\|not_listed), sample_rate` | Q8 | +| 19 | `search_performed` | both_server_authoritative | 1 | server:**只在 `src/app/api/skills/search/route.ts` 一处**(MCP 的 `search_skills` 就是转调它,见下);client(Phase 2):搜索框 | `surface(skills_api\|agents_api\|web), query_chars_bucket, result_count, zero_result(bool), locale, source(direct\|mcp\|…), is_mcp_loopback(bool)` | Q7 | + +> **16 / 17 的落点与枚举本次审校改过——原写法把事件挂在了没有复制按钮的文件上。** +> 全仓写 `navigator.clipboard` 的地方只有四个:`src/components/ui/InstallTabs.tsx`、 +> `src/app/[locale]/skills/[slug]/page.tsx`、`src/components/BadgeSnippet.tsx`、 +> `src/app/[locale]/dashboard/page.tsx`。三条推论: +> ① **`src/app/[locale]/install/page.tsx` 里没有复制按钮**,它只是 `` 的宿主—— +> 事件要发在 `InstallTabs` 的 `CopyButton` 里,那一个组件覆盖 claude/codex/opencode 的原生命令、 +> 通用 curl、npx、MCP 配置**以及 `--uninstall`**(所以 `target` 枚举里补了 `uninstall`: +> 复制卸载命令是一个真实且重要的负向信号,原枚举把它漏掉了)。 +> ② **`/badge` 页今天只把片段渲染在 `` 里,没有复制按钮**(它是 server component)。 +> 想在那里发事件,得先给它加一个按钮——这是产品改动,不是埋点改动,别写进埋点 PR 就以为有了。 +> ③ **`agent_detail` / `home` / `badge_page` 三个 surface 值今天没有对应的复制入口**, +> 已从枚举里删掉。留着一个永远为空的枚举值,等于给未来的读者一个"这里没数据 = 没人用"的 +> 错觉,而真相是"这里根本没有按钮"。 +> **另记一笔(不属于事件 16,但同一批 clipboard 里)**:`dashboard/page.tsx` 的复制是 +> **API key 明文复制**。那里可以发一个 `api_key_copied`(只带 `api_key_id`), +> 但**绝不允许把被复制的字符串或其长度带进任何 prop**(§9.1)。Phase 2 再定,不进首批。 +> +> **19 是本方案唯一的双端事件,且刻意标成 `both_server_authoritative`。**理由与蓝图 §2.1 一致: +> 搜索量是要拿去做供给决策("哪些查询零结果 → 该补什么 agent")的,客户端副本可伪造。 +> Phase 2 之前它是纯 server;Phase 2 加 client 副本时,`/api/telemetry/event` 入口 +> **必须拒绝**这个名字的 client 副本(allowlist 从 `EVENT_EMITTERS` 推导,自动拒)。 +> **搜索词原文永不入任何 sink**——只记长度分桶与结果数。要做"零结果查询词"分析时, +> 走一条单独的、显式批准的、只在 admin 侧留存 7 天的通道(§9.2)。 +> +> **落点从"API 路由 + MCP 工具"改成"只有 API 路由"(本次审校修正)。**原写法会保证双计: +> MCP 的 `search_skills` 并不自己查库,而是 `getJson('/api/skills/search?…')` 回调同一条路由 +> (H7)。在两处都发,一次 MCP 搜索就是两条 `search_performed`——而且这两条的 `surface` 不同 +> (`mcp_tool` / `skills_api`),连去重都做不了,正好复刻蓝图案例 A「所有 client 事件被双计 ~2 倍、 +> 数月无人发现」的形态。**正确做法**:`search_performed` 只在 API 路由发一次, +> 由 `X-Tako-Client` 带来的 `source='mcp'` + `is_mcp_loopback` 区分它是不是 MCP 来的; +> MCP 侧那次调用由 `mcp_tool_invoked`(事件 11)单独覆盖,**两个事件语义不同、不相加**。 +> `surface` 枚举里的 `mcp_tool` 因此删掉——它是这个 bug 的化石。 + +#### E. 钱(全部 server-only,审计脚本会强制) + +| # | 事件名 | emitter | Phase | 落点 | props | 回答 | +|---|---|---|---|---|---|---| +| 20 | `credit_topup_started` | server | 0 | `src/app/api/billing/topup/route.ts` | `user_id, amount_usd, fee_usd, provider(paypal), balance_before_usd` | Q6 | +| 21 | `credit_topup_completed` | server | 0 | `src/app/api/billing/topup/return/route.ts` + `/webhook` | `user_id, amount_usd, fee_usd, net_credit_usd, balance_after_usd, minutes_since_started, path(return\|webhook)` | Q6 | +| 22 | `credit_debited` | server | 0 | `src/lib/billing.ts` `meterInvocation()` 的 DEBIT 分支 | `user_id, api_key_id, invocation_id, amount_usd, balance_after_usd, agent_slug` | Q6 | +| 23 | `credit_exhausted` | server | 0 | **`src/lib/gateway.ts` 的 `withGateway()` 里,`checkCreditPreflight()` 返回 `ok:false` 的那个分支**(**不是** billing.ts 内部,见下注) | `user_id, api_key_id, balance_usd, required_usd, floor_usd, agent_slug, route, blocked_value_usd(= required_usd)` | **Q6 的核心** | + +> **21 的 `path` 字段**:PayPal 的 return 与 webhook 两条路都能触发 capture,很可能**双发**。 +> 口径上营收**永远从 `LedgerEntry` 数,不从事件数**(§7.4)。这个字段只用来诊断双发。 +> +> **23 为什么不发在 `checkCreditPreflight()` 里面(本次审校修正)。** +> 它今天的签名是 `checkCreditPreflight(userId, pricingModel, unitPriceUsd)`——**函数体内既没有 +> `agent.slug` 也没有 `agent.id`,更不知道自己是被哪条路由调的**,`agent_slug` / `route` 两个 +> prop 在那里物理上取不到。要么改签名(把 agent 传进一个纯计算函数,污染它的职责), +> 要么在调用点发——**选调用点**,而 H3 的 `withGateway()` 正好就是那个唯一的调用点: +> 它在同一个分支里既发 `gateway_call_rejected{reason:"insufficient_credit"}` 又发 +> `credit_exhausted`,两条事件同源、字段一致。这与事件 9 的 `keyLastUsedAt` 是同一类陷阱 +> (props 写在纸上、落点取不到),本方案在这里踩过两次,第三次请先问"这个字段在那个作用域里 +> 真的存在吗"。 +> +> **23 的一条口径边界**:`checkCreditPreflight()` 在读余额出错时 **fail-open** +> (`catch { return { ok: true } }`,`src/lib/billing.ts` 的注释写明了这是刻意的)。 +> 所以 `credit_exhausted` 与 `blocked_revenue_usd` **在 DB 抖动期间会系统性偏低**—— +> 那些调用被放行了而不是被拒了。Q6 的报表要么切掉 DB 不可用的时间窗,要么在结论上标注 +> "下界"。别把一个 fail-open 的 gate 的拒绝数当成需求上限。 + +#### F. 供给与运营 + +| # | 事件名 | emitter | Phase | 落点 | props | 回答 | +|---|---|---|---|---|---|---| +| 24 | `agent_submitted` | server | 0 | `src/app/api/agents/submit/route.ts` | `user_id, protocol, has_endpoint, pricing_model, source(form\|cron_import\|scrape)` | Q4 | +| 25 | `skill_submitted` | server | 1 | `src/app/api/skills/submit/route.ts` | `user_id, repo_host, category_slug, source(form\|scrape)` | Q7 | +| 26 | `agent_status_changed` | server | 1 | `src/app/api/admin/agents/**`(经 `withAdmin()`) | `agent_slug, from_status, to_status, admin_user_id, reason` | Q4 Q5 | + +#### G. 打点自身健康 + +| # | 事件名 | emitter | Phase | 落点 | props | 回答 | +|---|---|---|---|---|---|---| +| 27 | `tako_analytics_error` | server | 0 | `src/lib/analytics.ts` 各失败分支 | `sink(log\|db), reason(row_build_failed\|db_insert_failed\|buffer_overflow\|serialize_too_large), event_name, dropped_count` | §8.2 的告警源 | + +> 这个事件**不进 DB sink**(DB 挂了时它正好发不出去)。它只进 Cloud Logging,且**不采样**。 + +--- + +## §5 身份模型 + +### 5.1 三层锚点,主锚点不是 user_id + +蓝图 §4.2 的不变量是「每个事件至少落在 `userId` 或 `anonId` 之一」,且 `userId` 是唯一权威。 +**TakoAPI 要改这条**: + +> **网关侧的主体是 `api_key_id`,不是 `user_id`。** + +理由不是洁癖,是口径正确性:一个人可以持多把 key,而每把 key 通常对应**一个集成 / 一台机器 / +一个 CI job**。用 `user_id` 算"激活"会把"第二把 key 的首调"算成老用户的普通调用, +Q2 的漏斗(第 4 步"发出第一次成功调用")就永远数不对。所以: + +| 锚点 | 语义 | 何时非空 | 用在哪 | +|---|---|---|---| +| `api_key_id` | **机器主体**:一个集成 | 全部 `/v1/*` 与 MCP `invoke_agent` | 激活、留存、限流、来源分析 | +| `user_id` | **人 / 租户** | 网页会话、以及经 `ApiKey.userId` 回溯的网关调用 | 计费、GDPR、跨 key 汇总 | +| `client_fp` | **匿名日指纹** | 目录读、MCP 读工具、徽章 | 日级去重、机器人识别 | + +**锚点不变量(本项目版)**:每条事件至少有 `api_key_id` / `user_id` / `client_fp` 之一非空。 +在 `ProductEvent` 热表上用 DB CHECK 约束钉死(蓝图 §4.2 的做法适用): + +```sql +ALTER TABLE "ProductEvent" ADD CONSTRAINT "ProductEvent_identity_ck" + CHECK ("apiKeyId" IS NOT NULL OR "userId" IS NOT NULL OR "clientFp" IS NOT NULL); +``` + +**唯一豁免**:cron 事件(`agent_health_checked`)三者皆空,`source='cron'`。所以 CHECK 要写成 +`… OR "source" = 'cron'`。别指望代码纪律记住这个例外。 + +### 5.2 `grade`:身份可信度三级(蓝图 §4.5 的本项目形态) + +蓝图的 `identity_grade` 是 verified / asserted / anonymous。TakoAPI 的三级不同: + +| grade | 含义 | 可信度 | +|---|---|---| +| `key` | 通过 `authenticateApiKey()` 的 SHA-256 比对 | **最高**。密码学证明,可用于计费与所有金钱指标 | +| `session` | NextAuth JWT 会话 | 高。但注意 `session: { strategy: "jwt" }` 意味着 `Session` 表 0 行,撤销只能等 JWT 过期 | +| `anon` | 只有 `client_fp` | **最低**。日级、可伪造(IP+UA 都能改) | + +**规则**:任何进入北极星、收入、留存的指标只允许 `grade='key'` 的行。`anon` 行只能进 +"流量 / 曝光 / 供给发现"类指标,且报表上必须显式标注。 + +**遗留坑(必须写进代码注释)**:`src/lib/admin.ts` 的 `requireAdmin()` 有一条走 +`x-api-key` 匹配 **`User.apiKey` 明文 unique 列**的分支,与新的哈希 `ApiKey` 体系并存。 +经这条路进来的请求 grade 是什么?**答:不要给它任何 grade,落 `null` 并打 +`legacy_admin_key=true`**。绝不能在埋点里顺手带上 `User.apiKey` 的值——那是明文密钥。 + +### 5.3 匿名态:日轮换指纹,不是 anon cookie + +蓝图 §4.1「自持 anon ID cookie / SecureStore」**在 TakoAPI 不成立**——匿名主力流量是 +`curl`、MCP JSON-RPC、GitHub camo 代理拉徽章,**它们都不带 cookie、不执行 JS、不存状态**。 +`next-intl` 的 `NEXT_LOCALE` cookie 也只在用户主动切语言时才写(`localeDetection: false`)。 + +替代方案: + +```ts +// src/lib/analytics.ts +function clientFp(req: NextRequest): string | null { + const salt = process.env.TAKO_ANALYTICS_SALT; + if (!salt) return null; // 未配置 → 整体关闭,不降级成明文 IP + const day = new Date().toISOString().slice(0, 10); // UTC 日 + const ip = extractClientIp(req); // 复用 src/lib/ratelimit.ts + const ua = req.headers.get("user-agent")?.slice(0, 200) ?? ""; + return createHmac("sha256", salt).update(`${day}|${ip}|${ua}`).digest("hex").slice(0, 16); +} +``` + +**四条必须写进口径文档的语义边界**: + +1. **日轮换 = 跨日不可关联。**所以 `client_fp` **不能**做留存、cohort、跨日漏斗。它的全部 + 语义就是"同一天内的去重上限"。写查询的人会想当然地拿它当用户 ID——所以字段名叫 `client_fp` + 而不是 `anon_id`,就是为了让这个错误在读代码时就显眼。 +2. **它是"日活设备数的上界",不是设备数。**NAT 后的一栋楼共享一个出口 IP,UA 又高度同质 + (`curl/8.x`),会被合并成一个指纹;反过来,同一台机器换 UA(curl → MCP → 浏览器)会裂成三个。 + **两个方向的偏差同时存在,且不可校正。** +3. **不写 cookie、不写任何客户端存储**——这是它相对 anon cookie 的合规优势(§9)。 +4. **`TAKO_ANALYTICS_SALT` 轮换 = 历史指纹全部作废。**盐进 Secret Manager 后不要"顺手轮换"。 + +### 5.4 显式来源标识:`X-Tako-Client` + +`client_fp` 猜不出"这是谁的哪个集成"。所以引入一个**自报的、非机密的、公开可见的** header: + +``` +X-Tako-Client: / +``` + +约定值(全部写进公开分发物,正因为仓库是 MIT 公开的,这些值本来就该是公开的): + +| 值 | 注入点 | 备注 | +|---|---|---| +| `takoapi-mcp/` | **`src/lib/mcp/tools.ts` 的 `getJson()` 与 `invoke_agent` 的 `fetch` —— 两处都要,不是只有 `invoke_agent`** | **H1 + H7 的共同修复点**。`invoke_agent` 那处不加,MCP 转调与裸 curl 在 `Invocation` 里完全一样;`getJson()` 那处不加,三个读工具的 loopback 会在 `/api/registry`、`/api/agents/{slug}`、`/api/skills/search` 上伪装成匿名外部流量,并把 `client_fp` 污染成 Cloud Run 出口 IP 的指纹。`getJson()` 是**一个函数覆盖三个工具**,改一处即可 | +| `takoapi-skill/` | `takoapi_skill/SKILL.md` 与 `public/install.sh` 写入的 skill 文本里的 curl 示例 | 让 SKILL 里的 curl 自报家门 | +| `takoapi-plugin/` | `plugins/takoapi/skills/takoapi/SKILL.md` | Claude Code 原生插件 | +| `takoapi-install/` | `packages/takoapi-install/bin/cli.js` 写出的示例 | npx 路径 | +| (缺失) | 裸 curl / OpenAI SDK | → `source='direct'` 或 `openai_shim`(按 route 判) | + +服务端把它映射成 `source` 枚举,**原样值也保留在 `client` 字段**(截断 64 字符)——枚举会漏掉 +第三方集成,原样值让它们可被发现。 + +> **这是"安装侧遥测"问题的解法,也是本方案最重要的一个合规取舍。** +> 调研指出 `install.sh` 与 `npx cli` **零 phone-home**,所以装机量、卸载率不可测。 +> **方案:保持零 phone-home。**理由:`public/install.sh` 的产品承诺原文是「只写自己命名空间的 +> 文件、从不改共享配置、可 `--uninstall`」,且仓库 MIT 公开——一个静默上报的安装脚本会 +> 在第一个读源码的开发者那里毁掉信任,代价远大于一个装机数。 +> **替代(三条,全部是代理指标,必须标注为代理)**: +> ① `public/install.sh` 的 GET 次数——Cloud Run 访问日志里**已经免费存在**,是"下载数"不是 +> "安装数"(CI 缓存、重复执行、`--uninstall` 都混在里面); +> ② `X-Tako-Client` 在**用户主动调用 API 时**自报——遥测只发生在用户已经在用产品的时刻, +> 不发生在安装时刻; +> ③ npm registry 的 `takoapi-install` 下载数(外部数据源,手工记录)。 +> **不可测的部分要认**:卸载率、"装了但从没调过"的沉默安装量,**这三条代理指标都测不出来**。 +> 如果将来非要测,唯一可接受的形态是 **opt-in**(`install.sh --telemetry`,默认关,且在 +> `--help` 里成文),不是 opt-out。 + +### 5.5 徽章归因:camo 剥 Referer 的替代锚点 + +`src/app/api/badge/[slug]/route.ts` 的注释已经记录了限制:GitHub 的 camo 图片代理会剥掉 +Referer,所以**徽章渲染 → 站点访问**这一跳无法归因。 + +替代做法(Phase 1,低成本):**徽章的外层链接加 `?ref=badge&a=`**。片段由我们自己生成, +**全仓恰好两处,都是一行常量拼接**(改动量按分钟计): + +| 文件 | 那一行 | 覆盖 | +|---|---|---| +| `src/components/BadgeSnippet.tsx` | `const link = \`${SITE_URL}/agents/${slug}\`` | `/agents/[slug]` 详情页的一键复制(md + html 两个片段共用这个 `link`) | +| `src/app/[locale]/badge/page.tsx` | 拼 `md` 的那一行(`[![TakoAPI](…/api/badge/${exSlug})](…/agents/${exSlug})`) | `/badge` 教程页的示例片段 | + +**只改一处等于漏掉一半的新片段**——两处生成的是同一个东西,却没有共用函数。落地时顺手把 +链接拼接抽成 `src/lib/seo.ts` 里的一个 helper,下次改口径就只有一个点。 + +```md +[![TakoAPI](https://takoapi.com/api/badge/my-agent)](https://takoapi.com/agents/my-agent?ref=badge) +``` + +点击是**用户浏览器直接发起的**,不经 camo,所以 `?ref=badge` 会原样到达。落在 +`agent_detail_viewed.referrer_host` 旁边加一个 `ref` 属性即可。 + +**诚实的边界**:这只覆盖**我们生成的新片段**;已经嵌在别人 README 里的旧片段不会自动改。 +所以 `ref=badge` 的绝对值天然低估,只能看**趋势**和**相对结构**,不能算"徽章带来的总访问量"。 + +### 5.6 多租户 + +**没有 Organization / Team / Workspace 模型**(`prisma/schema.prisma` 里一个都没有;要数模型 +就跑 `grep -c '^model ' prisma/schema.prisma`,**别把数字写进这段话**——§3.5 的规则对本文自己 +同样生效,本次审校就是在这里抓到过一个过时的数字)。所以租户边界 = `user_id`, +隔离手段只有一层 `where: { userId }`。 + +打点侧要钉死三条: + +1. **publisher 看板只给聚合。**`Agent.publisherId → User.id`,publisher 也是普通 User。 + 他能看到自己 agent 的:调用数、成功率、p50/p95 延迟、分成金额、按天趋势。 + **绝不能看到**:`api_key_id`、调用方 `user_id`、`client_fp`、`ip`、`user_agent`、 + `prompt_chars_bucket`(够细的话能推断具体调用)。 + 实现规则:publisher 查询走**预聚合视图**,不给他任何能触到明细行的路径。 +2. **小样本抑制。**当某天某 agent 的调用方 distinct `api_key_id` < 5 时,publisher 侧的 + 任何按天明细都要合并成周粒度——否则"某天只有 1 个调用方"配上时间戳,等于泄露单个客户的 + 使用模式。阈值 5 是 T1 量级下的保守值,量级起来后可降到 3。 +3. **admin 面板要意识到 `RequestLog` 里已经存着别人的个人数据**(明文 IP + UA)。 + H5 修完之后这条压力小很多,但在修完之前,`/admin/logs` 页面就是一个 PII 展示面。 + +--- + +## §6 存储与 sink 选型 + +### 6.1 选型论证 + +候选与否决理由: + +| 候选 | 判断 | 理由 | +|---|---|---| +| BigQuery(蓝图的仓) | **Phase 2 才上** | 不是因为贵(当前量级下预期 $0/月,见 6.5),而是因为它要新增 GCP API、服务账号、凭据、依赖包,而 Cloud Logging 已经在跑。Phase 2 用 **log sink** 接过去,代码零改动 | +| PostHog / Amplitude / Mixpanel 等 SaaS | **否决** | 成本红线明确写着"不引入按量计费的第三方 SaaS 底价";且蓝图案例 A 就是 SaaS 配额爆掉断供六天 | +| Redis / Upstash 做缓冲 | **否决** | 架构文档把 Redis 列为"先建"但未落地;为打点单独引入一个有状态依赖,违背"先用已有基建"的原则。`RateLimitBucket` 已经证明 Postgres 能扛这类工作 | +| 新建通用 `AnalyticsEvent` 大表 | **否决全量,只留白名单** | db-f1-micro 0.6 GB。全量事件入 Postgres 是把 OLTP 库当归档用——蓝图 §5.1 的红线 | +| 复用死表 `SkillEvent` | **否决** | 见 6.3 | +| Cloud Logging(stdout JSON) | **选它当仓** | 零新依赖、零新凭据、免费额度充裕、`logging: CLOUD_LOGGING_ONLY` 已配、`console.log` 天然满足 fire-and-forget | +| Postgres 精简热表 `ProductEvent` | **选它当热表** | 需要与 `Agent`/`Skill`/`User` join 的少数事件,Prisma 一把查 | + +### 6.2 Sink A:Cloud Logging("仓") + +- **写法**:`console.log(JSON.stringify({ severity: "INFO", tako_event: name, ... }))`。 + Cloud Run 会把 stdout 的单行 JSON 自动解析成 `jsonPayload`,`severity` 字段被识别为日志级别。 +- **查法(Phase 1,无 BigQuery)**:Log Explorer 的 + `jsonPayload.tako_event="gateway_call_completed"`,或 `gcloud logging read`。 + 够用来做周度北极星统计,不够用来做 cohort——**这是刻意接受的 Phase 1 局限**。 +- **保留期**:`_Default` bucket 默认 30 天(**待确认**,§0.4)。 +- **单行上限**:Cloud Logging 单条 LogEntry 上限 256 KB。本方案所有事件的 props 都是标量与 + 短枚举,远低于此;但 `trackEvent` 仍要在序列化后做长度检查,超 32 KB 直接丢弃并发 + `tako_analytics_error{reason:"serialize_too_large"}`——蓝图 §3.5 记录了 参照项目 把这个上限 + 只写在注释里从未实现的教训,**这次真的实现它**。 +- **Phase 2 升级路径**:建 log sink → BigQuery dataset `takoapi_analytics`, + 按天分区、`partition_expiration_days = 730`、cluster `(tako_event, source, agent_slug)`。 + 代码零改动。**注意蓝图 §5.1 的踩坑**:分区过期不等于永久保留,保留期只信 `bq show`。 + +### 6.3 Sink B:Postgres 热表 `ProductEvent`(白名单) + +**为什么不复用现成的 `SkillEvent` 表**(它已存在、已有 `@@index([skillId, createdAt])`、 +`@@index([skillId, type])`,看起来是白捡): + +1. **schema 太窄**:`skillId String`(**非空**),装不下 agent / 网关 / 计费事件; + 没有 `userId` / `apiKeyId` 列,锚点不变量无处安放。 +2. **`id String @id` 没有 `@default(cuid())`**——复用要么改 schema(那就不是"零迁移"了), + 要么每处显式传 `randomUUID()`。 +3. **它是一张来源不明的死表**(生产 1 行,`src/` 零引用,`docs/00-infrastructure.md` 的 + 「待澄清事项」明确写着不知道谁写的)。把一个语义模糊、可能有外部写入方的表放进关键路径, + 是拿一个已知的小便宜换一个未知的大风险。 +4. 用户 2026-04-22 的决策是「未搞清用途的功能全部保留,不删不改」——复用它就是"改"。 + +新表(白名单极小,只放**需要与业务表 join** 的事件): + +```prisma +model ProductEvent { + id String @id @default(cuid()) + name String // TakoEventName + ts DateTime @default(now()) + userId String? + apiKeyId String? + clientFp String? // 日轮换指纹,见 §5.3 + source String? // direct|mcp|skill|plugin|openai_shim|web|cron + agentId String? // 便于 join Agent + skillId String? // 便于 join Skill + props Json // 逐事件形状 + sampleRate Float @default(1) + + @@index([name, ts]) + @@index([userId, ts]) + @@index([agentId, ts]) + @@index([ts]) // 保留期 cron 专用:上面三个都以别的列打头,`WHERE ts < $1` 用不上它们 +} +``` + +**`@@index([ts])` 不是冗余的**:`[name, ts]` / `[userId, ts]` / `[agentId, ts]` 的前导列都不是 +`ts`,保留期清理的 `WHERE ts < $1` 在它们上面只能退化成全表扫描。在 0.6 GB 的实例上, +一次月度全表扫描就是一次业务抖动。**锚点不变量的 CHECK 约束同理要留心**:Prisma schema +**表达不了 `CHECK`**,§5.1 那条 `ALTER TABLE … ADD CONSTRAINT` 必须手写进 migration 的 SQL +文件里;而本仓历史上是用 `prisma db push` 的(`docs/00-infrastructure.md` 有记录), +**`db push` 不会带上手写约束**——所以这张表必须走 `prisma migrate`,不能 push。 + +**白名单(Phase 1 初始集合,写在 `analytics-schema.ts` 里由代码推导,不手工维护第二份)**: +`gateway_call_completed`、`gateway_call_rejected`、`api_key_created`、`api_key_first_call`、 +`api_key_revoked`、`account_registered`、`credit_topup_completed`、`credit_debited`、 +`credit_exhausted`、`agent_submitted`、`agent_status_changed`、`agent_health_checked`。 + +不在白名单里的(`registry_fetched`、`skill_detail_viewed`、`badge_rendered`、 +`mcp_tool_invoked` 等高频匿名事件)**只进 Cloud Logging**。 + +**一个蓝图踩坑在这里被结构性避开了,值得记一笔**:蓝图 §5.2 的热表要**额外**再过一道 +"client 副本不许进热表"的交集,理由是 *参照项目 的 EventLog 没有 `source` 列*,双端事件的 +client 副本进了热表就会在后台双计。本方案的 `ProductEvent` **有 `source` 列**(上面 schema +第 6 个字段),所以那道交集不需要——去重靠 `WHERE source = …` 谓词,而不是靠一份要手工维护的 +第二名单。**但这个便利有个前提**:`source` 必须**永远非空且语义唯一**。所以 §7.2 的 +`is_mcp_loopback` 不能用"`source` 缺失"来表达,必须是独立布尔——一旦有人开始拿 `source IS NULL` +当"未知来源"用,这里就退化成 参照项目 那个坑。 + +**红线(照抄蓝图 §5.1)**:热表**永远不当归档用**。它和 OLTP 同实例,膨胀会伤业务库。 +保留期 400 天,月度 cron 清理,上下界 30..730(防一个 typo 清空全表)。 + +**新增 cron**:`src/app/api/cron/retention/route.ts`(走现有的 `src/lib/cron-auth.ts` ++ `CRON_SECRET` Bearer 范式),每月一次,同时清 `ProductEvent`(400 天)与 +`RequestLog`(`TAKO_REQUESTLOG_RETENTION_DAYS`,默认 90 天)。 +**分批删**(db-f1-micro 上一把删几十万行会长时间持锁并撑爆 WAL)。**注意 PostgreSQL 的 +`DELETE` 没有 `LIMIT` 子句**——直接写 `DELETE … WHERE ts < $1 LIMIT 5000` 会语法报错 +(这是 MySQL 的写法)。正确形状是子查询 + `ctid`: + +```sql +-- 循环执行直到 rowCount < 5000 +DELETE FROM "ProductEvent" +WHERE ctid IN ( + SELECT ctid FROM "ProductEvent" WHERE "ts" < $1 ORDER BY "ts" LIMIT 5000 +); +``` + +Prisma 侧用 `prisma.$executeRaw` 跑它(`deleteMany` 同样不支持 `LIMIT`),每轮之间让出事件循环, +并给整个 cron 设一个墙钟上限(Cloud Run 请求超时之内),跑不完下个月接着跑——**保留期清理是 +幂等的,宁可慢,不要一把梭**。上下界 30..730 的校验放在读 env 之后、进 SQL 之前。 + +### 6.4 `RequestLog` 的处置(不是新建,是收敛) + +`RequestLog` 现在承担两件不该由一张表承担的事:HTTP 访问日志 + 徽章采纳计数。处置: + +1. **`ip` → `ipHash`**:改 `src/lib/requestLog.ts`,写 `clientFp(req)` 的结果而不是明文。 + **迁移**:加 `ipHash` 列,新写只写它;旧的 `ip` 列在一次性 `UPDATE "RequestLog" SET ip = NULL` + 之后再 drop(分两步,避免长事务)。 +2. **加 TTL**:见 6.3 的 cron。 +3. **徽章迁出**:`src/app/api/badge/[slug]/route.ts:73` 的 + `` logRequest({ path: `/api/badge/${slug}` }) `` 改成 + `trackEvent("badge_rendered", { agent_slug: <规范 slug>, … })`,`RequestLog` 那行删掉。 + 这同时解决了 §0.2-2 的高基数 path 污染,也让 `@@index([path, createdAt])` 不再随徽章采纳膨胀。 + **但别直接把 URL 里那个 `slug` 变量填进去**:这条路由的查询是 + `where: { OR: [{ slug }, { id: slug }], status: "APPROVED" }`——**URL 段既可能是 slug 也可能 + 是 cuid**。两种写法嵌在不同 repo 的 README 里,指的是同一个 agent,而 Q8 的 + `badge_repos_weekly = COUNT(DISTINCT agent_slug)` 会把它数成两个。 + 落地动作:给那次 `findFirst` 的 `select` 加上 `slug: true`(今天只 select 了 `kind` 与 + `stars`),事件里发 `agent.slug`,**同时**保留一个 `lookup_key_kind(slug|id)` 属性—— + 前者保证计数正确,后者让"有多少 repo 嵌的是 cuid 形式"这个迁移问题可见。 + `404`(未收录)那条分支查不到 agent,`agent_slug` 落 URL 原值并标 `value_kind='not_listed'`。 +4. **网关与 `/mcp` 是否要包 `withRequestLog`**:**不包**。它们有更精确的事件(1/2/3/10/11), + 再包一层就是同一次请求写两行 Postgres——在 db-f1-micro 上是纯浪费。 + `RequestLog` 的定位收敛为"非网关 `/api/*` 路由的 HTTP 访问日志"。 + +### 6.5 成本量级估算 + +按 §0.5 的三档给。**这些是估算,不是实测**——上线一个月后要用真实数字回填。 + +| | T0(今天) | T1(日 1k 事件) | T2(日 50k 事件) | +|---|---|---|---| +| Cloud Logging 摄取 | < 1 MB/月 | ~15 MB/月 | ~750 MB/月 | +| vs 免费额度(50 GiB/月/项目) | 0.002% | 0.03% | 1.5% | +| Cloud Logging 费用 | $0 | $0 | $0 | +| `ProductEvent` 行/月(白名单约占 20%) | ~0 | ~6k | ~300k | +| `ProductEvent` 存储(400 天保留,~350 B/行) | ~0 | ~28 MB | ~1.4 GB ⚠️ | +| `RequestLog` 存储(90 天保留) | 待确认 | ~10 MB | ~500 MB | +| BigQuery(Phase 2,log sink) | — | 存储 < 1 GB → $0(10 GB 免费);查询 < 1 TB/月 → $0 | 存储 ~18 GB(730 天)→ ~$0.4/月;查询仍在免费额度内 | + +**T2 的红旗**:`ProductEvent` 到 1.4 GB 时,db-f1-micro(0.6 GB RAM)已经装不下热索引。 +**触发条件即行动**:`ProductEvent` 行数 > 200k 时,要么升 DB 规格,要么把白名单进一步收窄 +(把 `gateway_call_completed` 移出热表,只留在 Cloud Logging + BigQuery)。这个决策点要提前 +写进 runbook,别等磁盘告警。 + +**成本护栏(蓝图 §7.6 的本项目版)**: + +- ❌ **BigQuery `maximumBytesBilled`** —— Phase 1 无 BQ,不适用。Phase 2 设 1 GiB(不是蓝图的 + 5 GiB,量级差得远)。 +- ✅ **账单预算 $5**(不是蓝图的 $50),三档 50/90/100%。 + **必须显式设 `EXCLUDE_ALL_CREDITS`** —— 蓝图记录了 GCP 预算默认 `INCLUDE` credits、 + 账户有赠金时永远不触发的坑。TakoAPI 是新 GCP 项目,**极可能有赠金**,这个坑命中概率很高。 +- ✅ **Postgres 行数护栏**(本项目独有):`ProductEvent` 或 `RequestLog` 行数超阈值告警。 + 这比 BigQuery 预算重要得多——0.6 GB 的实例撑爆是**业务宕机**,不是账单超支。 + +--- + +## §7 口径规则:TakoAPI 最容易数错的那些事 + +蓝图 §6.3 的思想是「把容易写错的规则编进数据库视图,让最省事的查询恰好就是正确的查询」。 +Phase 1 阶段本项目没有 BigQuery,视图落在 **Postgres 视图**上,**用 `prisma/migrations/` 里 +手写的 `CREATE VIEW` 语句**,Phase 2 迁到 BQ。 +(**别用 `prisma/sql/`**:那是 Prisma 的 TypedSQL,需要在 generator 里开 +`previewFeatures = ["typedSql"]`,本仓的 generator 块是光板的 `provider = "prisma-client-js"`, +目录也不存在——为两个视图引入一个 preview feature 不划算,读侧用 `$queryRaw` 就够。) +下面每条都标注了**不这么做会得到什么错数**。 + +### 7.1 「成功完成的 agent 任务」的精确定义(北极星) + +``` +success := status < 400 + AND error_code IS NULL + AND task_state NOT IN ('failed', 'canceled', 'rejected', 'input-required') +``` + +- **`status < 400` 单独不够**:A2A 的 JSON-RPC 在任务失败时照样回 HTTP 200,失败信息在 + `result.status.state` 里。今天三条网关路由**都不解析它**(`meterInvocation` 的 `taskState` + 参数从没被传过)——所以在 H2 修好之前,北极星只能是**上界**,必须在报表上标注 + "task_state 未采集,此数为上界"。 +- **`input-required` 算不算成功?** 不算。它是"agent 需要更多输入",任务没完成。 + 但它也不是错误——所以单独在报表上列一列 `needs_input`,别混进 `task_failed`。 +- **不这么做的错数**:把 HTTP 200 的失败任务全算成功。上游 agent 越不稳,北极星越好看—— + 指标方向和产品健康**反向**,这是最糟的一类指标缺陷。 + +**这条定义必须固化成视图,不能只活在这一段 prose 里**(蓝图 §6.3 的整个论点:*prose 无法强制 +执行任何口径,每个新分析师都会重推一遍,推错的那个把错数发出去*)。本方案先前只给了 §7.7 的 +`v_event_counts` 一个视图,把北极星、route 不可相加(§7.5)、MCP loopback 去重(§7.2)三条 +最容易写错的规则全留在了文字里——**本次审校补上下面这个**,与 `ProductEvent` 热表同一批 migration 落地: + +```sql +CREATE VIEW v_gateway_calls_canon AS +SELECT + e."ts", + e."userId", e."apiKeyId", e."agentId", + e."props"->>'agent_slug' AS agent_slug, + e."props"->>'route' AS route, -- 永远不许 SUM 掉(§7.5) + COALESCE(e."source", 'unknown') AS source, -- 非空,见 §6.3 + COALESCE((e."props"->>'is_mcp_loopback')::bool, false) AS is_mcp_loopback, + (e."props"->>'status')::int AS status, + e."props"->>'error_code' AS error_code, + e."props"->>'task_state' AS task_state, + (e."props"->>'latency_ms')::int AS latency_ms, + -- 唯一权威的成功判定。改这里 = 改北极星,改之前先读 §7.1。 + ( (e."props"->>'status')::int < 400 + AND e."props"->>'error_code' IS NULL + AND COALESCE(e."props"->>'task_state', '') NOT IN + ('failed','canceled','rejected','input-required') ) AS is_success, + -- task_state 尚未采集时(H2 未修完),is_success 是上界。这一列让报表能自己发现这件事。 + (e."props"->>'task_state' IS NULL) AS task_state_missing +FROM "ProductEvent" e +WHERE e."name" = 'gateway_call_completed'; +-- KEEP A DATE PREDICATE:调用方仍需自己加 ts 下界。 +-- 规则出处:docs/analytics-plan-2026-08-21.md §7.1 / §7.2 / §7.5 +-- 红线:① 任何按 route 汇总必须显式 GROUP BY route(message 与 stream 的"成功"是两种物理事件); +-- ② 北极星只数这张视图,永不加 mcp_tool_invoked(§7.2 特例); +-- ③ task_state_missing 为 true 的行占比要与结果一起报出来,否则读者不知道自己看的是上界。 +``` + +**为什么值得单开一个视图而不是让大家自己写 `WHERE`**:上面三条红线里的每一条,写错的形态都是 +**一个正常的正整数**——没有任何监控会因为漏了 `GROUP BY route` 而报红(§7.9 结尾的同一个论点)。 +视图的价值不在省字数,在于**让最省事的查询恰好就是正确的查询**。 + +### 7.2 MCP 的**每一个**工具都会双计,不只是 `invoke_agent` + +先把机制说准(这是本节上一版写窄了的地方):`src/lib/mcp/tools.ts` **没有一个工具直接查库**。 +它的头注释自己写着 "a thin facade over TakoAPI's OWN public REST/gateway API"——四个工具全部 +经公网 loopback 打回自己: + +| MCP 工具 | 内部实际打的 endpoint | 于是同时产生 | +|---|---|---| +| `search_agents` | `GET /api/registry?format=json&…` | `mcp_tool_invoked` + `registry_fetched` | +| `get_agent` | `GET /api/agents/{slug}` | `mcp_tool_invoked` + 该路由的 `RequestLog` | +| `search_skills`(带 query) | `GET /api/skills/search?q=…` | `mcp_tool_invoked` + `search_performed` | +| `search_skills`(无 query) | `GET /api/agent?format=json` | `mcp_tool_invoked` + `registry_fetched` | +| `invoke_agent` | `POST /v1/agents/{slug}/message` | `mcp_tool_invoked` + `gateway_call_completed` | + +所以规则不是一条,是**一条通则加一个特例**: + +> **通则:任何"MCP 工具层"事件与"API/网关层"事件,永远不相加。** +> 一次 MCP 用户行为在两层各留一条记录,这是 facade 架构的必然结果,不是缺陷。 +> 每张报表必须显式声明自己数的是哪一层: +> **业务量(北极星、搜索量、目录拉取量)一律数 API/网关层**(那是唯一能同时看见 MCP 与非 MCP 的层); +> **MCP 采用度(有多少客户端在用我们的 MCP、哪个工具最常被调、工具级失败率)数工具层。** +> +> **特例:北极星永远只数 `gateway_call_completed`,永远不加 `mcp_tool_invoked`。** +> 因为 `invoke_agent` 的失败可能发生在两层中的任一层(工具层的参数校验失败 / 缺 token, +> 与网关层的 402/429/502),只有网关层的口径与非 MCP 调用可比。 + +**实现侧的配套(不做这一步,上面的规则在 SQL 里根本写不出来)**:API/网关层事件必须能回答 +"这条是不是 MCP loopback 打来的",靠的是 §5.4 的 `X-Tako-Client` → `source='mcp'` + +`is_mcp_loopback=true`。没有这个字段,`registry_fetched` 里 MCP 流量与真实外部 curl +**在数据上完全同形**,"MCP 带来多少目录流量"(Q3)就永远算不出来。 + +这条要写进视图注释与报表标题。蓝图案例 A 的核心教训就是双计数——PostHog 里所有 client 事件 +被双计 ~2 倍,历史上所有分析虚高一倍且数月无人发现。**本项目的双计不是实现失误,是架构自带的, +所以它不会被"修好",只能被口径纪律一直挡住。** + +### 7.3 三个"看起来是转化指标但不是"的计数器 + +| 字段 | 真实语义 | 禁止用法 | +|---|---|---| +| `Agent.callsCount` | `meterInvocation()` 对**所有** status 递增,包括 502 和 4xx | 禁止当"成功调用数"。它只配做目录页排序 | +| `Skill.downloads` | **scraper 从 GitHub 抓来的外部数字**,产品代码从不递增 | 禁止当我们的转化指标(调研已点名) | +| `Skill.viewsCount` | 只在 `GET /api/skills/[id]` 里 `increment: 1`。而**这个端点同时服务浏览器和 LLM**(任何 curl 目录的人、任何拿到 skill id 的 agent) | 禁止当"人类浏览量"。**人机混计**,且爬虫/无 JS 客户端完全不计(详情页是 client component,靠浏览器 fetch)。更要命的是它**只是一个单调计数器,没有任何带时间戳的行**——这条路由没被 `withRequestLog` 包住,所以"这个 skill 上周被看了几次"今天**根本无法回答**,只能看总数。要分人机、要按天,就得靠 `skill_detail_viewed.ua_class`,**`viewsCount` 保持不动**——它是排序用的虚荣数字,改它会动 `/trending` 与 `/admin` 的排序 | + +### 7.4 营收只从 `LedgerEntry` 数 + +`credit_topup_completed` 有 `path(return|webhook)` 两条触发路径,可能双发;`credit_debited` +是 `meterInvocation` 的一个视角。**规则**: + +> 任何美元金额(充值额、5% 费收入、消耗、余额)**一律从 `LedgerEntry` + `CreditBalance` 算**, +> 事件只用于诊断与漏斗,**永不用于财务口径**。 + +理由:`LedgerEntry` 是不可变流水且与 `CreditBalance` 在同一事务里写;事件是 fire-and-forget, +按契约就是允许丢的。**让允许丢的东西去数钱,是设计错误。** + +### 7.5 SSE 与 message 的成功率不可比、不可相加 + +见 §0.2-4。在 `gateway_stream_closed`(事件 3)落地之前: + +- stream 的"成功"含义是"**上游返回了 header**",message 的是"**上游返回了完整 body**"。 +- 两者相加得到的成功率是**路由分布的函数**,不是产品质量的函数。这是蓝图 §6.2「网格每卡计一次 + vs 全屏每屏计一次,相加得到的是布局的函数」在本项目的对应形态。 +- **视图层强制**:北极星视图里 `route` 必须是显式维度,不允许 `SUM` 掉。 + +事件 3 落地后,stream 的成功口径改为:`reason='complete' AND bytes_out > 0`。 +这个定义**顺带修掉了 `!upstream.body` 那条已收费的假成功**(§0.2-4):那次调用的 +`gateway_stream_closed.reason='no_body'`、`bytes_out=0`,于是它在成功率里正确地落到分母而不是分子, +同时因为它带着 `billed_usd > 0`,还能被单独捞出来做**退款候选清单**——这是本方案里唯一一个 +"打点直接指向一笔该退的钱"的地方,别让它只停在看板上。 + +### 7.6 locale 的解析陷阱 + +`src/i18n/routing.ts` 配的是 **en 不带前缀,其余 14 个带前缀**。所以: + +- 从 URL path 解析 locale 时,**"没有 locale 段"这个状态就是 `en`**。naive 的 + `path.split('/')[1]` 会把 `/agents/foo` 解析成 locale=`agents`。 +- GA4 的 pageview 按 URL path 计,同一个页面在 15 个 locale 下是 15 个不同 path—— + 做 Q9 的分母时必须先归一化。 +- 事件里的 `locale` **不要从 URL 现推**,直接用 next-intl 已经解析好的值 + (server component 里 `params.locale`;API 路由里从 `Accept-Language` 或调用方显式传)。 + URL 解析在服务端做一遍,是把一个已经解决的问题重新解决错。 +- **最大的一个陷阱不在解析,在分母**:`src/proxy.ts` 已经对**非 en 的 `/agents/*` 与 + `/skills/*` 详情页**主动打了 `X-Robots-Tag: noindex, follow`(`isNonDefaultLocaleEntityDetail()`; + 理由写在函数上方注释里——详情页正文是抓来的英文,14 个 locale 只是 UI 壳,全量索引会被 + 判成 scaled/duplicate content)。所以: + - **"15 语言 SEO"实际只对列表页 / scenario 页 / 营销页成立**,详情页的非英语版本是**故意** + 不进索引的。 + - 算 Q9 的"曝光 → 注册"转化时,分母(GSC 曝光)里根本不会有那些 noindex 页, + 但分子(`agent_detail_viewed` / `skill_detail_viewed` 的非 en 行)里**会有**—— + 直接相除会得到一个凭空虚高的转化率。 + - **规则**:Q9 的 locale ROI 必须**按页面类型分开算**:`scenario_page_viewed` 与列表页走 + "GSC 曝光 → 注册"的正常漏斗;`*_detail_viewed` 的非 en 行只能算"站内流转", + **不许拿 GSC 曝光当它的分母**。 + - 这条同时是一个**下游改名风险点**(蓝图案例 B):哪天有人放开 noindex,Q9 的口径要跟着改, + 否则会把新增曝光当成 SEO 见效。 + +### 7.7 采样还原:`sample_rate` 必须入库 + +任何采样事件的计数都要 `SUM(1.0 / sample_rate)` 而不是 `COUNT(*)`。 +**规则**:采样率**必须作为事件属性写进每一行**,绝不能只活在代码常量里—— +改了 env 之后,历史行的还原系数就错了,而没人会记得某天改过。 + +**视图强制**(Phase 1 建): + +```sql +CREATE VIEW v_event_counts AS +SELECT name, date_trunc('day', ts) AS day, + SUM(1.0 / COALESCE("sampleRate", 1)) AS weighted_count, + COUNT(*) AS raw_rows -- 显式命名为 raw_rows,防止有人误当计数 +FROM "ProductEvent" GROUP BY 1, 2; +-- KEEP A DATE PREDICATE:调用方仍需自己加 ts 下界。 +-- 规则出处:docs/analytics-plan-2026-08-21.md §7.7 +``` + +### 7.8 比率的到达滞后:**分路由裁决,不一刀切 T-2** + +蓝图 §6.4 的全局规则是"比率一律切 T-2"。TakoAPI 要分开: + +| 指标族 | 规则 | 理由 | +|---|---|---| +| 网关(北极星、成功率、延迟) | **T-0 可用,但当天只许读日志 sink** | 分子分母走同一条代码路径,没有跨管线的到达滞后。**但 `meterInvocation` 是 `void` 调的**:在 Cloud Run 的请求级 CPU 下,`Invocation` 行可能被推迟到该实例的下一次请求才落库,甚至随实例回收而丢(§2.4)。所以"今天的北极星"读 `gateway_call_completed`(响应返回之前就已写出),`Invocation` 要到 T-1 才算到齐 | +| 徽章(采纳数、渲染量) | **切 T-2** | `Cache-Control: s-maxage=3600` + GitHub camo 自己的缓存,渲染量的到达是被 CDN 平滑过的 | +| SEO / GSC(曝光、点击) | **切 T-3** | GSC 数据本身有 2–3 天延迟,且与站内事件是完全不同的管线 | +| 激活漏斗(注册 → key → 首调) | **按 cohort 成熟度切**,不是按天 | 首调可能发生在注册后 7 天。分母(注册)到齐了,分子(首调)还在累积 → 未成熟 cohort 的转化率**必然虚低**。规则:只报 **cohort 已满 14 天**的转化率 | + +蓝图案例 C(66% 的假 CTR)的机制正是"分子分母走不同管线"。TakoAPI 的对应形态不是 CTR, +**是激活漏斗**——它比 CTR 更容易骗人,因为"最近一周注册的人还没首调"看起来完全合理。 + +### 7.9 身份单位对照表(蓝图 §6.1 的本项目版——这条本方案先前漏了) + +蓝图 §6.1 的规则是"留存 / cohort / DAU / 漏斗一律 `COUNT(DISTINCT userId)`,禁止 `COUNT(*)` +和 `COUNT(DISTINCT session_id)` 当人数用"。TakoAPI **不能照抄**,因为这里有**三个**候选单位 +(§5.1),混用比在 参照项目 更容易发生:`user_id` 是人、`api_key_id` 是集成、`client_fp` 是 +"同一天内的设备上界"。规则必须逐报表钉死: + +| 报表 / 指标 | 唯一合法的计数单位 | 为什么不是别的 | +|---|---|---| +| 北极星(周成功任务数) | `COUNT(*)` on `gateway_call_completed WHERE outcome='success'` | 它本来就是**动作总量**,不是人数。报表标题必须写"任务数",不许写"调用用户数" | +| 开发者激活 / 漏斗(Q2) | `COUNT(DISTINCT api_key_id)` | 用 `user_id` 会把"同一人的第二把 key 首调"算成老用户的普通调用,第 4 步永远数不对(§5.1) | +| 付费 / 充值 / 余额(Q6) | `COUNT(DISTINCT user_id)` | 钱挂在人身上(`CreditBalance.userId` 是主键),不挂在 key 上 | +| 周留存 / cohort | `COUNT(DISTINCT api_key_id)`,**且只取 `grade='key'` 的行** | `client_fp` 日轮换、跨日不可关联,**永远不能做留存**(§5.3 边界 1) | +| 目录 / SEO 流量(Q7 Q9) | `SUM(1/sample_rate)` 做量、`COUNT(DISTINCT client_fp)` 做"上界" | 匿名侧没有真身份。任何用 `client_fp` 算出来的"人数"在报表上**必须**带"上界/estimate"字样 | +| 供给侧(Q4 Q5) | `COUNT(DISTINCT agent_slug)` | 单位是 agent,不是人 | + +**两条硬规则**: + +1. **同一张图里不许混单位。**"本周 120 个开发者调用了 45 个 agent"——这句话里的 120 如果是 + `api_key_id` 而读者以为是人,决策就错了。图例必须写单位名("120 keys",不是"120 devs")。 +2. **`COUNT(*)` 只允许出现在明确标注"动作总量"的地方**,且标题里必须有"数/次"而不是"人/用户"。 + 这条和蓝图 §6.1 完全一致,是唯一可以照抄的一句。 + +**为什么这条排在采样与滞后之后仍然重要**:前面几节挡的是"数错了多少",这一节挡的是 +"数的根本不是同一种东西"。后者不会在任何监控里报红——两个都是正整数,图也画得出来。 + +--- + +## §8 监控 + +蓝图 §7 是四件套且"不可合并"。TakoAPI 对应关系如下,**有两件在当前量级下不适用,另加两件 +本项目独有的**。 + +### 8.1 Ingestion floor —— **适用,但 T0 只能做"绝对零"型** + +- **查什么(T0)**:Cloud Run 有请求(`run.googleapis.com/request_count > 0`)但 + `jsonPayload.tako_event` 的 logs-based metric 连续 **6 小时为 0** → 告警。 +- **为什么不能抄蓝图的"< 5 次/小时持续 3 小时"**:那是在 21k events/天的量级下调出来的。 + TakoAPI 今天的正常状态**就是接近零**,任何比率型地板都会天天误报,然后被静音,然后失效。 +- **升级触发条件(写进告警 YAML 的 OPS note)**:当连续 7 天日均 `tako_event` > 200 时, + 把这条告警改成"日均量跌破 14 天健康日中值的 30%"。 +- **为什么在日志层而不是 sink 层**:日志层在两个 sink 的上游,Phase 2 换成 BigQuery 时监控不用动。 + +### 8.2 Sink 失败告警 —— **适用,原样上** + +- **查什么**:logs-based metric 过滤 `jsonPayload.tako_event="tako_analytics_error"`, + 5 分钟窗口内 ≥ 1 条即告警。 +- **为什么**:fire-and-forget 的代价就是失败静默;这条告警是那个设计决定的**对价**, + **必须与 sink 同一天上线**,不能排到 Phase 1。 +- **本项目特有的注意**:`tako_analytics_error` 本身不进 DB sink(DB 挂了时它正好发不出去), + 也不采样。 +- **改字符串前先搜 YAML**:`tako_analytics_error` 与 `tako_event` 这两个字面量被 logs-based + metric 的 filter 匹配。改它们 = 静默失明。这条要写进 `AGENTS.md` / `CLAUDE.md` 的长期纪律。 + +### 8.3 双 sink 日对账 —— **降级为 Phase 2;Phase 0 先做更重要的"财务三件套对账"** + +蓝图 §7.3 对的是"两个 sink 都活着但不一致"。在 TakoAPI,Cloud Logging sink 是 `console.log`, +它几乎不会"活着但丢行"(唯一的丢法是 Cloud Run 实例被强杀时 stdout buffer 未 flush)。 +所以**全白名单逐事件比率对账**的边际价值低,推到 Phase 2(有 BigQuery 后再做,因为那时才好查)。 + +**但有一条例外必须 Phase 0 就上**:日志侧 vs `Invocation` 的行数比(下表 ④)。 +它不是"两个 sink 不一致",而是"DB 那一侧的**写入根本没发生**"——在 Cloud Run 的请求级 CPU 下 +这是有物理机制的(§2.4),且丢的是钱。两者形态不同,别因为都叫"对账"就一起排到 Phase 2。 + +**Phase 0 优先做的是本项目独有的、价值高得多的财务对账**(新 cron +`src/app/api/cron/reconcile/route.ts`,每天 10:00 UTC,走 `CRON_SECRET`): + +| 对账 | SQL 关系 | 不等意味着 | +|---|---|---| +| ① | **左右都用 `LedgerEntry.invocationId` 对上**,不是两边各数一个 `COUNT`:`Invocation WHERE billedUsd > 0` 与 `LedgerEntry WHERE type='DEBIT'` 应**一一对应**(`FULL OUTER JOIN … ON le."invocationId" = i.id`,两侧的孤儿数都必须是 0) | 左孤儿 = 计了费没扣款;右孤儿 = 扣了款没有对应调用。**用两个独立 `COUNT` 相等来判断是错的**:它会把"一条左孤儿 + 一条右孤儿"判成正常。`LedgerEntry.invocationId` 有 `@@index`,join 很便宜——现成的列不用是浪费。另注:`meterInvocation` 的 DEBIT 分支条件是 `billedUsd > 0 && input.userId`,所以一条 `userId` 为 null 的计费调用会天然产生左孤儿;今天网关路径上 `keyRecord.userId` 恒非空(`ApiKey.userId` 是必填列),**这个前提哪天变了,这条对账会第一个叫** | +| ② | `SUM(LedgerEntry.amountUsd) GROUP BY userId` == `CreditBalance.balanceUsd` | 有人在 `grantCredit` / `meterInvocation` 之外改过余额——**这是资金安全事件** | +| ③ | `SUM(Invocation.billedUsd)` == `-SUM(LedgerEntry WHERE type='DEBIT')` | 计费金额与扣款金额漂移 | +| ④ | **日志侧 `COUNT(gateway_call_completed)` == DB 侧 `COUNT(Invocation)`**(同一时间窗,日志为分母) | **计量事务根本没跑**——①②③ 比的都是"已落库的行之间是否自洽",事务没开始时三边同时缺,比出来全绿。这一条是唯一能看见它的(§2.4 R4)。差值即丢失率,**> 0.5% 触发 §2.4 R3 的二选一决策** | + +**三个照抄蓝图的实现细节**: +① mismatch 时**返回 HTTP 200**(`ok:false` 在 body 里)——Cloud Scheduler 对非 2xx 会重试, +重试一个"正确地发现了不一致"的对账只是烧钱重放同一发现;告警走结构化日志 +`tako_event:"reconcile_result", outcome:"mismatch"` → logs-based metric。 +② DB 不可达 ≠ mismatch,单独计数。 +③ 容差:**②必须精确相等**(Decimal,不是 float);①③ 允许 T-0 当天的在途差(`meterInvocation` +是 fire-and-forget,事务可能还没落),所以**对 T-1 那天对账**,不对今天。 + +### 8.4 Per-event 量级回归 —— **T0/T1 不适用** + +蓝图 §7.4 的基线是"14 天健康日中值"。TakoAPI 今天所有事件的中值都是 0,检测器输出恒为噪音。 + +- **启用条件**:某事件连续 14 天日均 ≥ 50 行。达标的事件才进入这个检测器,不达标的不进。 +- **T0/T1 的替代**:对**四个关键事件**做"绝对零"告警—— + `gateway_call_completed`、`account_registered`、`api_key_created`、`credit_topup_completed` + 中任一在**过去 7 天有过行、但最近 48 小时为零** → 告警。 + 这条能抓到蓝图案例 A 那种"单事件归零而总量看不出来"的形态,且在零流量下不误报。 + +### 8.5 本项目独有:上游 agent SLO 看板(蓝图无对应物) + +这是 TakoAPI 的核心运营抓手——**我们把自己的信誉借给了上游 agent**。 + +- **看板**:新增 `/admin/agents` 的一个 tab,per-agent 显示:7 日成功率、p50/p95 `latency_ms`、 + `down_ratio`(healthStatus 为 down/degraded 的时长占比)、`error_code` 分布、调用量。 +- **数据源**:事件 1、3、4。**注意口径可用的时间不一致**:成功率、p50/p95、`error_code` 分布 + 可以从**已有的 `Invocation` 历史**回算(`latencyMs` / `status` / `errorCode` 三列早就在写), + 上线当天就有历史曲线;而 **`down_ratio` 只能从事件 4 开始的那天往后攒**—— + `Agent.healthStatus` 是被每轮 cron 覆写的当前值,没有历史(§1 Q5)。 + 看板上这两组指标必须**分开标注起算日**,否则第一个月的 `down_ratio` 会因为窗口不满而虚低, + 正好把最该被降权的 agent 放过去。 +- **SLO 与动作(阈值属 T1 档,量级起来后回测重调)**: + | 条件(7 日窗口,调用量 ≥ 20) | 动作 | + |---|---| + | 成功率 < 90% | 目录页标注 "unstable" | + | 成功率 < 70% 或 `down_ratio` > 30% | 从默认排序降权 | + | 连续 14 天 `healthStatus='down'` | 转 `status` 为 PENDING(下架),通知 publisher | +- **为什么必须有**:Q5 直接问了这个;而且没有它,一个挂掉的上游 agent 会一直消耗我们的 + 429/502 预算和用户信任,直到有人手工发现。 + +### 8.6 本项目独有:Postgres 容量护栏 + +- `ProductEvent` 行数 > 200k **或** `RequestLog` 行数 > 500k → 告警(见 §6.5 的红旗)。 +- Cloud SQL 磁盘使用率 > 70% → 告警。db-f1-micro 上这是**业务宕机**风险,不是账单风险。 +- 实现:`src/app/api/cron/reconcile/route.ts` 顺手多跑两条 `COUNT(*)`,超阈值发结构化日志。 + +### 8.7 告警 YAML 防漂移 —— **Phase 2** + +蓝图 §7.5。TakoAPI 当前**一条告警策略都没有**,所以先有策略再谈漂移检查。 +告警 YAML 进仓路径定为 `docs/monitoring/alert-*.yaml`(与蓝图同构,方便照抄脚本)。 +**记一笔**:账单预算是账单账户资源,不在 drift check 覆盖范围内,要单独记。 + +--- + +## §9 合规与隐私 + +### 9.1 硬规则(写进 `src/lib/analytics.ts` 头注释,违反即事故) + +| 禁止入任何 sink 的东西 | 出处 | +|---|---| +| **prompt 原文 / agent 响应体** | `docs/agent-marketplace/03-technical-architecture.md` §11(OWASP LLM Top 10)已把"日志默认脱敏"定为架构约束;`Invocation` 现在刻意不存 `body.text`。只记 `prompt_chars_bucket`(分桶,**不是精确长度**——精确长度对短 prompt 有区分度) | +| **prompt 的 hash** | 短 prompt 可撞库还原。连 hash 都不要 | +| **API key 明文或 prefix** | `src/lib/apikey.ts` 只存 SHA-256 + 6 位 prefix。埋点只能出现 `api_key_id`(cuid)。**prefix 也不行**——它是 key 的前缀,属于凭据材料 | +| **`User.apiKey`(遗留明文列)** | 仍被 `src/lib/admin.ts` 的 `requireAdmin()` 使用。绝不能在埋点里顺手带上(§5.2) | +| **明文 IP** | H5。只存 `client_fp`(HMAC + 日轮换) | +| **邮箱 / 用户名 / bio / website** | 身份特征不入仓(蓝图 §8.4)。需要时 JOIN 回 `User` 表 | +| **搜索词原文** | 只记 `query_chars_bucket` + `result_count` + `zero_result` | +| **完整 User-Agent** | 只记 `ua_class` 枚举 + 用于指纹的哈希输入(哈希后不可还原) | +| **上游 agent 的 endpoint URL 全文** | 只记 `upstream_host`(域名)。完整 URL 可能带 query 里的凭据 | + +**开源可见性这条最硬**:仓库 MIT 公开在 GitHub,`public/install.sh`、`takoapi_skill/SKILL.md`、 +`packages/takoapi-install` 是公开分发物。**任何写进代码或前端 bundle 的 endpoint / key 等同公开** +(现有 GA4 ID 就是硬编码并注释「Safe to commit」的)。所以: + +- `TAKO_ANALYTICS_SALT` **必须**走 Secret Manager(`--set-secrets`),不能进 `--set-env-vars`。 + 前置条件已就绪(Secret Manager API 与 SA 授权 2026-04-22 完成,§2.3)。 + `origin/main` 的 `cloudbuild.yaml` 已经在用 `--update-secrets` 绑定 6 个 secret, + 照它的写法加一条即可(初稿这里写的"已与线上漂移、照它部署会覆盖生产密钥"**是错的**, + 见 §0.1 下方的注)。真正要记住的是:**没有触发器在跑这个文件**,实际生效的是人工 + `gcloud run deploy`,两处都要加。 +- Phase 2 若引入客户端埋点,`NEXT_PUBLIC_*` 变量的值等同公开——**只能是 write-only 的公开 + measurement id 类值,绝不能是带读权限的 token**。 + +### 9.2 "零结果查询词"的例外通道(一条显式、留痕的决定) + +Q7 需要知道"用户搜什么搜不到"。这需要**原始文本**,与 §9.1 冲突。蓝图 §8.4 的规则是: +凡用户原始文本要入库,必须是一条**显式、留痕**的决定(谁批的、为什么、影响哪个字段)。 + +**本方案的决定**(留痕于此,Phase 2 落地前需要维护者书面确认): + +- 只对 `zero_result = true` 的查询记原文,命中结果的不记。 +- 只进 **admin 侧独立表**(不是 `ProductEvent`,不是 Cloud Logging),保留 **7 天**, + 只有 `role=admin` 可读。 +- 记录时**不带任何身份锚点**(无 `user_id` / `api_key_id` / `client_fp`)——切断与人的关联。 +- **重估触发条件**:若查询量级起来后出现明显的个人信息(邮箱、姓名、密钥形状)泄漏进查询词, + 立即关闭这条通道并清表。 + +**没有触发条件的例外决定会变成永久盲区**(蓝图 §8.2)。这条写在这里就是为了防止它变成默认行为。 + +### 9.3 GDPR:本项目的删除面比 参照项目 小一个量级,但有一个新盲区 + +- **蓝图 §8.3 的 streaming-buffer 日扫不适用**:TakoAPI 没有 streaming insert。 +- **`deletePerson(userId)` 的表面积**:`ProductEvent`(`WHERE "userId" = $1` DML 删除) + + `RequestLog`(同)+ 业务表(`User` / `ApiKey` / `Invocation` / `LedgerEntry`)。 + **注意 `LedgerEntry` 是财务记录**,多数司法辖区要求保留——删除时应做**假名化** + (`userId` → 墓碑值)而不是物理删除,且这个决定要单独留痕。 +- **新盲区:Cloud Logging 是 append-only,无法按行删除。** + 这是本方案选 Cloud Logging 当仓的**已知代价**,必须显式承认: + - Phase 1 的缓解:`_Default` bucket 30 天自然过期(**待确认**,§0.4)= 天然的删除边界。 + 所以**日志里除 `user_id` 之外绝不能有任何身份特征**(§9.1 已强制), + 30 天后 `user_id` 与人的关联随行过期而消失。 + - Phase 2 挂 BigQuery 后,BQ 表支持 DML,`deletePerson` 要加一条 + `DELETE FROM takoapi_analytics.events WHERE user_id IN UNNEST(@userIds)` + (批量,一个 job 清一批,不是每人一个 job)。 + - **RTBF 审计轨**:删除日志记 subject id(截断到 200 个)——"证明你删了这个人"要有据可查。 +- **删除失败只记日志不抛**:删除流程不能因打点清理失败而卡死(蓝图 §8.3)。 +- **前置:注销流程今天不存在(§0.4 已实测)。**`src/` 下零个 `prisma.user.delete` / + delete-account 路由,`/api/admin/users/[id]/route.ts` 只有 `PATCH`,`User` 也没有 + `status` / `deletedAt`。所以本节**今天没有触发点**,实现随注销功能一起做。 + 但**规则现在就要定**,而且要定成一条**验收条件**而不是备忘: + > 将来实现注销的那个 PR,**必须同时**清 `ProductEvent` 与 `RequestLog` 的对应行, + > 并对 `LedgerEntry` 做假名化。 + 写在这里还不够——本条要同步进 `AGENTS.md` / `CLAUDE.md` 的长期纪律,否则它会以 + 蓝图 §2.4 的经典方式失效:一条只活在文档里、没人验证过的防线。 + 另外,`User` 没有 `deletedAt` 意味着**蓝图 §8.3 那种"扫 status+updatedAt 窗口"的幂等日扫 + 在本项目今天连人群都圈不出来**——做注销时优先选**软删 + `deletedAt`**,别选物理删除, + 否则打点侧的补删将永远无从下手。 + +### 9.4 Consent:Phase 0 不需要,Phase 2 必须,GA4 是既存债务 + +- **Phase 0 的服务端事件不需要 consent**:不写 cookie、不写任何客户端存储、不做跨站追踪、 + 无第三方处理器(Cloud Logging 与 Postgres 都是自持基础设施)、身份特征不入库、 + 匿名指纹日轮换不可长期追踪。这套姿势下,服务端日志属于运营必要处理。 +- **既存债务**:GA4(`G-PPXV98MJ4Y`)已在 15 个 locale 面向全球流量运行,**没有任何 consent + banner 或 gating**,且是第三方处理器 + 持久 client_id。这是**现在就存在**的欧盟合规敞口, + 不是本方案引入的。本方案**不扩大它**(Phase 0 一行客户端代码都不加)。 +- **Phase 2 的硬条件**:建 `/api/telemetry/event` 客户端入口时,**必须同期**落地: + ① 区域化 consent gate(未知地区默认 `opt_in`,fail-closed;显式 allowlist 跳过弹窗); + ② 尊重 `Sec-GPC`;③ 服务端复查(绕过客户端的调用也被挡); + ④ 顺便决定 GA4 的去留——**要么给它加 gate,要么用自持事件替代它然后关掉**。 + 挂载点唯一:`src/app/[locale]/layout.tsx`(第 116 行附近的 ``)。 + **不允许"先上埋点、consent 下个迭代补"**——那正是把一个已知敞口从"一个第三方脚本"扩大成 + "一整套自建追踪"。 + +### 9.5 自托管与离线:默认可跑、可关 + +`docker-compose.yml` 支持本地起全栈;`public/install.sh` 的产品承诺是「只写自己命名空间的文件、 +从不改共享配置、可 `--uninstall`」。对应规则: + +- 两个 sink 都由 env 开关(§2.3)。**这里要说准,否则和 §2.3 的默认值自相矛盾**: + - `TAKO_ANALYTICS_DB` **不设 = 关**,热表一行都不写。 + - `TAKO_ANALYTICS_LOG` 在 `NODE_ENV=production` 下**默认开**——但它写的是**自托管者自己进程的 + stdout**,没有任何出站请求,数据全部留在他自己的机器/日志系统里。所以准确的表述是 + **"零外发遥测",不是"零日志"**;要连本地这行 JSON 都不要,设 `TAKO_ANALYTICS_LOG=0`。 + - 这条差别要写进 README 的自托管章节。把"默认开的本地 stdout"含糊成"不配置即 no-op", + 是那种**读源码的人一眼就能戳穿**的措辞——而 §9.1 说过,戳穿它的代价是信任,不是一个数字。 +- `install.sh` / `npx cli` **零 phone-home**(§5.4)。 +- `X-Tako-Client` 是**自报**的,用户可以不发或改写——这是特性不是缺陷。埋点必须容忍它缺失 + (`source='unknown'`),**绝不能用它做任何门禁**(不做限流豁免、不做鉴权、不做定价分档)。 + **一个必须提前认下的副作用**:H7 让服务端在看到 `takoapi-mcp/*` 时把 `client_fp` 落 null, + 所以任何外部调用方只要自己带上这个 header,就能让自己不被匿名计数。 + **这是可接受的**,因为 `client_fp` 只用于计数、从不用于门禁——被伪造的代价是 + "匿名量被低估",不是"有人绕过了什么"。真要防,只能在 loopback 侧改用一个只有服务端知道的 + 内部 header,但那等于往一个 MIT 公开仓里塞一个共享秘密,收益(计数更准一点) + 远小于代价。**记在这里是为了让下一个人不必重推一遍这个取舍。** + +--- + +## §10 分阶段落地 checklist + +排序逻辑(蓝图 §10):**补洞先于加事件**(在错的地基上加事件只会得到更精确的错数); +**告警先于看板**(没人看的数据断了也没人知道);**类型闸先于事件膨胀**。 + +### Phase 0 — 与"补洞"同一批 PR,不可延后 + +**前置(阻塞项)** +- [ ] 确认 `prisma/migrations/` 的 baseline 可用,新 migration 能干净 apply(`0_init` 已存在;`docs/00-infrastructure.md:127` 的"无 migrations 目录"说法已过时,先修文档) +- [x] ~~先把 `cloudbuild.yaml` 修回与线上一致~~ **不再是阻塞项**:`origin/main` 上 PR #51 已经把它改成 `--update-env-vars` + `--update-secrets`,初稿说的"占位串、照跑会覆盖生产密钥"是在陈旧 checkout 上得出的错误结论(§0.1 注)。仍需注意的是**没有触发器在跑它**,遥测开关必须同时加进人工 `gcloud run deploy`,否则生产不生效(§2.3 第 2 条) +- [ ] `gcloud secrets create tako-analytics-salt` + 给 Cloud Run SA 授 `secretAccessor` + 部署加 `--set-secrets`。**Secret Manager API 与 SA 授权已在 2026-04-22 就绪,这里只是再加一个 secret**。未就绪则 `client_fp` 恒为 null,**不得退回明文 IP** +- [x] ~~查清线上 `run.googleapis.com/cpu-throttling` 与 `minScale`~~ **已实测(2026-08-21)**:`cpu-throttling` 注解**为空**(= 默认「仅在请求处理期间分配 CPU」),**无 `minScale`**(= min-instances 0),`maxScale=20`,`containerConcurrency=80`。结论:**请求级 CPU,fire-and-forget 确实可能跑不完,扣款确实可能在丢**;`trackEvent` 必须按 §2.4 的 R1 在响应返回前写出。**下一步是人工给 Cloud Run 打 `--no-cpu-throttling`**(见 §2.4) + +**补洞(§0.3)** +- [ ] H1:`Invocation` 加 `source String?` / `client String?` 列 + migration +- [ ] H1 + H7:`src/lib/mcp/tools.ts` 的 **`getJson()` 与 `invoke_agent` 两处 fetch** 都加 `X-Tako-Client: takoapi-mcp/`(`getJson()` 一处覆盖三个读工具) +- [ ] H7:服务端识别 `X-Tako-Client` 后,**loopback 请求的 `client_fp` 落 null**(不是算出来的 Cloud Run 出口 IP 指纹),并置 `is_mcp_loopback=true` +- [ ] H2:`src/app/v1/agents/[slug]/message/route.ts` 解析 A2A `result.status.state` → `meterInvocation({ taskState })` +- [ ] H3:新建 `src/lib/gateway.ts` 的 `withGateway()`,三条 `/v1/*` 路由改用它。**必须带 `resolveSlug` 与 `renderError` 两个回调**,否则 OpenAI shim 的错误契约会被做坏(§4.1) +- [ ] H4:`stream/route.ts` 的 `close`/`cancel`/error **以及 `!upstream.body`** 分支发 `gateway_stream_closed` +- [ ] H5:`src/lib/requestLog.ts` 的 `ip` → `ipHash`;旧 `ip` 列先置 NULL 再 drop(两步) +- [ ] H5:新建 `src/app/api/cron/retention/route.ts` + Cloud Scheduler(月度) +- [ ] H6:`src/app/mcp/route.ts` 的 `initialize` 保存 `params.clientInfo`(今天只取了 `protocolVersion`) +- [ ] 事件 1/9 的前置:**`MeterInput` 按 §4.1 的七行表整体加宽**(`agentSlug` / `route` / `pricingModel` / `upstreamHost` / `promptChars` / `source` / `client` / `keyLastUsedAt`),三条网关路由全部补传。只加 `keyLastUsedAt` 是不够的——事件 1 的多数维度都从这里来 +- [ ] 事件 4 的前置:`src/lib/health.ts` 的 `probeAgentHealth()` 改回 `{ health, latencyMs, httpStatus, errorCode }`;`runHealthChecks()` 的 `select` 加 `slug` 与 `healthStatus`(§4.3 A 组注) +- [ ] 事件 5/6 的前置:`src/lib/auth.ts` 新增 `events` 块(今天只有 `callbacks`) + +**打点骨架** +- [ ] `src/lib/analytics-schema.ts`:discriminated union + `EVENT_EMITTERS`(四个 emitter 值全保留) +- [ ] `src/lib/analytics.ts`:`trackEvent()` 同步 void 签名 + Cloud Logging sink + 32 KB 序列化上限 + `clientFp()` +- [ ] **日志 sink 的 `console.log` 写在响应返回之前**(§2.4 R1)——这是"Cloud Logging 当仓"整个选型的前提,不是风格问题 +- [ ] Sink 开关 env 化(§2.3 全表),本地默认 no-op +- [ ] A/B/C/E/F/G 组的 Phase-0 事件:1–11、20–24、27 + +**监控(与 sink 同天上线,不许拖)** +- [ ] logs-based metric `tako_event_count`(filter `jsonPayload.tako_event:*`) +- [ ] 告警 ①:有请求但 6 小时零事件(§8.1) +- [ ] 告警 ②:`tako_analytics_error` ≥ 1 / 5min(§8.2) +- [ ] 告警 ③:四个关键事件的"7 天有过、48 小时归零"(§8.4) +- [ ] 财务三件套对账 cron `src/app/api/cron/reconcile/route.ts` + mismatch 告警(§8.3) +- [ ] 对账 ④:日志 `gateway_call_completed` ↔ `Invocation` 行数(§2.4 R4)。**跑出的第一个丢失率数字要写回本文档 §0.2-5**——它是 R3 那个二选一决策的唯一依据 +- [ ] 账单预算 **$5**,三档,**`EXCLUDE_ALL_CREDITS`**(§6.5) +- [ ] `docs/monitoring/alert-*.yaml` 四份 YAML 进仓 + +**Phase 0 验收(每条都要真的做一遍)** +- [ ] 故意 `trackEvent("not_declared", {})` → **TS 编译红** +- [ ] 故意不给新 union arm 声明 emitter → **TS2739 编译红** +- [ ] 本地 `docker-compose up` 跑一次网关调用 → Postgres 零新增行、stdout 零 `tako_event`、接口行为不变 +- [ ] 把 `DATABASE_URL` 指向一个挂掉的库,跑一次网关调用 → **接口仍 200/502 正常返回**,且 `tako_analytics_error` 告警响。**同一次实验再看一件事**:连打 50 次调用,确认失败的批次**没有被重排进队列反复重试**(`db_insert_failed` 的条数应约等于批次数,而不是随时间线性增长)——重试放大器在这台 0.6 GB 的实例上是真实风险,不是理论风险(§2.1) +- [ ] 用一把余额为 0 的 key 调一个收费 agent → 有 `gateway_call_rejected{reason:"insufficient_credit"}` 与 `credit_exhausted`,且**没有** `Invocation` 行(确认 §0.2-3 的口径) +- [ ] 经 MCP `invoke_agent` 调一次 → `Invocation.source='mcp'`,且 `mcp_tool_invoked` 与 `gateway_call_completed` 各一条 +- [ ] **经 MCP `search_skills` 搜一次 → `mcp_tool_invoked` 一条 + `search_performed` 恰好一条**(不是两条),且那条 `search_performed` 的 `source='mcp'`、`is_mcp_loopback=true`、`client_fp` 为 null(H7 的验收;不做这一条,双计与指纹污染都会静默上线) +- [ ] 用 OpenAI SDK 打一次余额不足的 shim 调用 → 返回体仍是 `{error:{type:"insufficient_quota",code:"insufficient_quota"}}`(`withGateway()` 没把 OpenAI 契约做坏) +- [ ] `kill -TERM` 掉本地实例 → buffer 里的行落库(或在 5s 超时后放弃),进程正常退出(§2.1 的 drain) +- [ ] 网关调用返回 200 后**立刻** `kill -9` 本地实例,**重复 10 次**,数两边各命中几次。期望:`gateway_call_completed` 的日志行命中率**显著高于** `Invocation` 行的命中率。这不是 bug,是 §2.4 要度量的那个形态。**三条读法**:① 关键判据是**方向**(日志 ≫ DB),不是"日志必须 10/10"——stdout 在容器里是 pipe,异步写,强杀时尾部本来就会丢一点(§2.1、§8.3);② 若出现"DB 有、日志无",说明 R1 被违反了(日志沦为响应后执行),整个"日志当仓"的前提失效,立刻停下来查 `trackEvent` 的调用位置;③ 若两边命中率**一样低**,说明日志 sink 也被挪到了响应之后——同样是 R1 违反 + +### Phase 1 — 第一批看板需求出现时 + +- [ ] `ProductEvent` 热表 + CHECK 约束(§5.1)+ 白名单从 schema 推导 +- [ ] DB sink(有界 buffer、溢出丢最旧并报错)+ **flush 由流量驱动**:`after()` 触发,`setInterval` 只作第二道(§2.4 R2)。验收:停止发请求 60s → 不落库;下一个无关请求到来 → 补落库 +- [ ] **SIGTERM drain**:`flushProductEvents()` 照抄 `src/lib/requestLog.ts` 的 `flushRequestLogs()` 形状(`inflight` Set + `allSettled`),**新建**一个 `process.on("SIGTERM")` 把它和 `flushRequestLogs()` 一起挂进去,带 ≤5s 超时(§2.1)。**注意仓里今天一个 SIGTERM handler 都没有,`flushRequestLogs()` 也零调用点——这是"新建 + 顺手接线",不是"加进已有的那个"** +- [ ] **flush 失败丢本批、不重试、不回队**(§2.1)——db-f1-micro 无连接池,重试循环会去和计费事务抢连接 +- [ ] 保留期 cron 扩到 `ProductEvent`(400 天,上下界 30..730) +- [ ] 采样事件:12、13、14、15、18、19、25、26;`sample_rate` 入库 +- [ ] 徽章从 `RequestLog` 迁到 `badge_rendered`(`select` 加 `slug`,发规范 slug 而不是 URL 段,§6.4);`?ref=badge` **改 `src/components/BadgeSnippet.tsx` 与 `src/app/[locale]/badge/page.tsx` 两处**(§5.5) +- [ ] ESLint 两条 `no-restricted-syntax`(§3.3)+ **故意违规验证会红** +- [ ] `scripts/audit-analytics-events.mjs`(含敏感命名分词检查 + partition 断言) +- [ ] `.github/workflows/analytics-audit.yml`(**与脚本同一个 PR**)+ **故意制造违规验证 CI 变红** +- [ ] Postgres 视图 **两个**:`v_gateway_calls_canon`(北极星成功定义 + route 不可相加 + MCP loopback 去重,§7.1)与 `v_event_counts`(采样还原,§7.7)。**两个视图的头注释都要写明规则出处与红线**(蓝图 §6.3:让最省事的查询恰好就是正确的查询) +- [ ] **身份单位对照表进 runbook 并在每张看板的图例里标出单位**(§7.9)——三个候选单位混用是本项目最不会报红的一类错 +- [ ] 上游 agent SLO 看板 + 三档动作(§8.5) +- [ ] Postgres 容量护栏告警(§8.6) +- [ ] 用真实一周流量**回测并重设** §8.1 的地板值 + +### Phase 2 — 客户端入口 + 仓升级 + +- [ ] **consent gate 与客户端入口同一个 PR**(§9.4):区域 fail-closed + `Sec-GPC` + 服务端复查 +- [ ] GA4 去留决策(加 gate / 用自持替代后关掉),决定写进本文档 +- [ ] `POST /api/telemetry/event`:batch envelope(上限 30 = 客户端 flush 阈值 10 的 3 倍)+ + 从 `EVENT_EMITTERS` 推导的 allowlist + 限流 + **真的实现 properties 上限**(蓝图 §3.5 的教训) +- [ ] `src/lib/telemetry-client.ts` chokepoint(事件 16、17、19-web) +- [ ] 时钟 clamp `[now−1h, now+60s]`,窗口外**丢弃 ts 用到达时间**(§4.2) +- [ ] Cloud Logging → BigQuery log sink(dataset `takoapi_analytics`,730 天分区, + cluster `(tako_event, source, agent_slug)`),**用 `bq show` 确认分区过期而不是信文档** +- [ ] BigQuery `maximumBytesBilled` 1 GiB +- [ ] 双 sink 日对账(§8.3 的事件侧) +- [ ] per-event 量级回归(达到 §8.4 的启用条件后) +- [ ] GDPR `deletePerson` 双 sink 版 + `LedgerEntry` 假名化决定留痕(§9.3) +- [ ] publisher 看板 + 小样本抑制(§5.6) +- [ ] 告警 YAML drift check(§8.7) + +### 长期纪律(没有完成态) + +- 新事件三件套:union arm + emitter 声明 +(若含敏感 token)审计脚本的例外表书面理由。 +- **改 `tako_event` / `tako_analytics_error` 这两个字面量前,先搜 `docs/monitoring/*.yaml`** + ——它们被 logs-based metric 的 filter 匹配,改了 = 静默失明。 +- 声明任何事件为 `both` / `both_server_authoritative` 之前,先用数据验证 server 副本真的在生产 + 路径上落行(蓝图 §2.1 的 `ai_generate_started` 教训:声明后封掉 client 副本 = 直接杀死该事件, + 下游读零 16 天无人发现)。 +- 每次 `source` / 事件改名 = 一次全仓下游过滤搜索(蓝图案例 B 的三周静默漏计)。 +- **不许把日志 sink 的 `console.log` 挪到响应之后**(`after()` / `setTimeout` / `.then()` 尾巴)。 + 看起来是"让响应更快"的优化,实际是把权威"仓"交给一个在 Cloud Run 上不保证执行的时钟(§2.4 R1)。 + 同理:给网关加任何"响应后再做"的收尾逻辑之前,先问它丢了会不会有人发现。 +- 每次改 `TAKO_ANALYTICS_SAMPLE_*` 都是一次口径事件——历史行的 `sample_rate` 不变, + 但报表上要标注变更日期。 + +--- + +## 附录 A:蓝图中**不适用于 TakoAPI** 的条目及理由 + +这一节比照抄适用的部分更有价值。每条给:不适用什么、为什么、替代做法。 + +| 蓝图条目 | 判断 | 为什么不适用 | 替代做法 | +|---|---|---|---| +| **§1.3** 热表"不配置这个状态天然不存在" | **反转** | 蓝图说热表和业务库同库所以没有"不配置"态。TakoAPI 的热表**必须**能关——db-f1-micro 是真瓶颈,事故时要能一键停掉打点写入 | `TAKO_ANALYTICS_DB` 默认**关**,Phase 1 才在生产打开 | +| **§1.1** fire-and-forget 的**实现范式**:内存 buffer + 定时 flush + SIGTERM drain | **规则对,范式失效** | 那套范式默认进程常驻。Cloud Run 默认"CPU 仅在处理请求期间分配":响应 flush 后未 settle 的 promise 会被挂起或随实例回收丢弃,`setInterval` 在两次请求之间不 tick。**今天的 `void meterInvocation(...)`(含扣款)已经在这条约束下** | 日志 sink **请求路径内写出(响应返回之前)**;DB sink **流量驱动 flush**(Next 16 的 `after()`);drain 降级为尽力而为;新增"日志 ↔ `Invocation` 丢失率"对账,> 0.5% 触发 `await` 或 `--no-cpu-throttling` 二选一(§2.4) | +| **§2.2** 三端 chokepoint(web/mobile/server) | **裁剪为 1 端** | 仓库里没有任何 iOS/Android/RN/Expo 代码;web 侧 Phase 0 无事件 | server 一个 `src/lib/analytics.ts`;client Phase 2;mobile 永不 | +| **§2.3** ESLint 禁 `posthog.capture()` / `sendBeacon` | **规则形态全换** | 本项目没有 PostHog、没有 sendBeacon | 禁 `prisma.productEvent.*` 与裸 `console.log(JSON.stringify(...))`(§3.3) | +| **§2.5** 敏感命名**单测** | **裁剪:并进审计脚本** | 本仓**无测试框架**(`package.json` 无 test script)。为一条检查引入 vitest + 配置 + CI job,成本远高于收益 | 并进 `scripts/audit-analytics-events.mjs`,输出四列诊断信息补偿单测的可读性(§3.4) | +| **§3.1–3.6** 整节:单一客户端入口、batch envelope、legacy 单事件 sniff、时钟 clamp、防伪造 allowlist、properties 上限、anonId 入口校验 | **Phase 0 整节不适用** | 没有客户端事件就没有客户端入口。钱和北极星 100% 在服务端 | Phase 2 一次性建齐,且 clamp 窗口收紧到 `[now−1h, now+60s]`(无离线 outbox,24h 窗口只引入坏时钟风险) | +| **§3.3** 时钟 clamp 的 24h 窗口 + "离线批次整批重打送达日"的失真记账 | **不适用** | 移动端 outbox 才需要 24h;浏览器不离线攒事件 | 见上;且 TakoAPI 的对应失真是**激活漏斗的 cohort 成熟度**(§7.8),不是时钟 | +| **§3.4** 240 次/分/IP 的速率限制("余量给 carrier NAT") | **数字不适用** | 那是移动端 carrier NAT 场景。TakoAPI 的匿名读流量是 CI 机器与 LLM agent,共享出口 IP 的形态不同 | 复用现成的 `src/lib/ratelimit.ts`(`mcp` 命名空间 120/60s 已在跑),埋点不新增限流层 | +| **§4.1** 自持 anon ID(web HttpOnly cookie + mobile SecureStore) | **不适用** | 匿名主力流量是 curl / MCP JSON-RPC / GitHub camo——**不带 cookie、不执行 JS、不存状态**。`NEXT_LOCALE` 也只在主动切语言时才写 | 日轮换 HMAC 指纹 `client_fp`(§5.3)+ 自报 `X-Tako-Client`(§5.4)。**并接受它的四条语义边界**:跨日不可关联、NAT 合并、换 UA 分裂、不可做留存 | +| **§4.2** "`userId` 是唯一权威 ID,其余都是别名" | **改写** | 网关侧一个人多把 key,每把 key ≈ 一个集成/一台机器。用 `user_id` 算激活会把"第二把 key 的首调"算成老用户普通调用 | **主锚点是 `api_key_id`**;`user_id` 是租户/计费维度(§5.1) | +| **§4.3** AnonIdentityLink 桥(匿名 → 注册的同设备缝合) | **不适用** | 匿名读者(curl/camo/MCP)与注册用户之间**没有任何共享标识**——不是"桥没人过",是**桥的两端物理上不相连** | 注册来源改用 `account_registered.referrer_host` + 徽章 `?ref=badge`。"同 IP/UA 24h 内先读目录后注册"只是**弱关联**,可用于探索,**不可用于归因结论** | +| **§4.4** platform 归类(`X-App-OS` → UA → null) | **替换语义** | TakoAPI 没有 ios/android 维度 | 对应物是 `source`(direct/mcp/skill/plugin/openai_shim/web/cron)+ `client`。优先级同构:显式 header → route 推断 → `unknown` | +| **§4.5** identity_grade 的四步收紧计划 | **部分适用,语义换掉** | 蓝图的 asserted 是"自报 cuid"。TakoAPI 没有这种形态 | 三级换成 `key` / `session` / `anon`(§5.2),规则是"金钱与留存指标只认 `key`" | +| **§5.1** 仓 = BigQuery | **Phase 1 替换** | 成本红线 + 新增依赖/凭据。而 `logging: CLOUD_LOGGING_ONLY` 已在跑,stdout 免费进 Cloud Logging | Phase 1 用 Cloud Logging,Phase 2 挂 log sink → BigQuery,**代码零改动** | +| **§5.4** 仓插入的 `insertId` 幂等 + `skipInvalidRows`/`ignoreUnknownValues` | **Phase 1 不适用** | 那是 BigQuery streaming insert 的机制。`console.log` 没有这些概念 | `event_id` 字段照样生成(Phase 2 直接当 insertId 用);Phase 1 的去重靠 `event_id` 在查询侧 `DISTINCT` | +| **§6.2** 分端曝光/游玩口径、`billing_unit`(card vs screenful)、"两者相加是布局的函数" | **无直接对应物** | 没有信息流、没有游戏 | **思想适用,形态换成**:`message` vs `stream` 的"成功"是两种物理事件,不可比不可相加(§7.5);以及 MCP 转调的双计(§7.2) | +| **§7.3** 双 sink 日对账(15% 容差、量 < 50 跳过) | **降级 Phase 2,且被更重要的对账替代** | Cloud Logging sink 是 `console.log`,"活着但丢行"的形态几乎不存在,边际价值低 | **Phase 0 先做财务三件套对账**(Invocation ↔ DEBIT ↔ CreditBalance,§8.3)——那是钱,比事件一致性重要一个量级 | +| **§7.4** per-event 量级回归(14 天健康日中值、跌破 10% 报警) | **T0/T1 不适用** | 今天所有事件的中值都是 0,检测器输出恒为噪音 | 四个关键事件的"7 天有过、48 小时归零"绝对零告警(§8.4)。启用条件:某事件连续 14 天日均 ≥ 50 行 | +| **§7.1** "< 5 次/小时持续 3 小时"的地板值 | **数字不适用** | 那是 21k events/天量级下调的。TakoAPI 今天的正常状态就是接近零,比率型地板会天天误报→被静音→失效 | T0 用"有请求但 6 小时零事件";达到 T1 后回测重设(§8.1) | +| **§7.6** "先测集中度再谈采样,多数体量下答案是不采"(参照项目 实测最大单事件仅 10.7%) | **结论反转** | 参照项目 的仓是 BigQuery(写便宜)。TakoAPI 的热表是 0.6 GB 的 db-f1-micro,且有三类天生高频低价值的流量:`/api/registry` 默认 `limit=1000` 全量吐目录、徽章每渲染一次写一行、MCP 三个读工具全匿名 | **必须采样**,且 `sample_rate` 入库(§7.7)。采样率 env 化以便事故时调节 | +| **§7.6** BigQuery `maximumBytesBilled` + $50 预算 | **数字与工具都换** | Phase 1 无 BQ;量级差 3–4 个数量级 | 预算 **$5**(仍要 `EXCLUDE_ALL_CREDITS`——新 GCP 项目大概率有赠金,这个坑命中概率高)+ **Postgres 行数/磁盘护栏**(后者才是宕机风险,§8.6) | +| **§8.1** 区域化 consent gate + 客户端 gate + 撤回时清队列 | **Phase 0 不适用** | Phase 0 零客户端代码:不写 cookie、不写客户端存储、无第三方处理器、身份特征不入库、指纹日轮换 | Phase 2 与客户端入口**同一个 PR**落地(§9.4)。**GA4 的既存敞口单独挂账**,不是本方案引入的,本方案也不扩大它 | +| **§8.2** "mobile 刻意不设 consent gate + 四条重估触发器" | **对象换掉** | 没有 mobile | 对应物是 **§9.2 的"零结果查询词"例外通道**——同样是一条"显式、留痕、带重估触发条件"的数据处理例外 | +| **§8.3** GDPR streaming-buffer 日扫(DML 摸不到 90 分钟内的行) | **不适用** | 没有 streaming insert | Phase 1:Cloud Logging append-only **无法按行删** → 这是选它当仓的**已知代价**,缓解是"日志里除 `user_id` 外零身份特征" + 30 天自然过期。Phase 2 上 BQ 后补批量 DML(§9.3) | +| **§9** 三个事故案例(PostHog 断供 / 曝光按拉取计费 / CTR 到达滞后) | **案例本身不适用,机制适用** | 没有 PostHog、没有信息流、没有 banner | 机制的本项目形态:**双计数** → MCP 转调(§7.2);**同名不同物** → message vs stream(§7.5);**到达滞后** → 激活漏斗 cohort 成熟度(§7.8)。三个都已经存在于代码里,只是还没人数过 | +| **规模参照表** 的所有阈值 | **全部不可直接抄** | 参照项目 21k events/天,TakoAPI 接近 0,差 3–4 个数量级 | §0.5 的 T0/T1/T2 三档,每个阈值标注所属档位与升级触发条件 | + +**一条元规则**:上表里凡标"Phase 2"的,都不是"以后有空再说",而是**有明确前置条件的排期** +(客户端入口 → consent;BigQuery → log sink;per-event 回归 → 日均 50 行)。 +前置条件达成而没做,就是蓝图 §2.4 记录的那种病——**一条没人验证过的防线,比没有防线更危险, +因为它让人停止担心。** + +--- + +## 附录 B:本方案会新增/修改的文件清单 + +**新建** +- `src/lib/analytics-schema.ts` — 事件 union + `EVENT_EMITTERS` + 热表白名单推导 +- `src/lib/analytics.ts` — `trackEvent()` chokepoint + 两个 sink + `clientFp()` +- `src/lib/gateway.ts` — `withGateway()`,三条 `/v1/*` 的共用前置逻辑 + `gateway_call_rejected` +- `src/app/api/cron/retention/route.ts` — `ProductEvent` / `RequestLog` 保留期清理 +- `src/app/api/cron/reconcile/route.ts` — 财务三件套对账 + **日志↔`Invocation` 丢失率(④)** + 容量护栏 +- `scripts/audit-analytics-events.mjs` — 第四层审计(含敏感命名分词检查) +- `.github/workflows/analytics-audit.yml` — lint + audit +- `docs/monitoring/alert-*.yaml` — 四份告警策略(意图进仓) +- `prisma/migrations/0NN_analytics/` — `ProductEvent` 表(含 `@@index([ts])`)、**手写的 + `ProductEvent_identity_ck` CHECK 约束**(Prisma schema 表达不了,必须走 `migrate` 而非 `db push`,§6.3)、 + `Invocation.source/client`、`RequestLog.ipHash`、以及两个视图 + `v_gateway_calls_canon`(§7.1)与 `v_event_counts`(§7.7) +- **SIGTERM handler**(放 `src/lib/analytics.ts` 或一个 `src/lib/shutdown.ts`)——仓里现在 + **一个都没有**,`flushRequestLogs()` 至今零调用点(§2.1) +- `src/lib/telemetry-client.ts` + `src/app/api/telemetry/event/route.ts` —(Phase 2) + +**修改** +- `src/lib/billing.ts` — `MeterInput` 加 `keyLastUsedAt`;`meterInvocation()` 发事件 1、9、22;`checkCreditPreflight()` 发事件 23 +- `src/lib/requestLog.ts` — `ip` → `ipHash`;`flushRequestLogs()` 与新的 `flushProductEvents()` 一起挂 SIGTERM +- `src/lib/apikey.ts` — **不改逻辑,只加一条注释钉死现状**:`record` 来自 `findUnique`、那句 `lastUsedAt` 的 update 是 fire-and-forget 且不回写,所以 `record.lastUsedAt` 就是"更新前的旧值",事件 9 依赖它——**谁把它改成 update 的返回值,谁就悄悄杀死了激活漏斗**(§4.1) +- `src/lib/health.ts` — `probeAgentHealth()` 返回 `{health, latencyMs, httpStatus, errorCode}`;`runHealthChecks()` 的 select 加 `slug` / `healthStatus`;发事件 4 +- `src/lib/mcp/tools.ts` — **`getJson()` 与 `invoke_agent` 两处** fetch 注入 `X-Tako-Client`(H1 + H7) +- `src/app/mcp/route.ts` — `initialize` 保存 `clientInfo`;`tools/call` 发事件 11 +- `src/app/v1/agents/[slug]/message/route.ts` — 用 `withGateway()`;解析 `taskState` +- `src/app/v1/agents/[slug]/stream/route.ts` — 用 `withGateway()`;流终局发事件 3 +- `src/app/v1/chat/completions/route.ts` — 用 `withGateway()` +- `src/app/api/badge/[slug]/route.ts` — `logRequest` → `trackEvent("badge_rendered")`;`findFirst` 的 `select` 加 `slug`(URL 段可能是 cuid,§6.4) +- `src/components/BadgeSnippet.tsx` + `src/app/[locale]/badge/page.tsx` — 徽章外层链接加 `?ref=badge`(**两处都要,全仓只有这两处生成片段**,§5.5);Phase 2 时 `BadgeSnippet.copy()` 发事件 17 +- `src/components/ui/InstallTabs.tsx` —(Phase 2)`CopyButton` 发事件 16,`target` 含 `uninstall` +- `src/app/[locale]/skills/[slug]/page.tsx` —(Phase 2)复制按钮发事件 16(`surface='skill_detail'`) +- `src/app/[locale]/agents/[slug]/page.tsx` — server component 里发事件 14 +- `src/app/api/skills/[id]/route.ts` — 发事件 13 +- `src/lib/auth.ts` — **新增 `events` 块**(今天只有 `callbacks`),`events.createUser` / `events.signIn` 发事件 5、6 +- `src/app/api/auth/register/route.ts` — credentials 注册不走 adapter,`events.createUser` 不会为它触发,这里补发事件 5 +- `src/app/api/keys/route.ts` / `[id]/route.ts` — 事件 7、8 +- `src/app/api/billing/topup/**` — 事件 20、21 +- `prisma/schema.prisma` — `ProductEvent`、`Invocation.source/client`、`RequestLog.ipHash` +- `eslint.config.mjs` — 两条 `no-restricted-syntax` +- `cloudbuild.yaml` — 加遥测开关;`TAKO_ANALYTICS_SALT` 走 `--update-secrets`(文件已是安全版本,无需先"修漂移"——那条断言已被推翻)。**同一份开关必须同时加进人工 `gcloud run deploy`**,因为没有触发器在跑这个文件 +- `docs/00-infrastructure.md` — 修掉第 127 行"无 `prisma/migrations/` 目录"的过时说法(实际有 11 个目录含 `0_init`) +- `AGENTS.md` / `CLAUDE.md` — 加长期纪律条目(改 logs-based metric 匹配字符串前先搜 YAML) diff --git a/messages/ar.json b/messages/ar.json index 85a33c10d..85595f86e 100644 --- a/messages/ar.json +++ b/messages/ar.json @@ -7,60 +7,78 @@ "label": "اللغة" }, "Header": { - "searchPlaceholder": "ابحث عن الوكلاء...", "agents": "الوكلاء", "skills": "المهارات", - "trending": "الرائج", - "publish": "نشر", "publishAgent": "نشر وكيل", "account": "الحساب", - "mySkills": "مهاراتي", - "myBookmarks": "المحفوظات", - "apiUsage": "الواجهة البرمجية والاستخدام", + "myBookmarks": "الوكلاء المحفوظون", + "apiUsage": "مفاتيح API والاستخدام", "adminPanel": "لوحة الإدارة", "signIn": "تسجيل الدخول", - "signOut": "تسجيل الخروج" + "signOut": "تسجيل الخروج", + "mainNav": "التنقّل الرئيسي", + "skipToContent": "تخطَّ إلى المحتوى", + "openMenu": "فتح القائمة", + "closeMenu": "إغلاق القائمة", + "projects": "المشاريع", + "install": "التثبيت", + "getApiKey": "احصل على مفتاح API", + "search": "بحث", + "searchScope": "ابحث في", + "scopeAgents": "الوكلاء", + "scopeProjects": "المشاريع", + "scopeSkills": "المهارات", + "searchAgentsPlaceholder": "ابحث عن الوكلاء حسب ما يفعلونه…", + "searchProjectsPlaceholder": "ابحث في مشاريع الوكلاء مفتوحة المصدر…", + "searchSkillsPlaceholder": "ابحث عن مهارات لوكيل البرمجة لديك…", + "myListings": "إدراجاتي", + "docs": "التوثيق" }, "Footer": { - "tagline": "واجهة برمجية واحدة للوصول إلى جميع الوكلاء — إضافةً إلى آلاف مهارات OpenClaw لوكيل البرمجة لديك.", + "tagline": "واجهة برمجية واحدة للوصول إلى جميع الوكلاء — اكتشف أي وكيل واستدعِه واحسب استهلاكه عبر نقطة نهاية واحدة.", "agentRegistry": "سجلّ الوكلاء:", "platform": "المنصة", "resources": "الموارد", - "installInAgent": "ثبّته في وكيلك", + "installInAgent": "ثبّته في وكيل البرمجة لديك", "browseAgents": "تصفّح الوكلاء", "publishAnAgent": "نشر وكيل", - "developerDashboard": "لوحة المطوّرين", + "developerDashboard": "مفاتيح API والاستخدام", "browseSkills": "تصفّح المهارات", - "awesomeList": "قائمة Awesome", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. صُمِّم لمجتمع OpenClaw.", - "blog": "المدونة" + "copyright": "© {year} TakoAPI. مرخَّص بموجب MIT.", + "blog": "المدونة", + "badge": "شارة README", + "scenarios": "تصفّح حسب السيناريو", + "a2aSpec": "بروتوكول A2A", + "github": "GitHub", + "docs": "توثيق واجهة API" }, "Home": { "heroTitle": "واجهة برمجية واحدة للوصول إلى جميع الوكلاء", - "heroSubtitle": "اكتشف واستدعِ {agents, number} من وكلاء الذكاء الاصطناعي عبر واجهة برمجية موحَّدة — إضافةً إلى {skills, number} مهارة لوكيل البرمجة لديك.", + "heroSubtitle": "اكتشف وكلاء الذكاء الاصطناعي، ثم استدعِ أيًّا منهم عبر نقطة نهاية واحدة وبمفتاح واحد. يدعم A2A أصلًا، ومتوافق مع OpenAI، ومحسوب لكل استدعاء.", "browseAgents": "تصفّح الوكلاء", - "publishAnAgent": "نشر وكيل", - "agentRegistry": "سجلّ الوكلاء:", - "browseByScenario": "تصفّح حسب السيناريو", - "allAgents": "جميع الوكلاء", + "browseByScenario": "تصفّح حسب المهمة التي تريد إنجازها", "featuredAgents": "الوكلاء المميَّزون", "browseAll": "تصفّح الكل", "popularProjects": "مشاريع مفتوحة المصدر شائعة", "selfHostable": "{count, number} قابل للاستضافة الذاتية", - "skillTitle": "مهارة TakoAPI لـ OpenClaw", - "official": "رسمي", - "skillDescription": "ابحث عن مهارات OpenClaw وثبّتها وأدِرها عبر محادثة طبيعية — مدعومة بسوق TakoAPI.", - "viewOnGithub": "عرض على GitHub", - "askAgent": "أو اطلب من وكيلك: «ثبّت مهارة TakoAPI»", - "usingCodingAgent": "أتستخدم Claude Code أو Codex أو OpenCode؟", - "oneCommandInstall": "تثبيت بأمر واحد", - "mustHaveSkills": "مهارات لا غنى عنها", - "viewRankings": "عرض التصنيفات", - "categories": "الفئات", - "viewAll": "عرض الكل", - "moreCategories": "{count, plural, zero {} one {فئة واحدة إضافية+} two {فئتان إضافيتان+} few {+# فئات إضافية} many {+# فئة إضافية} other {+# فئة إضافية}}", - "latestSkills": "أحدث المهارات" + "categories": "الفئات:", + "moreCategories": "{count, plural, zero {+# فئة إضافية} one {+فئة واحدة إضافية} two {+فئتان إضافيتان} few {+# فئات إضافية} many {+# فئة إضافية} other {+# فئة إضافية}}", + "getApiKey": "احصل على مفتاح API", + "catalogSummary": "{agents, number} وكيل قابل للاستدعاء · {projects, number} مشروع مفتوح المصدر · {skills, number} مهارة لوكلاء البرمجة", + "quickstartLabel": "أول استدعاء خلال 30 ثانية", + "quickstartNote": "أتفضّل العمل من الطرفية؟ ثبّت TakoAPI في وكيل البرمجة لديك.", + "allScenarios": "جميع السيناريوهات", + "callableNow": "{count, number} قابل للاستدعاء عبر البوابة", + "installTitle": "استخدم TakoAPI من وكيل البرمجة لديك", + "installDescription": "أمر واحد يعلّم Claude Code أو Codex أو OpenCode البحث في الدليل واستدعاء الوكلاء نيابةً عنك — أو سجّل خادم MCP المُستضاف بدلًا من ذلك.", + "installCta": "دليل التثبيت", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "مهارات لوكيل البرمجة لديك", + "skillsSubtitle": "{count, number} مهارة OpenClaw، الأكثر تثبيتًا والأحدث.", + "quickstartTabDiscover": "١. الاكتشاف", + "quickstartTabCall": "٢. الاستدعاء", + "quickstartTabFromAgent": "٣. من وكيلك" }, "Scenarios": { "frontend": "الواجهة الأمامية", @@ -83,10 +101,8 @@ }, "AgentDetail": { "notFound": "لم يُعثَر على الوكيل", - "backToMarketplace": "← سوق الوكلاء", + "backToMarketplace": "→ سوق الوكلاء", "openSourceProject": "مشروع مفتوح المصدر", - "signedCard": "بطاقة موقَّعة", - "verifiedPublisher": "ناشر موثَّق", "by": "بواسطة {name}", "publisherOpenSource": "مفتوح المصدر", "publisherUnknown": "غير معروف", @@ -98,20 +114,44 @@ "skills": "المهارات", "skillsCount": "({count, number})", "noSkillsProject": "مشروع مفتوح المصدر — استكشف الشيفرة واستضِفه ذاتيًا من GitHub.", - "noSkillsHosted": "لم يُعلن هذا الوكيل عن مهارات منظَّمة في بطاقة AgentCard الخاصة به.", + "noSkillsHosted": "لم يُعلن هذا الوكيل عن مهارات منظَّمة في AgentCard الخاصة به.", "inputMode": "الإدخال: {mode}", "outputMode": "الإخراج: {mode}", - "openSourceProjectHeading": "مشروع مفتوح المصدر", "openSourceProjectNote": "استضِفه ذاتيًا أو استكشف الشيفرة. غير قابل للاستدعاء عبر بوابة TakoAPI.", "viewOnGithub": "عرض على GitHub", - "integration": "التكامل", "endpoint": "نقطة النهاية", "agentCard": "AgentCard", "homepage": "الصفحة الرئيسية", "callThroughTakoApi": "استدعِه عبر TakoAPI", - "gatewayPhase2": "بوابة موحَّدة — سيتوفّر الاستدعاء في المرحلة الثانية.", "breadcrumbHome": "الرئيسية", - "breadcrumbMarketplace": "سوق الوكلاء" + "breadcrumbMarketplace": "سوق الوكلاء", + "gatewayHint": "استدعِه بمفتاح API الخاص بك — أنشئ مفتاحًا من لوحة التحكم.", + "healthOk": "يستجيب", + "healthDegraded": "أداء متدهور", + "healthDown": "لا يستجيب", + "healthCheckedAt": "آخر فحص {time}", + "atAGlance": "لمحة سريعة", + "protocols": "البروتوكولات", + "protocolsNone": "غير مُعلَنة", + "yes": "نعم", + "no": "لا", + "pricing": "التسعير", + "health": "حالة التشغيل", + "healthUnknown": "لم يُفحَص بعد", + "totalCalls": "إجمالي الاستدعاءات", + "numberValue": "{count, number}", + "averageRating": "متوسط التقييم", + "ratingValue": "{rating} / 5 · {count, number} تقييم", + "notRated": "لم يُقيَّم بعد", + "callExamplesLabel": "أمثلة الاستدعاء", + "repository": "المستودع", + "language": "اللغة", + "license": "الترخيص", + "licenseNone": "غير محدَّد", + "lastPush": "آخر دفع", + "topics": "المواضيع", + "readmeHeading": "من ملف README", + "readmeNote": "مقتطف من ملف README الخاص بالمشروع على GitHub." }, "Agents": { "metaTitle": "سوق الوكلاء", @@ -140,16 +180,29 @@ "pricingPerCall": "لكل استدعاء", "pricingPerTask": "لكل مهمة", "pricingPerToken": "لكل ألف رمز", - "count": "{count, plural, zero {لا وكلاء} one {وكيل واحد} two {وكيلان} few {# وكلاء} many {# وكيلًا} other {# وكيل}}", + "count": "{count, plural, =0 {لا وكلاء} one {وكيل واحد} two {وكيلان} few {# وكلاء} many {# وكيلًا} other {# وكيل}}", "emptyTitle": "لا يوجد وكلاء يطابقون عوامل التصفية بعد.", "emptyCta": "انشر أول وكيل ←", - "prev": "← السابق", - "next": "التالي →", - "pageOf": "الصفحة {page} من {total}" + "prev": "→ السابق", + "next": "التالي ←", + "pageOf": "الصفحة {page} من {total}", + "filtersTitle": "عوامل التصفية", + "filtersWithCount": "عوامل التصفية ({count})", + "activeFiltersLabel": "عوامل التصفية النشطة:", + "removeFilter": "إزالة عامل التصفية: {label}", + "clearAll": "مسح الكل", + "kindLabel": "النوع", + "scenarioGroup": "السيناريو", + "protocolGroup": "البروتوكول", + "pricingGroup": "التسعير", + "categoryGroup": "الفئة", + "searchChip": "بحث: {query}", + "resultsLabel": "النتائج", + "paginationLabel": "ترقيم الصفحات", + "goToPage": "الانتقال إلى الصفحة {page}", + "showingRange": "عرض {from}–{to} من {total}" }, "Components": { - "homeSearchPlaceholder": "ابحث عن المهارات بالاسم أو الوصف أو المؤلف...", - "homeSearchButton": "بحث", "agentFree": "مجاني", "agentPerCall": "{price}/استدعاء", "agentPerTask": "{price}/مهمة", @@ -163,7 +216,11 @@ "agentSelfHost": "استضافة ذاتية", "agentSkills": "{count, plural, zero {لا مهارات} one {مهارة واحدة} two {مهارتان} few {# مهارات} many {# مهارة} other {# مهارة}}", "skillBy": "بواسطة {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} استدعاء", + "healthOk": "يستجيب", + "healthDegraded": "أداء متدهور", + "healthDown": "لا يستجيب" }, "Install": { "metaTitle": "التثبيت في Claude Code وCodex وOpenCode", @@ -181,7 +238,7 @@ "whatTeachesAgent": "يعلّم وكيلك استدعاء سجلّ TakoAPI والبوابة عبر HTTP — دون تبعيات إضافية ودون خادم لتشغيله.", "whatMcpServer": "أتفضّل الأدوات المُستضافة؟ سجّل خادم MCP الخاص بـ TakoAPI بدلًا من ذلك — claude mcp add --transport http takoapi https://takoapi.com/mcp — ليكشف عن search_agents وget_agent وsearch_skills وinvoke_agent كأدوات MCP أصيلة.", "whatIdempotent": "متماثل النتيجة (آمن لإعادة التشغيل) وقابل للتراجع بالكامل عبر --uninstall.", - "whatApiKey": "للبوابة المحسوبة بالاستخدام، أنشئ مفتاح واجهة برمجية في لوحة التحكم الخاصة بك وصدّره كـ TAKO_KEY.", + "whatApiKey": "للبوابة المحسوبة بالاستخدام، أنشئ مفتاح API في لوحة التحكم الخاصة بك وصدّره في TAKO_KEY.", "footnote": "أتعمل على Windows أو ضمن Node بالفعل؟ استخدم npx takoapi-install. أتفضّل قراءة النص أولًا؟ إنه نص shell بسيط على takoapi.com/install.sh." }, "InstallTabs": { @@ -195,8 +252,8 @@ "writesHeading": "يكتب", "howToUseHeading": "كيفية الاستخدام", "claudeCodeUse": "يُحمَّل تلقائيًا — ما عليك سوى أن تطلب من Claude العثور على وكيل على TakoAPI.", - "codexUse": "استدعِه عبر ‎$takoapi، أو اختره من قائمة /skills.", - "opencodeUse": "شغّل ‎/takoapi ، أو سلّمه إلى ‎@takoapi.", + "codexUse": "استدعِه عبر ‎$takoapi، أو اختره من قائمة ‎/skills.", + "opencodeUse": "شغّل ‎/takoapi ''، أو سلّمه إلى ‎@takoapi.", "mcpHeading": "خادم MCP — أدوات مُستضافة، لا شيء لتثبيته", "claudeCodeMcpHint": "أمر واحد. أضِف ‎--header \"Authorization: Bearer $TAKO_KEY\"‎ لتفعيل invoke_agent.", "codexMcpHint": "أضِفه إلى ‎~/.codex/config.toml", @@ -226,12 +283,17 @@ "requirements": "المتطلبات", "installation": "التثبيت", "clawHubCli": "واجهة ClawHub السطرية", - "openClawChat": "محادثة OpenClaw (الصق هذا في محادثتك)", "links": "الروابط", "github": "GitHub", - "copyGithubUrl": "نسخ رابط GitHub", "clawHub": "ClawHub", - "skillNotFound": "لم يُعثَر على المهارة" + "codingAgent": "وكيل البرمجة", + "agentPrompt": "ثبّت المهارة من {url}", + "installHintRegistry": "شغّل أمر واجهة سطر الأوامر، أو الصق النص في Claude Code أو Cursor أو Codex أو أي وكيل برمجة تستخدمه.", + "installHintRepo": "هذه المهارة غير منشورة في سجلّ ClawHub. الصق النص في وكيل البرمجة لديك — Claude Code أو Cursor أو Codex — وسيثبّت المهارة مباشرةً من المستودع المصدر.", + "installUnavailable": "لم يُنشر بعد أي مصدر تثبيت لهذه المهارة.", + "openRepository": "افتح المستودع", + "signInToLike": "سجّل الدخول للإعجاب بهذه المهارة", + "downloadsSource": "التنزيلات وفق ما يذكره clawskills.sh" }, "Skills": { "metaTitle": "تصفّح المهارات", @@ -240,7 +302,7 @@ "ogDescription": "آلاف مهارات OpenClaw لوكيل البرمجة لديك.", "heading": "تصفّح المهارات", "searchLabel": "بحث: «{query}»", - "count": "{count, number} مهارة عُثر عليها", + "count": "عُثر على {count, number} مهارة", "sortLatest": "الأحدث", "sortPopular": "الأكثر شيوعًا", "sortViews": "الأكثر مشاهدة", @@ -257,13 +319,32 @@ "all": "الكل", "emptyTitle": "لم يُعثَر على مهارات", "emptyHint": "حاول تعديل بحثك أو عوامل التصفية", - "pageOf": "الصفحة {page} من {total}" + "pageOf": "الصفحة {page} من {total}", + "metaTitleSearch": "المهارات المطابقة لـ «{query}»", + "metaTitlePaged": "تصفّح المهارات — الصفحة {page}", + "searchInputLabel": "البحث في المهارات", + "searchPlaceholder": "ابحث عن المهارات بالاسم أو الوصف أو المؤلف", + "searchButton": "بحث", + "agentFilter": "الوكيل", + "agentZed": "Zed", + "sourceFilter": "المصدر", + "sourceGithub": "GitHub", + "sourceCommunity": "المجتمع", + "sourceCurated": "منتقاة", + "categoryFilter": "الفئة", + "sortLabel": "الترتيب", + "sortRelevance": "الأكثر صلة", + "clearFilters": "مسح عوامل التصفية", + "paginationLabel": "صفحات قائمة المهارات", + "goToPage": "الانتقال إلى الصفحة {page}", + "prev": "السابق", + "next": "التالي" }, "Trending": { "metaTitle": "المهارات الرائجة", - "metaDescription": "أكثر مهارات OpenClaw تنزيلًا وإعجابًا الآن — لوحة تصدُّر حيّة لما تثبّته وكلاء البرمجة.", + "metaDescription": "أكثر مهارات OpenClaw تنزيلًا وإعجابًا — لوحة تصدُّر مبنية على أعداد التنزيل المنشورة من clawskills.sh، تُحدَّث يوميًا.", "ogTitle": "المهارات الرائجة — {siteName}", - "ogDescription": "لوحة تصدُّر حيّة لأكثر مهارات OpenClaw شيوعًا.", + "ogDescription": "لوحة تصدُّر تُحدَّث يوميًا لأكثر مهارات OpenClaw شيوعًا.", "title": "المهارات الرائجة", "subtitle": "اكتشف أكثر مهارات OpenClaw شيوعًا مرتَّبة حسب المجتمع", "searchPlaceholder": "ابحث في المهارات الرائجة…", @@ -274,14 +355,20 @@ "likes": "الإعجابات", "views": "المشاهدات", "skill": "المهارة", - "count": "{count, plural, zero {لا مهارات} one {مهارة واحدة} two {مهارتان} few {# مهارات} many {# مهارة} other {# مهارة}}", - "countInCategory": "{count, plural, zero {لا مهارات} one {مهارة واحدة} two {مهارتان} few {# مهارات} many {# مهارة} other {# مهارة}} في {category}", + "count": "{count, plural, =0 {لا مهارات} one {مهارة واحدة} two {مهارتان} few {# مهارات} many {# مهارة} other {# مهارة}}", + "countInCategory": "{count, plural, =0 {لا مهارات} one {مهارة واحدة} two {مهارتان} few {# مهارات} many {# مهارة} other {# مهارة}} في {category}", "emptyTitle": "لم يُعثَر على مهارات", "emptySubtitle": "حاول تعديل بحثك أو عوامل التصفية", "byAuthor": "بواسطة {author}", "showing": "عرض {from, number}–{to, number} من {total, number}", "prev": "السابق", - "next": "التالي" + "next": "التالي", + "provenanceNote": "تأتي أعداد التنزيلات والنجوم من الكتالوج المصدر — clawskills.sh وGitHub — وتُحدَّث ليليًا؛ وهي لا تقيس عمليات التثبيت عبر TakoAPI. أما الإعجابات والمشاهدات فتُحتسب هنا.", + "search": "بحث", + "sortLabel": "الترتيب حسب", + "rank": "المرتبة", + "goToPage": "الانتقال إلى الصفحة {page}", + "paginationLabel": "صفحات لوحة التصدُّر" }, "Admin": { "accessDenied": "تم رفض الوصول", @@ -319,7 +406,7 @@ "confirm": "تأكيد", "preview": "معاينة", "agentsTitle": "الوكلاء", - "agentsSubtitle": "راجِع الوكلاء المُرسَلين وأدِر إشرافهم.", + "agentsSubtitle": "راجِع الوكلاء المُرسَلين وأشرِف عليهم.", "filterPending": "قيد الانتظار", "filterApproved": "مُعتمَد", "filterRejected": "مرفوض", @@ -335,8 +422,8 @@ "actionReject": "رفض", "feature": "تمييز", "unfeature": "إلغاء التمييز", - "scenariosEditorHint": "场景 السيناريوهات — 点击切换 / انقر للتبديل والتعيين", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "السيناريوهات — انقر للتعيين أو الإزالة", + "scenarioOption": "{name}", "selectedCount": "{count, number} محدَّد", "rejectReasonPrompt": "سبب الرفض (اختياري):", "deleteAgentConfirm": "أتريد حذف هذا الوكيل نهائيًا؟", @@ -392,7 +479,7 @@ "somethingWentWrong": "حدث خطأ ما", "update": "تحديث", "create": "إنشاء", - "colSlug": "المُعرّف اللطيف", + "colSlug": "Slug", "colSkills": "المهارات", "colDescription": "الوصف", "noCategoriesFound": "لم يُعثَر على فئات. أضِف واحدة أعلاه.", @@ -436,7 +523,46 @@ "targetSkill": "مهارة", "targetAgent": "وكيل", "targetCategory": "فئة", - "targetUser": "مستخدم" + "targetUser": "مستخدم", + "adminNavLabel": "أقسام الإدارة", + "retry": "إعادة المحاولة", + "close": "إغلاق", + "prevPage": "الصفحة السابقة", + "nextPage": "الصفحة التالية", + "sectionAgents": "الوكلاء", + "sectionSkills": "المهارات والمجتمع", + "statTotalAgents": "إجمالي الوكلاء", + "statPendingAgents": "الوكلاء قيد الانتظار", + "statHostedAgents": "مُستضاف", + "statProjectAgents": "المشاريع", + "pendingAgentsTitle": "وكلاء بانتظار المراجعة", + "noPendingAgents": "لا يوجد وكلاء بانتظار المراجعة.", + "kindHosted": "مُستضاف", + "kindProject": "مشروع", + "filterByStatus": "التصفية حسب الحالة", + "filterDisabled": "مُعطَّل", + "actionDisable": "تعطيل", + "actionEnable": "إعادة التفعيل", + "disableAgentTitle": "تعطيل الوكيل", + "disableAgentConfirm": "سيُخفى {name} من الكتالوج العام حتى تعيد تفعيله. ولن يُحذف شيء.", + "deleteAgentTitle": "حذف الوكيل", + "deleteAgentConfirmNamed": "هل أنت متأكد من رغبتك في حذف {name}؟ لا يمكن التراجع عن هذا الإجراء.", + "rejectAgentTitle": "رفض الوكيل", + "rejectReasonLabel": "السبب (اختياري، يُعرَض للناشر)", + "rejectReasonPlaceholder": "مثال: نقطة النهاية تُعيد 500 في المهمة النموذجية", + "healthOk": "سليم", + "healthDegraded": "متدهور", + "healthDown": "متوقّف", + "healthUnknown": "لم يُفحَص", + "healthCheckedAt": "آخر فحص للحالة: {date}", + "healthNeverChecked": "لا يوجد فحص حالة مسجَّل", + "batchDeleteTitle": "حذف المهارات", + "selectAllRows": "تحديد كل الصفوف", + "selectRow": "تحديد {name}", + "editSkillNamed": "تعديل المهارة {name}", + "deleteSkillNamed": "حذف المهارة {name}", + "editCategoryNamed": "تعديل الفئة {name}", + "deleteCategoryNamed": "حذف الفئة {name}" }, "Auth": { "signInTitle": "تسجيل الدخول إلى TakoAPI", @@ -462,14 +588,28 @@ "noAccount": "أليس لديك حساب؟", "signUpLink": "إنشاء حساب", "haveAccount": "ألديك حساب بالفعل؟", - "signInLink": "تسجيل الدخول" + "signInLink": "تسجيل الدخول", + "forgotPassword": "هل نسيت كلمة المرور؟", + "forgotTitle": "إعادة تعيين كلمة المرور", + "forgotSubtitle": "سنرسل إليك بالبريد رابطًا لاختيار كلمة مرور جديدة", + "sendResetLink": "إرسال رابط إعادة التعيين", + "sendingResetLink": "جارٍ الإرسال...", + "resetLinkSent": "إن كان لهذا العنوان حساب، فقد أرسلنا إليه رابط إعادة تعيين كلمة المرور. تفقّد بريدك الوارد.", + "backToSignIn": "العودة إلى تسجيل الدخول", + "resetTitle": "اختر كلمة مرور جديدة", + "resetSubtitle": "أدخِل كلمة مرور جديدة لحسابك", + "newPasswordHint": "كلمة المرور الجديدة (8 أحرف على الأقل)", + "updatePassword": "تحديث كلمة المرور", + "updatingPassword": "جارٍ التحديث...", + "passwordUpdated": "تم تحديث كلمة مرورك. جارٍ نقلك إلى تسجيل الدخول...", + "resetLinkInvalid": "رابط إعادة تعيين كلمة المرور هذا غير صالح أو انتهت صلاحيته.", + "requestNewLink": "اطلب رابطًا جديدًا" }, "Dashboard": { - "title": "لوحة المطوّرين", - "signInPrompt": "سجّل الدخول لإدارة مفاتيح الواجهة البرمجية وعرض الاستخدام.", - "signIn": "تسجيل الدخول", - "description": "أدِر مفاتيح الواجهة البرمجية وراقِب استخدام البوابة. مفتاح واحد، فاتورة واحدة، أي وكيل.", - "apiKeys": "مفاتيح الواجهة البرمجية", + "title": "لوحة تحكم المطوّرين", + "signInPrompt": "سجّل الدخول لإدارة مفاتيح API وعرض الاستخدام.", + "description": "أدِر مفاتيح API وراقِب استخدام البوابة. مفتاح واحد، فاتورة واحدة، أي وكيل.", + "apiKeys": "مفاتيح API", "createKey": "إنشاء مفتاح", "creating": "جارٍ الإنشاء…", "copyKeyWarning": "انسخ هذا المفتاح الآن — لن يُعرَض مرة أخرى.", @@ -495,9 +635,8 @@ "tableCost": "التكلفة", "noCalls": "لا استدعاءات بعد. أنشئ مفتاحًا واستدعِ وكيلًا لرؤية الاستخدام هنا.", "billing": "الرصيد والفوترة", - "creditBalance": "رصيد الاعتماد", + "creditBalance": "الرصيد المتاح", "totalSpend": "إجمالي الإنفاق", - "topUpComingSoon": "تتم إضافة الرصيد من قِبل المنصّة حاليًا؛ وسيتوفّر الشحن الذاتي عبر PayPal قريبًا.", "topUpLabel": "إضافة رصيد", "topUpButton": "اشحن عبر PayPal", "topUpRedirecting": "جارٍ التحويل…", @@ -510,38 +649,68 @@ "ledgerNote": "ملاحظة", "ledgerTopUp": "شحن", "ledgerTopUpFee": "رسوم الشحن", - "ledgerDebit": "رسوم الاستدعاء", + "ledgerDebit": "رسوم استدعاء", "ledgerPayout": "تسوية", "ledgerRefund": "استرداد", - "ledgerAdjust": "تعديل" + "ledgerAdjust": "تعديل", + "onboardingTitle": "شغّل أول استدعاء لك", + "onboardingSubtitle": "ثلاث خطوات تفصلك عن وكيل يردّ عليك.", + "stepKeyTitle": "أنشئ مفتاح API", + "stepKeyBody": "مفتاح واحد يستدعي كل وكيل على TakoAPI. ويُعرَض السرّ مرة واحدة فقط، فور إنشائه.", + "stepCallTitle": "نفّذ أول استدعاء", + "stepCallBody": "انسخ أحد الأمثلة أدناه، واضبط TAKO_KEY على مفتاحك، ثم شغّله. سيظهر الاستدعاء ضمن «الاستخدام» خلال ثوانٍ.", + "stepInstallTitle": "ثبّته في وكيل البرمجة لديك", + "stepInstallBody": "علّم Claude Code أو Codex أو OpenCode اكتشاف الوكلاء واستدعاءهم عبر TakoAPI.", + "stepInstallLink": "افتح دليل التثبيت", + "stepOptional": "اختياري", + "stepDone": "تم", + "stepTodo": "لم يتم بعد", + "keyNameLabel": "اسم المفتاح", + "keyNamePlaceholder": "مفتاح البوابة", + "createKeyError": "تعذّر إنشاء المفتاح. حاول مرة أخرى.", + "revokeNamed": "إبطال {name}", + "quickstartHint": "اضبط TAKO_KEY على أحد المفاتيح أعلاه، ثم استدعِ أي وكيل عبر slug الخاص به.", + "creditRule": "الوكلاء المجانيون لا يكلّفون شيئًا؛ أما المدفوعون فيخصمون من رصيدك المدفوع مسبقًا، ويُرفض الاستدعاء بالرمز HTTP 402 فور أن يهبط الرصيد دون الصفر.", + "topUpUnavailable": "الشحن الذاتي غير متاح بعد — تُضيف المنصّة الأرصدة حاليًا. ورصيدك أعلاه محدَّث في الحالتين.", + "dismiss": "إخفاء", + "loadError": "تعذّر تحميل بعض بيانات لوحة التحكم.", + "retry": "إعادة المحاولة", + "recentCallsCaption": "أحدث استدعاءاتك عبر البوابة", + "chartTitle": "الاستدعاءات يوميًا، آخر {days} يوم", + "chartTotal": "{count, number} إجمالًا", + "chartAria": "رسم بياني شريطي لاستدعاءات البوابة يوميًا خلال آخر {days} يوم، بإجمالي {count, number} استدعاء.", + "chartDay": "{date}: {calls, number} استدعاء، {errors, number} فاشل" }, "Profile": { - "title": "ملفّي الشخصي", - "signInPrompt": "يُرجى تسجيل الدخول لعرض ملفك الشخصي.", - "signIn": "تسجيل الدخول", - "submissions": "الإرساليات", + "title": "إدراجاتي", + "signInPrompt": "سجّل الدخول لعرض الوكلاء والمهارات التي نشرتها.", "likes": "الإعجابات", - "views": "المشاهدات", - "statValue": "{count, number}", - "mySubmissions": "إرسالياتي", - "submitNew": "إرسال جديد", + "views": "مشاهدة", "tab_all": "الكل", - "tab_approved": "مُعتمَد", - "tab_pending": "قيد الانتظار", - "tab_rejected": "مرفوض", - "tabCount": "{count, number}", - "emptyTab": "لا إرساليات في هذا التبويب", + "emptyTab": "لا مهارات هنا بعد.", "submitFirst": "أرسِل أول مهارة لك", - "statusApproved": "مُعتمَد", - "statusPending": "بانتظار المراجعة", + "statusApproved": "منشور", + "statusPending": "قيد المراجعة", "statusRejected": "مرفوض", - "reviewerNote": "ملاحظة المراجِع:" + "reviewerNote": "ملاحظة المراجِع:", + "publishAgent": "نشر وكيل", + "submitSkill": "إرسال مهارة", + "statAgents": "الوكلاء", + "statCalls": "استدعاءات البوابة", + "statSkills": "المهارات", + "tabAgents": "الوكلاء", + "tabSkills": "المهارات", + "statusDisabled": "مُعطَّل", + "kindHosted": "قابل للاستدعاء", + "kindProject": "مفتوح المصدر", + "noAgents": "لا وكلاء هنا بعد.", + "publishFirstAgent": "انشر أول وكيل لك", + "myListings": "إدراجاتي" }, "Submit": { "title": "إرسال مهارة", "subtitle": "شارك مهارة OpenClaw الخاصة بك مع المجتمع", "signInPrompt": "يُرجى تسجيل الدخول لإرسال المهارات.", - "signIn": "تسجيل الدخول", "skillSource": "مصدر المهارة", "githubUrl": "رابط GitHub", "clawSkillsUrl": "رابط ClawSkills.sh", @@ -571,8 +740,15 @@ "submitting": "جارٍ الإرسال...", "enterUrlFirst": "يُرجى إدخال رابط أولًا", "autoFillFailed": "فشلت التعبئة التلقائية", - "submitFailed": "فشل الإرسال", - "somethingWentWrong": "حدث خطأ ما" + "somethingWentWrong": "حدث خطأ ما", + "successTitle": "تم الإرسال 🎉", + "pendingReview": "{name} الآن بانتظار المراجعة.", + "nextStepsTitle": "ما الذي يحدث بعد ذلك", + "nextStepReview": "يتحقّق أحد المراجعين من المستودع ومن الوصف.", + "nextStepEmail": "سنراسلك بالبريد الإلكتروني فور اكتمال المراجعة.", + "nextStepProfile": "تعرض صفحة إدراجاتك دائمًا حالتها الحالية وأي ملاحظة من المراجِع.", + "viewMyListings": "عرض إدراجاتي", + "backToSkills": "→ العودة إلى المهارات" }, "SubmitAgent": { "title": "نشر وكيل", @@ -611,36 +787,69 @@ "successTitle": "تم الإرسال 🎉", "pendingReview": "{name} الآن بانتظار المراجعة.", "successDetail": "سنتحقّق من بطاقة AgentCard وننشرها في السوق بمجرد اعتمادها.", - "backToMarketplace": "← العودة إلى السوق" + "backToMarketplace": "→ العودة إلى السوق", + "checkCard": "افحص البطاقة", + "checking": "جارٍ الفحص…", + "enterCardUrlFirst": "أدخِل رابط AgentCard أولًا.", + "previewTitle": "هكذا سندرِجه", + "previewNoDescription": "البطاقة بلا وصف.", + "previewEndpoint": "نقطة النهاية", + "previewCapabilities": "القدرات", + "previewSkillCount": "{count, plural, =0 {لا قدرات مُعلَنة} one {قدرة واحدة} two {قدرتان} few {# قدرات} many {# قدرة} other {# قدرة}}", + "previewSupports": "يدعم", + "previewStreaming": "البث", + "previewPush": "الإشعارات الفورية", + "previewHint": "لم يُحفَظ شيء بعد — انشره أدناه لإرساله إلى المراجعة.", + "nextStepsTitle": "ما الذي يحدث بعد ذلك", + "nextStepReview": "يتحقّق أحد المراجعين من بطاقة AgentCard ومن استجابة نقطة النهاية لديك.", + "nextStepEmail": "سنراسلك بالبريد الإلكتروني فور اكتمال المراجعة.", + "nextStepProfile": "تعرض صفحة إدراجاتك دائمًا حالتها الحالية وأي ملاحظة من المراجِع.", + "viewMyListings": "عرض إدراجاتي" }, "AgentEngagement": { "yourRating": "تقييمك", - "ratingsCount": "{count, number} تقييمات", - "noRatings": "لا توجد تقييمات بعد", + "ratingsCount": "{count, number} تقييم", + "noRatings": "لم يُقيَّم بعد", "bookmark": "حفظ", "bookmarked": "محفوظ", - "signInToRate": "سجّل الدخول لتقييم هذا الوكيل أو حفظه" + "signInToRate": "سجّل الدخول لتقييم هذا الوكيل أو حفظه", + "rateStars": "{count, plural, zero {قيّم بـ # نجوم} one {قيّم بنجمة واحدة} two {قيّم بنجمتين} few {قيّم بـ # نجوم} many {قيّم بـ # نجمة} other {قيّم بـ # نجمة}}" }, "Bookmarks": { "title": "الوكلاء المحفوظون", "subtitle": "الوكلاء الذين حفظتهم.", "empty": "لا توجد عناصر محفوظة بعد.", "browseAgents": "تصفّح الوكلاء", - "signInPrompt": "سجّل الدخول لرؤية محفوظاتك.", - "signIn": "تسجيل الدخول" + "signInPrompt": "سجّل الدخول لرؤية محفوظاتك." }, "ScenarioPage": { "metaTitle": "وكلاء الذكاء الاصطناعي لـ {scenario}", "metaDescription": "اكتشف واستدعِ وكلاء الذكاء الاصطناعي لـ {scenario} عبر واجهة برمجية موحّدة — موصوفين ببطاقات A2A AgentCard المفتوحة على TakoAPI.", - "heroSubtitle": "وكلاء ذكاء اصطناعي لـ {scenario}، يمكن استدعاؤهم عبر واجهة برمجية واحدة.", + "heroSubtitle": "وكلاء ذكاء اصطناعي لـ {scenario}، يمكن استدعاؤهم عبر واجهة برمجية موحّدة.", "empty": "لا يوجد وكلاء في هذا السيناريو بعد.", "browseAll": "تصفّح كل الوكلاء", - "back": "← كل السيناريوهات", + "back": "→ كل السيناريوهات", "breadcrumbScenarios": "السيناريوهات", "indexTitle": "تصفّح وكلاء الذكاء الاصطناعي حسب السيناريو", "indexDescription": "اعثر على وكلاء الذكاء الاصطناعي حسب الاستخدام — الواجهات الأمامية والاستثمار والتسويق والدعم وغيرها — جميعها قابلة للاستدعاء عبر واجهة برمجية واحدة.", "indexHeroTitle": "تصفّح حسب السيناريو", - "indexHeroSubtitle": "اعثر على وكلاء الذكاء الاصطناعي حسب المهمة التي تريد إنجازها." + "indexHeroSubtitle": "اعثر على وكلاء الذكاء الاصطناعي حسب المهمة التي تريد إنجازها.", + "indexPopularHeading": "تغطية جيدة", + "indexPopularHint": "سيناريوهات تضم كتالوجًا غنيًا من الوكلاء للمقارنة بينهم.", + "indexSparseHeading": "في بداياتها", + "indexSparseHint": "عدد قليل من الوكلاء حتى الآن — فرصة سهلة لتكون الأول.", + "indexSparseCta": "انشر وكيلًا في أحد هذه السيناريوهات ←", + "metaTitlePaged": "وكلاء الذكاء الاصطناعي لـ {scenario} — الصفحة {page}", + "resultsLabel": "وكلاء {scenario}", + "paginationLabel": "ترقيم الصفحات", + "showingRange": "عرض {from}–{to} من {total}", + "prev": "→ السابق", + "next": "التالي ←", + "goToPage": "الانتقال إلى الصفحة {page}", + "ctaTitle": "أتعرف وكيلًا لـ {scenario}؟", + "ctaBody": "لا يزال هذا السيناريو قليل المحتوى. انشر وكيلًا وسيظهر هنا لكل من يتصفّح {scenario}.", + "ctaButton": "نشر وكيل", + "relatedHeading": "استكشف سيناريوهات أخرى" }, "Badge": { "snippetTitle": "إضافة شارة", @@ -648,7 +857,7 @@ "copied": "تم النسخ", "snippetHint": "الصقها في ملف README لربطها بصفحتك على TakoAPI.", "pageTitle": "شارة README الخاصة بـ TakoAPI", - "pageIntro": "أظهر أن مشروعك مُدرَج على TakoAPI — شارة صغيرة تربط بصفحتك وتعرض عدد النجوم المحدّث.", + "pageIntro": "أظهر أن مشروعك مُدرَج على TakoAPI — شارة صغيرة تربط بصفحتك وتعرض عدد نجومك المحدَّث.", "example": "مثال", "step1Title": "اعثر على صفحتك", "step1": "ابحث عن مشروعك في دليل الوكلاء.", @@ -656,15 +865,160 @@ "step2": "في صفحتك، انسخ Markdown من مربع الشارة.", "step3Title": "الصقها في README", "step3": "أضِفها قرب الأعلى — يحصل الزوار ومحركات البحث على رابط لصفحتك على TakoAPI.", - "findListing": "اعثر على صفحتك" + "findListing": "اعثر على صفحتك", + "exampleAlt": "شارة TakoAPI" }, "Blog": { "title": "المدونة", "subtitle": "أدلة حول A2A وبوابات الوكلاء والبناء باستخدام TakoAPI.", "readMore": "اقرأ المزيد", - "backToBlog": "← كل المقالات", - "minRead": "قراءة {min} دقائق", + "backToBlog": "→ كل المقالات", + "minRead": "مدة القراءة {min} دقيقة", "published": "نُشر", "faqTitle": "الأسئلة الشائعة" + }, + "NotFound": { + "title": "تعذّر العثور على هذه الصفحة", + "description": "قد يكون الرابط قديمًا، أو ربما أُزيل الإدراج. جرّب الدليل أو ابحث من الشريط العلوي.", + "home": "الرئيسية", + "agents": "تصفّح الوكلاء", + "skills": "تصفّح المهارات" + }, + "ErrorPage": { + "title": "حدث خطأ ما", + "description": "تعذّر تحميل هذه الصفحة. غالبًا ما تنجح إعادة المحاولة؛ وإن لم تنجح، فالمرجع أدناه يساعدنا على تتبّعها في السجلّات.", + "reference": "المرجع: {id}", + "retry": "أعد المحاولة", + "home": "الذهاب إلى الرئيسية" + }, + "Consent": { + "title": "التحليلات", + "description": "نستخدم Google Analytics لمعرفة الصفحات التي يجدها الناس مفيدة. ولا يُحمَّل شيء ولا تُضبط أي ملفات تعريف ارتباط ما لم توافق.", + "accept": "أوافق", + "decline": "أرفض" + }, + "Docs": { + "metaTitle": "توثيق API", + "title": "مرجع واجهة TakoAPI البرمجية", + "description": "انتقل من مفتاح API إلى استدعاء وكيل يعمل فعلًا: المصادقة، ونقاط نهاية الاكتشاف، وبوابة الوكلاء الموحّدة، وخادم MCP المُستضاف، ورموز الأخطاء، وحدود المعدّل، والفوترة.", + "heroBadge": "توثيق المطوّرين", + "heroTitle": "مرجع واجهة TakoAPI البرمجية", + "heroSubtitle": "مفتاح واحد، وكل الوكلاء. وجّه أي عميل متوافق مع OpenAI إلى TakoAPI واستدعِ وكيلًا عبر slug الخاص به — أو تصفّح الكتالوج كاملًا بلا مفتاح على الإطلاق.", + "heroKeyCta": "أنشئ مفتاح API", + "heroBrowseCta": "تصفّح الوكلاء", + "tocTitle": "في هذه الصفحة", + "tocLabel": "جدول المحتويات", + "quickstartTitle": "بداية سريعة", + "quickstartIntro": "ثلاث خطوات من الصفر إلى ردّ. وكل طلب في هذه الصفحة هو استدعاء حقيقي على الواجهة المباشرة.", + "quickstartStep1Title": "أنشئ مفتاحًا", + "quickstartStep1": "سجّل الدخول وأنشئ مفتاح API من لوحة التحكم. ويُعرَض السرّ مرة واحدة، عند الإنشاء.", + "quickstartStep2Title": "صدّره", + "quickstartStep2": "كل مثال في هذه الصفحة يقرأ المفتاح من TAKO_KEY.", + "quickstartStep3Title": "استدعِ وكيلًا", + "quickstartStep3": "اضبط model — أو الـ slug في الرابط — على أي وكيل مُستضاف من الكتالوج.", + "quickstartSamplesLabel": "أمثلة استدعاء البداية السريعة", + "quickstartNote": "الـ slug في هذه الأمثلة عنصر نائب لا وكيل حقيقي. انسخ واحدًا حقيقيًا من أي صفحة وكيل؛ فالوكلاء المُدرَجون كمُستضافين وحدهم هم القابلون للاستدعاء.", + "authenticationTitle": "المصادقة", + "authenticationIntro": "تُصادَق استدعاءات البوابة برمز حامل (bearer). أما نقاط نهاية الاكتشاف فمجهولة الهوية ولا تحتاج مفتاحًا إطلاقًا.", + "authHeaderNote": "يُقبل x-api-key بديلًا عن ترويسة Authorization. وتبدأ المفاتيح دائمًا بـ tako_live_.", + "authWhereTitle": "من أين تأتي المفاتيح", + "authWhere": "أنشئ مفتاحًا واحدًا لكل عميل أو بيئة من لوحة التحكم، وأبطِل ما تتوقّف عن استخدامه.", + "authOnceTitle": "يُعرَض السرّ مرة واحدة", + "authOnce": "لا يُخزَّن سوى تجزئة SHA-256 للمفتاح وبادئة عرض قصيرة، لذا لا يمكن استعادة سرّ مفقود — أنشئ بديلًا وأبطِل المفتاح القديم.", + "authRevokeTitle": "الإبطال فوري", + "authRevoke": "كل استدعاء يعيد التحقّق من المفتاح في قاعدة البيانات ولا شيء يُخزَّن مؤقتًا، لذا يُرفض المفتاح المُبطَل في الطلب التالي مباشرةً. ويبقى استخدامك السابق في سجلّك.", + "discoveryTitle": "الاكتشاف", + "discoveryIntro": "أربع نقاط نهاية عامة قابلة للتخزين المؤقت ولا تحتاج مفتاحًا. ولا تُعاد سوى الوكلاء والمهارات المعتمَدة.", + "endpointRegistrySummary": "كتالوج الوكلاء كاملًا — Markdown للنموذج، وJSON للشيفرة.", + "endpointRegistryNote": "بدون format=json تكون الاستجابة بصيغة Markdown ‏(text/markdown)، مقسَّمة إلى جدول للوكلاء المُستضافين وجدول للمشاريع مفتوحة المصدر، وبحجم يناسب سياق النموذج مباشرةً — وهذا ما تقرؤه مهارة TakoAPI المثبَّتة. وكلتا الصيغتين محدودتان بـ limit، لذا تحقّق من truncated ومن الأعداد الإجمالية قبل أن تفترض أنك حصلت على كل شيء. وتُقتطع معاملات النص الحر عند 100 حرف.", + "paramRegistryFormat": "md ‏(الافتراضي) أو json.", + "paramRegistryQ": "مطابقة نص حر على اسم الوكيل ووصفه.", + "paramRegistryCategory": "slug الفئة.", + "paramRegistryProtocol": "إعادة الوكلاء المُعلِنين عن هذا البروتوكول فقط.", + "paramRegistryKind": "وكلاء HOSTED قابلون للاستدعاء عبر البوابة؛ أما مدخلات PROJECT فهي مستودعات مفتوحة المصدر مُدرَجة للاكتشاف فقط.", + "paramRegistrySort": "الترتيب. الافتراضي هو المميَّزون أولًا، ثم الأكثر استدعاءً، ثم الأحدث — والأكثر نجومًا للمشاريع.", + "paramRegistryLimit": "إجمالي المدخلات المُعادة. يُملأ الوكلاء المُستضافون أولًا، ثم المشاريع.", + "endpointAgentDetailSummary": "التفاصيل العامة الكاملة لوكيل واحد، بما فيها مهارات A2A التي يعلن عنها.", + "endpointAgentDetailNote": "لا يُستجاب إلا للوكلاء المعتمَدين؛ وأي شيء آخر يعطي 404. ولا تكون حقول الإشراف — ملاحظات المراجعة والمعرّفات الداخلية وصور الناشرين — جزءًا من هذا الشكل أبدًا. مخزَّن مؤقتًا على الحافة لمدة 60 ثانية.", + "paramAgentDetailSlug": "جزء من المسار. slug الوكيل كما يعيده السجلّ.", + "endpointSkillsDirectorySummary": "كتالوج مهارات OpenClaw، بصيغة Markdown افتراضيًا.", + "endpointSkillsDirectoryNote": "يعتمد Markdown أولًا كما في السجلّ. وتكون install بقيمة null للمهارات التي لا أمر تثبيت منشورًا لها — عد عندئذٍ إلى رابط github. أما downloads فرقم من طرف ثالث مأخوذ من صفحة المهارة نفسها، لا عدد تثبيتات عبر TakoAPI.", + "paramSkillsDirFormat": "md ‏(الافتراضي) أو json.", + "paramSkillsDirQ": "مطابقة نص حر على اسم المهارة ووصفها.", + "paramSkillsDirCategory": "slug الفئة.", + "paramSkillsDirPage": "رقم الصفحة، يبدأ من 1.", + "paramSkillsDirLimit": "عدد النتائج في الصفحة.", + "endpointSkillsSearchSummary": "بحث نصّي كامل في كتالوج المهارات، بصيغة JSON فقط.", + "endpointSkillsSearchNote": "q مطلوب: بدونه تعيد نقطة النهاية قائمة فارغة بدلًا من الكتالوج كاملًا. وكل نتيجة هي صف الكتالوج كاملًا باستثناء readme وreviewNote؛ والمثال أعلاه مختصر.", + "paramSkillsSearchQ": "مصطلح البحث. يطابق اسم المهارة ووصفها ومؤلفها.", + "paramSkillsSearchCategory": "slug الفئة.", + "paramSkillsSearchPage": "رقم الصفحة، يبدأ من 1.", + "paramSkillsSearchLimit": "عدد النتائج في الصفحة.", + "gatewayTitle": "البوابة", + "gatewayIntro": "ثلاث طرق لاستدعاء الوكيل نفسه. الثلاث تتطلّب مفتاحًا، وتُفوتر بالطريقة نفسها، وتقبل جميعها slug الوكيل في الموضع الذي يُوضع فيه اسم النموذج عادةً. ولا يمكن استدعاء سوى الوكلاء من نوع HOSTED.", + "endpointChatCompletionsSummary": "متوافقة مع OpenAI. وجّه أي حزمة تطوير من OpenAI إلى ‎/v1 واضبط model على slug وكيل.", + "endpointChatCompletionsNote": "تُمرَّر المحادثة كاملة: جزء نصّي A2A واحد لكل دور، بالترتيب، مع إرفاق دور OpenAI بكل جزء. وحقل usage موجود دائمًا لكنه صفر دائمًا — فلا شيء في هذه البوابة يعدّ الرموز، والوكيل ليس بالضرورة نموذجًا لغويًا. اضبط stream: true للحصول على الصيغة SSE أعلاه، وstream_options.include_usage للحصول أيضًا على مقطع usage الختامي. وتنتهي مهلة الاستدعاء غير المتدفّق بعد 30 ثانية، ومهلة المتدفّق بعد 120.", + "endpointA2AMessageSummary": "تمرير A2A أصيل. يعيد استجابة JSON-RPC الخاصة بالوكيل دون تعديل.", + "endpointA2AMessageNote": "يُمرَّر الطلب كاستدعاء A2A من نوع message/send. أرسِل الصيغة النصّية المختصرة، أو رسالة A2A كاملة — بأجزائها وtaskId وcontextId — لمتابعة مهمة متعدّدة الأدوار أو لتمرير أجزاء الملفات والبيانات. ويُنقل رمز حالة الوكيل وجسم استجابته كما هما، ويُرفض أي طلب بلا محتوى بالرمز 400 بدلًا من فوترته. وتنتهي المهلة بعد 30 ثانية.", + "endpointA2AStreamSummary": "الاستدعاء نفسه عبر Server-Sent Events، للوكلاء الذين يدعمون البث.", + "endpointA2AStreamNote": "ينقل إطارات A2A من نوع message/stream حرفيًا بصيغة text/event-stream. وتقبل نقطة النهاية هذه الصيغة النصّية المختصرة فقط. ويُقطع الاتصال بعد 120 ثانية، ولا يُحتسب الاستدعاء إلا بعد أن يبلغ البث نهايته — راجِع «حدود المعدّل والفوترة».", + "labelQueryParams": "المعاملات", + "labelRequest": "جسم الطلب", + "labelExample": "مثال", + "labelResponse": "الاستجابة", + "labelStreaming": "الاستجابة مع stream: true", + "labelA2AMessageForm": "صيغة رسالة A2A الكاملة", + "labelAuthRequired": "يتطلّب مفتاحًا", + "labelNoAuth": "بلا مفتاح", + "labelBillable": "قابل للفوترة", + "labelFree": "مجاني", + "labelParamName": "الاسم", + "labelParamType": "النوع", + "labelParamDesc": "الوصف", + "paramsTableCaption": "معاملات {path}", + "mcpTitle": "خادم MCP", + "mcpIntro": "يشغّل TakoAPI خادم Model Context Protocol مُستضافًا وعديم الحالة عبر Streamable HTTP على {url}. سجّله مرة واحدة ليتمكّن وكيل البرمجة لديك من البحث في الكتالوج واستدعائه كأدوات أصيلة.", + "mcpRegisterTitle": "سجّله", + "mcpRegisterLabel": "أوامر تسجيل MCP", + "mcpAuthNote": "تعمل أدوات القراءة الثلاث دون هوية. أما invoke_agent فيستهلك رصيدًا، لذا يحتاج مفتاحك: مرّره في ترويسة Authorization ‏(Claude Code)، أو عبر bearer_token_env_var ‏(Codex)، أو ضمن كتلة headers ‏(OpenCode).", + "mcpToolsTitle": "الأدوات", + "mcpToolsTableCaption": "الأدوات التي يوفّرها خادم MCP الخاص بـ TakoAPI", + "mcpToolColumn": "الأداة", + "mcpToolAccessColumn": "الوصول", + "mcpToolWhatColumn": "وظيفتها", + "mcpToolKeyRequired": "يتطلّب مفتاحًا", + "mcpToolAnonymous": "دون هوية", + "mcpToolSearchAgents": "ابحث في السجلّ بكلمة مفتاحية أو فئة أو بروتوكول. يعيد الـ slugs والبروتوكولات والتسعير.", + "mcpToolGetAgent": "كل ما هو معروف عن وكيل واحد عبر slug الخاص به — القدرات والبروتوكولات ونقطة النهاية والتسعير والمهارات المُعلَنة.", + "mcpToolSearchSkills": "ابحث في كتالوج مهارات OpenClaw. وبلا استعلام يعيد أفضل المهارات المنتقاة.", + "mcpToolInvokeAgent": "استدعِ وكيلًا عبر البوابة واحصل على ردّه. يتطلّب مفتاحًا ويستهلك رصيدًا.", + "mcpRateNote": "نقطة نهاية MCP محدودة بـ 120 طلبًا في الدقيقة لكل عنوان IP، بمعزل عن حصة البوابة الخاصة بمفتاحك.", + "mcpInstallNote": "أتفضّل ملف مهارة على خادم؟ المثبِّت يعلّم Claude Code وCodex وOpenCode استدعاء نقاط النهاية هذه عبر HTTP مباشرةً بدلًا من ذلك.", + "errorsTitle": "الأخطاء", + "errorsIntro": "كل إخفاق في البوابة يأتي بصيغة JSON. وهذه هي الحالات التي تعيدها البوابة نفسها.", + "errorsTableCaption": "حالات الأخطاء التي تعيدها البوابة", + "errorStatusColumn": "الحالة", + "errorCodeColumn": "الرمز", + "errorWhenColumn": "متى", + "errorBodyColumn": "مثال على الجسم", + "error400When": "الـ slug يشير إلى مشروع مفتوح المصدر بلا نقطة نهاية قابلة للاستدعاء، أو فشل التحقّق من جسم الطلب، أو كانت كل أدوار الرسائل فارغة.", + "error401When": "المفتاح مفقود أو مُشوَّه أو غير معروف أو مُبطَل.", + "error402When": "الوكيل مدفوع، وخصم الاستدعاء سيهبط برصيدك دون الصفر. يُفحَص قبل الاتصال بالوكيل، فلا يُنفَق شيء.", + "error404When": "لا يوجد وكيل معتمَد بهذا الـ slug.", + "error429When": "تجاوز المفتاح حصّته خلال آخر 60 ثانية. وترويسة Retry-After تعطي عدد الثواني حتى إعادة ضبط النافذة.", + "error502When": "لم يردّ الوكيل، أو انتهت مهلته، أو لم يُعِد أي بث، أو أبلغ عن خطأ JSON-RPC ضمن استجابة HTTP 200. ولا يُحتسب أبدًا.", + "errorsOpenAiNote": "على /v1/chat/completions تصل الإخفاقات نفسها ضمن غلاف OpenAI — مع وجود type وparam وcode دائمًا — فترفع حزمة OpenAI أنواع الاستثناءات المعتادة لديها:", + "errorsPassthroughNote": "تُنقل الحالة التي يعيدها الوكيل نفسه دون تغيير عبر نقطة نهاية message في A2A، لذا قد يكون الخطأ 4xx هناك من الوكيل لا منّا.", + "limitsTitle": "حدود المعدّل والفوترة", + "limitsRateTitle": "حدود المعدّل", + "limitsRate": "تسمح البوابة بـ 120 طلبًا كل 60 ثانية لكل مفتاح API. والنافذة مشتركة بين جميع نسخ الخادم ومرتبطة بالمفتاح نفسه لا بعنوان IP الخاص بك، فلا تتراخى تحت الحِمل ولا تتشدّد خلف NAT مشترك.", + "limitsRateOverride": "يمكن أن يحمل المفتاح حصّته الخاصة، فتحلّ محلّ الحصة الافتراضية لذلك المفتاح وحده. وعند تجاوز الحد تحصل على 429 مع ترويسة Retry-After بالثواني.", + "limitsBillingTitle": "الفوترة", + "limitsBillingFree": "الوكلاء المسعَّرون FREE لا يكلّفون شيئًا — وكذلك أي وكيل سعر وحدته مفقود أو صفر. والبوابة لا تخترع رسومًا أبدًا.", + "limitsBillingPriced": "يخصم الوكيل المدفوع من رصيدك المدفوع مسبقًا بسعر وحدته، مرة واحدة لكل استدعاء. ولا توجد محاسبة على مستوى الرموز، فالاستدعاء وحدة واحدة مهما طال.", + "limitsBillingPreflight": "يُفحَص رصيدك قبل الاتصال بالوكيل. وإذا كان الخصم سيهبط بك دون الصفر يُرفض الاستدعاء بالرمز 402 ولا يُستدعى الوكيل إطلاقًا — فلا سحب على المكشوف.", + "limitsBillingFailures": "الاستدعاءات الفاشلة لا تُحتسب. فخطأ 4xx أو 5xx من الوكيل، أو انتهاء المهلة، أو نقطة نهاية يتعذّر الوصول إليها، أو خطأ JSON-RPC — كلها تُسجَّل كمحاولة ولا تُفوتر.", + "limitsBillingStreams": "لا تُحتسب عمليات البث إلا عند اكتمالها. فالبث الذي يُلغى أو تنتهي مهلته أو يفشل في منتصفه أو تنقطع أنت عنه مبكرًا يُسجَّل ولا يُفوتر.", + "limitsBillingLedger": "كل خصم يكتب مدخلًا غير قابل للتعديل في السجلّ مقابل الاستدعاء الذي سبّبه. والرصيد والسجلّ والاستخدام لكل استدعاء كلها في لوحة التحكم." } } diff --git a/messages/de.json b/messages/de.json index f9fda6cf2..6664df654 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1,66 +1,84 @@ { "Metadata": { - "tagline": "Eine API für den Zugriff auf alle Agenten", + "tagline": "Eine API für alle Agenten", "description": "Entdecke und rufe KI-Agenten über eine einheitliche API auf — ein OpenRouter für Agenten. Durchstöbere ein Verzeichnis mit Hunderten Open-Source-Agentenprojekten und Tausenden OpenClaw-Skills für deinen Coding-Agenten." }, "LocaleSwitcher": { "label": "Sprache" }, "Header": { - "searchPlaceholder": "Agenten suchen...", "agents": "Agenten", "skills": "Skills", - "trending": "Im Trend", - "publish": "Veröffentlichen", "publishAgent": "Agent veröffentlichen", "account": "Konto", - "mySkills": "Meine Skills", - "myBookmarks": "Lesezeichen", - "apiUsage": "API & Nutzung", + "myBookmarks": "Gespeicherte Agenten", + "apiUsage": "API-Schlüssel & Nutzung", "adminPanel": "Admin-Panel", "signIn": "Anmelden", - "signOut": "Abmelden" + "signOut": "Abmelden", + "mainNav": "Hauptnavigation", + "skipToContent": "Zum Inhalt springen", + "openMenu": "Menü öffnen", + "closeMenu": "Menü schließen", + "projects": "Projekte", + "install": "Installieren", + "getApiKey": "API-Schlüssel holen", + "search": "Suchen", + "searchScope": "Suchen in", + "scopeAgents": "Agenten", + "scopeProjects": "Projekte", + "scopeSkills": "Skills", + "searchAgentsPlaceholder": "Agenten nach ihrer Aufgabe suchen…", + "searchProjectsPlaceholder": "Open-Source-Agentenprojekte suchen…", + "searchSkillsPlaceholder": "Skills für deinen Coding-Agenten suchen…", + "myListings": "Meine Einträge", + "docs": "Dokumentation" }, "Footer": { - "tagline": "Eine API für den Zugriff auf alle Agenten — plus Tausende OpenClaw-Skills für deinen Coding-Agenten.", + "tagline": "Eine API für alle Agenten — jeden Agenten über einen einzigen Endpunkt finden, aufrufen und abrechnen.", "agentRegistry": "Agenten-Registry:", "platform": "Plattform", "resources": "Ressourcen", - "installInAgent": "In deinem Agenten installieren", + "installInAgent": "In deinen Coding-Agenten installieren", "browseAgents": "Agenten durchsuchen", "publishAnAgent": "Agent veröffentlichen", - "developerDashboard": "Entwickler-Dashboard", + "developerDashboard": "API-Schlüssel & Nutzung", "browseSkills": "Skills durchsuchen", - "awesomeList": "Awesome-Liste", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Entwickelt für die OpenClaw-Community.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. MIT-lizenziert.", + "blog": "Blog", + "badge": "README-Badge", + "scenarios": "Nach Szenario durchsuchen", + "a2aSpec": "A2A-Protokoll", + "github": "GitHub", + "docs": "API-Dokumentation" }, "Home": { - "heroTitle": "Eine API für den Zugriff auf alle Agenten", - "heroSubtitle": "Entdecke und rufe {agents, number} KI-Agenten über eine einheitliche API auf — plus {skills, number} Skills für deinen Coding-Agenten.", + "heroTitle": "Eine API für alle Agenten", + "heroSubtitle": "Entdecke KI-Agenten und rufe jeden davon über einen Endpunkt mit einem Schlüssel auf. A2A-nativ, OpenAI-kompatibel, pro Aufruf abgerechnet.", "browseAgents": "Agenten durchsuchen", - "publishAnAgent": "Agent veröffentlichen", - "agentRegistry": "Agenten-Registry:", - "browseByScenario": "Nach Szenario durchsuchen", - "allAgents": "Alle Agenten", + "browseByScenario": "Nach Einsatzzweck durchsuchen", "featuredAgents": "Empfohlene Agenten", "browseAll": "Alle durchsuchen", "popularProjects": "Beliebte Open-Source-Projekte", "selfHostable": "{count, number} selbst hostbar", - "skillTitle": "TakoAPI-Skill für OpenClaw", - "official": "Offiziell", - "skillDescription": "Suche, installiere und verwalte OpenClaw-Skills im natürlichen Dialog — angetrieben vom TakoAPI-Marktplatz.", - "viewOnGithub": "Auf GitHub ansehen", - "askAgent": "oder frage deinen Agenten: „Installiere den TakoAPI-Skill“", - "usingCodingAgent": "Nutzt du Claude Code, Codex oder OpenCode?", - "oneCommandInstall": "Installation mit einem Befehl", - "mustHaveSkills": "Unverzichtbare Skills", - "viewRankings": "Rankings ansehen", - "categories": "Kategorien", - "viewAll": "Alle ansehen", + "categories": "Kategorien:", "moreCategories": "{count, plural, one {+# weitere Kategorie} other {+# weitere Kategorien}}", - "latestSkills": "Neueste Skills" + "getApiKey": "API-Schlüssel erstellen", + "catalogSummary": "{agents, number} aufrufbare Agenten · {projects, number} Open-Source-Projekte · {skills, number} Skills für Coding-Agenten", + "quickstartLabel": "In 30 Sekunden zum ersten Aufruf", + "quickstartNote": "Arbeitest du lieber im Terminal? Installiere TakoAPI in deinen Coding-Agenten.", + "allScenarios": "Alle Szenarien", + "callableNow": "{count, number} über das Gateway aufrufbar", + "installTitle": "TakoAPI aus deinem Coding-Agenten nutzen", + "installDescription": "Ein Befehl bringt Claude Code, Codex oder OpenCode bei, das Verzeichnis zu durchsuchen und Agenten für dich aufzurufen — oder registriere stattdessen den gehosteten MCP-Server.", + "installCta": "Installationsanleitung", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Skills für deinen Coding-Agenten", + "skillsSubtitle": "{count, number} OpenClaw-Skills — die meistinstallierten und die neuesten.", + "quickstartTabDiscover": "1. Finden", + "quickstartTabCall": "2. Aufrufen", + "quickstartTabFromAgent": "3. Aus deinem Agent" }, "Scenarios": { "frontend": "Frontend", @@ -85,8 +103,6 @@ "notFound": "Agent nicht gefunden", "backToMarketplace": "← Agenten-Marktplatz", "openSourceProject": "Open-Source-Projekt", - "signedCard": "Signierte Card", - "verifiedPublisher": "Verifizierter Anbieter", "by": "von {name}", "publisherOpenSource": "Open Source", "publisherUnknown": "unbekannt", @@ -97,29 +113,53 @@ "pushNotifications": "Push-Benachrichtigungen", "skills": "Skills", "skillsCount": "({count, number})", - "noSkillsProject": "Ein Open-Source-Projekt — erkunde den Code und hoste es selbst von GitHub.", + "noSkillsProject": "Ein Open-Source-Projekt — erkunde den Code und hoste es selbst von GitHub aus.", "noSkillsHosted": "Dieser Agent hat in seiner AgentCard keine strukturierten Skills angegeben.", "inputMode": "Eingabe: {mode}", "outputMode": "Ausgabe: {mode}", - "openSourceProjectHeading": "Open-Source-Projekt", "openSourceProjectNote": "Selbst hosten oder den Code erkunden. Nicht über das TakoAPI-Gateway aufrufbar.", "viewOnGithub": "Auf GitHub ansehen", - "integration": "Integration", "endpoint": "Endpunkt", "agentCard": "AgentCard", "homepage": "Homepage", "callThroughTakoApi": "Über TakoAPI aufrufen", - "gatewayPhase2": "Einheitliches Gateway — Aufrufe folgen in Phase 2.", "breadcrumbHome": "Start", - "breadcrumbMarketplace": "Agenten-Marktplatz" + "breadcrumbMarketplace": "Agenten-Marktplatz", + "gatewayHint": "Rufe ihn mit deinem API-Schlüssel auf — erstelle einen im Dashboard.", + "healthOk": "Antwortet", + "healthDegraded": "Eingeschränkt", + "healthDown": "Antwortet nicht", + "healthCheckedAt": "Zuletzt geprüft: {time}", + "atAGlance": "Auf einen Blick", + "protocols": "Protokolle", + "protocolsNone": "Nicht angegeben", + "yes": "Ja", + "no": "Nein", + "pricing": "Preis", + "health": "Erreichbarkeit", + "healthUnknown": "Noch nicht geprüft", + "totalCalls": "Aufrufe gesamt", + "numberValue": "{count, number}", + "averageRating": "Durchschnittliche Bewertung", + "ratingValue": "{rating} / 5 · {count, number} Bewertungen", + "notRated": "Noch nicht bewertet", + "callExamplesLabel": "Aufruf-Beispiele", + "repository": "Repository", + "language": "Sprache", + "license": "Lizenz", + "licenseNone": "Nicht angegeben", + "lastPush": "Letzter Push", + "topics": "Themen", + "readmeHeading": "Aus dem README", + "readmeNote": "Auszug aus dem README des Projekts auf GitHub." }, "Agents": { "metaTitle": "Agenten-Marktplatz", - "metaDescription": "Eine API für den Zugriff auf alle Agenten. Durchsuche und rufe KI-Agenten sowie Hunderte Open-Source-Agentenprojekte über eine einheitliche API auf — beschrieben durch offene A2A-AgentCards.", + "metaDescription": "Eine API für alle Agenten. Durchsuche und rufe KI-Agenten sowie Hunderte Open-Source-Agentenprojekte über eine einheitliche API auf — beschrieben durch offene A2A-AgentCards.", "ogTitle": "Agenten-Marktplatz — {siteName}", "ogDescription": "Entdecke und rufe KI-Agenten und Open-Source-Agentenprojekte über eine einheitliche API auf.", "heroTitle": "Agenten-Marktplatz", - "heroTagline": "Eine API für den Zugriff auf alle Agenten.", + "heroTagline": "Eine API für alle Agenten.", "heroSubtitle": "Entdecke und rufe KI-Agenten über eine einheitliche API auf — beschrieben durch offene A2A-AgentCards.", "searchPlaceholder": "Agenten nach Name oder Beschreibung suchen…", "search": "Suchen", @@ -128,7 +168,7 @@ "kindProjects": "Projekte", "scenarioFilter": "Szenario:", "protocolFilter": "Protokoll:", - "pricingFilter": "Preise:", + "pricingFilter": "Preis:", "categoryFilter": "Kategorie:", "sortLabel": "Sortieren:", "sortLatest": "Neueste", @@ -141,15 +181,28 @@ "pricingPerTask": "pro Aufgabe", "pricingPerToken": "Pro 1k Tokens", "count": "{count, plural, =0 {Keine Agenten} one {# Agent} other {# Agenten}}", - "emptyTitle": "Noch keine Agenten passen zu deinen Filtern.", + "emptyTitle": "Keine Agenten passen zu deinen Filtern.", "emptyCta": "Veröffentliche den ersten →", "prev": "← Zurück", "next": "Weiter →", - "pageOf": "Seite {page} von {total}" + "pageOf": "Seite {page} von {total}", + "filtersTitle": "Filter", + "filtersWithCount": "Filter ({count})", + "activeFiltersLabel": "Aktive Filter:", + "removeFilter": "Filter entfernen: {label}", + "clearAll": "Alle zurücksetzen", + "kindLabel": "Typ", + "scenarioGroup": "Szenario", + "protocolGroup": "Protokoll", + "pricingGroup": "Preis", + "categoryGroup": "Kategorie", + "searchChip": "Suche: {query}", + "resultsLabel": "Ergebnisse", + "paginationLabel": "Seitennavigation", + "goToPage": "Zu Seite {page}", + "showingRange": "Ergebnisse {from}–{to} von {total}" }, "Components": { - "homeSearchPlaceholder": "Skills nach Name, Beschreibung oder Autor suchen...", - "homeSearchButton": "Suchen", "agentFree": "Kostenlos", "agentPerCall": "{price}/Aufruf", "agentPerTask": "{price}/Aufgabe", @@ -163,7 +216,11 @@ "agentSelfHost": "selbst hosten", "agentSkills": "{count, plural, one {# Skill} other {# Skills}}", "skillBy": "von {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} Aufrufe", + "healthOk": "Antwortet", + "healthDegraded": "Eingeschränkt", + "healthDown": "Antwortet nicht" }, "Install": { "metaTitle": "In Claude Code, Codex & OpenCode installieren", @@ -196,7 +253,7 @@ "howToUseHeading": "Verwendung", "claudeCodeUse": "Lädt automatisch — bitte Claude einfach, einen Agenten auf TakoAPI zu finden.", "codexUse": "Mit $takoapi aufrufen oder im Menü /skills auswählen.", - "opencodeUse": "Führe /takoapi aus oder übergib an @takoapi.", + "opencodeUse": "Führe /takoapi '' aus oder übergib an @takoapi.", "mcpHeading": "MCP-Server — gehostete Tools, nichts zu installieren", "claudeCodeMcpHint": "Ein Befehl. Füge --header \"Authorization: Bearer $TAKO_KEY\" hinzu, um invoke_agent zu aktivieren.", "codexMcpHint": "Zu ~/.codex/config.toml hinzufügen", @@ -218,7 +275,7 @@ "brief": "Kurzbeschreibung", "description": "Beschreibung", "whatThisSkillDoes": "Was dieser Skill macht", - "whenToUseIt": "Wann du ihn einsetzen solltest", + "whenToUseIt": "Wann du ihn einsetzt", "exampleWorkflow": "Beispiel-Workflow", "workflowInput": "EINGABE", "workflowAgent": "AGENT", @@ -226,12 +283,17 @@ "requirements": "Voraussetzungen", "installation": "Installation", "clawHubCli": "ClawHub-CLI", - "openClawChat": "OpenClaw-Chat (füge dies in deine Unterhaltung ein)", "links": "Links", "github": "GitHub", - "copyGithubUrl": "GitHub-URL kopieren", "clawHub": "ClawHub", - "skillNotFound": "Skill nicht gefunden" + "codingAgent": "Coding-Agent", + "agentPrompt": "Installiere den Skill von {url}", + "installHintRegistry": "Führe den CLI-Befehl aus oder füge den Prompt in Claude Code, Cursor, Codex oder deinen Coding-Agenten ein.", + "installHintRepo": "Dieser Skill ist nicht in der ClawHub-Registry veröffentlicht. Füge den Prompt in deinen Coding-Agenten ein — Claude Code, Cursor, Codex — und er installiert den Skill direkt aus dem Quell-Repository.", + "installUnavailable": "Für diesen Skill wurde noch keine Installationsquelle veröffentlicht.", + "openRepository": "Repository öffnen", + "signInToLike": "Melde dich an, um diesen Skill zu liken", + "downloadsSource": "Downloadzahlen laut clawskills.sh" }, "Skills": { "metaTitle": "Skills durchsuchen", @@ -257,15 +319,34 @@ "all": "Alle", "emptyTitle": "Keine Skills gefunden", "emptyHint": "Passe deine Suche oder Filter an", - "pageOf": "Seite {page} von {total}" + "pageOf": "Seite {page} von {total}", + "metaTitleSearch": "Skills passend zu „{query}“", + "metaTitlePaged": "Skills durchsuchen — Seite {page}", + "searchInputLabel": "Skills suchen", + "searchPlaceholder": "Skills nach Name, Beschreibung oder Autor suchen", + "searchButton": "Suchen", + "agentFilter": "Agent", + "agentZed": "Zed", + "sourceFilter": "Quelle", + "sourceGithub": "GitHub", + "sourceCommunity": "Community", + "sourceCurated": "Kuratiert", + "categoryFilter": "Kategorie", + "sortLabel": "Sortieren", + "sortRelevance": "Beste Treffer", + "clearFilters": "Filter zurücksetzen", + "paginationLabel": "Seiten der Skill-Liste", + "goToPage": "Zu Seite {page}", + "prev": "Zurück", + "next": "Weiter" }, "Trending": { "metaTitle": "Skills im Trend", - "metaDescription": "Die meistheruntergeladenen und beliebtesten OpenClaw-Skills gerade jetzt — ein Live-Leaderboard dessen, was Coding-Agenten installieren.", + "metaDescription": "Die meistheruntergeladenen und beliebtesten OpenClaw-Skills — ein Leaderboard auf Basis der von clawskills.sh gemeldeten Downloadzahlen, täglich aktualisiert.", "ogTitle": "Skills im Trend — {siteName}", - "ogDescription": "Ein Live-Leaderboard der beliebtesten OpenClaw-Skills.", + "ogDescription": "Ein täglich aktualisiertes Leaderboard der beliebtesten OpenClaw-Skills.", "title": "Skills im Trend", - "subtitle": "Entdecke die beliebtesten OpenClaw-Skills, gereiht von der Community", + "subtitle": "Entdecke die beliebtesten OpenClaw-Skills, gerankt von der Community", "searchPlaceholder": "Trend-Skills suchen…", "categories": "Kategorien", "allCategories": "Alle Kategorien", @@ -279,9 +360,15 @@ "emptyTitle": "Keine Skills gefunden", "emptySubtitle": "Passe deine Suche oder Filter an", "byAuthor": "von {author}", - "showing": "{from, number}–{to, number} von {total, number} werden angezeigt", + "showing": "Ergebnisse {from, number}–{to, number} von {total, number}", "prev": "Zurück", - "next": "Weiter" + "next": "Weiter", + "provenanceNote": "Download- und Sternezahlen stammen aus dem Ursprungskatalog — clawskills.sh und GitHub — und werden nächtlich aktualisiert; sie messen keine Installationen über TakoAPI. Likes und Aufrufe werden hier gezählt.", + "search": "Suchen", + "sortLabel": "Sortieren nach", + "rank": "Rang", + "goToPage": "Zu Seite {page}", + "paginationLabel": "Leaderboard-Seiten" }, "Admin": { "accessDenied": "Zugriff verweigert", @@ -335,8 +422,8 @@ "actionReject": "Ablehnen", "feature": "Hervorheben", "unfeature": "Hervorhebung aufheben", - "scenariosEditorHint": "场景 Szenarien — 点击切换 / zum Zuweisen umschalten", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Szenarien — zum Zuweisen oder Entfernen klicken", + "scenarioOption": "{name}", "selectedCount": "{count, number} ausgewählt", "rejectReasonPrompt": "Grund für die Ablehnung (optional):", "deleteAgentConfirm": "Diesen Agenten endgültig löschen?", @@ -363,7 +450,7 @@ "colCreated": "Erstellt", "colActions": "Aktionen", "noSkillsFound": "Keine Skills gefunden.", - "showingRange": "{from, number}–{to, number} von {total, number} werden angezeigt", + "showingRange": "Ergebnisse {from, number}–{to, number} von {total, number}", "editSkillTitle": "Skill bearbeiten", "fieldName": "Name", "fieldAuthor": "Autor", @@ -436,7 +523,46 @@ "targetSkill": "Skill", "targetAgent": "Agent", "targetCategory": "Kategorie", - "targetUser": "Benutzer" + "targetUser": "Benutzer", + "adminNavLabel": "Admin-Bereiche", + "retry": "Erneut versuchen", + "close": "Schließen", + "prevPage": "Vorherige Seite", + "nextPage": "Nächste Seite", + "sectionAgents": "Agenten", + "sectionSkills": "Skills & Community", + "statTotalAgents": "Agenten gesamt", + "statPendingAgents": "Ausstehende Agenten", + "statHostedAgents": "Gehostet", + "statProjectAgents": "Projekte", + "pendingAgentsTitle": "Agenten zur Prüfung", + "noPendingAgents": "Es warten keine Agenten auf Prüfung.", + "kindHosted": "Gehostet", + "kindProject": "Projekt", + "filterByStatus": "Nach Status filtern", + "filterDisabled": "Deaktiviert", + "actionDisable": "Deaktivieren", + "actionEnable": "Reaktivieren", + "disableAgentTitle": "Agent deaktivieren", + "disableAgentConfirm": "{name} wird aus dem öffentlichen Katalog ausgeblendet, bis du ihn reaktivierst. Es wird nichts gelöscht.", + "deleteAgentTitle": "Agent löschen", + "deleteAgentConfirmNamed": "Möchtest du {name} wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.", + "rejectAgentTitle": "Agent ablehnen", + "rejectReasonLabel": "Grund (optional, wird dem Anbieter angezeigt)", + "rejectReasonPlaceholder": "z. B. der Endpunkt liefert 500 bei der Beispielaufgabe", + "healthOk": "Erreichbar", + "healthDegraded": "Eingeschränkt", + "healthDown": "Ausgefallen", + "healthUnknown": "Ungeprüft", + "healthCheckedAt": "Letzter Health-Check: {date}", + "healthNeverChecked": "Kein Health-Check erfasst", + "batchDeleteTitle": "Skills löschen", + "selectAllRows": "Alle Zeilen auswählen", + "selectRow": "{name} auswählen", + "editSkillNamed": "Skill {name} bearbeiten", + "deleteSkillNamed": "Skill {name} löschen", + "editCategoryNamed": "Kategorie {name} bearbeiten", + "deleteCategoryNamed": "Kategorie {name} löschen" }, "Auth": { "signInTitle": "Bei TakoAPI anmelden", @@ -462,12 +588,26 @@ "noAccount": "Noch kein Konto?", "signUpLink": "Registrieren", "haveAccount": "Bereits ein Konto?", - "signInLink": "Anmelden" + "signInLink": "Anmelden", + "forgotPassword": "Passwort vergessen?", + "forgotTitle": "Passwort zurücksetzen", + "forgotSubtitle": "Wir schicken dir per E-Mail einen Link für ein neues Passwort", + "sendResetLink": "Link zum Zurücksetzen senden", + "sendingResetLink": "Wird gesendet...", + "resetLinkSent": "Falls es zu dieser Adresse ein Konto gibt, haben wir einen Link zum Zurücksetzen geschickt. Sieh in deinem Postfach nach.", + "backToSignIn": "Zurück zur Anmeldung", + "resetTitle": "Neues Passwort wählen", + "resetSubtitle": "Gib ein neues Passwort für dein Konto ein", + "newPasswordHint": "Neues Passwort (mind. 8 Zeichen)", + "updatePassword": "Passwort aktualisieren", + "updatingPassword": "Wird aktualisiert...", + "passwordUpdated": "Dein Passwort wurde aktualisiert. Weiter zur Anmeldung...", + "resetLinkInvalid": "Dieser Link zum Zurücksetzen ist ungültig oder abgelaufen.", + "requestNewLink": "Neuen Link anfordern" }, "Dashboard": { "title": "Entwickler-Dashboard", "signInPrompt": "Melde dich an, um API-Schlüssel zu verwalten und die Nutzung einzusehen.", - "signIn": "Anmelden", "description": "Verwalte API-Schlüssel und überwache die Gateway-Nutzung. Ein Schlüssel, eine Rechnung, jeder Agent.", "apiKeys": "API-Schlüssel", "createKey": "Schlüssel erstellen", @@ -497,11 +637,10 @@ "billing": "Guthaben & Abrechnung", "creditBalance": "Guthaben", "totalSpend": "Gesamtausgaben", - "topUpComingSoon": "Guthaben wird vorerst von der Plattform aufgeladen – Self-Service-Aufladung über PayPal folgt in Kürze.", "topUpLabel": "Guthaben aufladen", "topUpButton": "Mit PayPal aufladen", "topUpRedirecting": "Weiterleitung…", - "topUpSuccess": "Guthaben gutgeschrieben – dein Saldo ist aktualisiert.", + "topUpSuccess": "Guthaben gutgeschrieben — dein Saldo ist aktualisiert.", "topUpError": "Aufladung nicht abgeschlossen. Bitte versuche es erneut.", "topUpCancel": "Aufladung abgebrochen.", "noLedger": "Noch keine Guthabenbewegungen.", @@ -510,38 +649,68 @@ "ledgerNote": "Notiz", "ledgerTopUp": "Aufladung", "ledgerTopUpFee": "Aufladegebühr", - "ledgerDebit": "Abrufgebühr", + "ledgerDebit": "Aufrufgebühr", "ledgerPayout": "Auszahlung", "ledgerRefund": "Rückerstattung", - "ledgerAdjust": "Anpassung" + "ledgerAdjust": "Anpassung", + "onboardingTitle": "Bring deinen ersten Aufruf zum Laufen", + "onboardingSubtitle": "Drei Schritte, bis dir ein Agent antwortet.", + "stepKeyTitle": "API-Schlüssel erstellen", + "stepKeyBody": "Ein Schlüssel ruft jeden Agenten auf TakoAPI auf. Das Secret wird nur einmal angezeigt, direkt nach dem Erstellen.", + "stepCallTitle": "Ersten Aufruf machen", + "stepCallBody": "Kopiere unten ein Beispiel, setze TAKO_KEY auf deinen Schlüssel und führe es aus. Der Aufruf erscheint innerhalb von Sekunden unter „Nutzung“.", + "stepInstallTitle": "In deinen Coding-Agenten installieren", + "stepInstallBody": "Bring Claude Code, Codex oder OpenCode bei, Agenten über TakoAPI zu finden und aufzurufen.", + "stepInstallLink": "Zur Installationsanleitung", + "stepOptional": "Optional", + "stepDone": "Erledigt", + "stepTodo": "Noch offen", + "keyNameLabel": "Schlüsselname", + "keyNamePlaceholder": "Gateway-Schlüssel", + "createKeyError": "Der Schlüssel konnte nicht erstellt werden. Bitte versuche es erneut.", + "revokeNamed": "{name} widerrufen", + "quickstartHint": "Setze TAKO_KEY auf einen Schlüssel von oben und rufe dann jeden Agenten über seinen Slug auf.", + "creditRule": "Kostenlose Agenten kosten nichts; kostenpflichtige Agenten belasten dein vorausbezahltes Guthaben, und ein Aufruf wird mit HTTP 402 abgelehnt, sobald der Saldo unter null fallen würde.", + "topUpUnavailable": "Self-Service-Aufladung gibt es noch nicht — Guthaben wird vorerst von der Plattform gutgeschrieben. Dein Saldo oben ist trotzdem aktuell.", + "dismiss": "Ausblenden", + "loadError": "Einige Dashboard-Daten konnten nicht geladen werden.", + "retry": "Erneut versuchen", + "recentCallsCaption": "Deine letzten Gateway-Aufrufe", + "chartTitle": "Aufrufe pro Tag, letzte {days} Tage", + "chartTotal": "{count, number} gesamt", + "chartAria": "Balkendiagramm der Gateway-Aufrufe pro Tag über die letzten {days} Tage, insgesamt {count, number} Aufrufe.", + "chartDay": "{date}: {calls, number} Aufrufe, {errors, number} fehlgeschlagen" }, "Profile": { - "title": "Mein Profil", - "signInPrompt": "Bitte melde dich an, um dein Profil anzusehen.", - "signIn": "Anmelden", - "submissions": "Einreichungen", + "title": "Meine Einträge", + "signInPrompt": "Melde dich an, um deine veröffentlichten Agenten und Skills zu sehen.", "likes": "Likes", "views": "Aufrufe", - "statValue": "{count, number}", - "mySubmissions": "Meine Einreichungen", - "submitNew": "Neu einreichen", "tab_all": "Alle", - "tab_approved": "Genehmigt", - "tab_pending": "Ausstehend", - "tab_rejected": "Abgelehnt", - "tabCount": "{count, number}", - "emptyTab": "Keine Einreichungen in diesem Tab", + "emptyTab": "Hier sind noch keine Skills.", "submitFirst": "Reiche deinen ersten Skill ein", - "statusApproved": "Genehmigt", - "statusPending": "Prüfung ausstehend", + "statusApproved": "Live", + "statusPending": "In Prüfung", "statusRejected": "Abgelehnt", - "reviewerNote": "Anmerkung des Prüfers:" + "reviewerNote": "Anmerkung des Prüfers:", + "publishAgent": "Agent veröffentlichen", + "submitSkill": "Skill einreichen", + "statAgents": "Agenten", + "statCalls": "Gateway-Aufrufe", + "statSkills": "Skills", + "tabAgents": "Agenten", + "tabSkills": "Skills", + "statusDisabled": "Deaktiviert", + "kindHosted": "Aufrufbar", + "kindProject": "Open Source", + "noAgents": "Hier sind noch keine Agenten.", + "publishFirstAgent": "Veröffentliche deinen ersten Agenten", + "myListings": "Meine Einträge" }, "Submit": { "title": "Einen Skill einreichen", "subtitle": "Teile deinen OpenClaw-Skill mit der Community", "signInPrompt": "Bitte melde dich an, um Skills einzureichen.", - "signIn": "Anmelden", "skillSource": "Skill-Quelle", "githubUrl": "GitHub-URL", "clawSkillsUrl": "ClawSkills.sh-URL", @@ -571,8 +740,15 @@ "submitting": "Wird eingereicht...", "enterUrlFirst": "Bitte gib zuerst eine URL ein", "autoFillFailed": "Automatisches Ausfüllen fehlgeschlagen", - "submitFailed": "Einreichen fehlgeschlagen", - "somethingWentWrong": "Etwas ist schiefgelaufen" + "somethingWentWrong": "Etwas ist schiefgelaufen", + "successTitle": "Eingereicht 🎉", + "pendingReview": "{name} wartet nun auf die Prüfung.", + "nextStepsTitle": "Wie es weitergeht", + "nextStepReview": "Ein Prüfer sieht sich das Repository und die Beschreibung an.", + "nextStepEmail": "Wir schicken dir eine E-Mail, sobald die Prüfung durch ist.", + "nextStepProfile": "Auf deiner Eintragsseite siehst du jederzeit den aktuellen Status und jede Anmerkung des Prüfers.", + "viewMyListings": "Meine Einträge ansehen", + "backToSkills": "← Zurück zu den Skills" }, "SubmitAgent": { "title": "Agent veröffentlichen", @@ -594,7 +770,7 @@ "protocolOpenAiCompat": "OpenAI-kompatibel", "categoryLabel": "Kategorie", "uncategorized": "Nicht kategorisiert", - "pricingLabel": "Preise", + "pricingLabel": "Preis", "pricingFree": "Kostenlos", "pricingPerCall": "Pro Aufruf", "pricingPerTask": "Pro Aufgabe", @@ -611,36 +787,69 @@ "successTitle": "Eingereicht 🎉", "pendingReview": "{name} wartet nun auf die Prüfung.", "successDetail": "Wir verifizieren die AgentCard und veröffentlichen sie nach der Genehmigung auf dem Marktplatz.", - "backToMarketplace": "← Zurück zum Marktplatz" + "backToMarketplace": "← Zurück zum Marktplatz", + "checkCard": "Card prüfen", + "checking": "Wird geprüft…", + "enterCardUrlFirst": "Gib zuerst eine AgentCard-URL ein.", + "previewTitle": "So würde der Eintrag aussehen", + "previewNoDescription": "Die Card enthält keine Beschreibung.", + "previewEndpoint": "Endpunkt", + "previewCapabilities": "Fähigkeiten", + "previewSkillCount": "{count, plural, =0 {Keine Fähigkeiten angegeben} one {# Fähigkeit} other {# Fähigkeiten}}", + "previewSupports": "Unterstützt", + "previewStreaming": "Streaming", + "previewPush": "Push-Benachrichtigungen", + "previewHint": "Noch ist nichts gespeichert — veröffentliche unten, um den Eintrag zur Prüfung zu schicken.", + "nextStepsTitle": "Wie es weitergeht", + "nextStepReview": "Ein Prüfer sieht sich die AgentCard an und prüft, ob dein Endpunkt antwortet.", + "nextStepEmail": "Wir schicken dir eine E-Mail, sobald die Prüfung durch ist.", + "nextStepProfile": "Auf deiner Eintragsseite siehst du jederzeit den aktuellen Status und jede Anmerkung des Prüfers.", + "viewMyListings": "Meine Einträge ansehen" }, "AgentEngagement": { "yourRating": "Deine Bewertung", "ratingsCount": "{count, number} Bewertungen", "noRatings": "Noch nicht bewertet", - "bookmark": "Merken", - "bookmarked": "Gemerkt", - "signInToRate": "Melde dich an, um zu bewerten oder zu speichern" + "bookmark": "Speichern", + "bookmarked": "Gespeichert", + "signInToRate": "Melde dich an, um diesen Agenten zu bewerten oder zu speichern", + "rateStars": "{count, plural, one {# Stern} other {# Sterne}} geben" }, "Bookmarks": { - "title": "Gespeicherte Agents", - "subtitle": "Von dir gemerkte Agents.", - "empty": "Noch keine Lesezeichen.", - "browseAgents": "Agents durchsuchen", - "signInPrompt": "Melde dich an, um deine Lesezeichen zu sehen.", - "signIn": "Anmelden" + "title": "Gespeicherte Agenten", + "subtitle": "Agenten, die du gespeichert hast.", + "empty": "Noch keine gespeicherten Agenten.", + "browseAgents": "Agenten durchsuchen", + "signInPrompt": "Melde dich an, um deine gespeicherten Agenten zu sehen." }, "ScenarioPage": { "metaTitle": "{scenario}-KI-Agenten", - "metaDescription": "Entdecke und rufe {scenario}-KI-Agenten über eine einheitliche API auf – beschrieben durch offene A2A-AgentCards auf TakoAPI.", + "metaDescription": "Entdecke und rufe {scenario}-KI-Agenten über eine einheitliche API auf — beschrieben durch offene A2A-AgentCards auf TakoAPI.", "heroSubtitle": "KI-Agenten für {scenario}, aufrufbar über eine einheitliche API.", "empty": "Noch keine Agenten in diesem Szenario.", "browseAll": "Alle Agenten durchsuchen", "back": "← Alle Szenarien", "breadcrumbScenarios": "Szenarien", "indexTitle": "KI-Agenten nach Szenario durchsuchen", - "indexDescription": "Finde KI-Agenten nach Einsatzzweck – Frontend, Investing, Marketing, Support und mehr – alle über eine API aufrufbar.", + "indexDescription": "Finde KI-Agenten nach Einsatzzweck — Frontend, Investing, Marketing, Support und mehr — alle über eine API aufrufbar.", "indexHeroTitle": "Nach Szenario durchsuchen", - "indexHeroSubtitle": "Finde KI-Agenten nach der Aufgabe, die du erledigen willst." + "indexHeroSubtitle": "Finde KI-Agenten nach der Aufgabe, die du erledigen willst.", + "indexPopularHeading": "Gut abgedeckt", + "indexPopularHint": "Szenarien mit einem großen Katalog an Agenten zum Vergleichen.", + "indexSparseHeading": "Ganz am Anfang", + "indexSparseHint": "Bisher nur eine Handvoll Agenten — hier bist du leicht der Erste.", + "indexSparseCta": "Veröffentliche einen Agenten in einem davon →", + "metaTitlePaged": "{scenario}-KI-Agenten — Seite {page}", + "resultsLabel": "{scenario}-Agenten", + "paginationLabel": "Seitennavigation", + "showingRange": "Ergebnisse {from}–{to} von {total}", + "prev": "← Zurück", + "next": "Weiter →", + "goToPage": "Zu Seite {page}", + "ctaTitle": "Kennst du einen Agenten für {scenario}?", + "ctaBody": "Dieses Szenario ist noch dünn besetzt. Veröffentliche einen Agenten, und er erscheint hier für alle, die {scenario} durchsuchen.", + "ctaButton": "Agent veröffentlichen", + "relatedHeading": "Andere Szenarien entdecken" }, "Badge": { "snippetTitle": "Badge hinzufügen", @@ -648,15 +857,16 @@ "copied": "Kopiert", "snippetHint": "Füge es in dein README ein, um auf deinen TakoAPI-Eintrag zu verlinken.", "pageTitle": "TakoAPI-README-Badge", - "pageIntro": "Zeige, dass dein Projekt auf TakoAPI gelistet ist – ein kleines Badge, das auf deinen Eintrag verlinkt und deine aktuelle Sternezahl anzeigt.", + "pageIntro": "Zeige, dass dein Projekt auf TakoAPI gelistet ist — ein kleines Badge, das auf deinen Eintrag verlinkt und deine aktuelle Sternezahl anzeigt.", "example": "Beispiel", "step1Title": "Eintrag finden", - "step1": "Suche im Agent-Verzeichnis nach deinem Projekt.", + "step1": "Suche im Agenten-Verzeichnis nach deinem Projekt.", "step2Title": "Badge kopieren", "step2": "Kopiere das Markdown aus der Badge-Box auf deiner Eintragsseite.", "step3Title": "In dein README einfügen", - "step3": "Füge es oben ein – Besucher und Suchmaschinen erhalten einen Link zu deiner TakoAPI-Seite.", - "findListing": "Eintrag finden" + "step3": "Füge es weit oben ein — Besucher und Suchmaschinen bekommen einen Link zu deiner TakoAPI-Seite.", + "findListing": "Eintrag finden", + "exampleAlt": "TakoAPI-Badge" }, "Blog": { "title": "Blog", @@ -666,5 +876,149 @@ "minRead": "{min} Min. Lesezeit", "published": "Veröffentlicht", "faqTitle": "FAQ" + }, + "NotFound": { + "title": "Diese Seite haben wir nicht gefunden", + "description": "Der Link ist womöglich veraltet, oder der Eintrag wurde entfernt. Versuch es über das Verzeichnis oder die Suche oben.", + "home": "Start", + "agents": "Agenten durchsuchen", + "skills": "Skills durchsuchen" + }, + "ErrorPage": { + "title": "Etwas ist schiefgelaufen", + "description": "Diese Seite konnte nicht geladen werden. Ein zweiter Versuch hilft oft; falls nicht, hilft uns die Referenz unten beim Suchen in den Logs.", + "reference": "Referenz: {id}", + "retry": "Erneut versuchen", + "home": "Zur Startseite" + }, + "Consent": { + "title": "Analytics", + "description": "Wir nutzen Google Analytics, um zu sehen, welche Seiten nützlich sind. Ohne deine Zustimmung wird nichts geladen und es werden keine Cookies gesetzt.", + "accept": "Akzeptieren", + "decline": "Ablehnen" + }, + "Docs": { + "metaTitle": "API-Dokumentation", + "title": "API-Referenz von TakoAPI", + "description": "Vom API-Schlüssel zum funktionierenden Agenten-Aufruf: Authentifizierung, die Discovery-Endpunkte, das einheitliche Agenten-Gateway, der gehostete MCP-Server, Fehlercodes, Rate-Limits und Abrechnung.", + "heroBadge": "Entwickler-Doku", + "heroTitle": "Die TakoAPI-API-Referenz", + "heroSubtitle": "Ein Schlüssel, alle Agenten. Richte einen beliebigen OpenAI-kompatiblen Client auf TakoAPI und rufe einen Agenten über seinen Slug auf — oder durchsuche den gesamten Katalog ganz ohne Schlüssel.", + "heroKeyCta": "API-Schlüssel erstellen", + "heroBrowseCta": "Agenten durchsuchen", + "tocTitle": "Auf dieser Seite", + "tocLabel": "Inhaltsverzeichnis", + "quickstartTitle": "Schnellstart", + "quickstartIntro": "Drei Schritte von null zur Antwort. Jede Anfrage auf dieser Seite ist ein echter Aufruf gegen die Live-API.", + "quickstartStep1Title": "Schlüssel erstellen", + "quickstartStep1": "Melde dich an und erstelle einen API-Schlüssel in deinem Dashboard. Das Secret wird nur einmal angezeigt, beim Erstellen.", + "quickstartStep2Title": "Exportieren", + "quickstartStep2": "Jedes Beispiel auf dieser Seite liest den Schlüssel aus TAKO_KEY.", + "quickstartStep3Title": "Agenten aufrufen", + "quickstartStep3": "Setze das Modell — oder den Slug in der URL — auf einen beliebigen gehosteten Agenten aus dem Katalog.", + "quickstartSamplesLabel": "Beispielaufrufe für den Schnellstart", + "quickstartNote": "Der Slug in diesen Beispielen ist ein Platzhalter, kein echter Agent. Kopiere einen echten von einer beliebigen Agenten-Seite; aufrufbar sind nur Agenten, die als gehostet gelistet sind.", + "authenticationTitle": "Authentifizierung", + "authenticationIntro": "Gateway-Aufrufe werden mit einem Bearer-Token authentifiziert. Die Discovery-Endpunkte sind anonym und brauchen gar keinen Schlüssel.", + "authHeaderNote": "x-api-key wird als Alternative zum Authorization-Header akzeptiert. Schlüssel beginnen immer mit tako_live_.", + "authWhereTitle": "Woher Schlüssel kommen", + "authWhere": "Erstelle in deinem Dashboard einen Schlüssel pro Client oder Umgebung und widerrufe die, die du nicht mehr nutzt.", + "authOnceTitle": "Das Secret wird nur einmal angezeigt", + "authOnce": "Gespeichert werden nur ein SHA-256-Hash des Schlüssels und ein kurzes Anzeige-Präfix; ein verlorenes Secret lässt sich daher nicht wiederherstellen — erstelle einen Ersatz und widerrufe den alten Schlüssel.", + "authRevokeTitle": "Widerrufen wirkt sofort", + "authRevoke": "Jeder Aufruf prüft den Schlüssel erneut gegen die Datenbank, nichts wird zwischengespeichert — ein widerrufener Schlüssel wird schon bei der nächsten Anfrage abgelehnt. Die bisherige Nutzung bleibt in deiner Historie.", + "discoveryTitle": "Discovery", + "discoveryIntro": "Vier öffentliche, cachebare Endpunkte ohne Schlüssel. Zurückgegeben werden ausschließlich genehmigte Agenten und Skills.", + "endpointRegistrySummary": "Der gesamte Agenten-Katalog — Markdown für ein Modell, JSON für Code.", + "endpointRegistryNote": "Ohne format=json ist die Antwort Markdown (text/markdown), aufgeteilt in eine Tabelle mit gehosteten Agenten und eine mit Open-Source-Projekten und so bemessen, dass sie direkt in den Kontext eines Modells passt — genau das liest der installierte TakoAPI-Skill. Beide Formate sind durch limit begrenzt; prüfe also truncated und die Gesamtzahlen, bevor du annimmst, alles zu haben. Freitext-Parameter werden auf 100 Zeichen gekürzt.", + "paramRegistryFormat": "md (Standard) oder json.", + "paramRegistryQ": "Freitext-Treffer in Agentenname und Beschreibung.", + "paramRegistryCategory": "Kategorie-Slug.", + "paramRegistryProtocol": "Gibt nur Agenten zurück, die dieses Protokoll angeben.", + "paramRegistryKind": "HOSTED-Agenten sind über das Gateway aufrufbar; PROJECT-Einträge sind Open-Source-Repositories, die nur zum Entdecken gelistet sind.", + "paramRegistrySort": "Sortierung. Standardmäßig zuerst empfohlene, dann meistaufgerufene, dann neueste — und bei Projekten die mit den meisten Sternen.", + "paramRegistryLimit": "Gesamtzahl der zurückgegebenen Einträge. Zuerst werden gehostete Agenten aufgefüllt, dann Projekte.", + "endpointAgentDetailSummary": "Alle öffentlichen Details zu einem Agenten, inklusive der angegebenen A2A-Skills.", + "endpointAgentDetailNote": "Nur genehmigte Agenten werden aufgelöst; alles andere ist ein 404. Moderationsfelder — Prüfnotizen, interne IDs und Anbieter-Avatare — sind nie Teil dieser Struktur. Wird 60 Sekunden am Edge gecacht.", + "paramAgentDetailSlug": "Pfadsegment. Der Slug des Agenten, wie ihn die Registry zurückgibt.", + "endpointSkillsDirectorySummary": "Der OpenClaw-Skills-Katalog, standardmäßig als Markdown.", + "endpointSkillsDirectoryNote": "Markdown zuerst, wie bei der Registry. install ist null für Skills ohne veröffentlichten Installationsbefehl — greif dann auf den github-Link zurück. downloads ist ein Wert von Dritten, ausgelesen aus dem Eintrag des Skills selbst, und keine Installationszahl von TakoAPI.", + "paramSkillsDirFormat": "md (Standard) oder json.", + "paramSkillsDirQ": "Freitext-Treffer in Skill-Name und Beschreibung.", + "paramSkillsDirCategory": "Kategorie-Slug.", + "paramSkillsDirPage": "Seitenzahl, beginnend bei 1.", + "paramSkillsDirLimit": "Ergebnisse pro Seite.", + "endpointSkillsSearchSummary": "Volltextsuche im gesamten Skills-Katalog, nur JSON.", + "endpointSkillsSearchNote": "q ist erforderlich: ohne den Parameter liefert der Endpunkt eine leere Liste statt des gesamten Katalogs. Jedes Ergebnis ist die vollständige Katalogzeile ohne readme und reviewNote; das Beispiel oben ist gekürzt.", + "paramSkillsSearchQ": "Suchbegriff. Trifft Skill-Name, Beschreibung und Autor.", + "paramSkillsSearchCategory": "Kategorie-Slug.", + "paramSkillsSearchPage": "Seitenzahl, beginnend bei 1.", + "paramSkillsSearchLimit": "Ergebnisse pro Seite.", + "gatewayTitle": "Gateway", + "gatewayIntro": "Drei Wege, denselben Agenten aufzurufen. Alle drei brauchen einen Schlüssel, rechnen identisch ab und nehmen den Slug des Agenten dort entgegen, wo sonst ein Modellname steht. Aufrufbar sind nur Agenten vom Typ HOSTED.", + "endpointChatCompletionsSummary": "OpenAI-kompatibel. Richte ein beliebiges OpenAI-SDK auf /v1 und setze model auf einen Agenten-Slug.", + "endpointChatCompletionsNote": "Die gesamte Unterhaltung wird weitergereicht: ein A2A-Textteil pro Zug, in Reihenfolge, mit der jeweiligen OpenAI-Rolle am Teil. usage ist immer vorhanden, aber immer null — dieses Gateway zählt keine Tokens, und ein Agent muss kein LLM sein. Setze stream: true für die SSE-Form oben und stream_options.include_usage, um zusätzlich den abschließenden usage-Chunk zu bekommen. Ein Aufruf ohne Streaming läuft nach 30 Sekunden ab, ein Stream nach 120.", + "endpointA2AMessageSummary": "Nativer A2A-Passthrough. Gibt die JSON-RPC-Antwort des Agenten unverändert zurück.", + "endpointA2AMessageNote": "Wird als A2A-Aufruf message/send weitergereicht. Schick die Text-Kurzform oder eine vollständige A2A-Nachricht — mit parts, taskId und contextId —, um eine mehrstufige Aufgabe fortzusetzen oder Datei- und Datenteile durchzureichen. Statuscode und Body des Agenten werden unverändert weitergereicht, und eine Anfrage ohne Inhalt wird mit einem 400 abgelehnt statt abgerechnet. Läuft nach 30 Sekunden ab.", + "endpointA2AStreamSummary": "Derselbe Aufruf über Server-Sent Events, für Agenten, die streamen.", + "endpointA2AStreamNote": "Reicht die A2A-Frames message/stream des Agenten wortgetreu als text/event-stream weiter. Dieser Endpunkt nimmt nur die Text-Kurzform. Die Verbindung wird nach 120 Sekunden gekappt, und der Aufruf wird erst abgerechnet, wenn der Stream sein Ende erreicht — siehe Rate-Limits und Abrechnung.", + "labelQueryParams": "Parameter", + "labelRequest": "Request-Body", + "labelExample": "Beispiel", + "labelResponse": "Response", + "labelStreaming": "Response mit stream: true", + "labelA2AMessageForm": "Vollständige A2A-Nachrichtenform", + "labelAuthRequired": "Schlüssel nötig", + "labelNoAuth": "Kein Schlüssel", + "labelBillable": "Kostenpflichtig", + "labelFree": "Kostenlos", + "labelParamName": "Name", + "labelParamType": "Typ", + "labelParamDesc": "Beschreibung", + "paramsTableCaption": "Parameter für {path}", + "mcpTitle": "MCP-Server", + "mcpIntro": "TakoAPI betreibt unter {url} einen gehosteten, zustandslosen Model-Context-Protocol-Server über Streamable HTTP. Registriere ihn einmal, und dein Coding-Agent kann den Katalog als native Tools durchsuchen und aufrufen.", + "mcpRegisterTitle": "Registrieren", + "mcpRegisterLabel": "Befehle zur MCP-Registrierung", + "mcpAuthNote": "Die drei Lese-Tools funktionieren anonym. invoke_agent verbraucht Guthaben und braucht deshalb deinen Schlüssel: übergib ihn als Authorization-Header (Claude Code), als bearer_token_env_var (Codex) oder in einem headers-Block (OpenCode).", + "mcpToolsTitle": "Tools", + "mcpToolsTableCaption": "Tools, die der TakoAPI-MCP-Server bereitstellt", + "mcpToolColumn": "Tool", + "mcpToolAccessColumn": "Zugriff", + "mcpToolWhatColumn": "Funktion", + "mcpToolKeyRequired": "Schlüssel nötig", + "mcpToolAnonymous": "Anonym", + "mcpToolSearchAgents": "Durchsucht die Registry nach Stichwort, Kategorie oder Protokoll. Liefert Slugs, Protokolle und Preise.", + "mcpToolGetAgent": "Alles, was zu einem Agenten per Slug bekannt ist — Fähigkeiten, Protokolle, Endpunkt, Preise und angegebene Skills.", + "mcpToolSearchSkills": "Durchsucht den OpenClaw-Skills-Katalog. Ohne Suchbegriff kommen die kuratierten Top-Skills zurück.", + "mcpToolInvokeAgent": "Ruft einen Agenten über das Gateway auf und gibt seine Antwort zurück. Braucht einen Schlüssel und verbraucht Guthaben.", + "mcpRateNote": "Der MCP-Endpunkt ist auf 120 Anfragen pro Minute und IP-Adresse begrenzt, getrennt vom Gateway-Kontingent deines Schlüssels.", + "mcpInstallNote": "Lieber eine Skill-Datei als einen Server? Der Installer bringt Claude Code, Codex und OpenCode bei, diese Endpunkte stattdessen über einfaches HTTP aufzurufen.", + "errorsTitle": "Fehler", + "errorsIntro": "Jeder Gateway-Fehler ist JSON. Das sind die Status, die das Gateway selbst zurückgibt.", + "errorsTableCaption": "Fehlerstatus, die das Gateway zurückgibt", + "errorStatusColumn": "Status", + "errorCodeColumn": "Code", + "errorWhenColumn": "Wann", + "errorBodyColumn": "Beispiel-Body", + "error400When": "Der Slug bezeichnet ein Open-Source-Projekt ohne aufrufbaren Endpunkt, der Request-Body hat die Validierung nicht bestanden, oder alle Nachrichten-Züge waren leer.", + "error401When": "Der Schlüssel fehlt, ist fehlerhaft, unbekannt oder widerrufen.", + "error402When": "Der Agent ist kostenpflichtig, und die Abrechnung des Aufrufs würde deinen Saldo unter null bringen. Wird geprüft, bevor der Agent kontaktiert wird — es wird also nichts ausgegeben.", + "error404When": "Kein genehmigter Agent hat diesen Slug.", + "error429When": "Der Schlüssel hat sein Kontingent in den letzten 60 Sekunden überschritten. Retry-After gibt die Sekunden bis zum Zurücksetzen des Fensters an.", + "error502When": "Der Agent hat nicht geantwortet, ist in einen Timeout gelaufen, hat keinen Stream geliefert oder einen JSON-RPC-Fehler über ein HTTP 200 gemeldet. Wird nie abgerechnet.", + "errorsOpenAiNote": "Unter /v1/chat/completions kommen dieselben Fehler in OpenAIs Envelope an — mit stets vorhandenem type, param und code —, sodass ein OpenAI-SDK seine üblichen Exception-Typen wirft:", + "errorsPassthroughNote": "Einen Status, den der Agent selbst zurückgegeben hat, reicht der A2A-Endpunkt message unverändert durch — ein 4xx dort kann also vom Agenten stammen und nicht von uns.", + "limitsTitle": "Rate-Limits & Abrechnung", + "limitsRateTitle": "Rate-Limits", + "limitsRate": "Das Gateway erlaubt 120 Anfragen pro 60 Sekunden und API-Schlüssel. Das Fenster gilt über alle Serverinstanzen hinweg und hängt am Schlüssel selbst statt an deiner IP-Adresse — es wird also unter Last nicht großzügiger und hinter einem gemeinsamen NAT nicht strenger.", + "limitsRateOverride": "Ein Schlüssel kann ein eigenes Kontingent haben, das den Standard nur für diesen Schlüssel ersetzt. Über dem Limit bekommst du ein 429 mit einem Retry-After-Header in Sekunden.", + "limitsBillingTitle": "Abrechnung", + "limitsBillingFree": "Agenten mit dem Preis FREE kosten nichts — ebenso jeder Agent, dessen Stückpreis fehlt oder null ist. Das Gateway erfindet nie eine Gebühr.", + "limitsBillingPriced": "Ein kostenpflichtiger Agent belastet dein vorausbezahltes Guthaben einmal pro Aufruf mit seinem Stückpreis. Es gibt keine Abrechnung auf Token-Ebene — ein Aufruf ist eine Einheit, egal wie lange er läuft.", + "limitsBillingPreflight": "Dein Saldo wird geprüft, bevor der Agent kontaktiert wird. Würde die Gebühr dich unter null bringen, wird der Aufruf mit einem 402 abgelehnt und der Agent nie aufgerufen — es gibt keine Überziehung.", + "limitsBillingFailures": "Fehlgeschlagene Aufrufe werden nicht abgerechnet. Ein 4xx oder 5xx vom Agenten, ein Timeout, ein nicht erreichbarer Endpunkt oder ein JSON-RPC-Fehler halten den Versuch fest und kosten nichts.", + "limitsBillingStreams": "Streams werden nur abgerechnet, wenn sie vollständig durchlaufen. Ein Stream, der abbricht, in einen Timeout läuft, mittendrin fehlschlägt oder den du früh trennst, wird erfasst und nicht abgerechnet.", + "limitsBillingLedger": "Jede Gebühr schreibt einen unveränderlichen Ledger-Eintrag zum verursachenden Aufruf. Saldo, Ledger und die Nutzung pro Aufruf findest du in deinem Dashboard." } } diff --git a/messages/en.json b/messages/en.json index 3a08af2f9..e8515fa8e 100644 --- a/messages/en.json +++ b/messages/en.json @@ -7,60 +7,78 @@ "label": "Language" }, "Header": { - "searchPlaceholder": "Search agents...", "agents": "Agents", "skills": "Skills", - "trending": "Trending", - "publish": "Publish", - "publishAgent": "Publish Agent", + "publishAgent": "Publish an agent", "account": "Account", - "mySkills": "My Skills", - "myBookmarks": "Bookmarks", - "apiUsage": "API & Usage", - "adminPanel": "Admin Panel", + "myBookmarks": "Saved agents", + "apiUsage": "API keys & usage", + "adminPanel": "Admin panel", "signIn": "Sign in", - "signOut": "Sign out" + "signOut": "Sign out", + "mainNav": "Main", + "skipToContent": "Skip to content", + "openMenu": "Open menu", + "closeMenu": "Close menu", + "projects": "Projects", + "install": "Install", + "getApiKey": "Get API key", + "search": "Search", + "searchScope": "Search in", + "scopeAgents": "Agents", + "scopeProjects": "Projects", + "scopeSkills": "Skills", + "searchAgentsPlaceholder": "Search agents by what they do…", + "searchProjectsPlaceholder": "Search open-source agent projects…", + "searchSkillsPlaceholder": "Search skills for your coding agent…", + "myListings": "My listings", + "docs": "Docs" }, "Footer": { - "tagline": "One API to access all agents — plus thousands of OpenClaw skills for your coding agent.", + "tagline": "One API to access all agents — discover, call, and meter any agent through a single endpoint.", "agentRegistry": "Agent registry:", "platform": "Platform", "resources": "Resources", - "installInAgent": "Install in your agent", - "browseAgents": "Browse Agents", - "publishAnAgent": "Publish an Agent", - "developerDashboard": "Developer Dashboard", - "browseSkills": "Browse Skills", - "awesomeList": "Awesome List", + "installInAgent": "Install in your coding agent", + "browseAgents": "Browse agents", + "publishAnAgent": "Publish an agent", + "developerDashboard": "API keys & usage", + "browseSkills": "Browse skills", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Built for the OpenClaw community.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. MIT licensed.", + "blog": "Blog", + "badge": "README badge", + "scenarios": "Browse by scenario", + "a2aSpec": "A2A protocol", + "github": "GitHub", + "docs": "API documentation" }, "Home": { "heroTitle": "One API to access all agents", - "heroSubtitle": "Discover and invoke {agents, number} AI agents through one unified API — plus {skills, number} skills for your coding agent.", + "heroSubtitle": "Discover AI agents, then call any of them through one endpoint with one key. A2A native, OpenAI-compatible, metered per call.", "browseAgents": "Browse agents", - "publishAnAgent": "Publish an agent", - "agentRegistry": "Agent registry:", - "browseByScenario": "Browse by scenario", - "allAgents": "All agents", - "featuredAgents": "Featured Agents", + "browseByScenario": "Browse by what you need done", + "featuredAgents": "Featured agents", "browseAll": "Browse all", "popularProjects": "Popular open-source projects", "selfHostable": "{count, number} self-hostable", - "skillTitle": "TakoAPI Skill for OpenClaw", - "official": "Official", - "skillDescription": "Search, install, and manage OpenClaw skills through natural conversation — powered by the TakoAPI marketplace.", - "viewOnGithub": "View on GitHub", - "askAgent": "or ask your agent: \"Install the TakoAPI skill\"", - "usingCodingAgent": "Using Claude Code, Codex or OpenCode?", - "oneCommandInstall": "One-command install", - "mustHaveSkills": "Must-Have Skills", - "viewRankings": "View rankings", - "categories": "Categories", - "viewAll": "View all", + "categories": "Categories:", "moreCategories": "{count, plural, one {+# more category} other {+# more categories}}", - "latestSkills": "Latest Skills" + "getApiKey": "Get an API key", + "catalogSummary": "{agents, number} callable agents · {projects, number} open-source projects · {skills, number} coding-agent skills", + "quickstartLabel": "30 seconds to your first call", + "quickstartNote": "Prefer to work from your terminal? Install TakoAPI into your coding agent.", + "allScenarios": "All scenarios", + "callableNow": "{count, number} callable through the gateway", + "installTitle": "Use TakoAPI from your coding agent", + "installDescription": "One command teaches Claude Code, Codex or OpenCode to search the directory and call agents for you — or register the hosted MCP server instead.", + "installCta": "Installation guide", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Skills for your coding agent", + "skillsSubtitle": "{count, number} OpenClaw skills, most-installed and newest.", + "quickstartTabDiscover": "1. Discover", + "quickstartTabCall": "2. Call", + "quickstartTabFromAgent": "3. From your agent" }, "Scenarios": { "frontend": "Frontend", @@ -85,8 +103,6 @@ "notFound": "Agent not found", "backToMarketplace": "← Agent Marketplace", "openSourceProject": "Open-source project", - "signedCard": "Signed card", - "verifiedPublisher": "Verified publisher", "by": "by {name}", "publisherOpenSource": "open-source", "publisherUnknown": "unknown", @@ -101,17 +117,41 @@ "noSkillsHosted": "This agent did not advertise structured skills in its AgentCard.", "inputMode": "in: {mode}", "outputMode": "out: {mode}", - "openSourceProjectHeading": "Open-source project", "openSourceProjectNote": "Self-host or explore the code. Not invokable through the TakoAPI gateway.", "viewOnGithub": "View on GitHub", - "integration": "Integration", "endpoint": "Endpoint", "agentCard": "AgentCard", "homepage": "Homepage", "callThroughTakoApi": "Call it through TakoAPI", - "gatewayPhase2": "Unified gateway — invocation ships in Phase 2.", "breadcrumbHome": "Home", - "breadcrumbMarketplace": "Agent Marketplace" + "breadcrumbMarketplace": "Agent Marketplace", + "gatewayHint": "Call it with your API key — create one in the dashboard.", + "healthOk": "Responding", + "healthDegraded": "Degraded", + "healthDown": "Not responding", + "healthCheckedAt": "Last checked {time}", + "atAGlance": "At a glance", + "protocols": "Protocols", + "protocolsNone": "Not declared", + "yes": "Yes", + "no": "No", + "pricing": "Pricing", + "health": "Health", + "healthUnknown": "Not checked yet", + "totalCalls": "Total calls", + "numberValue": "{count, number}", + "averageRating": "Average rating", + "ratingValue": "{rating} / 5 · {count, number} ratings", + "notRated": "Not yet rated", + "callExamplesLabel": "Call examples", + "repository": "Repository", + "language": "Language", + "license": "Licence", + "licenseNone": "Not specified", + "lastPush": "Last push", + "topics": "Topics", + "readmeHeading": "From the README", + "readmeNote": "Excerpt from the project's README on GitHub." }, "Agents": { "metaTitle": "Agent Marketplace", @@ -145,11 +185,24 @@ "emptyCta": "Publish the first one →", "prev": "← Prev", "next": "Next →", - "pageOf": "Page {page} of {total}" + "pageOf": "Page {page} of {total}", + "filtersTitle": "Filters", + "filtersWithCount": "Filters ({count})", + "activeFiltersLabel": "Active filters:", + "removeFilter": "Remove filter: {label}", + "clearAll": "Clear all", + "kindLabel": "Type", + "scenarioGroup": "Scenario", + "protocolGroup": "Protocol", + "pricingGroup": "Pricing", + "categoryGroup": "Category", + "searchChip": "Search: {query}", + "resultsLabel": "Results", + "paginationLabel": "Pagination", + "goToPage": "Go to page {page}", + "showingRange": "Showing {from}–{to} of {total}" }, "Components": { - "homeSearchPlaceholder": "Search skills by name, description, or author...", - "homeSearchButton": "Search", "agentFree": "Free", "agentPerCall": "{price}/call", "agentPerTask": "{price}/task", @@ -163,7 +216,11 @@ "agentSelfHost": "self-host", "agentSkills": "{count, plural, one {# skill} other {# skills}}", "skillBy": "by {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} calls", + "healthOk": "Responding", + "healthDegraded": "Degraded", + "healthDown": "Not responding" }, "Install": { "metaTitle": "Install in Claude Code, Codex & OpenCode", @@ -196,7 +253,7 @@ "howToUseHeading": "How to use", "claudeCodeUse": "Loads automatically — just ask Claude to find an agent on TakoAPI.", "codexUse": "Invoke with $takoapi, or pick it from the /skills menu.", - "opencodeUse": "Run /takoapi , or hand off to @takoapi.", + "opencodeUse": "Run /takoapi '', or hand off to @takoapi.", "mcpHeading": "MCP server — hosted tools, nothing to install", "claudeCodeMcpHint": "One command. Add --header \"Authorization: Bearer $TAKO_KEY\" to enable invoke_agent.", "codexMcpHint": "Add to ~/.codex/config.toml", @@ -226,12 +283,17 @@ "requirements": "Requirements", "installation": "Installation", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClaw Chat (paste this into your conversation)", "links": "Links", "github": "GitHub", - "copyGithubUrl": "Copy GitHub URL", "clawHub": "ClawHub", - "skillNotFound": "Skill not found" + "codingAgent": "Coding agent", + "agentPrompt": "Install the skill from {url}", + "installHintRegistry": "Run the CLI command, or paste the prompt into Claude Code, Cursor, Codex or whichever coding agent you use.", + "installHintRepo": "This skill is not published on the ClawHub registry. Paste the prompt into your coding agent — Claude Code, Cursor, Codex — and it will install the skill straight from the source repository.", + "installUnavailable": "No install source has been published for this skill yet.", + "openRepository": "Open the repository", + "signInToLike": "Sign in to like this skill", + "downloadsSource": "Downloads reported by clawskills.sh" }, "Skills": { "metaTitle": "Browse Skills", @@ -257,13 +319,32 @@ "all": "All", "emptyTitle": "No skills found", "emptyHint": "Try adjusting your search or filters", - "pageOf": "Page {page} of {total}" + "pageOf": "Page {page} of {total}", + "metaTitleSearch": "Skills matching \"{query}\"", + "metaTitlePaged": "Browse Skills — page {page}", + "searchInputLabel": "Search skills", + "searchPlaceholder": "Search skills by name, description or author", + "searchButton": "Search", + "agentFilter": "Agent", + "agentZed": "Zed", + "sourceFilter": "Source", + "sourceGithub": "GitHub", + "sourceCommunity": "Community", + "sourceCurated": "Curated", + "categoryFilter": "Category", + "sortLabel": "Sort", + "sortRelevance": "Best match", + "clearFilters": "Clear filters", + "paginationLabel": "Skill listing pages", + "goToPage": "Go to page {page}", + "prev": "Previous", + "next": "Next" }, "Trending": { "metaTitle": "Trending Skills", - "metaDescription": "The most-downloaded and most-liked OpenClaw skills right now — a live leaderboard of what coding agents are installing.", + "metaDescription": "The most-downloaded and most-liked OpenClaw skills — a leaderboard built from download counts reported by clawskills.sh, refreshed daily.", "ogTitle": "Trending Skills — {siteName}", - "ogDescription": "A live leaderboard of the most popular OpenClaw skills.", + "ogDescription": "A daily leaderboard of the most popular OpenClaw skills.", "title": "Trending Skills", "subtitle": "Discover the most popular OpenClaw skills ranked by the community", "searchPlaceholder": "Search trending skills…", @@ -281,7 +362,13 @@ "byAuthor": "by {author}", "showing": "Showing {from, number}–{to, number} of {total, number}", "prev": "Prev", - "next": "Next" + "next": "Next", + "provenanceNote": "Download and star counts come from the upstream catalogue — clawskills.sh and GitHub — and are refreshed nightly; they do not measure installs through TakoAPI. Likes and views are counted here.", + "search": "Search", + "sortLabel": "Sort by", + "rank": "Rank", + "goToPage": "Go to page {page}", + "paginationLabel": "Leaderboard pages" }, "Admin": { "accessDenied": "Access Denied", @@ -335,8 +422,8 @@ "actionReject": "Reject", "feature": "Feature", "unfeature": "Unfeature", - "scenariosEditorHint": "场景 Scenarios — 点击切换 / toggle to assign", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Scenarios — click to assign or remove", + "scenarioOption": "{name}", "selectedCount": "{count, number} selected", "rejectReasonPrompt": "Reason for rejection (optional):", "deleteAgentConfirm": "Permanently delete this agent?", @@ -436,7 +523,46 @@ "targetSkill": "skill", "targetAgent": "agent", "targetCategory": "category", - "targetUser": "user" + "targetUser": "user", + "adminNavLabel": "Admin sections", + "retry": "Retry", + "close": "Close", + "prevPage": "Previous page", + "nextPage": "Next page", + "sectionAgents": "Agents", + "sectionSkills": "Skills & community", + "statTotalAgents": "Total Agents", + "statPendingAgents": "Pending Agents", + "statHostedAgents": "Hosted", + "statProjectAgents": "Projects", + "pendingAgentsTitle": "Agents awaiting review", + "noPendingAgents": "No agents are waiting for review.", + "kindHosted": "Hosted", + "kindProject": "Project", + "filterByStatus": "Filter by status", + "filterDisabled": "Disabled", + "actionDisable": "Disable", + "actionEnable": "Re-enable", + "disableAgentTitle": "Disable Agent", + "disableAgentConfirm": "{name} will be hidden from the public catalog until you re-enable it. Nothing is deleted.", + "deleteAgentTitle": "Delete Agent", + "deleteAgentConfirmNamed": "Are you sure you want to delete {name}? This action cannot be undone.", + "rejectAgentTitle": "Reject Agent", + "rejectReasonLabel": "Reason (optional, shown to the publisher)", + "rejectReasonPlaceholder": "e.g. the endpoint returns 500 on the sample task", + "healthOk": "Healthy", + "healthDegraded": "Degraded", + "healthDown": "Down", + "healthUnknown": "Unchecked", + "healthCheckedAt": "Last health check: {date}", + "healthNeverChecked": "No health check recorded", + "batchDeleteTitle": "Delete Skills", + "selectAllRows": "Select all rows", + "selectRow": "Select {name}", + "editSkillNamed": "Edit skill {name}", + "deleteSkillNamed": "Delete skill {name}", + "editCategoryNamed": "Edit category {name}", + "deleteCategoryNamed": "Delete category {name}" }, "Auth": { "signInTitle": "Sign in to TakoAPI", @@ -462,12 +588,26 @@ "noAccount": "Don't have an account?", "signUpLink": "Sign up", "haveAccount": "Already have an account?", - "signInLink": "Sign in" + "signInLink": "Sign in", + "forgotPassword": "Forgot your password?", + "forgotTitle": "Reset your password", + "forgotSubtitle": "We'll email you a link to choose a new one", + "sendResetLink": "Send reset link", + "sendingResetLink": "Sending...", + "resetLinkSent": "If that address has an account, we've sent a password reset link. Check your inbox.", + "backToSignIn": "Back to sign in", + "resetTitle": "Choose a new password", + "resetSubtitle": "Enter a new password for your account", + "newPasswordHint": "New password (min 8 characters)", + "updatePassword": "Update password", + "updatingPassword": "Updating...", + "passwordUpdated": "Your password has been updated. Taking you to sign in...", + "resetLinkInvalid": "This password reset link is invalid or has expired.", + "requestNewLink": "Request a new link" }, "Dashboard": { "title": "Developer Dashboard", "signInPrompt": "Sign in to manage API keys and view usage.", - "signIn": "Sign in", "description": "Manage API keys and monitor gateway usage. One key, one bill, any agent.", "apiKeys": "API Keys", "createKey": "Create key", @@ -497,7 +637,6 @@ "billing": "Credits & Billing", "creditBalance": "Credit balance", "totalSpend": "Total spend", - "topUpComingSoon": "Credits are funded by the platform for now — self-serve PayPal top-up is coming soon.", "topUpLabel": "Add credits", "topUpButton": "Top up with PayPal", "topUpRedirecting": "Redirecting…", @@ -513,35 +652,65 @@ "ledgerDebit": "Call charge", "ledgerPayout": "Payout", "ledgerRefund": "Refund", - "ledgerAdjust": "Adjustment" + "ledgerAdjust": "Adjustment", + "onboardingTitle": "Get your first call working", + "onboardingSubtitle": "Three steps from here to an agent answering you.", + "stepKeyTitle": "Create an API key", + "stepKeyBody": "One key calls every agent on TakoAPI. The secret is shown once, right after you create it.", + "stepCallTitle": "Make your first call", + "stepCallBody": "Copy a sample below, set TAKO_KEY to your key, and run it. The call appears under Usage within seconds.", + "stepInstallTitle": "Install into your coding agent", + "stepInstallBody": "Teach Claude Code, Codex or OpenCode to discover and call agents through TakoAPI.", + "stepInstallLink": "Open the install guide", + "stepOptional": "Optional", + "stepDone": "Done", + "stepTodo": "Not done yet", + "keyNameLabel": "Key name", + "keyNamePlaceholder": "Gateway key", + "createKeyError": "Couldn't create the key. Please try again.", + "revokeNamed": "Revoke {name}", + "quickstartHint": "Set TAKO_KEY to a key above, then call any agent by its slug.", + "creditRule": "Free agents cost nothing; priced agents debit your prepaid credit, and a call is refused with HTTP 402 as soon as the balance would go below zero.", + "topUpUnavailable": "Self-serve top-up isn't available yet — credits are added by the platform for now. Your balance above is live either way.", + "dismiss": "Dismiss", + "loadError": "Some dashboard data couldn't be loaded.", + "retry": "Retry", + "recentCallsCaption": "Your most recent gateway calls", + "chartTitle": "Calls per day, last {days} days", + "chartTotal": "{count, number} total", + "chartAria": "Bar chart of gateway calls per day over the last {days} days, {count, number} calls in total.", + "chartDay": "{date}: {calls, number} calls, {errors, number} failed" }, "Profile": { - "title": "My Profile", - "signInPrompt": "Please sign in to view your profile.", - "signIn": "Sign in", - "submissions": "Submissions", + "title": "My listings", + "signInPrompt": "Sign in to see the agents and skills you've published.", "likes": "Likes", - "views": "Views", - "statValue": "{count, number}", - "mySubmissions": "My Submissions", - "submitNew": "Submit new", + "views": "views", "tab_all": "All", - "tab_approved": "Approved", - "tab_pending": "Pending", - "tab_rejected": "Rejected", - "tabCount": "{count, number}", - "emptyTab": "No submissions in this tab", + "emptyTab": "No skills here yet.", "submitFirst": "Submit your first skill", - "statusApproved": "Approved", - "statusPending": "Pending review", + "statusApproved": "Live", + "statusPending": "In review", "statusRejected": "Rejected", - "reviewerNote": "Reviewer note:" + "reviewerNote": "Reviewer note:", + "publishAgent": "Publish an agent", + "submitSkill": "Submit a skill", + "statAgents": "Agents", + "statCalls": "Gateway calls", + "statSkills": "Skills", + "tabAgents": "Agents", + "tabSkills": "Skills", + "statusDisabled": "Disabled", + "kindHosted": "Callable", + "kindProject": "Open-source", + "noAgents": "No agents here yet.", + "publishFirstAgent": "Publish your first agent", + "myListings": "My listings" }, "Submit": { "title": "Submit a Skill", "subtitle": "Share your OpenClaw skill with the community", "signInPrompt": "Please sign in to submit skills.", - "signIn": "Sign in", "skillSource": "Skill Source", "githubUrl": "GitHub URL", "clawSkillsUrl": "ClawSkills.sh URL", @@ -571,8 +740,15 @@ "submitting": "Submitting...", "enterUrlFirst": "Please enter a URL first", "autoFillFailed": "Auto-fill failed", - "submitFailed": "Failed to submit", - "somethingWentWrong": "Something went wrong" + "somethingWentWrong": "Something went wrong", + "successTitle": "Submitted 🎉", + "pendingReview": "{name} is now pending review.", + "nextStepsTitle": "What happens next", + "nextStepReview": "A reviewer checks the repository and the description.", + "nextStepEmail": "We'll email you when it's been reviewed.", + "nextStepProfile": "Your listings page always shows its current status and any reviewer note.", + "viewMyListings": "View my listings", + "backToSkills": "← Back to skills" }, "SubmitAgent": { "title": "Publish an Agent", @@ -611,7 +787,24 @@ "successTitle": "Submitted 🎉", "pendingReview": "{name} is now pending review.", "successDetail": "We'll verify the AgentCard and publish it to the marketplace once approved.", - "backToMarketplace": "← Back to marketplace" + "backToMarketplace": "← Back to marketplace", + "checkCard": "Check card", + "checking": "Checking…", + "enterCardUrlFirst": "Enter an AgentCard URL first.", + "previewTitle": "This is what we'd list", + "previewNoDescription": "The card has no description.", + "previewEndpoint": "Endpoint", + "previewCapabilities": "Capabilities", + "previewSkillCount": "{count, plural, =0 {No capabilities advertised} one {# capability} other {# capabilities}}", + "previewSupports": "Supports", + "previewStreaming": "Streaming", + "previewPush": "Push notifications", + "previewHint": "Nothing is saved yet — publish below to send it for review.", + "nextStepsTitle": "What happens next", + "nextStepReview": "A reviewer checks the AgentCard and that your endpoint responds.", + "nextStepEmail": "We'll email you when it's been reviewed.", + "nextStepProfile": "Your listings page always shows its current status and any reviewer note.", + "viewMyListings": "View my listings" }, "AgentEngagement": { "yourRating": "Your rating", @@ -619,15 +812,15 @@ "noRatings": "Not yet rated", "bookmark": "Save", "bookmarked": "Saved", - "signInToRate": "Sign in to rate or save this agent" + "signInToRate": "Sign in to rate or save this agent", + "rateStars": "Rate {count, plural, one {# star} other {# stars}}" }, "Bookmarks": { "title": "Saved agents", "subtitle": "Agents you've bookmarked.", "empty": "No bookmarks yet.", "browseAgents": "Browse agents", - "signInPrompt": "Sign in to see your bookmarks.", - "signIn": "Sign in" + "signInPrompt": "Sign in to see your bookmarks." }, "ScenarioPage": { "metaTitle": "{scenario} AI agents", @@ -640,7 +833,23 @@ "indexTitle": "Browse AI agents by scenario", "indexDescription": "Find AI agents by what you'd use them for — frontend, investing, marketing, support, and more — all callable through one API.", "indexHeroTitle": "Browse by scenario", - "indexHeroSubtitle": "Find AI agents by the job you need done." + "indexHeroSubtitle": "Find AI agents by the job you need done.", + "indexPopularHeading": "Well covered", + "indexPopularHint": "Scenarios with a deep catalog of agents to compare.", + "indexSparseHeading": "Just getting started", + "indexSparseHint": "Only a handful of agents so far — an easy place to be the first.", + "indexSparseCta": "Publish an agent in one of these →", + "metaTitlePaged": "{scenario} AI agents — page {page}", + "resultsLabel": "{scenario} agents", + "paginationLabel": "Pagination", + "showingRange": "Showing {from}–{to} of {total}", + "prev": "← Prev", + "next": "Next →", + "goToPage": "Go to page {page}", + "ctaTitle": "Know a {scenario} agent?", + "ctaBody": "This scenario is still thin. Publish an agent and it shows up here for everyone browsing {scenario}.", + "ctaButton": "Publish an agent", + "relatedHeading": "Explore other scenarios" }, "Badge": { "snippetTitle": "Add a badge", @@ -656,7 +865,8 @@ "step2": "On your listing page, copy the Markdown from the badge box.", "step3Title": "Paste into your README", "step3": "Add it near the top — visitors and search engines get a link to your TakoAPI page.", - "findListing": "Find your listing" + "findListing": "Find your listing", + "exampleAlt": "TakoAPI badge" }, "Blog": { "title": "Blog", @@ -666,5 +876,149 @@ "minRead": "{min} min read", "published": "Published", "faqTitle": "FAQ" + }, + "NotFound": { + "title": "We couldn't find that page", + "description": "The link may be out of date, or the listing may have been removed. Try the directory or search from the header.", + "home": "Home", + "agents": "Browse agents", + "skills": "Browse skills" + }, + "ErrorPage": { + "title": "Something went wrong", + "description": "This page failed to load. Trying again often works; if it doesn't, the reference below helps us find it in the logs.", + "reference": "Reference: {id}", + "retry": "Try again", + "home": "Go home" + }, + "Consent": { + "title": "Analytics", + "description": "We use Google Analytics to see which pages people find useful. Nothing is loaded and no cookies are set unless you accept.", + "accept": "Accept", + "decline": "Decline" + }, + "Docs": { + "metaTitle": "API documentation", + "title": "TakoAPI API reference", + "description": "Go from an API key to a working agent call: authentication, the discovery endpoints, the unified agent gateway, the hosted MCP server, error codes, rate limits and billing.", + "heroBadge": "Developer docs", + "heroTitle": "The TakoAPI API reference", + "heroSubtitle": "One key, every agent. Point any OpenAI-compatible client at TakoAPI and call an agent by its slug — or browse the whole catalog with no key at all.", + "heroKeyCta": "Create an API key", + "heroBrowseCta": "Browse agents", + "tocTitle": "On this page", + "tocLabel": "Table of contents", + "quickstartTitle": "Quickstart", + "quickstartIntro": "Three steps from nothing to a reply. Every request on this page is a real call against the live API.", + "quickstartStep1Title": "Create a key", + "quickstartStep1": "Sign in and create an API key in your dashboard. The secret is shown once, at creation.", + "quickstartStep2Title": "Export it", + "quickstartStep2": "Every example on this page reads the key from TAKO_KEY.", + "quickstartStep3Title": "Call an agent", + "quickstartStep3": "Set the model — or the slug in the URL — to any hosted agent from the catalog.", + "quickstartSamplesLabel": "Quickstart call examples", + "quickstartNote": "The slug in these samples is a placeholder, not a real agent. Copy a real one from any agent page; only agents listed as hosted can be invoked.", + "authenticationTitle": "Authentication", + "authenticationIntro": "Gateway calls are authenticated with a bearer token. The discovery endpoints are anonymous and need no key at all.", + "authHeaderNote": "x-api-key is accepted as an alternative to the Authorization header. Keys always start with tako_live_.", + "authWhereTitle": "Where keys come from", + "authWhere": "Create one key per client or environment in your dashboard, and revoke the ones you stop using.", + "authOnceTitle": "The secret is shown once", + "authOnce": "Only a SHA-256 hash of the key and a short display prefix are stored, so a lost secret cannot be recovered — create a replacement and revoke the old key.", + "authRevokeTitle": "Revoking is immediate", + "authRevoke": "Every call re-checks the key against the database and nothing is cached, so a revoked key is rejected on the very next request. Past usage stays in your history.", + "discoveryTitle": "Discovery", + "discoveryIntro": "Four public, cacheable endpoints that need no key. Only approved agents and skills are ever returned.", + "endpointRegistrySummary": "The whole agent catalog — Markdown for a model, JSON for code.", + "endpointRegistryNote": "Without format=json the response is Markdown (text/markdown), split into a hosted-agents table and an open-source-projects table and sized to drop straight into a model's context — that is what the installed TakoAPI skill reads. Both formats are capped by limit, so check truncated and the total counts before assuming you have everything. Free-text parameters are truncated to 100 characters.", + "paramRegistryFormat": "md (default) or json.", + "paramRegistryQ": "Free-text match on agent name and description.", + "paramRegistryCategory": "Category slug.", + "paramRegistryProtocol": "Return only agents advertising this protocol.", + "paramRegistryKind": "HOSTED agents are callable through the gateway; PROJECT entries are open-source repositories listed for discovery only.", + "paramRegistrySort": "Ordering. Defaults to featured first, then most-called, then newest — and to most-starred for projects.", + "paramRegistryLimit": "Total entries to return. Hosted agents are filled first, then projects.", + "endpointAgentDetailSummary": "Full public detail for one agent, including its advertised A2A skills.", + "endpointAgentDetailNote": "Only approved agents resolve; anything else is a 404. Moderation fields — review notes, internal ids and publisher avatars — are never part of this shape. Cached at the edge for 60 seconds.", + "paramAgentDetailSlug": "Path segment. The agent's slug, as returned by the registry.", + "endpointSkillsDirectorySummary": "The OpenClaw skills catalog, Markdown by default.", + "endpointSkillsDirectoryNote": "Markdown-first like the registry. install is null for skills with no published install command — fall back to the github link. downloads is a third-party figure scraped from the skill's own listing, not a TakoAPI install count.", + "paramSkillsDirFormat": "md (default) or json.", + "paramSkillsDirQ": "Free-text match on skill name and description.", + "paramSkillsDirCategory": "Category slug.", + "paramSkillsDirPage": "1-based page number.", + "paramSkillsDirLimit": "Results per page.", + "endpointSkillsSearchSummary": "Full-text search across the skills catalog, JSON only.", + "endpointSkillsSearchNote": "q is required: without it the endpoint returns an empty list rather than the whole catalog. Each result is the full catalog row minus readme and reviewNote; the example above is abridged.", + "paramSkillsSearchQ": "Search term. Matches skill name, description and author.", + "paramSkillsSearchCategory": "Category slug.", + "paramSkillsSearchPage": "1-based page number.", + "paramSkillsSearchLimit": "Results per page.", + "gatewayTitle": "Gateway", + "gatewayIntro": "Three ways to invoke the same agent. All three take a key, all three bill identically, and all three accept the agent's slug where a model name would normally go. Only agents of kind HOSTED can be called.", + "endpointChatCompletionsSummary": "OpenAI-compatible. Point any OpenAI SDK at /v1 and set model to an agent slug.", + "endpointChatCompletionsNote": "The whole conversation is forwarded: one A2A text part per turn, in order, with the OpenAI role attached to each part. usage is always present but always zero — nothing in this gateway counts tokens, and an agent need not be an LLM. Set stream: true for the SSE form above, and stream_options.include_usage to also get the trailing usage chunk. A non-streamed call times out after 30 seconds, a stream after 120.", + "endpointA2AMessageSummary": "Native A2A passthrough. Returns the agent's own JSON-RPC response, unmodified.", + "endpointA2AMessageNote": "Relayed as an A2A message/send call. Send the text shorthand, or a full A2A message — with parts, taskId and contextId — to continue a multi-turn task or pass file and data parts through. The agent's status code and body are relayed untouched, and a request with no content is rejected with a 400 rather than billed. Times out after 30 seconds.", + "endpointA2AStreamSummary": "The same call over Server-Sent Events, for agents that stream.", + "endpointA2AStreamNote": "Relays the agent's A2A message/stream frames verbatim as text/event-stream. This endpoint takes the text shorthand only. The connection is cut after 120 seconds, and the call is charged only once the stream reaches its end — see Rate limits and billing.", + "labelQueryParams": "Parameters", + "labelRequest": "Request body", + "labelExample": "Example", + "labelResponse": "Response", + "labelStreaming": "Response with stream: true", + "labelA2AMessageForm": "Full A2A message form", + "labelAuthRequired": "Key required", + "labelNoAuth": "No key", + "labelBillable": "Billable", + "labelFree": "Free", + "labelParamName": "Name", + "labelParamType": "Type", + "labelParamDesc": "Description", + "paramsTableCaption": "Parameters for {path}", + "mcpTitle": "MCP server", + "mcpIntro": "TakoAPI runs a hosted, stateless Model Context Protocol server over Streamable HTTP at {url}. Register it once and your coding agent can search and invoke the catalog as native tools.", + "mcpRegisterTitle": "Register it", + "mcpRegisterLabel": "MCP registration commands", + "mcpAuthNote": "The three read tools work anonymously. invoke_agent spends credit, so it needs your key: pass it as an Authorization header (Claude Code), as bearer_token_env_var (Codex), or in a headers block (OpenCode).", + "mcpToolsTitle": "Tools", + "mcpToolsTableCaption": "Tools exposed by the TakoAPI MCP server", + "mcpToolColumn": "Tool", + "mcpToolAccessColumn": "Access", + "mcpToolWhatColumn": "What it does", + "mcpToolKeyRequired": "Key required", + "mcpToolAnonymous": "Anonymous", + "mcpToolSearchAgents": "Search the registry by keyword, category or protocol. Returns slugs, protocols and pricing.", + "mcpToolGetAgent": "Everything known about one agent by slug — capabilities, protocols, endpoint, pricing and advertised skills.", + "mcpToolSearchSkills": "Search the OpenClaw skills catalog. With no query it returns the curated top skills.", + "mcpToolInvokeAgent": "Invoke an agent through the gateway and return its reply. Requires a key and spends credit.", + "mcpRateNote": "The MCP endpoint is limited to 120 requests per minute per IP address, separately from your key's gateway allowance.", + "mcpInstallNote": "Prefer a skill file to a server? The installer teaches Claude Code, Codex and OpenCode to call these endpoints over plain HTTP instead.", + "errorsTitle": "Errors", + "errorsIntro": "Every gateway failure is JSON. These are the statuses the gateway itself returns.", + "errorsTableCaption": "Error statuses returned by the gateway", + "errorStatusColumn": "Status", + "errorCodeColumn": "Code", + "errorWhenColumn": "When", + "errorBodyColumn": "Example body", + "error400When": "The slug names an open-source project with no invokable endpoint, the request body failed validation, or every message turn was empty.", + "error401When": "The key is missing, malformed, unknown or revoked.", + "error402When": "The agent is priced and charging the call would take your balance below zero. Checked before the agent is contacted, so nothing is spent.", + "error404When": "No approved agent has that slug.", + "error429When": "The key went over its allowance in the last 60 seconds. Retry-After gives the seconds until the window resets.", + "error502When": "The agent did not answer, timed out, returned no stream, or reported a JSON-RPC error over an HTTP 200. Never charged.", + "errorsOpenAiNote": "On /v1/chat/completions the same failures arrive in OpenAI's envelope — with type, param and code always present — so an OpenAI SDK raises its usual exception types:", + "errorsPassthroughNote": "A status the agent itself returned is relayed unchanged by the A2A message endpoint, so a 4xx there may be the agent's rather than ours.", + "limitsTitle": "Rate limits & billing", + "limitsRateTitle": "Rate limits", + "limitsRate": "The gateway allows 120 requests per 60 seconds per API key. The window is shared across every server instance and is keyed on the key itself rather than your IP address, so it neither loosens under load nor tightens behind a shared NAT.", + "limitsRateOverride": "A key can carry its own allowance, which replaces the default for that key alone. Over the limit you get a 429 with a Retry-After header, in seconds.", + "limitsBillingTitle": "Billing", + "limitsBillingFree": "Agents priced FREE cost nothing — and so does any agent whose unit price is missing or zero. The gateway never invents a charge.", + "limitsBillingPriced": "A priced agent debits your prepaid credit at its unit price, once per call. There is no token-level metering, so a call is one unit however long it runs.", + "limitsBillingPreflight": "Your balance is checked before the agent is contacted. If the charge would take you below zero the call is refused with a 402 and the agent is never called — there is no overdraft.", + "limitsBillingFailures": "Calls that fail are not charged. A 4xx or 5xx from the agent, a timeout, an unreachable endpoint or a JSON-RPC error all record the attempt and bill nothing.", + "limitsBillingStreams": "Streams are charged only when they complete. A stream that is aborted, times out, fails mid-flight, or that you disconnect from early is recorded and not billed.", + "limitsBillingLedger": "Every charge writes an immutable ledger entry against the invocation that caused it. Balance, ledger and per-call usage are all in your dashboard." } } diff --git a/messages/es.json b/messages/es.json index 6df79e6f3..248109a18 100644 --- a/messages/es.json +++ b/messages/es.json @@ -1,66 +1,84 @@ { "Metadata": { "tagline": "Una API para acceder a todos los agentes", - "description": "Descubre e invoca agentes de IA a través de una API unificada — un OpenRouter para agentes. Explora un directorio de cientos de proyectos de agentes de código abierto y miles de skills de OpenClaw para tu agente de programación." + "description": "Descubre e invoca agentes de IA a través de una API unificada — un OpenRouter para agentes. Explora un directorio con cientos de proyectos de agentes de código abierto y miles de skills de OpenClaw para tu agente de programación." }, "LocaleSwitcher": { "label": "Idioma" }, "Header": { - "searchPlaceholder": "Buscar agentes...", "agents": "Agentes", "skills": "Skills", - "trending": "Tendencias", - "publish": "Publicar", - "publishAgent": "Publicar agente", + "publishAgent": "Publicar un agente", "account": "Cuenta", - "mySkills": "Mis skills", - "myBookmarks": "Guardados", - "apiUsage": "API y uso", + "myBookmarks": "Agentes guardados", + "apiUsage": "Claves de API y uso", "adminPanel": "Panel de administración", "signIn": "Iniciar sesión", - "signOut": "Cerrar sesión" + "signOut": "Cerrar sesión", + "mainNav": "Principal", + "skipToContent": "Saltar al contenido", + "openMenu": "Abrir menú", + "closeMenu": "Cerrar menú", + "projects": "Proyectos", + "install": "Instalar", + "getApiKey": "Obtener clave de API", + "search": "Buscar", + "searchScope": "Buscar en", + "scopeAgents": "Agentes", + "scopeProjects": "Proyectos", + "scopeSkills": "Skills", + "searchAgentsPlaceholder": "Busca agentes por lo que hacen…", + "searchProjectsPlaceholder": "Busca proyectos de agentes de código abierto…", + "searchSkillsPlaceholder": "Busca skills para tu agente de programación…", + "myListings": "Mis publicaciones", + "docs": "Documentación" }, "Footer": { - "tagline": "Una API para acceder a todos los agentes — más miles de skills de OpenClaw para tu agente de programación.", + "tagline": "Una API para acceder a todos los agentes: descubre, invoca y mide cualquier agente desde un único endpoint.", "agentRegistry": "Registro de agentes:", "platform": "Plataforma", "resources": "Recursos", - "installInAgent": "Instalar en tu agente", + "installInAgent": "Instalar en tu agente de programación", "browseAgents": "Explorar agentes", "publishAnAgent": "Publicar un agente", - "developerDashboard": "Panel de desarrollador", + "developerDashboard": "Claves de API y uso", "browseSkills": "Explorar skills", - "awesomeList": "Lista Awesome", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Creado para la comunidad de OpenClaw.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. Licencia MIT.", + "blog": "Blog", + "badge": "Insignia para el README", + "scenarios": "Explorar por escenario", + "a2aSpec": "Protocolo A2A", + "github": "GitHub", + "docs": "Documentación de la API" }, "Home": { "heroTitle": "Una API para acceder a todos los agentes", - "heroSubtitle": "Descubre e invoca {agents, number} agentes de IA a través de una API unificada — más {skills, number} skills para tu agente de programación.", + "heroSubtitle": "Descubre agentes de IA y luego invoca cualquiera de ellos desde un solo endpoint con una sola clave. Nativo en A2A, compatible con OpenAI, medido por invocación.", "browseAgents": "Explorar agentes", - "publishAnAgent": "Publicar un agente", - "agentRegistry": "Registro de agentes:", - "browseByScenario": "Explorar por escenario", - "allAgents": "Todos los agentes", + "browseByScenario": "Explora por lo que necesitas resolver", "featuredAgents": "Agentes destacados", "browseAll": "Explorar todo", "popularProjects": "Proyectos de código abierto populares", "selfHostable": "{count, number} autoalojables", - "skillTitle": "Skill de TakoAPI para OpenClaw", - "official": "Oficial", - "skillDescription": "Busca, instala y gestiona skills de OpenClaw mediante conversación natural — con la tecnología del marketplace de TakoAPI.", - "viewOnGithub": "Ver en GitHub", - "askAgent": "o pídele a tu agente: «Instala la skill de TakoAPI»", - "usingCodingAgent": "¿Usas Claude Code, Codex u OpenCode?", - "oneCommandInstall": "Instalación con un solo comando", - "mustHaveSkills": "Skills imprescindibles", - "viewRankings": "Ver clasificación", - "categories": "Categorías", - "viewAll": "Ver todo", + "categories": "Categorías:", "moreCategories": "{count, plural, one {+# categoría más} other {+# categorías más}}", - "latestSkills": "Últimas skills" + "getApiKey": "Consigue una clave de API", + "catalogSummary": "{agents, number} agentes invocables · {projects, number} proyectos de código abierto · {skills, number} skills para agentes de programación", + "quickstartLabel": "30 segundos hasta tu primera invocación", + "quickstartNote": "¿Prefieres trabajar desde la terminal? Instala TakoAPI en tu agente de programación.", + "allScenarios": "Todos los escenarios", + "callableNow": "{count, number} invocables a través del gateway", + "installTitle": "Usa TakoAPI desde tu agente de programación", + "installDescription": "Un solo comando enseña a Claude Code, Codex u OpenCode a buscar en el directorio e invocar agentes por ti — o registra el servidor MCP alojado en su lugar.", + "installCta": "Guía de instalación", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Skills para tu agente de programación", + "skillsSubtitle": "{count, number} skills de OpenClaw, las más instaladas y las más recientes.", + "quickstartTabDiscover": "1. Descubrir", + "quickstartTabCall": "2. Invocar", + "quickstartTabFromAgent": "3. Desde tu agente" }, "Scenarios": { "frontend": "Frontend", @@ -85,8 +103,6 @@ "notFound": "Agente no encontrado", "backToMarketplace": "← Marketplace de agentes", "openSourceProject": "Proyecto de código abierto", - "signedCard": "Tarjeta firmada", - "verifiedPublisher": "Editor verificado", "by": "por {name}", "publisherOpenSource": "código abierto", "publisherUnknown": "desconocido", @@ -101,17 +117,41 @@ "noSkillsHosted": "Este agente no anunció skills estructuradas en su AgentCard.", "inputMode": "entrada: {mode}", "outputMode": "salida: {mode}", - "openSourceProjectHeading": "Proyecto de código abierto", - "openSourceProjectNote": "Autoaloja o explora el código. No invocable a través del gateway de TakoAPI.", + "openSourceProjectNote": "Autoalójalo o explora el código. No se puede invocar a través del gateway de TakoAPI.", "viewOnGithub": "Ver en GitHub", - "integration": "Integración", "endpoint": "Endpoint", "agentCard": "AgentCard", "homepage": "Página principal", "callThroughTakoApi": "Invócalo a través de TakoAPI", - "gatewayPhase2": "Gateway unificado — la invocación llega en la Fase 2.", "breadcrumbHome": "Inicio", - "breadcrumbMarketplace": "Marketplace de agentes" + "breadcrumbMarketplace": "Marketplace de agentes", + "gatewayHint": "Invócalo con tu clave de API — crea una en el panel.", + "healthOk": "Responde", + "healthDegraded": "Degradado", + "healthDown": "No responde", + "healthCheckedAt": "Última comprobación: {time}", + "atAGlance": "De un vistazo", + "protocols": "Protocolos", + "protocolsNone": "Sin declarar", + "yes": "Sí", + "no": "No", + "pricing": "Precio", + "health": "Estado", + "healthUnknown": "Aún sin comprobar", + "totalCalls": "Total de invocaciones", + "numberValue": "{count, number}", + "averageRating": "Valoración media", + "ratingValue": "{rating} / 5 · {count, number} valoraciones", + "notRated": "Aún sin valorar", + "callExamplesLabel": "Ejemplos de invocación", + "repository": "Repositorio", + "language": "Lenguaje", + "license": "Licencia", + "licenseNone": "Sin especificar", + "lastPush": "Último push", + "topics": "Temas", + "readmeHeading": "Del README", + "readmeNote": "Extracto del README del proyecto en GitHub." }, "Agents": { "metaTitle": "Marketplace de agentes", @@ -145,11 +185,24 @@ "emptyCta": "Publica el primero →", "prev": "← Anterior", "next": "Siguiente →", - "pageOf": "Página {page} de {total}" + "pageOf": "Página {page} de {total}", + "filtersTitle": "Filtros", + "filtersWithCount": "Filtros ({count})", + "activeFiltersLabel": "Filtros activos:", + "removeFilter": "Quitar filtro: {label}", + "clearAll": "Borrar todo", + "kindLabel": "Tipo", + "scenarioGroup": "Escenario", + "protocolGroup": "Protocolo", + "pricingGroup": "Precio", + "categoryGroup": "Categoría", + "searchChip": "Búsqueda: {query}", + "resultsLabel": "Resultados", + "paginationLabel": "Paginación", + "goToPage": "Ir a la página {page}", + "showingRange": "Mostrando {from}–{to} de {total}" }, "Components": { - "homeSearchPlaceholder": "Buscar skills por nombre, descripción o autor...", - "homeSearchButton": "Buscar", "agentFree": "Gratis", "agentPerCall": "{price}/invocación", "agentPerTask": "{price}/tarea", @@ -163,7 +216,11 @@ "agentSelfHost": "autoalojar", "agentSkills": "{count, plural, one {# skill} other {# skills}}", "skillBy": "por {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} invocaciones", + "healthOk": "Responde", + "healthDegraded": "Degradado", + "healthDown": "No responde" }, "Install": { "metaTitle": "Instalar en Claude Code, Codex y OpenCode", @@ -196,7 +253,7 @@ "howToUseHeading": "Cómo usarla", "claudeCodeUse": "Se carga automáticamente — solo pídele a Claude que encuentre un agente en TakoAPI.", "codexUse": "Invócala con $takoapi o selecciónala desde el menú /skills.", - "opencodeUse": "Ejecuta /takoapi o derívalo a @takoapi.", + "opencodeUse": "Ejecuta /takoapi '' o pásalo a @takoapi.", "mcpHeading": "Servidor MCP — herramientas alojadas, nada que instalar", "claudeCodeMcpHint": "Un solo comando. Añade --header \"Authorization: Bearer $TAKO_KEY\" para habilitar invoke_agent.", "codexMcpHint": "Añade a ~/.codex/config.toml", @@ -226,12 +283,17 @@ "requirements": "Requisitos", "installation": "Instalación", "clawHubCli": "CLI de ClawHub", - "openClawChat": "Chat de OpenClaw (pega esto en tu conversación)", "links": "Enlaces", "github": "GitHub", - "copyGithubUrl": "Copiar URL de GitHub", "clawHub": "ClawHub", - "skillNotFound": "Skill no encontrada" + "codingAgent": "Agente de programación", + "agentPrompt": "Instala la skill desde {url}", + "installHintRegistry": "Ejecuta el comando de la CLI o pega el prompt en Claude Code, Cursor, Codex o el agente de programación que uses.", + "installHintRepo": "Esta skill no está publicada en el registro de ClawHub. Pega el prompt en tu agente de programación — Claude Code, Cursor, Codex — y la instalará directamente desde el repositorio de origen.", + "installUnavailable": "Todavía no se ha publicado ninguna fuente de instalación para esta skill.", + "openRepository": "Abrir el repositorio", + "signInToLike": "Inicia sesión para dar me gusta a esta skill", + "downloadsSource": "Descargas según clawskills.sh" }, "Skills": { "metaTitle": "Explorar skills", @@ -256,14 +318,33 @@ "agentGeneric": "Genérico", "all": "Todos", "emptyTitle": "No se encontraron skills", - "emptyHint": "Prueba a ajustar tu búsqueda o filtros", - "pageOf": "Página {page} de {total}" + "emptyHint": "Prueba a ajustar tu búsqueda o tus filtros", + "pageOf": "Página {page} de {total}", + "metaTitleSearch": "Skills que coinciden con «{query}»", + "metaTitlePaged": "Explorar skills — página {page}", + "searchInputLabel": "Buscar skills", + "searchPlaceholder": "Busca skills por nombre, descripción o autor", + "searchButton": "Buscar", + "agentFilter": "Agente", + "agentZed": "Zed", + "sourceFilter": "Origen", + "sourceGithub": "GitHub", + "sourceCommunity": "Comunidad", + "sourceCurated": "Seleccionadas", + "categoryFilter": "Categoría", + "sortLabel": "Ordenar", + "sortRelevance": "Mejor coincidencia", + "clearFilters": "Borrar filtros", + "paginationLabel": "Páginas del listado de skills", + "goToPage": "Ir a la página {page}", + "prev": "Anterior", + "next": "Siguiente" }, "Trending": { "metaTitle": "Skills en tendencia", - "metaDescription": "Las skills de OpenClaw más descargadas y con más me gusta en este momento — una tabla en vivo de lo que están instalando los agentes de programación.", + "metaDescription": "Las skills de OpenClaw más descargadas y con más me gusta: una clasificación construida con las cifras de descargas publicadas por clawskills.sh, actualizada a diario.", "ogTitle": "Skills en tendencia — {siteName}", - "ogDescription": "Una tabla en vivo de las skills de OpenClaw más populares.", + "ogDescription": "Una clasificación diaria de las skills de OpenClaw más populares.", "title": "Skills en tendencia", "subtitle": "Descubre las skills de OpenClaw más populares clasificadas por la comunidad", "searchPlaceholder": "Buscar skills en tendencia…", @@ -277,11 +358,17 @@ "count": "{count, plural, =0 {Ninguna skill} one {# skill} other {# skills}}", "countInCategory": "{count, plural, =0 {Ninguna skill} one {# skill} other {# skills}} en {category}", "emptyTitle": "No se encontraron skills", - "emptySubtitle": "Prueba a ajustar tu búsqueda o filtros", + "emptySubtitle": "Prueba a ajustar tu búsqueda o tus filtros", "byAuthor": "por {author}", "showing": "Mostrando {from, number}–{to, number} de {total, number}", "prev": "Anterior", - "next": "Siguiente" + "next": "Siguiente", + "provenanceNote": "Las cifras de descargas y estrellas vienen del catálogo original — clawskills.sh y GitHub — y se actualizan cada noche; no miden instalaciones hechas a través de TakoAPI. Los me gusta y las vistas se cuentan aquí.", + "search": "Buscar", + "sortLabel": "Ordenar por", + "rank": "Puesto", + "goToPage": "Ir a la página {page}", + "paginationLabel": "Páginas de la clasificación" }, "Admin": { "accessDenied": "Acceso denegado", @@ -335,8 +422,8 @@ "actionReject": "Rechazar", "feature": "Destacar", "unfeature": "Quitar de destacados", - "scenariosEditorHint": "场景 Escenarios — 点击切换 / pulsa para asignar", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Escenarios — pulsa para asignar o quitar", + "scenarioOption": "{name}", "selectedCount": "{count, number} seleccionados", "rejectReasonPrompt": "Motivo del rechazo (opcional):", "deleteAgentConfirm": "¿Eliminar este agente permanentemente?", @@ -436,7 +523,46 @@ "targetSkill": "skill", "targetAgent": "agente", "targetCategory": "categoría", - "targetUser": "usuario" + "targetUser": "usuario", + "adminNavLabel": "Secciones de administración", + "retry": "Reintentar", + "close": "Cerrar", + "prevPage": "Página anterior", + "nextPage": "Página siguiente", + "sectionAgents": "Agentes", + "sectionSkills": "Skills y comunidad", + "statTotalAgents": "Total de agentes", + "statPendingAgents": "Agentes pendientes", + "statHostedAgents": "Alojados", + "statProjectAgents": "Proyectos", + "pendingAgentsTitle": "Agentes pendientes de revisión", + "noPendingAgents": "No hay ningún agente esperando revisión.", + "kindHosted": "Alojado", + "kindProject": "Proyecto", + "filterByStatus": "Filtrar por estado", + "filterDisabled": "Desactivados", + "actionDisable": "Desactivar", + "actionEnable": "Reactivar", + "disableAgentTitle": "Desactivar agente", + "disableAgentConfirm": "{name} quedará oculto en el catálogo público hasta que lo reactives. No se elimina nada.", + "deleteAgentTitle": "Eliminar agente", + "deleteAgentConfirmNamed": "¿Seguro que quieres eliminar {name}? Esta acción no se puede deshacer.", + "rejectAgentTitle": "Rechazar agente", + "rejectReasonLabel": "Motivo (opcional, visible para quien lo publicó)", + "rejectReasonPlaceholder": "p. ej. el endpoint devuelve 500 en la tarea de ejemplo", + "healthOk": "Operativo", + "healthDegraded": "Degradado", + "healthDown": "Caído", + "healthUnknown": "Sin comprobar", + "healthCheckedAt": "Última comprobación de estado: {date}", + "healthNeverChecked": "No hay ninguna comprobación de estado registrada", + "batchDeleteTitle": "Eliminar skills", + "selectAllRows": "Seleccionar todas las filas", + "selectRow": "Seleccionar {name}", + "editSkillNamed": "Editar la skill {name}", + "deleteSkillNamed": "Eliminar la skill {name}", + "editCategoryNamed": "Editar la categoría {name}", + "deleteCategoryNamed": "Eliminar la categoría {name}" }, "Auth": { "signInTitle": "Inicia sesión en TakoAPI", @@ -462,12 +588,26 @@ "noAccount": "¿No tienes cuenta?", "signUpLink": "Regístrate", "haveAccount": "¿Ya tienes cuenta?", - "signInLink": "Inicia sesión" + "signInLink": "Inicia sesión", + "forgotPassword": "¿Olvidaste tu contraseña?", + "forgotTitle": "Restablece tu contraseña", + "forgotSubtitle": "Te enviaremos por correo un enlace para elegir una nueva", + "sendResetLink": "Enviar enlace", + "sendingResetLink": "Enviando...", + "resetLinkSent": "Si esa dirección tiene una cuenta, te hemos enviado un enlace para restablecer la contraseña. Revisa tu bandeja de entrada.", + "backToSignIn": "Volver a iniciar sesión", + "resetTitle": "Elige una nueva contraseña", + "resetSubtitle": "Introduce una nueva contraseña para tu cuenta", + "newPasswordHint": "Nueva contraseña (mín. 8 caracteres)", + "updatePassword": "Actualizar contraseña", + "updatingPassword": "Actualizando...", + "passwordUpdated": "Tu contraseña se ha actualizado. Te llevamos a iniciar sesión...", + "resetLinkInvalid": "Este enlace para restablecer la contraseña no es válido o ha caducado.", + "requestNewLink": "Solicitar un enlace nuevo" }, "Dashboard": { "title": "Panel de desarrollador", "signInPrompt": "Inicia sesión para gestionar claves de API y ver el uso.", - "signIn": "Iniciar sesión", "description": "Gestiona claves de API y monitoriza el uso del gateway. Una clave, una factura, cualquier agente.", "apiKeys": "Claves de API", "createKey": "Crear clave", @@ -497,7 +637,6 @@ "billing": "Créditos y facturación", "creditBalance": "Saldo de créditos", "totalSpend": "Gasto total", - "topUpComingSoon": "Por ahora los créditos los añade la plataforma; la recarga con PayPal llegará pronto.", "topUpLabel": "Añadir créditos", "topUpButton": "Recargar con PayPal", "topUpRedirecting": "Redirigiendo…", @@ -510,38 +649,68 @@ "ledgerNote": "Nota", "ledgerTopUp": "Recarga", "ledgerTopUpFee": "Comisión de recarga", - "ledgerDebit": "Cargo por llamada", + "ledgerDebit": "Cargo por invocación", "ledgerPayout": "Liquidación", "ledgerRefund": "Reembolso", - "ledgerAdjust": "Ajuste" + "ledgerAdjust": "Ajuste", + "onboardingTitle": "Consigue que funcione tu primera invocación", + "onboardingSubtitle": "Tres pasos desde aquí hasta que un agente te responda.", + "stepKeyTitle": "Crea una clave de API", + "stepKeyBody": "Una sola clave invoca todos los agentes de TakoAPI. El secreto se muestra una vez, justo después de crearla.", + "stepCallTitle": "Haz tu primera invocación", + "stepCallBody": "Copia uno de los ejemplos de abajo, asigna tu clave a TAKO_KEY y ejecútalo. La invocación aparece en Uso en unos segundos.", + "stepInstallTitle": "Instálalo en tu agente de programación", + "stepInstallBody": "Enseña a Claude Code, Codex u OpenCode a descubrir e invocar agentes a través de TakoAPI.", + "stepInstallLink": "Abrir la guía de instalación", + "stepOptional": "Opcional", + "stepDone": "Hecho", + "stepTodo": "Aún sin hacer", + "keyNameLabel": "Nombre de la clave", + "keyNamePlaceholder": "Clave del gateway", + "createKeyError": "No se pudo crear la clave. Inténtalo de nuevo.", + "revokeNamed": "Revocar {name}", + "quickstartHint": "Asigna a TAKO_KEY una de las claves de arriba y luego invoca cualquier agente por su slug.", + "creditRule": "Los agentes gratuitos no cuestan nada; los de pago descuentan de tu crédito prepago, y una invocación se rechaza con HTTP 402 en cuanto el saldo quedaría por debajo de cero.", + "topUpUnavailable": "La recarga autogestionada todavía no está disponible: por ahora los créditos los añade la plataforma. En cualquier caso, el saldo de arriba está actualizado.", + "dismiss": "Descartar", + "loadError": "No se han podido cargar algunos datos del panel.", + "retry": "Reintentar", + "recentCallsCaption": "Tus invocaciones más recientes al gateway", + "chartTitle": "Invocaciones por día, últimos {days} días", + "chartTotal": "{count, number} en total", + "chartAria": "Gráfico de barras de las invocaciones al gateway por día durante los últimos {days} días, {count, number} invocaciones en total.", + "chartDay": "{date}: {calls, number} invocaciones, {errors, number} fallidas" }, "Profile": { - "title": "Mi perfil", - "signInPrompt": "Inicia sesión para ver tu perfil.", - "signIn": "Iniciar sesión", - "submissions": "Envíos", + "title": "Mis publicaciones", + "signInPrompt": "Inicia sesión para ver los agentes y las skills que has publicado.", "likes": "Me gusta", - "views": "Vistas", - "statValue": "{count, number}", - "mySubmissions": "Mis envíos", - "submitNew": "Enviar nuevo", + "views": "vistas", "tab_all": "Todos", - "tab_approved": "Aprobados", - "tab_pending": "Pendientes", - "tab_rejected": "Rechazados", - "tabCount": "{count, number}", - "emptyTab": "No hay envíos en esta pestaña", + "emptyTab": "Aún no hay skills aquí.", "submitFirst": "Envía tu primera skill", - "statusApproved": "Aprobada", - "statusPending": "Pendiente de revisión", - "statusRejected": "Rechazada", - "reviewerNote": "Nota del revisor:" + "statusApproved": "Publicado", + "statusPending": "En revisión", + "statusRejected": "Rechazado", + "reviewerNote": "Nota del revisor:", + "publishAgent": "Publicar un agente", + "submitSkill": "Enviar una skill", + "statAgents": "Agentes", + "statCalls": "Invocaciones al gateway", + "statSkills": "Skills", + "tabAgents": "Agentes", + "tabSkills": "Skills", + "statusDisabled": "Desactivado", + "kindHosted": "Invocable", + "kindProject": "Código abierto", + "noAgents": "Aún no hay agentes aquí.", + "publishFirstAgent": "Publica tu primer agente", + "myListings": "Mis publicaciones" }, "Submit": { "title": "Enviar una skill", "subtitle": "Comparte tu skill de OpenClaw con la comunidad", "signInPrompt": "Inicia sesión para enviar skills.", - "signIn": "Iniciar sesión", "skillSource": "Origen de la skill", "githubUrl": "URL de GitHub", "clawSkillsUrl": "URL de ClawSkills.sh", @@ -571,8 +740,15 @@ "submitting": "Enviando...", "enterUrlFirst": "Introduce primero una URL", "autoFillFailed": "El autocompletado falló", - "submitFailed": "No se pudo enviar", - "somethingWentWrong": "Algo salió mal" + "somethingWentWrong": "Algo salió mal", + "successTitle": "Enviada 🎉", + "pendingReview": "{name} está ahora pendiente de revisión.", + "nextStepsTitle": "Qué pasa ahora", + "nextStepReview": "Alguien del equipo revisa el repositorio y la descripción.", + "nextStepEmail": "Te avisaremos por correo cuando esté revisada.", + "nextStepProfile": "Tu página de publicaciones muestra siempre su estado actual y cualquier nota del revisor.", + "viewMyListings": "Ver mis publicaciones", + "backToSkills": "← Volver a skills" }, "SubmitAgent": { "title": "Publicar un agente", @@ -611,28 +787,45 @@ "successTitle": "Enviado 🎉", "pendingReview": "{name} está ahora pendiente de revisión.", "successDetail": "Verificaremos la AgentCard y la publicaremos en el marketplace una vez aprobada.", - "backToMarketplace": "← Volver al marketplace" + "backToMarketplace": "← Volver al marketplace", + "checkCard": "Comprobar tarjeta", + "checking": "Comprobando…", + "enterCardUrlFirst": "Introduce primero una URL de AgentCard.", + "previewTitle": "Esto es lo que publicaríamos", + "previewNoDescription": "La tarjeta no tiene descripción.", + "previewEndpoint": "Endpoint", + "previewCapabilities": "Capacidades", + "previewSkillCount": "{count, plural, =0 {No anuncia capacidades} one {# capacidad} other {# capacidades}}", + "previewSupports": "Admite", + "previewStreaming": "Streaming", + "previewPush": "Notificaciones push", + "previewHint": "Todavía no se ha guardado nada — publica abajo para enviarlo a revisión.", + "nextStepsTitle": "Qué pasa ahora", + "nextStepReview": "Alguien del equipo revisa la AgentCard y comprueba que tu endpoint responde.", + "nextStepEmail": "Te avisaremos por correo cuando esté revisado.", + "nextStepProfile": "Tu página de publicaciones muestra siempre su estado actual y cualquier nota del revisor.", + "viewMyListings": "Ver mis publicaciones" }, "AgentEngagement": { "yourRating": "Tu valoración", "ratingsCount": "{count, number} valoraciones", - "noRatings": "Sin valoraciones aún", + "noRatings": "Aún sin valoraciones", "bookmark": "Guardar", "bookmarked": "Guardado", - "signInToRate": "Inicia sesión para valorar o guardar este agente" + "signInToRate": "Inicia sesión para valorar o guardar este agente", + "rateStars": "Valorar con {count, plural, one {# estrella} other {# estrellas}}" }, "Bookmarks": { "title": "Agentes guardados", "subtitle": "Agentes que has guardado.", - "empty": "Aún no hay guardados.", + "empty": "Aún no hay agentes guardados.", "browseAgents": "Explorar agentes", - "signInPrompt": "Inicia sesión para ver tus guardados.", - "signIn": "Iniciar sesión" + "signInPrompt": "Inicia sesión para ver tus agentes guardados." }, "ScenarioPage": { "metaTitle": "Agentes de IA para {scenario}", - "metaDescription": "Descubre e invoca agentes de IA para {scenario} a través de una sola API unificada, descritos con AgentCards A2A abiertas en TakoAPI.", - "heroSubtitle": "Agentes de IA para {scenario}, invocables a través de una sola API.", + "metaDescription": "Descubre e invoca agentes de IA para {scenario} a través de una API unificada — descritos mediante AgentCards abiertas de A2A en TakoAPI.", + "heroSubtitle": "Agentes de IA para {scenario}, invocables a través de una única API.", "empty": "Aún no hay agentes en este escenario.", "browseAll": "Explorar todos los agentes", "back": "← Todos los escenarios", @@ -640,7 +833,23 @@ "indexTitle": "Explora agentes de IA por escenario", "indexDescription": "Encuentra agentes de IA según para qué los usarías: frontend, inversión, marketing, soporte y más, todos invocables a través de una API.", "indexHeroTitle": "Explorar por escenario", - "indexHeroSubtitle": "Encuentra agentes de IA según la tarea que necesitas resolver." + "indexHeroSubtitle": "Encuentra agentes de IA según la tarea que necesitas resolver.", + "indexPopularHeading": "Bien cubiertos", + "indexPopularHint": "Escenarios con un catálogo amplio de agentes que comparar.", + "indexSparseHeading": "Recién empezando", + "indexSparseHint": "Solo un puñado de agentes por ahora — un buen sitio para ser el primero.", + "indexSparseCta": "Publica un agente en alguno de estos →", + "metaTitlePaged": "Agentes de IA para {scenario} — página {page}", + "resultsLabel": "Agentes para {scenario}", + "paginationLabel": "Paginación", + "showingRange": "Mostrando {from}–{to} de {total}", + "prev": "← Anterior", + "next": "Siguiente →", + "goToPage": "Ir a la página {page}", + "ctaTitle": "¿Conoces algún agente para {scenario}?", + "ctaBody": "Este escenario todavía está poco poblado. Publica un agente y aparecerá aquí para todo el que explore {scenario}.", + "ctaButton": "Publicar un agente", + "relatedHeading": "Explora otros escenarios" }, "Badge": { "snippetTitle": "Añadir insignia", @@ -656,7 +865,8 @@ "step2": "En la página de tu ficha, copia el Markdown del cuadro de la insignia.", "step3Title": "Pégala en tu README", "step3": "Añádela cerca del inicio: visitantes y buscadores obtienen un enlace a tu página de TakoAPI.", - "findListing": "Encuentra tu ficha" + "findListing": "Encuentra tu ficha", + "exampleAlt": "Insignia de TakoAPI" }, "Blog": { "title": "Blog", @@ -666,5 +876,149 @@ "minRead": "{min} min de lectura", "published": "Publicado", "faqTitle": "Preguntas frecuentes" + }, + "NotFound": { + "title": "No hemos encontrado esa página", + "description": "Puede que el enlace esté desactualizado o que la ficha se haya retirado. Prueba con el directorio o busca desde la cabecera.", + "home": "Inicio", + "agents": "Explorar agentes", + "skills": "Explorar skills" + }, + "ErrorPage": { + "title": "Algo salió mal", + "description": "Esta página no se ha podido cargar. Volver a intentarlo suele funcionar; si no, la referencia de abajo nos ayuda a localizarla en los registros.", + "reference": "Referencia: {id}", + "retry": "Reintentar", + "home": "Ir al inicio" + }, + "Consent": { + "title": "Analítica", + "description": "Usamos Google Analytics para ver qué páginas le resultan útiles a la gente. No se carga nada ni se instala ninguna cookie a menos que lo aceptes.", + "accept": "Aceptar", + "decline": "Rechazar" + }, + "Docs": { + "metaTitle": "Documentación de la API", + "title": "Referencia de la API de TakoAPI", + "description": "Pasa de una clave de API a una invocación que funciona: autenticación, endpoints de descubrimiento, el gateway unificado de agentes, el servidor MCP alojado, códigos de error, límites de frecuencia y facturación.", + "heroBadge": "Documentación para desarrolladores", + "heroTitle": "La referencia de la API de TakoAPI", + "heroSubtitle": "Una clave, todos los agentes. Apunta cualquier cliente compatible con OpenAI a TakoAPI e invoca un agente por su slug — o explora todo el catálogo sin ninguna clave.", + "heroKeyCta": "Crear una clave de API", + "heroBrowseCta": "Explorar agentes", + "tocTitle": "En esta página", + "tocLabel": "Índice de contenidos", + "quickstartTitle": "Inicio rápido", + "quickstartIntro": "Tres pasos de cero a una respuesta. Todas las peticiones de esta página son invocaciones reales contra la API en producción.", + "quickstartStep1Title": "Crea una clave", + "quickstartStep1": "Inicia sesión y crea una clave de API en tu panel. El secreto se muestra una vez, al crearla.", + "quickstartStep2Title": "Expórtala", + "quickstartStep2": "Todos los ejemplos de esta página leen la clave de TAKO_KEY.", + "quickstartStep3Title": "Invoca un agente", + "quickstartStep3": "Pon en el modelo — o en el slug de la URL — cualquier agente alojado del catálogo.", + "quickstartSamplesLabel": "Ejemplos de invocación del inicio rápido", + "quickstartNote": "El slug de estos ejemplos es un marcador de posición, no un agente real. Copia uno real desde cualquier página de agente; solo se pueden invocar los agentes listados como alojados.", + "authenticationTitle": "Autenticación", + "authenticationIntro": "Las invocaciones al gateway se autentican con un token bearer. Los endpoints de descubrimiento son anónimos y no necesitan ninguna clave.", + "authHeaderNote": "x-api-key se acepta como alternativa a la cabecera Authorization. Las claves siempre empiezan por tako_live_.", + "authWhereTitle": "De dónde salen las claves", + "authWhere": "Crea una clave por cliente o por entorno en tu panel y revoca las que dejes de usar.", + "authOnceTitle": "El secreto se muestra una sola vez", + "authOnce": "Solo se guardan un hash SHA-256 de la clave y un prefijo corto para mostrarla, así que un secreto perdido no se puede recuperar — crea una clave de repuesto y revoca la antigua.", + "authRevokeTitle": "La revocación es inmediata", + "authRevoke": "Cada invocación vuelve a comprobar la clave contra la base de datos y no se cachea nada, así que una clave revocada se rechaza en la petición siguiente. El uso anterior se mantiene en tu historial.", + "discoveryTitle": "Descubrimiento", + "discoveryIntro": "Cuatro endpoints públicos y cacheables que no necesitan clave. Solo se devuelven agentes y skills aprobados.", + "endpointRegistrySummary": "El catálogo completo de agentes — Markdown para un modelo, JSON para código.", + "endpointRegistryNote": "Sin format=json la respuesta es Markdown (text/markdown), dividida en una tabla de agentes alojados y otra de proyectos de código abierto, y dimensionada para caber directamente en el contexto de un modelo — es lo que lee la skill de TakoAPI instalada. Ambos formatos están limitados por limit, así que comprueba truncated y los totales antes de dar por hecho que lo tienes todo. Los parámetros de texto libre se truncan a 100 caracteres.", + "paramRegistryFormat": "md (por defecto) o json.", + "paramRegistryQ": "Coincidencia de texto libre en el nombre y la descripción del agente.", + "paramRegistryCategory": "Slug de la categoría.", + "paramRegistryProtocol": "Devuelve solo los agentes que anuncian este protocolo.", + "paramRegistryKind": "Los agentes HOSTED se pueden invocar a través del gateway; las entradas PROJECT son repositorios de código abierto listados solo para descubrirlos.", + "paramRegistrySort": "Orden. Por defecto, primero los destacados, luego los más invocados y después los más recientes — y los más estrellados en el caso de los proyectos.", + "paramRegistryLimit": "Número total de entradas a devolver. Primero se rellenan los agentes alojados y después los proyectos.", + "endpointAgentDetailSummary": "Todo el detalle público de un agente, incluidas las skills A2A que anuncia.", + "endpointAgentDetailNote": "Solo resuelven los agentes aprobados; cualquier otra cosa da un 404. Los campos de moderación — notas de revisión, ids internos y avatares de quien publica — nunca forman parte de esta estructura. Se cachea en el edge durante 60 segundos.", + "paramAgentDetailSlug": "Segmento de la ruta. El slug del agente, tal como lo devuelve el registro.", + "endpointSkillsDirectorySummary": "El catálogo de skills de OpenClaw, en Markdown por defecto.", + "endpointSkillsDirectoryNote": "Markdown primero, como el registro. install es null para las skills sin comando de instalación publicado — recurre entonces al enlace de github. downloads es una cifra de terceros extraída de la propia ficha de la skill, no un recuento de instalaciones de TakoAPI.", + "paramSkillsDirFormat": "md (por defecto) o json.", + "paramSkillsDirQ": "Coincidencia de texto libre en el nombre y la descripción de la skill.", + "paramSkillsDirCategory": "Slug de la categoría.", + "paramSkillsDirPage": "Número de página, empezando en 1.", + "paramSkillsDirLimit": "Resultados por página.", + "endpointSkillsSearchSummary": "Búsqueda de texto completo en el catálogo de skills, solo JSON.", + "endpointSkillsSearchNote": "q es obligatorio: sin él, el endpoint devuelve una lista vacía en lugar del catálogo entero. Cada resultado es la fila completa del catálogo menos readme y reviewNote; el ejemplo de arriba está abreviado.", + "paramSkillsSearchQ": "Término de búsqueda. Coincide con el nombre, la descripción y el autor de la skill.", + "paramSkillsSearchCategory": "Slug de la categoría.", + "paramSkillsSearchPage": "Número de página, empezando en 1.", + "paramSkillsSearchLimit": "Resultados por página.", + "gatewayTitle": "Gateway", + "gatewayIntro": "Tres formas de invocar el mismo agente. Las tres requieren clave, las tres facturan igual y las tres aceptan el slug del agente donde normalmente iría un nombre de modelo. Solo se pueden invocar los agentes de tipo HOSTED.", + "endpointChatCompletionsSummary": "Compatible con OpenAI. Apunta cualquier SDK de OpenAI a /v1 y pon en model el slug de un agente.", + "endpointChatCompletionsNote": "Se reenvía la conversación entera: una parte de texto A2A por turno, en orden, con el rol de OpenAI adjunto a cada parte. usage siempre está presente pero siempre vale cero — en este gateway nada cuenta tokens, y un agente no tiene por qué ser un LLM. Usa stream: true para la forma SSE de arriba, y stream_options.include_usage para recibir además el fragmento final de usage. Una invocación sin streaming expira a los 30 segundos; un stream, a los 120.", + "endpointA2AMessageSummary": "Passthrough A2A nativo. Devuelve la respuesta JSON-RPC del propio agente, sin modificar.", + "endpointA2AMessageNote": "Se retransmite como una llamada A2A message/send. Envía el atajo de texto, o un mensaje A2A completo — con parts, taskId y contextId — para continuar una tarea de varios turnos o pasar partes de archivo y de datos. El código de estado y el cuerpo del agente se retransmiten intactos, y una petición sin contenido se rechaza con un 400 en lugar de facturarse. Expira a los 30 segundos.", + "endpointA2AStreamSummary": "La misma invocación sobre Server-Sent Events, para agentes que hacen streaming.", + "endpointA2AStreamNote": "Retransmite tal cual los frames A2A message/stream del agente como text/event-stream. Este endpoint solo admite el atajo de texto. La conexión se corta a los 120 segundos, y la invocación se cobra solo cuando el stream llega a su fin — consulta Límites de frecuencia y facturación.", + "labelQueryParams": "Parámetros", + "labelRequest": "Cuerpo de la petición", + "labelExample": "Ejemplo", + "labelResponse": "Respuesta", + "labelStreaming": "Respuesta con stream: true", + "labelA2AMessageForm": "Forma completa del mensaje A2A", + "labelAuthRequired": "Requiere clave", + "labelNoAuth": "Sin clave", + "labelBillable": "Facturable", + "labelFree": "Gratis", + "labelParamName": "Nombre", + "labelParamType": "Tipo", + "labelParamDesc": "Descripción", + "paramsTableCaption": "Parámetros de {path}", + "mcpTitle": "Servidor MCP", + "mcpIntro": "TakoAPI ofrece un servidor Model Context Protocol alojado y sin estado sobre Streamable HTTP en {url}. Regístralo una vez y tu agente de programación podrá buscar en el catálogo e invocarlo como herramientas nativas.", + "mcpRegisterTitle": "Regístralo", + "mcpRegisterLabel": "Comandos de registro de MCP", + "mcpAuthNote": "Las tres herramientas de lectura funcionan de forma anónima. invoke_agent gasta crédito, así que necesita tu clave: pásala como cabecera Authorization (Claude Code), como bearer_token_env_var (Codex) o en un bloque headers (OpenCode).", + "mcpToolsTitle": "Herramientas", + "mcpToolsTableCaption": "Herramientas que expone el servidor MCP de TakoAPI", + "mcpToolColumn": "Herramienta", + "mcpToolAccessColumn": "Acceso", + "mcpToolWhatColumn": "Qué hace", + "mcpToolKeyRequired": "Requiere clave", + "mcpToolAnonymous": "Anónimo", + "mcpToolSearchAgents": "Busca en el registro por palabra clave, categoría o protocolo. Devuelve slugs, protocolos y precios.", + "mcpToolGetAgent": "Todo lo que se sabe de un agente a partir de su slug: capacidades, protocolos, endpoint, precio y skills anunciadas.", + "mcpToolSearchSkills": "Busca en el catálogo de skills de OpenClaw. Sin consulta, devuelve las mejores skills seleccionadas.", + "mcpToolInvokeAgent": "Invoca un agente a través del gateway y devuelve su respuesta. Requiere clave y gasta crédito.", + "mcpRateNote": "El endpoint MCP está limitado a 120 peticiones por minuto y dirección IP, aparte del cupo del gateway de tu clave.", + "mcpInstallNote": "¿Prefieres un archivo de skill a un servidor? El instalador enseña a Claude Code, Codex y OpenCode a invocar estos endpoints por HTTP normal.", + "errorsTitle": "Errores", + "errorsIntro": "Todo fallo del gateway es JSON. Estos son los estados que devuelve el propio gateway.", + "errorsTableCaption": "Estados de error que devuelve el gateway", + "errorStatusColumn": "Estado", + "errorCodeColumn": "Código", + "errorWhenColumn": "Cuándo", + "errorBodyColumn": "Cuerpo de ejemplo", + "error400When": "El slug corresponde a un proyecto de código abierto sin endpoint invocable, el cuerpo de la petición no pasó la validación, o todos los turnos del mensaje estaban vacíos.", + "error401When": "La clave falta, está mal formada, es desconocida o está revocada.", + "error402When": "El agente es de pago y cobrar la invocación dejaría tu saldo por debajo de cero. Se comprueba antes de contactar con el agente, así que no se gasta nada.", + "error404When": "Ningún agente aprobado tiene ese slug.", + "error429When": "La clave superó su cupo en los últimos 60 segundos. Retry-After indica los segundos que faltan para que se reinicie la ventana.", + "error502When": "El agente no respondió, expiró, no devolvió stream o informó de un error JSON-RPC con un HTTP 200. Nunca se cobra.", + "errorsOpenAiNote": "En /v1/chat/completions los mismos fallos llegan en el envoltorio de OpenAI — con type, param y code siempre presentes — de modo que un SDK de OpenAI lanza sus excepciones habituales:", + "errorsPassthroughNote": "Un estado devuelto por el propio agente se retransmite sin cambios por el endpoint message de A2A, así que un 4xx ahí puede ser suyo y no nuestro.", + "limitsTitle": "Límites de frecuencia y facturación", + "limitsRateTitle": "Límites de frecuencia", + "limitsRate": "El gateway permite 120 peticiones cada 60 segundos por clave de API. La ventana se comparte entre todas las instancias del servidor y se calcula sobre la propia clave, no sobre tu dirección IP, así que ni se relaja bajo carga ni se estrecha detrás de un NAT compartido.", + "limitsRateOverride": "Una clave puede llevar su propio cupo, que sustituye al valor por defecto solo para esa clave. Al superar el límite recibes un 429 con una cabecera Retry-After, en segundos.", + "limitsBillingTitle": "Facturación", + "limitsBillingFree": "Los agentes con precio FREE no cuestan nada — ni tampoco los agentes cuyo precio unitario falta o es cero. El gateway nunca se inventa un cargo.", + "limitsBillingPriced": "Un agente de pago descuenta su precio unitario de tu crédito prepago, una vez por invocación. No hay medición por tokens, así que una invocación es una unidad, dure lo que dure.", + "limitsBillingPreflight": "Tu saldo se comprueba antes de contactar con el agente. Si el cargo te dejara por debajo de cero, la invocación se rechaza con un 402 y nunca se llama al agente — no hay descubierto.", + "limitsBillingFailures": "Las invocaciones que fallan no se cobran. Un 4xx o 5xx del agente, un tiempo de espera agotado, un endpoint inalcanzable o un error JSON-RPC registran el intento y no facturan nada.", + "limitsBillingStreams": "Los streams solo se cobran cuando se completan. Un stream que se aborta, expira, falla a mitad o del que te desconectas antes de tiempo se registra y no se factura.", + "limitsBillingLedger": "Cada cargo escribe una entrada inmutable en el libro de movimientos, asociada a la invocación que lo provocó. El saldo, el libro de movimientos y el uso por invocación están todos en tu panel." } } diff --git a/messages/fr.json b/messages/fr.json index a961904d3..3be034b27 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -1,66 +1,84 @@ { "Metadata": { "tagline": "Une seule API pour accéder à tous les agents", - "description": "Découvrez et invoquez des agents IA via une API unifiée — un OpenRouter pour les agents. Parcourez un annuaire de centaines de projets d'agents open source et de milliers de skills OpenClaw pour votre agent de code." + "description": "Découvre et invoque des agents IA via une API unifiée — un OpenRouter pour les agents. Parcours un annuaire de centaines de projets d'agents open source et de milliers de skills OpenClaw pour ton agent de code." }, "LocaleSwitcher": { "label": "Langue" }, "Header": { - "searchPlaceholder": "Rechercher des agents...", "agents": "Agents", "skills": "Skills", - "trending": "Tendances", - "publish": "Publier", "publishAgent": "Publier un agent", "account": "Compte", - "mySkills": "Mes skills", - "myBookmarks": "Favoris", - "apiUsage": "API et utilisation", + "myBookmarks": "Agents enregistrés", + "apiUsage": "Clés API et utilisation", "adminPanel": "Panneau d'administration", "signIn": "Se connecter", - "signOut": "Se déconnecter" + "signOut": "Se déconnecter", + "mainNav": "Navigation principale", + "skipToContent": "Aller au contenu", + "openMenu": "Ouvrir le menu", + "closeMenu": "Fermer le menu", + "projects": "Projets", + "install": "Installer", + "getApiKey": "Obtenir une clé API", + "search": "Rechercher", + "searchScope": "Rechercher dans", + "scopeAgents": "Agents", + "scopeProjects": "Projets", + "scopeSkills": "Skills", + "searchAgentsPlaceholder": "Cherche un agent par ce qu'il fait…", + "searchProjectsPlaceholder": "Cherche un projet d'agent open source…", + "searchSkillsPlaceholder": "Cherche un skill pour ton agent de code…", + "myListings": "Mes publications", + "docs": "Documentation" }, "Footer": { - "tagline": "Une seule API pour accéder à tous les agents — plus des milliers de skills OpenClaw pour votre agent de code.", - "agentRegistry": "Registre d'agents :", + "tagline": "Une seule API pour accéder à tous les agents — découvre, appelle et facture n'importe quel agent via un seul endpoint.", + "agentRegistry": "Registre d'agents :", "platform": "Plateforme", "resources": "Ressources", - "installInAgent": "Installer dans votre agent", + "installInAgent": "Installer dans ton agent de code", "browseAgents": "Parcourir les agents", "publishAnAgent": "Publier un agent", - "developerDashboard": "Tableau de bord développeur", + "developerDashboard": "Clés API et utilisation", "browseSkills": "Parcourir les skills", - "awesomeList": "Awesome List", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Conçu pour la communauté OpenClaw.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. Sous licence MIT.", + "blog": "Blog", + "badge": "Badge README", + "scenarios": "Parcourir par scénario", + "a2aSpec": "Protocole A2A", + "github": "GitHub", + "docs": "Documentation de l'API" }, "Home": { "heroTitle": "Une seule API pour accéder à tous les agents", - "heroSubtitle": "Découvrez et invoquez {agents, number} agents IA via une API unifiée — plus {skills, number} skills pour votre agent de code.", + "heroSubtitle": "Découvre des agents IA, puis appelle n'importe lequel via un seul endpoint avec une seule clé. Natif A2A, compatible OpenAI, facturé à l'appel.", "browseAgents": "Parcourir les agents", - "publishAnAgent": "Publier un agent", - "agentRegistry": "Registre d'agents :", - "browseByScenario": "Parcourir par scénario", - "allAgents": "Tous les agents", + "browseByScenario": "Parcourir par besoin", "featuredAgents": "Agents en vedette", "browseAll": "Tout parcourir", "popularProjects": "Projets open source populaires", - "selfHostable": "{count, number} auto-hébergeable(s)", - "skillTitle": "Skill TakoAPI pour OpenClaw", - "official": "Officiel", - "skillDescription": "Recherchez, installez et gérez les skills OpenClaw par simple conversation — propulsé par la marketplace TakoAPI.", - "viewOnGithub": "Voir sur GitHub", - "askAgent": "ou demandez à votre agent : « Installe le skill TakoAPI »", - "usingCodingAgent": "Vous utilisez Claude Code, Codex ou OpenCode ?", - "oneCommandInstall": "Installation en une commande", - "mustHaveSkills": "Skills incontournables", - "viewRankings": "Voir le classement", - "categories": "Catégories", - "viewAll": "Tout voir", - "moreCategories": "{count, plural, one {+# catégorie de plus} other {+# catégories de plus}}", - "latestSkills": "Derniers skills" + "selfHostable": "{count, number} auto-hébergeables", + "categories": "Catégories :", + "moreCategories": "{count, plural, one {+# autre catégorie} many {+# autres catégories} other {+# autres catégories}}", + "getApiKey": "Obtenir une clé API", + "catalogSummary": "{agents, number} agents appelables · {projects, number} projets open source · {skills, number} skills pour agents de code", + "quickstartLabel": "30 secondes jusqu'au premier appel", + "quickstartNote": "Tu préfères travailler depuis ton terminal ? Installe TakoAPI dans ton agent de code.", + "allScenarios": "Tous les scénarios", + "callableNow": "{count, number} appelables via la passerelle", + "installTitle": "Utilise TakoAPI depuis ton agent de code", + "installDescription": "Une seule commande apprend à Claude Code, Codex ou OpenCode à chercher dans l'annuaire et à appeler des agents pour toi — ou enregistre plutôt le serveur MCP hébergé.", + "installCta": "Guide d'installation", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Skills pour ton agent de code", + "skillsSubtitle": "{count, number} skills OpenClaw, les plus installés et les plus récents.", + "quickstartTabDiscover": "1. Découvrir", + "quickstartTabCall": "2. Appeler", + "quickstartTabFromAgent": "3. Depuis ton agent" }, "Scenarios": { "frontend": "Frontend", @@ -85,52 +103,74 @@ "notFound": "Agent introuvable", "backToMarketplace": "← Marketplace des agents", "openSourceProject": "Projet open source", - "signedCard": "Carte signée", - "verifiedPublisher": "Éditeur vérifié", "by": "par {name}", "publisherOpenSource": "open source", "publisherUnknown": "inconnu", - "scenario": "Scénario :", + "scenario": "Scénario :", "stars": "{count, number} étoiles", "selfHost": "Auto-héberger", "streaming": "Streaming", "pushNotifications": "Notifications push", "skills": "Skills", "skillsCount": "({count, number})", - "noSkillsProject": "Un projet open source — explorez le code et auto-hébergez-le depuis GitHub.", + "noSkillsProject": "Un projet open source — explore le code et auto-héberge-le depuis GitHub.", "noSkillsHosted": "Cet agent n'a pas déclaré de skills structurés dans son AgentCard.", - "inputMode": "entrée : {mode}", - "outputMode": "sortie : {mode}", - "openSourceProjectHeading": "Projet open source", - "openSourceProjectNote": "Auto-hébergez-le ou explorez le code. Non invocable via la passerelle TakoAPI.", + "inputMode": "entrée : {mode}", + "outputMode": "sortie : {mode}", + "openSourceProjectNote": "Auto-héberge-le ou explore le code. Non invocable via la passerelle TakoAPI.", "viewOnGithub": "Voir sur GitHub", - "integration": "Intégration", "endpoint": "Endpoint", "agentCard": "AgentCard", - "homepage": "Page d'accueil", - "callThroughTakoApi": "Invoquez-le via TakoAPI", - "gatewayPhase2": "Passerelle unifiée — l'invocation arrive en phase 2.", + "homepage": "Site web", + "callThroughTakoApi": "Appelle-le via TakoAPI", "breadcrumbHome": "Accueil", - "breadcrumbMarketplace": "Marketplace des agents" + "breadcrumbMarketplace": "Marketplace des agents", + "gatewayHint": "Appelle-le avec ta clé API — crées-en une dans le tableau de bord.", + "healthOk": "Répond", + "healthDegraded": "Dégradé", + "healthDown": "Ne répond pas", + "healthCheckedAt": "Dernière vérification {time}", + "atAGlance": "En bref", + "protocols": "Protocoles", + "protocolsNone": "Non déclarés", + "yes": "Oui", + "no": "Non", + "pricing": "Tarification", + "health": "État", + "healthUnknown": "Pas encore vérifié", + "totalCalls": "Total des appels", + "numberValue": "{count, number}", + "averageRating": "Note moyenne", + "ratingValue": "{rating} / 5 · {count, number} avis", + "notRated": "Pas encore noté", + "callExamplesLabel": "Exemples d'appel", + "repository": "Dépôt", + "language": "Langage", + "license": "Licence", + "licenseNone": "Non précisée", + "lastPush": "Dernier push", + "topics": "Sujets", + "readmeHeading": "Depuis le README", + "readmeNote": "Extrait du README du projet sur GitHub." }, "Agents": { "metaTitle": "Marketplace des agents", - "metaDescription": "Une seule API pour accéder à tous les agents. Parcourez et invoquez des agents IA et des centaines de projets d'agents open source via une API unifiée — décrits par des AgentCards A2A ouvertes.", + "metaDescription": "Une seule API pour accéder à tous les agents. Parcours et invoque des agents IA et des centaines de projets d'agents open source via une API unifiée — décrits par des AgentCards A2A ouvertes.", "ogTitle": "Marketplace des agents — {siteName}", - "ogDescription": "Découvrez et invoquez des agents IA et des projets d'agents open source via une API unifiée.", + "ogDescription": "Découvre et invoque des agents IA et des projets d'agents open source via une API unifiée.", "heroTitle": "Marketplace des agents", "heroTagline": "Une seule API pour accéder à tous les agents.", - "heroSubtitle": "Découvrez et invoquez des agents IA via une API unifiée — décrits par des AgentCards A2A ouvertes.", + "heroSubtitle": "Découvre et invoque des agents IA via une API unifiée — décrits par des AgentCards A2A ouvertes.", "searchPlaceholder": "Rechercher des agents par nom ou description…", "search": "Rechercher", "all": "Tous", "kindAgents": "Agents", "kindProjects": "Projets", - "scenarioFilter": "Scénario :", - "protocolFilter": "Protocole :", - "pricingFilter": "Tarification :", - "categoryFilter": "Catégorie :", - "sortLabel": "Trier :", + "scenarioFilter": "Scénario :", + "protocolFilter": "Protocole :", + "pricingFilter": "Tarification :", + "categoryFilter": "Catégorie :", + "sortLabel": "Trier :", "sortLatest": "Plus récents", "sortStars": "Étoiles", "sortPopular": "Populaires", @@ -140,16 +180,29 @@ "pricingPerCall": "par appel", "pricingPerTask": "par tâche", "pricingPerToken": "Par 1k tokens", - "count": "{count, plural, =0 {Aucun agent} one {# agent} other {# agents}}", - "emptyTitle": "Aucun agent ne correspond encore à vos filtres.", - "emptyCta": "Publiez le premier →", + "count": "{count, plural, =0 {Aucun agent} one {# agent} many {# d'agents} other {# agents}}", + "emptyTitle": "Aucun agent ne correspond encore à tes filtres.", + "emptyCta": "Publie le premier →", "prev": "← Précédent", "next": "Suivant →", - "pageOf": "Page {page} sur {total}" + "pageOf": "Page {page} sur {total}", + "filtersTitle": "Filtres", + "filtersWithCount": "Filtres ({count})", + "activeFiltersLabel": "Filtres actifs :", + "removeFilter": "Retirer le filtre : {label}", + "clearAll": "Tout effacer", + "kindLabel": "Type", + "scenarioGroup": "Scénario", + "protocolGroup": "Protocole", + "pricingGroup": "Tarification", + "categoryGroup": "Catégorie", + "searchChip": "Recherche : {query}", + "resultsLabel": "Résultats", + "paginationLabel": "Pagination", + "goToPage": "Aller à la page {page}", + "showingRange": "Affichage de {from} à {to} sur {total}" }, "Components": { - "homeSearchPlaceholder": "Rechercher des skills par nom, description ou auteur...", - "homeSearchButton": "Rechercher", "agentFree": "Gratuit", "agentPerCall": "{price}/appel", "agentPerTask": "{price}/tâche", @@ -161,49 +214,53 @@ "agentBadgeStreaming": "streaming", "agentOpenSource": "open source", "agentSelfHost": "auto-héberger", - "agentSkills": "{count, plural, one {# skill} other {# skills}}", + "agentSkills": "{count, plural, one {# skill} many {# de skills} other {# skills}}", "skillBy": "par {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} appels", + "healthOk": "Répond", + "healthDegraded": "Dégradé", + "healthDown": "Ne répond pas" }, "Install": { "metaTitle": "Installer dans Claude Code, Codex et OpenCode", "title": "Installer TakoAPI dans Claude Code, Codex et OpenCode", - "description": "Une seule commande installe le skill TakoAPI dans votre agent de code — découvrez et invoquez des agents IA via une passerelle unifiée, et explorez le catalogue de skills OpenClaw. Compatible avec Claude Code, Codex et OpenCode.", - "stepRunInstallerName": "Lancer le programme d'installation", - "stepRunInstallerText": "Exécutez : curl -fsSL https://takoapi.com/install.sh | sh", - "stepUseAgentName": "Utiliser TakoAPI depuis votre agent", - "stepUseAgentText": "Demandez à votre agent de code de trouver ou d'invoquer un agent IA sur TakoAPI.", + "description": "Une seule commande installe le skill TakoAPI dans ton agent de code — découvre et invoque des agents IA via une passerelle unifiée, et cherche dans le catalogue de skills OpenClaw. Compatible avec Claude Code, Codex et OpenCode.", + "stepRunInstallerName": "Lancer l'installateur", + "stepRunInstallerText": "Exécute : curl -fsSL https://takoapi.com/install.sh | sh", + "stepUseAgentName": "Utiliser TakoAPI depuis ton agent", + "stepUseAgentText": "Demande à ton agent de code de trouver ou d'invoquer un agent IA sur TakoAPI.", "heroBadge": "Une commande, trois agents", - "heroTitle": "Installez TakoAPI dans votre agent de code", - "heroSubtitle": "Déposez le skill TakoAPI dans Claude Code, Codex ou OpenCode — puis découvrez et invoquez n'importe quel agent IA via une passerelle unifiée, directement depuis votre terminal.", - "whatTitle": "Ce que fait le programme d'installation", - "whatWritesSkill": "Écrit un unique skill TakoAPI sous espace de noms dans le répertoire de skills propre à chaque agent — il ne modifie jamais vos fichiers de configuration partagés.", - "whatTeachesAgent": "Apprend à votre agent à appeler le registre TakoAPI et la passerelle via HTTP — aucune dépendance supplémentaire, aucun serveur à exécuter.", - "whatMcpServer": "Vous préférez des outils hébergés ? Enregistrez plutôt le serveur MCP TakoAPI — claude mcp add --transport http takoapi https://takoapi.com/mcp — qui expose search_agents, get_agent, search_skills et invoke_agent comme outils MCP natifs.", + "heroTitle": "Installe TakoAPI dans ton agent de code", + "heroSubtitle": "Dépose le skill TakoAPI dans Claude Code, Codex ou OpenCode — puis découvre et invoque n'importe quel agent IA via une passerelle unifiée, directement depuis ton terminal.", + "whatTitle": "Ce que fait l'installateur", + "whatWritesSkill": "Écrit un unique skill TakoAPI, sous son propre espace de noms, dans le répertoire de skills de chaque agent — il ne modifie jamais tes fichiers de configuration partagés.", + "whatTeachesAgent": "Apprend à ton agent à appeler le registre TakoAPI et la passerelle via HTTP — aucune dépendance supplémentaire, aucun serveur à faire tourner.", + "whatMcpServer": "Tu préfères des outils hébergés ? Enregistre plutôt le serveur MCP TakoAPI — claude mcp add --transport http takoapi https://takoapi.com/mcp — qui expose search_agents, get_agent, search_skills et invoke_agent comme outils MCP natifs.", "whatIdempotent": "Est idempotent (sûr à réexécuter) et entièrement réversible avec --uninstall.", - "whatApiKey": "Pour la passerelle facturée à l'usage, créez une clé API dans votre tableau de bord et exportez-la dans TAKO_KEY.", - "footnote": "Sous Windows ou déjà sous Node ? Utilisez npx takoapi-install. Vous préférez lire le script d'abord ? Il s'agit de shell simple, sur takoapi.com/install.sh." + "whatApiKey": "Pour la passerelle facturée à l'usage, crée une clé API dans ton tableau de bord et exporte-la dans TAKO_KEY.", + "footnote": "Sous Windows ou déjà sous Node ? Utilise npx takoapi-install. Tu préfères lire le script d'abord ? C'est du shell simple, sur takoapi.com/install.sh." }, "InstallTabs": { - "universalNote": "Installe le skill TakoAPI dans Claude Code, Codex et OpenCode — détectés automatiquement. Sûr à réexécuter, sans root. Sous Windows ou sous Node, utilisez .", + "universalNote": "Installe le skill TakoAPI dans Claude Code, Codex et OpenCode — détectés automatiquement. Sûr à réexécuter, sans root. Sous Windows ou sous Node, utilise .", "tablistLabel": "Agents de code", "claudeCodeTagline": "La CLI d'Anthropic", "codexTagline": "La CLI d'OpenAI", "opencodeTagline": "opencode.ai", - "skillHeading": "Skill — apprend à votre agent à appeler TakoAPI", - "skillUniversalHint": "…ou utilisez la commande universelle ci-dessus — elle couvre aussi {name}.", + "skillHeading": "Skill — apprend à ton agent à appeler TakoAPI", + "skillUniversalHint": "…ou utilise la commande universelle ci-dessus — elle couvre aussi {name}.", "writesHeading": "Écrit", "howToUseHeading": "Comment l'utiliser", - "claudeCodeUse": "Se charge automatiquement — demandez simplement à Claude de trouver un agent sur TakoAPI.", - "codexUse": "Invoquez avec $takoapi, ou choisissez-le dans le menu /skills.", - "opencodeUse": "Exécutez /takoapi , ou transmettez à @takoapi.", + "claudeCodeUse": "Se charge automatiquement — demande simplement à Claude de trouver un agent sur TakoAPI.", + "codexUse": "Invoque avec $takoapi, ou choisis-le dans le menu /skills.", + "opencodeUse": "Exécute /takoapi '', ou passe la main à @takoapi.", "mcpHeading": "Serveur MCP — outils hébergés, rien à installer", - "claudeCodeMcpHint": "Une seule commande. Ajoutez --header \"Authorization: Bearer $TAKO_KEY\" pour activer invoke_agent.", - "codexMcpHint": "Ajoutez à ~/.codex/config.toml", - "opencodeMcpHint": "Fusionnez dans ~/.config/opencode/opencode.json", - "mcpToolsNote": "Ajoute les outils : search_agents, get_agent, search_skills, invoke_agent.", + "claudeCodeMcpHint": "Une seule commande. Ajoute --header \"Authorization: Bearer $TAKO_KEY\" pour activer invoke_agent.", + "codexMcpHint": "Ajoute à ~/.codex/config.toml", + "opencodeMcpHint": "Fusionne dans ~/.config/opencode/opencode.json", + "mcpToolsNote": "Ajoute les outils : search_agents, get_agent, search_skills, invoke_agent.", "uninstallHeading": "Désinstaller", - "uninstallNote": "Supprime uniquement les fichiers propres à TakoAPI — il ne touche jamais à la configuration de vos autres agents.", + "uninstallNote": "Supprime uniquement les fichiers propres à TakoAPI — il ne touche jamais à la configuration de tes autres agents.", "copy": "Copier", "copied": "Copié" }, @@ -214,7 +271,7 @@ "downloads": "Téléchargements", "stars": "Étoiles", "views": "Vues", - "likes": "{count, plural, one {# mention J'aime} other {# mentions J'aime}}", + "likes": "{count, plural, one {# mention J'aime} many {# de mentions J'aime} other {# mentions J'aime}}", "brief": "Résumé", "description": "Description", "whatThisSkillDoes": "Ce que fait ce skill", @@ -226,20 +283,25 @@ "requirements": "Prérequis", "installation": "Installation", "clawHubCli": "CLI ClawHub", - "openClawChat": "OpenClaw Chat (collez ceci dans votre conversation)", "links": "Liens", "github": "GitHub", - "copyGithubUrl": "Copier l'URL GitHub", "clawHub": "ClawHub", - "skillNotFound": "Skill introuvable" + "codingAgent": "Agent de code", + "agentPrompt": "Installe le skill depuis {url}", + "installHintRegistry": "Exécute la commande CLI, ou colle l'instruction dans Claude Code, Cursor, Codex ou l'agent de code que tu utilises.", + "installHintRepo": "Ce skill n'est pas publié sur le registre ClawHub. Colle l'instruction dans ton agent de code — Claude Code, Cursor, Codex — et il installera le skill directement depuis le dépôt source.", + "installUnavailable": "Aucune source d'installation n'a encore été publiée pour ce skill.", + "openRepository": "Ouvrir le dépôt", + "signInToLike": "Connecte-toi pour aimer ce skill", + "downloadsSource": "Téléchargements rapportés par clawskills.sh" }, "Skills": { "metaTitle": "Parcourir les skills", - "metaDescription": "Parcourez des milliers de skills OpenClaw pour votre agent de code, répartis dans des dizaines de catégories — avec recherche, filtres et un classement des tendances.", + "metaDescription": "Parcours des milliers de skills OpenClaw pour ton agent de code, répartis dans des dizaines de catégories — avec recherche, filtres et un classement des tendances.", "ogTitle": "Parcourir les skills — {siteName}", - "ogDescription": "Des milliers de skills OpenClaw pour votre agent de code.", + "ogDescription": "Des milliers de skills OpenClaw pour ton agent de code.", "heading": "Parcourir les skills", - "searchLabel": "Recherche : « {query} »", + "searchLabel": "Recherche : « {query} »", "count": "{count, number} skills trouvés", "sortLatest": "Plus récents", "sortPopular": "Les plus populaires", @@ -256,16 +318,35 @@ "agentGeneric": "Générique", "all": "Tous", "emptyTitle": "Aucun skill trouvé", - "emptyHint": "Essayez d'ajuster votre recherche ou vos filtres", - "pageOf": "Page {page} sur {total}" + "emptyHint": "Essaie d'ajuster ta recherche ou tes filtres", + "pageOf": "Page {page} sur {total}", + "metaTitleSearch": "Skills correspondant à « {query} »", + "metaTitlePaged": "Parcourir les skills — page {page}", + "searchInputLabel": "Rechercher des skills", + "searchPlaceholder": "Rechercher des skills par nom, description ou auteur", + "searchButton": "Rechercher", + "agentFilter": "Agent", + "agentZed": "Zed", + "sourceFilter": "Source", + "sourceGithub": "GitHub", + "sourceCommunity": "Communauté", + "sourceCurated": "Sélection", + "categoryFilter": "Catégorie", + "sortLabel": "Trier", + "sortRelevance": "Pertinence", + "clearFilters": "Effacer les filtres", + "paginationLabel": "Pages de la liste de skills", + "goToPage": "Aller à la page {page}", + "prev": "Précédent", + "next": "Suivant" }, "Trending": { "metaTitle": "Skills tendance", - "metaDescription": "Les skills OpenClaw les plus téléchargés et les plus appréciés en ce moment — un classement en direct de ce que les agents de code installent.", + "metaDescription": "Les skills OpenClaw les plus téléchargés et les plus appréciés — un classement établi à partir des chiffres de téléchargement publiés par clawskills.sh, mis à jour chaque jour.", "ogTitle": "Skills tendance — {siteName}", - "ogDescription": "Un classement en direct des skills OpenClaw les plus populaires.", + "ogDescription": "Un classement quotidien des skills OpenClaw les plus populaires.", "title": "Skills tendance", - "subtitle": "Découvrez les skills OpenClaw les plus populaires, classés par la communauté", + "subtitle": "Découvre les skills OpenClaw les plus populaires, classés par la communauté", "searchPlaceholder": "Rechercher des skills tendance…", "categories": "Catégories", "allCategories": "Toutes les catégories", @@ -274,18 +355,24 @@ "likes": "Mentions J'aime", "views": "Vues", "skill": "Skill", - "count": "{count, plural, =0 {Aucun skill} one {# skill} other {# skills}}", - "countInCategory": "{count, plural, =0 {Aucun skill} one {# skill} other {# skills}} dans {category}", + "count": "{count, plural, =0 {Aucun skill} one {# skill} many {# de skills} other {# skills}}", + "countInCategory": "{count, plural, =0 {Aucun skill} one {# skill} many {# de skills} other {# skills}} dans {category}", "emptyTitle": "Aucun skill trouvé", - "emptySubtitle": "Essayez d'ajuster votre recherche ou vos filtres", + "emptySubtitle": "Essaie d'ajuster ta recherche ou tes filtres", "byAuthor": "par {author}", "showing": "Affichage de {from, number} à {to, number} sur {total, number}", "prev": "Précédent", - "next": "Suivant" + "next": "Suivant", + "provenanceNote": "Les nombres de téléchargements et d'étoiles viennent du catalogue amont — clawskills.sh et GitHub — et sont actualisés chaque nuit ; ils ne mesurent pas les installations via TakoAPI. Les mentions J'aime et les vues sont comptées ici.", + "search": "Rechercher", + "sortLabel": "Trier par", + "rank": "Rang", + "goToPage": "Aller à la page {page}", + "paginationLabel": "Pages du classement" }, "Admin": { "accessDenied": "Accès refusé", - "accessDeniedDescription": "Vous avez besoin de privilèges administrateur pour accéder à cette page.", + "accessDeniedDescription": "Tu as besoin de privilèges administrateur pour accéder à cette page.", "goHome": "Aller à l'accueil", "backToSite": "Retour au site", "panelTitle": "Panneau d'administration", @@ -319,7 +406,7 @@ "confirm": "Confirmer", "preview": "Aperçu", "agentsTitle": "Agents", - "agentsSubtitle": "Examinez et modérez les agents soumis.", + "agentsSubtitle": "Examine et modère les agents soumis.", "filterPending": "En attente", "filterApproved": "Approuvés", "filterRejected": "Rejetés", @@ -329,17 +416,17 @@ "uncategorized": "sans catégorie", "agentMetaSkills": "{count, number} skills", "noScenarios": "aucun scénario", - "reviewNoteLabel": "Note : {note}", + "reviewNoteLabel": "Note : {note}", "editScenarios": "Modifier les scénarios", "actionApprove": "Approuver", "actionReject": "Rejeter", "feature": "Mettre en vedette", "unfeature": "Retirer de la vedette", - "scenariosEditorHint": "场景 Scénarios — 点击切换 / cliquez pour assigner", - "scenarioOption": "{nameZh} · {name}", - "selectedCount": "{count, number} sélectionné(s)", - "rejectReasonPrompt": "Motif du rejet (facultatif) :", - "deleteAgentConfirm": "Supprimer définitivement cet agent ?", + "scenariosEditorHint": "Scénarios — clique pour assigner ou retirer", + "scenarioOption": "{name}", + "selectedCount": "{count, number} sélectionnés", + "rejectReasonPrompt": "Motif du rejet (facultatif) :", + "deleteAgentConfirm": "Supprimer définitivement cet agent ?", "skillsTitle": "Gestion des skills", "searchSkillsPlaceholder": "Rechercher des skills...", "allStatuses": "Tous les statuts", @@ -347,12 +434,12 @@ "filterStatusApproved": "Approuvés", "filterStatusRejected": "Rejetés", "allCategories": "Toutes les catégories", - "selectedLabel": "{count, number} sélectionné(s)", + "selectedLabel": "{count, number} sélectionnés", "batchApprove": "Approuver", "batchReject": "Rejeter", "batchFeature": "Mettre en vedette", "batchDelete": "Supprimer", - "batchDeleteConfirm": "Supprimer {count, number} skill(s) ? Cette action est irréversible.", + "batchDeleteConfirm": "Supprimer {count, number} skill(s) ? Cette action est irréversible.", "colName": "Nom", "colAuthor": "Auteur", "colCategory": "Catégorie", @@ -374,7 +461,7 @@ "optionRejected": "Rejeté", "fieldFeatured": "En vedette", "deleteSkillTitle": "Supprimer le skill", - "deleteSkillConfirm": "Voulez-vous vraiment supprimer {name} ? Cette action est irréversible.", + "deleteSkillConfirm": "Veux-tu vraiment supprimer {name} ? Cette action est irréversible.", "categoriesTitle": "Catégories", "addCategory": "Ajouter une catégorie", "editCategoryTitle": "Modifier la catégorie", @@ -395,12 +482,12 @@ "colSlug": "Slug", "colSkills": "Skills", "colDescription": "Description", - "noCategoriesFound": "Aucune catégorie trouvée. Ajoutez-en une ci-dessus.", + "noCategoriesFound": "Aucune catégorie trouvée. Ajoutes-en une ci-dessus.", "deleting": "...", - "cannotDeleteHasSkills": "Suppression impossible : contient des skills", - "categoriesTotal": "{count, number} {count, plural, one {catégorie} other {catégories}} au total", + "cannotDeleteHasSkills": "Suppression impossible : contient des skills", + "categoriesTotal": "{count, number} {count, plural, one {catégorie} many {catégories} other {catégories}} au total", "usersTitle": "Utilisateurs", - "usersTotal": "{count, number} {count, plural, one {utilisateur} other {utilisateurs}} au total", + "usersTotal": "{count, number} {count, plural, one {utilisateur} many {utilisateurs} other {utilisateurs}} au total", "searchUsersPlaceholder": "Rechercher par nom ou e-mail...", "colEmail": "E-mail", "colRole": "Rôle", @@ -417,7 +504,7 @@ "demoteToUser": "Rétrograder en utilisateur", "promoteToAdmin": "Promouvoir administrateur", "logsTitle": "Journaux d'administration", - "logsTotal": "{count, number} {count, plural, one {entrée} other {entrées}} au total", + "logsTotal": "{count, number} {count, plural, one {entrée} many {entrées} other {entrées}} au total", "colTimestamp": "Horodatage", "colAdmin": "Admin", "colAction": "Action", @@ -436,13 +523,52 @@ "targetSkill": "skill", "targetAgent": "agent", "targetCategory": "catégorie", - "targetUser": "utilisateur" + "targetUser": "utilisateur", + "adminNavLabel": "Sections d'administration", + "retry": "Réessayer", + "close": "Fermer", + "prevPage": "Page précédente", + "nextPage": "Page suivante", + "sectionAgents": "Agents", + "sectionSkills": "Skills et communauté", + "statTotalAgents": "Total des agents", + "statPendingAgents": "Agents en attente", + "statHostedAgents": "Hébergés", + "statProjectAgents": "Projets", + "pendingAgentsTitle": "Agents en attente de validation", + "noPendingAgents": "Aucun agent n'attend de validation.", + "kindHosted": "Hébergé", + "kindProject": "Projet", + "filterByStatus": "Filtrer par statut", + "filterDisabled": "Désactivés", + "actionDisable": "Désactiver", + "actionEnable": "Réactiver", + "disableAgentTitle": "Désactiver l'agent", + "disableAgentConfirm": "{name} sera masqué du catalogue public jusqu'à ce que tu le réactives. Rien n'est supprimé.", + "deleteAgentTitle": "Supprimer l'agent", + "deleteAgentConfirmNamed": "Veux-tu vraiment supprimer {name} ? Cette action est irréversible.", + "rejectAgentTitle": "Rejeter l'agent", + "rejectReasonLabel": "Motif (facultatif, visible par l'éditeur)", + "rejectReasonPlaceholder": "ex. l'endpoint renvoie 500 sur la tâche d'exemple", + "healthOk": "Sain", + "healthDegraded": "Dégradé", + "healthDown": "Hors service", + "healthUnknown": "Non vérifié", + "healthCheckedAt": "Dernier contrôle : {date}", + "healthNeverChecked": "Aucun contrôle de santé enregistré", + "batchDeleteTitle": "Supprimer les skills", + "selectAllRows": "Sélectionner toutes les lignes", + "selectRow": "Sélectionner {name}", + "editSkillNamed": "Modifier le skill {name}", + "deleteSkillNamed": "Supprimer le skill {name}", + "editCategoryNamed": "Modifier la catégorie {name}", + "deleteCategoryNamed": "Supprimer la catégorie {name}" }, "Auth": { "signInTitle": "Se connecter à TakoAPI", - "signInSubtitle": "Accédez à votre tableau de bord de skills", + "signInSubtitle": "Accède à ton tableau de bord de skills", "signUpTitle": "Créer un compte", - "signUpSubtitle": "Rejoignez la communauté TakoAPI", + "signUpSubtitle": "Rejoins la communauté TakoAPI", "continueWithGoogle": "Continuer avec Google", "continueWithApple": "Continuer avec Apple", "signUpWithGoogle": "S'inscrire avec Google", @@ -459,29 +585,43 @@ "invalidCredentials": "E-mail ou mot de passe incorrect", "registrationFailed": "Échec de l'inscription", "somethingWentWrong": "Une erreur s'est produite", - "noAccount": "Vous n'avez pas de compte ?", + "noAccount": "Pas encore de compte ?", "signUpLink": "S'inscrire", - "haveAccount": "Vous avez déjà un compte ?", - "signInLink": "Se connecter" + "haveAccount": "Tu as déjà un compte ?", + "signInLink": "Se connecter", + "forgotPassword": "Mot de passe oublié ?", + "forgotTitle": "Réinitialiser ton mot de passe", + "forgotSubtitle": "On t'envoie par e-mail un lien pour en choisir un nouveau", + "sendResetLink": "Envoyer le lien", + "sendingResetLink": "Envoi...", + "resetLinkSent": "Si un compte existe pour cette adresse, un lien de réinitialisation vient d'être envoyé. Vérifie ta boîte de réception.", + "backToSignIn": "Retour à la connexion", + "resetTitle": "Choisis un nouveau mot de passe", + "resetSubtitle": "Saisis un nouveau mot de passe pour ton compte", + "newPasswordHint": "Nouveau mot de passe (8 caractères minimum)", + "updatePassword": "Mettre à jour le mot de passe", + "updatingPassword": "Mise à jour...", + "passwordUpdated": "Ton mot de passe a été mis à jour. Redirection vers la connexion...", + "resetLinkInvalid": "Ce lien de réinitialisation est invalide ou a expiré.", + "requestNewLink": "Demander un nouveau lien" }, "Dashboard": { "title": "Tableau de bord développeur", - "signInPrompt": "Connectez-vous pour gérer vos clés API et consulter votre utilisation.", - "signIn": "Se connecter", - "description": "Gérez vos clés API et surveillez l'utilisation de la passerelle. Une clé, une facture, tous les agents.", + "signInPrompt": "Connecte-toi pour gérer tes clés API et consulter ton utilisation.", + "description": "Gère tes clés API et surveille l'utilisation de la passerelle. Une clé, une facture, tous les agents.", "apiKeys": "Clés API", "createKey": "Créer une clé", "creating": "Création…", - "copyKeyWarning": "Copiez cette clé maintenant — elle ne sera plus affichée.", + "copyKeyWarning": "Copie cette clé maintenant — elle ne sera plus affichée.", "copy": "Copier", "copied": "Copié", - "noKeys": "Aucune clé pour l'instant. Créez-en une pour commencer à invoquer des agents.", + "noKeys": "Aucune clé pour l'instant. Crées-en une pour commencer à appeler des agents.", "keyFallbackName": "clé", "created": "créée le {date}", "lastUsed": "dernière utilisation le {date}", "neverUsed": "jamais utilisée", "revoke": "Révoquer", - "revokeConfirm": "Révoquer cette clé ? Les applications qui l'utilisent cesseront de fonctionner.", + "revokeConfirm": "Révoquer cette clé ? Les applications qui l'utilisent cesseront de fonctionner.", "quickstart": "Démarrage rapide", "usage": "Utilisation", "totalCalls": "Total des appels", @@ -493,16 +633,15 @@ "tableLatency": "Latence", "tableWhen": "Quand", "tableCost": "Coût", - "noCalls": "Aucun appel pour l'instant. Créez une clé et invoquez un agent pour voir l'utilisation ici.", + "noCalls": "Aucun appel pour l'instant. Crée une clé et appelle un agent pour voir l'utilisation ici.", "billing": "Crédits et facturation", "creditBalance": "Solde de crédits", "totalSpend": "Dépense totale", - "topUpComingSoon": "Les crédits sont alimentés par la plateforme pour l’instant ; la recharge PayPal en libre-service arrive bientôt.", "topUpLabel": "Ajouter des crédits", "topUpButton": "Recharger avec PayPal", "topUpRedirecting": "Redirection…", - "topUpSuccess": "Crédits ajoutés — votre solde est à jour.", - "topUpError": "La recharge n'a pas abouti. Veuillez réessayer.", + "topUpSuccess": "Crédits ajoutés — ton solde est à jour.", + "topUpError": "La recharge n'a pas abouti. Réessaie.", "topUpCancel": "Recharge annulée.", "noLedger": "Aucun mouvement de crédit pour le moment.", "ledgerType": "Type", @@ -510,38 +649,68 @@ "ledgerNote": "Note", "ledgerTopUp": "Recharge", "ledgerTopUpFee": "Frais de recharge", - "ledgerDebit": "Frais d’appel", + "ledgerDebit": "Frais d'appel", "ledgerPayout": "Versement", "ledgerRefund": "Remboursement", - "ledgerAdjust": "Ajustement" + "ledgerAdjust": "Ajustement", + "onboardingTitle": "Réussis ton premier appel", + "onboardingSubtitle": "Trois étapes avant qu'un agent te réponde.", + "stepKeyTitle": "Créer une clé API", + "stepKeyBody": "Une seule clé appelle tous les agents de TakoAPI. Le secret n'est affiché qu'une fois, juste après sa création.", + "stepCallTitle": "Faire ton premier appel", + "stepCallBody": "Copie un exemple ci-dessous, mets ta clé dans TAKO_KEY et exécute-le. L'appel apparaît sous Utilisation en quelques secondes.", + "stepInstallTitle": "Installer dans ton agent de code", + "stepInstallBody": "Apprends à Claude Code, Codex ou OpenCode à découvrir et appeler des agents via TakoAPI.", + "stepInstallLink": "Ouvrir le guide d'installation", + "stepOptional": "Facultatif", + "stepDone": "Fait", + "stepTodo": "Pas encore fait", + "keyNameLabel": "Nom de la clé", + "keyNamePlaceholder": "Clé de passerelle", + "createKeyError": "Impossible de créer la clé. Réessaie.", + "revokeNamed": "Révoquer {name}", + "quickstartHint": "Mets une clé ci-dessus dans TAKO_KEY, puis appelle n'importe quel agent par son slug.", + "creditRule": "Les agents gratuits ne coûtent rien ; les agents payants débitent ton crédit prépayé, et un appel est refusé par un HTTP 402 dès que le solde passerait sous zéro.", + "topUpUnavailable": "La recharge en libre-service n'est pas encore disponible — les crédits sont ajoutés par la plateforme pour l'instant. Ton solde ci-dessus reste à jour dans tous les cas.", + "dismiss": "Ignorer", + "loadError": "Certaines données du tableau de bord n'ont pas pu être chargées.", + "retry": "Réessayer", + "recentCallsCaption": "Tes appels de passerelle les plus récents", + "chartTitle": "Appels par jour, {days} derniers jours", + "chartTotal": "{count, number} au total", + "chartAria": "Graphique à barres des appels de passerelle par jour sur les {days} derniers jours, {count, number} appels au total.", + "chartDay": "{date} : {calls, number} appels, {errors, number} en échec" }, "Profile": { - "title": "Mon profil", - "signInPrompt": "Veuillez vous connecter pour consulter votre profil.", - "signIn": "Se connecter", - "submissions": "Soumissions", + "title": "Mes publications", + "signInPrompt": "Connecte-toi pour voir les agents et les skills que tu as publiés.", "likes": "Mentions J'aime", - "views": "Vues", - "statValue": "{count, number}", - "mySubmissions": "Mes soumissions", - "submitNew": "Soumettre", - "tab_all": "Toutes", - "tab_approved": "Approuvées", - "tab_pending": "En attente", - "tab_rejected": "Rejetées", - "tabCount": "{count, number}", - "emptyTab": "Aucune soumission dans cet onglet", - "submitFirst": "Soumettez votre premier skill", - "statusApproved": "Approuvé", - "statusPending": "En attente de validation", + "views": "vues", + "tab_all": "Tout", + "emptyTab": "Aucun skill ici pour l'instant.", + "submitFirst": "Soumets ton premier skill", + "statusApproved": "En ligne", + "statusPending": "En validation", "statusRejected": "Rejeté", - "reviewerNote": "Note du relecteur :" + "reviewerNote": "Note du relecteur :", + "publishAgent": "Publier un agent", + "submitSkill": "Soumettre un skill", + "statAgents": "Agents", + "statCalls": "Appels de passerelle", + "statSkills": "Skills", + "tabAgents": "Agents", + "tabSkills": "Skills", + "statusDisabled": "Désactivé", + "kindHosted": "Appelable", + "kindProject": "Open source", + "noAgents": "Aucun agent ici pour l'instant.", + "publishFirstAgent": "Publie ton premier agent", + "myListings": "Mes publications" }, "Submit": { "title": "Soumettre un skill", - "subtitle": "Partagez votre skill OpenClaw avec la communauté", - "signInPrompt": "Veuillez vous connecter pour soumettre des skills.", - "signIn": "Se connecter", + "subtitle": "Partage ton skill OpenClaw avec la communauté", + "signInPrompt": "Connecte-toi pour soumettre des skills.", "skillSource": "Source du skill", "githubUrl": "URL GitHub", "clawSkillsUrl": "URL ClawSkills.sh", @@ -549,45 +718,52 @@ "clawSkillsUrlPlaceholder": "https://clawskills.sh/skills/author-skillname", "autoFill": "Remplissage automatique", "fetching": "Récupération...", - "filled": "Rempli !", + "filled": "Rempli !", "skillName": "Nom du skill", "skillNamePlaceholder": "ex. my-awesome-skill", "category": "Catégorie", - "selectCategory": "Sélectionnez une catégorie", + "selectCategory": "Sélectionne une catégorie", "categoryOption": "{name} ({count})", "brief": "Résumé", "briefHelper": "- Résumé en une ligne", - "briefPlaceholder": "Une courte description de ce que fait votre skill", + "briefPlaceholder": "Une courte description de ce que fait ton skill", "skillDetails": "Détails du skill", "skillDetailsHelper": "- Rempli automatiquement à partir du README", "whatItDoes": "Ce que fait ce skill", "whatItDoesPlaceholder": "Explication détaillée de ce que fait ce skill, de ses fonctionnalités et de ses capacités...", "exampleWorkflow": "Exemple de workflow", "exampleWorkflowHelper": "- ENTRÉE / étapes AGENT / SORTIE", - "exampleWorkflowPlaceholder": "ENTRÉE\nL'utilisateur demande : Faire quelque chose d'utile\n\nAGENT\n1\nExécuter `my-skill action` pour réaliser la tâche\n2\nVérifier le résultat\n\nSORTIE\nTâche réalisée avec succès", + "exampleWorkflowPlaceholder": "ENTRÉE\nL'utilisateur demande : Faire quelque chose d'utile\n\nAGENT\n1\nExécuter `my-skill action` pour réaliser la tâche\n2\nVérifier le résultat\n\nSORTIE\nTâche réalisée avec succès", "requirements": "Prérequis", "requirementsPlaceholder": "- Node.js 18+\n- Clé API du service X\n- etc.", "submitSkill": "Soumettre le skill", "submitting": "Soumission...", - "enterUrlFirst": "Veuillez d'abord saisir une URL", + "enterUrlFirst": "Saisis d'abord une URL", "autoFillFailed": "Échec du remplissage automatique", - "submitFailed": "Échec de la soumission", - "somethingWentWrong": "Une erreur s'est produite" + "somethingWentWrong": "Une erreur s'est produite", + "successTitle": "Soumis 🎉", + "pendingReview": "{name} est maintenant en attente de validation.", + "nextStepsTitle": "Ce qui se passe ensuite", + "nextStepReview": "Un relecteur vérifie le dépôt et la description.", + "nextStepEmail": "Tu recevras un e-mail une fois la validation faite.", + "nextStepProfile": "Ta page de publications affiche toujours le statut actuel et toute note du relecteur.", + "viewMyListings": "Voir mes publications", + "backToSkills": "← Retour aux skills" }, "SubmitAgent": { "title": "Publier un agent", - "signInPrompt": "Veuillez vous connecter pour publier un agent.", + "signInPrompt": "Connecte-toi pour publier un agent.", "signIn": "Se connecter", - "intro": "Référencez un agent invocable sur la marketplace. Soumettez l'URL de son AgentCard A2A et nous analyserons le reste, ou saisissez-le manuellement.", + "intro": "Référence un agent invocable sur la marketplace. Soumets l'URL de son AgentCard A2A et on analyse le reste, ou remplis le formulaire à la main.", "tabUrl": "URL de l'AgentCard", "tabManual": "Manuel", "cardUrlLabel": "URL de l'AgentCard", "cardUrlPlaceholder": "https://your-agent.com/.well-known/agent-card.json", - "cardUrlHelp": "Nous récupérons et validons l'AgentCard A2A (nom, endpoint, capacités, skills).", + "cardUrlHelp": "On récupère et on valide l'AgentCard A2A (nom, endpoint, capacités, skills).", "nameLabel": "Nom de l'agent", "namePlaceholder": "ex. Weather Oracle", "descriptionLabel": "Description", - "descriptionPlaceholder": "Que fait cet agent ?", + "descriptionPlaceholder": "Que fait cet agent ?", "endpointLabel": "URL de l'endpoint", "endpointPlaceholder": "https://your-agent.com/a2a", "protocolsLabel": "Protocoles", @@ -601,7 +777,7 @@ "pricingPerToken": "Par 1k tokens", "unitPriceLabel": "Prix unitaire (USD)", "unitPricePlaceholder": "0.002", - "homepageLabel": "Page d'accueil", + "homepageLabel": "Site web", "optional": "facultatif", "homepagePlaceholder": "https://your-agent.com", "submit": "Publier l'agent", @@ -610,61 +786,239 @@ "somethingWentWrong": "Une erreur s'est produite", "successTitle": "Soumis 🎉", "pendingReview": "{name} est maintenant en attente de validation.", - "successDetail": "Nous vérifierons l'AgentCard et le publierons sur la marketplace une fois approuvé.", - "backToMarketplace": "← Retour à la marketplace" + "successDetail": "On vérifiera l'AgentCard et on le publiera sur la marketplace une fois approuvé.", + "backToMarketplace": "← Retour à la marketplace", + "checkCard": "Vérifier la carte", + "checking": "Vérification…", + "enterCardUrlFirst": "Saisis d'abord une URL d'AgentCard.", + "previewTitle": "Voici ce qui serait référencé", + "previewNoDescription": "La carte n'a pas de description.", + "previewEndpoint": "Endpoint", + "previewCapabilities": "Capacités", + "previewSkillCount": "{count, plural, =0 {Aucune capacité déclarée} one {# capacité} many {# de capacités} other {# capacités}}", + "previewSupports": "Prend en charge", + "previewStreaming": "Streaming", + "previewPush": "Notifications push", + "previewHint": "Rien n'est encore enregistré — publie ci-dessous pour l'envoyer en validation.", + "nextStepsTitle": "Ce qui se passe ensuite", + "nextStepReview": "Un relecteur vérifie l'AgentCard et que ton endpoint répond.", + "nextStepEmail": "Tu recevras un e-mail une fois la validation faite.", + "nextStepProfile": "Ta page de publications affiche toujours le statut actuel et toute note du relecteur.", + "viewMyListings": "Voir mes publications" }, "AgentEngagement": { - "yourRating": "Votre note", + "yourRating": "Ta note", "ratingsCount": "{count, number} avis", "noRatings": "Pas encore noté", "bookmark": "Enregistrer", "bookmarked": "Enregistré", - "signInToRate": "Connectez-vous pour noter ou enregistrer cet agent" + "signInToRate": "Connecte-toi pour noter ou enregistrer cet agent", + "rateStars": "Noter {count, plural, one {# étoile} many {# d'étoiles} other {# étoiles}}" }, "Bookmarks": { "title": "Agents enregistrés", - "subtitle": "Les agents que vous avez enregistrés.", + "subtitle": "Les agents que tu as mis en favoris.", "empty": "Aucun favori pour l'instant.", "browseAgents": "Parcourir les agents", - "signInPrompt": "Connectez-vous pour voir vos favoris.", - "signIn": "Se connecter" + "signInPrompt": "Connecte-toi pour voir tes favoris." }, "ScenarioPage": { "metaTitle": "Agents IA pour {scenario}", - "metaDescription": "Découvrez et invoquez des agents IA pour {scenario} via une seule API unifiée, décrits par des AgentCards A2A ouvertes sur TakoAPI.", - "heroSubtitle": "Des agents IA pour {scenario}, appelables via une seule API.", + "metaDescription": "Découvre et invoque des agents IA pour {scenario} via une API unifiée — décrits par des AgentCards A2A ouvertes sur TakoAPI.", + "heroSubtitle": "Des agents IA pour {scenario}, appelables via une seule API unifiée.", "empty": "Aucun agent dans ce scénario pour l'instant.", "browseAll": "Parcourir tous les agents", "back": "← Tous les scénarios", "breadcrumbScenarios": "Scénarios", "indexTitle": "Parcourir les agents IA par scénario", - "indexDescription": "Trouvez des agents IA selon l'usage : frontend, investissement, marketing, support et plus, tous appelables via une API.", + "indexDescription": "Trouve des agents IA selon l'usage — frontend, investissement, marketing, support et plus — tous appelables via une seule API.", "indexHeroTitle": "Parcourir par scénario", - "indexHeroSubtitle": "Trouvez des agents IA selon la tâche à accomplir." + "indexHeroSubtitle": "Trouve des agents IA selon la tâche à accomplir.", + "indexPopularHeading": "Bien couverts", + "indexPopularHint": "Des scénarios avec un large catalogue d'agents à comparer.", + "indexSparseHeading": "Tout juste lancés", + "indexSparseHint": "Une poignée d'agents seulement pour l'instant — l'occasion d'être le premier.", + "indexSparseCta": "Publie un agent dans l'un d'eux →", + "metaTitlePaged": "Agents IA pour {scenario} — page {page}", + "resultsLabel": "Agents pour {scenario}", + "paginationLabel": "Pagination", + "showingRange": "Affichage de {from} à {to} sur {total}", + "prev": "← Précédent", + "next": "Suivant →", + "goToPage": "Aller à la page {page}", + "ctaTitle": "Tu connais un agent pour {scenario} ?", + "ctaBody": "Ce scénario est encore peu fourni. Publie un agent et il apparaîtra ici pour tous ceux qui parcourent {scenario}.", + "ctaButton": "Publier un agent", + "relatedHeading": "Explorer d'autres scénarios" }, "Badge": { "snippetTitle": "Ajouter un badge", "copy": "Copier", "copied": "Copié", - "snippetHint": "Collez-le dans votre README pour créer un lien vers votre fiche TakoAPI.", + "snippetHint": "Colle-le dans ton README pour renvoyer vers ta fiche TakoAPI.", "pageTitle": "Badge README TakoAPI", - "pageIntro": "Montrez que votre projet est référencé sur TakoAPI — un petit badge qui renvoie vers votre fiche et affiche votre nombre d'étoiles en direct.", + "pageIntro": "Montre que ton projet est référencé sur TakoAPI — un petit badge qui renvoie vers ta fiche et affiche ton nombre d'étoiles en direct.", "example": "Exemple", - "step1Title": "Trouvez votre fiche", - "step1": "Cherchez votre projet dans l'annuaire d'agents.", - "step2Title": "Copiez le badge", - "step2": "Sur la page de votre fiche, copiez le Markdown depuis l'encadré du badge.", - "step3Title": "Collez-le dans votre README", - "step3": "Ajoutez-le près du haut — visiteurs et moteurs de recherche obtiennent un lien vers votre page TakoAPI.", - "findListing": "Trouver votre fiche" + "step1Title": "Trouve ta fiche", + "step1": "Cherche ton projet dans l'annuaire d'agents.", + "step2Title": "Copie le badge", + "step2": "Sur la page de ta fiche, copie le Markdown depuis l'encadré du badge.", + "step3Title": "Colle-le dans ton README", + "step3": "Ajoute-le près du haut — visiteurs et moteurs de recherche obtiennent un lien vers ta page TakoAPI.", + "findListing": "Trouver ta fiche", + "exampleAlt": "Badge TakoAPI" }, "Blog": { "title": "Blog", - "subtitle": "Guides sur A2A, les passerelles d'agents et la création avec TakoAPI.", + "subtitle": "Guides sur A2A, les passerelles d'agents et le développement avec TakoAPI.", "readMore": "Lire la suite", "backToBlog": "← Tous les articles", "minRead": "{min} min de lecture", "published": "Publié", "faqTitle": "FAQ" + }, + "NotFound": { + "title": "Page introuvable", + "description": "Le lien est peut-être obsolète, ou la fiche a été retirée. Essaie l'annuaire, ou la recherche dans l'en-tête.", + "home": "Accueil", + "agents": "Parcourir les agents", + "skills": "Parcourir les skills" + }, + "ErrorPage": { + "title": "Une erreur s'est produite", + "description": "Cette page n'a pas pu se charger. Réessayer suffit souvent ; sinon, la référence ci-dessous nous aide à la retrouver dans les journaux.", + "reference": "Référence : {id}", + "retry": "Réessayer", + "home": "Retour à l'accueil" + }, + "Consent": { + "title": "Mesure d'audience", + "description": "On utilise Google Analytics pour voir quelles pages sont utiles. Rien n'est chargé et aucun cookie n'est déposé sans ton accord.", + "accept": "Accepter", + "decline": "Refuser" + }, + "Docs": { + "metaTitle": "Documentation de l'API", + "title": "Référence de l'API TakoAPI", + "description": "De la clé API à un appel d'agent qui fonctionne : authentification, endpoints de découverte, passerelle d'agents unifiée, serveur MCP hébergé, codes d'erreur, limites de débit et facturation.", + "heroBadge": "Documentation développeur", + "heroTitle": "La référence de l'API TakoAPI", + "heroSubtitle": "Une clé, tous les agents. Pointe n'importe quel client compatible OpenAI vers TakoAPI et appelle un agent par son slug — ou parcours tout le catalogue sans aucune clé.", + "heroKeyCta": "Créer une clé API", + "heroBrowseCta": "Parcourir les agents", + "tocTitle": "Sur cette page", + "tocLabel": "Table des matières", + "quickstartTitle": "Démarrage rapide", + "quickstartIntro": "Trois étapes pour passer de rien à une réponse. Chaque requête de cette page est un vrai appel à l'API en production.", + "quickstartStep1Title": "Créer une clé", + "quickstartStep1": "Connecte-toi et crée une clé API dans ton tableau de bord. Le secret n'est affiché qu'une fois, à la création.", + "quickstartStep2Title": "L'exporter", + "quickstartStep2": "Tous les exemples de cette page lisent la clé depuis TAKO_KEY.", + "quickstartStep3Title": "Appeler un agent", + "quickstartStep3": "Mets comme modèle — ou comme slug dans l'URL — n'importe quel agent hébergé du catalogue.", + "quickstartSamplesLabel": "Exemples d'appel pour le démarrage rapide", + "quickstartNote": "Le slug de ces exemples est un espace réservé, pas un vrai agent. Copies-en un vrai depuis n'importe quelle page d'agent ; seuls les agents référencés comme hébergés peuvent être invoqués.", + "authenticationTitle": "Authentification", + "authenticationIntro": "Les appels à la passerelle s'authentifient avec un jeton bearer. Les endpoints de découverte sont anonymes et ne demandent aucune clé.", + "authHeaderNote": "x-api-key est accepté comme alternative à l'en-tête Authorization. Les clés commencent toujours par tako_live_.", + "authWhereTitle": "D'où viennent les clés", + "authWhere": "Crée une clé par client ou par environnement dans ton tableau de bord, et révoque celles que tu n'utilises plus.", + "authOnceTitle": "Le secret n'est affiché qu'une fois", + "authOnce": "Seuls un hachage SHA-256 de la clé et un court préfixe d'affichage sont stockés : un secret perdu est irrécupérable — crée une clé de remplacement et révoque l'ancienne.", + "authRevokeTitle": "La révocation est immédiate", + "authRevoke": "Chaque appel revérifie la clé dans la base de données et rien n'est mis en cache : une clé révoquée est rejetée dès la requête suivante. L'utilisation passée reste dans ton historique.", + "discoveryTitle": "Découverte", + "discoveryIntro": "Quatre endpoints publics et cachables, sans clé. Seuls les agents et les skills approuvés sont renvoyés.", + "endpointRegistrySummary": "Tout le catalogue d'agents — Markdown pour un modèle, JSON pour du code.", + "endpointRegistryNote": "Sans format=json, la réponse est du Markdown (text/markdown), séparé en un tableau d'agents hébergés et un tableau de projets open source, dimensionné pour tenir directement dans le contexte d'un modèle — c'est ce que lit le skill TakoAPI installé. Les deux formats sont plafonnés par limit : vérifie truncated et les totaux avant de supposer que tu as tout. Les paramètres en texte libre sont tronqués à 100 caractères.", + "paramRegistryFormat": "md (par défaut) ou json.", + "paramRegistryQ": "Correspondance en texte libre sur le nom et la description de l'agent.", + "paramRegistryCategory": "Slug de la catégorie.", + "paramRegistryProtocol": "Ne renvoie que les agents déclarant ce protocole.", + "paramRegistryKind": "Les agents HOSTED sont appelables via la passerelle ; les entrées PROJECT sont des dépôts open source référencés pour la découverte uniquement.", + "paramRegistrySort": "Ordre de tri. Par défaut : les agents en vedette d'abord, puis les plus appelés, puis les plus récents — et les plus étoilés pour les projets.", + "paramRegistryLimit": "Nombre total d'entrées à renvoyer. Les agents hébergés sont servis en premier, puis les projets.", + "endpointAgentDetailSummary": "Tout le détail public d'un agent, y compris les skills A2A qu'il déclare.", + "endpointAgentDetailNote": "Seuls les agents approuvés répondent ; tout le reste renvoie un 404. Les champs de modération — notes de relecture, identifiants internes et avatars des éditeurs — ne font jamais partie de cette structure. Mis en cache en périphérie pendant 60 secondes.", + "paramAgentDetailSlug": "Segment d'URL. Le slug de l'agent, tel que renvoyé par le registre.", + "endpointSkillsDirectorySummary": "Le catalogue de skills OpenClaw, en Markdown par défaut.", + "endpointSkillsDirectoryNote": "Markdown d'abord, comme le registre. install vaut null pour les skills sans commande d'installation publiée — replie-toi sur le lien github. downloads est un chiffre tiers récupéré sur la fiche du skill, pas un nombre d'installations via TakoAPI.", + "paramSkillsDirFormat": "md (par défaut) ou json.", + "paramSkillsDirQ": "Correspondance en texte libre sur le nom et la description du skill.", + "paramSkillsDirCategory": "Slug de la catégorie.", + "paramSkillsDirPage": "Numéro de page, à partir de 1.", + "paramSkillsDirLimit": "Résultats par page.", + "endpointSkillsSearchSummary": "Recherche plein texte dans le catalogue de skills, JSON uniquement.", + "endpointSkillsSearchNote": "q est obligatoire : sans lui, l'endpoint renvoie une liste vide plutôt que tout le catalogue. Chaque résultat est la ligne de catalogue complète, moins readme et reviewNote ; l'exemple ci-dessus est abrégé.", + "paramSkillsSearchQ": "Terme de recherche. Correspond au nom, à la description et à l'auteur du skill.", + "paramSkillsSearchCategory": "Slug de la catégorie.", + "paramSkillsSearchPage": "Numéro de page, à partir de 1.", + "paramSkillsSearchLimit": "Résultats par page.", + "gatewayTitle": "Passerelle", + "gatewayIntro": "Trois façons d'invoquer le même agent. Les trois demandent une clé, les trois facturent de la même manière, et les trois acceptent le slug de l'agent là où irait normalement un nom de modèle. Seuls les agents de type HOSTED peuvent être appelés.", + "endpointChatCompletionsSummary": "Compatible OpenAI. Pointe n'importe quel SDK OpenAI vers /v1 et mets un slug d'agent dans model.", + "endpointChatCompletionsNote": "Toute la conversation est transmise : une part texte A2A par tour, dans l'ordre, avec le rôle OpenAI attaché à chaque part. usage est toujours présent mais toujours à zéro — rien dans cette passerelle ne compte les tokens, et un agent n'est pas forcément un LLM. Mets stream: true pour la forme SSE ci-dessus, et stream_options.include_usage pour recevoir aussi le chunk usage final. Un appel non streamé expire après 30 secondes, un stream après 120.", + "endpointA2AMessageSummary": "Passthrough A2A natif. Renvoie la réponse JSON-RPC de l'agent, telle quelle.", + "endpointA2AMessageNote": "Relayé comme un appel A2A message/send. Envoie la forme courte texte, ou un message A2A complet — avec parts, taskId et contextId — pour poursuivre une tâche multi-tours ou faire passer des parts file et data. Le code de statut et le corps de l'agent sont relayés sans modification, et une requête sans contenu est rejetée par un 400 plutôt que facturée. Expire après 30 secondes.", + "endpointA2AStreamSummary": "Le même appel en Server-Sent Events, pour les agents qui streament.", + "endpointA2AStreamNote": "Relaie mot pour mot les frames A2A message/stream de l'agent, en text/event-stream. Cet endpoint n'accepte que la forme courte texte. La connexion est coupée après 120 secondes, et l'appel n'est facturé qu'une fois le stream arrivé à son terme — voir Limites de débit et facturation.", + "labelQueryParams": "Paramètres", + "labelRequest": "Corps de la requête", + "labelExample": "Exemple", + "labelResponse": "Réponse", + "labelStreaming": "Réponse avec stream: true", + "labelA2AMessageForm": "Forme message A2A complète", + "labelAuthRequired": "Clé requise", + "labelNoAuth": "Sans clé", + "labelBillable": "Facturable", + "labelFree": "Gratuit", + "labelParamName": "Nom", + "labelParamType": "Type", + "labelParamDesc": "Description", + "paramsTableCaption": "Paramètres de {path}", + "mcpTitle": "Serveur MCP", + "mcpIntro": "TakoAPI fait tourner un serveur Model Context Protocol hébergé et sans état, en Streamable HTTP, à l'adresse {url}. Enregistre-le une fois et ton agent de code peut chercher dans le catalogue et l'invoquer comme des outils natifs.", + "mcpRegisterTitle": "L'enregistrer", + "mcpRegisterLabel": "Commandes d'enregistrement MCP", + "mcpAuthNote": "Les trois outils de lecture fonctionnent anonymement. invoke_agent consomme du crédit, il lui faut donc ta clé : passe-la dans un en-tête Authorization (Claude Code), via bearer_token_env_var (Codex), ou dans un bloc headers (OpenCode).", + "mcpToolsTitle": "Outils", + "mcpToolsTableCaption": "Outils exposés par le serveur MCP TakoAPI", + "mcpToolColumn": "Outil", + "mcpToolAccessColumn": "Accès", + "mcpToolWhatColumn": "Ce qu'il fait", + "mcpToolKeyRequired": "Clé requise", + "mcpToolAnonymous": "Anonyme", + "mcpToolSearchAgents": "Cherche dans le registre par mot-clé, catégorie ou protocole. Renvoie les slugs, les protocoles et les tarifs.", + "mcpToolGetAgent": "Tout ce qu'on sait d'un agent à partir de son slug — capacités, protocoles, endpoint, tarifs et skills déclarés.", + "mcpToolSearchSkills": "Cherche dans le catalogue de skills OpenClaw. Sans requête, il renvoie une sélection des meilleurs skills.", + "mcpToolInvokeAgent": "Invoque un agent via la passerelle et renvoie sa réponse. Nécessite une clé et consomme du crédit.", + "mcpRateNote": "L'endpoint MCP est limité à 120 requêtes par minute et par adresse IP, indépendamment du quota de passerelle de ta clé.", + "mcpInstallNote": "Tu préfères un fichier skill à un serveur ? L'installateur apprend plutôt à Claude Code, Codex et OpenCode à appeler ces endpoints en HTTP simple.", + "errorsTitle": "Erreurs", + "errorsIntro": "Toute erreur de la passerelle est renvoyée en JSON. Voici les statuts que la passerelle elle-même renvoie.", + "errorsTableCaption": "Statuts d'erreur renvoyés par la passerelle", + "errorStatusColumn": "Statut", + "errorCodeColumn": "Code", + "errorWhenColumn": "Quand", + "errorBodyColumn": "Exemple de corps", + "error400When": "Le slug désigne un projet open source sans endpoint invocable, le corps de la requête a échoué à la validation, ou tous les tours de message étaient vides.", + "error401When": "La clé est absente, mal formée, inconnue ou révoquée.", + "error402When": "L'agent est payant et facturer l'appel ferait passer ton solde sous zéro. Vérifié avant de contacter l'agent, donc rien n'est dépensé.", + "error404When": "Aucun agent approuvé n'a ce slug.", + "error429When": "La clé a dépassé son quota sur les 60 dernières secondes. Retry-After indique le nombre de secondes avant la réinitialisation de la fenêtre.", + "error502When": "L'agent n'a pas répondu, a expiré, n'a renvoyé aucun stream, ou a signalé une erreur JSON-RPC dans un HTTP 200. Jamais facturé.", + "errorsOpenAiNote": "Sur /v1/chat/completions, les mêmes erreurs arrivent dans l'enveloppe d'OpenAI — avec type, param et code toujours présents — un SDK OpenAI lève donc ses types d'exception habituels :", + "errorsPassthroughNote": "Un statut renvoyé par l'agent lui-même est relayé tel quel par l'endpoint A2A message : un 4xx à cet endroit peut donc venir de l'agent et non de nous.", + "limitsTitle": "Limites de débit et facturation", + "limitsRateTitle": "Limites de débit", + "limitsRate": "La passerelle autorise 120 requêtes par tranche de 60 secondes et par clé API. La fenêtre est partagée entre toutes les instances du serveur et indexée sur la clé elle-même plutôt que sur ton adresse IP : elle ne se relâche donc pas sous la charge et ne se resserre pas derrière un NAT partagé.", + "limitsRateOverride": "Une clé peut avoir son propre quota, qui remplace la valeur par défaut pour cette clé uniquement. Au-delà de la limite, tu reçois un 429 avec un en-tête Retry-After, en secondes.", + "limitsBillingTitle": "Facturation", + "limitsBillingFree": "Les agents tarifés FREE ne coûtent rien — pas plus qu'un agent dont le prix unitaire est absent ou nul. La passerelle n'invente jamais de facturation.", + "limitsBillingPriced": "Un agent payant débite ton crédit prépayé à son prix unitaire, une fois par appel. Il n'y a aucun décompte au token : un appel vaut une unité, quelle que soit sa durée.", + "limitsBillingPreflight": "Ton solde est vérifié avant que l'agent soit contacté. Si la facturation te faisait passer sous zéro, l'appel est refusé par un 402 et l'agent n'est jamais appelé — il n'y a pas de découvert.", + "limitsBillingFailures": "Les appels en échec ne sont pas facturés. Un 4xx ou 5xx de l'agent, un délai dépassé, un endpoint injoignable ou une erreur JSON-RPC enregistrent la tentative sans rien facturer.", + "limitsBillingStreams": "Les streams ne sont facturés que lorsqu'ils vont à leur terme. Un stream interrompu, expiré, en échec en cours de route, ou dont tu te déconnectes trop tôt est enregistré mais non facturé.", + "limitsBillingLedger": "Chaque facturation écrit une entrée de registre immuable rattachée à l'invocation qui l'a provoquée. Solde, registre et utilisation par appel sont tous dans ton tableau de bord." } } diff --git a/messages/hi.json b/messages/hi.json index 1d444198f..f776a6bab 100644 --- a/messages/hi.json +++ b/messages/hi.json @@ -7,60 +7,78 @@ "label": "भाषा" }, "Header": { - "searchPlaceholder": "एजेंट्स खोजें...", "agents": "एजेंट्स", "skills": "स्किल्स", - "trending": "ट्रेंडिंग", - "publish": "पब्लिश करें", "publishAgent": "एजेंट पब्लिश करें", "account": "अकाउंट", - "mySkills": "मेरी स्किल्स", - "myBookmarks": "बुकमार्क", - "apiUsage": "API और उपयोग", + "myBookmarks": "सहेजे गए एजेंट्स", + "apiUsage": "API कीज़ और उपयोग", "adminPanel": "एडमिन पैनल", "signIn": "साइन इन करें", - "signOut": "साइन आउट करें" + "signOut": "साइन आउट करें", + "mainNav": "मुख्य", + "skipToContent": "कंटेंट पर जाएँ", + "openMenu": "मेन्यू खोलें", + "closeMenu": "मेन्यू बंद करें", + "projects": "प्रोजेक्ट्स", + "install": "इंस्टॉल", + "getApiKey": "API की लें", + "search": "खोजें", + "searchScope": "इसमें खोजें", + "scopeAgents": "एजेंट्स", + "scopeProjects": "प्रोजेक्ट्स", + "scopeSkills": "स्किल्स", + "searchAgentsPlaceholder": "एजेंट्स को उनके काम से खोजें…", + "searchProjectsPlaceholder": "ओपन-सोर्स एजेंट प्रोजेक्ट्स खोजें…", + "searchSkillsPlaceholder": "अपने कोडिंग एजेंट के लिए स्किल्स खोजें…", + "myListings": "मेरी लिस्टिंग", + "docs": "दस्तावेज़" }, "Footer": { - "tagline": "सभी एजेंट्स तक पहुँचने के लिए एक API — साथ ही अपने कोडिंग एजेंट के लिए हज़ारों OpenClaw स्किल्स।", + "tagline": "सभी एजेंट्स तक पहुँचने के लिए एक API — किसी भी एजेंट को एक ही एंडपॉइंट से खोजें, कॉल करें और मीटर करें।", "agentRegistry": "एजेंट रजिस्ट्री:", "platform": "प्लेटफ़ॉर्म", "resources": "संसाधन", - "installInAgent": "अपने एजेंट में इंस्टॉल करें", + "installInAgent": "अपने कोडिंग एजेंट में इंस्टॉल करें", "browseAgents": "एजेंट्स ब्राउज़ करें", "publishAnAgent": "एजेंट पब्लिश करें", - "developerDashboard": "डेवलपर डैशबोर्ड", + "developerDashboard": "API कीज़ और उपयोग", "browseSkills": "स्किल्स ब्राउज़ करें", - "awesomeList": "Awesome सूची", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. OpenClaw समुदाय के लिए बनाया गया।", - "blog": "ब्लॉग" + "copyright": "© {year} TakoAPI. MIT लाइसेंस।", + "blog": "ब्लॉग", + "badge": "README बैज", + "scenarios": "परिदृश्य अनुसार ब्राउज़ करें", + "a2aSpec": "A2A प्रोटोकॉल", + "github": "GitHub", + "docs": "API दस्तावेज़" }, "Home": { "heroTitle": "सभी एजेंट्स तक पहुँचने के लिए एक API", - "heroSubtitle": "एक एकीकृत API के ज़रिए {agents, number} AI एजेंट्स खोजें और इनवोक करें — साथ ही अपने कोडिंग एजेंट के लिए {skills, number} स्किल्स।", + "heroSubtitle": "AI एजेंट्स खोजें, फिर उनमें से किसी को भी एक की के साथ एक ही एंडपॉइंट से कॉल करें। A2A नेटिव, OpenAI-संगत, प्रति कॉल मीटर्ड।", "browseAgents": "एजेंट्स ब्राउज़ करें", - "publishAnAgent": "एजेंट पब्लिश करें", - "agentRegistry": "एजेंट रजिस्ट्री:", - "browseByScenario": "परिदृश्य अनुसार ब्राउज़ करें", - "allAgents": "सभी एजेंट्स", + "browseByScenario": "जो काम करवाना है उसके अनुसार ब्राउज़ करें", "featuredAgents": "फ़ीचर्ड एजेंट्स", "browseAll": "सभी ब्राउज़ करें", "popularProjects": "लोकप्रिय ओपन-सोर्स प्रोजेक्ट्स", "selfHostable": "{count, number} सेल्फ़-होस्टेबल", - "skillTitle": "OpenClaw के लिए TakoAPI स्किल", - "official": "आधिकारिक", - "skillDescription": "स्वाभाविक बातचीत के ज़रिए OpenClaw स्किल्स खोजें, इंस्टॉल करें और मैनेज करें — TakoAPI मार्केटप्लेस द्वारा संचालित।", - "viewOnGithub": "GitHub पर देखें", - "askAgent": "या अपने एजेंट से कहें: \"TakoAPI स्किल इंस्टॉल करो\"", - "usingCodingAgent": "Claude Code, Codex या OpenCode इस्तेमाल कर रहे हैं?", - "oneCommandInstall": "एक-कमांड इंस्टॉल", - "mustHaveSkills": "ज़रूरी स्किल्स", - "viewRankings": "रैंकिंग देखें", - "categories": "श्रेणियाँ", - "viewAll": "सभी देखें", + "categories": "श्रेणियाँ:", "moreCategories": "{count, plural, one {+# और श्रेणी} other {+# और श्रेणियाँ}}", - "latestSkills": "नवीनतम स्किल्स" + "getApiKey": "API की लें", + "catalogSummary": "{agents, number} कॉल करने योग्य एजेंट्स · {projects, number} ओपन-सोर्स प्रोजेक्ट्स · {skills, number} कोडिंग-एजेंट स्किल्स", + "quickstartLabel": "पहली कॉल तक 30 सेकंड", + "quickstartNote": "टर्मिनल से काम करना पसंद है? TakoAPI को अपने कोडिंग एजेंट में इंस्टॉल करें।", + "allScenarios": "सभी परिदृश्य", + "callableNow": "{count, number} गेटवे से कॉल किए जा सकते हैं", + "installTitle": "अपने कोडिंग एजेंट से TakoAPI इस्तेमाल करें", + "installDescription": "एक कमांड Claude Code, Codex या OpenCode को डायरेक्टरी खोजना और आपके लिए एजेंट्स कॉल करना सिखा देती है — या इसके बजाय होस्टेड MCP सर्वर रजिस्टर करें।", + "installCta": "इंस्टॉलेशन गाइड", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "आपके कोडिंग एजेंट के लिए स्किल्स", + "skillsSubtitle": "{count, number} OpenClaw स्किल्स — सर्वाधिक इंस्टॉल की गईं और नवीनतम।", + "quickstartTabDiscover": "1. खोजें", + "quickstartTabCall": "2. कॉल करें", + "quickstartTabFromAgent": "3. अपने एजेंट से" }, "Scenarios": { "frontend": "फ़्रंटएंड", @@ -85,8 +103,6 @@ "notFound": "एजेंट नहीं मिला", "backToMarketplace": "← एजेंट मार्केटप्लेस", "openSourceProject": "ओपन-सोर्स प्रोजेक्ट", - "signedCard": "साइन किया गया कार्ड", - "verifiedPublisher": "सत्यापित पब्लिशर", "by": "{name} द्वारा", "publisherOpenSource": "ओपन-सोर्स", "publisherUnknown": "अज्ञात", @@ -101,17 +117,41 @@ "noSkillsHosted": "इस एजेंट ने अपने AgentCard में संरचित स्किल्स नहीं दर्शाईं।", "inputMode": "इनपुट: {mode}", "outputMode": "आउटपुट: {mode}", - "openSourceProjectHeading": "ओपन-सोर्स प्रोजेक्ट", "openSourceProjectNote": "सेल्फ़-होस्ट करें या कोड एक्सप्लोर करें। TakoAPI गेटवे के ज़रिए इनवोक नहीं किया जा सकता।", "viewOnGithub": "GitHub पर देखें", - "integration": "इंटीग्रेशन", "endpoint": "एंडपॉइंट", "agentCard": "AgentCard", "homepage": "होमपेज", "callThroughTakoApi": "इसे TakoAPI के ज़रिए कॉल करें", - "gatewayPhase2": "एकीकृत गेटवे — इनवोकेशन Phase 2 में आएगा।", "breadcrumbHome": "होम", - "breadcrumbMarketplace": "एजेंट मार्केटप्लेस" + "breadcrumbMarketplace": "एजेंट मार्केटप्लेस", + "gatewayHint": "इसे अपनी API की से कॉल करें — डैशबोर्ड में एक बनाएँ।", + "healthOk": "रिस्पॉन्स दे रहा है", + "healthDegraded": "डिग्रेडेड", + "healthDown": "रिस्पॉन्स नहीं दे रहा", + "healthCheckedAt": "अंतिम जाँच {time}", + "atAGlance": "एक नज़र में", + "protocols": "प्रोटोकॉल", + "protocolsNone": "घोषित नहीं", + "yes": "हाँ", + "no": "नहीं", + "pricing": "प्राइसिंग", + "health": "हेल्थ", + "healthUnknown": "अभी जाँच नहीं हुई", + "totalCalls": "कुल कॉल", + "numberValue": "{count, number}", + "averageRating": "औसत रेटिंग", + "ratingValue": "{rating} / 5 · {count, number} रेटिंग", + "notRated": "अभी तक कोई रेटिंग नहीं", + "callExamplesLabel": "कॉल उदाहरण", + "repository": "रिपॉज़िटरी", + "language": "भाषा", + "license": "लाइसेंस", + "licenseNone": "निर्दिष्ट नहीं", + "lastPush": "अंतिम पुश", + "topics": "टॉपिक्स", + "readmeHeading": "README से", + "readmeNote": "GitHub पर प्रोजेक्ट के README का अंश।" }, "Agents": { "metaTitle": "एजेंट मार्केटप्लेस", @@ -145,11 +185,24 @@ "emptyCta": "पहला पब्लिश करें →", "prev": "← पिछला", "next": "अगला →", - "pageOf": "पेज {page}, कुल {total} में से" + "pageOf": "पेज {page}, कुल {total} में से", + "filtersTitle": "फ़िल्टर", + "filtersWithCount": "फ़िल्टर ({count})", + "activeFiltersLabel": "सक्रिय फ़िल्टर:", + "removeFilter": "फ़िल्टर हटाएँ: {label}", + "clearAll": "सभी हटाएँ", + "kindLabel": "प्रकार", + "scenarioGroup": "परिदृश्य", + "protocolGroup": "प्रोटोकॉल", + "pricingGroup": "प्राइसिंग", + "categoryGroup": "श्रेणी", + "searchChip": "खोज: {query}", + "resultsLabel": "परिणाम", + "paginationLabel": "पेजिनेशन", + "goToPage": "पेज {page} पर जाएँ", + "showingRange": "{total} में से {from}–{to} दिखा रहे हैं" }, "Components": { - "homeSearchPlaceholder": "नाम, विवरण या लेखक से स्किल्स खोजें...", - "homeSearchButton": "खोजें", "agentFree": "मुफ़्त", "agentPerCall": "{price}/कॉल", "agentPerTask": "{price}/टास्क", @@ -163,12 +216,16 @@ "agentSelfHost": "सेल्फ़-होस्ट", "agentSkills": "{count, plural, one {# स्किल} other {# स्किल्स}}", "skillBy": "{author} द्वारा", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} कॉल", + "healthOk": "रिस्पॉन्स दे रहा है", + "healthDegraded": "डिग्रेडेड", + "healthDown": "रिस्पॉन्स नहीं दे रहा" }, "Install": { "metaTitle": "Claude Code, Codex और OpenCode में इंस्टॉल करें", "title": "Claude Code, Codex और OpenCode में TakoAPI इंस्टॉल करें", - "description": "एक कमांड आपके कोडिंग एजेंट में TakoAPI स्किल इंस्टॉल करती है — एक एकीकृत गेटवे के ज़रिए AI एजेंट्स खोजें और इनवोक करें, और OpenClaw स्किल्स कैटलॉग खोजें। Claude Code, Codex और OpenCode के साथ काम करता है।", + "description": "एक कमांड आपके कोडिंग एजेंट में TakoAPI स्किल इंस्टॉल कर देती है — एक एकीकृत गेटवे के ज़रिए AI एजेंट्स खोजें और इनवोक करें, और OpenClaw स्किल्स कैटलॉग खोजें। Claude Code, Codex और OpenCode के साथ काम करता है।", "stepRunInstallerName": "इंस्टॉलर चलाएँ", "stepRunInstallerText": "चलाएँ: curl -fsSL https://takoapi.com/install.sh | sh", "stepUseAgentName": "अपने एजेंट से TakoAPI इस्तेमाल करें", @@ -196,7 +253,7 @@ "howToUseHeading": "कैसे इस्तेमाल करें", "claudeCodeUse": "अपने आप लोड होता है — बस Claude से TakoAPI पर कोई एजेंट खोजने को कहें।", "codexUse": "$takoapi से इनवोक करें, या इसे /skills मेन्यू से चुनें।", - "opencodeUse": "/takoapi चलाएँ, या @takoapi को सौंप दें।", + "opencodeUse": "/takoapi '' चलाएँ, या @takoapi को सौंप दें।", "mcpHeading": "MCP सर्वर — होस्टेड टूल्स, इंस्टॉल करने को कुछ नहीं", "claudeCodeMcpHint": "एक कमांड। invoke_agent सक्षम करने के लिए --header \"Authorization: Bearer $TAKO_KEY\" जोड़ें।", "codexMcpHint": "~/.codex/config.toml में जोड़ें", @@ -226,12 +283,17 @@ "requirements": "आवश्यकताएँ", "installation": "इंस्टॉलेशन", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClaw Chat (इसे अपनी बातचीत में पेस्ट करें)", "links": "लिंक", "github": "GitHub", - "copyGithubUrl": "GitHub URL कॉपी करें", "clawHub": "ClawHub", - "skillNotFound": "स्किल नहीं मिली" + "codingAgent": "कोडिंग एजेंट", + "agentPrompt": "{url} से यह स्किल इंस्टॉल करो", + "installHintRegistry": "CLI कमांड चलाएँ, या प्रॉम्प्ट को Claude Code, Cursor, Codex या जो भी कोडिंग एजेंट आप इस्तेमाल करते हैं उसमें पेस्ट करें।", + "installHintRepo": "यह स्किल ClawHub रजिस्ट्री पर पब्लिश नहीं है। प्रॉम्प्ट को अपने कोडिंग एजेंट — Claude Code, Cursor, Codex — में पेस्ट करें, और वह स्किल को सीधे सोर्स रिपॉज़िटरी से इंस्टॉल कर देगा।", + "installUnavailable": "इस स्किल के लिए अभी तक कोई इंस्टॉल सोर्स पब्लिश नहीं हुआ है।", + "openRepository": "रिपॉज़िटरी खोलें", + "signInToLike": "इस स्किल को लाइक करने के लिए साइन इन करें", + "downloadsSource": "clawskills.sh द्वारा बताए गए डाउनलोड" }, "Skills": { "metaTitle": "स्किल्स ब्राउज़ करें", @@ -257,13 +319,32 @@ "all": "सभी", "emptyTitle": "कोई स्किल नहीं मिली", "emptyHint": "अपनी खोज या फ़िल्टर बदलकर देखें", - "pageOf": "पेज {page}, कुल {total} में से" + "pageOf": "पेज {page}, कुल {total} में से", + "metaTitleSearch": "\"{query}\" से मेल खाती स्किल्स", + "metaTitlePaged": "स्किल्स ब्राउज़ करें — पेज {page}", + "searchInputLabel": "स्किल्स खोजें", + "searchPlaceholder": "नाम, विवरण या लेखक से स्किल्स खोजें", + "searchButton": "खोजें", + "agentFilter": "एजेंट", + "agentZed": "Zed", + "sourceFilter": "सोर्स", + "sourceGithub": "GitHub", + "sourceCommunity": "समुदाय", + "sourceCurated": "क्यूरेटेड", + "categoryFilter": "श्रेणी", + "sortLabel": "क्रमबद्ध करें", + "sortRelevance": "सर्वाधिक प्रासंगिक", + "clearFilters": "फ़िल्टर हटाएँ", + "paginationLabel": "स्किल लिस्टिंग पेज", + "goToPage": "पेज {page} पर जाएँ", + "prev": "पिछला", + "next": "अगला" }, "Trending": { "metaTitle": "ट्रेंडिंग स्किल्स", - "metaDescription": "अभी सबसे ज़्यादा डाउनलोड और सबसे ज़्यादा लाइक की गई OpenClaw स्किल्स — कोडिंग एजेंट्स क्या इंस्टॉल कर रहे हैं इसका लाइव लीडरबोर्ड।", + "metaDescription": "सबसे ज़्यादा डाउनलोड और सबसे ज़्यादा लाइक की गई OpenClaw स्किल्स — clawskills.sh द्वारा बताए गए डाउनलोड आंकड़ों पर आधारित लीडरबोर्ड, हर दिन अपडेट होता है।", "ogTitle": "ट्रेंडिंग स्किल्स — {siteName}", - "ogDescription": "सबसे लोकप्रिय OpenClaw स्किल्स का लाइव लीडरबोर्ड।", + "ogDescription": "सबसे लोकप्रिय OpenClaw स्किल्स का रोज़ अपडेट होने वाला लीडरबोर्ड।", "title": "ट्रेंडिंग स्किल्स", "subtitle": "समुदाय द्वारा रैंक की गई सबसे लोकप्रिय OpenClaw स्किल्स खोजें", "searchPlaceholder": "ट्रेंडिंग स्किल्स खोजें…", @@ -281,7 +362,13 @@ "byAuthor": "{author} द्वारा", "showing": "{total, number} में से {from, number}–{to, number} दिखा रहे हैं", "prev": "पिछला", - "next": "अगला" + "next": "अगला", + "provenanceNote": "डाउनलोड और स्टार की संख्या अपस्ट्रीम कैटलॉग — clawskills.sh और GitHub — से आती है और रोज़ रात को अपडेट होती है; यह TakoAPI के ज़रिए हुए इंस्टॉल नहीं मापती। लाइक्स और व्यूज़ यहीं गिने जाते हैं।", + "search": "खोजें", + "sortLabel": "इसके अनुसार क्रमबद्ध करें", + "rank": "रैंक", + "goToPage": "पेज {page} पर जाएँ", + "paginationLabel": "लीडरबोर्ड पेज" }, "Admin": { "accessDenied": "एक्सेस अस्वीकृत", @@ -335,8 +422,8 @@ "actionReject": "अस्वीकृत करें", "feature": "फ़ीचर करें", "unfeature": "अनफ़ीचर करें", - "scenariosEditorHint": "场景 परिदृश्य — 点击切换 / असाइन करने के लिए टॉगल करें", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "परिदृश्य — असाइन या हटाने के लिए क्लिक करें", + "scenarioOption": "{name}", "selectedCount": "{count, number} चयनित", "rejectReasonPrompt": "अस्वीकृति का कारण (वैकल्पिक):", "deleteAgentConfirm": "इस एजेंट को स्थायी रूप से हटाएँ?", @@ -436,7 +523,46 @@ "targetSkill": "स्किल", "targetAgent": "एजेंट", "targetCategory": "श्रेणी", - "targetUser": "उपयोगकर्ता" + "targetUser": "उपयोगकर्ता", + "adminNavLabel": "एडमिन सेक्शन", + "retry": "फिर कोशिश करें", + "close": "बंद करें", + "prevPage": "पिछला पेज", + "nextPage": "अगला पेज", + "sectionAgents": "एजेंट्स", + "sectionSkills": "स्किल्स और समुदाय", + "statTotalAgents": "कुल एजेंट्स", + "statPendingAgents": "लंबित एजेंट्स", + "statHostedAgents": "होस्टेड", + "statProjectAgents": "प्रोजेक्ट्स", + "pendingAgentsTitle": "समीक्षा के लिए लंबित एजेंट्स", + "noPendingAgents": "कोई एजेंट समीक्षा के लिए लंबित नहीं है।", + "kindHosted": "होस्टेड", + "kindProject": "प्रोजेक्ट", + "filterByStatus": "स्थिति से फ़िल्टर करें", + "filterDisabled": "अक्षम", + "actionDisable": "अक्षम करें", + "actionEnable": "फिर सक्षम करें", + "disableAgentTitle": "एजेंट अक्षम करें", + "disableAgentConfirm": "{name} को तब तक सार्वजनिक कैटलॉग से छिपा दिया जाएगा जब तक आप इसे फिर सक्षम नहीं करते। कुछ भी हटाया नहीं जाता।", + "deleteAgentTitle": "एजेंट हटाएँ", + "deleteAgentConfirmNamed": "क्या आप वाकई {name} को हटाना चाहते हैं? इस क्रिया को पूर्ववत नहीं किया जा सकता।", + "rejectAgentTitle": "एजेंट अस्वीकृत करें", + "rejectReasonLabel": "कारण (वैकल्पिक, पब्लिशर को दिखाया जाएगा)", + "rejectReasonPlaceholder": "जैसे सैंपल टास्क पर एंडपॉइंट 500 लौटाता है", + "healthOk": "स्वस्थ", + "healthDegraded": "डिग्रेडेड", + "healthDown": "डाउन", + "healthUnknown": "जाँच नहीं हुई", + "healthCheckedAt": "अंतिम हेल्थ जाँच: {date}", + "healthNeverChecked": "कोई हेल्थ जाँच दर्ज नहीं", + "batchDeleteTitle": "स्किल्स हटाएँ", + "selectAllRows": "सभी पंक्तियाँ चुनें", + "selectRow": "{name} चुनें", + "editSkillNamed": "स्किल {name} एडिट करें", + "deleteSkillNamed": "स्किल {name} हटाएँ", + "editCategoryNamed": "श्रेणी {name} एडिट करें", + "deleteCategoryNamed": "श्रेणी {name} हटाएँ" }, "Auth": { "signInTitle": "TakoAPI में साइन इन करें", @@ -462,22 +588,36 @@ "noAccount": "अकाउंट नहीं है?", "signUpLink": "साइन अप करें", "haveAccount": "पहले से अकाउंट है?", - "signInLink": "साइन इन करें" + "signInLink": "साइन इन करें", + "forgotPassword": "पासवर्ड भूल गए?", + "forgotTitle": "अपना पासवर्ड रीसेट करें", + "forgotSubtitle": "नया पासवर्ड चुनने के लिए हम आपको ईमेल से एक लिंक भेजेंगे", + "sendResetLink": "रीसेट लिंक भेजें", + "sendingResetLink": "भेजा जा रहा है...", + "resetLinkSent": "अगर उस पते पर अकाउंट है, तो हमने पासवर्ड रीसेट लिंक भेज दिया है। अपना इनबॉक्स देखें।", + "backToSignIn": "साइन इन पर वापस", + "resetTitle": "नया पासवर्ड चुनें", + "resetSubtitle": "अपने अकाउंट के लिए नया पासवर्ड दर्ज करें", + "newPasswordHint": "नया पासवर्ड (कम से कम 8 अक्षर)", + "updatePassword": "पासवर्ड अपडेट करें", + "updatingPassword": "अपडेट हो रहा है...", + "passwordUpdated": "आपका पासवर्ड अपडेट हो गया है। आपको साइन इन पर ले जा रहे हैं...", + "resetLinkInvalid": "यह पासवर्ड रीसेट लिंक अमान्य है या इसकी अवधि समाप्त हो चुकी है।", + "requestNewLink": "नया लिंक मांगें" }, "Dashboard": { "title": "डेवलपर डैशबोर्ड", "signInPrompt": "API कीज़ मैनेज करने और उपयोग देखने के लिए साइन इन करें।", - "signIn": "साइन इन करें", "description": "API कीज़ मैनेज करें और गेटवे उपयोग पर नज़र रखें। एक की, एक बिल, कोई भी एजेंट।", "apiKeys": "API कीज़", "createKey": "की बनाएँ", - "creating": "बन रहा है…", + "creating": "बन रही है…", "copyKeyWarning": "इस की को अभी कॉपी करें — यह दोबारा नहीं दिखाई जाएगी।", "copy": "कॉपी करें", "copied": "कॉपी हो गया", "noKeys": "अभी तक कोई की नहीं। एजेंट्स कॉल करना शुरू करने के लिए एक बनाएँ।", "keyFallbackName": "की", - "created": "{date} को बनाया गया", + "created": "{date} को बनाई गई", "lastUsed": "अंतिम उपयोग {date}", "neverUsed": "कभी इस्तेमाल नहीं हुई", "revoke": "रद्द करें", @@ -497,7 +637,6 @@ "billing": "क्रेडिट और बिलिंग", "creditBalance": "क्रेडिट शेष", "totalSpend": "कुल खर्च", - "topUpComingSoon": "फ़िलहाल क्रेडिट प्लेटफ़ॉर्म द्वारा जोड़े जाते हैं; PayPal से सेल्फ़-सर्विस टॉप-अप जल्द आ रहा है।", "topUpLabel": "क्रेडिट जोड़ें", "topUpButton": "PayPal से टॉप-अप करें", "topUpRedirecting": "रीडायरेक्ट हो रहा है…", @@ -513,35 +652,65 @@ "ledgerDebit": "कॉल शुल्क", "ledgerPayout": "भुगतान", "ledgerRefund": "रिफ़ंड", - "ledgerAdjust": "समायोजन" + "ledgerAdjust": "समायोजन", + "onboardingTitle": "अपनी पहली कॉल चलाएँ", + "onboardingSubtitle": "यहाँ से एजेंट के जवाब देने तक तीन कदम।", + "stepKeyTitle": "API की बनाएँ", + "stepKeyBody": "एक ही की से TakoAPI के हर एजेंट को कॉल करें। सीक्रेट सिर्फ़ एक बार दिखाया जाता है, बनाने के तुरंत बाद।", + "stepCallTitle": "अपनी पहली कॉल करें", + "stepCallBody": "नीचे से कोई सैंपल कॉपी करें, TAKO_KEY में अपनी की सेट करें और उसे चलाएँ। कॉल कुछ ही सेकंड में उपयोग के नीचे दिख जाती है।", + "stepInstallTitle": "अपने कोडिंग एजेंट में इंस्टॉल करें", + "stepInstallBody": "Claude Code, Codex या OpenCode को TakoAPI के ज़रिए एजेंट्स खोजना और कॉल करना सिखाएँ।", + "stepInstallLink": "इंस्टॉल गाइड खोलें", + "stepOptional": "वैकल्पिक", + "stepDone": "हो गया", + "stepTodo": "अभी बाकी है", + "keyNameLabel": "की का नाम", + "keyNamePlaceholder": "गेटवे की", + "createKeyError": "की नहीं बन सकी। कृपया फिर से प्रयास करें।", + "revokeNamed": "{name} रद्द करें", + "quickstartHint": "TAKO_KEY में ऊपर दी गई कोई की सेट करें, फिर किसी भी एजेंट को उसके स्लग से कॉल करें।", + "creditRule": "मुफ़्त एजेंट्स का कोई शुल्क नहीं; सशुल्क एजेंट्स आपके प्रीपेड क्रेडिट से कटते हैं, और जैसे ही शेष शून्य से नीचे जाने लगे, कॉल HTTP 402 के साथ अस्वीकार कर दी जाती है।", + "topUpUnavailable": "सेल्फ़-सर्विस टॉप-अप अभी उपलब्ध नहीं है — फ़िलहाल क्रेडिट प्लेटफ़ॉर्म द्वारा जोड़े जाते हैं। ऊपर दिखा शेष दोनों ही स्थिति में लाइव है।", + "dismiss": "बंद करें", + "loadError": "डैशबोर्ड का कुछ डेटा लोड नहीं हो सका।", + "retry": "फिर कोशिश करें", + "recentCallsCaption": "आपकी सबसे हाल की गेटवे कॉल", + "chartTitle": "प्रति दिन कॉल, पिछले {days} दिन", + "chartTotal": "कुल {count, number}", + "chartAria": "पिछले {days} दिनों में प्रति दिन गेटवे कॉल का बार चार्ट, कुल {count, number} कॉल।", + "chartDay": "{date}: {calls, number} कॉल, {errors, number} विफल" }, "Profile": { - "title": "मेरी प्रोफ़ाइल", - "signInPrompt": "अपनी प्रोफ़ाइल देखने के लिए कृपया साइन इन करें।", - "signIn": "साइन इन करें", - "submissions": "सबमिशन", + "title": "मेरी लिस्टिंग", + "signInPrompt": "आपने जो एजेंट्स और स्किल्स पब्लिश की हैं, उन्हें देखने के लिए साइन इन करें।", "likes": "लाइक्स", "views": "व्यूज़", - "statValue": "{count, number}", - "mySubmissions": "मेरे सबमिशन", - "submitNew": "नया सबमिट करें", "tab_all": "सभी", - "tab_approved": "स्वीकृत", - "tab_pending": "लंबित", - "tab_rejected": "अस्वीकृत", - "tabCount": "{count, number}", - "emptyTab": "इस टैब में कोई सबमिशन नहीं", + "emptyTab": "यहाँ अभी कोई स्किल नहीं।", "submitFirst": "अपनी पहली स्किल सबमिट करें", - "statusApproved": "स्वीकृत", - "statusPending": "समीक्षा लंबित", + "statusApproved": "लाइव", + "statusPending": "समीक्षा में", "statusRejected": "अस्वीकृत", - "reviewerNote": "समीक्षक नोट:" + "reviewerNote": "समीक्षक नोट:", + "publishAgent": "एजेंट पब्लिश करें", + "submitSkill": "स्किल सबमिट करें", + "statAgents": "एजेंट्स", + "statCalls": "गेटवे कॉल", + "statSkills": "स्किल्स", + "tabAgents": "एजेंट्स", + "tabSkills": "स्किल्स", + "statusDisabled": "अक्षम", + "kindHosted": "कॉल करने योग्य", + "kindProject": "ओपन-सोर्स", + "noAgents": "यहाँ अभी कोई एजेंट नहीं।", + "publishFirstAgent": "अपना पहला एजेंट पब्लिश करें", + "myListings": "मेरी लिस्टिंग" }, "Submit": { "title": "एक स्किल सबमिट करें", "subtitle": "अपनी OpenClaw स्किल समुदाय के साथ साझा करें", "signInPrompt": "स्किल्स सबमिट करने के लिए कृपया साइन इन करें।", - "signIn": "साइन इन करें", "skillSource": "स्किल स्रोत", "githubUrl": "GitHub URL", "clawSkillsUrl": "ClawSkills.sh URL", @@ -563,16 +732,23 @@ "whatItDoes": "यह स्किल क्या करती है", "whatItDoesPlaceholder": "यह स्किल क्या करती है, इसकी विशेषताएँ और क्षमताएँ क्या हैं इसका विस्तृत विवरण...", "exampleWorkflow": "उदाहरण वर्कफ़्लो", - "exampleWorkflowHelper": "- इनपुट / एजेंट चरण / आउटपुट", - "exampleWorkflowPlaceholder": "इनपुट\nउपयोगकर्ता पूछता है: कुछ उपयोगी करो\n\nएजेंट\n1\nटास्क पूरा करने के लिए `my-skill action` चलाएँ\n2\nपरिणाम सत्यापित करें\n\nआउटपुट\nटास्क सफलतापूर्वक पूरा हुआ", + "exampleWorkflowHelper": "- INPUT / AGENT चरण / OUTPUT", + "exampleWorkflowPlaceholder": "INPUT\nउपयोगकर्ता पूछता है: कुछ उपयोगी करो\n\nAGENT\n1\nटास्क पूरा करने के लिए `my-skill action` चलाएँ\n2\nपरिणाम सत्यापित करें\n\nOUTPUT\nटास्क सफलतापूर्वक पूरा हुआ", "requirements": "आवश्यकताएँ", "requirementsPlaceholder": "- Node.js 18+\n- सेवा X से API की\n- आदि।", "submitSkill": "स्किल सबमिट करें", "submitting": "सबमिट हो रहा है...", "enterUrlFirst": "कृपया पहले एक URL दर्ज करें", "autoFillFailed": "ऑटो-फ़िल विफल", - "submitFailed": "सबमिट करने में विफल", - "somethingWentWrong": "कुछ गलत हो गया" + "somethingWentWrong": "कुछ गलत हो गया", + "successTitle": "सबमिट हो गया 🎉", + "pendingReview": "{name} अब समीक्षा के लिए लंबित है।", + "nextStepsTitle": "आगे क्या होगा", + "nextStepReview": "एक समीक्षक रिपॉज़िटरी और विवरण जाँचता है।", + "nextStepEmail": "समीक्षा हो जाने पर हम आपको ईमेल कर देंगे।", + "nextStepProfile": "आपका लिस्टिंग पेज हमेशा इसकी मौजूदा स्थिति और कोई भी समीक्षक नोट दिखाता है।", + "viewMyListings": "मेरी लिस्टिंग देखें", + "backToSkills": "← स्किल्स पर वापस" }, "SubmitAgent": { "title": "एक एजेंट पब्लिश करें", @@ -611,7 +787,24 @@ "successTitle": "सबमिट हो गया 🎉", "pendingReview": "{name} अब समीक्षा के लिए लंबित है।", "successDetail": "हम AgentCard सत्यापित करेंगे और स्वीकृत होते ही इसे मार्केटप्लेस पर पब्लिश कर देंगे।", - "backToMarketplace": "← मार्केटप्लेस पर वापस" + "backToMarketplace": "← मार्केटप्लेस पर वापस", + "checkCard": "कार्ड जाँचें", + "checking": "जाँच हो रही है…", + "enterCardUrlFirst": "पहले AgentCard URL दर्ज करें।", + "previewTitle": "हम इसे इस तरह सूचीबद्ध करेंगे", + "previewNoDescription": "कार्ड में कोई विवरण नहीं है।", + "previewEndpoint": "एंडपॉइंट", + "previewCapabilities": "क्षमताएँ", + "previewSkillCount": "{count, plural, =0 {कोई क्षमता घोषित नहीं} one {# क्षमता} other {# क्षमताएँ}}", + "previewSupports": "सपोर्ट करता है", + "previewStreaming": "स्ट्रीमिंग", + "previewPush": "पुश नोटिफ़िकेशन", + "previewHint": "अभी कुछ भी सहेजा नहीं गया — समीक्षा के लिए भेजने हेतु नीचे पब्लिश करें।", + "nextStepsTitle": "आगे क्या होगा", + "nextStepReview": "एक समीक्षक AgentCard जाँचता है और देखता है कि आपका एंडपॉइंट रिस्पॉन्स देता है या नहीं।", + "nextStepEmail": "समीक्षा हो जाने पर हम आपको ईमेल कर देंगे।", + "nextStepProfile": "आपका लिस्टिंग पेज हमेशा इसकी मौजूदा स्थिति और कोई भी समीक्षक नोट दिखाता है।", + "viewMyListings": "मेरी लिस्टिंग देखें" }, "AgentEngagement": { "yourRating": "आपकी रेटिंग", @@ -619,44 +812,61 @@ "noRatings": "अभी तक कोई रेटिंग नहीं", "bookmark": "सहेजें", "bookmarked": "सहेजा गया", - "signInToRate": "इस एजेंट को रेट या सहेजने के लिए साइन इन करें" + "signInToRate": "इस एजेंट को रेट या सहेजने के लिए साइन इन करें", + "rateStars": "{count, plural, one {# स्टार} other {# स्टार}} दें" }, "Bookmarks": { - "title": "सहेजे गए एजेंट", - "subtitle": "आपके द्वारा बुकमार्क किए गए एजेंट।", + "title": "सहेजे गए एजेंट्स", + "subtitle": "आपके द्वारा बुकमार्क किए गए एजेंट्स।", "empty": "अभी तक कोई बुकमार्क नहीं।", - "browseAgents": "एजेंट ब्राउज़ करें", - "signInPrompt": "अपने बुकमार्क देखने के लिए साइन इन करें।", - "signIn": "साइन इन करें" + "browseAgents": "एजेंट्स ब्राउज़ करें", + "signInPrompt": "अपने बुकमार्क देखने के लिए साइन इन करें।" }, "ScenarioPage": { - "metaTitle": "{scenario} के लिए AI एजेंट", - "metaDescription": "{scenario} के लिए AI एजेंट को एक एकीकृत API से खोजें और कॉल करें — TakoAPI पर खुले A2A AgentCard द्वारा वर्णित।", - "heroSubtitle": "{scenario} के लिए AI एजेंट, एक API से कॉल किए जा सकते हैं।", + "metaTitle": "{scenario} AI एजेंट्स", + "metaDescription": "{scenario} AI एजेंट्स को एक एकीकृत API के ज़रिए खोजें और इनवोक करें — TakoAPI पर ओपन A2A AgentCards द्वारा वर्णित।", + "heroSubtitle": "{scenario} के लिए AI एजेंट्स, एक एकीकृत API के ज़रिए कॉल किए जा सकते हैं।", "empty": "इस परिदृश्य में अभी कोई एजेंट नहीं है।", - "browseAll": "सभी एजेंट ब्राउज़ करें", + "browseAll": "सभी एजेंट्स ब्राउज़ करें", "back": "← सभी परिदृश्य", "breadcrumbScenarios": "परिदृश्य", - "indexTitle": "परिदृश्य के अनुसार AI एजेंट ब्राउज़ करें", - "indexDescription": "उपयोग के अनुसार AI एजेंट खोजें — फ्रंटएंड, निवेश, मार्केटिंग, सपोर्ट और बहुत कुछ — सभी एक API से कॉल किए जा सकते हैं।", + "indexTitle": "परिदृश्य के अनुसार AI एजेंट्स ब्राउज़ करें", + "indexDescription": "उपयोग के अनुसार AI एजेंट्स खोजें — फ़्रंटएंड, निवेश, मार्केटिंग, सपोर्ट और बहुत कुछ — सभी एक API से कॉल किए जा सकते हैं।", "indexHeroTitle": "परिदृश्य के अनुसार ब्राउज़ करें", - "indexHeroSubtitle": "जो काम करना है उसके अनुसार AI एजेंट खोजें।" + "indexHeroSubtitle": "जो काम करवाना है उसके अनुसार AI एजेंट्स खोजें।", + "indexPopularHeading": "अच्छी तरह कवर किए गए", + "indexPopularHint": "ऐसे परिदृश्य जिनमें तुलना करने के लिए एजेंट्स का बड़ा कैटलॉग है।", + "indexSparseHeading": "अभी शुरुआत ही है", + "indexSparseHint": "अभी सिर्फ़ गिने-चुने एजेंट्स — पहला बनने के लिए आसान जगह।", + "indexSparseCta": "इनमें से किसी में एजेंट पब्लिश करें →", + "metaTitlePaged": "{scenario} AI एजेंट्स — पेज {page}", + "resultsLabel": "{scenario} एजेंट्स", + "paginationLabel": "पेजिनेशन", + "showingRange": "{total} में से {from}–{to} दिखा रहे हैं", + "prev": "← पिछला", + "next": "अगला →", + "goToPage": "पेज {page} पर जाएँ", + "ctaTitle": "कोई {scenario} एजेंट जानते हैं?", + "ctaBody": "इस परिदृश्य में अभी कम एजेंट्स हैं। एजेंट पब्लिश करें और वह {scenario} ब्राउज़ करने वाले सभी लोगों को यहाँ दिखेगा।", + "ctaButton": "एजेंट पब्लिश करें", + "relatedHeading": "दूसरे परिदृश्य देखें" }, "Badge": { "snippetTitle": "बैज जोड़ें", "copy": "कॉपी करें", "copied": "कॉपी हो गया", - "snippetHint": "अपने TakoAPI लिस्टिंग से लिंक करने के लिए इसे अपने README में पेस्ट करें।", + "snippetHint": "अपनी TakoAPI लिस्टिंग से लिंक करने के लिए इसे अपने README में पेस्ट करें।", "pageTitle": "TakoAPI README बैज", "pageIntro": "दिखाएँ कि आपका प्रोजेक्ट TakoAPI पर सूचीबद्ध है — एक छोटा बैज जो आपकी लिस्टिंग से लिंक करता है और आपकी लाइव स्टार संख्या दिखाता है।", "example": "उदाहरण", "step1Title": "अपनी लिस्टिंग खोजें", "step1": "एजेंट डायरेक्टरी में अपना प्रोजेक्ट खोजें।", "step2Title": "बैज कॉपी करें", - "step2": "अपनी लिस्टिंग पेज पर बैज बॉक्स से Markdown कॉपी करें।", + "step2": "अपने लिस्टिंग पेज पर बैज बॉक्स से Markdown कॉपी करें।", "step3Title": "README में पेस्ट करें", "step3": "इसे ऊपर की ओर जोड़ें — विज़िटर और सर्च इंजन को आपके TakoAPI पेज का लिंक मिलता है।", - "findListing": "अपनी लिस्टिंग खोजें" + "findListing": "अपनी लिस्टिंग खोजें", + "exampleAlt": "TakoAPI बैज" }, "Blog": { "title": "ब्लॉग", @@ -666,5 +876,149 @@ "minRead": "{min} मिनट का पठन", "published": "प्रकाशित", "faqTitle": "अक्सर पूछे जाने वाले प्रश्न" + }, + "NotFound": { + "title": "वह पेज नहीं मिला", + "description": "हो सकता है लिंक पुराना हो, या लिस्टिंग हटा दी गई हो। डायरेक्टरी देखें या हेडर से खोजें।", + "home": "होम", + "agents": "एजेंट्स ब्राउज़ करें", + "skills": "स्किल्स ब्राउज़ करें" + }, + "ErrorPage": { + "title": "कुछ गलत हो गया", + "description": "यह पेज लोड नहीं हो सका। दोबारा कोशिश करने से अक्सर काम बन जाता है; अगर नहीं, तो नीचे दिया रेफ़रेंस हमें इसे लॉग्स में ढूँढ़ने में मदद करता है।", + "reference": "रेफ़रेंस: {id}", + "retry": "फिर कोशिश करें", + "home": "होम जाएँ" + }, + "Consent": { + "title": "एनालिटिक्स", + "description": "हम Google Analytics का इस्तेमाल यह देखने के लिए करते हैं कि लोगों को कौन-से पेज उपयोगी लगते हैं। जब तक आप स्वीकार नहीं करते, न कुछ लोड होता है और न कोई कुकी सेट होती है।", + "accept": "स्वीकार करें", + "decline": "अस्वीकार करें" + }, + "Docs": { + "metaTitle": "API डॉक्युमेंटेशन", + "title": "TakoAPI API रेफ़रेंस", + "description": "API की से लेकर चलती हुई एजेंट कॉल तक: ऑथेंटिकेशन, डिस्कवरी एंडपॉइंट्स, एकीकृत एजेंट गेटवे, होस्टेड MCP सर्वर, एरर कोड, रेट लिमिट और बिलिंग।", + "heroBadge": "डेवलपर डॉक्स", + "heroTitle": "TakoAPI API रेफ़रेंस", + "heroSubtitle": "एक की, हर एजेंट। किसी भी OpenAI-संगत क्लाइंट को TakoAPI पर पॉइंट करें और एजेंट को उसके स्लग से कॉल करें — या बिना किसी की के पूरा कैटलॉग ब्राउज़ करें।", + "heroKeyCta": "API की बनाएँ", + "heroBrowseCta": "एजेंट्स ब्राउज़ करें", + "tocTitle": "इस पेज पर", + "tocLabel": "विषय-सूची", + "quickstartTitle": "क्विकस्टार्ट", + "quickstartIntro": "शून्य से जवाब तक तीन कदम। इस पेज का हर रिक्वेस्ट लाइव API पर की गई असली कॉल है।", + "quickstartStep1Title": "की बनाएँ", + "quickstartStep1": "साइन इन करें और अपने डैशबोर्ड में एक API की बनाएँ। सीक्रेट सिर्फ़ एक बार, बनाते समय दिखाया जाता है।", + "quickstartStep2Title": "इसे एक्सपोर्ट करें", + "quickstartStep2": "इस पेज का हर उदाहरण की को TAKO_KEY से पढ़ता है।", + "quickstartStep3Title": "एजेंट को कॉल करें", + "quickstartStep3": "model — या URL में दिए स्लग — को कैटलॉग के किसी भी होस्टेड एजेंट पर सेट करें।", + "quickstartSamplesLabel": "क्विकस्टार्ट कॉल उदाहरण", + "quickstartNote": "इन सैंपल्स का स्लग सिर्फ़ प्लेसहोल्डर है, असली एजेंट नहीं। किसी भी एजेंट पेज से असली स्लग कॉपी करें; सिर्फ़ होस्टेड के रूप में सूचीबद्ध एजेंट्स ही इनवोक किए जा सकते हैं।", + "authenticationTitle": "ऑथेंटिकेशन", + "authenticationIntro": "गेटवे कॉल bearer टोकन से ऑथेंटिकेट होती हैं। डिस्कवरी एंडपॉइंट्स एनॉनिमस हैं और उन्हें किसी की की ज़रूरत नहीं।", + "authHeaderNote": "Authorization हेडर के विकल्प के रूप में x-api-key भी स्वीकार किया जाता है। कीज़ हमेशा tako_live_ से शुरू होती हैं।", + "authWhereTitle": "कीज़ कहाँ से आती हैं", + "authWhere": "अपने डैशबोर्ड में हर क्लाइंट या एनवायरनमेंट के लिए एक की बनाएँ, और जिनका इस्तेमाल बंद कर दें उन्हें रद्द कर दें।", + "authOnceTitle": "सीक्रेट सिर्फ़ एक बार दिखता है", + "authOnce": "सिर्फ़ की का SHA-256 हैश और एक छोटा डिस्प्ले प्रीफ़िक्स स्टोर होता है, इसलिए खोया हुआ सीक्रेट वापस नहीं पाया जा सकता — नई की बनाएँ और पुरानी रद्द कर दें।", + "authRevokeTitle": "रद्द करना तुरंत लागू होता है", + "authRevoke": "हर कॉल की को डेटाबेस से दोबारा जाँचती है और कुछ भी कैश नहीं होता, इसलिए रद्द की गई की अगले ही रिक्वेस्ट पर अस्वीकार हो जाती है। पिछला उपयोग आपकी हिस्ट्री में बना रहता है।", + "discoveryTitle": "डिस्कवरी", + "discoveryIntro": "चार सार्वजनिक, कैश किए जा सकने वाले एंडपॉइंट्स जिन्हें किसी की की ज़रूरत नहीं। सिर्फ़ स्वीकृत एजेंट्स और स्किल्स ही लौटाए जाते हैं।", + "endpointRegistrySummary": "पूरा एजेंट कैटलॉग — मॉडल के लिए Markdown, कोड के लिए JSON।", + "endpointRegistryNote": "format=json के बिना रिस्पॉन्स Markdown (text/markdown) होता है, जो होस्टेड-एजेंट्स और ओपन-सोर्स-प्रोजेक्ट्स की दो टेबल में बँटा होता है और इस आकार का रखा जाता है कि सीधे मॉडल के कॉन्टेक्स्ट में डाला जा सके — इंस्टॉल की गई TakoAPI स्किल यही पढ़ती है। दोनों फ़ॉर्मैट limit से सीमित होते हैं, इसलिए यह मानने से पहले कि आपके पास सब कुछ है, truncated और कुल संख्याएँ जाँच लें। फ़्री-टेक्स्ट पैरामीटर 100 अक्षरों पर काट दिए जाते हैं।", + "paramRegistryFormat": "md (डिफ़ॉल्ट) या json।", + "paramRegistryQ": "एजेंट के नाम और विवरण पर फ़्री-टेक्स्ट मैच।", + "paramRegistryCategory": "श्रेणी स्लग।", + "paramRegistryProtocol": "सिर्फ़ वे एजेंट्स लौटाएँ जो यह प्रोटोकॉल घोषित करते हैं।", + "paramRegistryKind": "HOSTED एजेंट्स गेटवे के ज़रिए कॉल किए जा सकते हैं; PROJECT एंट्रीज़ सिर्फ़ खोज के लिए सूचीबद्ध ओपन-सोर्स रिपॉज़िटरी हैं।", + "paramRegistrySort": "क्रम। डिफ़ॉल्ट रूप से पहले फ़ीचर्ड, फिर सर्वाधिक कॉल किए गए, फिर नवीनतम — और प्रोजेक्ट्स के लिए सर्वाधिक स्टार वाले।", + "paramRegistryLimit": "कुल कितनी एंट्रीज़ लौटानी हैं। पहले होस्टेड एजेंट्स भरे जाते हैं, फिर प्रोजेक्ट्स।", + "endpointAgentDetailSummary": "एक एजेंट का पूरा सार्वजनिक विवरण, उसकी घोषित A2A स्किल्स सहित।", + "endpointAgentDetailNote": "सिर्फ़ स्वीकृत एजेंट्स ही रिज़ॉल्व होते हैं; बाकी सब 404 है। मॉडरेशन फ़ील्ड्स — समीक्षा नोट, इंटरनल id और पब्लिशर अवतार — कभी इस शेप का हिस्सा नहीं होते। एज पर 60 सेकंड के लिए कैश होता है।", + "paramAgentDetailSlug": "पाथ सेगमेंट। एजेंट का स्लग, जैसा रजिस्ट्री लौटाती है।", + "endpointSkillsDirectorySummary": "OpenClaw स्किल्स कैटलॉग, डिफ़ॉल्ट रूप से Markdown।", + "endpointSkillsDirectoryNote": "रजिस्ट्री की तरह पहले Markdown। जिन स्किल्स के लिए कोई इंस्टॉल कमांड पब्लिश नहीं है उनके लिए install null होता है — तब github लिंक इस्तेमाल करें। downloads एक थर्ड-पार्टी आंकड़ा है जो स्किल की अपनी लिस्टिंग से लिया गया है, TakoAPI की इंस्टॉल गिनती नहीं।", + "paramSkillsDirFormat": "md (डिफ़ॉल्ट) या json।", + "paramSkillsDirQ": "स्किल के नाम और विवरण पर फ़्री-टेक्स्ट मैच।", + "paramSkillsDirCategory": "श्रेणी स्लग।", + "paramSkillsDirPage": "पेज नंबर, 1 से शुरू।", + "paramSkillsDirLimit": "प्रति पेज परिणाम।", + "endpointSkillsSearchSummary": "स्किल्स कैटलॉग में फ़ुल-टेक्स्ट सर्च, सिर्फ़ JSON।", + "endpointSkillsSearchNote": "q ज़रूरी है: इसके बिना एंडपॉइंट पूरा कैटलॉग नहीं, बल्कि खाली सूची लौटाता है। हर परिणाम पूरी कैटलॉग रो है, बस readme और reviewNote के बिना; ऊपर दिया उदाहरण संक्षिप्त है।", + "paramSkillsSearchQ": "सर्च टर्म। स्किल के नाम, विवरण और लेखक से मैच करता है।", + "paramSkillsSearchCategory": "श्रेणी स्लग।", + "paramSkillsSearchPage": "पेज नंबर, 1 से शुरू।", + "paramSkillsSearchLimit": "प्रति पेज परिणाम।", + "gatewayTitle": "गेटवे", + "gatewayIntro": "एक ही एजेंट को इनवोक करने के तीन तरीके। तीनों को की चाहिए, तीनों की बिलिंग एक जैसी है, और तीनों वहाँ एजेंट का स्लग स्वीकार करते हैं जहाँ आम तौर पर मॉडल का नाम आता है। सिर्फ़ HOSTED प्रकार के एजेंट्स ही कॉल किए जा सकते हैं।", + "endpointChatCompletionsSummary": "OpenAI-संगत। किसी भी OpenAI SDK को /v1 पर पॉइंट करें और model को एजेंट स्लग पर सेट करें।", + "endpointChatCompletionsNote": "पूरी बातचीत फ़ॉरवर्ड की जाती है: हर टर्न के लिए एक A2A text part, क्रम में, और हर part के साथ OpenAI role जुड़ा हुआ। usage हमेशा मौजूद रहता है पर हमेशा शून्य — इस गेटवे में कुछ भी टोकन नहीं गिनता, और ज़रूरी नहीं कि एजेंट कोई LLM हो। ऊपर वाले SSE रूप के लिए stream: true सेट करें, और आख़िरी usage चंक भी पाने के लिए stream_options.include_usage। बिना स्ट्रीम वाली कॉल 30 सेकंड बाद टाइमआउट होती है, स्ट्रीम 120 सेकंड बाद।", + "endpointA2AMessageSummary": "नेटिव A2A पासथ्रू। एजेंट का अपना JSON-RPC रिस्पॉन्स बिना बदले लौटाता है।", + "endpointA2AMessageNote": "इसे A2A message/send कॉल के रूप में रिले किया जाता है। text शॉर्टहैंड भेजें, या पूरा A2A message — parts, taskId और contextId के साथ — ताकि मल्टी-टर्न टास्क जारी रखा जा सके या file और data parts आगे भेजे जा सकें। एजेंट का स्टेटस कोड और बॉडी बिना छेड़े रिले होते हैं, और बिना कंटेंट वाला रिक्वेस्ट बिल किए बिना 400 के साथ अस्वीकार कर दिया जाता है। 30 सेकंड बाद टाइमआउट होता है।", + "endpointA2AStreamSummary": "वही कॉल Server-Sent Events पर, उन एजेंट्स के लिए जो स्ट्रीम करते हैं।", + "endpointA2AStreamNote": "एजेंट के A2A message/stream फ़्रेम्स को हूबहू text/event-stream के रूप में रिले करता है। यह एंडपॉइंट सिर्फ़ text शॉर्टहैंड लेता है। कनेक्शन 120 सेकंड बाद काट दिया जाता है, और कॉल का शुल्क तभी लगता है जब स्ट्रीम अपने अंत तक पहुँचती है — देखें रेट लिमिट और बिलिंग।", + "labelQueryParams": "पैरामीटर", + "labelRequest": "रिक्वेस्ट बॉडी", + "labelExample": "उदाहरण", + "labelResponse": "रिस्पॉन्स", + "labelStreaming": "stream: true के साथ रिस्पॉन्स", + "labelA2AMessageForm": "पूरा A2A message रूप", + "labelAuthRequired": "की ज़रूरी", + "labelNoAuth": "की नहीं", + "labelBillable": "बिल होने योग्य", + "labelFree": "मुफ़्त", + "labelParamName": "नाम", + "labelParamType": "प्रकार", + "labelParamDesc": "विवरण", + "paramsTableCaption": "{path} के पैरामीटर", + "mcpTitle": "MCP सर्वर", + "mcpIntro": "TakoAPI {url} पर Streamable HTTP के ज़रिए एक होस्टेड, स्टेटलेस Model Context Protocol सर्वर चलाता है। इसे एक बार रजिस्टर करें और आपका कोडिंग एजेंट कैटलॉग को नेटिव टूल्स की तरह खोज और इनवोक कर सकता है।", + "mcpRegisterTitle": "इसे रजिस्टर करें", + "mcpRegisterLabel": "MCP रजिस्ट्रेशन कमांड", + "mcpAuthNote": "तीनों रीड टूल्स एनॉनिमस चलते हैं। invoke_agent क्रेडिट खर्च करता है, इसलिए उसे आपकी की चाहिए: इसे Authorization हेडर (Claude Code), bearer_token_env_var (Codex), या headers ब्लॉक (OpenCode) में दें।", + "mcpToolsTitle": "टूल्स", + "mcpToolsTableCaption": "TakoAPI MCP सर्वर द्वारा एक्सपोज़ किए गए टूल्स", + "mcpToolColumn": "टूल", + "mcpToolAccessColumn": "एक्सेस", + "mcpToolWhatColumn": "यह क्या करता है", + "mcpToolKeyRequired": "की ज़रूरी", + "mcpToolAnonymous": "एनॉनिमस", + "mcpToolSearchAgents": "कीवर्ड, श्रेणी या प्रोटोकॉल से रजिस्ट्री खोजें। स्लग, प्रोटोकॉल और प्राइसिंग लौटाता है।", + "mcpToolGetAgent": "स्लग से एक एजेंट के बारे में सब कुछ — क्षमताएँ, प्रोटोकॉल, एंडपॉइंट, प्राइसिंग और घोषित स्किल्स।", + "mcpToolSearchSkills": "OpenClaw स्किल्स कैटलॉग खोजें। बिना क्वेरी के यह क्यूरेटेड टॉप स्किल्स लौटाता है।", + "mcpToolInvokeAgent": "गेटवे के ज़रिए एजेंट को इनवोक करें और उसका जवाब लौटाएँ। इसके लिए की ज़रूरी है और क्रेडिट खर्च होता है।", + "mcpRateNote": "MCP एंडपॉइंट प्रति IP पते प्रति मिनट 120 रिक्वेस्ट तक सीमित है, जो आपकी की के गेटवे कोटे से अलग है।", + "mcpInstallNote": "सर्वर के बजाय स्किल फ़ाइल पसंद है? इंस्टॉलर Claude Code, Codex और OpenCode को इन एंडपॉइंट्स को सादे HTTP पर कॉल करना सिखा देता है।", + "errorsTitle": "एरर", + "errorsIntro": "गेटवे की हर विफलता JSON होती है। ये वे स्टेटस हैं जो गेटवे खुद लौटाता है।", + "errorsTableCaption": "गेटवे द्वारा लौटाए गए एरर स्टेटस", + "errorStatusColumn": "स्टेटस", + "errorCodeColumn": "कोड", + "errorWhenColumn": "कब", + "errorBodyColumn": "उदाहरण बॉडी", + "error400When": "स्लग किसी ऐसे ओपन-सोर्स प्रोजेक्ट का है जिसका कोई इनवोक करने योग्य एंडपॉइंट नहीं, रिक्वेस्ट बॉडी वैलिडेशन में विफल रही, या हर message टर्न खाली था।", + "error401When": "की गायब है, गलत बनावट की है, अनजान है या रद्द की जा चुकी है।", + "error402When": "एजेंट सशुल्क है और कॉल का शुल्क लेने पर आपका शेष शून्य से नीचे चला जाएगा। यह एजेंट से संपर्क करने से पहले जाँचा जाता है, इसलिए कुछ भी खर्च नहीं होता।", + "error404When": "उस स्लग वाला कोई स्वीकृत एजेंट नहीं है।", + "error429When": "की पिछले 60 सेकंड में अपने कोटे से आगे निकल गई। Retry-After बताता है कि विंडो रीसेट होने में कितने सेकंड बाकी हैं।", + "error502When": "एजेंट ने जवाब नहीं दिया, टाइमआउट हो गया, कोई स्ट्रीम नहीं लौटाई, या HTTP 200 पर JSON-RPC एरर बताया। इसका शुल्क कभी नहीं लगता।", + "errorsOpenAiNote": "/v1/chat/completions पर वही विफलताएँ OpenAI के एनवेलप में आती हैं — जिनमें type, param और code हमेशा मौजूद रहते हैं — इसलिए OpenAI SDK अपने सामान्य exception टाइप ही उठाता है:", + "errorsPassthroughNote": "एजेंट ने खुद जो स्टेटस लौटाया है उसे A2A message एंडपॉइंट बिना बदले रिले करता है, इसलिए वहाँ का 4xx हमारा नहीं, एजेंट का हो सकता है।", + "limitsTitle": "रेट लिमिट और बिलिंग", + "limitsRateTitle": "रेट लिमिट", + "limitsRate": "गेटवे प्रति API की, प्रति 60 सेकंड 120 रिक्वेस्ट की अनुमति देता है। यह विंडो हर सर्वर इंस्टेंस में साझा होती है और आपके IP पते के बजाय की पर ही आधारित है, इसलिए न यह लोड में ढीली पड़ती है और न साझा NAT के पीछे सख़्त होती है।", + "limitsRateOverride": "किसी की का अपना कोटा भी हो सकता है, जो सिर्फ़ उसी की के लिए डिफ़ॉल्ट की जगह ले लेता है। लिमिट पार होने पर आपको Retry-After हेडर (सेकंड में) के साथ 429 मिलता है।", + "limitsBillingTitle": "बिलिंग", + "limitsBillingFree": "FREE कीमत वाले एजेंट्स का कोई शुल्क नहीं — और न ही उन एजेंट्स का जिनका यूनिट मूल्य गायब या शून्य है। गेटवे कभी अपने आप कोई शुल्क नहीं गढ़ता।", + "limitsBillingPriced": "सशुल्क एजेंट प्रति कॉल एक बार, अपने यूनिट मूल्य पर आपके प्रीपेड क्रेडिट से काटता है। टोकन-स्तर की कोई मीटरिंग नहीं है, इसलिए कॉल कितनी भी लंबी चले, वह एक ही यूनिट है।", + "limitsBillingPreflight": "आपका शेष एजेंट से संपर्क करने से पहले जाँचा जाता है। अगर शुल्क से आप शून्य से नीचे चले जाएँगे तो कॉल 402 के साथ अस्वीकार कर दी जाती है और एजेंट को कभी कॉल नहीं किया जाता — ओवरड्राफ़्ट नहीं होता।", + "limitsBillingFailures": "विफल होने वाली कॉल्स का शुल्क नहीं लगता। एजेंट से आया 4xx या 5xx, टाइमआउट, न पहुँच पाने वाला एंडपॉइंट या JSON-RPC एरर — ये सब कोशिश दर्ज करते हैं और कोई बिल नहीं बनाते।", + "limitsBillingStreams": "स्ट्रीम का शुल्क तभी लगता है जब वह पूरी हो। जो स्ट्रीम रद्द हो जाए, टाइमआउट हो जाए, बीच में विफल हो जाए, या जिससे आप जल्दी डिस्कनेक्ट हो जाएँ, वह दर्ज तो होती है पर बिल नहीं होती।", + "limitsBillingLedger": "हर शुल्क उसी इनवोकेशन के विरुद्ध एक अपरिवर्तनीय लेजर एंट्री लिखता है जिससे वह बना। शेष, लेजर और प्रति-कॉल उपयोग सब आपके डैशबोर्ड में हैं।" } } diff --git a/messages/id.json b/messages/id.json index f42713f91..3a4cd773f 100644 --- a/messages/id.json +++ b/messages/id.json @@ -7,60 +7,78 @@ "label": "Bahasa" }, "Header": { - "searchPlaceholder": "Cari agen...", "agents": "Agen", "skills": "Skill", - "trending": "Tren", - "publish": "Terbitkan", - "publishAgent": "Terbitkan Agen", + "publishAgent": "Terbitkan agen", "account": "Akun", - "mySkills": "Skill Saya", - "myBookmarks": "Tersimpan", - "apiUsage": "API & Penggunaan", - "adminPanel": "Panel Admin", + "myBookmarks": "Agen tersimpan", + "apiUsage": "Kunci API & penggunaan", + "adminPanel": "Panel admin", "signIn": "Masuk", - "signOut": "Keluar" + "signOut": "Keluar", + "mainNav": "Utama", + "skipToContent": "Lewati ke konten", + "openMenu": "Buka menu", + "closeMenu": "Tutup menu", + "projects": "Proyek", + "install": "Pasang", + "getApiKey": "Dapatkan kunci API", + "search": "Cari", + "searchScope": "Cari di", + "scopeAgents": "Agen", + "scopeProjects": "Proyek", + "scopeSkills": "Skill", + "searchAgentsPlaceholder": "Cari agen berdasarkan fungsinya…", + "searchProjectsPlaceholder": "Cari proyek agen open-source…", + "searchSkillsPlaceholder": "Cari skill untuk coding agent Anda…", + "myListings": "Listing saya", + "docs": "Dokumentasi" }, "Footer": { - "tagline": "Satu API untuk mengakses semua agen — plus ribuan skill OpenClaw untuk coding agent Anda.", + "tagline": "Satu API untuk mengakses semua agen — temukan, panggil, dan hitung pemakaian agen mana pun lewat satu endpoint.", "agentRegistry": "Registri agen:", "platform": "Platform", - "resources": "Sumber Daya", - "installInAgent": "Pasang di agen Anda", - "browseAgents": "Jelajahi Agen", - "publishAnAgent": "Terbitkan Agen", - "developerDashboard": "Dasbor Developer", - "browseSkills": "Jelajahi Skill", - "awesomeList": "Awesome List", + "resources": "Sumber daya", + "installInAgent": "Pasang di coding agent Anda", + "browseAgents": "Jelajahi agen", + "publishAnAgent": "Terbitkan agen", + "developerDashboard": "Kunci API & penggunaan", + "browseSkills": "Jelajahi skill", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Dibuat untuk komunitas OpenClaw.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. Berlisensi MIT.", + "blog": "Blog", + "badge": "Lencana README", + "scenarios": "Jelajahi per skenario", + "a2aSpec": "Protokol A2A", + "github": "GitHub", + "docs": "Dokumentasi API" }, "Home": { "heroTitle": "Satu API untuk mengakses semua agen", - "heroSubtitle": "Temukan dan panggil {agents, number} agen AI melalui satu API terpadu — plus {skills, number} skill untuk coding agent Anda.", + "heroSubtitle": "Temukan agen AI, lalu panggil semuanya lewat satu endpoint dengan satu kunci. Native A2A, kompatibel dengan OpenAI, dihitung per panggilan.", "browseAgents": "Jelajahi agen", - "publishAnAgent": "Terbitkan agen", - "agentRegistry": "Registri agen:", - "browseByScenario": "Jelajahi berdasarkan skenario", - "allAgents": "Semua agen", - "featuredAgents": "Agen Unggulan", + "browseByScenario": "Jelajahi berdasarkan kebutuhan Anda", + "featuredAgents": "Agen unggulan", "browseAll": "Jelajahi semua", "popularProjects": "Proyek open-source populer", "selfHostable": "{count, number} bisa di-self-host", - "skillTitle": "Skill TakoAPI untuk OpenClaw", - "official": "Resmi", - "skillDescription": "Cari, pasang, dan kelola skill OpenClaw lewat percakapan alami — didukung oleh marketplace TakoAPI.", - "viewOnGithub": "Lihat di GitHub", - "askAgent": "atau minta agen Anda: \"Pasang skill TakoAPI\"", - "usingCodingAgent": "Memakai Claude Code, Codex, atau OpenCode?", - "oneCommandInstall": "Pasang dengan satu perintah", - "mustHaveSkills": "Skill Wajib Punya", - "viewRankings": "Lihat peringkat", - "categories": "Kategori", - "viewAll": "Lihat semua", + "categories": "Kategori:", "moreCategories": "{count, plural, other {+# kategori lagi}}", - "latestSkills": "Skill Terbaru" + "getApiKey": "Dapatkan kunci API", + "catalogSummary": "{agents, number} agen yang dapat dipanggil · {projects, number} proyek open-source · {skills, number} skill coding agent", + "quickstartLabel": "30 detik menuju panggilan pertama", + "quickstartNote": "Lebih suka bekerja dari terminal? Pasang TakoAPI ke coding agent Anda.", + "allScenarios": "Semua skenario", + "callableNow": "{count, number} dapat dipanggil lewat gateway", + "installTitle": "Gunakan TakoAPI dari coding agent Anda", + "installDescription": "Satu perintah mengajari Claude Code, Codex, atau OpenCode untuk mencari direktori dan memanggil agen untuk Anda — atau daftarkan MCP server yang di-hosting.", + "installCta": "Panduan instalasi", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Skill untuk coding agent Anda", + "skillsSubtitle": "{count, number} skill OpenClaw, yang paling banyak dipasang dan yang terbaru.", + "quickstartTabDiscover": "1. Temukan", + "quickstartTabCall": "2. Panggil", + "quickstartTabFromAgent": "3. Dari agen Anda" }, "Scenarios": { "frontend": "Frontend", @@ -85,8 +103,6 @@ "notFound": "Agen tidak ditemukan", "backToMarketplace": "← Marketplace Agen", "openSourceProject": "Proyek open-source", - "signedCard": "Kartu bertanda tangan", - "verifiedPublisher": "Penerbit terverifikasi", "by": "oleh {name}", "publisherOpenSource": "open-source", "publisherUnknown": "tidak diketahui", @@ -101,17 +117,41 @@ "noSkillsHosted": "Agen ini tidak mencantumkan skill terstruktur di AgentCard-nya.", "inputMode": "masuk: {mode}", "outputMode": "keluar: {mode}", - "openSourceProjectHeading": "Proyek open-source", "openSourceProjectNote": "Self-host atau telusuri kodenya. Tidak dapat dipanggil melalui gateway TakoAPI.", "viewOnGithub": "Lihat di GitHub", - "integration": "Integrasi", "endpoint": "Endpoint", "agentCard": "AgentCard", "homepage": "Beranda", "callThroughTakoApi": "Panggil melalui TakoAPI", - "gatewayPhase2": "Gateway terpadu — pemanggilan hadir di Fase 2.", "breadcrumbHome": "Beranda", - "breadcrumbMarketplace": "Marketplace Agen" + "breadcrumbMarketplace": "Marketplace Agen", + "gatewayHint": "Panggil dengan kunci API Anda — buat satu di dasbor.", + "healthOk": "Merespons", + "healthDegraded": "Menurun", + "healthDown": "Tidak merespons", + "healthCheckedAt": "Terakhir diperiksa {time}", + "atAGlance": "Sekilas", + "protocols": "Protokol", + "protocolsNone": "Tidak dicantumkan", + "yes": "Ya", + "no": "Tidak", + "pricing": "Harga", + "health": "Kesehatan", + "healthUnknown": "Belum diperiksa", + "totalCalls": "Total panggilan", + "numberValue": "{count, number}", + "averageRating": "Rating rata-rata", + "ratingValue": "{rating} / 5 · {count, number} penilaian", + "notRated": "Belum ada penilaian", + "callExamplesLabel": "Contoh panggilan", + "repository": "Repositori", + "language": "Bahasa", + "license": "Lisensi", + "licenseNone": "Tidak disebutkan", + "lastPush": "Push terakhir", + "topics": "Topik", + "readmeHeading": "Dari README", + "readmeNote": "Kutipan dari README proyek di GitHub." }, "Agents": { "metaTitle": "Marketplace Agen", @@ -140,16 +180,29 @@ "pricingPerCall": "per panggilan", "pricingPerTask": "per tugas", "pricingPerToken": "Per 1k token", - "count": "{count, plural, other {# agen}}", + "count": "{count, plural, =0 {Tidak ada agen} other {# agen}}", "emptyTitle": "Belum ada agen yang cocok dengan filter Anda.", "emptyCta": "Terbitkan yang pertama →", "prev": "← Sebelumnya", "next": "Berikutnya →", - "pageOf": "Halaman {page} dari {total}" + "pageOf": "Halaman {page} dari {total}", + "filtersTitle": "Filter", + "filtersWithCount": "Filter ({count})", + "activeFiltersLabel": "Filter aktif:", + "removeFilter": "Hapus filter: {label}", + "clearAll": "Hapus semua", + "kindLabel": "Tipe", + "scenarioGroup": "Skenario", + "protocolGroup": "Protokol", + "pricingGroup": "Harga", + "categoryGroup": "Kategori", + "searchChip": "Pencarian: {query}", + "resultsLabel": "Hasil", + "paginationLabel": "Paginasi", + "goToPage": "Ke halaman {page}", + "showingRange": "Menampilkan {from}–{to} dari {total}" }, "Components": { - "homeSearchPlaceholder": "Cari skill berdasarkan nama, deskripsi, atau penulis...", - "homeSearchButton": "Cari", "agentFree": "Gratis", "agentPerCall": "{price}/panggilan", "agentPerTask": "{price}/tugas", @@ -163,7 +216,11 @@ "agentSelfHost": "self-host", "agentSkills": "{count, plural, other {# skill}}", "skillBy": "oleh {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} panggilan", + "healthOk": "Merespons", + "healthDegraded": "Menurun", + "healthDown": "Tidak merespons" }, "Install": { "metaTitle": "Pasang di Claude Code, Codex & OpenCode", @@ -181,7 +238,7 @@ "whatTeachesAgent": "Mengajari agen Anda memanggil registri TakoAPI dan gateway lewat HTTP — tanpa dependensi tambahan, tanpa server yang perlu dijalankan.", "whatMcpServer": "Lebih suka tool yang di-hosting? Daftarkan MCP server TakoAPI sebagai gantinya — claude mcp add --transport http takoapi https://takoapi.com/mcp — yang mengekspos search_agents, get_agent, search_skills, dan invoke_agent sebagai tool MCP native.", "whatIdempotent": "Bersifat idempoten (aman dijalankan ulang) dan sepenuhnya dapat dibatalkan dengan --uninstall.", - "whatApiKey": "Untuk gateway berbasis kuota, buat kunci API di dasbor Anda dan ekspor sebagai TAKO_KEY.", + "whatApiKey": "Untuk gateway yang dihitung per panggilan, buat kunci API di dasbor Anda dan ekspor sebagai TAKO_KEY.", "footnote": "Di Windows atau sudah pakai Node? Gunakan npx takoapi-install. Ingin membaca skripnya dulu? Skrip itu berupa shell biasa di takoapi.com/install.sh." }, "InstallTabs": { @@ -196,7 +253,7 @@ "howToUseHeading": "Cara menggunakan", "claudeCodeUse": "Dimuat otomatis — cukup minta Claude menemukan agen di TakoAPI.", "codexUse": "Panggil dengan $takoapi, atau pilih dari menu /skills.", - "opencodeUse": "Jalankan /takoapi , atau serahkan ke @takoapi.", + "opencodeUse": "Jalankan /takoapi '', atau serahkan ke @takoapi.", "mcpHeading": "MCP server — tool yang di-hosting, tanpa perlu instalasi", "claudeCodeMcpHint": "Satu perintah. Tambahkan --header \"Authorization: Bearer $TAKO_KEY\" untuk mengaktifkan invoke_agent.", "codexMcpHint": "Tambahkan ke ~/.codex/config.toml", @@ -221,17 +278,22 @@ "whenToUseIt": "Kapan Menggunakannya", "exampleWorkflow": "Contoh Alur Kerja", "workflowInput": "INPUT", - "workflowAgent": "AGEN", + "workflowAgent": "AGENT", "workflowOutput": "OUTPUT", "requirements": "Persyaratan", "installation": "Instalasi", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClaw Chat (tempel ini ke percakapan Anda)", "links": "Tautan", "github": "GitHub", - "copyGithubUrl": "Salin URL GitHub", "clawHub": "ClawHub", - "skillNotFound": "Skill tidak ditemukan" + "codingAgent": "Coding agent", + "agentPrompt": "Pasang skill dari {url}", + "installHintRegistry": "Jalankan perintah CLI-nya, atau tempel prompt ke Claude Code, Cursor, Codex, atau coding agent apa pun yang Anda pakai.", + "installHintRepo": "Skill ini belum dipublikasikan di registri ClawHub. Tempel prompt ke coding agent Anda — Claude Code, Cursor, Codex — dan skill-nya akan dipasang langsung dari repositori sumbernya.", + "installUnavailable": "Belum ada sumber instalasi yang dipublikasikan untuk skill ini.", + "openRepository": "Buka repositori", + "signInToLike": "Masuk untuk menyukai skill ini", + "downloadsSource": "Unduhan dilaporkan oleh clawskills.sh" }, "Skills": { "metaTitle": "Jelajahi Skill", @@ -243,7 +305,7 @@ "count": "{count, number} skill ditemukan", "sortLatest": "Terbaru", "sortPopular": "Paling Populer", - "sortViews": "Paling Banyak Ditonton", + "sortViews": "Paling Banyak Dilihat", "sortStars": "Paling Banyak Dibintangi", "agentAll": "Semua agen", "agentClaudeCode": "Claude Code", @@ -257,13 +319,32 @@ "all": "Semua", "emptyTitle": "Tidak ada skill ditemukan", "emptyHint": "Coba sesuaikan pencarian atau filter Anda", - "pageOf": "Halaman {page} dari {total}" + "pageOf": "Halaman {page} dari {total}", + "metaTitleSearch": "Skill yang cocok dengan \"{query}\"", + "metaTitlePaged": "Jelajahi Skill — halaman {page}", + "searchInputLabel": "Cari skill", + "searchPlaceholder": "Cari skill berdasarkan nama, deskripsi, atau penulis", + "searchButton": "Cari", + "agentFilter": "Agen", + "agentZed": "Zed", + "sourceFilter": "Sumber", + "sourceGithub": "GitHub", + "sourceCommunity": "Komunitas", + "sourceCurated": "Kurasi", + "categoryFilter": "Kategori", + "sortLabel": "Urutkan", + "sortRelevance": "Paling relevan", + "clearFilters": "Hapus filter", + "paginationLabel": "Halaman daftar skill", + "goToPage": "Ke halaman {page}", + "prev": "Sebelumnya", + "next": "Berikutnya" }, "Trending": { "metaTitle": "Skill Tren", - "metaDescription": "Skill OpenClaw yang paling banyak diunduh dan paling banyak disukai saat ini — papan peringkat langsung tentang apa yang sedang dipasang coding agent.", + "metaDescription": "Skill OpenClaw yang paling banyak diunduh dan paling disukai — papan peringkat berdasarkan jumlah unduhan yang dilaporkan clawskills.sh, diperbarui setiap hari.", "ogTitle": "Skill Tren — {siteName}", - "ogDescription": "Papan peringkat langsung untuk skill OpenClaw paling populer.", + "ogDescription": "Papan peringkat harian skill OpenClaw terpopuler.", "title": "Skill Tren", "subtitle": "Temukan skill OpenClaw paling populer yang diperingkat oleh komunitas", "searchPlaceholder": "Cari skill tren…", @@ -274,14 +355,20 @@ "likes": "Suka", "views": "Tayangan", "skill": "Skill", - "count": "{count, plural, other {# skill}}", - "countInCategory": "{count, plural, other {# skill}} di {category}", + "count": "{count, plural, =0 {Tidak ada skill} other {# skill}}", + "countInCategory": "{count, plural, =0 {Tidak ada skill} other {# skill}} di {category}", "emptyTitle": "Tidak ada skill ditemukan", "emptySubtitle": "Coba sesuaikan pencarian atau filter Anda", "byAuthor": "oleh {author}", "showing": "Menampilkan {from, number}–{to, number} dari {total, number}", "prev": "Sebelumnya", - "next": "Berikutnya" + "next": "Berikutnya", + "provenanceNote": "Jumlah unduhan dan bintang berasal dari katalog hulu — clawskills.sh dan GitHub — dan diperbarui tiap malam; angka itu tidak mengukur instalasi lewat TakoAPI. Suka dan tayangan dihitung di sini.", + "search": "Cari", + "sortLabel": "Urutkan berdasarkan", + "rank": "Peringkat", + "goToPage": "Ke halaman {page}", + "paginationLabel": "Halaman papan peringkat" }, "Admin": { "accessDenied": "Akses Ditolak", @@ -335,8 +422,8 @@ "actionReject": "Tolak", "feature": "Jadikan Unggulan", "unfeature": "Batalkan Unggulan", - "scenariosEditorHint": "场景 Skenario — 点击切换 / klik untuk menetapkan", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Skenario — klik untuk menetapkan atau menghapus", + "scenarioOption": "{name}", "selectedCount": "{count, number} dipilih", "rejectReasonPrompt": "Alasan penolakan (opsional):", "deleteAgentConfirm": "Hapus agen ini secara permanen?", @@ -436,7 +523,46 @@ "targetSkill": "skill", "targetAgent": "agen", "targetCategory": "kategori", - "targetUser": "pengguna" + "targetUser": "pengguna", + "adminNavLabel": "Bagian admin", + "retry": "Coba lagi", + "close": "Tutup", + "prevPage": "Halaman sebelumnya", + "nextPage": "Halaman berikutnya", + "sectionAgents": "Agen", + "sectionSkills": "Skill & komunitas", + "statTotalAgents": "Total Agen", + "statPendingAgents": "Agen Menunggu", + "statHostedAgents": "Hosted", + "statProjectAgents": "Proyek", + "pendingAgentsTitle": "Agen yang menunggu tinjauan", + "noPendingAgents": "Tidak ada agen yang menunggu tinjauan.", + "kindHosted": "Hosted", + "kindProject": "Proyek", + "filterByStatus": "Filter berdasarkan status", + "filterDisabled": "Dinonaktifkan", + "actionDisable": "Nonaktifkan", + "actionEnable": "Aktifkan lagi", + "disableAgentTitle": "Nonaktifkan Agen", + "disableAgentConfirm": "{name} akan disembunyikan dari katalog publik sampai Anda mengaktifkannya lagi. Tidak ada yang dihapus.", + "deleteAgentTitle": "Hapus Agen", + "deleteAgentConfirmNamed": "Anda yakin ingin menghapus {name}? Tindakan ini tidak dapat dibatalkan.", + "rejectAgentTitle": "Tolak Agen", + "rejectReasonLabel": "Alasan (opsional, ditampilkan ke penerbit)", + "rejectReasonPlaceholder": "mis. endpoint mengembalikan 500 pada tugas contoh", + "healthOk": "Sehat", + "healthDegraded": "Menurun", + "healthDown": "Mati", + "healthUnknown": "Belum diperiksa", + "healthCheckedAt": "Pemeriksaan kesehatan terakhir: {date}", + "healthNeverChecked": "Belum ada catatan pemeriksaan kesehatan", + "batchDeleteTitle": "Hapus Skill", + "selectAllRows": "Pilih semua baris", + "selectRow": "Pilih {name}", + "editSkillNamed": "Edit skill {name}", + "deleteSkillNamed": "Hapus skill {name}", + "editCategoryNamed": "Edit kategori {name}", + "deleteCategoryNamed": "Hapus kategori {name}" }, "Auth": { "signInTitle": "Masuk ke TakoAPI", @@ -462,12 +588,26 @@ "noAccount": "Belum punya akun?", "signUpLink": "Daftar", "haveAccount": "Sudah punya akun?", - "signInLink": "Masuk" + "signInLink": "Masuk", + "forgotPassword": "Lupa kata sandi?", + "forgotTitle": "Atur ulang kata sandi Anda", + "forgotSubtitle": "Kami akan mengirim tautan untuk memilih kata sandi baru", + "sendResetLink": "Kirim tautan atur ulang", + "sendingResetLink": "Mengirim...", + "resetLinkSent": "Jika alamat itu memang punya akun, kami sudah mengirim tautan atur ulang kata sandi. Cek kotak masuk Anda.", + "backToSignIn": "Kembali ke halaman masuk", + "resetTitle": "Pilih kata sandi baru", + "resetSubtitle": "Masukkan kata sandi baru untuk akun Anda", + "newPasswordHint": "Kata sandi baru (min. 8 karakter)", + "updatePassword": "Perbarui kata sandi", + "updatingPassword": "Memperbarui...", + "passwordUpdated": "Kata sandi Anda sudah diperbarui. Mengarahkan ke halaman masuk...", + "resetLinkInvalid": "Tautan atur ulang kata sandi ini tidak valid atau sudah kedaluwarsa.", + "requestNewLink": "Minta tautan baru" }, "Dashboard": { "title": "Dasbor Developer", "signInPrompt": "Masuk untuk mengelola kunci API dan melihat penggunaan.", - "signIn": "Masuk", "description": "Kelola kunci API dan pantau penggunaan gateway. Satu kunci, satu tagihan, agen apa pun.", "apiKeys": "Kunci API", "createKey": "Buat kunci", @@ -497,7 +637,6 @@ "billing": "Kredit & Tagihan", "creditBalance": "Saldo kredit", "totalSpend": "Total pengeluaran", - "topUpComingSoon": "Untuk saat ini kredit diisi oleh platform; isi ulang mandiri via PayPal segera hadir.", "topUpLabel": "Tambah kredit", "topUpButton": "Isi ulang dengan PayPal", "topUpRedirecting": "Mengalihkan…", @@ -513,35 +652,65 @@ "ledgerDebit": "Biaya panggilan", "ledgerPayout": "Pembayaran", "ledgerRefund": "Pengembalian dana", - "ledgerAdjust": "Penyesuaian" + "ledgerAdjust": "Penyesuaian", + "onboardingTitle": "Jalankan panggilan pertama Anda", + "onboardingSubtitle": "Tiga langkah sampai ada agen yang menjawab Anda.", + "stepKeyTitle": "Buat kunci API", + "stepKeyBody": "Satu kunci memanggil semua agen di TakoAPI. Rahasianya ditampilkan sekali saja, tepat setelah Anda membuatnya.", + "stepCallTitle": "Lakukan panggilan pertama", + "stepCallBody": "Salin salah satu contoh di bawah, set TAKO_KEY ke kunci Anda, lalu jalankan. Panggilannya muncul di bagian Penggunaan dalam hitungan detik.", + "stepInstallTitle": "Pasang ke coding agent Anda", + "stepInstallBody": "Ajari Claude Code, Codex, atau OpenCode untuk menemukan dan memanggil agen lewat TakoAPI.", + "stepInstallLink": "Buka panduan instalasi", + "stepOptional": "Opsional", + "stepDone": "Selesai", + "stepTodo": "Belum selesai", + "keyNameLabel": "Nama kunci", + "keyNamePlaceholder": "Kunci gateway", + "createKeyError": "Kunci gagal dibuat. Silakan coba lagi.", + "revokeNamed": "Cabut {name}", + "quickstartHint": "Set TAKO_KEY ke salah satu kunci di atas, lalu panggil agen mana pun lewat slug-nya.", + "creditRule": "Agen gratis tidak dikenai biaya; agen berbayar memotong kredit prabayar Anda, dan panggilan ditolak dengan HTTP 402 begitu saldo akan jatuh di bawah nol.", + "topUpUnavailable": "Isi ulang mandiri belum tersedia — untuk saat ini kredit ditambahkan oleh platform. Saldo di atas tetap menampilkan data terkini.", + "dismiss": "Tutup", + "loadError": "Sebagian data dasbor gagal dimuat.", + "retry": "Coba lagi", + "recentCallsCaption": "Panggilan gateway terbaru Anda", + "chartTitle": "Panggilan per hari, {days} hari terakhir", + "chartTotal": "total {count, number}", + "chartAria": "Diagram batang panggilan gateway per hari selama {days} hari terakhir, total {count, number} panggilan.", + "chartDay": "{date}: {calls, number} panggilan, {errors, number} gagal" }, "Profile": { - "title": "Profil Saya", - "signInPrompt": "Silakan masuk untuk melihat profil Anda.", - "signIn": "Masuk", - "submissions": "Kiriman", + "title": "Listing saya", + "signInPrompt": "Masuk untuk melihat agen dan skill yang sudah Anda terbitkan.", "likes": "Suka", - "views": "Tayangan", - "statValue": "{count, number}", - "mySubmissions": "Kiriman Saya", - "submitNew": "Kirim baru", + "views": "tayangan", "tab_all": "Semua", - "tab_approved": "Disetujui", - "tab_pending": "Menunggu", - "tab_rejected": "Ditolak", - "tabCount": "{count, number}", - "emptyTab": "Tidak ada kiriman di tab ini", + "emptyTab": "Belum ada skill di sini.", "submitFirst": "Kirim skill pertama Anda", - "statusApproved": "Disetujui", - "statusPending": "Menunggu tinjauan", + "statusApproved": "Tayang", + "statusPending": "Sedang ditinjau", "statusRejected": "Ditolak", - "reviewerNote": "Catatan peninjau:" + "reviewerNote": "Catatan peninjau:", + "publishAgent": "Terbitkan agen", + "submitSkill": "Kirim skill", + "statAgents": "Agen", + "statCalls": "Panggilan gateway", + "statSkills": "Skill", + "tabAgents": "Agen", + "tabSkills": "Skill", + "statusDisabled": "Dinonaktifkan", + "kindHosted": "Dapat dipanggil", + "kindProject": "Open-source", + "noAgents": "Belum ada agen di sini.", + "publishFirstAgent": "Terbitkan agen pertama Anda", + "myListings": "Listing saya" }, "Submit": { "title": "Kirim Skill", "subtitle": "Bagikan skill OpenClaw Anda dengan komunitas", "signInPrompt": "Silakan masuk untuk mengirim skill.", - "signIn": "Masuk", "skillSource": "Sumber Skill", "githubUrl": "URL GitHub", "clawSkillsUrl": "URL ClawSkills.sh", @@ -563,16 +732,23 @@ "whatItDoes": "Apa yang Dilakukan Skill Ini", "whatItDoesPlaceholder": "Penjelasan rinci tentang apa yang dilakukan skill ini, fitur, dan kemampuannya...", "exampleWorkflow": "Contoh Alur Kerja", - "exampleWorkflowHelper": "- Langkah INPUT / AGEN / OUTPUT", - "exampleWorkflowPlaceholder": "INPUT\nPengguna meminta: Lakukan sesuatu yang berguna\n\nAGEN\n1\nJalankan `my-skill action` untuk menjalankan tugas\n2\nVerifikasi hasilnya\n\nOUTPUT\nTugas berhasil diselesaikan", + "exampleWorkflowHelper": "- Langkah INPUT / AGENT / OUTPUT", + "exampleWorkflowPlaceholder": "INPUT\nPengguna meminta: Lakukan sesuatu yang berguna\n\nAGENT\n1\nJalankan `my-skill action` untuk menjalankan tugas\n2\nVerifikasi hasilnya\n\nOUTPUT\nTugas berhasil diselesaikan", "requirements": "Persyaratan", "requirementsPlaceholder": "- Node.js 18+\n- Kunci API dari layanan X\n- dll.", "submitSkill": "Kirim Skill", "submitting": "Mengirim...", "enterUrlFirst": "Silakan masukkan URL terlebih dahulu", "autoFillFailed": "Pengisian otomatis gagal", - "submitFailed": "Gagal mengirim", - "somethingWentWrong": "Terjadi kesalahan" + "somethingWentWrong": "Terjadi kesalahan", + "successTitle": "Terkirim 🎉", + "pendingReview": "{name} kini menunggu tinjauan.", + "nextStepsTitle": "Apa yang terjadi berikutnya", + "nextStepReview": "Peninjau memeriksa repositori dan deskripsinya.", + "nextStepEmail": "Kami akan mengirim email setelah selesai ditinjau.", + "nextStepProfile": "Halaman listing Anda selalu menampilkan status terkini dan catatan peninjau.", + "viewMyListings": "Lihat listing saya", + "backToSkills": "← Kembali ke skill" }, "SubmitAgent": { "title": "Terbitkan Agen", @@ -611,7 +787,24 @@ "successTitle": "Terkirim 🎉", "pendingReview": "{name} kini menunggu tinjauan.", "successDetail": "Kami akan memverifikasi AgentCard dan menerbitkannya ke marketplace setelah disetujui.", - "backToMarketplace": "← Kembali ke marketplace" + "backToMarketplace": "← Kembali ke marketplace", + "checkCard": "Periksa kartu", + "checking": "Memeriksa…", + "enterCardUrlFirst": "Masukkan URL AgentCard terlebih dahulu.", + "previewTitle": "Beginilah nanti kami menampilkannya", + "previewNoDescription": "Kartu ini tidak punya deskripsi.", + "previewEndpoint": "Endpoint", + "previewCapabilities": "Kapabilitas", + "previewSkillCount": "{count, plural, =0 {Tidak ada kapabilitas yang dicantumkan} other {# kapabilitas}}", + "previewSupports": "Mendukung", + "previewStreaming": "Streaming", + "previewPush": "Notifikasi push", + "previewHint": "Belum ada yang disimpan — terbitkan di bawah untuk mengirimnya ke tinjauan.", + "nextStepsTitle": "Apa yang terjadi berikutnya", + "nextStepReview": "Peninjau memeriksa AgentCard dan memastikan endpoint Anda merespons.", + "nextStepEmail": "Kami akan mengirim email setelah selesai ditinjau.", + "nextStepProfile": "Halaman listing Anda selalu menampilkan status terkini dan catatan peninjau.", + "viewMyListings": "Lihat listing saya" }, "AgentEngagement": { "yourRating": "Penilaian Anda", @@ -619,44 +812,61 @@ "noRatings": "Belum ada penilaian", "bookmark": "Simpan", "bookmarked": "Tersimpan", - "signInToRate": "Masuk untuk menilai atau menyimpan agen ini" + "signInToRate": "Masuk untuk menilai atau menyimpan agen ini", + "rateStars": "{count, plural, other {Beri # bintang}}" }, "Bookmarks": { "title": "Agen tersimpan", "subtitle": "Agen yang Anda simpan.", "empty": "Belum ada yang tersimpan.", "browseAgents": "Jelajahi agen", - "signInPrompt": "Masuk untuk melihat yang tersimpan.", - "signIn": "Masuk" + "signInPrompt": "Masuk untuk melihat agen tersimpan Anda." }, "ScenarioPage": { "metaTitle": "Agen AI untuk {scenario}", - "metaDescription": "Temukan dan panggil agen AI untuk {scenario} melalui satu API terpadu — dijelaskan oleh A2A AgentCard terbuka di TakoAPI.", - "heroSubtitle": "Agen AI untuk {scenario}, dapat dipanggil melalui satu API.", + "metaDescription": "Temukan dan panggil agen AI untuk {scenario} melalui satu API terpadu — dideskripsikan oleh AgentCard A2A terbuka di TakoAPI.", + "heroSubtitle": "Agen AI untuk {scenario}, dapat dipanggil melalui satu API terpadu.", "empty": "Belum ada agen untuk skenario ini.", "browseAll": "Jelajahi semua agen", "back": "← Semua skenario", "breadcrumbScenarios": "Skenario", "indexTitle": "Jelajahi agen AI berdasarkan skenario", - "indexDescription": "Temukan agen AI berdasarkan kegunaannya — frontend, investasi, marketing, dukungan, dan lainnya — semua dapat dipanggil melalui satu API.", + "indexDescription": "Temukan agen AI berdasarkan kegunaannya — frontend, investasi, pemasaran, dukungan, dan lainnya — semua dapat dipanggil melalui satu API.", "indexHeroTitle": "Jelajahi berdasarkan skenario", - "indexHeroSubtitle": "Temukan agen AI berdasarkan pekerjaan yang perlu diselesaikan." + "indexHeroSubtitle": "Temukan agen AI berdasarkan pekerjaan yang perlu diselesaikan.", + "indexPopularHeading": "Sudah lengkap", + "indexPopularHint": "Skenario dengan katalog agen yang cukup dalam untuk dibandingkan.", + "indexSparseHeading": "Baru dimulai", + "indexSparseHint": "Baru ada segelintir agen — kesempatan mudah untuk jadi yang pertama.", + "indexSparseCta": "Terbitkan agen di salah satunya →", + "metaTitlePaged": "Agen AI untuk {scenario} — halaman {page}", + "resultsLabel": "Agen {scenario}", + "paginationLabel": "Paginasi", + "showingRange": "Menampilkan {from}–{to} dari {total}", + "prev": "← Sebelumnya", + "next": "Berikutnya →", + "goToPage": "Ke halaman {page}", + "ctaTitle": "Tahu agen {scenario}?", + "ctaBody": "Skenario ini masih sepi. Terbitkan agen dan agen itu akan muncul di sini untuk semua orang yang menjelajahi {scenario}.", + "ctaButton": "Terbitkan agen", + "relatedHeading": "Jelajahi skenario lain" }, "Badge": { "snippetTitle": "Tambahkan lencana", "copy": "Salin", "copied": "Tersalin", - "snippetHint": "Tempel ke README Anda untuk menautkan ke halaman TakoAPI Anda.", + "snippetHint": "Tempel ke README Anda untuk menautkan kembali ke halaman TakoAPI Anda.", "pageTitle": "Lencana README TakoAPI", "pageIntro": "Tunjukkan bahwa proyek Anda terdaftar di TakoAPI — lencana kecil yang menautkan ke halaman Anda dan menampilkan jumlah bintang terkini.", "example": "Contoh", "step1Title": "Temukan halaman Anda", "step1": "Cari proyek Anda di direktori agen.", - "step2Title": "Salin lencana", + "step2Title": "Salin lencananya", "step2": "Di halaman Anda, salin Markdown dari kotak lencana.", "step3Title": "Tempel ke README", - "step3": "Tambahkan di dekat atas — pengunjung dan mesin pencari mendapat tautan ke halaman TakoAPI Anda.", - "findListing": "Temukan halaman Anda" + "step3": "Tambahkan di dekat bagian atas — pengunjung dan mesin pencari mendapat tautan ke halaman TakoAPI Anda.", + "findListing": "Temukan halaman Anda", + "exampleAlt": "Lencana TakoAPI" }, "Blog": { "title": "Blog", @@ -666,5 +876,149 @@ "minRead": "{min} mnt baca", "published": "Diterbitkan", "faqTitle": "FAQ" + }, + "NotFound": { + "title": "Kami tidak menemukan halaman itu", + "description": "Tautannya mungkin sudah tidak berlaku, atau listing-nya sudah dihapus. Coba direktorinya, atau cari lewat header.", + "home": "Beranda", + "agents": "Jelajahi agen", + "skills": "Jelajahi skill" + }, + "ErrorPage": { + "title": "Terjadi kesalahan", + "description": "Halaman ini gagal dimuat. Mencoba lagi biasanya berhasil; kalau tidak, referensi di bawah membantu kami menemukannya di log.", + "reference": "Referensi: {id}", + "retry": "Coba lagi", + "home": "Ke beranda" + }, + "Consent": { + "title": "Analitik", + "description": "Kami memakai Google Analytics untuk melihat halaman mana yang berguna bagi orang. Tidak ada yang dimuat dan tidak ada cookie yang dipasang kecuali Anda menerimanya.", + "accept": "Terima", + "decline": "Tolak" + }, + "Docs": { + "metaTitle": "Dokumentasi API", + "title": "Referensi API TakoAPI", + "description": "Dari kunci API sampai panggilan agen yang berjalan: autentikasi, endpoint penemuan, gateway agen terpadu, MCP server yang di-hosting, kode error, batas laju, dan penagihan.", + "heroBadge": "Dokumentasi developer", + "heroTitle": "Referensi API TakoAPI", + "heroSubtitle": "Satu kunci, semua agen. Arahkan klien apa pun yang kompatibel dengan OpenAI ke TakoAPI dan panggil agen lewat slug-nya — atau jelajahi seluruh katalog tanpa kunci sama sekali.", + "heroKeyCta": "Buat kunci API", + "heroBrowseCta": "Jelajahi agen", + "tocTitle": "Di halaman ini", + "tocLabel": "Daftar isi", + "quickstartTitle": "Mulai Cepat", + "quickstartIntro": "Tiga langkah dari nol sampai dapat balasan. Setiap request di halaman ini adalah panggilan nyata ke API yang live.", + "quickstartStep1Title": "Buat kunci", + "quickstartStep1": "Masuk dan buat kunci API di dasbor Anda. Rahasianya ditampilkan sekali saja, saat dibuat.", + "quickstartStep2Title": "Ekspor kuncinya", + "quickstartStep2": "Setiap contoh di halaman ini membaca kunci dari TAKO_KEY.", + "quickstartStep3Title": "Panggil agen", + "quickstartStep3": "Set model — atau slug di URL — ke agen hosted mana pun dari katalog.", + "quickstartSamplesLabel": "Contoh panggilan untuk mulai cepat", + "quickstartNote": "Slug pada contoh ini hanya placeholder, bukan agen sungguhan. Salin slug asli dari halaman agen mana pun; hanya agen yang terdaftar sebagai hosted yang dapat dipanggil.", + "authenticationTitle": "Autentikasi", + "authenticationIntro": "Panggilan gateway diautentikasi dengan bearer token. Endpoint penemuan bersifat anonim dan sama sekali tidak butuh kunci.", + "authHeaderNote": "x-api-key diterima sebagai alternatif header Authorization. Kunci selalu diawali tako_live_.", + "authWhereTitle": "Dari mana kunci berasal", + "authWhere": "Buat satu kunci per klien atau per environment di dasbor Anda, dan cabut kunci yang tidak lagi Anda pakai.", + "authOnceTitle": "Rahasianya ditampilkan sekali saja", + "authOnce": "Yang disimpan hanya hash SHA-256 dari kunci dan prefiks tampilan yang pendek, jadi rahasia yang hilang tidak bisa dipulihkan — buat penggantinya lalu cabut kunci lama.", + "authRevokeTitle": "Pencabutan berlaku seketika", + "authRevoke": "Setiap panggilan memeriksa ulang kunci ke database dan tidak ada yang di-cache, jadi kunci yang dicabut langsung ditolak pada request berikutnya. Riwayat pemakaian sebelumnya tetap tersimpan.", + "discoveryTitle": "Penemuan", + "discoveryIntro": "Empat endpoint publik yang bisa di-cache dan tidak butuh kunci. Hanya agen dan skill yang sudah disetujui yang dikembalikan.", + "endpointRegistrySummary": "Seluruh katalog agen — Markdown untuk model, JSON untuk kode.", + "endpointRegistryNote": "Tanpa format=json, responsnya berupa Markdown (text/markdown), dipecah menjadi tabel agen hosted dan tabel proyek open-source, dengan ukuran yang pas untuk langsung masuk ke konteks model — itulah yang dibaca skill TakoAPI yang terpasang. Kedua format dibatasi oleh limit, jadi periksa truncated dan angka total sebelum menganggap datanya sudah lengkap. Parameter teks bebas dipotong sampai 100 karakter.", + "paramRegistryFormat": "md (default) atau json.", + "paramRegistryQ": "Pencocokan teks bebas pada nama dan deskripsi agen.", + "paramRegistryCategory": "Slug kategori.", + "paramRegistryProtocol": "Hanya mengembalikan agen yang mencantumkan protokol ini.", + "paramRegistryKind": "Agen HOSTED dapat dipanggil lewat gateway; entri PROJECT adalah repositori open-source yang didaftarkan hanya untuk penemuan.", + "paramRegistrySort": "Urutan. Default-nya unggulan dulu, lalu paling sering dipanggil, lalu terbaru — dan paling banyak dibintangi untuk proyek.", + "paramRegistryLimit": "Total entri yang dikembalikan. Agen hosted diisi lebih dulu, baru proyek.", + "endpointAgentDetailSummary": "Detail publik lengkap untuk satu agen, termasuk skill A2A yang dicantumkannya.", + "endpointAgentDetailNote": "Hanya agen yang disetujui yang bisa diakses; selain itu 404. Field moderasi — catatan tinjauan, id internal, dan avatar penerbit — tidak pernah menjadi bagian dari bentuk ini. Di-cache di edge selama 60 detik.", + "paramAgentDetailSlug": "Segmen path. Slug agen, seperti yang dikembalikan registri.", + "endpointSkillsDirectorySummary": "Katalog skill OpenClaw, Markdown secara default.", + "endpointSkillsDirectoryNote": "Markdown lebih dulu, sama seperti registri. install bernilai null untuk skill yang belum punya perintah instalasi terpublikasi — gunakan tautan github sebagai gantinya. downloads adalah angka pihak ketiga yang di-scrape dari halaman skill itu sendiri, bukan jumlah instalasi lewat TakoAPI.", + "paramSkillsDirFormat": "md (default) atau json.", + "paramSkillsDirQ": "Pencocokan teks bebas pada nama dan deskripsi skill.", + "paramSkillsDirCategory": "Slug kategori.", + "paramSkillsDirPage": "Nomor halaman, dimulai dari 1.", + "paramSkillsDirLimit": "Jumlah hasil per halaman.", + "endpointSkillsSearchSummary": "Pencarian teks lengkap di seluruh katalog skill, hanya JSON.", + "endpointSkillsSearchNote": "q wajib diisi: tanpa itu endpoint mengembalikan daftar kosong, bukan seluruh katalog. Setiap hasil adalah baris katalog lengkap tanpa readme dan reviewNote; contoh di atas dipersingkat.", + "paramSkillsSearchQ": "Kata pencarian. Mencocokkan nama, deskripsi, dan penulis skill.", + "paramSkillsSearchCategory": "Slug kategori.", + "paramSkillsSearchPage": "Nomor halaman, dimulai dari 1.", + "paramSkillsSearchLimit": "Jumlah hasil per halaman.", + "gatewayTitle": "Gateway", + "gatewayIntro": "Tiga cara memanggil agen yang sama. Ketiganya butuh kunci, ketiganya ditagih sama, dan ketiganya menerima slug agen di tempat nama model biasanya ditulis. Hanya agen berjenis HOSTED yang dapat dipanggil.", + "endpointChatCompletionsSummary": "Kompatibel dengan OpenAI. Arahkan SDK OpenAI mana pun ke /v1 dan set model ke slug agen.", + "endpointChatCompletionsNote": "Seluruh percakapan diteruskan: satu bagian teks A2A per giliran, berurutan, dengan role OpenAI menempel pada tiap bagian. usage selalu ada tetapi selalu nol — tidak ada yang menghitung token di gateway ini, dan agen tidak harus berupa LLM. Set stream: true untuk bentuk SSE di atas, dan stream_options.include_usage untuk ikut menerima potongan usage di akhir. Panggilan non-streaming timeout setelah 30 detik, stream setelah 120.", + "endpointA2AMessageSummary": "Passthrough A2A native. Mengembalikan respons JSON-RPC milik agen itu sendiri, tanpa diubah.", + "endpointA2AMessageNote": "Diteruskan sebagai panggilan A2A message/send. Kirim bentuk singkat teks, atau pesan A2A lengkap — dengan parts, taskId, dan contextId — untuk melanjutkan tugas multi-giliran atau meneruskan bagian file dan data. Kode status dan body dari agen diteruskan apa adanya, dan request tanpa konten ditolak dengan 400, bukan ditagih. Timeout setelah 30 detik.", + "endpointA2AStreamSummary": "Panggilan yang sama lewat Server-Sent Events, untuk agen yang mendukung streaming.", + "endpointA2AStreamNote": "Meneruskan frame A2A message/stream dari agen apa adanya sebagai text/event-stream. Endpoint ini hanya menerima bentuk singkat teks. Koneksi diputus setelah 120 detik, dan panggilan baru ditagih setelah stream-nya selesai — lihat Batas laju & penagihan.", + "labelQueryParams": "Parameter", + "labelRequest": "Body request", + "labelExample": "Contoh", + "labelResponse": "Respons", + "labelStreaming": "Respons dengan stream: true", + "labelA2AMessageForm": "Bentuk pesan A2A lengkap", + "labelAuthRequired": "Butuh kunci", + "labelNoAuth": "Tanpa kunci", + "labelBillable": "Berbayar", + "labelFree": "Gratis", + "labelParamName": "Nama", + "labelParamType": "Tipe", + "labelParamDesc": "Deskripsi", + "paramsTableCaption": "Parameter untuk {path}", + "mcpTitle": "MCP server", + "mcpIntro": "TakoAPI menjalankan MCP (Model Context Protocol) server yang di-hosting dan stateless lewat Streamable HTTP di {url}. Daftarkan sekali dan coding agent Anda bisa mencari serta memanggil katalog sebagai tool native.", + "mcpRegisterTitle": "Daftarkan", + "mcpRegisterLabel": "Perintah pendaftaran MCP", + "mcpAuthNote": "Tiga tool baca bekerja secara anonim. invoke_agent memakai kredit, jadi butuh kunci Anda: kirim sebagai header Authorization (Claude Code), sebagai bearer_token_env_var (Codex), atau di dalam blok headers (OpenCode).", + "mcpToolsTitle": "Tool", + "mcpToolsTableCaption": "Tool yang disediakan MCP server TakoAPI", + "mcpToolColumn": "Tool", + "mcpToolAccessColumn": "Akses", + "mcpToolWhatColumn": "Fungsinya", + "mcpToolKeyRequired": "Butuh kunci", + "mcpToolAnonymous": "Anonim", + "mcpToolSearchAgents": "Cari di registri berdasarkan kata kunci, kategori, atau protokol. Mengembalikan slug, protokol, dan harga.", + "mcpToolGetAgent": "Semua yang diketahui tentang satu agen berdasarkan slug — kapabilitas, protokol, endpoint, harga, dan skill yang dicantumkan.", + "mcpToolSearchSkills": "Cari di katalog skill OpenClaw. Tanpa kueri, hasilnya adalah skill teratas hasil kurasi.", + "mcpToolInvokeAgent": "Panggil agen lewat gateway dan kembalikan balasannya. Butuh kunci dan memakai kredit.", + "mcpRateNote": "Endpoint MCP dibatasi 120 request per menit per alamat IP, terpisah dari jatah gateway milik kunci Anda.", + "mcpInstallNote": "Lebih suka file skill daripada server? Installer mengajari Claude Code, Codex, dan OpenCode untuk memanggil endpoint ini lewat HTTP biasa.", + "errorsTitle": "Error", + "errorsIntro": "Setiap kegagalan gateway berupa JSON. Berikut status yang dikembalikan gateway itu sendiri.", + "errorsTableCaption": "Status error yang dikembalikan gateway", + "errorStatusColumn": "Status", + "errorCodeColumn": "Kode", + "errorWhenColumn": "Kapan", + "errorBodyColumn": "Contoh body", + "error400When": "Slug menunjuk proyek open-source yang tidak punya endpoint untuk dipanggil, body request gagal validasi, atau semua giliran pesan kosong.", + "error401When": "Kunci tidak ada, salah format, tidak dikenal, atau sudah dicabut.", + "error402When": "Agen berbayar dan menagih panggilan ini akan membuat saldo Anda jatuh di bawah nol. Diperiksa sebelum agen dihubungi, jadi tidak ada yang terpakai.", + "error404When": "Tidak ada agen disetujui dengan slug tersebut.", + "error429When": "Kunci melewati jatahnya dalam 60 detik terakhir. Retry-After memberi jumlah detik sampai jendelanya direset.", + "error502When": "Agen tidak menjawab, timeout, tidak mengirim stream, atau melaporkan error JSON-RPC lewat HTTP 200. Tidak pernah ditagih.", + "errorsOpenAiNote": "Di /v1/chat/completions kegagalan yang sama datang dalam amplop milik OpenAI — dengan type, param, dan code yang selalu ada — sehingga SDK OpenAI memunculkan tipe exception yang biasa:", + "errorsPassthroughNote": "Status yang dikembalikan agen itu sendiri diteruskan tanpa diubah oleh endpoint A2A message, jadi 4xx di sana bisa jadi milik agen, bukan milik kami.", + "limitsTitle": "Batas laju & penagihan", + "limitsRateTitle": "Batas laju", + "limitsRate": "Gateway mengizinkan 120 request per 60 detik per kunci API. Jendelanya dibagi ke semua instance server dan dikunci pada kuncinya sendiri, bukan pada alamat IP Anda, jadi batas ini tidak melonggar saat beban tinggi maupun mengetat di balik NAT bersama.", + "limitsRateOverride": "Sebuah kunci bisa punya jatahnya sendiri, yang menggantikan default untuk kunci itu saja. Jika melewati batas, Anda mendapat 429 dengan header Retry-After, dalam detik.", + "limitsBillingTitle": "Penagihan", + "limitsBillingFree": "Agen berharga FREE tidak dikenai biaya — begitu pula agen yang harga satuannya kosong atau nol. Gateway tidak pernah mengarang tagihan.", + "limitsBillingPriced": "Agen berbayar memotong kredit prabayar Anda sebesar harga satuannya, sekali per panggilan. Tidak ada perhitungan per token, jadi satu panggilan tetap satu unit berapa pun lamanya.", + "limitsBillingPreflight": "Saldo Anda diperiksa sebelum agen dihubungi. Jika tagihannya akan membuat saldo jatuh di bawah nol, panggilan ditolak dengan 402 dan agen tidak pernah dipanggil — tidak ada saldo minus.", + "limitsBillingFailures": "Panggilan yang gagal tidak ditagih. 4xx atau 5xx dari agen, timeout, endpoint yang tak terjangkau, atau error JSON-RPC semuanya dicatat tanpa menimbulkan biaya.", + "limitsBillingStreams": "Stream hanya ditagih ketika selesai. Stream yang dibatalkan, timeout, gagal di tengah jalan, atau Anda putuskan lebih awal akan dicatat dan tidak ditagih.", + "limitsBillingLedger": "Setiap tagihan menulis entri ledger yang tak dapat diubah terhadap invokasi penyebabnya. Saldo, ledger, dan penggunaan per panggilan semuanya ada di dasbor Anda." } } diff --git a/messages/it.json b/messages/it.json index f30dc9fc7..ea9655a70 100644 --- a/messages/it.json +++ b/messages/it.json @@ -7,60 +7,78 @@ "label": "Lingua" }, "Header": { - "searchPlaceholder": "Cerca agenti...", "agents": "Agenti", "skills": "Skill", - "trending": "Di tendenza", - "publish": "Pubblica", - "publishAgent": "Pubblica agente", + "publishAgent": "Pubblica un agente", "account": "Account", - "mySkills": "Le mie skill", - "myBookmarks": "Salvati", - "apiUsage": "API e utilizzo", + "myBookmarks": "Agenti salvati", + "apiUsage": "Chiavi API e utilizzo", "adminPanel": "Pannello admin", "signIn": "Accedi", - "signOut": "Esci" + "signOut": "Esci", + "mainNav": "Principale", + "skipToContent": "Vai al contenuto", + "openMenu": "Apri il menu", + "closeMenu": "Chiudi il menu", + "projects": "Progetti", + "install": "Installa", + "getApiKey": "Ottieni una chiave API", + "search": "Cerca", + "searchScope": "Cerca in", + "scopeAgents": "Agenti", + "scopeProjects": "Progetti", + "scopeSkills": "Skill", + "searchAgentsPlaceholder": "Cerca agenti per quello che fanno…", + "searchProjectsPlaceholder": "Cerca progetti di agenti open-source…", + "searchSkillsPlaceholder": "Cerca skill per il tuo agente di coding…", + "myListings": "Le mie schede", + "docs": "Documentazione" }, "Footer": { - "tagline": "Un'unica API per accedere a tutti gli agenti — più migliaia di skill OpenClaw per il tuo agente di coding.", + "tagline": "Un'unica API per accedere a tutti gli agenti: scopri, invoca e misura qualsiasi agente da un unico endpoint.", "agentRegistry": "Registro degli agenti:", "platform": "Piattaforma", "resources": "Risorse", - "installInAgent": "Installa nel tuo agente", + "installInAgent": "Installa nel tuo agente di coding", "browseAgents": "Esplora gli agenti", "publishAnAgent": "Pubblica un agente", - "developerDashboard": "Dashboard per sviluppatori", + "developerDashboard": "Chiavi API e utilizzo", "browseSkills": "Esplora le skill", - "awesomeList": "Awesome List", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Realizzato per la community OpenClaw.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. Licenza MIT.", + "blog": "Blog", + "badge": "Badge README", + "scenarios": "Esplora per scenario", + "a2aSpec": "Protocollo A2A", + "github": "GitHub", + "docs": "Documentazione API" }, "Home": { "heroTitle": "Un'unica API per accedere a tutti gli agenti", - "heroSubtitle": "Scopri e invoca {agents, number} agenti IA tramite un'unica API unificata — più {skills, number} skill per il tuo agente di coding.", + "heroSubtitle": "Scopri gli agenti IA, poi invocali tutti da un unico endpoint con una sola chiave. Nativo A2A, compatibile con OpenAI, a consumo per chiamata.", "browseAgents": "Esplora gli agenti", - "publishAnAgent": "Pubblica un agente", - "agentRegistry": "Registro degli agenti:", - "browseByScenario": "Esplora per scenario", - "allAgents": "Tutti gli agenti", + "browseByScenario": "Esplora per quello che ti serve fare", "featuredAgents": "Agenti in evidenza", "browseAll": "Esplora tutti", "popularProjects": "Progetti open-source popolari", "selfHostable": "{count, number} self-hostable", - "skillTitle": "Skill TakoAPI per OpenClaw", - "official": "Ufficiale", - "skillDescription": "Cerca, installa e gestisci le skill OpenClaw tramite conversazione naturale — basato sul marketplace TakoAPI.", - "viewOnGithub": "Vedi su GitHub", - "askAgent": "oppure chiedi al tuo agente: \"Installa la skill TakoAPI\"", - "usingCodingAgent": "Usi Claude Code, Codex o OpenCode?", - "oneCommandInstall": "Installazione con un comando", - "mustHaveSkills": "Skill imperdibili", - "viewRankings": "Vedi le classifiche", - "categories": "Categorie", - "viewAll": "Vedi tutto", - "moreCategories": "{count, plural, one {+# altra categoria} other {+# altre categorie}}", - "latestSkills": "Skill più recenti" + "categories": "Categorie:", + "moreCategories": "{count, plural, one {+# altra categoria} many {+# di altre categorie} other {+# altre categorie}}", + "getApiKey": "Ottieni una chiave API", + "catalogSummary": "{agents, number} agenti invocabili · {projects, number} progetti open-source · {skills, number} skill per agenti di coding", + "quickstartLabel": "30 secondi alla tua prima chiamata", + "quickstartNote": "Preferisci lavorare dal terminale? Installa TakoAPI nel tuo agente di coding.", + "allScenarios": "Tutti gli scenari", + "callableNow": "{count, number} invocabili tramite il gateway", + "installTitle": "Usa TakoAPI dal tuo agente di coding", + "installDescription": "Un comando insegna a Claude Code, Codex o OpenCode a cercare nella directory e a invocare gli agenti al posto tuo — oppure registra il server MCP hosted.", + "installCta": "Guida all'installazione", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Skill per il tuo agente di coding", + "skillsSubtitle": "{count, number} skill OpenClaw, le più installate e le più recenti.", + "quickstartTabDiscover": "1. Scopri", + "quickstartTabCall": "2. Chiama", + "quickstartTabFromAgent": "3. Dal tuo agente" }, "Scenarios": { "frontend": "Frontend", @@ -85,8 +103,6 @@ "notFound": "Agente non trovato", "backToMarketplace": "← Marketplace degli agenti", "openSourceProject": "Progetto open-source", - "signedCard": "Card firmata", - "verifiedPublisher": "Publisher verificato", "by": "di {name}", "publisherOpenSource": "open-source", "publisherUnknown": "sconosciuto", @@ -101,17 +117,41 @@ "noSkillsHosted": "Questo agente non ha dichiarato skill strutturate nella sua AgentCard.", "inputMode": "in: {mode}", "outputMode": "out: {mode}", - "openSourceProjectHeading": "Progetto open-source", "openSourceProjectNote": "Fai il self-host o esplora il codice. Non invocabile tramite il gateway TakoAPI.", "viewOnGithub": "Vedi su GitHub", - "integration": "Integrazione", "endpoint": "Endpoint", "agentCard": "AgentCard", "homepage": "Homepage", "callThroughTakoApi": "Invocalo tramite TakoAPI", - "gatewayPhase2": "Gateway unificato — l'invocazione arriva nella Fase 2.", "breadcrumbHome": "Home", - "breadcrumbMarketplace": "Marketplace degli agenti" + "breadcrumbMarketplace": "Marketplace degli agenti", + "gatewayHint": "Invocalo con la tua chiave API — creane una nella dashboard.", + "healthOk": "Risponde", + "healthDegraded": "Degradato", + "healthDown": "Non risponde", + "healthCheckedAt": "Ultimo controllo {time}", + "atAGlance": "In sintesi", + "protocols": "Protocolli", + "protocolsNone": "Non dichiarati", + "yes": "Sì", + "no": "No", + "pricing": "Prezzo", + "health": "Stato di salute", + "healthUnknown": "Non ancora verificato", + "totalCalls": "Chiamate totali", + "numberValue": "{count, number}", + "averageRating": "Valutazione media", + "ratingValue": "{rating} / 5 · {count, number} valutazioni", + "notRated": "Non ancora valutato", + "callExamplesLabel": "Esempi di chiamata", + "repository": "Repository", + "language": "Linguaggio", + "license": "Licenza", + "licenseNone": "Non specificata", + "lastPush": "Ultimo push", + "topics": "Topic", + "readmeHeading": "Dal README", + "readmeNote": "Estratto dal README del progetto su GitHub." }, "Agents": { "metaTitle": "Marketplace degli agenti", @@ -140,16 +180,29 @@ "pricingPerCall": "per chiamata", "pricingPerTask": "per task", "pricingPerToken": "Per 1k token", - "count": "{count, plural, =0 {Nessun agente} one {# agente} other {# agenti}}", + "count": "{count, plural, =0 {Nessun agente} one {# agente} many {# di agenti} other {# agenti}}", "emptyTitle": "Nessun agente corrisponde ancora ai tuoi filtri.", "emptyCta": "Pubblica il primo →", "prev": "← Prec", "next": "Succ →", - "pageOf": "Pagina {page} di {total}" + "pageOf": "Pagina {page} di {total}", + "filtersTitle": "Filtri", + "filtersWithCount": "Filtri ({count})", + "activeFiltersLabel": "Filtri attivi:", + "removeFilter": "Rimuovi filtro: {label}", + "clearAll": "Cancella tutto", + "kindLabel": "Tipo", + "scenarioGroup": "Scenario", + "protocolGroup": "Protocollo", + "pricingGroup": "Prezzo", + "categoryGroup": "Categoria", + "searchChip": "Ricerca: {query}", + "resultsLabel": "Risultati", + "paginationLabel": "Paginazione", + "goToPage": "Vai alla pagina {page}", + "showingRange": "Da {from} a {to} di {total}" }, "Components": { - "homeSearchPlaceholder": "Cerca skill per nome, descrizione o autore...", - "homeSearchButton": "Cerca", "agentFree": "Gratis", "agentPerCall": "{price}/chiamata", "agentPerTask": "{price}/task", @@ -161,9 +214,13 @@ "agentBadgeStreaming": "streaming", "agentOpenSource": "open-source", "agentSelfHost": "self-host", - "agentSkills": "{count, plural, one {# skill} other {# skill}}", + "agentSkills": "{count, plural, one {# skill} many {# di skill} other {# skill}}", "skillBy": "di {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} chiamate", + "healthOk": "Risponde", + "healthDegraded": "Degradato", + "healthDown": "Non risponde" }, "Install": { "metaTitle": "Installa in Claude Code, Codex e OpenCode", @@ -196,7 +253,7 @@ "howToUseHeading": "Come usarla", "claudeCodeUse": "Si carica automaticamente — basta chiedere a Claude di trovare un agente su TakoAPI.", "codexUse": "Invocala con $takoapi oppure scegliela dal menu /skills.", - "opencodeUse": "Esegui /takoapi oppure passa la mano a @takoapi.", + "opencodeUse": "Esegui /takoapi '' oppure passa la mano a @takoapi.", "mcpHeading": "Server MCP — strumenti hosted, niente da installare", "claudeCodeMcpHint": "Un comando. Aggiungi --header \"Authorization: Bearer $TAKO_KEY\" per abilitare invoke_agent.", "codexMcpHint": "Aggiungi a ~/.codex/config.toml", @@ -214,7 +271,7 @@ "downloads": "Download", "stars": "Stelle", "views": "Visualizzazioni", - "likes": "{count, plural, one {# like} other {# like}}", + "likes": "{count, plural, one {# like} many {# di like} other {# like}}", "brief": "Sintesi", "description": "Descrizione", "whatThisSkillDoes": "Cosa fa questa skill", @@ -226,12 +283,17 @@ "requirements": "Requisiti", "installation": "Installazione", "clawHubCli": "CLI di ClawHub", - "openClawChat": "OpenClaw Chat (incolla questo nella tua conversazione)", "links": "Link", "github": "GitHub", - "copyGithubUrl": "Copia l'URL di GitHub", "clawHub": "ClawHub", - "skillNotFound": "Skill non trovata" + "codingAgent": "Agente di coding", + "agentPrompt": "Installa la skill da {url}", + "installHintRegistry": "Esegui il comando CLI oppure incolla il prompt in Claude Code, Cursor, Codex o nell'agente di coding che usi.", + "installHintRepo": "Questa skill non è pubblicata sul registro ClawHub. Incolla il prompt nel tuo agente di coding — Claude Code, Cursor, Codex — e installerà la skill direttamente dal repository sorgente.", + "installUnavailable": "Per questa skill non è ancora stata pubblicata una sorgente di installazione.", + "openRepository": "Apri il repository", + "signInToLike": "Accedi per mettere like a questa skill", + "downloadsSource": "Download riportati da clawskills.sh" }, "Skills": { "metaTitle": "Esplora le skill", @@ -257,13 +319,32 @@ "all": "Tutte", "emptyTitle": "Nessuna skill trovata", "emptyHint": "Prova a modificare la ricerca o i filtri", - "pageOf": "Pagina {page} di {total}" + "pageOf": "Pagina {page} di {total}", + "metaTitleSearch": "Skill che corrispondono a \"{query}\"", + "metaTitlePaged": "Esplora le skill — pagina {page}", + "searchInputLabel": "Cerca skill", + "searchPlaceholder": "Cerca skill per nome, descrizione o autore", + "searchButton": "Cerca", + "agentFilter": "Agente", + "agentZed": "Zed", + "sourceFilter": "Origine", + "sourceGithub": "GitHub", + "sourceCommunity": "Community", + "sourceCurated": "Selezionate", + "categoryFilter": "Categoria", + "sortLabel": "Ordina", + "sortRelevance": "Più pertinenti", + "clearFilters": "Cancella i filtri", + "paginationLabel": "Pagine dell'elenco skill", + "goToPage": "Vai alla pagina {page}", + "prev": "Precedente", + "next": "Successiva" }, "Trending": { "metaTitle": "Skill di tendenza", - "metaDescription": "Le skill OpenClaw più scaricate e più apprezzate del momento — una classifica live di ciò che gli agenti di coding stanno installando.", + "metaDescription": "Le skill OpenClaw più scaricate e più apprezzate — una classifica basata sui download riportati da clawskills.sh, aggiornata ogni giorno.", "ogTitle": "Skill di tendenza — {siteName}", - "ogDescription": "Una classifica live delle skill OpenClaw più popolari.", + "ogDescription": "Una classifica aggiornata ogni giorno delle skill OpenClaw più popolari.", "title": "Skill di tendenza", "subtitle": "Scopri le skill OpenClaw più popolari classificate dalla community", "searchPlaceholder": "Cerca skill di tendenza…", @@ -274,14 +355,20 @@ "likes": "Like", "views": "Visualizzazioni", "skill": "Skill", - "count": "{count, plural, =0 {Nessuna skill} one {# skill} other {# skill}}", - "countInCategory": "{count, plural, =0 {Nessuna skill} one {# skill} other {# skill}} in {category}", + "count": "{count, plural, =0 {Nessuna skill} one {# skill} many {# di skill} other {# skill}}", + "countInCategory": "{count, plural, =0 {Nessuna skill} one {# skill} many {# di skill} other {# skill}} in {category}", "emptyTitle": "Nessuna skill trovata", "emptySubtitle": "Prova a modificare la ricerca o i filtri", "byAuthor": "di {author}", "showing": "Da {from, number} a {to, number} di {total, number}", "prev": "Prec", - "next": "Succ" + "next": "Succ", + "provenanceNote": "I conteggi di download e stelle provengono dal catalogo upstream — clawskills.sh e GitHub — e vengono aggiornati ogni notte; non misurano le installazioni tramite TakoAPI. Like e visualizzazioni sono contati qui.", + "search": "Cerca", + "sortLabel": "Ordina per", + "rank": "Posizione", + "goToPage": "Vai alla pagina {page}", + "paginationLabel": "Pagine della classifica" }, "Admin": { "accessDenied": "Accesso negato", @@ -335,8 +422,8 @@ "actionReject": "Rifiuta", "feature": "Metti in evidenza", "unfeature": "Togli dall'evidenza", - "scenariosEditorHint": "场景 Scenari — 点击切换 / tocca per assegnare", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Scenari — clicca per assegnare o rimuovere", + "scenarioOption": "{name}", "selectedCount": "{count, number} selezionati", "rejectReasonPrompt": "Motivo del rifiuto (facoltativo):", "deleteAgentConfirm": "Eliminare definitivamente questo agente?", @@ -398,9 +485,9 @@ "noCategoriesFound": "Nessuna categoria trovata. Aggiungine una sopra.", "deleting": "...", "cannotDeleteHasSkills": "Impossibile eliminare: contiene skill", - "categoriesTotal": "{count, number} {count, plural, one {categoria} other {categorie}} in totale", + "categoriesTotal": "{count, number} {count, plural, one {categoria} many {di categorie} other {categorie}} in totale", "usersTitle": "Utenti", - "usersTotal": "{count, number} {count, plural, one {utente} other {utenti}} in totale", + "usersTotal": "{count, number} {count, plural, one {utente} many {di utenti} other {utenti}} in totale", "searchUsersPlaceholder": "Cerca per nome o email...", "colEmail": "Email", "colRole": "Ruolo", @@ -417,7 +504,7 @@ "demoteToUser": "Retrocedi a utente", "promoteToAdmin": "Promuovi ad admin", "logsTitle": "Log admin", - "logsTotal": "{count, number} {count, plural, one {voce} other {voci}} in totale", + "logsTotal": "{count, number} {count, plural, one {voce} many {di voci} other {voci}} in totale", "colTimestamp": "Timestamp", "colAdmin": "Admin", "colAction": "Azione", @@ -436,7 +523,46 @@ "targetSkill": "skill", "targetAgent": "agente", "targetCategory": "categoria", - "targetUser": "utente" + "targetUser": "utente", + "adminNavLabel": "Sezioni admin", + "retry": "Riprova", + "close": "Chiudi", + "prevPage": "Pagina precedente", + "nextPage": "Pagina successiva", + "sectionAgents": "Agenti", + "sectionSkills": "Skill e community", + "statTotalAgents": "Agenti totali", + "statPendingAgents": "Agenti in attesa", + "statHostedAgents": "Hosted", + "statProjectAgents": "Progetti", + "pendingAgentsTitle": "Agenti in attesa di revisione", + "noPendingAgents": "Nessun agente è in attesa di revisione.", + "kindHosted": "Hosted", + "kindProject": "Progetto", + "filterByStatus": "Filtra per stato", + "filterDisabled": "Disabilitati", + "actionDisable": "Disabilita", + "actionEnable": "Riabilita", + "disableAgentTitle": "Disabilita agente", + "disableAgentConfirm": "{name} sarà nascosto dal catalogo pubblico finché non lo riabiliti. Non viene eliminato nulla.", + "deleteAgentTitle": "Elimina agente", + "deleteAgentConfirmNamed": "Vuoi davvero eliminare {name}? Questa azione non può essere annullata.", + "rejectAgentTitle": "Rifiuta agente", + "rejectReasonLabel": "Motivo (facoltativo, mostrato al publisher)", + "rejectReasonPlaceholder": "es. l'endpoint restituisce 500 sul task di esempio", + "healthOk": "In salute", + "healthDegraded": "Degradato", + "healthDown": "Non raggiungibile", + "healthUnknown": "Non verificato", + "healthCheckedAt": "Ultimo controllo di salute: {date}", + "healthNeverChecked": "Nessun controllo di salute registrato", + "batchDeleteTitle": "Elimina skill", + "selectAllRows": "Seleziona tutte le righe", + "selectRow": "Seleziona {name}", + "editSkillNamed": "Modifica la skill {name}", + "deleteSkillNamed": "Elimina la skill {name}", + "editCategoryNamed": "Modifica la categoria {name}", + "deleteCategoryNamed": "Elimina la categoria {name}" }, "Auth": { "signInTitle": "Accedi a TakoAPI", @@ -462,12 +588,26 @@ "noAccount": "Non hai un account?", "signUpLink": "Registrati", "haveAccount": "Hai già un account?", - "signInLink": "Accedi" + "signInLink": "Accedi", + "forgotPassword": "Password dimenticata?", + "forgotTitle": "Reimposta la password", + "forgotSubtitle": "Ti invieremo per email un link per sceglierne una nuova", + "sendResetLink": "Invia il link", + "sendingResetLink": "Invio in corso...", + "resetLinkSent": "Se a quell'indirizzo è associato un account, abbiamo inviato un link per reimpostare la password. Controlla la posta in arrivo.", + "backToSignIn": "Torna all'accesso", + "resetTitle": "Scegli una nuova password", + "resetSubtitle": "Inserisci una nuova password per il tuo account", + "newPasswordHint": "Nuova password (minimo 8 caratteri)", + "updatePassword": "Aggiorna la password", + "updatingPassword": "Aggiornamento...", + "passwordUpdated": "La tua password è stata aggiornata. Ti portiamo all'accesso...", + "resetLinkInvalid": "Questo link per reimpostare la password non è valido o è scaduto.", + "requestNewLink": "Richiedi un nuovo link" }, "Dashboard": { "title": "Dashboard per sviluppatori", "signInPrompt": "Accedi per gestire le chiavi API e vedere l'utilizzo.", - "signIn": "Accedi", "description": "Gestisci le chiavi API e monitora l'utilizzo del gateway. Una chiave, una fattura, qualsiasi agente.", "apiKeys": "Chiavi API", "createKey": "Crea chiave", @@ -497,7 +637,6 @@ "billing": "Crediti e fatturazione", "creditBalance": "Saldo crediti", "totalSpend": "Spesa totale", - "topUpComingSoon": "Per ora i crediti vengono aggiunti dalla piattaforma; la ricarica self-service con PayPal arriverà presto.", "topUpLabel": "Aggiungi crediti", "topUpButton": "Ricarica con PayPal", "topUpRedirecting": "Reindirizzamento…", @@ -513,35 +652,65 @@ "ledgerDebit": "Addebito chiamata", "ledgerPayout": "Liquidazione", "ledgerRefund": "Rimborso", - "ledgerAdjust": "Rettifica" + "ledgerAdjust": "Rettifica", + "onboardingTitle": "Fai funzionare la tua prima chiamata", + "onboardingSubtitle": "Tre passaggi da qui a un agente che ti risponde.", + "stepKeyTitle": "Crea una chiave API", + "stepKeyBody": "Una sola chiave invoca tutti gli agenti su TakoAPI. Il segreto viene mostrato una volta sola, subito dopo la creazione.", + "stepCallTitle": "Fai la tua prima chiamata", + "stepCallBody": "Copia un esempio qui sotto, imposta TAKO_KEY con la tua chiave ed eseguilo. La chiamata compare in Utilizzo nel giro di pochi secondi.", + "stepInstallTitle": "Installa nel tuo agente di coding", + "stepInstallBody": "Insegna a Claude Code, Codex o OpenCode a scoprire e invocare gli agenti tramite TakoAPI.", + "stepInstallLink": "Apri la guida all'installazione", + "stepOptional": "Facoltativo", + "stepDone": "Fatto", + "stepTodo": "Non ancora fatto", + "keyNameLabel": "Nome della chiave", + "keyNamePlaceholder": "Chiave gateway", + "createKeyError": "Non è stato possibile creare la chiave. Riprova.", + "revokeNamed": "Revoca {name}", + "quickstartHint": "Imposta TAKO_KEY con una delle chiavi qui sopra, poi invoca qualsiasi agente tramite il suo slug.", + "creditRule": "Gli agenti gratuiti non costano nulla; quelli a pagamento addebitano il tuo credito prepagato e la chiamata viene rifiutata con HTTP 402 non appena il saldo scenderebbe sotto zero.", + "topUpUnavailable": "La ricarica self-service non è ancora disponibile: per ora i crediti vengono aggiunti dalla piattaforma. In ogni caso il saldo qui sopra è aggiornato.", + "dismiss": "Chiudi", + "loadError": "Non è stato possibile caricare alcuni dati della dashboard.", + "retry": "Riprova", + "recentCallsCaption": "Le tue chiamate più recenti al gateway", + "chartTitle": "Chiamate al giorno, ultimi {days} giorni", + "chartTotal": "{count, number} in totale", + "chartAria": "Grafico a barre delle chiamate al gateway per giorno negli ultimi {days} giorni, {count, number} chiamate in totale.", + "chartDay": "{date}: {calls, number} chiamate, {errors, number} non riuscite" }, "Profile": { - "title": "Il mio profilo", - "signInPrompt": "Accedi per visualizzare il tuo profilo.", - "signIn": "Accedi", - "submissions": "Invii", + "title": "Le mie schede", + "signInPrompt": "Accedi per vedere gli agenti e le skill che hai pubblicato.", "likes": "Like", - "views": "Visualizzazioni", - "statValue": "{count, number}", - "mySubmissions": "I miei invii", - "submitNew": "Invia nuovo", - "tab_all": "Tutti", - "tab_approved": "Approvati", - "tab_pending": "In attesa", - "tab_rejected": "Rifiutati", - "tabCount": "{count, number}", - "emptyTab": "Nessun invio in questa scheda", + "views": "visualizzazioni", + "tab_all": "Tutte", + "emptyTab": "Ancora nessuna skill qui.", "submitFirst": "Invia la tua prima skill", - "statusApproved": "Approvata", - "statusPending": "In attesa di revisione", + "statusApproved": "Online", + "statusPending": "In revisione", "statusRejected": "Rifiutata", - "reviewerNote": "Nota del revisore:" + "reviewerNote": "Nota del revisore:", + "publishAgent": "Pubblica un agente", + "submitSkill": "Invia una skill", + "statAgents": "Agenti", + "statCalls": "Chiamate al gateway", + "statSkills": "Skill", + "tabAgents": "Agenti", + "tabSkills": "Skill", + "statusDisabled": "Disabilitato", + "kindHosted": "Invocabile", + "kindProject": "Open-source", + "noAgents": "Ancora nessun agente qui.", + "publishFirstAgent": "Pubblica il tuo primo agente", + "myListings": "Le mie schede" }, "Submit": { "title": "Invia una skill", "subtitle": "Condividi la tua skill OpenClaw con la community", "signInPrompt": "Accedi per inviare le skill.", - "signIn": "Accedi", "skillSource": "Origine della skill", "githubUrl": "URL GitHub", "clawSkillsUrl": "URL ClawSkills.sh", @@ -571,8 +740,15 @@ "submitting": "Invio in corso...", "enterUrlFirst": "Inserisci prima un URL", "autoFillFailed": "Compilazione automatica non riuscita", - "submitFailed": "Invio non riuscito", - "somethingWentWrong": "Qualcosa è andato storto" + "somethingWentWrong": "Qualcosa è andato storto", + "successTitle": "Inviata 🎉", + "pendingReview": "{name} è ora in attesa di revisione.", + "nextStepsTitle": "Cosa succede ora", + "nextStepReview": "Un revisore controlla il repository e la descrizione.", + "nextStepEmail": "Ti scriviamo per email quando la revisione è conclusa.", + "nextStepProfile": "La pagina delle tue schede mostra sempre lo stato attuale e l'eventuale nota del revisore.", + "viewMyListings": "Vedi le mie schede", + "backToSkills": "← Torna alle skill" }, "SubmitAgent": { "title": "Pubblica un agente", @@ -611,7 +787,24 @@ "successTitle": "Inviato 🎉", "pendingReview": "{name} è ora in attesa di revisione.", "successDetail": "Verificheremo l'AgentCard e la pubblicheremo sul marketplace una volta approvata.", - "backToMarketplace": "← Torna al marketplace" + "backToMarketplace": "← Torna al marketplace", + "checkCard": "Verifica la card", + "checking": "Verifica in corso…", + "enterCardUrlFirst": "Inserisci prima l'URL di una AgentCard.", + "previewTitle": "Ecco cosa pubblicheremmo", + "previewNoDescription": "La card non ha una descrizione.", + "previewEndpoint": "Endpoint", + "previewCapabilities": "Capacità", + "previewSkillCount": "{count, plural, =0 {Nessuna capacità dichiarata} one {# capacità} many {# di capacità} other {# capacità}}", + "previewSupports": "Supporta", + "previewStreaming": "Streaming", + "previewPush": "Notifiche push", + "previewHint": "Non è ancora stato salvato nulla: pubblica qui sotto per inviarlo in revisione.", + "nextStepsTitle": "Cosa succede ora", + "nextStepReview": "Un revisore controlla l'AgentCard e verifica che il tuo endpoint risponda.", + "nextStepEmail": "Ti scriviamo per email quando la revisione è conclusa.", + "nextStepProfile": "La pagina delle tue schede mostra sempre lo stato attuale e l'eventuale nota del revisore.", + "viewMyListings": "Vedi le mie schede" }, "AgentEngagement": { "yourRating": "La tua valutazione", @@ -619,28 +812,44 @@ "noRatings": "Non ancora valutato", "bookmark": "Salva", "bookmarked": "Salvato", - "signInToRate": "Accedi per valutare o salvare questo agente" + "signInToRate": "Accedi per valutare o salvare questo agente", + "rateStars": "Valuta {count, plural, one {# stella} many {# di stelle} other {# stelle}}" }, "Bookmarks": { "title": "Agenti salvati", "subtitle": "Gli agenti che hai salvato.", "empty": "Ancora nessun salvataggio.", "browseAgents": "Esplora gli agenti", - "signInPrompt": "Accedi per vedere i tuoi salvati.", - "signIn": "Accedi" + "signInPrompt": "Accedi per vedere i tuoi salvati." }, "ScenarioPage": { "metaTitle": "Agenti IA per {scenario}", - "metaDescription": "Scopri e invoca agenti IA per {scenario} tramite un'unica API unificata, descritti da AgentCard A2A aperte su TakoAPI.", - "heroSubtitle": "Agenti IA per {scenario}, richiamabili tramite un'unica API.", + "metaDescription": "Scopri e invoca agenti IA per {scenario} tramite un'unica API unificata — descritti da AgentCard A2A aperte su TakoAPI.", + "heroSubtitle": "Agenti IA per {scenario}, invocabili tramite un'unica API unificata.", "empty": "Ancora nessun agente in questo scenario.", "browseAll": "Esplora tutti gli agenti", "back": "← Tutti gli scenari", "breadcrumbScenarios": "Scenari", "indexTitle": "Esplora gli agenti IA per scenario", - "indexDescription": "Trova agenti IA in base all'uso: frontend, investimenti, marketing, assistenza e altro, tutti richiamabili tramite un'API.", + "indexDescription": "Trova agenti IA in base all'uso che ne faresti — frontend, investimenti, marketing, assistenza e altro — tutti invocabili tramite un'unica API.", "indexHeroTitle": "Esplora per scenario", - "indexHeroSubtitle": "Trova agenti IA in base al compito da svolgere." + "indexHeroSubtitle": "Trova agenti IA in base al compito da svolgere.", + "indexPopularHeading": "Ben coperti", + "indexPopularHint": "Scenari con un catalogo ricco di agenti da confrontare.", + "indexSparseHeading": "Appena avviati", + "indexSparseHint": "Per ora solo pochi agenti: un posto facile in cui essere il primo.", + "indexSparseCta": "Pubblica un agente in uno di questi →", + "metaTitlePaged": "Agenti IA per {scenario} — pagina {page}", + "resultsLabel": "Agenti per {scenario}", + "paginationLabel": "Paginazione", + "showingRange": "Da {from} a {to} di {total}", + "prev": "← Prec", + "next": "Succ →", + "goToPage": "Vai alla pagina {page}", + "ctaTitle": "Conosci un agente per {scenario}?", + "ctaBody": "Questo scenario è ancora poco popolato. Pubblica un agente e comparirà qui per chiunque esplori {scenario}.", + "ctaButton": "Pubblica un agente", + "relatedHeading": "Esplora altri scenari" }, "Badge": { "snippetTitle": "Aggiungi un badge", @@ -656,7 +865,8 @@ "step2": "Nella pagina della tua scheda, copia il Markdown dal riquadro del badge.", "step3Title": "Incollalo nel tuo README", "step3": "Aggiungilo in alto: visitatori e motori di ricerca ottengono un link alla tua pagina TakoAPI.", - "findListing": "Trova la tua scheda" + "findListing": "Trova la tua scheda", + "exampleAlt": "Badge TakoAPI" }, "Blog": { "title": "Blog", @@ -666,5 +876,149 @@ "minRead": "{min} min di lettura", "published": "Pubblicato", "faqTitle": "Domande frequenti" + }, + "NotFound": { + "title": "Non abbiamo trovato questa pagina", + "description": "Il link potrebbe non essere più valido oppure la scheda è stata rimossa. Prova la directory o cerca dall'intestazione.", + "home": "Home", + "agents": "Esplora gli agenti", + "skills": "Esplora le skill" + }, + "ErrorPage": { + "title": "Qualcosa è andato storto", + "description": "Il caricamento di questa pagina non è riuscito. Spesso basta riprovare; se non funziona, il riferimento qui sotto ci aiuta a ritrovarla nei log.", + "reference": "Riferimento: {id}", + "retry": "Riprova", + "home": "Vai alla home" + }, + "Consent": { + "title": "Analytics", + "description": "Usiamo Google Analytics per capire quali pagine risultano utili. Non viene caricato nulla e non viene impostato alcun cookie se non accetti.", + "accept": "Accetta", + "decline": "Rifiuta" + }, + "Docs": { + "metaTitle": "Documentazione API", + "title": "Riferimento API di TakoAPI", + "description": "Dalla chiave API a una chiamata funzionante: autenticazione, endpoint di discovery, gateway unificato per gli agenti, server MCP hosted, codici di errore, rate limit e fatturazione.", + "heroBadge": "Documentazione per sviluppatori", + "heroTitle": "Il riferimento API di TakoAPI", + "heroSubtitle": "Una chiave, tutti gli agenti. Punta un qualsiasi client compatibile con OpenAI su TakoAPI e invoca un agente tramite il suo slug — oppure esplora l'intero catalogo senza alcuna chiave.", + "heroKeyCta": "Crea una chiave API", + "heroBrowseCta": "Esplora gli agenti", + "tocTitle": "In questa pagina", + "tocLabel": "Indice dei contenuti", + "quickstartTitle": "Avvio rapido", + "quickstartIntro": "Tre passaggi da zero a una risposta. Ogni richiesta in questa pagina è una chiamata reale all'API live.", + "quickstartStep1Title": "Crea una chiave", + "quickstartStep1": "Accedi e crea una chiave API nella tua dashboard. Il segreto viene mostrato una sola volta, alla creazione.", + "quickstartStep2Title": "Esportala", + "quickstartStep2": "Tutti gli esempi in questa pagina leggono la chiave da TAKO_KEY.", + "quickstartStep3Title": "Invoca un agente", + "quickstartStep3": "Imposta il model — o lo slug nell'URL — su un qualsiasi agente hosted del catalogo.", + "quickstartSamplesLabel": "Esempi di chiamata per l'avvio rapido", + "quickstartNote": "Lo slug in questi esempi è un segnaposto, non un agente reale. Copiane uno vero da una qualsiasi pagina agente; solo gli agenti indicati come hosted possono essere invocati.", + "authenticationTitle": "Autenticazione", + "authenticationIntro": "Le chiamate al gateway si autenticano con un bearer token. Gli endpoint di discovery sono anonimi e non richiedono alcuna chiave.", + "authHeaderNote": "x-api-key è accettato in alternativa all'header Authorization. Le chiavi iniziano sempre con tako_live_.", + "authWhereTitle": "Da dove arrivano le chiavi", + "authWhere": "Crea una chiave per ogni client o ambiente nella tua dashboard e revoca quelle che smetti di usare.", + "authOnceTitle": "Il segreto viene mostrato una sola volta", + "authOnce": "Vengono salvati solo un hash SHA-256 della chiave e un breve prefisso di visualizzazione, quindi un segreto perso non è recuperabile: creane una sostitutiva e revoca quella vecchia.", + "authRevokeTitle": "La revoca è immediata", + "authRevoke": "Ogni chiamata riverifica la chiave sul database e nulla viene messo in cache, quindi una chiave revocata è rifiutata già alla richiesta successiva. L'utilizzo passato resta nella tua cronologia.", + "discoveryTitle": "Discovery", + "discoveryIntro": "Quattro endpoint pubblici e cacheabili che non richiedono chiave. Vengono restituiti solo agenti e skill approvati.", + "endpointRegistrySummary": "L'intero catalogo degli agenti — Markdown per un modello, JSON per il codice.", + "endpointRegistryNote": "Senza format=json la risposta è Markdown (text/markdown), divisa in una tabella di agenti hosted e una di progetti open-source e dimensionata per finire direttamente nel contesto di un modello: è ciò che legge la skill TakoAPI installata. Entrambi i formati sono limitati da limit, quindi controlla truncated e i conteggi totali prima di dare per scontato di avere tutto. I parametri di testo libero vengono troncati a 100 caratteri.", + "paramRegistryFormat": "md (predefinito) o json.", + "paramRegistryQ": "Corrispondenza in testo libero su nome e descrizione dell'agente.", + "paramRegistryCategory": "Slug della categoria.", + "paramRegistryProtocol": "Restituisce solo gli agenti che dichiarano questo protocollo.", + "paramRegistryKind": "Gli agenti HOSTED sono invocabili tramite il gateway; le voci PROJECT sono repository open-source elencati solo per la discovery.", + "paramRegistrySort": "Ordinamento. Per impostazione predefinita prima quelli in evidenza, poi i più invocati, poi i più recenti — e i più stellati per i progetti.", + "paramRegistryLimit": "Numero totale di voci da restituire. Vengono riempiti prima gli agenti hosted, poi i progetti.", + "endpointAgentDetailSummary": "Il dettaglio pubblico completo di un agente, incluse le skill A2A che dichiara.", + "endpointAgentDetailNote": "Si risolvono solo gli agenti approvati; tutto il resto è un 404. I campi di moderazione — note di revisione, id interni e avatar dei publisher — non fanno mai parte di questa struttura. In cache all'edge per 60 secondi.", + "paramAgentDetailSlug": "Segmento di path. Lo slug dell'agente, così come restituito dal registro.", + "endpointSkillsDirectorySummary": "Il catalogo delle skill OpenClaw, in Markdown per impostazione predefinita.", + "endpointSkillsDirectoryNote": "Markdown per primo, come il registro. install è null per le skill senza un comando di installazione pubblicato: in quel caso usa il link github. downloads è un dato di terze parti raccolto dalla scheda della skill stessa, non un conteggio di installazioni TakoAPI.", + "paramSkillsDirFormat": "md (predefinito) o json.", + "paramSkillsDirQ": "Corrispondenza in testo libero su nome e descrizione della skill.", + "paramSkillsDirCategory": "Slug della categoria.", + "paramSkillsDirPage": "Numero di pagina, a partire da 1.", + "paramSkillsDirLimit": "Risultati per pagina.", + "endpointSkillsSearchSummary": "Ricerca full-text su tutto il catalogo delle skill, solo JSON.", + "endpointSkillsSearchNote": "q è obbligatorio: senza, l'endpoint restituisce una lista vuota anziché l'intero catalogo. Ogni risultato è la riga completa del catalogo meno readme e reviewNote; l'esempio qui sopra è abbreviato.", + "paramSkillsSearchQ": "Termine di ricerca. Cerca in nome, descrizione e autore della skill.", + "paramSkillsSearchCategory": "Slug della categoria.", + "paramSkillsSearchPage": "Numero di pagina, a partire da 1.", + "paramSkillsSearchLimit": "Risultati per pagina.", + "gatewayTitle": "Gateway", + "gatewayIntro": "Tre modi per invocare lo stesso agente. Tutti e tre richiedono una chiave, fatturano allo stesso modo e accettano lo slug dell'agente dove di norma andrebbe il nome di un modello. Possono essere invocati solo gli agenti di tipo HOSTED.", + "endpointChatCompletionsSummary": "Compatibile con OpenAI. Punta un qualsiasi SDK OpenAI su /v1 e imposta model sullo slug di un agente.", + "endpointChatCompletionsNote": "Viene inoltrata l'intera conversazione: una text part A2A per turno, in ordine, con il ruolo OpenAI associato a ciascuna part. usage è sempre presente ma sempre a zero: in questo gateway nulla conta i token e un agente non deve per forza essere un LLM. Imposta stream: true per la forma SSE qui sopra e stream_options.include_usage per ricevere anche il chunk finale di usage. Una chiamata non in streaming va in timeout dopo 30 secondi, uno stream dopo 120.", + "endpointA2AMessageSummary": "Passthrough A2A nativo. Restituisce la risposta JSON-RPC dell'agente, senza modifiche.", + "endpointA2AMessageNote": "Inoltrata come chiamata A2A message/send. Invia la forma abbreviata text oppure un messaggio A2A completo — con parts, taskId e contextId — per proseguire un task multi-turno o far passare part di tipo file e data. Status code e body dell'agente vengono inoltrati intatti e una richiesta senza contenuto viene rifiutata con un 400 anziché fatturata. Timeout dopo 30 secondi.", + "endpointA2AStreamSummary": "La stessa chiamata su Server-Sent Events, per gli agenti che supportano lo streaming.", + "endpointA2AStreamNote": "Inoltra i frame A2A message/stream dell'agente così come sono, in text/event-stream. Questo endpoint accetta solo la forma abbreviata text. La connessione viene chiusa dopo 120 secondi e la chiamata viene addebitata solo quando lo stream arriva alla fine — vedi Rate limit e fatturazione.", + "labelQueryParams": "Parametri", + "labelRequest": "Body della richiesta", + "labelExample": "Esempio", + "labelResponse": "Risposta", + "labelStreaming": "Risposta con stream: true", + "labelA2AMessageForm": "Forma completa del messaggio A2A", + "labelAuthRequired": "Chiave richiesta", + "labelNoAuth": "Senza chiave", + "labelBillable": "A pagamento", + "labelFree": "Gratis", + "labelParamName": "Nome", + "labelParamType": "Tipo", + "labelParamDesc": "Descrizione", + "paramsTableCaption": "Parametri di {path}", + "mcpTitle": "Server MCP", + "mcpIntro": "TakoAPI espone un server Model Context Protocol hosted e stateless su Streamable HTTP all'indirizzo {url}. Registralo una volta e il tuo agente di coding potrà cercare e invocare il catalogo come strumenti nativi.", + "mcpRegisterTitle": "Registralo", + "mcpRegisterLabel": "Comandi di registrazione MCP", + "mcpAuthNote": "I tre strumenti di lettura funzionano in modo anonimo. invoke_agent consuma credito, quindi richiede la tua chiave: passala come header Authorization (Claude Code), come bearer_token_env_var (Codex) o in un blocco headers (OpenCode).", + "mcpToolsTitle": "Strumenti", + "mcpToolsTableCaption": "Strumenti esposti dal server MCP di TakoAPI", + "mcpToolColumn": "Strumento", + "mcpToolAccessColumn": "Accesso", + "mcpToolWhatColumn": "Cosa fa", + "mcpToolKeyRequired": "Chiave richiesta", + "mcpToolAnonymous": "Anonimo", + "mcpToolSearchAgents": "Cerca nel registro per parola chiave, categoria o protocollo. Restituisce slug, protocolli e prezzi.", + "mcpToolGetAgent": "Tutto ciò che si sa di un agente a partire dallo slug: capacità, protocolli, endpoint, prezzo e skill dichiarate.", + "mcpToolSearchSkills": "Cerca nel catalogo delle skill OpenClaw. Senza query restituisce le migliori skill selezionate.", + "mcpToolInvokeAgent": "Invoca un agente tramite il gateway e ne restituisce la risposta. Richiede una chiave e consuma credito.", + "mcpRateNote": "L'endpoint MCP è limitato a 120 richieste al minuto per indirizzo IP, separatamente dal limite gateway della tua chiave.", + "mcpInstallNote": "Preferisci un file skill a un server? L’installer insegna a Claude Code, Codex e OpenCode a chiamare invece questi endpoint via HTTP semplice.", + "errorsTitle": "Errori", + "errorsIntro": "Ogni errore del gateway è in JSON. Questi sono gli status che il gateway restituisce direttamente.", + "errorsTableCaption": "Status di errore restituiti dal gateway", + "errorStatusColumn": "Status", + "errorCodeColumn": "Codice", + "errorWhenColumn": "Quando", + "errorBodyColumn": "Esempio di body", + "error400When": "Lo slug indica un progetto open-source senza endpoint invocabile, il body della richiesta non ha superato la validazione oppure tutti i turni del messaggio erano vuoti.", + "error401When": "La chiave manca, è malformata, sconosciuta o revocata.", + "error402When": "L'agente è a pagamento e addebitare la chiamata porterebbe il saldo sotto zero. Il controllo avviene prima di contattare l'agente, quindi non viene speso nulla.", + "error404When": "Nessun agente approvato ha quello slug.", + "error429When": "La chiave ha superato il proprio limite negli ultimi 60 secondi. Retry-After indica i secondi che mancano al reset della finestra.", + "error502When": "L'agente non ha risposto, è andato in timeout, non ha restituito alcuno stream oppure ha segnalato un errore JSON-RPC dentro un HTTP 200. Mai addebitato.", + "errorsOpenAiNote": "Su /v1/chat/completions gli stessi errori arrivano nell'envelope di OpenAI — con type, param e code sempre presenti — così un SDK OpenAI solleva le sue solite eccezioni:", + "errorsPassthroughNote": "Uno status restituito dall'agente stesso viene inoltrato invariato dall'endpoint A2A message, quindi lì un 4xx può essere dell'agente e non nostro.", + "limitsTitle": "Rate limit e fatturazione", + "limitsRateTitle": "Rate limit", + "limitsRate": "Il gateway consente 120 richieste ogni 60 secondi per chiave API. La finestra è condivisa tra tutte le istanze del server ed è basata sulla chiave stessa anziché sul tuo indirizzo IP, quindi non si allenta sotto carico né si restringe dietro un NAT condiviso.", + "limitsRateOverride": "Una chiave può avere un limite proprio, che sostituisce quello predefinito solo per quella chiave. Oltre il limite ricevi un 429 con un header Retry-After, in secondi.", + "limitsBillingTitle": "Fatturazione", + "limitsBillingFree": "Gli agenti con prezzo FREE non costano nulla — e nemmeno quelli il cui prezzo unitario manca o è zero. Il gateway non inventa mai un addebito.", + "limitsBillingPriced": "Un agente a pagamento addebita il tuo credito prepagato al suo prezzo unitario, una volta per chiamata. Non c'è misurazione a livello di token, quindi una chiamata vale un'unità per quanto a lungo duri.", + "limitsBillingPreflight": "Il saldo viene controllato prima di contattare l'agente. Se l'addebito ti portasse sotto zero la chiamata viene rifiutata con un 402 e l'agente non viene mai invocato: non esiste scoperto.", + "limitsBillingFailures": "Le chiamate non riuscite non vengono addebitate. Un 4xx o 5xx dall'agente, un timeout, un endpoint irraggiungibile o un errore JSON-RPC registrano il tentativo e non fatturano nulla.", + "limitsBillingStreams": "Gli stream vengono addebitati solo quando si completano. Uno stream interrotto, andato in timeout, fallito a metà o da cui ti disconnetti in anticipo viene registrato ma non fatturato.", + "limitsBillingLedger": "Ogni addebito scrive una voce di registro immutabile associata all'invocazione che l'ha generata. Saldo, registro e utilizzo per chiamata sono tutti nella tua dashboard." } } diff --git a/messages/ja.json b/messages/ja.json index 7accc47ff..12ad82416 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -7,60 +7,78 @@ "label": "言語" }, "Header": { - "searchPlaceholder": "エージェントを検索...", "agents": "エージェント", "skills": "スキル", - "trending": "トレンド", - "publish": "公開", "publishAgent": "エージェントを公開", "account": "アカウント", - "mySkills": "マイスキル", - "myBookmarks": "ブックマーク", - "apiUsage": "API・利用状況", + "myBookmarks": "保存したエージェント", + "apiUsage": "APIキーと利用状況", "adminPanel": "管理パネル", "signIn": "サインイン", - "signOut": "サインアウト" + "signOut": "サインアウト", + "mainNav": "メイン", + "skipToContent": "本文へスキップ", + "openMenu": "メニューを開く", + "closeMenu": "メニューを閉じる", + "projects": "プロジェクト", + "install": "インストール", + "getApiKey": "APIキーを取得", + "search": "検索", + "searchScope": "検索対象", + "scopeAgents": "エージェント", + "scopeProjects": "プロジェクト", + "scopeSkills": "スキル", + "searchAgentsPlaceholder": "できることからエージェントを検索…", + "searchProjectsPlaceholder": "オープンソースのエージェントプロジェクトを検索…", + "searchSkillsPlaceholder": "コーディングエージェント向けスキルを検索…", + "myListings": "マイ掲載", + "docs": "ドキュメント" }, "Footer": { - "tagline": "すべてのエージェントに、ひとつのAPIで — さらにコーディングエージェント向けの数千のOpenClawスキルも。", + "tagline": "すべてのエージェントに、ひとつのAPIで — 単一のエンドポイントから、どのエージェントも発見・呼び出し・従量計測できます。", "agentRegistry": "エージェントレジストリ:", "platform": "プラットフォーム", "resources": "リソース", - "installInAgent": "エージェントにインストール", + "installInAgent": "コーディングエージェントにインストール", "browseAgents": "エージェントを見る", "publishAnAgent": "エージェントを公開", - "developerDashboard": "開発者ダッシュボード", + "developerDashboard": "APIキーと利用状況", "browseSkills": "スキルを見る", - "awesomeList": "Awesomeリスト", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. OpenClawコミュニティのために構築。", - "blog": "ブログ" + "copyright": "© {year} TakoAPI. MITライセンス。", + "blog": "ブログ", + "badge": "READMEバッジ", + "scenarios": "シナリオから探す", + "a2aSpec": "A2Aプロトコル", + "github": "GitHub", + "docs": "APIドキュメント" }, "Home": { "heroTitle": "ひとつのAPIで、すべてのエージェントに", - "heroSubtitle": "ひとつの統合APIで{agents, number}のAIエージェントを発見・呼び出し — さらにコーディングエージェント向けの{skills, number}のスキルも。", + "heroSubtitle": "AIエージェントを見つけて、ひとつのキーとひとつのエンドポイントからどれでも呼び出せます。A2Aネイティブ、OpenAI互換、呼び出しごとの従量課金。", "browseAgents": "エージェントを見る", - "publishAnAgent": "エージェントを公開", - "agentRegistry": "エージェントレジストリ:", - "browseByScenario": "シナリオから探す", - "allAgents": "すべてのエージェント", + "browseByScenario": "やりたいことから探す", "featuredAgents": "注目のエージェント", "browseAll": "すべて見る", "popularProjects": "人気のオープンソースプロジェクト", "selfHostable": "セルフホスト可能 {count, number}件", - "skillTitle": "OpenClaw向けTakoAPIスキル", - "official": "公式", - "skillDescription": "自然な対話でOpenClawスキルを検索・インストール・管理 — TakoAPIマーケットプレイスが提供。", - "viewOnGithub": "GitHubで見る", - "askAgent": "またはエージェントに頼む:「TakoAPIスキルをインストールして」", - "usingCodingAgent": "Claude Code、Codex、OpenCodeをお使いですか?", - "oneCommandInstall": "ワンコマンドでインストール", - "mustHaveSkills": "必携スキル", - "viewRankings": "ランキングを見る", - "categories": "カテゴリ", - "viewAll": "すべて見る", + "categories": "カテゴリ:", "moreCategories": "{count, plural, other {他+#カテゴリ}}", - "latestSkills": "最新スキル" + "getApiKey": "APIキーを取得", + "catalogSummary": "呼び出し可能なエージェント {agents, number}件 · オープンソースプロジェクト {projects, number}件 · コーディングエージェント向けスキル {skills, number}件", + "quickstartLabel": "30秒で最初の呼び出しまで", + "quickstartNote": "ターミナルで作業したいですか? TakoAPIをコーディングエージェントにインストール。", + "allScenarios": "すべてのシナリオ", + "callableNow": "ゲートウェイ経由で呼び出し可能 {count, number}件", + "installTitle": "コーディングエージェントからTakoAPIを使う", + "installDescription": "ワンコマンドで、Claude Code、Codex、OpenCodeがディレクトリを検索してエージェントを呼び出せるようになります。代わりにホスト型MCPサーバーを登録することもできます。", + "installCta": "インストールガイド", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "コーディングエージェント向けスキル", + "skillsSubtitle": "{count, number}件のOpenClawスキルから、インストール数上位と最新をご紹介します。", + "quickstartTabDiscover": "1. 探す", + "quickstartTabCall": "2. 呼び出す", + "quickstartTabFromAgent": "3. エージェントから" }, "Scenarios": { "frontend": "フロントエンド", @@ -85,8 +103,6 @@ "notFound": "エージェントが見つかりません", "backToMarketplace": "← エージェントマーケットプレイス", "openSourceProject": "オープンソースプロジェクト", - "signedCard": "署名付きカード", - "verifiedPublisher": "認証済み公開者", "by": "{name} 作", "publisherOpenSource": "オープンソース", "publisherUnknown": "不明", @@ -101,17 +117,41 @@ "noSkillsHosted": "このエージェントはAgentCardに構造化されたスキルを公開していません。", "inputMode": "入力: {mode}", "outputMode": "出力: {mode}", - "openSourceProjectHeading": "オープンソースプロジェクト", "openSourceProjectNote": "セルフホストするか、コードを確認できます。TakoAPIゲートウェイ経由では呼び出せません。", "viewOnGithub": "GitHubで見る", - "integration": "連携", "endpoint": "エンドポイント", "agentCard": "AgentCard", "homepage": "ホームページ", "callThroughTakoApi": "TakoAPI経由で呼び出す", - "gatewayPhase2": "統合ゲートウェイ — 呼び出し機能はフェーズ2で提供予定。", "breadcrumbHome": "ホーム", - "breadcrumbMarketplace": "エージェントマーケットプレイス" + "breadcrumbMarketplace": "エージェントマーケットプレイス", + "gatewayHint": "APIキーで呼び出せます — ダッシュボードで作成してください。", + "healthOk": "応答あり", + "healthDegraded": "不安定", + "healthDown": "応答なし", + "healthCheckedAt": "最終確認 {time}", + "atAGlance": "概要", + "protocols": "プロトコル", + "protocolsNone": "宣言なし", + "yes": "はい", + "no": "いいえ", + "pricing": "料金", + "health": "稼働状況", + "healthUnknown": "未確認", + "totalCalls": "総呼び出し数", + "numberValue": "{count, number}", + "averageRating": "平均評価", + "ratingValue": "{rating} / 5 · {count, number}件の評価", + "notRated": "まだ評価がありません", + "callExamplesLabel": "呼び出し例", + "repository": "リポジトリ", + "language": "言語", + "license": "ライセンス", + "licenseNone": "指定なし", + "lastPush": "最終プッシュ", + "topics": "トピック", + "readmeHeading": "READMEより", + "readmeNote": "GitHub上のプロジェクトREADMEからの抜粋です。" }, "Agents": { "metaTitle": "エージェントマーケットプレイス", @@ -140,16 +180,29 @@ "pricingPerCall": "呼び出しごと", "pricingPerTask": "タスクごと", "pricingPerToken": "1kトークンあたり", - "count": "{count, plural, other {#件のエージェント}}", + "count": "{count, plural, =0 {エージェントなし} other {#件のエージェント}}", "emptyTitle": "条件に一致するエージェントはまだありません。", "emptyCta": "最初の1件を公開する →", "prev": "← 前へ", "next": "次へ →", - "pageOf": "{total}ページ中{page}ページ" + "pageOf": "{total}ページ中{page}ページ", + "filtersTitle": "フィルター", + "filtersWithCount": "フィルター ({count})", + "activeFiltersLabel": "適用中のフィルター:", + "removeFilter": "フィルターを解除: {label}", + "clearAll": "すべて解除", + "kindLabel": "種別", + "scenarioGroup": "シナリオ", + "protocolGroup": "プロトコル", + "pricingGroup": "料金", + "categoryGroup": "カテゴリ", + "searchChip": "検索: {query}", + "resultsLabel": "検索結果", + "paginationLabel": "ページ送り", + "goToPage": "{page}ページ目へ", + "showingRange": "{total}件中{from}〜{to}件を表示" }, "Components": { - "homeSearchPlaceholder": "名前・説明・作者でスキルを検索...", - "homeSearchButton": "検索", "agentFree": "無料", "agentPerCall": "{price}/呼び出し", "agentPerTask": "{price}/タスク", @@ -163,7 +216,11 @@ "agentSelfHost": "セルフホスト", "agentSkills": "{count, plural, other {#件のスキル}}", "skillBy": "{author} 作", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number}件の呼び出し", + "healthOk": "応答あり", + "healthDegraded": "不安定", + "healthDown": "応答なし" }, "Install": { "metaTitle": "Claude Code、Codex、OpenCodeにインストール", @@ -196,7 +253,7 @@ "howToUseHeading": "使い方", "claudeCodeUse": "自動で読み込まれます — Claudeに、TakoAPI上のエージェントを探すよう頼むだけです。", "codexUse": "$takoapiで呼び出すか、/skillsメニューから選択します。", - "opencodeUse": "/takoapi を実行するか、@takoapiに引き継ぎます。", + "opencodeUse": "/takoapi '' を実行するか、@takoapiに引き継ぎます。", "mcpHeading": "MCPサーバー — ホスト型ツール、インストール不要", "claudeCodeMcpHint": "ワンコマンド。invoke_agentを有効にするには --header \"Authorization: Bearer $TAKO_KEY\" を追加します。", "codexMcpHint": "~/.codex/config.tomlに追加", @@ -226,12 +283,17 @@ "requirements": "要件", "installation": "インストール", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClawチャット(会話に貼り付けてください)", "links": "リンク", "github": "GitHub", - "copyGithubUrl": "GitHub URLをコピー", "clawHub": "ClawHub", - "skillNotFound": "スキルが見つかりません" + "codingAgent": "コーディングエージェント", + "agentPrompt": "{url} からスキルをインストールして", + "installHintRegistry": "CLIコマンドを実行するか、Claude Code、Cursor、Codexなどお使いのコーディングエージェントにプロンプトを貼り付けてください。", + "installHintRepo": "このスキルはClawHubレジストリには公開されていません。Claude Code、Cursor、Codexなどのコーディングエージェントにプロンプトを貼り付けると、ソースリポジトリから直接インストールされます。", + "installUnavailable": "このスキルのインストール元はまだ公開されていません。", + "openRepository": "リポジトリを開く", + "signInToLike": "サインインするとこのスキルにいいねできます", + "downloadsSource": "ダウンロード数はclawskills.shが公開している値です" }, "Skills": { "metaTitle": "スキルを見る", @@ -257,13 +319,32 @@ "all": "すべて", "emptyTitle": "スキルが見つかりません", "emptyHint": "検索条件やフィルターを調整してみてください", - "pageOf": "{total}ページ中{page}ページ" + "pageOf": "{total}ページ中{page}ページ", + "metaTitleSearch": "「{query}」に一致するスキル", + "metaTitlePaged": "スキルを見る — {page}ページ目", + "searchInputLabel": "スキルを検索", + "searchPlaceholder": "名前・説明・作者でスキルを検索", + "searchButton": "検索", + "agentFilter": "エージェント", + "agentZed": "Zed", + "sourceFilter": "ソース", + "sourceGithub": "GitHub", + "sourceCommunity": "コミュニティ", + "sourceCurated": "厳選", + "categoryFilter": "カテゴリ", + "sortLabel": "並び順", + "sortRelevance": "関連度順", + "clearFilters": "フィルターを解除", + "paginationLabel": "スキル一覧のページ送り", + "goToPage": "{page}ページ目へ", + "prev": "前へ", + "next": "次へ" }, "Trending": { "metaTitle": "トレンドのスキル", - "metaDescription": "いま最もダウンロードされ、最もいいねを集めているOpenClawスキル — コーディングエージェントが何をインストールしているかをリアルタイムで示すリーダーボード。", + "metaDescription": "ダウンロード数と「いいね」が最も多いOpenClawスキル — clawskills.shが公開するダウンロード数をもとにした、毎日更新のランキング。", "ogTitle": "トレンドのスキル — {siteName}", - "ogDescription": "人気のOpenClawスキルをリアルタイムで示すリーダーボード。", + "ogDescription": "OpenClawの人気スキルを毎日更新するランキング。", "title": "トレンドのスキル", "subtitle": "コミュニティのランキングで人気のOpenClawスキルを発見しましょう", "searchPlaceholder": "トレンドのスキルを検索…", @@ -274,14 +355,20 @@ "likes": "いいね", "views": "閲覧数", "skill": "スキル", - "count": "{count, plural, other {#件のスキル}}", - "countInCategory": "{category}内に{count, plural, other {#件のスキル}}", + "count": "{count, plural, =0 {スキルなし} other {#件のスキル}}", + "countInCategory": "{category}に{count, plural, =0 {スキルなし} other {#件のスキル}}", "emptyTitle": "スキルが見つかりません", "emptySubtitle": "検索条件やフィルターを調整してみてください", "byAuthor": "{author} 作", "showing": "{total, number}件中{from, number}〜{to, number}件を表示", "prev": "前へ", - "next": "次へ" + "next": "次へ", + "provenanceNote": "ダウンロード数とスター数は上流のカタログ(clawskills.shとGitHub)の値を毎晩取得したもので、TakoAPI経由のインストール数ではありません。いいねと閲覧数はこのサイトで計測しています。", + "search": "検索", + "sortLabel": "並び順", + "rank": "順位", + "goToPage": "{page}ページ目へ", + "paginationLabel": "ランキングのページ送り" }, "Admin": { "accessDenied": "アクセスが拒否されました", @@ -335,8 +422,8 @@ "actionReject": "却下", "feature": "注目に設定", "unfeature": "注目を解除", - "scenariosEditorHint": "场景 シナリオ — 点击切换 / タップで割り当て", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "シナリオ — クリックして割り当て・解除", + "scenarioOption": "{name}", "selectedCount": "{count, number}件選択中", "rejectReasonPrompt": "却下の理由(任意):", "deleteAgentConfirm": "このエージェントを完全に削除しますか?", @@ -398,9 +485,9 @@ "noCategoriesFound": "カテゴリが見つかりません。上から追加してください。", "deleting": "...", "cannotDeleteHasSkills": "削除できません: スキルが存在します", - "categoriesTotal": "{count, plural, other {#件のカテゴリ}}合計", + "categoriesTotal": "合計{count, number}件のカテゴリ", "usersTitle": "ユーザー", - "usersTotal": "{count, plural, other {#人のユーザー}}合計", + "usersTotal": "合計{count, number}人のユーザー", "searchUsersPlaceholder": "名前またはメールで検索...", "colEmail": "メール", "colRole": "ロール", @@ -417,7 +504,7 @@ "demoteToUser": "ユーザーに降格", "promoteToAdmin": "管理者に昇格", "logsTitle": "管理ログ", - "logsTotal": "{count, plural, other {#件のエントリ}}合計", + "logsTotal": "合計{count, number}件のエントリ", "colTimestamp": "タイムスタンプ", "colAdmin": "管理者", "colAction": "アクション", @@ -436,7 +523,46 @@ "targetSkill": "スキル", "targetAgent": "エージェント", "targetCategory": "カテゴリ", - "targetUser": "ユーザー" + "targetUser": "ユーザー", + "adminNavLabel": "管理セクション", + "retry": "再試行", + "close": "閉じる", + "prevPage": "前のページ", + "nextPage": "次のページ", + "sectionAgents": "エージェント", + "sectionSkills": "スキルとコミュニティ", + "statTotalAgents": "総エージェント数", + "statPendingAgents": "レビュー待ちエージェント", + "statHostedAgents": "ホスト型", + "statProjectAgents": "プロジェクト", + "pendingAgentsTitle": "レビュー待ちのエージェント", + "noPendingAgents": "レビュー待ちのエージェントはありません。", + "kindHosted": "ホスト型", + "kindProject": "プロジェクト", + "filterByStatus": "ステータスで絞り込む", + "filterDisabled": "無効", + "actionDisable": "無効化", + "actionEnable": "再有効化", + "disableAgentTitle": "エージェントを無効化", + "disableAgentConfirm": "{name}は再有効化するまで公開カタログに表示されなくなります。削除はされません。", + "deleteAgentTitle": "エージェントを削除", + "deleteAgentConfirmNamed": "{name}を削除してもよろしいですか? この操作は取り消せません。", + "rejectAgentTitle": "エージェントを却下", + "rejectReasonLabel": "理由(任意、公開者に表示されます)", + "rejectReasonPlaceholder": "例: サンプルタスクでエンドポイントが500を返す", + "healthOk": "正常", + "healthDegraded": "不安定", + "healthDown": "停止", + "healthUnknown": "未確認", + "healthCheckedAt": "最終ヘルスチェック: {date}", + "healthNeverChecked": "ヘルスチェックの記録はありません", + "batchDeleteTitle": "スキルを削除", + "selectAllRows": "すべての行を選択", + "selectRow": "{name}を選択", + "editSkillNamed": "スキル{name}を編集", + "deleteSkillNamed": "スキル{name}を削除", + "editCategoryNamed": "カテゴリ{name}を編集", + "deleteCategoryNamed": "カテゴリ{name}を削除" }, "Auth": { "signInTitle": "TakoAPIにサインイン", @@ -462,12 +588,26 @@ "noAccount": "アカウントをお持ちでないですか?", "signUpLink": "登録", "haveAccount": "すでにアカウントをお持ちですか?", - "signInLink": "サインイン" + "signInLink": "サインイン", + "forgotPassword": "パスワードをお忘れですか?", + "forgotTitle": "パスワードを再設定", + "forgotSubtitle": "新しいパスワードを設定するリンクをメールでお送りします", + "sendResetLink": "再設定リンクを送信", + "sendingResetLink": "送信しています...", + "resetLinkSent": "そのアドレスのアカウントが存在する場合、パスワード再設定リンクを送信しました。受信トレイをご確認ください。", + "backToSignIn": "サインインに戻る", + "resetTitle": "新しいパスワードを設定", + "resetSubtitle": "アカウントの新しいパスワードを入力してください", + "newPasswordHint": "新しいパスワード(8文字以上)", + "updatePassword": "パスワードを更新", + "updatingPassword": "更新しています...", + "passwordUpdated": "パスワードを更新しました。サインイン画面へ移動します...", + "resetLinkInvalid": "このパスワード再設定リンクは無効か、有効期限が切れています。", + "requestNewLink": "新しいリンクを再送する" }, "Dashboard": { "title": "開発者ダッシュボード", "signInPrompt": "APIキーを管理し、利用状況を確認するにはサインインしてください。", - "signIn": "サインイン", "description": "APIキーを管理し、ゲートウェイの利用状況を監視します。ひとつのキー、ひとつの請求で、どのエージェントも。", "apiKeys": "APIキー", "createKey": "キーを作成", @@ -497,9 +637,8 @@ "billing": "クレジットと請求", "creditBalance": "クレジット残高", "totalSpend": "合計利用額", - "topUpComingSoon": "現在クレジットはプラットフォームが付与しています。PayPal によるセルフチャージは近日対応予定です。", "topUpLabel": "クレジットを追加", - "topUpButton": "PayPal でチャージ", + "topUpButton": "PayPalでチャージ", "topUpRedirecting": "リダイレクト中…", "topUpSuccess": "クレジットを追加しました。残高が更新されました。", "topUpError": "チャージが完了しませんでした。もう一度お試しください。", @@ -513,35 +652,65 @@ "ledgerDebit": "呼び出し料金", "ledgerPayout": "支払い", "ledgerRefund": "返金", - "ledgerAdjust": "調整" + "ledgerAdjust": "調整", + "onboardingTitle": "最初の呼び出しを成功させる", + "onboardingSubtitle": "エージェントから応答が返るまで、ここから3ステップです。", + "stepKeyTitle": "APIキーを作成", + "stepKeyBody": "ひとつのキーでTakoAPI上のすべてのエージェントを呼び出せます。シークレットは作成直後に一度だけ表示されます。", + "stepCallTitle": "最初の呼び出しを実行", + "stepCallBody": "下のサンプルをコピーし、TAKO_KEYに自分のキーを設定して実行してください。数秒で「利用状況」に呼び出しが表示されます。", + "stepInstallTitle": "コーディングエージェントにインストール", + "stepInstallBody": "Claude Code、Codex、OpenCodeがTakoAPI経由でエージェントを発見・呼び出せるようになります。", + "stepInstallLink": "インストールガイドを開く", + "stepOptional": "任意", + "stepDone": "完了", + "stepTodo": "未完了", + "keyNameLabel": "キー名", + "keyNamePlaceholder": "ゲートウェイキー", + "createKeyError": "キーを作成できませんでした。もう一度お試しください。", + "revokeNamed": "{name}を失効", + "quickstartHint": "上のいずれかのキーをTAKO_KEYに設定し、スラッグで任意のエージェントを呼び出してください。", + "creditRule": "無料エージェントは課金されません。有料エージェントはプリペイドクレジットから引き落とされ、残高がマイナスになる場合は呼び出しがHTTP 402で拒否されます。", + "topUpUnavailable": "セルフサービスのチャージはまだ利用できません。現時点ではクレジットはプラットフォームが付与します。いずれの場合も上の残高は最新の値です。", + "dismiss": "閉じる", + "loadError": "一部のダッシュボードデータを読み込めませんでした。", + "retry": "再試行", + "recentCallsCaption": "直近のゲートウェイ呼び出し", + "chartTitle": "直近{days}日間の日別呼び出し数", + "chartTotal": "合計{count, number}件", + "chartAria": "直近{days}日間の日別ゲートウェイ呼び出し数の棒グラフ。合計{count, number}件。", + "chartDay": "{date}: 呼び出し{calls, number}件、失敗{errors, number}件" }, "Profile": { - "title": "マイプロフィール", - "signInPrompt": "プロフィールを表示するにはサインインしてください。", - "signIn": "サインイン", - "submissions": "投稿", + "title": "マイ掲載", + "signInPrompt": "公開したエージェントとスキルを表示するにはサインインしてください。", "likes": "いいね", "views": "閲覧数", - "statValue": "{count, number}", - "mySubmissions": "マイ投稿", - "submitNew": "新規投稿", "tab_all": "すべて", - "tab_approved": "承認済み", - "tab_pending": "保留中", - "tab_rejected": "却下", - "tabCount": "{count, number}", - "emptyTab": "このタブに投稿はありません", + "emptyTab": "スキルはまだありません。", "submitFirst": "最初のスキルを投稿する", - "statusApproved": "承認済み", - "statusPending": "レビュー待ち", + "statusApproved": "公開中", + "statusPending": "レビュー中", "statusRejected": "却下", - "reviewerNote": "レビュアーのメモ:" + "reviewerNote": "レビュアーのメモ:", + "publishAgent": "エージェントを公開", + "submitSkill": "スキルを投稿", + "statAgents": "エージェント", + "statCalls": "ゲートウェイ呼び出し", + "statSkills": "スキル", + "tabAgents": "エージェント", + "tabSkills": "スキル", + "statusDisabled": "無効", + "kindHosted": "呼び出し可能", + "kindProject": "オープンソース", + "noAgents": "エージェントはまだありません。", + "publishFirstAgent": "最初のエージェントを公開する", + "myListings": "マイ掲載" }, "Submit": { "title": "スキルを投稿", "subtitle": "あなたのOpenClawスキルをコミュニティに共有しましょう", "signInPrompt": "スキルを投稿するにはサインインしてください。", - "signIn": "サインイン", "skillSource": "スキルのソース", "githubUrl": "GitHub URL", "clawSkillsUrl": "ClawSkills.sh URL", @@ -563,7 +732,7 @@ "whatItDoes": "このスキルでできること", "whatItDoesPlaceholder": "このスキルの機能、特徴、できることを詳しく説明...", "exampleWorkflow": "ワークフロー例", - "exampleWorkflowHelper": "- 入力 / エージェントの手順 / 出力", + "exampleWorkflowHelper": "- INPUT / AGENTの手順 / OUTPUT", "exampleWorkflowPlaceholder": "INPUT\nユーザーの依頼: 何か役立つことをする\n\nAGENT\n1\n`my-skill action`を実行してタスクを処理\n2\n結果を確認\n\nOUTPUT\nタスクが正常に完了", "requirements": "要件", "requirementsPlaceholder": "- Node.js 18+\n- サービスXのAPIキー\n- など", @@ -571,8 +740,15 @@ "submitting": "投稿中...", "enterUrlFirst": "先にURLを入力してください", "autoFillFailed": "自動入力に失敗しました", - "submitFailed": "投稿に失敗しました", - "somethingWentWrong": "問題が発生しました" + "somethingWentWrong": "問題が発生しました", + "successTitle": "送信しました 🎉", + "pendingReview": "{name}はレビュー待ちになりました。", + "nextStepsTitle": "この後の流れ", + "nextStepReview": "レビュアーがリポジトリと説明を確認します。", + "nextStepEmail": "レビューが完了したらメールでお知らせします。", + "nextStepProfile": "掲載ページでは、いつでも現在のステータスとレビュアーのメモを確認できます。", + "viewMyListings": "マイ掲載を見る", + "backToSkills": "← スキル一覧に戻る" }, "SubmitAgent": { "title": "エージェントを公開", @@ -606,65 +782,243 @@ "homepagePlaceholder": "https://your-agent.com", "submit": "エージェントを公開", "submitting": "送信中…", - "submitFailed": "投稿に失敗しました", + "submitFailed": "送信に失敗しました", "somethingWentWrong": "問題が発生しました", "successTitle": "送信しました 🎉", "pendingReview": "{name}はレビュー待ちになりました。", "successDetail": "AgentCardを検証し、承認され次第マーケットプレイスに公開します。", - "backToMarketplace": "← マーケットプレイスに戻る" + "backToMarketplace": "← マーケットプレイスに戻る", + "checkCard": "カードを確認", + "checking": "確認中…", + "enterCardUrlFirst": "先にAgentCardのURLを入力してください。", + "previewTitle": "掲載される内容のプレビュー", + "previewNoDescription": "カードに説明がありません。", + "previewEndpoint": "エンドポイント", + "previewCapabilities": "機能", + "previewSkillCount": "{count, plural, =0 {公開されている機能はありません} other {#件の機能}}", + "previewSupports": "対応", + "previewStreaming": "ストリーミング", + "previewPush": "プッシュ通知", + "previewHint": "まだ保存されていません。下のボタンで公開すると、レビューに送信されます。", + "nextStepsTitle": "この後の流れ", + "nextStepReview": "レビュアーがAgentCardと、エンドポイントが応答することを確認します。", + "nextStepEmail": "レビューが完了したらメールでお知らせします。", + "nextStepProfile": "掲載ページでは、いつでも現在のステータスとレビュアーのメモを確認できます。", + "viewMyListings": "マイ掲載を見る" }, "AgentEngagement": { "yourRating": "あなたの評価", - "ratingsCount": "{count, number} 件の評価", + "ratingsCount": "{count, number}件の評価", "noRatings": "まだ評価がありません", "bookmark": "保存", "bookmarked": "保存済み", - "signInToRate": "ログインすると評価・保存できます" + "signInToRate": "サインインするとこのエージェントを評価・保存できます", + "rateStars": "{count, plural, other {#つ星}}で評価" }, "Bookmarks": { "title": "保存したエージェント", "subtitle": "ブックマークしたエージェント。", "empty": "まだブックマークはありません。", "browseAgents": "エージェントを見る", - "signInPrompt": "ログインするとブックマークを表示できます。", - "signIn": "ログイン" + "signInPrompt": "ブックマークを表示するにはサインインしてください。" }, "ScenarioPage": { - "metaTitle": "{scenario}向け AI エージェント", - "metaDescription": "{scenario}向けの AI エージェントを統一 API で発見・呼び出し。TakoAPI のオープンな A2A AgentCard で記述。", - "heroSubtitle": "{scenario}向けの AI エージェント。統一 API で呼び出せます。", + "metaTitle": "{scenario}向けAIエージェント", + "metaDescription": "{scenario}向けのAIエージェントをひとつの統合APIで発見・呼び出し — TakoAPIのオープンなA2A AgentCardで記述されています。", + "heroSubtitle": "{scenario}向けのAIエージェント。ひとつの統合APIで呼び出せます。", "empty": "このシナリオのエージェントはまだありません。", "browseAll": "すべてのエージェントを見る", "back": "← すべてのシナリオ", "breadcrumbScenarios": "シナリオ", - "indexTitle": "シナリオ別に AI エージェントを探す", - "indexDescription": "用途で AI エージェントを探す——フロントエンド、投資、マーケティング、サポートなど、すべて 1 つの API で呼び出し可能。", + "indexTitle": "シナリオ別にAIエージェントを探す", + "indexDescription": "用途からAIエージェントを探せます — フロントエンド、投資、マーケティング、サポートなど、すべてひとつのAPIで呼び出せます。", "indexHeroTitle": "シナリオ別に探す", - "indexHeroSubtitle": "やりたいことから AI エージェントを探しましょう。" + "indexHeroSubtitle": "やりたいことからAIエージェントを探しましょう。", + "indexPopularHeading": "充実しているシナリオ", + "indexPopularHint": "比較できるエージェントが十分に揃っているシナリオです。", + "indexSparseHeading": "まだこれからのシナリオ", + "indexSparseHint": "エージェントはまだ数件だけ — 最初の1件になるチャンスです。", + "indexSparseCta": "この中のシナリオでエージェントを公開する →", + "metaTitlePaged": "{scenario}向けAIエージェント — {page}ページ目", + "resultsLabel": "{scenario}のエージェント", + "paginationLabel": "ページ送り", + "showingRange": "{total}件中{from}〜{to}件を表示", + "prev": "← 前へ", + "next": "次へ →", + "goToPage": "{page}ページ目へ", + "ctaTitle": "{scenario}向けのエージェントをご存じですか?", + "ctaBody": "このシナリオはまだ手薄です。エージェントを公開すると、{scenario}を見ている人にここで表示されます。", + "ctaButton": "エージェントを公開", + "relatedHeading": "他のシナリオを見る" }, "Badge": { "snippetTitle": "バッジを追加", "copy": "コピー", "copied": "コピーしました", - "snippetHint": "README に貼り付けると、TakoAPI の掲載ページにリンクできます。", - "pageTitle": "TakoAPI README バッジ", - "pageIntro": "あなたのプロジェクトが TakoAPI に掲載されていることを示す小さなバッジ。掲載ページへのリンクと最新のスター数を表示します。", + "snippetHint": "READMEに貼り付けると、TakoAPIの掲載ページにリンクできます。", + "pageTitle": "TakoAPI READMEバッジ", + "pageIntro": "あなたのプロジェクトがTakoAPIに掲載されていることを示す小さなバッジ。掲載ページへのリンクと最新のスター数を表示します。", "example": "例", "step1Title": "掲載ページを探す", "step1": "エージェントディレクトリでプロジェクトを検索します。", "step2Title": "バッジをコピー", - "step2": "掲載ページのバッジ欄から Markdown をコピーします。", - "step3Title": "README に貼り付け", - "step3": "上部付近に追加すると、訪問者や検索エンジンが TakoAPI ページへのリンクを得られます。", - "findListing": "掲載ページを探す" + "step2": "掲載ページのバッジ欄からMarkdownをコピーします。", + "step3Title": "READMEに貼り付け", + "step3": "上部付近に追加すると、訪問者や検索エンジンがTakoAPIページへのリンクを得られます。", + "findListing": "掲載ページを探す", + "exampleAlt": "TakoAPIバッジ" }, "Blog": { "title": "ブログ", - "subtitle": "A2A、エージェントゲートウェイ、TakoAPI での構築に関するガイド。", + "subtitle": "A2A、エージェントゲートウェイ、TakoAPIでの構築に関するガイド。", "readMore": "続きを読む", "backToBlog": "← 記事一覧", - "minRead": "約 {min} 分で読めます", + "minRead": "約{min}分で読めます", "published": "公開日", "faqTitle": "よくある質問" + }, + "NotFound": { + "title": "ページが見つかりませんでした", + "description": "リンクが古いか、掲載が削除された可能性があります。ディレクトリを見るか、ヘッダーから検索してみてください。", + "home": "ホーム", + "agents": "エージェントを見る", + "skills": "スキルを見る" + }, + "ErrorPage": { + "title": "問題が発生しました", + "description": "このページを読み込めませんでした。多くの場合は再試行で解決します。解決しない場合は、下の参照IDからログを調査できます。", + "reference": "参照ID: {id}", + "retry": "再試行", + "home": "ホームへ" + }, + "Consent": { + "title": "アナリティクス", + "description": "どのページが役立っているかを把握するためにGoogle Analyticsを使用しています。同意しない限り、読み込みもCookieの設定も行いません。", + "accept": "同意する", + "decline": "同意しない" + }, + "Docs": { + "metaTitle": "APIドキュメント", + "title": "TakoAPI APIリファレンス", + "description": "APIキーの取得からエージェント呼び出しの成功まで: 認証、ディスカバリーエンドポイント、統合エージェントゲートウェイ、ホスト型MCPサーバー、エラーコード、レート制限、課金。", + "heroBadge": "開発者ドキュメント", + "heroTitle": "TakoAPI APIリファレンス", + "heroSubtitle": "ひとつのキーで、すべてのエージェントを。OpenAI互換のクライアントをTakoAPIに向けて、スラッグでエージェントを呼び出せます。カタログ全体の閲覧はキーなしでも可能です。", + "heroKeyCta": "APIキーを作成", + "heroBrowseCta": "エージェントを見る", + "tocTitle": "このページの内容", + "tocLabel": "目次", + "quickstartTitle": "クイックスタート", + "quickstartIntro": "3ステップで応答が返ってきます。このページのリクエストはすべて、稼働中のAPIに対する実際の呼び出しです。", + "quickstartStep1Title": "キーを作成", + "quickstartStep1": "サインインしてダッシュボードでAPIキーを作成します。シークレットは作成時に一度だけ表示されます。", + "quickstartStep2Title": "環境変数に設定", + "quickstartStep2": "このページの例はすべてTAKO_KEYからキーを読み取ります。", + "quickstartStep3Title": "エージェントを呼び出す", + "quickstartStep3": "modelに(またはURL中のスラッグに)、カタログにあるホスト型エージェントを指定します。", + "quickstartSamplesLabel": "クイックスタートの呼び出し例", + "quickstartNote": "サンプル中のスラッグはプレースホルダーで、実在するエージェントではありません。各エージェントのページから実際のスラッグをコピーしてください。呼び出せるのはホスト型として掲載されているエージェントのみです。", + "authenticationTitle": "認証", + "authenticationIntro": "ゲートウェイの呼び出しはベアラートークンで認証します。ディスカバリーエンドポイントは匿名で、キーは一切不要です。", + "authHeaderNote": "Authorizationヘッダーの代わりにx-api-keyも使用できます。キーは必ずtako_live_で始まります。", + "authWhereTitle": "キーの発行元", + "authWhere": "ダッシュボードでクライアントや環境ごとにキーを作成し、使わなくなったキーは失効させてください。", + "authOnceTitle": "シークレットは一度だけ表示されます", + "authOnce": "保存されるのはキーのSHA-256ハッシュと表示用の短いプレフィックスだけなので、紛失したシークレットは復元できません。新しいキーを作成し、古いキーを失効させてください。", + "authRevokeTitle": "失効は即時に反映されます", + "authRevoke": "呼び出しのたびにデータベースでキーを再確認し、キャッシュは行いません。そのため失効したキーは次のリクエストから拒否されます。過去の利用履歴はそのまま残ります。", + "discoveryTitle": "ディスカバリー", + "discoveryIntro": "キー不要でキャッシュ可能な公開エンドポイントが4つあります。返されるのは承認済みのエージェントとスキルのみです。", + "endpointRegistrySummary": "エージェントカタログ全体 — モデル向けはMarkdown、コード向けはJSON。", + "endpointRegistryNote": "format=jsonを指定しない場合、レスポンスはMarkdown(text/markdown)で、ホスト型エージェントの表とオープンソースプロジェクトの表に分かれ、そのままモデルのコンテキストに収まるサイズになります。インストール済みのTakoAPIスキルが読み取るのもこれです。どちらの形式もlimitで上限が設けられているため、すべて取得できたと判断する前にtruncatedと総件数を確認してください。フリーテキストのパラメータは100文字に切り詰められます。", + "paramRegistryFormat": "md(デフォルト)またはjson。", + "paramRegistryQ": "エージェント名と説明に対するフリーテキスト検索。", + "paramRegistryCategory": "カテゴリのスラッグ。", + "paramRegistryProtocol": "このプロトコルを公開しているエージェントのみを返します。", + "paramRegistryKind": "HOSTEDのエージェントはゲートウェイ経由で呼び出せます。PROJECTのエントリは発見用に掲載されたオープンソースリポジトリです。", + "paramRegistrySort": "並び順。デフォルトは注目、呼び出し数、新着の順で、プロジェクトはスター数順です。", + "paramRegistryLimit": "返すエントリの総数。まずホスト型エージェントで埋め、その後プロジェクトが入ります。", + "endpointAgentDetailSummary": "1つのエージェントの公開情報すべて。公開されているA2Aスキルを含みます。", + "endpointAgentDetailNote": "解決されるのは承認済みのエージェントのみで、それ以外は404です。レビューメモ、内部ID、公開者のアバターといったモデレーション用のフィールドがこのレスポンスに含まれることはありません。エッジで60秒間キャッシュされます。", + "paramAgentDetailSlug": "パスセグメント。レジストリが返すエージェントのスラッグ。", + "endpointSkillsDirectorySummary": "OpenClawスキルのカタログ。デフォルトはMarkdownです。", + "endpointSkillsDirectoryNote": "レジストリと同様にMarkdownが既定です。インストールコマンドが公開されていないスキルではinstallがnullになるため、githubのリンクを使ってください。downloadsはスキル自身の掲載ページから取得したサードパーティの数値で、TakoAPI経由のインストール数ではありません。", + "paramSkillsDirFormat": "md(デフォルト)またはjson。", + "paramSkillsDirQ": "スキル名と説明に対するフリーテキスト検索。", + "paramSkillsDirCategory": "カテゴリのスラッグ。", + "paramSkillsDirPage": "ページ番号(1始まり)。", + "paramSkillsDirLimit": "1ページあたりの件数。", + "endpointSkillsSearchSummary": "スキルカタログ全体の全文検索。JSONのみ。", + "endpointSkillsSearchNote": "qは必須です。指定しない場合、このエンドポイントはカタログ全体ではなく空のリストを返します。各結果はカタログの行からreadmeとreviewNoteを除いたもので、上の例は一部を省略しています。", + "paramSkillsSearchQ": "検索語。スキル名、説明、作者に一致します。", + "paramSkillsSearchCategory": "カテゴリのスラッグ。", + "paramSkillsSearchPage": "ページ番号(1始まり)。", + "paramSkillsSearchLimit": "1ページあたりの件数。", + "gatewayTitle": "ゲートウェイ", + "gatewayIntro": "同じエージェントを呼び出す方法が3通りあります。いずれもキーが必要で、課金は同一、通常はモデル名を指定する位置にエージェントのスラッグを渡します。呼び出せるのはkindがHOSTEDのエージェントのみです。", + "endpointChatCompletionsSummary": "OpenAI互換。任意のOpenAI SDKを/v1に向け、modelにエージェントのスラッグを設定します。", + "endpointChatCompletionsNote": "会話全体が転送されます。1ターンにつきA2Aのテキストパートを1つ、順番どおりに、各パートにOpenAIのroleを付けて送ります。usageは常に含まれますが常にゼロです。このゲートウェイはトークンを数えず、エージェントがLLMである必要もありません。上のSSE形式にするにはstream: trueを、末尾のusageチャンクも受け取るにはstream_options.include_usageを指定します。非ストリーミングの呼び出しは30秒、ストリーミングは120秒でタイムアウトします。", + "endpointA2AMessageSummary": "ネイティブなA2Aパススルー。エージェント自身のJSON-RPCレスポンスをそのまま返します。", + "endpointA2AMessageNote": "A2Aのmessage/send呼び出しとして中継されます。テキストの簡易形式を送るか、parts、taskId、contextIdを含む完全なA2Aメッセージを送ることで、複数ターンのタスクを継続したり、fileやdataのパートをそのまま渡したりできます。エージェントのステータスコードとボディは変更せずに中継され、内容が空のリクエストは課金されずに400で拒否されます。30秒でタイムアウトします。", + "endpointA2AStreamSummary": "同じ呼び出しをServer-Sent Eventsで行う形式。ストリーミング対応のエージェント向けです。", + "endpointA2AStreamNote": "エージェントのA2A message/streamフレームをtext/event-streamとしてそのまま中継します。このエンドポイントはテキストの簡易形式のみを受け付けます。接続は120秒で切断され、課金はストリームが最後まで到達したときにのみ発生します —「レート制限と課金」を参照してください。", + "labelQueryParams": "パラメータ", + "labelRequest": "リクエストボディ", + "labelExample": "例", + "labelResponse": "レスポンス", + "labelStreaming": "stream: true のときのレスポンス", + "labelA2AMessageForm": "完全なA2Aメッセージ形式", + "labelAuthRequired": "キー必須", + "labelNoAuth": "キー不要", + "labelBillable": "課金対象", + "labelFree": "無料", + "labelParamName": "名前", + "labelParamType": "型", + "labelParamDesc": "説明", + "paramsTableCaption": "{path}のパラメータ", + "mcpTitle": "MCPサーバー", + "mcpIntro": "TakoAPIは、Streamable HTTP上で動作するステートレスなModel Context Protocolサーバーを{url}でホストしています。一度登録すれば、コーディングエージェントはカタログの検索と呼び出しをネイティブツールとして利用できます。", + "mcpRegisterTitle": "登録する", + "mcpRegisterLabel": "MCP登録コマンド", + "mcpAuthNote": "読み取り系の3つのツールは匿名で動作します。invoke_agentはクレジットを消費するためキーが必要です。Authorizationヘッダー(Claude Code)、bearer_token_env_var(Codex)、headersブロック(OpenCode)のいずれかで渡してください。", + "mcpToolsTitle": "ツール", + "mcpToolsTableCaption": "TakoAPI MCPサーバーが公開するツール", + "mcpToolColumn": "ツール", + "mcpToolAccessColumn": "アクセス", + "mcpToolWhatColumn": "機能", + "mcpToolKeyRequired": "キー必須", + "mcpToolAnonymous": "匿名", + "mcpToolSearchAgents": "キーワード、カテゴリ、プロトコルでレジストリを検索します。スラッグ、プロトコル、料金を返します。", + "mcpToolGetAgent": "スラッグで指定した1つのエージェントの既知情報すべて — 機能、プロトコル、エンドポイント、料金、公開されているスキル。", + "mcpToolSearchSkills": "OpenClawスキルのカタログを検索します。クエリを指定しない場合は厳選された人気スキルを返します。", + "mcpToolInvokeAgent": "ゲートウェイ経由でエージェントを呼び出し、その応答を返します。キーが必要で、クレジットを消費します。", + "mcpRateNote": "MCPエンドポイントはIPアドレスごとに毎分120リクエストまでで、キーごとのゲートウェイ上限とは別に適用されます。", + "mcpInstallNote": "サーバーよりスキルファイルがお好みですか? インストーラーを使えば、Claude Code、Codex、OpenCodeがこれらのエンドポイントを素のHTTPで呼び出せるようになります。", + "errorsTitle": "エラー", + "errorsIntro": "ゲートウェイのエラーはすべてJSONです。以下はゲートウェイ自身が返すステータスです。", + "errorsTableCaption": "ゲートウェイが返すエラーステータス", + "errorStatusColumn": "ステータス", + "errorCodeColumn": "コード", + "errorWhenColumn": "発生条件", + "errorBodyColumn": "ボディの例", + "error400When": "スラッグが、呼び出し可能なエンドポイントを持たないオープンソースプロジェクトを指している。リクエストボディの検証に失敗した。またはすべてのメッセージターンが空だった。", + "error401When": "キーが未指定、形式不正、未登録、または失効している。", + "error402When": "有料エージェントで、この呼び出しを課金すると残高がマイナスになる。エージェントに接続する前に確認されるため、消費は発生しません。", + "error404When": "そのスラッグを持つ承認済みエージェントが存在しない。", + "error429When": "直近60秒でキーの上限を超えた。Retry-Afterにウィンドウがリセットされるまでの秒数が入ります。", + "error502When": "エージェントが応答しない、タイムアウトした、ストリームを返さなかった、またはHTTP 200でJSON-RPCエラーを返した。課金されることはありません。", + "errorsOpenAiNote": "/v1/chat/completionsでは同じエラーがOpenAIのエンベロープで返り、type、param、codeが常に含まれます。そのためOpenAI SDKは通常どおりの例外型を送出します:", + "errorsPassthroughNote": "A2Aのmessageエンドポイントはエージェント自身が返したステータスをそのまま中継するため、そこでの4xxはTakoAPI側ではなくエージェント側のものである場合があります。", + "limitsTitle": "レート制限と課金", + "limitsRateTitle": "レート制限", + "limitsRate": "ゲートウェイはAPIキーごとに60秒あたり120リクエストまで許可します。このウィンドウはすべてのサーバーインスタンスで共有され、IPアドレスではなくキー自体を基準とするため、負荷が高くても緩まず、共有NATの背後でも厳しくなりません。", + "limitsRateOverride": "キーごとに独自の上限を設定でき、その場合はそのキーだけデフォルトが置き換わります。上限を超えると429と、秒数を示すRetry-Afterヘッダーが返ります。", + "limitsBillingTitle": "課金", + "limitsBillingFree": "料金がFREEのエージェントは無料です。単価が未設定またはゼロのエージェントも同様です。ゲートウェイが独自に課金することはありません。", + "limitsBillingPriced": "有料エージェントは、呼び出し1回につき単価分をプリペイドクレジットから引き落とします。トークン単位の計測はないため、実行時間にかかわらず1回の呼び出しは1単位です。", + "limitsBillingPreflight": "残高はエージェントに接続する前に確認されます。課金によって残高がマイナスになる場合、呼び出しは402で拒否され、エージェントは呼び出されません。残高がマイナスになることはありません。", + "limitsBillingFailures": "失敗した呼び出しは課金されません。エージェントからの4xxや5xx、タイムアウト、到達不能なエンドポイント、JSON-RPCエラーは、いずれも試行として記録されるだけで課金は発生しません。", + "limitsBillingStreams": "ストリームは完了した場合にのみ課金されます。中断、タイムアウト、途中での失敗、こちらからの早期切断が起きたストリームは、記録はされますが課金されません。", + "limitsBillingLedger": "課金のたびに、その原因となった呼び出しに紐づく変更不可の台帳エントリが記録されます。残高、台帳、呼び出しごとの利用状況はすべてダッシュボードで確認できます。" } } diff --git a/messages/ko.json b/messages/ko.json index a9f717237..216f77b92 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -1,66 +1,84 @@ { "Metadata": { "tagline": "모든 에이전트에 접근하는 단 하나의 API", - "description": "하나의 통합 API로 AI 에이전트를 검색하고 호출하세요 — 에이전트를 위한 OpenRouter입니다. 수백 개의 오픈소스 에이전트 프로젝트와 수천 개의 OpenClaw 스킬 디렉터리를 코딩 에이전트용으로 둘러보세요." + "description": "하나의 통합 API로 AI 에이전트를 검색하고 호출하세요 — 에이전트를 위한 OpenRouter입니다. 수백 개의 오픈소스 에이전트 프로젝트와 코딩 에이전트용 OpenClaw 스킬 수천 개를 디렉터리에서 둘러보세요." }, "LocaleSwitcher": { "label": "언어" }, "Header": { - "searchPlaceholder": "에이전트 검색...", "agents": "에이전트", "skills": "스킬", - "trending": "트렌딩", - "publish": "게시", "publishAgent": "에이전트 게시", "account": "계정", - "mySkills": "내 스킬", - "myBookmarks": "북마크", - "apiUsage": "API 및 사용량", + "myBookmarks": "저장한 에이전트", + "apiUsage": "API 키 및 사용량", "adminPanel": "관리자 패널", "signIn": "로그인", - "signOut": "로그아웃" + "signOut": "로그아웃", + "mainNav": "메인", + "skipToContent": "본문으로 건너뛰기", + "openMenu": "메뉴 열기", + "closeMenu": "메뉴 닫기", + "projects": "프로젝트", + "install": "설치", + "getApiKey": "API 키 받기", + "search": "검색", + "searchScope": "검색 범위", + "scopeAgents": "에이전트", + "scopeProjects": "프로젝트", + "scopeSkills": "스킬", + "searchAgentsPlaceholder": "하는 일로 에이전트 검색…", + "searchProjectsPlaceholder": "오픈소스 에이전트 프로젝트 검색…", + "searchSkillsPlaceholder": "코딩 에이전트용 스킬 검색…", + "myListings": "내 등록 항목", + "docs": "문서" }, "Footer": { - "tagline": "모든 에이전트에 접근하는 단 하나의 API — 여기에 코딩 에이전트용 수천 개의 OpenClaw 스킬까지.", + "tagline": "모든 에이전트에 접근하는 단 하나의 API — 하나의 엔드포인트로 어떤 에이전트든 검색하고, 호출하고, 사용량을 측정하세요.", "agentRegistry": "에이전트 레지스트리:", "platform": "플랫폼", "resources": "리소스", - "installInAgent": "에이전트에 설치", + "installInAgent": "코딩 에이전트에 설치", "browseAgents": "에이전트 둘러보기", "publishAnAgent": "에이전트 게시", - "developerDashboard": "개발자 대시보드", + "developerDashboard": "API 키 및 사용량", "browseSkills": "스킬 둘러보기", - "awesomeList": "Awesome 목록", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. OpenClaw 커뮤니티를 위해 만들었습니다.", - "blog": "블로그" + "copyright": "© {year} TakoAPI. MIT 라이선스.", + "blog": "블로그", + "badge": "README 배지", + "scenarios": "시나리오별 둘러보기", + "a2aSpec": "A2A 프로토콜", + "github": "GitHub", + "docs": "API 문서" }, "Home": { "heroTitle": "모든 에이전트에 접근하는 단 하나의 API", - "heroSubtitle": "하나의 통합 API로 {agents, number}개의 AI 에이전트를 검색하고 호출하세요 — 여기에 코딩 에이전트용 {skills, number}개의 스킬까지.", + "heroSubtitle": "AI 에이전트를 찾고, 키 하나와 엔드포인트 하나로 어떤 에이전트든 호출하세요. A2A 네이티브, OpenAI 호환, 호출 단위 과금.", "browseAgents": "에이전트 둘러보기", - "publishAnAgent": "에이전트 게시", - "agentRegistry": "에이전트 레지스트리:", - "browseByScenario": "시나리오별 둘러보기", - "allAgents": "전체 에이전트", + "browseByScenario": "해야 할 일로 둘러보기", "featuredAgents": "추천 에이전트", "browseAll": "전체 보기", "popularProjects": "인기 오픈소스 프로젝트", "selfHostable": "셀프 호스팅 가능 {count, number}개", - "skillTitle": "OpenClaw용 TakoAPI 스킬", - "official": "공식", - "skillDescription": "자연스러운 대화로 OpenClaw 스킬을 검색, 설치, 관리하세요 — TakoAPI 마켓플레이스로 구동됩니다.", - "viewOnGithub": "GitHub에서 보기", - "askAgent": "또는 에이전트에게 요청하세요: \"TakoAPI 스킬을 설치해줘\"", - "usingCodingAgent": "Claude Code, Codex, OpenCode를 사용하시나요?", - "oneCommandInstall": "한 줄 명령으로 설치", - "mustHaveSkills": "꼭 필요한 스킬", - "viewRankings": "순위 보기", - "categories": "카테고리", - "viewAll": "전체 보기", - "moreCategories": "{count, plural, other {카테고리 +#개 더보기}}", - "latestSkills": "최신 스킬" + "categories": "카테고리:", + "moreCategories": "{count, plural, other {카테고리 +#개 더}}", + "getApiKey": "API 키 받기", + "catalogSummary": "호출 가능한 에이전트 {agents, number}개 · 오픈소스 프로젝트 {projects, number}개 · 코딩 에이전트 스킬 {skills, number}개", + "quickstartLabel": "첫 호출까지 30초", + "quickstartNote": "터미널에서 작업하는 편인가요? 코딩 에이전트에 TakoAPI를 설치하세요.", + "allScenarios": "전체 시나리오", + "callableNow": "게이트웨이로 호출 가능 {count, number}개", + "installTitle": "코딩 에이전트에서 TakoAPI 사용하기", + "installDescription": "한 줄 명령이면 Claude Code, Codex, OpenCode가 디렉터리를 검색하고 대신 에이전트를 호출합니다 — 또는 호스팅된 MCP 서버를 등록하세요.", + "installCta": "설치 가이드", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "코딩 에이전트용 스킬", + "skillsSubtitle": "OpenClaw 스킬 {count, number}개 — 설치 많은 순과 최신순.", + "quickstartTabDiscover": "1. 탐색", + "quickstartTabCall": "2. 호출", + "quickstartTabFromAgent": "3. 에이전트에서" }, "Scenarios": { "frontend": "프런트엔드", @@ -85,33 +103,55 @@ "notFound": "에이전트를 찾을 수 없습니다", "backToMarketplace": "← 에이전트 마켓플레이스", "openSourceProject": "오픈소스 프로젝트", - "signedCard": "서명된 카드", - "verifiedPublisher": "인증된 게시자", "by": "{name} 제작", "publisherOpenSource": "오픈소스", "publisherUnknown": "알 수 없음", "scenario": "시나리오:", - "stars": "별 {count, number}개", + "stars": "스타 {count, number}개", "selfHost": "셀프 호스팅", "streaming": "스트리밍", "pushNotifications": "푸시 알림", "skills": "스킬", "skillsCount": "({count, number})", - "noSkillsProject": "오픈소스 프로젝트입니다 — 코드를 살펴보고 GitHub에서 셀프 호스팅하세요.", + "noSkillsProject": "오픈소스 프로젝트입니다 — 코드를 살펴보고 GitHub에서 직접 호스팅하세요.", "noSkillsHosted": "이 에이전트는 AgentCard에 구조화된 스킬을 공개하지 않았습니다.", "inputMode": "입력: {mode}", "outputMode": "출력: {mode}", - "openSourceProjectHeading": "오픈소스 프로젝트", "openSourceProjectNote": "셀프 호스팅하거나 코드를 살펴보세요. TakoAPI 게이트웨이로는 호출할 수 없습니다.", "viewOnGithub": "GitHub에서 보기", - "integration": "연동", "endpoint": "엔드포인트", "agentCard": "AgentCard", "homepage": "홈페이지", "callThroughTakoApi": "TakoAPI로 호출하기", - "gatewayPhase2": "통합 게이트웨이 — 호출 기능은 2단계에 출시됩니다.", "breadcrumbHome": "홈", - "breadcrumbMarketplace": "에이전트 마켓플레이스" + "breadcrumbMarketplace": "에이전트 마켓플레이스", + "gatewayHint": "API 키로 호출하세요 — 대시보드에서 키를 만드세요.", + "healthOk": "응답함", + "healthDegraded": "불안정", + "healthDown": "응답 없음", + "healthCheckedAt": "마지막 확인 {time}", + "atAGlance": "한눈에 보기", + "protocols": "프로토콜", + "protocolsNone": "명시되지 않음", + "yes": "예", + "no": "아니요", + "pricing": "가격", + "health": "상태", + "healthUnknown": "아직 확인되지 않음", + "totalCalls": "전체 호출", + "numberValue": "{count, number}", + "averageRating": "평균 평점", + "ratingValue": "{rating} / 5 · 평가 {count, number}개", + "notRated": "아직 평가 없음", + "callExamplesLabel": "호출 예시", + "repository": "저장소", + "language": "언어", + "license": "라이선스", + "licenseNone": "명시되지 않음", + "lastPush": "마지막 푸시", + "topics": "토픽", + "readmeHeading": "README에서", + "readmeNote": "GitHub에 있는 프로젝트 README의 발췌입니다." }, "Agents": { "metaTitle": "에이전트 마켓플레이스", @@ -132,7 +172,7 @@ "categoryFilter": "카테고리:", "sortLabel": "정렬:", "sortLatest": "최신순", - "sortStars": "별점순", + "sortStars": "스타순", "sortPopular": "인기순", "sortCalls": "호출 많은 순", "sortRating": "평점순", @@ -145,11 +185,24 @@ "emptyCta": "첫 번째 에이전트를 게시하세요 →", "prev": "← 이전", "next": "다음 →", - "pageOf": "{total}페이지 중 {page}페이지" + "pageOf": "{total}페이지 중 {page}페이지", + "filtersTitle": "필터", + "filtersWithCount": "필터 ({count})", + "activeFiltersLabel": "적용된 필터:", + "removeFilter": "필터 제거: {label}", + "clearAll": "모두 지우기", + "kindLabel": "유형", + "scenarioGroup": "시나리오", + "protocolGroup": "프로토콜", + "pricingGroup": "가격", + "categoryGroup": "카테고리", + "searchChip": "검색: {query}", + "resultsLabel": "결과", + "paginationLabel": "페이지 이동", + "goToPage": "{page}페이지로 이동", + "showingRange": "{total}개 중 {from}–{to}개 표시" }, "Components": { - "homeSearchPlaceholder": "이름, 설명, 작성자로 스킬 검색...", - "homeSearchButton": "검색", "agentFree": "무료", "agentPerCall": "{price}/호출", "agentPerTask": "{price}/작업", @@ -163,7 +216,11 @@ "agentSelfHost": "셀프 호스팅", "agentSkills": "{count, plural, other {스킬 #개}}", "skillBy": "{author} 제작", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "호출 {count, number}회", + "healthOk": "응답함", + "healthDegraded": "불안정", + "healthDown": "응답 없음" }, "Install": { "metaTitle": "Claude Code, Codex, OpenCode에 설치", @@ -178,7 +235,7 @@ "heroSubtitle": "Claude Code, Codex, OpenCode에 TakoAPI 스킬을 추가하세요 — 그러면 터미널에서 바로 하나의 통합 게이트웨이로 모든 AI 에이전트를 검색하고 호출할 수 있습니다.", "whatTitle": "설치 프로그램이 하는 일", "whatWritesSkill": "네임스페이스가 지정된 단일 TakoAPI 스킬을 각 에이전트 고유의 스킬 디렉터리에 작성합니다 — 공유 설정 파일은 절대 수정하지 않습니다.", - "whatTeachesAgent": "에이전트가 HTTP를 통해 TakoAPI 레지스트리와 게이트웨이를 호출하도록 가르칩니다 — 추가 의존성도, 실행할 서버도 없습니다.", + "whatTeachesAgent": "에이전트가 HTTP로 TakoAPI 레지스트리와 게이트웨이를 호출하도록 가르칩니다 — 추가 의존성도, 실행할 서버도 없습니다.", "whatMcpServer": "호스팅된 도구가 더 좋으신가요? 대신 TakoAPI MCP 서버를 등록하세요 — claude mcp add --transport http takoapi https://takoapi.com/mcp — search_agents, get_agent, search_skills, invoke_agent를 네이티브 MCP 도구로 노출합니다.", "whatIdempotent": "멱등성을 가지며(다시 실행해도 안전) --uninstall로 완전히 되돌릴 수 있습니다.", "whatApiKey": "종량제 게이트웨이를 사용하려면 대시보드에서 API 키를 만들고 TAKO_KEY로 내보내세요.", @@ -196,7 +253,7 @@ "howToUseHeading": "사용 방법", "claudeCodeUse": "자동으로 로드됩니다 — Claude에게 TakoAPI에서 에이전트를 찾아달라고 요청하기만 하면 됩니다.", "codexUse": "$takoapi로 호출하거나 /skills 메뉴에서 선택하세요.", - "opencodeUse": "/takoapi 를 실행하거나 @takoapi로 넘기세요.", + "opencodeUse": "/takoapi ''를 실행하거나 @takoapi로 넘기세요.", "mcpHeading": "MCP 서버 — 호스팅된 도구, 설치할 것 없음", "claudeCodeMcpHint": "하나의 명령. invoke_agent를 활성화하려면 --header \"Authorization: Bearer $TAKO_KEY\"를 추가하세요.", "codexMcpHint": "~/.codex/config.toml에 추가", @@ -212,7 +269,7 @@ "backToSkills": "스킬로 돌아가기", "by": "{author} 제작", "downloads": "다운로드", - "stars": "별점", + "stars": "스타", "views": "조회수", "likes": "{count, plural, other {좋아요 #개}}", "brief": "요약", @@ -226,25 +283,30 @@ "requirements": "요구 사항", "installation": "설치", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClaw Chat (대화에 붙여넣으세요)", "links": "링크", "github": "GitHub", - "copyGithubUrl": "GitHub URL 복사", "clawHub": "ClawHub", - "skillNotFound": "스킬을 찾을 수 없습니다" + "codingAgent": "코딩 에이전트", + "agentPrompt": "{url}에서 스킬을 설치해줘", + "installHintRegistry": "CLI 명령을 실행하거나, 프롬프트를 Claude Code, Cursor, Codex 등 사용하는 코딩 에이전트에 붙여넣으세요.", + "installHintRepo": "이 스킬은 ClawHub 레지스트리에 게시되지 않았습니다. 프롬프트를 코딩 에이전트(Claude Code, Cursor, Codex)에 붙여넣으면 소스 저장소에서 바로 스킬을 설치합니다.", + "installUnavailable": "이 스킬에는 아직 게시된 설치 소스가 없습니다.", + "openRepository": "저장소 열기", + "signInToLike": "로그인하면 이 스킬에 좋아요를 누를 수 있습니다", + "downloadsSource": "clawskills.sh가 보고한 다운로드 수" }, "Skills": { "metaTitle": "스킬 둘러보기", - "metaDescription": "수십 개의 카테고리에 걸친 수천 개의 코딩 에이전트용 OpenClaw 스킬을 둘러보세요 — 검색, 필터링, 트렌딩 리더보드를 제공합니다.", + "metaDescription": "수십 개의 카테고리에 걸친 코딩 에이전트용 OpenClaw 스킬 수천 개를 둘러보세요 — 검색, 필터링, 트렌딩 순위표를 제공합니다.", "ogTitle": "스킬 둘러보기 — {siteName}", - "ogDescription": "코딩 에이전트용 수천 개의 OpenClaw 스킬.", + "ogDescription": "코딩 에이전트용 OpenClaw 스킬 수천 개.", "heading": "스킬 둘러보기", "searchLabel": "검색: \"{query}\"", "count": "스킬 {count, number}개 검색됨", "sortLatest": "최신순", "sortPopular": "인기순", "sortViews": "조회수순", - "sortStars": "별점순", + "sortStars": "스타순", "agentAll": "전체 에이전트", "agentClaudeCode": "Claude Code", "agentCursor": "Cursor", @@ -257,20 +319,39 @@ "all": "전체", "emptyTitle": "스킬을 찾을 수 없습니다", "emptyHint": "검색어나 필터를 조정해 보세요", - "pageOf": "{total}페이지 중 {page}페이지" + "pageOf": "{total}페이지 중 {page}페이지", + "metaTitleSearch": "\"{query}\"와 일치하는 스킬", + "metaTitlePaged": "스킬 둘러보기 — {page}페이지", + "searchInputLabel": "스킬 검색", + "searchPlaceholder": "이름, 설명, 작성자로 스킬 검색", + "searchButton": "검색", + "agentFilter": "에이전트", + "agentZed": "Zed", + "sourceFilter": "출처", + "sourceGithub": "GitHub", + "sourceCommunity": "커뮤니티", + "sourceCurated": "큐레이션", + "categoryFilter": "카테고리", + "sortLabel": "정렬", + "sortRelevance": "관련도순", + "clearFilters": "필터 지우기", + "paginationLabel": "스킬 목록 페이지", + "goToPage": "{page}페이지로 이동", + "prev": "이전", + "next": "다음" }, "Trending": { "metaTitle": "트렌딩 스킬", - "metaDescription": "지금 가장 많이 다운로드되고 좋아요를 받은 OpenClaw 스킬 — 코딩 에이전트가 설치 중인 스킬의 실시간 리더보드입니다.", + "metaDescription": "다운로드와 좋아요가 가장 많은 OpenClaw 스킬 — clawskills.sh가 공개한 다운로드 수로 만든 순위표이며 매일 갱신됩니다.", "ogTitle": "트렌딩 스킬 — {siteName}", - "ogDescription": "가장 인기 있는 OpenClaw 스킬의 실시간 리더보드.", + "ogDescription": "매일 갱신되는 인기 OpenClaw 스킬 순위표.", "title": "트렌딩 스킬", - "subtitle": "커뮤니티가 선정한 가장 인기 있는 OpenClaw 스킬을 만나보세요", + "subtitle": "커뮤니티가 매긴 순위로 가장 인기 있는 OpenClaw 스킬을 만나보세요", "searchPlaceholder": "트렌딩 스킬 검색…", "categories": "카테고리", "allCategories": "전체 카테고리", "downloads": "다운로드", - "stars": "별점", + "stars": "스타", "likes": "좋아요", "views": "조회수", "skill": "스킬", @@ -281,7 +362,13 @@ "byAuthor": "{author} 제작", "showing": "{total, number}개 중 {from, number}–{to, number}개 표시", "prev": "이전", - "next": "다음" + "next": "다음", + "provenanceNote": "다운로드와 스타 수는 상위 카탈로그(clawskills.sh와 GitHub)에서 가져와 매일 밤 갱신되며, TakoAPI를 통한 설치 수를 나타내지 않습니다. 좋아요와 조회수는 이곳에서 집계합니다.", + "search": "검색", + "sortLabel": "정렬 기준", + "rank": "순위", + "goToPage": "{page}페이지로 이동", + "paginationLabel": "순위표 페이지" }, "Admin": { "accessDenied": "접근 거부됨", @@ -335,8 +422,8 @@ "actionReject": "거부", "feature": "추천 설정", "unfeature": "추천 해제", - "scenariosEditorHint": "场景 시나리오 — 点击切换 / 클릭하여 지정", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "시나리오 — 클릭하여 지정하거나 해제", + "scenarioOption": "{name}", "selectedCount": "{count, number}개 선택됨", "rejectReasonPrompt": "거부 사유 (선택 사항):", "deleteAgentConfirm": "이 에이전트를 영구적으로 삭제하시겠습니까?", @@ -398,9 +485,9 @@ "noCategoriesFound": "카테고리를 찾을 수 없습니다. 위에서 추가하세요.", "deleting": "...", "cannotDeleteHasSkills": "삭제할 수 없음: 스킬이 있습니다", - "categoriesTotal": "{count, plural, other {카테고리 #개}} 전체", + "categoriesTotal": "전체 카테고리 {count, number}개", "usersTitle": "사용자", - "usersTotal": "{count, plural, other {사용자 #명}} 전체", + "usersTotal": "전체 사용자 {count, number}명", "searchUsersPlaceholder": "이름 또는 이메일로 검색...", "colEmail": "이메일", "colRole": "역할", @@ -417,7 +504,7 @@ "demoteToUser": "사용자로 강등", "promoteToAdmin": "관리자로 승격", "logsTitle": "관리자 로그", - "logsTotal": "{count, plural, other {항목 #개}} 전체", + "logsTotal": "전체 항목 {count, number}개", "colTimestamp": "타임스탬프", "colAdmin": "관리자", "colAction": "작업", @@ -436,7 +523,46 @@ "targetSkill": "스킬", "targetAgent": "에이전트", "targetCategory": "카테고리", - "targetUser": "사용자" + "targetUser": "사용자", + "adminNavLabel": "관리자 섹션", + "retry": "다시 시도", + "close": "닫기", + "prevPage": "이전 페이지", + "nextPage": "다음 페이지", + "sectionAgents": "에이전트", + "sectionSkills": "스킬 및 커뮤니티", + "statTotalAgents": "전체 에이전트", + "statPendingAgents": "대기 중 에이전트", + "statHostedAgents": "호스팅", + "statProjectAgents": "프로젝트", + "pendingAgentsTitle": "검토 대기 중인 에이전트", + "noPendingAgents": "검토를 기다리는 에이전트가 없습니다.", + "kindHosted": "호스팅", + "kindProject": "프로젝트", + "filterByStatus": "상태로 필터", + "filterDisabled": "비활성", + "actionDisable": "비활성화", + "actionEnable": "다시 활성화", + "disableAgentTitle": "에이전트 비활성화", + "disableAgentConfirm": "{name}은(는) 다시 활성화할 때까지 공개 카탈로그에서 숨겨집니다. 삭제되는 것은 없습니다.", + "deleteAgentTitle": "에이전트 삭제", + "deleteAgentConfirmNamed": "정말로 {name}을(를) 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다.", + "rejectAgentTitle": "에이전트 거부", + "rejectReasonLabel": "사유 (선택 사항, 게시자에게 표시됩니다)", + "rejectReasonPlaceholder": "예: 샘플 작업에서 엔드포인트가 500을 반환함", + "healthOk": "정상", + "healthDegraded": "불안정", + "healthDown": "다운", + "healthUnknown": "미확인", + "healthCheckedAt": "마지막 상태 점검: {date}", + "healthNeverChecked": "기록된 상태 점검이 없습니다", + "batchDeleteTitle": "스킬 삭제", + "selectAllRows": "모든 행 선택", + "selectRow": "{name} 선택", + "editSkillNamed": "스킬 {name} 편집", + "deleteSkillNamed": "스킬 {name} 삭제", + "editCategoryNamed": "카테고리 {name} 편집", + "deleteCategoryNamed": "카테고리 {name} 삭제" }, "Auth": { "signInTitle": "TakoAPI에 로그인", @@ -462,12 +588,26 @@ "noAccount": "계정이 없으신가요?", "signUpLink": "가입", "haveAccount": "이미 계정이 있으신가요?", - "signInLink": "로그인" + "signInLink": "로그인", + "forgotPassword": "비밀번호를 잊으셨나요?", + "forgotTitle": "비밀번호 재설정", + "forgotSubtitle": "새 비밀번호를 설정할 링크를 이메일로 보내드립니다", + "sendResetLink": "재설정 링크 보내기", + "sendingResetLink": "보내는 중...", + "resetLinkSent": "해당 주소로 등록된 계정이 있다면 비밀번호 재설정 링크를 보냈습니다. 받은편지함을 확인하세요.", + "backToSignIn": "로그인으로 돌아가기", + "resetTitle": "새 비밀번호 설정", + "resetSubtitle": "계정에 사용할 새 비밀번호를 입력하세요", + "newPasswordHint": "새 비밀번호 (최소 8자)", + "updatePassword": "비밀번호 변경", + "updatingPassword": "변경 중...", + "passwordUpdated": "비밀번호가 변경되었습니다. 로그인 화면으로 이동합니다...", + "resetLinkInvalid": "이 비밀번호 재설정 링크는 유효하지 않거나 만료되었습니다.", + "requestNewLink": "새 링크 요청" }, "Dashboard": { "title": "개발자 대시보드", "signInPrompt": "API 키를 관리하고 사용량을 확인하려면 로그인하세요.", - "signIn": "로그인", "description": "API 키를 관리하고 게이트웨이 사용량을 모니터링하세요. 하나의 키, 하나의 청구서, 모든 에이전트.", "apiKeys": "API 키", "createKey": "키 생성", @@ -480,8 +620,8 @@ "created": "{date} 생성됨", "lastUsed": "마지막 사용 {date}", "neverUsed": "사용된 적 없음", - "revoke": "취소", - "revokeConfirm": "이 키를 취소하시겠습니까? 이 키를 사용하는 앱이 작동을 멈춥니다.", + "revoke": "해지", + "revokeConfirm": "이 키를 해지하시겠습니까? 이 키를 사용하는 앱이 작동을 멈춥니다.", "quickstart": "빠른 시작", "usage": "사용량", "totalCalls": "전체 호출", @@ -497,7 +637,6 @@ "billing": "크레딧 및 결제", "creditBalance": "크레딧 잔액", "totalSpend": "총 사용액", - "topUpComingSoon": "현재 크레딧은 플랫폼에서 충전합니다. PayPal 셀프 충전은 곧 제공될 예정입니다.", "topUpLabel": "크레딧 충전", "topUpButton": "PayPal로 충전", "topUpRedirecting": "이동 중…", @@ -513,35 +652,65 @@ "ledgerDebit": "호출 요금", "ledgerPayout": "정산", "ledgerRefund": "환불", - "ledgerAdjust": "조정" + "ledgerAdjust": "조정", + "onboardingTitle": "첫 호출 성공시키기", + "onboardingSubtitle": "에이전트의 응답을 받기까지 세 단계.", + "stepKeyTitle": "API 키 만들기", + "stepKeyBody": "키 하나로 TakoAPI의 모든 에이전트를 호출합니다. 시크릿은 생성 직후 한 번만 표시됩니다.", + "stepCallTitle": "첫 호출 보내기", + "stepCallBody": "아래 샘플을 복사하고 TAKO_KEY에 키를 설정한 뒤 실행하세요. 몇 초 안에 사용량에 호출이 표시됩니다.", + "stepInstallTitle": "코딩 에이전트에 설치하기", + "stepInstallBody": "Claude Code, Codex, OpenCode가 TakoAPI로 에이전트를 찾고 호출하도록 설정하세요.", + "stepInstallLink": "설치 가이드 열기", + "stepOptional": "선택", + "stepDone": "완료", + "stepTodo": "아직 완료되지 않음", + "keyNameLabel": "키 이름", + "keyNamePlaceholder": "게이트웨이 키", + "createKeyError": "키를 만들지 못했습니다. 다시 시도해 주세요.", + "revokeNamed": "{name} 해지", + "quickstartHint": "위의 키를 TAKO_KEY에 설정한 뒤 슬러그로 원하는 에이전트를 호출하세요.", + "creditRule": "무료 에이전트는 비용이 없고, 유료 에이전트는 선불 크레딧에서 차감되며, 잔액이 0 미만이 되는 호출은 HTTP 402로 거부됩니다.", + "topUpUnavailable": "셀프 충전은 아직 제공되지 않습니다 — 당분간 크레딧은 플랫폼에서 추가합니다. 위 잔액은 어느 쪽이든 실시간입니다.", + "dismiss": "닫기", + "loadError": "일부 대시보드 데이터를 불러오지 못했습니다.", + "retry": "다시 시도", + "recentCallsCaption": "가장 최근 게이트웨이 호출", + "chartTitle": "최근 {days}일간 일별 호출 수", + "chartTotal": "총 {count, number}회", + "chartAria": "최근 {days}일간 일별 게이트웨이 호출 막대 차트, 총 {count, number}회.", + "chartDay": "{date}: 호출 {calls, number}회, 실패 {errors, number}회" }, "Profile": { - "title": "내 프로필", - "signInPrompt": "프로필을 보려면 로그인하세요.", - "signIn": "로그인", - "submissions": "제출", + "title": "내 등록 항목", + "signInPrompt": "게시한 에이전트와 스킬을 보려면 로그인하세요.", "likes": "좋아요", "views": "조회수", - "statValue": "{count, number}", - "mySubmissions": "내 제출", - "submitNew": "새로 제출", "tab_all": "전체", - "tab_approved": "승인됨", - "tab_pending": "대기 중", - "tab_rejected": "거부됨", - "tabCount": "{count, number}", - "emptyTab": "이 탭에는 제출 항목이 없습니다", + "emptyTab": "아직 스킬이 없습니다.", "submitFirst": "첫 스킬을 제출하세요", - "statusApproved": "승인됨", - "statusPending": "검토 대기 중", + "statusApproved": "공개됨", + "statusPending": "검토 중", "statusRejected": "거부됨", - "reviewerNote": "검토자 메모:" + "reviewerNote": "검토자 메모:", + "publishAgent": "에이전트 게시", + "submitSkill": "스킬 제출", + "statAgents": "에이전트", + "statCalls": "게이트웨이 호출", + "statSkills": "스킬", + "tabAgents": "에이전트", + "tabSkills": "스킬", + "statusDisabled": "비활성", + "kindHosted": "호출 가능", + "kindProject": "오픈소스", + "noAgents": "아직 에이전트가 없습니다.", + "publishFirstAgent": "첫 에이전트를 게시하세요", + "myListings": "내 등록 항목" }, "Submit": { "title": "스킬 제출", "subtitle": "OpenClaw 스킬을 커뮤니티와 공유하세요", "signInPrompt": "스킬을 제출하려면 로그인하세요.", - "signIn": "로그인", "skillSource": "스킬 출처", "githubUrl": "GitHub URL", "clawSkillsUrl": "ClawSkills.sh URL", @@ -571,8 +740,15 @@ "submitting": "제출 중...", "enterUrlFirst": "먼저 URL을 입력하세요", "autoFillFailed": "자동 채우기에 실패했습니다", - "submitFailed": "제출에 실패했습니다", - "somethingWentWrong": "문제가 발생했습니다" + "somethingWentWrong": "문제가 발생했습니다", + "successTitle": "제출 완료 🎉", + "pendingReview": "{name}이(가) 검토 대기 중입니다.", + "nextStepsTitle": "다음에 일어나는 일", + "nextStepReview": "검토자가 저장소와 설명을 확인합니다.", + "nextStepEmail": "검토가 끝나면 이메일로 알려드립니다.", + "nextStepProfile": "내 등록 항목 페이지에서 현재 상태와 검토자 메모를 언제든 확인할 수 있습니다.", + "viewMyListings": "내 등록 항목 보기", + "backToSkills": "← 스킬로 돌아가기" }, "SubmitAgent": { "title": "에이전트 게시", @@ -609,9 +785,26 @@ "submitFailed": "제출에 실패했습니다", "somethingWentWrong": "문제가 발생했습니다", "successTitle": "제출 완료 🎉", - "pendingReview": "{name}이(가) 이제 검토 대기 중입니다.", + "pendingReview": "{name}이(가) 검토 대기 중입니다.", "successDetail": "AgentCard를 검증한 뒤 승인되면 마켓플레이스에 게시합니다.", - "backToMarketplace": "← 마켓플레이스로 돌아가기" + "backToMarketplace": "← 마켓플레이스로 돌아가기", + "checkCard": "카드 확인", + "checking": "확인 중…", + "enterCardUrlFirst": "먼저 AgentCard URL을 입력하세요.", + "previewTitle": "이렇게 등록됩니다", + "previewNoDescription": "카드에 설명이 없습니다.", + "previewEndpoint": "엔드포인트", + "previewCapabilities": "기능", + "previewSkillCount": "{count, plural, =0 {공개된 기능 없음} other {기능 #개}}", + "previewSupports": "지원", + "previewStreaming": "스트리밍", + "previewPush": "푸시 알림", + "previewHint": "아직 저장되지 않았습니다 — 아래에서 게시하면 검토 요청이 전송됩니다.", + "nextStepsTitle": "다음에 일어나는 일", + "nextStepReview": "검토자가 AgentCard와 엔드포인트의 응답 여부를 확인합니다.", + "nextStepEmail": "검토가 끝나면 이메일로 알려드립니다.", + "nextStepProfile": "내 등록 항목 페이지에서 현재 상태와 검토자 메모를 언제든 확인할 수 있습니다.", + "viewMyListings": "내 등록 항목 보기" }, "AgentEngagement": { "yourRating": "내 평가", @@ -619,28 +812,44 @@ "noRatings": "아직 평가 없음", "bookmark": "저장", "bookmarked": "저장됨", - "signInToRate": "로그인하면 평가하거나 저장할 수 있습니다" + "signInToRate": "로그인하면 이 에이전트를 평가하거나 저장할 수 있습니다", + "rateStars": "{count, plural, other {별점 #점 주기}}" }, "Bookmarks": { "title": "저장한 에이전트", "subtitle": "북마크한 에이전트입니다.", "empty": "아직 북마크가 없습니다.", "browseAgents": "에이전트 둘러보기", - "signInPrompt": "로그인하면 북마크를 볼 수 있습니다.", - "signIn": "로그인" + "signInPrompt": "로그인하면 북마크를 볼 수 있습니다." }, "ScenarioPage": { "metaTitle": "{scenario} AI 에이전트", - "metaDescription": "{scenario}용 AI 에이전트를 하나의 통합 API로 발견하고 호출하세요 — TakoAPI의 개방형 A2A AgentCard로 기술됩니다.", - "heroSubtitle": "{scenario}용 AI 에이전트, 하나의 API로 호출.", + "metaDescription": "{scenario}용 AI 에이전트를 하나의 통합 API로 검색하고 호출하세요 — TakoAPI의 개방형 A2A AgentCard로 기술됩니다.", + "heroSubtitle": "{scenario}용 AI 에이전트, 하나의 통합 API로 호출할 수 있습니다.", "empty": "이 시나리오에는 아직 에이전트가 없습니다.", "browseAll": "모든 에이전트 보기", "back": "← 모든 시나리오", "breadcrumbScenarios": "시나리오", - "indexTitle": "시나리오별 AI 에이전트 찾기", - "indexDescription": "용도별로 AI 에이전트를 찾으세요 — 프런트엔드, 투자, 마케팅, 지원 등 모두 하나의 API로 호출 가능합니다.", - "indexHeroTitle": "시나리오별 보기", - "indexHeroSubtitle": "해야 할 일에 맞는 AI 에이전트를 찾으세요." + "indexTitle": "시나리오별 AI 에이전트 둘러보기", + "indexDescription": "용도별로 AI 에이전트를 찾으세요 — 프런트엔드, 투자, 마케팅, 지원 등 모두 하나의 API로 호출할 수 있습니다.", + "indexHeroTitle": "시나리오별 둘러보기", + "indexHeroSubtitle": "해야 할 일에 맞는 AI 에이전트를 찾으세요.", + "indexPopularHeading": "선택지가 넉넉한 시나리오", + "indexPopularHint": "비교해 볼 에이전트가 풍부한 시나리오입니다.", + "indexSparseHeading": "이제 막 시작한 시나리오", + "indexSparseHint": "아직 에이전트가 몇 개뿐입니다 — 첫 주자가 되기 좋습니다.", + "indexSparseCta": "이 중 하나에 에이전트를 게시하세요 →", + "metaTitlePaged": "{scenario} AI 에이전트 — {page}페이지", + "resultsLabel": "{scenario} 에이전트", + "paginationLabel": "페이지 이동", + "showingRange": "{total}개 중 {from}–{to}개 표시", + "prev": "← 이전", + "next": "다음 →", + "goToPage": "{page}페이지로 이동", + "ctaTitle": "{scenario} 에이전트를 알고 계신가요?", + "ctaBody": "이 시나리오는 아직 비어 있습니다. 에이전트를 게시하면 {scenario}를 둘러보는 모두에게 여기 노출됩니다.", + "ctaButton": "에이전트 게시", + "relatedHeading": "다른 시나리오 살펴보기" }, "Badge": { "snippetTitle": "배지 추가", @@ -656,15 +865,160 @@ "step2": "등록 페이지의 배지 상자에서 Markdown을 복사하세요.", "step3Title": "README에 붙여넣기", "step3": "상단 근처에 추가하면 방문자와 검색엔진이 TakoAPI 페이지로 연결됩니다.", - "findListing": "등록 페이지 찾기" + "findListing": "등록 페이지 찾기", + "exampleAlt": "TakoAPI 배지" }, "Blog": { "title": "블로그", - "subtitle": "A2A, 에이전트 게이트웨이, TakoAPI로 빌드하는 방법에 대한 가이드.", + "subtitle": "A2A, 에이전트 게이트웨이, TakoAPI로 개발하는 방법에 대한 가이드.", "readMore": "더 읽기", "backToBlog": "← 전체 글", "minRead": "{min}분 분량", "published": "게시일", "faqTitle": "자주 묻는 질문" + }, + "NotFound": { + "title": "페이지를 찾을 수 없습니다", + "description": "링크가 오래되었거나 등록 항목이 삭제되었을 수 있습니다. 디렉터리를 둘러보거나 상단에서 검색해 보세요.", + "home": "홈", + "agents": "에이전트 둘러보기", + "skills": "스킬 둘러보기" + }, + "ErrorPage": { + "title": "문제가 발생했습니다", + "description": "이 페이지를 불러오지 못했습니다. 다시 시도하면 해결되는 경우가 많고, 그래도 안 되면 아래 참조 번호로 로그에서 원인을 찾을 수 있습니다.", + "reference": "참조: {id}", + "retry": "다시 시도", + "home": "홈으로 이동" + }, + "Consent": { + "title": "분석", + "description": "어떤 페이지가 유용한지 파악하기 위해 Google Analytics를 사용합니다. 동의하기 전에는 아무것도 로드되지 않고 쿠키도 설정되지 않습니다.", + "accept": "동의", + "decline": "거부" + }, + "Docs": { + "metaTitle": "API 문서", + "title": "TakoAPI API 레퍼런스", + "description": "API 키에서 실제 에이전트 호출까지: 인증, 디스커버리 엔드포인트, 통합 에이전트 게이트웨이, 호스팅 MCP 서버, 오류 코드, 요청 한도와 과금.", + "heroBadge": "개발자 문서", + "heroTitle": "TakoAPI API 레퍼런스", + "heroSubtitle": "키 하나로 모든 에이전트를. OpenAI 호환 클라이언트를 TakoAPI로 향하게 하고 슬러그로 에이전트를 호출하세요 — 카탈로그 전체는 키 없이도 둘러볼 수 있습니다.", + "heroKeyCta": "API 키 만들기", + "heroBrowseCta": "에이전트 둘러보기", + "tocTitle": "이 페이지의 내용", + "tocLabel": "목차", + "quickstartTitle": "빠른 시작", + "quickstartIntro": "아무것도 없는 상태에서 응답까지 세 단계. 이 페이지의 모든 요청은 실제 API를 호출하는 예시입니다.", + "quickstartStep1Title": "키 만들기", + "quickstartStep1": "로그인한 뒤 대시보드에서 API 키를 만드세요. 시크릿은 생성 시 한 번만 표시됩니다.", + "quickstartStep2Title": "환경 변수로 내보내기", + "quickstartStep2": "이 페이지의 모든 예제는 TAKO_KEY에서 키를 읽습니다.", + "quickstartStep3Title": "에이전트 호출하기", + "quickstartStep3": "model 값 또는 URL의 슬러그를 카탈로그에 있는 호스팅 에이전트로 지정하세요.", + "quickstartSamplesLabel": "빠른 시작 호출 예시", + "quickstartNote": "예시에 쓰인 슬러그는 실제 에이전트가 아니라 자리표시자입니다. 에이전트 페이지에서 실제 슬러그를 복사하세요. 호스팅으로 등록된 에이전트만 호출할 수 있습니다.", + "authenticationTitle": "인증", + "authenticationIntro": "게이트웨이 호출은 베어러 토큰으로 인증합니다. 디스커버리 엔드포인트는 익명이며 키가 전혀 필요 없습니다.", + "authHeaderNote": "Authorization 헤더 대신 x-api-key도 사용할 수 있습니다. 키는 항상 tako_live_로 시작합니다.", + "authWhereTitle": "키를 만드는 곳", + "authWhere": "대시보드에서 클라이언트나 환경마다 키를 하나씩 만들고, 더 이상 쓰지 않는 키는 해지하세요.", + "authOnceTitle": "시크릿은 한 번만 표시됩니다", + "authOnce": "키의 SHA-256 해시와 짧은 표시용 접두사만 저장하므로 잃어버린 시크릿은 복구할 수 없습니다 — 새 키를 만들고 기존 키를 해지하세요.", + "authRevokeTitle": "해지는 즉시 적용됩니다", + "authRevoke": "모든 호출이 데이터베이스에서 키를 다시 확인하고 아무것도 캐시하지 않으므로, 해지된 키는 바로 다음 요청부터 거부됩니다. 지난 사용 내역은 그대로 남습니다.", + "discoveryTitle": "디스커버리", + "discoveryIntro": "키가 필요 없는 공개 엔드포인트 네 개이며 캐시할 수 있습니다. 승인된 에이전트와 스킬만 반환됩니다.", + "endpointRegistrySummary": "에이전트 카탈로그 전체 — 모델에는 Markdown, 코드에는 JSON.", + "endpointRegistryNote": "format=json이 없으면 응답은 Markdown(text/markdown)이며, 호스팅 에이전트 표와 오픈소스 프로젝트 표로 나뉘고 모델 컨텍스트에 바로 넣을 수 있는 크기로 맞춰집니다 — 설치된 TakoAPI 스킬이 읽는 형식이 이것입니다. 두 형식 모두 limit으로 제한되므로, 전부 받았다고 가정하기 전에 truncated와 전체 개수를 확인하세요. 자유 텍스트 파라미터는 100자로 잘립니다.", + "paramRegistryFormat": "md(기본값) 또는 json.", + "paramRegistryQ": "에이전트 이름과 설명에 대한 자유 텍스트 검색.", + "paramRegistryCategory": "카테고리 슬러그.", + "paramRegistryProtocol": "이 프로토콜을 공개한 에이전트만 반환합니다.", + "paramRegistryKind": "HOSTED 에이전트는 게이트웨이로 호출할 수 있고, PROJECT 항목은 검색용으로만 등록된 오픈소스 저장소입니다.", + "paramRegistrySort": "정렬 순서. 기본값은 추천 우선, 그다음 호출 많은 순, 그다음 최신순이며, 프로젝트는 스타 많은 순입니다.", + "paramRegistryLimit": "반환할 전체 항목 수. 호스팅 에이전트를 먼저 채우고 그다음 프로젝트를 채웁니다.", + "endpointAgentDetailSummary": "에이전트 한 개의 공개 상세 정보 전체 — 공개된 A2A 스킬 포함.", + "endpointAgentDetailNote": "승인된 에이전트만 조회되고 그 외에는 404입니다. 검토 메모, 내부 ID, 게시자 아바타 같은 모더레이션 필드는 이 응답에 포함되지 않습니다. 엣지에서 60초간 캐시됩니다.", + "paramAgentDetailSlug": "경로 세그먼트. 레지스트리가 반환한 에이전트 슬러그.", + "endpointSkillsDirectorySummary": "OpenClaw 스킬 카탈로그. 기본 형식은 Markdown입니다.", + "endpointSkillsDirectoryNote": "레지스트리와 마찬가지로 Markdown이 기본입니다. 게시된 설치 명령이 없는 스킬은 install이 null이므로 github 링크를 사용하세요. downloads는 스킬 원본 페이지에서 수집한 서드파티 수치이며 TakoAPI 설치 수가 아닙니다.", + "paramSkillsDirFormat": "md(기본값) 또는 json.", + "paramSkillsDirQ": "스킬 이름과 설명에 대한 자유 텍스트 검색.", + "paramSkillsDirCategory": "카테고리 슬러그.", + "paramSkillsDirPage": "1부터 시작하는 페이지 번호.", + "paramSkillsDirLimit": "페이지당 결과 수.", + "endpointSkillsSearchSummary": "스킬 카탈로그 전문 검색. JSON만 지원합니다.", + "endpointSkillsSearchNote": "q는 필수입니다. 없으면 카탈로그 전체가 아니라 빈 목록이 반환됩니다. 각 결과는 readme와 reviewNote를 제외한 전체 카탈로그 행이며, 위 예시는 일부를 생략한 것입니다.", + "paramSkillsSearchQ": "검색어. 스킬 이름, 설명, 작성자와 대조합니다.", + "paramSkillsSearchCategory": "카테고리 슬러그.", + "paramSkillsSearchPage": "1부터 시작하는 페이지 번호.", + "paramSkillsSearchLimit": "페이지당 결과 수.", + "gatewayTitle": "게이트웨이", + "gatewayIntro": "같은 에이전트를 호출하는 세 가지 방법입니다. 셋 모두 키가 필요하고, 과금 방식이 같으며, 보통 모델 이름이 들어가는 자리에 에이전트 슬러그를 받습니다. 종류가 HOSTED인 에이전트만 호출할 수 있습니다.", + "endpointChatCompletionsSummary": "OpenAI 호환. OpenAI SDK를 /v1로 향하게 하고 model에 에이전트 슬러그를 넣으세요.", + "endpointChatCompletionsNote": "대화 전체가 전달됩니다. 턴마다 A2A 텍스트 파트 하나가 순서대로 전달되고, 각 파트에 OpenAI role이 붙습니다. usage는 항상 존재하지만 항상 0입니다 — 이 게이트웨이는 토큰을 세지 않으며, 에이전트가 LLM일 필요도 없습니다. 위의 SSE 형식을 쓰려면 stream: true를, 마지막 usage 청크까지 받으려면 stream_options.include_usage를 설정하세요. 스트리밍이 아닌 호출은 30초, 스트림은 120초 후 타임아웃됩니다.", + "endpointA2AMessageSummary": "네이티브 A2A 패스스루. 에이전트의 JSON-RPC 응답을 수정 없이 그대로 반환합니다.", + "endpointA2AMessageNote": "A2A message/send 호출로 중계됩니다. 텍스트 축약형을 보내거나, parts·taskId·contextId를 포함한 전체 A2A 메시지를 보내 멀티턴 작업을 이어가거나 파일·데이터 파트를 그대로 전달할 수 있습니다. 에이전트의 상태 코드와 본문은 손대지 않고 중계되며, 내용이 없는 요청은 과금 없이 400으로 거부됩니다. 30초 후 타임아웃됩니다.", + "endpointA2AStreamSummary": "같은 호출을 Server-Sent Events로 — 스트리밍을 지원하는 에이전트용입니다.", + "endpointA2AStreamNote": "에이전트의 A2A message/stream 프레임을 text/event-stream으로 그대로 중계합니다. 이 엔드포인트는 텍스트 축약형만 받습니다. 연결은 120초 후 끊기며, 스트림이 끝까지 도달했을 때만 과금됩니다 — 요청 한도 및 과금을 참고하세요.", + "labelQueryParams": "파라미터", + "labelRequest": "요청 본문", + "labelExample": "예시", + "labelResponse": "응답", + "labelStreaming": "stream: true일 때의 응답", + "labelA2AMessageForm": "전체 A2A 메시지 형식", + "labelAuthRequired": "키 필요", + "labelNoAuth": "키 불필요", + "labelBillable": "과금됨", + "labelFree": "무료", + "labelParamName": "이름", + "labelParamType": "유형", + "labelParamDesc": "설명", + "paramsTableCaption": "{path}의 파라미터", + "mcpTitle": "MCP 서버", + "mcpIntro": "TakoAPI는 {url}에서 Streamable HTTP 기반의 호스팅 무상태 Model Context Protocol 서버를 운영합니다. 한 번만 등록하면 코딩 에이전트가 카탈로그를 네이티브 도구로 검색하고 호출할 수 있습니다.", + "mcpRegisterTitle": "등록하기", + "mcpRegisterLabel": "MCP 등록 명령", + "mcpAuthNote": "읽기 도구 세 개는 익명으로 동작합니다. invoke_agent는 크레딧을 소모하므로 키가 필요합니다. Authorization 헤더(Claude Code), bearer_token_env_var(Codex), headers 블록(OpenCode) 중 하나로 전달하세요.", + "mcpToolsTitle": "도구", + "mcpToolsTableCaption": "TakoAPI MCP 서버가 제공하는 도구", + "mcpToolColumn": "도구", + "mcpToolAccessColumn": "접근", + "mcpToolWhatColumn": "하는 일", + "mcpToolKeyRequired": "키 필요", + "mcpToolAnonymous": "익명", + "mcpToolSearchAgents": "키워드, 카테고리, 프로토콜로 레지스트리를 검색합니다. 슬러그, 프로토콜, 가격을 반환합니다.", + "mcpToolGetAgent": "슬러그로 조회한 에이전트에 대해 알려진 모든 정보 — 기능, 프로토콜, 엔드포인트, 가격, 공개된 스킬.", + "mcpToolSearchSkills": "OpenClaw 스킬 카탈로그를 검색합니다. 검색어가 없으면 큐레이션된 인기 스킬을 반환합니다.", + "mcpToolInvokeAgent": "게이트웨이로 에이전트를 호출하고 응답을 반환합니다. 키가 필요하며 크레딧을 소모합니다.", + "mcpRateNote": "MCP 엔드포인트는 키의 게이트웨이 한도와 별개로 IP 주소당 분당 120요청으로 제한됩니다.", + "mcpInstallNote": "서버보다 스킬 파일이 편한가요? 설치 프로그램을 쓰면 Claude Code, Codex, OpenCode가 이 엔드포인트를 일반 HTTP로 호출하게 됩니다.", + "errorsTitle": "오류", + "errorsIntro": "게이트웨이 오류는 모두 JSON입니다. 다음은 게이트웨이가 직접 반환하는 상태 코드입니다.", + "errorsTableCaption": "게이트웨이가 반환하는 오류 상태", + "errorStatusColumn": "상태", + "errorCodeColumn": "코드", + "errorWhenColumn": "발생 시점", + "errorBodyColumn": "본문 예시", + "error400When": "슬러그가 호출 가능한 엔드포인트 없는 오픈소스 프로젝트를 가리키거나, 요청 본문 검증에 실패했거나, 모든 메시지 턴이 비어 있습니다.", + "error401When": "키가 없거나, 형식이 잘못되었거나, 알 수 없거나, 해지되었습니다.", + "error402When": "유료 에이전트이고 이 호출을 과금하면 잔액이 0 미만이 됩니다. 에이전트에 연결하기 전에 확인하므로 아무것도 차감되지 않습니다.", + "error404When": "해당 슬러그를 가진 승인된 에이전트가 없습니다.", + "error429When": "최근 60초 동안 키가 허용 한도를 초과했습니다. Retry-After에 윈도가 초기화될 때까지 남은 초가 담깁니다.", + "error502When": "에이전트가 응답하지 않았거나, 타임아웃되었거나, 스트림을 반환하지 않았거나, HTTP 200으로 JSON-RPC 오류를 보고했습니다. 과금되지 않습니다.", + "errorsOpenAiNote": "/v1/chat/completions에서는 같은 오류가 OpenAI 형식으로 전달되며 type, param, code가 항상 포함되므로, OpenAI SDK가 평소의 예외 타입을 발생시킵니다:", + "errorsPassthroughNote": "A2A message 엔드포인트는 에이전트가 반환한 상태 코드를 그대로 중계하므로, 그곳의 4xx는 TakoAPI가 아니라 에이전트가 낸 것일 수 있습니다.", + "limitsTitle": "요청 한도 및 과금", + "limitsRateTitle": "요청 한도", + "limitsRate": "게이트웨이는 API 키당 60초에 120요청을 허용합니다. 이 윈도는 모든 서버 인스턴스가 공유하고 IP 주소가 아니라 키 자체를 기준으로 하므로, 부하가 커져도 느슨해지지 않고 공유 NAT 뒤에서도 빡빡해지지 않습니다.", + "limitsRateOverride": "키마다 자체 한도를 가질 수 있으며, 그 키에 한해 기본값을 대체합니다. 한도를 넘으면 초 단위 Retry-After 헤더와 함께 429가 반환됩니다.", + "limitsBillingTitle": "과금", + "limitsBillingFree": "가격이 FREE인 에이전트는 비용이 없으며, 단가가 없거나 0인 에이전트도 마찬가지입니다. 게이트웨이가 임의로 요금을 만들어 내지 않습니다.", + "limitsBillingPriced": "유료 에이전트는 호출 한 번마다 단가만큼 선불 크레딧에서 차감합니다. 토큰 단위 측정은 없으므로, 호출이 얼마나 오래 걸리든 1단위입니다.", + "limitsBillingPreflight": "잔액은 에이전트에 연결하기 전에 확인합니다. 과금 시 잔액이 0 미만이 되면 호출은 402로 거부되고 에이전트는 호출되지 않습니다 — 마이너스 잔액은 없습니다.", + "limitsBillingFailures": "실패한 호출은 과금되지 않습니다. 에이전트의 4xx·5xx, 타임아웃, 연결할 수 없는 엔드포인트, JSON-RPC 오류는 모두 시도만 기록되고 요금은 청구되지 않습니다.", + "limitsBillingStreams": "스트림은 완료되었을 때만 과금됩니다. 중단되거나, 타임아웃되거나, 도중에 실패하거나, 사용자가 먼저 연결을 끊은 스트림은 기록만 되고 과금되지 않습니다.", + "limitsBillingLedger": "모든 과금은 그 원인이 된 호출에 대해 변경 불가능한 원장 항목을 남깁니다. 잔액, 원장, 호출별 사용량은 모두 대시보드에서 확인할 수 있습니다." } } diff --git a/messages/pt.json b/messages/pt.json index 49d35fb97..f7cfbf8f4 100644 --- a/messages/pt.json +++ b/messages/pt.json @@ -7,60 +7,78 @@ "label": "Idioma" }, "Header": { - "searchPlaceholder": "Buscar agentes...", "agents": "Agentes", "skills": "Skills", - "trending": "Em alta", - "publish": "Publicar", - "publishAgent": "Publicar agente", + "publishAgent": "Publicar um agente", "account": "Conta", - "mySkills": "Minhas skills", - "myBookmarks": "Salvos", - "apiUsage": "API e uso", + "myBookmarks": "Agentes salvos", + "apiUsage": "Chaves de API e uso", "adminPanel": "Painel admin", "signIn": "Entrar", - "signOut": "Sair" + "signOut": "Sair", + "mainNav": "Principal", + "skipToContent": "Pular para o conteúdo", + "openMenu": "Abrir menu", + "closeMenu": "Fechar menu", + "projects": "Projetos", + "install": "Instalar", + "getApiKey": "Obter chave de API", + "search": "Buscar", + "searchScope": "Buscar em", + "scopeAgents": "Agentes", + "scopeProjects": "Projetos", + "scopeSkills": "Skills", + "searchAgentsPlaceholder": "Buscar agentes pelo que eles fazem…", + "searchProjectsPlaceholder": "Buscar projetos de agentes open-source…", + "searchSkillsPlaceholder": "Buscar skills para o seu agente de código…", + "myListings": "Minhas publicações", + "docs": "Documentação" }, "Footer": { - "tagline": "Uma API para acessar todos os agentes — além de milhares de skills do OpenClaw para o seu agente de código.", + "tagline": "Uma API para acessar todos os agentes — descubra, invoque e meça qualquer agente por um único endpoint.", "agentRegistry": "Registro de agentes:", "platform": "Plataforma", "resources": "Recursos", - "installInAgent": "Instale no seu agente", - "browseAgents": "Navegar pelos agentes", + "installInAgent": "Instale no seu agente de código", + "browseAgents": "Explorar agentes", "publishAnAgent": "Publicar um agente", - "developerDashboard": "Painel do desenvolvedor", - "browseSkills": "Navegar pelas skills", - "awesomeList": "Awesome List", + "developerDashboard": "Chaves de API e uso", + "browseSkills": "Explorar skills", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Feito para a comunidade OpenClaw.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. Licença MIT.", + "blog": "Blog", + "badge": "Selo para README", + "scenarios": "Explorar por cenário", + "a2aSpec": "Protocolo A2A", + "github": "GitHub", + "docs": "Documentação da API" }, "Home": { "heroTitle": "Uma API para acessar todos os agentes", - "heroSubtitle": "Descubra e invoque {agents, number} agentes de IA por meio de uma API unificada — além de {skills, number} skills para o seu agente de código.", - "browseAgents": "Navegar pelos agentes", - "publishAnAgent": "Publicar um agente", - "agentRegistry": "Registro de agentes:", - "browseByScenario": "Navegar por cenário", - "allAgents": "Todos os agentes", + "heroSubtitle": "Descubra agentes de IA e invoque qualquer um deles por um único endpoint, com uma única chave. Nativo em A2A, compatível com OpenAI, medido por invocação.", + "browseAgents": "Explorar agentes", + "browseByScenario": "Explore pelo que você precisa resolver", "featuredAgents": "Agentes em destaque", "browseAll": "Ver todos", "popularProjects": "Projetos open-source populares", "selfHostable": "{count, number} com self-hosting", - "skillTitle": "Skill TakoAPI para OpenClaw", - "official": "Oficial", - "skillDescription": "Busque, instale e gerencie skills do OpenClaw por meio de uma conversa natural — com a tecnologia do marketplace TakoAPI.", - "viewOnGithub": "Ver no GitHub", - "askAgent": "ou peça ao seu agente: \"Instale a skill do TakoAPI\"", - "usingCodingAgent": "Usa Claude Code, Codex ou OpenCode?", - "oneCommandInstall": "Instalação com um comando", - "mustHaveSkills": "Skills indispensáveis", - "viewRankings": "Ver rankings", - "categories": "Categorias", - "viewAll": "Ver tudo", - "moreCategories": "{count, plural, one {+# categoria} other {+# categorias}}", - "latestSkills": "Skills mais recentes" + "categories": "Categorias:", + "moreCategories": "{count, plural, one {+# categoria} many {+# de categorias} other {+# categorias}}", + "getApiKey": "Obter uma chave de API", + "catalogSummary": "{agents, number} agentes invocáveis · {projects, number} projetos open-source · {skills, number} skills para agentes de código", + "quickstartLabel": "30 segundos até a sua primeira invocação", + "quickstartNote": "Prefere trabalhar pelo terminal? Instale o TakoAPI no seu agente de código.", + "allScenarios": "Todos os cenários", + "callableNow": "{count, number} invocáveis pelo gateway", + "installTitle": "Use o TakoAPI a partir do seu agente de código", + "installDescription": "Um comando ensina o Claude Code, o Codex ou o OpenCode a buscar no diretório e invocar agentes por você — ou registre o servidor MCP hospedado.", + "installCta": "Guia de instalação", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Skills para o seu agente de código", + "skillsSubtitle": "{count, number} skills do OpenClaw — as mais instaladas e as mais recentes.", + "quickstartTabDiscover": "1. Descobrir", + "quickstartTabCall": "2. Invocar", + "quickstartTabFromAgent": "3. Do seu agente" }, "Scenarios": { "frontend": "Frontend", @@ -85,8 +103,6 @@ "notFound": "Agente não encontrado", "backToMarketplace": "← Marketplace de agentes", "openSourceProject": "Projeto open-source", - "signedCard": "Card assinado", - "verifiedPublisher": "Publicador verificado", "by": "por {name}", "publisherOpenSource": "open-source", "publisherUnknown": "desconhecido", @@ -101,17 +117,41 @@ "noSkillsHosted": "Este agente não anunciou skills estruturadas em seu AgentCard.", "inputMode": "entrada: {mode}", "outputMode": "saída: {mode}", - "openSourceProjectHeading": "Projeto open-source", "openSourceProjectNote": "Faça o self-hosting ou explore o código. Não invocável pelo gateway do TakoAPI.", "viewOnGithub": "Ver no GitHub", - "integration": "Integração", "endpoint": "Endpoint", "agentCard": "AgentCard", "homepage": "Página inicial", "callThroughTakoApi": "Invoque pelo TakoAPI", - "gatewayPhase2": "Gateway unificado — a invocação chega na Fase 2.", "breadcrumbHome": "Início", - "breadcrumbMarketplace": "Marketplace de agentes" + "breadcrumbMarketplace": "Marketplace de agentes", + "gatewayHint": "Invoque com a sua chave de API — crie uma no painel.", + "healthOk": "Respondendo", + "healthDegraded": "Degradado", + "healthDown": "Sem resposta", + "healthCheckedAt": "Última verificação {time}", + "atAGlance": "Visão geral", + "protocols": "Protocolos", + "protocolsNone": "Não declarados", + "yes": "Sim", + "no": "Não", + "pricing": "Preço", + "health": "Saúde", + "healthUnknown": "Ainda não verificado", + "totalCalls": "Total de invocações", + "numberValue": "{count, number}", + "averageRating": "Avaliação média", + "ratingValue": "{rating} / 5 · {count, number} avaliações", + "notRated": "Ainda sem avaliações", + "callExamplesLabel": "Exemplos de invocação", + "repository": "Repositório", + "language": "Linguagem", + "license": "Licença", + "licenseNone": "Não especificada", + "lastPush": "Último push", + "topics": "Tópicos", + "readmeHeading": "Do README", + "readmeNote": "Trecho do README do projeto no GitHub." }, "Agents": { "metaTitle": "Marketplace de agentes", @@ -140,16 +180,29 @@ "pricingPerCall": "por invocação", "pricingPerTask": "por tarefa", "pricingPerToken": "Por 1k tokens", - "count": "{count, plural, =0 {Nenhum agente} one {# agente} other {# agentes}}", + "count": "{count, plural, =0 {Nenhum agente} one {# agente} many {# de agentes} other {# agentes}}", "emptyTitle": "Nenhum agente corresponde aos seus filtros ainda.", "emptyCta": "Publique o primeiro →", "prev": "← Anterior", - "next": "Próximo →", - "pageOf": "Página {page} de {total}" + "next": "Próxima →", + "pageOf": "Página {page} de {total}", + "filtersTitle": "Filtros", + "filtersWithCount": "Filtros ({count})", + "activeFiltersLabel": "Filtros ativos:", + "removeFilter": "Remover filtro: {label}", + "clearAll": "Limpar tudo", + "kindLabel": "Tipo", + "scenarioGroup": "Cenário", + "protocolGroup": "Protocolo", + "pricingGroup": "Preço", + "categoryGroup": "Categoria", + "searchChip": "Busca: {query}", + "resultsLabel": "Resultados", + "paginationLabel": "Paginação", + "goToPage": "Ir para a página {page}", + "showingRange": "Exibindo {from}–{to} de {total}" }, "Components": { - "homeSearchPlaceholder": "Buscar skills por nome, descrição ou autor...", - "homeSearchButton": "Buscar", "agentFree": "Grátis", "agentPerCall": "{price}/invocação", "agentPerTask": "{price}/tarefa", @@ -161,9 +214,13 @@ "agentBadgeStreaming": "streaming", "agentOpenSource": "open-source", "agentSelfHost": "self-hosting", - "agentSkills": "{count, plural, one {# skill} other {# skills}}", + "agentSkills": "{count, plural, one {# skill} many {# de skills} other {# skills}}", "skillBy": "por {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} invocações", + "healthOk": "Respondendo", + "healthDegraded": "Degradado", + "healthDown": "Sem resposta" }, "Install": { "metaTitle": "Instale no Claude Code, Codex e OpenCode", @@ -196,7 +253,7 @@ "howToUseHeading": "Como usar", "claudeCodeUse": "Carrega automaticamente — basta pedir ao Claude para encontrar um agente no TakoAPI.", "codexUse": "Invoque com $takoapi ou escolha no menu /skills.", - "opencodeUse": "Execute /takoapi ou passe para @takoapi.", + "opencodeUse": "Execute /takoapi '' ou passe para @takoapi.", "mcpHeading": "Servidor MCP — ferramentas hospedadas, nada para instalar", "claudeCodeMcpHint": "Um comando. Adicione --header \"Authorization: Bearer $TAKO_KEY\" para habilitar invoke_agent.", "codexMcpHint": "Adicione a ~/.codex/config.toml", @@ -214,7 +271,7 @@ "downloads": "Downloads", "stars": "Estrelas", "views": "Visualizações", - "likes": "{count, plural, one {# curtida} other {# curtidas}}", + "likes": "{count, plural, one {# curtida} many {# de curtidas} other {# curtidas}}", "brief": "Resumo", "description": "Descrição", "whatThisSkillDoes": "O que esta skill faz", @@ -226,19 +283,24 @@ "requirements": "Requisitos", "installation": "Instalação", "clawHubCli": "CLI do ClawHub", - "openClawChat": "OpenClaw Chat (cole isto na sua conversa)", "links": "Links", "github": "GitHub", - "copyGithubUrl": "Copiar URL do GitHub", "clawHub": "ClawHub", - "skillNotFound": "Skill não encontrada" + "codingAgent": "Agente de código", + "agentPrompt": "Instale a skill a partir de {url}", + "installHintRegistry": "Execute o comando da CLI ou cole o prompt no Claude Code, Cursor, Codex ou no agente de código que você usa.", + "installHintRepo": "Esta skill não está publicada no registro do ClawHub. Cole o prompt no seu agente de código — Claude Code, Cursor, Codex — e ele instalará a skill direto do repositório de origem.", + "installUnavailable": "Ainda não há uma fonte de instalação publicada para esta skill.", + "openRepository": "Abrir o repositório", + "signInToLike": "Entre para curtir esta skill", + "downloadsSource": "Downloads informados pelo clawskills.sh" }, "Skills": { - "metaTitle": "Navegar pelas skills", + "metaTitle": "Explorar skills", "metaDescription": "Navegue por milhares de skills do OpenClaw para o seu agente de código em dezenas de categorias — com busca, filtros e um ranking de tendências.", - "ogTitle": "Navegar pelas skills — {siteName}", + "ogTitle": "Explorar skills — {siteName}", "ogDescription": "Milhares de skills do OpenClaw para o seu agente de código.", - "heading": "Navegar pelas skills", + "heading": "Explorar skills", "searchLabel": "Busca: \"{query}\"", "count": "{count, number} skills encontradas", "sortLatest": "Mais recentes", @@ -257,13 +319,32 @@ "all": "Todas", "emptyTitle": "Nenhuma skill encontrada", "emptyHint": "Tente ajustar sua busca ou filtros", - "pageOf": "Página {page} de {total}" + "pageOf": "Página {page} de {total}", + "metaTitleSearch": "Skills para \"{query}\"", + "metaTitlePaged": "Explorar skills — página {page}", + "searchInputLabel": "Buscar skills", + "searchPlaceholder": "Buscar skills por nome, descrição ou autor", + "searchButton": "Buscar", + "agentFilter": "Agente", + "agentZed": "Zed", + "sourceFilter": "Origem", + "sourceGithub": "GitHub", + "sourceCommunity": "Comunidade", + "sourceCurated": "Curadoria", + "categoryFilter": "Categoria", + "sortLabel": "Ordenar", + "sortRelevance": "Mais relevantes", + "clearFilters": "Limpar filtros", + "paginationLabel": "Páginas da lista de skills", + "goToPage": "Ir para a página {page}", + "prev": "Anterior", + "next": "Próxima" }, "Trending": { "metaTitle": "Skills em alta", - "metaDescription": "As skills do OpenClaw mais baixadas e mais curtidas agora — um ranking ao vivo do que os agentes de código estão instalando.", + "metaDescription": "As skills OpenClaw mais baixadas e mais curtidas — um ranking baseado nos downloads informados pelo clawskills.sh, atualizado diariamente.", "ogTitle": "Skills em alta — {siteName}", - "ogDescription": "Um ranking ao vivo das skills do OpenClaw mais populares.", + "ogDescription": "Um ranking atualizado diariamente das skills OpenClaw mais populares.", "title": "Skills em alta", "subtitle": "Descubra as skills do OpenClaw mais populares, ranqueadas pela comunidade", "searchPlaceholder": "Buscar skills em alta…", @@ -274,14 +355,20 @@ "likes": "Curtidas", "views": "Visualizações", "skill": "Skill", - "count": "{count, plural, =0 {Nenhuma skill} one {# skill} other {# skills}}", - "countInCategory": "{count, plural, =0 {Nenhuma skill} one {# skill} other {# skills}} em {category}", + "count": "{count, plural, =0 {Nenhuma skill} one {# skill} many {# de skills} other {# skills}}", + "countInCategory": "{count, plural, =0 {Nenhuma skill} one {# skill} many {# de skills} other {# skills}} em {category}", "emptyTitle": "Nenhuma skill encontrada", "emptySubtitle": "Tente ajustar sua busca ou filtros", "byAuthor": "por {author}", "showing": "Exibindo {from, number}–{to, number} de {total, number}", "prev": "Anterior", - "next": "Próximo" + "next": "Próxima", + "provenanceNote": "As contagens de downloads e estrelas vêm do catálogo de origem — clawskills.sh e GitHub — e são atualizadas todas as noites; elas não medem instalações feitas pelo TakoAPI. Curtidas e visualizações são contadas aqui.", + "search": "Buscar", + "sortLabel": "Ordenar por", + "rank": "Posição", + "goToPage": "Ir para a página {page}", + "paginationLabel": "Páginas do ranking" }, "Admin": { "accessDenied": "Acesso negado", @@ -335,8 +422,8 @@ "actionReject": "Rejeitar", "feature": "Destacar", "unfeature": "Remover destaque", - "scenariosEditorHint": "场景 Cenários — 点击切换 / clique para atribuir", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Cenários — clique para atribuir ou remover", + "scenarioOption": "{name}", "selectedCount": "{count, number} selecionados", "rejectReasonPrompt": "Motivo da rejeição (opcional):", "deleteAgentConfirm": "Excluir este agente permanentemente?", @@ -398,9 +485,9 @@ "noCategoriesFound": "Nenhuma categoria encontrada. Adicione uma acima.", "deleting": "...", "cannotDeleteHasSkills": "Não é possível excluir: possui skills", - "categoriesTotal": "{count, number} {count, plural, one {categoria} other {categorias}} no total", + "categoriesTotal": "{count, number} {count, plural, one {categoria} many {de categorias} other {categorias}} no total", "usersTitle": "Usuários", - "usersTotal": "{count, number} {count, plural, one {usuário} other {usuários}} no total", + "usersTotal": "{count, number} {count, plural, one {usuário} many {de usuários} other {usuários}} no total", "searchUsersPlaceholder": "Buscar por nome ou e-mail...", "colEmail": "E-mail", "colRole": "Função", @@ -417,7 +504,7 @@ "demoteToUser": "Rebaixar para usuário", "promoteToAdmin": "Promover a admin", "logsTitle": "Logs admin", - "logsTotal": "{count, number} {count, plural, one {entrada} other {entradas}} no total", + "logsTotal": "{count, number} {count, plural, one {entrada} many {de entradas} other {entradas}} no total", "colTimestamp": "Data/hora", "colAdmin": "Admin", "colAction": "Ação", @@ -436,7 +523,46 @@ "targetSkill": "skill", "targetAgent": "agente", "targetCategory": "categoria", - "targetUser": "usuário" + "targetUser": "usuário", + "adminNavLabel": "Seções do admin", + "retry": "Tentar novamente", + "close": "Fechar", + "prevPage": "Página anterior", + "nextPage": "Próxima página", + "sectionAgents": "Agentes", + "sectionSkills": "Skills e comunidade", + "statTotalAgents": "Total de agentes", + "statPendingAgents": "Agentes pendentes", + "statHostedAgents": "Hospedados", + "statProjectAgents": "Projetos", + "pendingAgentsTitle": "Agentes aguardando revisão", + "noPendingAgents": "Nenhum agente aguardando revisão.", + "kindHosted": "Hospedado", + "kindProject": "Projeto", + "filterByStatus": "Filtrar por status", + "filterDisabled": "Desativados", + "actionDisable": "Desativar", + "actionEnable": "Reativar", + "disableAgentTitle": "Desativar agente", + "disableAgentConfirm": "{name} ficará oculto do catálogo público até você reativá-lo. Nada é excluído.", + "deleteAgentTitle": "Excluir agente", + "deleteAgentConfirmNamed": "Tem certeza de que deseja excluir {name}? Esta ação não pode ser desfeita.", + "rejectAgentTitle": "Rejeitar agente", + "rejectReasonLabel": "Motivo (opcional, exibido a quem publicou)", + "rejectReasonPlaceholder": "ex.: o endpoint retorna 500 na tarefa de exemplo", + "healthOk": "Saudável", + "healthDegraded": "Degradado", + "healthDown": "Fora do ar", + "healthUnknown": "Não verificado", + "healthCheckedAt": "Última verificação de saúde: {date}", + "healthNeverChecked": "Nenhuma verificação de saúde registrada", + "batchDeleteTitle": "Excluir skills", + "selectAllRows": "Selecionar todas as linhas", + "selectRow": "Selecionar {name}", + "editSkillNamed": "Editar a skill {name}", + "deleteSkillNamed": "Excluir a skill {name}", + "editCategoryNamed": "Editar a categoria {name}", + "deleteCategoryNamed": "Excluir a categoria {name}" }, "Auth": { "signInTitle": "Entrar no TakoAPI", @@ -462,12 +588,26 @@ "noAccount": "Não tem uma conta?", "signUpLink": "Cadastre-se", "haveAccount": "Já tem uma conta?", - "signInLink": "Entrar" + "signInLink": "Entrar", + "forgotPassword": "Esqueceu sua senha?", + "forgotTitle": "Redefinir sua senha", + "forgotSubtitle": "Enviaremos por e-mail um link para você escolher uma nova", + "sendResetLink": "Enviar link de redefinição", + "sendingResetLink": "Enviando...", + "resetLinkSent": "Se esse endereço tiver uma conta, enviamos um link para redefinir a senha. Confira sua caixa de entrada.", + "backToSignIn": "Voltar ao login", + "resetTitle": "Escolha uma nova senha", + "resetSubtitle": "Digite uma nova senha para a sua conta", + "newPasswordHint": "Nova senha (mínimo de 8 caracteres)", + "updatePassword": "Atualizar senha", + "updatingPassword": "Atualizando...", + "passwordUpdated": "Sua senha foi atualizada. Redirecionando para o login...", + "resetLinkInvalid": "Este link de redefinição de senha é inválido ou expirou.", + "requestNewLink": "Solicitar um novo link" }, "Dashboard": { "title": "Painel do desenvolvedor", "signInPrompt": "Entre para gerenciar chaves de API e ver o uso.", - "signIn": "Entrar", "description": "Gerencie chaves de API e monitore o uso do gateway. Uma chave, uma fatura, qualquer agente.", "apiKeys": "Chaves de API", "createKey": "Criar chave", @@ -497,7 +637,6 @@ "billing": "Créditos e faturamento", "creditBalance": "Saldo de créditos", "totalSpend": "Gasto total", - "topUpComingSoon": "Por enquanto os créditos são adicionados pela plataforma; a recarga self-service via PayPal chega em breve.", "topUpLabel": "Adicionar créditos", "topUpButton": "Recarregar com PayPal", "topUpRedirecting": "Redirecionando…", @@ -510,44 +649,74 @@ "ledgerNote": "Nota", "ledgerTopUp": "Recarga", "ledgerTopUpFee": "Taxa de recarga", - "ledgerDebit": "Cobrança por chamada", + "ledgerDebit": "Cobrança por invocação", "ledgerPayout": "Repasse", "ledgerRefund": "Reembolso", - "ledgerAdjust": "Ajuste" + "ledgerAdjust": "Ajuste", + "onboardingTitle": "Faça a sua primeira invocação funcionar", + "onboardingSubtitle": "Três passos até um agente responder você.", + "stepKeyTitle": "Crie uma chave de API", + "stepKeyBody": "Uma chave invoca todos os agentes do TakoAPI. O segredo é exibido uma única vez, logo depois de criá-la.", + "stepCallTitle": "Faça a sua primeira invocação", + "stepCallBody": "Copie um exemplo abaixo, defina TAKO_KEY com a sua chave e execute. A invocação aparece em Uso em poucos segundos.", + "stepInstallTitle": "Instale no seu agente de código", + "stepInstallBody": "Ensine o Claude Code, o Codex ou o OpenCode a descobrir e invocar agentes pelo TakoAPI.", + "stepInstallLink": "Abrir o guia de instalação", + "stepOptional": "Opcional", + "stepDone": "Concluído", + "stepTodo": "Ainda não feito", + "keyNameLabel": "Nome da chave", + "keyNamePlaceholder": "Chave do gateway", + "createKeyError": "Não foi possível criar a chave. Tente novamente.", + "revokeNamed": "Revogar {name}", + "quickstartHint": "Defina TAKO_KEY com uma das chaves acima e invoque qualquer agente pelo slug.", + "creditRule": "Agentes gratuitos não custam nada; agentes pagos debitam do seu crédito pré-pago, e a invocação é recusada com HTTP 402 assim que o saldo ficaria negativo.", + "topUpUnavailable": "A recarga self-service ainda não está disponível — por enquanto, os créditos são adicionados pela plataforma. De todo modo, o saldo acima está sempre atualizado.", + "dismiss": "Dispensar", + "loadError": "Não foi possível carregar parte dos dados do painel.", + "retry": "Tentar novamente", + "recentCallsCaption": "As suas invocações mais recentes no gateway", + "chartTitle": "Invocações por dia, últimos {days} dias", + "chartTotal": "{count, number} no total", + "chartAria": "Gráfico de barras das invocações do gateway por dia nos últimos {days} dias, {count, number} invocações no total.", + "chartDay": "{date}: {calls, number} invocações, {errors, number} com falha" }, "Profile": { - "title": "Meu perfil", - "signInPrompt": "Entre para ver o seu perfil.", - "signIn": "Entrar", - "submissions": "Envios", + "title": "Minhas publicações", + "signInPrompt": "Entre para ver os agentes e as skills que você publicou.", "likes": "Curtidas", - "views": "Visualizações", - "statValue": "{count, number}", - "mySubmissions": "Meus envios", - "submitNew": "Enviar novo", + "views": "visualizações", "tab_all": "Todos", - "tab_approved": "Aprovados", - "tab_pending": "Pendentes", - "tab_rejected": "Rejeitados", - "tabCount": "{count, number}", - "emptyTab": "Nenhum envio nesta aba", + "emptyTab": "Ainda não há skills aqui.", "submitFirst": "Envie a sua primeira skill", - "statusApproved": "Aprovada", - "statusPending": "Aguardando revisão", + "statusApproved": "No ar", + "statusPending": "Em revisão", "statusRejected": "Rejeitada", - "reviewerNote": "Nota do revisor:" + "reviewerNote": "Nota do revisor:", + "publishAgent": "Publicar um agente", + "submitSkill": "Enviar uma skill", + "statAgents": "Agentes", + "statCalls": "Invocações no gateway", + "statSkills": "Skills", + "tabAgents": "Agentes", + "tabSkills": "Skills", + "statusDisabled": "Desativado", + "kindHosted": "Invocável", + "kindProject": "Open-source", + "noAgents": "Ainda não há agentes aqui.", + "publishFirstAgent": "Publique o seu primeiro agente", + "myListings": "Minhas publicações" }, "Submit": { "title": "Enviar uma skill", "subtitle": "Compartilhe a sua skill do OpenClaw com a comunidade", "signInPrompt": "Entre para enviar skills.", - "signIn": "Entrar", "skillSource": "Origem da skill", "githubUrl": "URL do GitHub", "clawSkillsUrl": "URL do ClawSkills.sh", "githubUrlPlaceholder": "https://github.com/username/repo", "clawSkillsUrlPlaceholder": "https://clawskills.sh/skills/author-skillname", - "autoFill": "Preencher automaticamente", + "autoFill": "Autopreencher", "fetching": "Buscando...", "filled": "Preenchido!", "skillName": "Nome da skill", @@ -559,7 +728,7 @@ "briefHelper": "- Resumo em uma linha", "briefPlaceholder": "Uma breve descrição do que a sua skill faz", "skillDetails": "Detalhes da skill", - "skillDetailsHelper": "- Preenchido automaticamente a partir do README", + "skillDetailsHelper": "- Preenchido a partir do README", "whatItDoes": "O que esta skill faz", "whatItDoesPlaceholder": "Explicação detalhada do que esta skill faz, seus recursos e capacidades...", "exampleWorkflow": "Exemplo de fluxo de trabalho", @@ -570,9 +739,16 @@ "submitSkill": "Enviar skill", "submitting": "Enviando...", "enterUrlFirst": "Insira uma URL primeiro", - "autoFillFailed": "Falha no preenchimento automático", - "submitFailed": "Falha ao enviar", - "somethingWentWrong": "Algo deu errado" + "autoFillFailed": "Falha no autopreenchimento", + "somethingWentWrong": "Algo deu errado", + "successTitle": "Enviado 🎉", + "pendingReview": "{name} está aguardando revisão.", + "nextStepsTitle": "O que acontece agora", + "nextStepReview": "Um revisor confere o repositório e a descrição.", + "nextStepEmail": "Avisaremos por e-mail assim que a revisão terminar.", + "nextStepProfile": "A sua página de publicações sempre mostra o status atual e qualquer nota do revisor.", + "viewMyListings": "Ver minhas publicações", + "backToSkills": "← Voltar às skills" }, "SubmitAgent": { "title": "Publicar um agente", @@ -611,7 +787,24 @@ "successTitle": "Enviado 🎉", "pendingReview": "{name} está aguardando revisão.", "successDetail": "Vamos verificar o AgentCard e publicá-lo no marketplace assim que for aprovado.", - "backToMarketplace": "← Voltar ao marketplace" + "backToMarketplace": "← Voltar ao marketplace", + "checkCard": "Verificar card", + "checking": "Verificando…", + "enterCardUrlFirst": "Informe primeiro a URL do AgentCard.", + "previewTitle": "É assim que a listagem ficaria", + "previewNoDescription": "O card não tem descrição.", + "previewEndpoint": "Endpoint", + "previewCapabilities": "Capacidades", + "previewSkillCount": "{count, plural, =0 {Nenhuma capacidade anunciada} one {# capacidade} many {# de capacidades} other {# capacidades}}", + "previewSupports": "Suporta", + "previewStreaming": "Streaming", + "previewPush": "Notificações push", + "previewHint": "Nada foi salvo ainda — publique abaixo para enviar para revisão.", + "nextStepsTitle": "O que acontece agora", + "nextStepReview": "Um revisor confere o AgentCard e se o seu endpoint responde.", + "nextStepEmail": "Avisaremos por e-mail assim que a revisão terminar.", + "nextStepProfile": "A sua página de publicações sempre mostra o status atual e qualquer nota do revisor.", + "viewMyListings": "Ver minhas publicações" }, "AgentEngagement": { "yourRating": "Sua avaliação", @@ -619,52 +812,213 @@ "noRatings": "Ainda sem avaliações", "bookmark": "Salvar", "bookmarked": "Salvo", - "signInToRate": "Entre para avaliar ou salvar este agente" + "signInToRate": "Entre para avaliar ou salvar este agente", + "rateStars": "Avaliar com {count, plural, one {# estrela} many {# de estrelas} other {# estrelas}}" }, "Bookmarks": { "title": "Agentes salvos", "subtitle": "Agentes que você salvou.", - "empty": "Ainda sem salvos.", + "empty": "Nenhum agente salvo ainda.", "browseAgents": "Explorar agentes", - "signInPrompt": "Entre para ver seus salvos.", - "signIn": "Entrar" + "signInPrompt": "Entre para ver os seus agentes salvos." }, "ScenarioPage": { "metaTitle": "Agentes de IA para {scenario}", - "metaDescription": "Descubra e invoque agentes de IA para {scenario} através de uma única API unificada, descritos por AgentCards A2A abertas no TakoAPI.", - "heroSubtitle": "Agentes de IA para {scenario}, invocáveis através de uma única API.", + "metaDescription": "Descubra e invoque agentes de IA para {scenario} por meio de uma API unificada — descritos por AgentCards A2A abertos no TakoAPI.", + "heroSubtitle": "Agentes de IA para {scenario}, invocáveis por uma API unificada.", "empty": "Ainda não há agentes neste cenário.", "browseAll": "Explorar todos os agentes", "back": "← Todos os cenários", "breadcrumbScenarios": "Cenários", "indexTitle": "Explore agentes de IA por cenário", - "indexDescription": "Encontre agentes de IA pelo uso: frontend, investimento, marketing, suporte e mais, todos invocáveis através de uma API.", + "indexDescription": "Encontre agentes de IA pelo uso que você faria deles — frontend, investimentos, marketing, suporte e mais — todos invocáveis por uma única API.", "indexHeroTitle": "Explorar por cenário", - "indexHeroSubtitle": "Encontre agentes de IA pela tarefa que precisa resolver." + "indexHeroSubtitle": "Encontre agentes de IA pela tarefa que você precisa resolver.", + "indexPopularHeading": "Bem cobertos", + "indexPopularHint": "Cenários com um catálogo amplo de agentes para comparar.", + "indexSparseHeading": "Começando agora", + "indexSparseHint": "Só um punhado de agentes até aqui — um bom lugar para ser o primeiro.", + "indexSparseCta": "Publique um agente em um destes →", + "metaTitlePaged": "Agentes de IA para {scenario} — página {page}", + "resultsLabel": "Agentes para {scenario}", + "paginationLabel": "Paginação", + "showingRange": "Exibindo {from}–{to} de {total}", + "prev": "← Anterior", + "next": "Próxima →", + "goToPage": "Ir para a página {page}", + "ctaTitle": "Conhece um agente para {scenario}?", + "ctaBody": "Este cenário ainda tem poucos agentes. Publique um e ele aparece aqui para todo mundo que explorar {scenario}.", + "ctaButton": "Publicar um agente", + "relatedHeading": "Explore outros cenários" }, "Badge": { - "snippetTitle": "Adicionar selo", + "snippetTitle": "Adicionar um selo", "copy": "Copiar", "copied": "Copiado", - "snippetHint": "Cole no seu README para criar um link para sua página no TakoAPI.", - "pageTitle": "Selo README do TakoAPI", - "pageIntro": "Mostre que seu projeto está listado no TakoAPI — um pequeno selo que liga à sua página e exibe sua contagem de estrelas em tempo real.", + "snippetHint": "Cole no seu README para criar um link de volta para a sua página no TakoAPI.", + "pageTitle": "Selo do TakoAPI para README", + "pageIntro": "Mostre que o seu projeto está listado no TakoAPI — um pequeno selo que leva à sua página e exibe a contagem de estrelas em tempo real.", "example": "Exemplo", - "step1Title": "Encontre sua página", - "step1": "Procure seu projeto no diretório de agentes.", + "step1Title": "Encontre a sua página", + "step1": "Procure o seu projeto no diretório de agentes.", "step2Title": "Copie o selo", "step2": "Na página da sua listagem, copie o Markdown da caixa do selo.", "step3Title": "Cole no seu README", - "step3": "Adicione perto do topo — visitantes e buscadores recebem um link para sua página no TakoAPI.", - "findListing": "Encontre sua página" + "step3": "Adicione perto do topo — visitantes e buscadores recebem um link para a sua página no TakoAPI.", + "findListing": "Encontre a sua página", + "exampleAlt": "Selo do TakoAPI" }, "Blog": { "title": "Blog", - "subtitle": "Guias sobre A2A, gateways de agentes e como construir com a TakoAPI.", + "subtitle": "Guias sobre A2A, gateways de agentes e como construir com o TakoAPI.", "readMore": "Ler mais", "backToBlog": "← Todos os artigos", "minRead": "{min} min de leitura", "published": "Publicado", "faqTitle": "Perguntas frequentes" + }, + "NotFound": { + "title": "Não encontramos essa página", + "description": "O link pode estar desatualizado ou a publicação pode ter sido removida. Tente o diretório ou use a busca no topo da página.", + "home": "Início", + "agents": "Explorar agentes", + "skills": "Explorar skills" + }, + "ErrorPage": { + "title": "Algo deu errado", + "description": "Esta página não carregou. Tentar de novo costuma resolver; se não resolver, a referência abaixo nos ajuda a encontrar o caso nos logs.", + "reference": "Referência: {id}", + "retry": "Tentar novamente", + "home": "Ir para o início" + }, + "Consent": { + "title": "Analytics", + "description": "Usamos o Google Analytics para saber quais páginas as pessoas acham úteis. Nada é carregado e nenhum cookie é definido sem a sua aceitação.", + "accept": "Aceitar", + "decline": "Recusar" + }, + "Docs": { + "metaTitle": "Documentação da API", + "title": "Referência da API do TakoAPI", + "description": "Da chave de API até uma invocação funcionando: autenticação, endpoints de descoberta, o gateway unificado de agentes, o servidor MCP hospedado, códigos de erro, limites de taxa e cobrança.", + "heroBadge": "Docs para desenvolvedores", + "heroTitle": "A referência da API do TakoAPI", + "heroSubtitle": "Uma chave, todos os agentes. Aponte qualquer cliente compatível com OpenAI para o TakoAPI e invoque um agente pelo slug — ou explore o catálogo inteiro sem chave nenhuma.", + "heroKeyCta": "Criar uma chave de API", + "heroBrowseCta": "Explorar agentes", + "tocTitle": "Nesta página", + "tocLabel": "Índice", + "quickstartTitle": "Início rápido", + "quickstartIntro": "Três passos do zero até uma resposta. Todas as requisições desta página são invocações reais contra a API em produção.", + "quickstartStep1Title": "Crie uma chave", + "quickstartStep1": "Entre e crie uma chave de API no seu painel. O segredo é exibido uma única vez, na criação.", + "quickstartStep2Title": "Exporte a chave", + "quickstartStep2": "Todos os exemplos desta página leem a chave de TAKO_KEY.", + "quickstartStep3Title": "Invoque um agente", + "quickstartStep3": "Defina o modelo — ou o slug na URL — como qualquer agente hospedado do catálogo.", + "quickstartSamplesLabel": "Exemplos de invocação do início rápido", + "quickstartNote": "O slug destes exemplos é um espaço reservado, não um agente real. Copie um slug real de qualquer página de agente; só é possível invocar agentes listados como hospedados.", + "authenticationTitle": "Autenticação", + "authenticationIntro": "As invocações do gateway são autenticadas com um bearer token. Os endpoints de descoberta são anônimos e não exigem chave alguma.", + "authHeaderNote": "x-api-key é aceito como alternativa ao cabeçalho Authorization. As chaves sempre começam com tako_live_.", + "authWhereTitle": "De onde vêm as chaves", + "authWhere": "Crie uma chave por cliente ou ambiente no seu painel e revogue as que você deixar de usar.", + "authOnceTitle": "O segredo é exibido uma única vez", + "authOnce": "Guardamos apenas um hash SHA-256 da chave e um prefixo curto para exibição, então um segredo perdido não pode ser recuperado — crie uma chave nova e revogue a antiga.", + "authRevokeTitle": "A revogação é imediata", + "authRevoke": "Cada invocação reverifica a chave no banco de dados e nada fica em cache, então uma chave revogada é rejeitada já na requisição seguinte. O uso anterior permanece no seu histórico.", + "discoveryTitle": "Descoberta", + "discoveryIntro": "Quatro endpoints públicos e cacheáveis que não exigem chave. Apenas agentes e skills aprovados são retornados.", + "endpointRegistrySummary": "O catálogo completo de agentes — Markdown para um modelo, JSON para código.", + "endpointRegistryNote": "Sem format=json, a resposta é Markdown (text/markdown), dividida em uma tabela de agentes hospedados e outra de projetos open-source, dimensionada para entrar direto no contexto de um modelo — é isso que a skill instalada do TakoAPI lê. Os dois formatos são limitados por limit, então verifique truncated e as contagens totais antes de supor que você tem tudo. Parâmetros de texto livre são truncados em 100 caracteres.", + "paramRegistryFormat": "md (padrão) ou json.", + "paramRegistryQ": "Busca em texto livre no nome e na descrição do agente.", + "paramRegistryCategory": "Slug da categoria.", + "paramRegistryProtocol": "Retorna apenas agentes que anunciam este protocolo.", + "paramRegistryKind": "Agentes HOSTED são invocáveis pelo gateway; entradas PROJECT são repositórios open-source listados apenas para descoberta.", + "paramRegistrySort": "Ordenação. O padrão é destaques primeiro, depois os mais invocados e então os mais recentes — e os mais estrelados no caso dos projetos.", + "paramRegistryLimit": "Total de entradas a retornar. Os agentes hospedados entram primeiro, depois os projetos.", + "endpointAgentDetailSummary": "Todos os detalhes públicos de um agente, incluindo as skills A2A que ele anuncia.", + "endpointAgentDetailNote": "Só agentes aprovados resolvem; qualquer outra coisa é 404. Campos de moderação — notas de revisão, ids internos e avatares de quem publicou — nunca fazem parte deste formato. Cache na borda por 60 segundos.", + "paramAgentDetailSlug": "Segmento de caminho. O slug do agente, como retornado pelo registro.", + "endpointSkillsDirectorySummary": "O catálogo de skills do OpenClaw, em Markdown por padrão.", + "endpointSkillsDirectoryNote": "Markdown em primeiro lugar, como no registro. install é null para skills sem comando de instalação publicado — nesses casos, use o link do github. downloads é um número de terceiros, extraído da própria listagem da skill, e não uma contagem de instalações do TakoAPI.", + "paramSkillsDirFormat": "md (padrão) ou json.", + "paramSkillsDirQ": "Busca em texto livre no nome e na descrição da skill.", + "paramSkillsDirCategory": "Slug da categoria.", + "paramSkillsDirPage": "Número da página, começando em 1.", + "paramSkillsDirLimit": "Resultados por página.", + "endpointSkillsSearchSummary": "Busca em texto completo no catálogo de skills, apenas em JSON.", + "endpointSkillsSearchNote": "q é obrigatório: sem ele, o endpoint retorna uma lista vazia em vez do catálogo inteiro. Cada resultado é a linha completa do catálogo menos readme e reviewNote; o exemplo acima está abreviado.", + "paramSkillsSearchQ": "Termo de busca. Corresponde ao nome, à descrição e ao autor da skill.", + "paramSkillsSearchCategory": "Slug da categoria.", + "paramSkillsSearchPage": "Número da página, começando em 1.", + "paramSkillsSearchLimit": "Resultados por página.", + "gatewayTitle": "Gateway", + "gatewayIntro": "Três formas de invocar o mesmo agente. As três exigem uma chave, cobram de forma idêntica e aceitam o slug do agente onde normalmente iria o nome de um modelo. Só agentes do tipo HOSTED podem ser invocados.", + "endpointChatCompletionsSummary": "Compatível com OpenAI. Aponte qualquer SDK da OpenAI para /v1 e defina model como o slug de um agente.", + "endpointChatCompletionsNote": "A conversa inteira é encaminhada: uma parte de texto A2A por turno, em ordem, com o papel da OpenAI associado a cada parte. usage está sempre presente, mas sempre zerado — nada neste gateway conta tokens, e um agente não precisa ser um LLM. Use stream: true para a forma SSE acima e stream_options.include_usage para receber também o bloco final de usage. Uma invocação sem streaming expira em 30 segundos; um stream, em 120.", + "endpointA2AMessageSummary": "Passagem A2A nativa. Retorna a própria resposta JSON-RPC do agente, sem modificações.", + "endpointA2AMessageNote": "Repassado como uma invocação A2A message/send. Envie o atalho de texto ou uma mensagem A2A completa — com parts, taskId e contextId — para continuar uma tarefa de múltiplos turnos ou repassar partes de arquivo e de dados. O código de status e o corpo do agente são repassados intactos, e uma requisição sem conteúdo é rejeitada com 400 em vez de cobrada. Expira em 30 segundos.", + "endpointA2AStreamSummary": "A mesma invocação sobre Server-Sent Events, para agentes que fazem streaming.", + "endpointA2AStreamNote": "Repassa os frames A2A message/stream do agente literalmente como text/event-stream. Este endpoint aceita apenas o atalho de texto. A conexão é encerrada após 120 segundos, e a invocação só é cobrada quando o stream chega ao fim — veja Limites de taxa e cobrança.", + "labelQueryParams": "Parâmetros", + "labelRequest": "Corpo da requisição", + "labelExample": "Exemplo", + "labelResponse": "Resposta", + "labelStreaming": "Resposta com stream: true", + "labelA2AMessageForm": "Forma completa da mensagem A2A", + "labelAuthRequired": "Exige chave", + "labelNoAuth": "Sem chave", + "labelBillable": "Cobrada", + "labelFree": "Grátis", + "labelParamName": "Nome", + "labelParamType": "Tipo", + "labelParamDesc": "Descrição", + "paramsTableCaption": "Parâmetros de {path}", + "mcpTitle": "Servidor MCP", + "mcpIntro": "O TakoAPI mantém um servidor Model Context Protocol hospedado e sem estado, sobre Streamable HTTP, em {url}. Registre-o uma vez e o seu agente de código passa a buscar e invocar o catálogo como ferramentas nativas.", + "mcpRegisterTitle": "Registre o servidor", + "mcpRegisterLabel": "Comandos de registro do MCP", + "mcpAuthNote": "As três ferramentas de leitura funcionam anonimamente. invoke_agent gasta crédito, então precisa da sua chave: passe-a no cabeçalho Authorization (Claude Code), como bearer_token_env_var (Codex) ou em um bloco headers (OpenCode).", + "mcpToolsTitle": "Ferramentas", + "mcpToolsTableCaption": "Ferramentas expostas pelo servidor MCP do TakoAPI", + "mcpToolColumn": "Ferramenta", + "mcpToolAccessColumn": "Acesso", + "mcpToolWhatColumn": "O que faz", + "mcpToolKeyRequired": "Exige chave", + "mcpToolAnonymous": "Anônimo", + "mcpToolSearchAgents": "Busca no registro por palavra-chave, categoria ou protocolo. Retorna slugs, protocolos e preços.", + "mcpToolGetAgent": "Tudo o que se sabe sobre um agente, pelo slug — capacidades, protocolos, endpoint, preço e skills anunciadas.", + "mcpToolSearchSkills": "Busca no catálogo de skills do OpenClaw. Sem consulta, retorna as principais skills selecionadas.", + "mcpToolInvokeAgent": "Invoca um agente pelo gateway e retorna a resposta dele. Exige uma chave e gasta crédito.", + "mcpRateNote": "O endpoint MCP é limitado a 120 requisições por minuto por endereço IP, separadamente da cota de gateway da sua chave.", + "mcpInstallNote": "Prefere um arquivo de skill a um servidor? O instalador ensina o Claude Code, o Codex e o OpenCode a invocar esses endpoints por HTTP puro.", + "errorsTitle": "Erros", + "errorsIntro": "Toda falha do gateway vem em JSON. Estes são os status que o próprio gateway retorna.", + "errorsTableCaption": "Status de erro retornados pelo gateway", + "errorStatusColumn": "Status", + "errorCodeColumn": "Código", + "errorWhenColumn": "Quando", + "errorBodyColumn": "Exemplo de corpo", + "error400When": "O slug aponta para um projeto open-source sem endpoint invocável, o corpo da requisição não passou na validação ou todos os turnos da mensagem estavam vazios.", + "error401When": "A chave está ausente, malformada, desconhecida ou revogada.", + "error402When": "O agente é pago e cobrar a invocação deixaria o seu saldo negativo. A verificação ocorre antes de o agente ser contatado, então nada é gasto.", + "error404When": "Nenhum agente aprovado tem esse slug.", + "error429When": "A chave excedeu a sua cota nos últimos 60 segundos. Retry-After informa quantos segundos faltam para a janela reiniciar.", + "error502When": "O agente não respondeu, expirou, não retornou stream ou reportou um erro JSON-RPC em um HTTP 200. Nunca é cobrado.", + "errorsOpenAiNote": "Em /v1/chat/completions, as mesmas falhas chegam no envelope da OpenAI — com type, param e code sempre presentes — então um SDK da OpenAI lança os tipos de exceção habituais:", + "errorsPassthroughNote": "Um status retornado pelo próprio agente é repassado sem alteração pelo endpoint A2A message, então um 4xx ali pode ser do agente, e não nosso.", + "limitsTitle": "Limites de taxa e cobrança", + "limitsRateTitle": "Limites de taxa", + "limitsRate": "O gateway permite 120 requisições a cada 60 segundos por chave de API. A janela é compartilhada entre todas as instâncias do servidor e é indexada pela própria chave, não pelo seu endereço IP, então ela não afrouxa sob carga nem aperta atrás de um NAT compartilhado.", + "limitsRateOverride": "Uma chave pode ter a própria cota, que substitui o padrão apenas para ela. Acima do limite, você recebe um 429 com o cabeçalho Retry-After, em segundos.", + "limitsBillingTitle": "Cobrança", + "limitsBillingFree": "Agentes marcados como FREE não custam nada — e o mesmo vale para qualquer agente cujo preço unitário esteja ausente ou seja zero. O gateway nunca inventa uma cobrança.", + "limitsBillingPriced": "Um agente pago debita do seu crédito pré-pago o preço unitário, uma vez por invocação. Não há medição por token, então uma invocação vale uma unidade, por mais longa que seja.", + "limitsBillingPreflight": "O seu saldo é verificado antes de o agente ser contatado. Se a cobrança deixasse você abaixo de zero, a invocação é recusada com 402 e o agente nunca é chamado — não existe saldo negativo.", + "limitsBillingFailures": "Invocações que falham não são cobradas. Um 4xx ou 5xx vindo do agente, um timeout, um endpoint inacessível ou um erro JSON-RPC registram a tentativa e não geram cobrança.", + "limitsBillingStreams": "Streams só são cobrados quando terminam. Um stream abortado, que expira, que falha no meio ou do qual você se desconecta antes do fim é registrado e não é cobrado.", + "limitsBillingLedger": "Toda cobrança grava um lançamento imutável no extrato, vinculado à invocação que a originou. Saldo, extrato e uso por invocação estão no seu painel." } } diff --git a/messages/ru.json b/messages/ru.json index 1be4c155b..c6875970a 100644 --- a/messages/ru.json +++ b/messages/ru.json @@ -1,66 +1,84 @@ { "Metadata": { "tagline": "Один API для доступа ко всем агентам", - "description": "Находите и вызывайте ИИ-агентов через единый API — OpenRouter для агентов. Просматривайте каталог из сотен open-source проектов агентов и тысяч навыков OpenClaw для вашего кодинг-агента." + "description": "Находите и вызывайте ИИ-агентов через единый API — OpenRouter для агентов. Каталог из сотен open-source проектов агентов и тысяч навыков OpenClaw для кодинг-агента." }, "LocaleSwitcher": { "label": "Язык" }, "Header": { - "searchPlaceholder": "Поиск агентов...", "agents": "Агенты", "skills": "Навыки", - "trending": "В тренде", - "publish": "Опубликовать", "publishAgent": "Опубликовать агента", "account": "Аккаунт", - "mySkills": "Мои навыки", - "myBookmarks": "Закладки", - "apiUsage": "API и использование", + "myBookmarks": "Сохранённые агенты", + "apiUsage": "API-ключи и использование", "adminPanel": "Панель администратора", "signIn": "Войти", - "signOut": "Выйти" + "signOut": "Выйти", + "mainNav": "Основное", + "skipToContent": "Перейти к содержимому", + "openMenu": "Открыть меню", + "closeMenu": "Закрыть меню", + "projects": "Проекты", + "install": "Установка", + "getApiKey": "Получить API-ключ", + "search": "Поиск", + "searchScope": "Искать в", + "scopeAgents": "Агенты", + "scopeProjects": "Проекты", + "scopeSkills": "Навыки", + "searchAgentsPlaceholder": "Поиск агентов по задаче…", + "searchProjectsPlaceholder": "Поиск open-source проектов агентов…", + "searchSkillsPlaceholder": "Поиск навыков для кодинг-агента…", + "myListings": "Мои публикации", + "docs": "Документация" }, "Footer": { - "tagline": "Один API для доступа ко всем агентам — плюс тысячи навыков OpenClaw для вашего кодинг-агента.", + "tagline": "Один API для доступа ко всем агентам — находите, вызывайте и тарифицируйте любого агента через один эндпоинт.", "agentRegistry": "Реестр агентов:", "platform": "Платформа", "resources": "Ресурсы", - "installInAgent": "Установить в вашего агента", + "installInAgent": "Установить в кодинг-агент", "browseAgents": "Просмотр агентов", "publishAnAgent": "Опубликовать агента", - "developerDashboard": "Панель разработчика", + "developerDashboard": "API-ключи и использование", "browseSkills": "Просмотр навыков", - "awesomeList": "Awesome-список", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Создано для сообщества OpenClaw.", - "blog": "Блог" + "copyright": "© {year} TakoAPI. Лицензия MIT.", + "blog": "Блог", + "badge": "README-бейдж", + "scenarios": "По сценариям", + "a2aSpec": "Протокол A2A", + "github": "GitHub", + "docs": "Документация API" }, "Home": { "heroTitle": "Один API для доступа ко всем агентам", - "heroSubtitle": "Находите и вызывайте {agents, number} ИИ-агентов через единый API — плюс {skills, number} навыков для вашего кодинг-агента.", + "heroSubtitle": "Находите ИИ-агентов и вызывайте любого из них через один эндпоинт одним ключом. Нативный A2A, совместимость с OpenAI, тарификация по вызовам.", "browseAgents": "Просмотр агентов", - "publishAnAgent": "Опубликовать агента", - "agentRegistry": "Реестр агентов:", - "browseByScenario": "По сценариям", - "allAgents": "Все агенты", + "browseByScenario": "По задаче, которую нужно решить", "featuredAgents": "Рекомендуемые агенты", "browseAll": "Смотреть все", "popularProjects": "Популярные open-source проекты", "selfHostable": "{count, number} с самостоятельным хостингом", - "skillTitle": "Навык TakoAPI для OpenClaw", - "official": "Официальный", - "skillDescription": "Ищите, устанавливайте и управляйте навыками OpenClaw в обычном диалоге — на базе маркетплейса TakoAPI.", - "viewOnGithub": "Открыть на GitHub", - "askAgent": "или попросите агента: «Установи навык TakoAPI»", - "usingCodingAgent": "Используете Claude Code, Codex или OpenCode?", - "oneCommandInstall": "Установка одной командой", - "mustHaveSkills": "Незаменимые навыки", - "viewRankings": "Смотреть рейтинг", - "categories": "Категории", - "viewAll": "Смотреть все", + "categories": "Категории:", "moreCategories": "{count, plural, one {+# категория} few {+# категории} many {+# категорий} other {+# категории}}", - "latestSkills": "Новые навыки" + "getApiKey": "Получить API-ключ", + "catalogSummary": "{agents, number} вызываемых агентов · {projects, number} open-source проектов · {skills, number} навыков для кодинг-агентов", + "quickstartLabel": "30 секунд до первого вызова", + "quickstartNote": "Удобнее работать из терминала? Установите TakoAPI в кодинг-агент.", + "allScenarios": "Все сценарии", + "callableNow": "{count, number} вызываются через шлюз", + "installTitle": "TakoAPI прямо из кодинг-агента", + "installDescription": "Одна команда научит Claude Code, Codex или OpenCode искать по каталогу и вызывать агентов — или подключите вместо этого размещённый MCP-сервер.", + "installCta": "Руководство по установке", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Навыки для кодинг-агента", + "skillsSubtitle": "{count, number} навыков OpenClaw — самые устанавливаемые и самые новые.", + "quickstartTabDiscover": "1. Поиск", + "quickstartTabCall": "2. Вызов", + "quickstartTabFromAgent": "3. Из вашего агента" }, "Scenarios": { "frontend": "Фронтенд", @@ -85,33 +103,55 @@ "notFound": "Агент не найден", "backToMarketplace": "← Маркетплейс агентов", "openSourceProject": "Open-source проект", - "signedCard": "Подписанная карточка", - "verifiedPublisher": "Проверенный издатель", "by": "от {name}", "publisherOpenSource": "open-source", "publisherUnknown": "неизвестно", "scenario": "Сценарий:", "stars": "{count, number} звёзд", "selfHost": "Свой хостинг", - "streaming": "Потоковая передача", + "streaming": "Стриминг", "pushNotifications": "Push-уведомления", "skills": "Навыки", "skillsCount": "({count, number})", - "noSkillsProject": "Open-source проект — изучите код и разверните его самостоятельно с GitHub.", - "noSkillsHosted": "Этот агент не объявил структурированные навыки в своей AgentCard.", + "noSkillsProject": "Open-source проект — изучите код и разверните его у себя с GitHub.", + "noSkillsHosted": "Агент не объявил структурированные навыки в своей AgentCard.", "inputMode": "вход: {mode}", "outputMode": "выход: {mode}", - "openSourceProjectHeading": "Open-source проект", - "openSourceProjectNote": "Разверните самостоятельно или изучите код. Недоступен для вызова через шлюз TakoAPI.", + "openSourceProjectNote": "Разверните у себя или изучите код. Через шлюз TakoAPI не вызывается.", "viewOnGithub": "Открыть на GitHub", - "integration": "Интеграция", "endpoint": "Endpoint", "agentCard": "AgentCard", - "homepage": "Главная страница", + "homepage": "Сайт", "callThroughTakoApi": "Вызвать через TakoAPI", - "gatewayPhase2": "Единый шлюз — вызов появится во второй фазе.", "breadcrumbHome": "Главная", - "breadcrumbMarketplace": "Маркетплейс агентов" + "breadcrumbMarketplace": "Маркетплейс агентов", + "gatewayHint": "Вызов выполняется с API-ключом — создайте его в панели.", + "healthOk": "Отвечает", + "healthDegraded": "С перебоями", + "healthDown": "Не отвечает", + "healthCheckedAt": "Последняя проверка: {time}", + "atAGlance": "Сводка", + "protocols": "Протоколы", + "protocolsNone": "Не указаны", + "yes": "Да", + "no": "Нет", + "pricing": "Тарификация", + "health": "Доступность", + "healthUnknown": "Ещё не проверялся", + "totalCalls": "Всего вызовов", + "numberValue": "{count, number}", + "averageRating": "Средняя оценка", + "ratingValue": "{rating} / 5 · оценок: {count, number}", + "notRated": "Ещё нет оценок", + "callExamplesLabel": "Примеры вызова", + "repository": "Репозиторий", + "language": "Язык", + "license": "Лицензия", + "licenseNone": "Не указана", + "lastPush": "Последний push", + "topics": "Темы", + "readmeHeading": "Из README", + "readmeNote": "Фрагмент README проекта на GitHub." }, "Agents": { "metaTitle": "Маркетплейс агентов", @@ -141,15 +181,28 @@ "pricingPerTask": "за задачу", "pricingPerToken": "За 1 тыс. токенов", "count": "{count, plural, =0 {Нет агентов} one {# агент} few {# агента} many {# агентов} other {# агента}}", - "emptyTitle": "Пока нет агентов, подходящих под ваши фильтры.", + "emptyTitle": "Под эти фильтры пока нет агентов.", "emptyCta": "Опубликуйте первого →", "prev": "← Назад", "next": "Вперёд →", - "pageOf": "Страница {page} из {total}" + "pageOf": "Страница {page} из {total}", + "filtersTitle": "Фильтры", + "filtersWithCount": "Фильтры ({count})", + "activeFiltersLabel": "Активные фильтры:", + "removeFilter": "Убрать фильтр: {label}", + "clearAll": "Сбросить все", + "kindLabel": "Тип", + "scenarioGroup": "Сценарий", + "protocolGroup": "Протокол", + "pricingGroup": "Тарификация", + "categoryGroup": "Категория", + "searchChip": "Поиск: {query}", + "resultsLabel": "Результаты", + "paginationLabel": "Постраничная навигация", + "goToPage": "Перейти на страницу {page}", + "showingRange": "Показаны {from}–{to} из {total}" }, "Components": { - "homeSearchPlaceholder": "Поиск навыков по названию, описанию или автору...", - "homeSearchButton": "Поиск", "agentFree": "Бесплатно", "agentPerCall": "{price}/вызов", "agentPerTask": "{price}/задача", @@ -158,52 +211,56 @@ "agentPriceFallbackTask": "задача", "agentPriceFallbackToken": "токен", "agentBadgeProject": "проект", - "agentBadgeStreaming": "потоковая передача", + "agentBadgeStreaming": "стриминг", "agentOpenSource": "open-source", "agentSelfHost": "свой хостинг", "agentSkills": "{count, plural, one {# навык} few {# навыка} many {# навыков} other {# навыка}}", "skillBy": "от {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "вызовов: {count, number}", + "healthOk": "Отвечает", + "healthDegraded": "С перебоями", + "healthDown": "Не отвечает" }, "Install": { "metaTitle": "Установка в Claude Code, Codex и OpenCode", "title": "Установите TakoAPI в Claude Code, Codex и OpenCode", - "description": "Одна команда устанавливает навык TakoAPI в ваш кодинг-агент — находите и вызывайте ИИ-агентов через единый шлюз и ищите в каталоге навыков OpenClaw. Работает с Claude Code, Codex и OpenCode.", + "description": "Одна команда устанавливает навык TakoAPI в кодинг-агент — находите и вызывайте ИИ-агентов через единый шлюз и ищите по каталогу навыков OpenClaw. Работает с Claude Code, Codex и OpenCode.", "stepRunInstallerName": "Запустите установщик", "stepRunInstallerText": "Выполните: curl -fsSL https://takoapi.com/install.sh | sh", - "stepUseAgentName": "Используйте TakoAPI из вашего агента", + "stepUseAgentName": "Используйте TakoAPI из своего агента", "stepUseAgentText": "Попросите кодинг-агента найти или вызвать ИИ-агента на TakoAPI.", "heroBadge": "Одна команда, три агента", - "heroTitle": "Установите TakoAPI в ваш кодинг-агент", + "heroTitle": "Установите TakoAPI в кодинг-агент", "heroSubtitle": "Добавьте навык TakoAPI в Claude Code, Codex или OpenCode — и находите и вызывайте любого ИИ-агента через единый шлюз прямо из терминала.", "whatTitle": "Что делает установщик", - "whatWritesSkill": "Записывает единый навык TakoAPI с собственным пространством имён в каталог навыков каждого агента — он никогда не редактирует ваши общие конфигурационные файлы.", - "whatTeachesAgent": "Обучает вашего агента обращаться к реестру TakoAPI и шлюзу по HTTP — без лишних зависимостей и без сервера, который нужно запускать.", - "whatMcpServer": "Предпочитаете размещённые инструменты? Зарегистрируйте MCP-сервер TakoAPI — claude mcp add --transport http takoapi https://takoapi.com/mcp — он предоставит search_agents, get_agent, search_skills и invoke_agent как нативные MCP-инструменты.", + "whatWritesSkill": "Записывает один навык TakoAPI с собственным пространством имён в каталог навыков каждого агента — общие конфигурационные файлы он не трогает.", + "whatTeachesAgent": "Учит агента обращаться к реестру TakoAPI и шлюзу по HTTP — без лишних зависимостей и без сервера, который нужно запускать.", + "whatMcpServer": "Предпочитаете размещённые инструменты? Зарегистрируйте вместо этого MCP-сервер TakoAPI — claude mcp add --transport http takoapi https://takoapi.com/mcp — он даёт search_agents, get_agent, search_skills и invoke_agent как нативные MCP-инструменты.", "whatIdempotent": "Идемпотентен (можно безопасно перезапускать) и полностью обратим через --uninstall.", "whatApiKey": "Для тарифицируемого шлюза создайте API-ключ в панели и экспортируйте его как TAKO_KEY.", "footnote": "На Windows или уже в Node? Используйте npx takoapi-install. Хотите сначала прочитать скрипт? Это обычный shell по адресу takoapi.com/install.sh." }, "InstallTabs": { - "universalNote": "Устанавливает навык TakoAPI в Claude Code, Codex и OpenCode — с автоопределением. Можно безопасно перезапускать, без root. На Windows или в Node используйте .", + "universalNote": "Устанавливает навык TakoAPI в Claude Code, Codex и OpenCode — с автоопределением. Можно безопасно перезапускать, root не нужен. На Windows или в Node используйте .", "tablistLabel": "Кодинг-агенты", "claudeCodeTagline": "CLI от Anthropic", "codexTagline": "CLI от OpenAI", "opencodeTagline": "opencode.ai", - "skillHeading": "Навык — обучает вашего агента вызывать TakoAPI", + "skillHeading": "Навык — учит вашего агента вызывать TakoAPI", "skillUniversalHint": "…или используйте универсальную команду выше — она охватывает и {name}.", "writesHeading": "Записывает", "howToUseHeading": "Как использовать", "claudeCodeUse": "Загружается автоматически — просто попросите Claude найти агента на TakoAPI.", "codexUse": "Вызовите через $takoapi или выберите в меню /skills.", - "opencodeUse": "Выполните /takoapi или передайте задачу @takoapi.", - "mcpHeading": "MCP-сервер — размещённые инструменты, ничего не нужно устанавливать", + "opencodeUse": "Выполните /takoapi '' или передайте задачу @takoapi.", + "mcpHeading": "MCP-сервер — размещённые инструменты, ставить нечего", "claudeCodeMcpHint": "Одна команда. Добавьте --header \"Authorization: Bearer $TAKO_KEY\", чтобы включить invoke_agent.", "codexMcpHint": "Добавьте в ~/.codex/config.toml", "opencodeMcpHint": "Объедините с ~/.config/opencode/opencode.json", "mcpToolsNote": "Добавляет инструменты: search_agents, get_agent, search_skills, invoke_agent.", "uninstallHeading": "Удаление", - "uninstallNote": "Удаляет только собственные файлы TakoAPI — никогда не затрагивает конфигурацию других ваших агентов.", + "uninstallNote": "Удаляет только собственные файлы TakoAPI — остальную конфигурацию агентов не трогает.", "copy": "Копировать", "copied": "Скопировано" }, @@ -226,24 +283,29 @@ "requirements": "Требования", "installation": "Установка", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClaw Chat (вставьте это в ваш диалог)", "links": "Ссылки", "github": "GitHub", - "copyGithubUrl": "Копировать URL GitHub", "clawHub": "ClawHub", - "skillNotFound": "Навык не найден" + "codingAgent": "Кодинг-агент", + "agentPrompt": "Установи навык из {url}", + "installHintRegistry": "Выполните команду CLI или вставьте промпт в Claude Code, Cursor, Codex — любой кодинг-агент, которым пользуетесь.", + "installHintRepo": "Этот навык не опубликован в реестре ClawHub. Вставьте промпт в кодинг-агент — Claude Code, Cursor, Codex — и он установит навык прямо из исходного репозитория.", + "installUnavailable": "Источник установки для этого навыка пока не опубликован.", + "openRepository": "Открыть репозиторий", + "signInToLike": "Войдите, чтобы поставить лайк навыку", + "downloadsSource": "Число загрузок по данным clawskills.sh" }, "Skills": { "metaTitle": "Просмотр навыков", - "metaDescription": "Просматривайте тысячи навыков OpenClaw для вашего кодинг-агента в десятках категорий — с поиском, фильтрами и таблицей лидеров трендов.", + "metaDescription": "Просматривайте тысячи навыков OpenClaw для кодинг-агента в десятках категорий — с поиском, фильтрами и рейтингом трендов.", "ogTitle": "Просмотр навыков — {siteName}", - "ogDescription": "Тысячи навыков OpenClaw для вашего кодинг-агента.", + "ogDescription": "Тысячи навыков OpenClaw для кодинг-агента.", "heading": "Просмотр навыков", "searchLabel": "Поиск: «{query}»", "count": "Найдено навыков: {count, number}", "sortLatest": "Новые", "sortPopular": "Популярные", - "sortViews": "Просматриваемые", + "sortViews": "По просмотрам", "sortStars": "По звёздам", "agentAll": "Все агенты", "agentClaudeCode": "Claude Code", @@ -256,16 +318,35 @@ "agentGeneric": "Универсальные", "all": "Все", "emptyTitle": "Навыки не найдены", - "emptyHint": "Попробуйте изменить поисковый запрос или фильтры", - "pageOf": "Страница {page} из {total}" + "emptyHint": "Попробуйте изменить запрос или фильтры", + "pageOf": "Страница {page} из {total}", + "metaTitleSearch": "Навыки по запросу «{query}»", + "metaTitlePaged": "Просмотр навыков — страница {page}", + "searchInputLabel": "Поиск навыков", + "searchPlaceholder": "Поиск навыков по названию, описанию или автору", + "searchButton": "Поиск", + "agentFilter": "Агент", + "agentZed": "Zed", + "sourceFilter": "Источник", + "sourceGithub": "GitHub", + "sourceCommunity": "Сообщество", + "sourceCurated": "Отобранные", + "categoryFilter": "Категория", + "sortLabel": "Сортировка", + "sortRelevance": "По релевантности", + "clearFilters": "Сбросить фильтры", + "paginationLabel": "Страницы каталога навыков", + "goToPage": "Перейти на страницу {page}", + "prev": "Назад", + "next": "Вперёд" }, "Trending": { "metaTitle": "Навыки в тренде", - "metaDescription": "Самые скачиваемые и популярные навыки OpenClaw прямо сейчас — живая таблица лидеров того, что устанавливают кодинг-агенты.", + "metaDescription": "Самые скачиваемые и залайканные навыки OpenClaw — рейтинг по числу загрузок с clawskills.sh, обновляется ежедневно.", "ogTitle": "Навыки в тренде — {siteName}", - "ogDescription": "Живая таблица лидеров самых популярных навыков OpenClaw.", + "ogDescription": "Ежедневный рейтинг самых популярных навыков OpenClaw.", "title": "Навыки в тренде", - "subtitle": "Откройте самые популярные навыки OpenClaw в рейтинге сообщества", + "subtitle": "Самые популярные навыки OpenClaw в рейтинге сообщества", "searchPlaceholder": "Поиск навыков в тренде…", "categories": "Категории", "allCategories": "Все категории", @@ -277,11 +358,17 @@ "count": "{count, plural, =0 {Нет навыков} one {# навык} few {# навыка} many {# навыков} other {# навыка}}", "countInCategory": "{count, plural, =0 {Нет навыков} one {# навык} few {# навыка} many {# навыков} other {# навыка}} в категории {category}", "emptyTitle": "Навыки не найдены", - "emptySubtitle": "Попробуйте изменить поисковый запрос или фильтры", + "emptySubtitle": "Попробуйте изменить запрос или фильтры", "byAuthor": "от {author}", "showing": "Показаны {from, number}–{to, number} из {total, number}", "prev": "Назад", - "next": "Вперёд" + "next": "Вперёд", + "provenanceNote": "Загрузки и звёзды приходят из внешнего каталога — clawskills.sh и GitHub — и обновляются каждую ночь; они не измеряют установки через TakoAPI. Лайки и просмотры считаются здесь.", + "search": "Поиск", + "sortLabel": "Сортировать по", + "rank": "Место", + "goToPage": "Перейти на страницу {page}", + "paginationLabel": "Страницы рейтинга" }, "Admin": { "accessDenied": "Доступ запрещён", @@ -319,7 +406,7 @@ "confirm": "Подтвердить", "preview": "Предпросмотр", "agentsTitle": "Агенты", - "agentsSubtitle": "Проверяйте и модерируйте отправленных агентов.", + "agentsSubtitle": "Проверка и модерация отправленных агентов.", "filterPending": "Ожидают", "filterApproved": "Одобренные", "filterRejected": "Отклонённые", @@ -327,7 +414,7 @@ "noAgentsInView": "В этом представлении нет агентов.", "unknownPublisher": "неизвестно", "uncategorized": "без категории", - "agentMetaSkills": "{count, number} навыков", + "agentMetaSkills": "навыков: {count, number}", "noScenarios": "нет сценариев", "reviewNoteLabel": "Примечание: {note}", "editScenarios": "Редактировать сценарии", @@ -335,8 +422,8 @@ "actionReject": "Отклонить", "feature": "В рекомендуемые", "unfeature": "Убрать из рекомендуемых", - "scenariosEditorHint": "场景 Сценарии — 点击切换 / нажмите для назначения", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Сценарии — нажмите, чтобы назначить или убрать", + "scenarioOption": "{name}", "selectedCount": "Выбрано: {count, number}", "rejectReasonPrompt": "Причина отклонения (необязательно):", "deleteAgentConfirm": "Удалить этого агента навсегда?", @@ -374,7 +461,7 @@ "optionRejected": "Отклонён", "fieldFeatured": "Рекомендуемый", "deleteSkillTitle": "Удалить навык", - "deleteSkillConfirm": "Вы уверены, что хотите удалить {name}? Это действие необратимо.", + "deleteSkillConfirm": "Точно удалить {name}? Это действие необратимо.", "categoriesTitle": "Категории", "addCategory": "Добавить категорию", "editCategoryTitle": "Редактировать категорию", @@ -436,7 +523,46 @@ "targetSkill": "навык", "targetAgent": "агент", "targetCategory": "категория", - "targetUser": "пользователь" + "targetUser": "пользователь", + "adminNavLabel": "Разделы администратора", + "retry": "Повторить", + "close": "Закрыть", + "prevPage": "Предыдущая страница", + "nextPage": "Следующая страница", + "sectionAgents": "Агенты", + "sectionSkills": "Навыки и сообщество", + "statTotalAgents": "Всего агентов", + "statPendingAgents": "Агенты на проверке", + "statHostedAgents": "Размещённые", + "statProjectAgents": "Проекты", + "pendingAgentsTitle": "Агенты, ожидающие проверки", + "noPendingAgents": "Нет агентов, ожидающих проверки.", + "kindHosted": "Размещённый", + "kindProject": "Проект", + "filterByStatus": "Фильтр по статусу", + "filterDisabled": "Отключённые", + "actionDisable": "Отключить", + "actionEnable": "Включить снова", + "disableAgentTitle": "Отключить агента", + "disableAgentConfirm": "{name} будет скрыт из публичного каталога до повторного включения. Ничего не удаляется.", + "deleteAgentTitle": "Удалить агента", + "deleteAgentConfirmNamed": "Точно удалить {name}? Это действие необратимо.", + "rejectAgentTitle": "Отклонить агента", + "rejectReasonLabel": "Причина (необязательно, видна издателю)", + "rejectReasonPlaceholder": "напр. эндпоинт возвращает 500 на тестовой задаче", + "healthOk": "Работает", + "healthDegraded": "С перебоями", + "healthDown": "Недоступен", + "healthUnknown": "Не проверялся", + "healthCheckedAt": "Последняя проверка доступности: {date}", + "healthNeverChecked": "Проверок доступности не было", + "batchDeleteTitle": "Удалить навыки", + "selectAllRows": "Выбрать все строки", + "selectRow": "Выбрать {name}", + "editSkillNamed": "Редактировать навык {name}", + "deleteSkillNamed": "Удалить навык {name}", + "editCategoryNamed": "Редактировать категорию {name}", + "deleteCategoryNamed": "Удалить категорию {name}" }, "Auth": { "signInTitle": "Вход в TakoAPI", @@ -457,31 +583,45 @@ "createAccount": "Создать аккаунт", "creatingAccount": "Создание аккаунта...", "invalidCredentials": "Неверный email или пароль", - "registrationFailed": "Регистрация не удалась", + "registrationFailed": "Не удалось зарегистрироваться", "somethingWentWrong": "Что-то пошло не так", "noAccount": "Нет аккаунта?", "signUpLink": "Зарегистрироваться", "haveAccount": "Уже есть аккаунт?", - "signInLink": "Войти" + "signInLink": "Войти", + "forgotPassword": "Забыли пароль?", + "forgotTitle": "Сброс пароля", + "forgotSubtitle": "Отправим на почту ссылку, чтобы задать новый", + "sendResetLink": "Отправить ссылку", + "sendingResetLink": "Отправка...", + "resetLinkSent": "Если у этого адреса есть аккаунт, ссылка для сброса пароля отправлена. Проверьте почту.", + "backToSignIn": "Назад ко входу", + "resetTitle": "Задайте новый пароль", + "resetSubtitle": "Введите новый пароль для аккаунта", + "newPasswordHint": "Новый пароль (минимум 8 символов)", + "updatePassword": "Обновить пароль", + "updatingPassword": "Обновление...", + "passwordUpdated": "Пароль обновлён. Перенаправляем на страницу входа...", + "resetLinkInvalid": "Ссылка для сброса пароля недействительна или устарела.", + "requestNewLink": "Запросить новую ссылку" }, "Dashboard": { "title": "Панель разработчика", - "signInPrompt": "Войдите, чтобы управлять API-ключами и просматривать использование.", - "signIn": "Войти", - "description": "Управляйте API-ключами и отслеживайте использование шлюза. Один ключ, один счёт, любой агент.", + "signInPrompt": "Войдите, чтобы управлять API-ключами и смотреть использование.", + "description": "Управляйте API-ключами и следите за использованием шлюза. Один ключ, один счёт, любой агент.", "apiKeys": "API-ключи", "createKey": "Создать ключ", "creating": "Создание…", - "copyKeyWarning": "Скопируйте этот ключ сейчас — он больше не будет показан.", + "copyKeyWarning": "Скопируйте ключ сейчас — больше он не показывается.", "copy": "Копировать", "copied": "Скопировано", - "noKeys": "Пока нет ключей. Создайте один, чтобы начать вызывать агентов.", + "noKeys": "Ключей пока нет. Создайте ключ, чтобы начать вызывать агентов.", "keyFallbackName": "ключ", "created": "создан {date}", "lastUsed": "последнее использование {date}", "neverUsed": "не использовался", "revoke": "Отозвать", - "revokeConfirm": "Отозвать этот ключ? Приложения, использующие его, перестанут работать.", + "revokeConfirm": "Отозвать этот ключ? Приложения, которые его используют, перестанут работать.", "quickstart": "Быстрый старт", "usage": "Использование", "totalCalls": "Всего вызовов", @@ -493,18 +633,17 @@ "tableLatency": "Задержка", "tableWhen": "Когда", "tableCost": "Стоимость", - "noCalls": "Пока нет вызовов. Создайте ключ и вызовите агента, чтобы увидеть использование здесь.", + "noCalls": "Вызовов пока нет. Создайте ключ и вызовите агента — использование появится здесь.", "billing": "Кредиты и оплата", "creditBalance": "Баланс кредитов", "totalSpend": "Всего потрачено", - "topUpComingSoon": "Пока кредиты пополняет платформа; самостоятельное пополнение через PayPal появится скоро.", "topUpLabel": "Пополнить баланс", "topUpButton": "Пополнить через PayPal", "topUpRedirecting": "Перенаправление…", "topUpSuccess": "Кредиты зачислены — баланс обновлён.", - "topUpError": "Пополнение не завершено. Попробуйте снова.", + "topUpError": "Пополнение не завершилось. Попробуйте ещё раз.", "topUpCancel": "Пополнение отменено.", - "noLedger": "Пока нет движений по кредитам.", + "noLedger": "Движений по кредитам пока нет.", "ledgerType": "Тип", "ledgerAmount": "Сумма", "ledgerNote": "Примечание", @@ -513,35 +652,65 @@ "ledgerDebit": "Списание за вызов", "ledgerPayout": "Выплата", "ledgerRefund": "Возврат", - "ledgerAdjust": "Корректировка" + "ledgerAdjust": "Корректировка", + "onboardingTitle": "Доведите первый вызов до ответа", + "onboardingSubtitle": "Три шага до ответа от агента.", + "stepKeyTitle": "Создайте API-ключ", + "stepKeyBody": "Одним ключом вызываются все агенты на TakoAPI. Секрет показывается один раз, сразу после создания.", + "stepCallTitle": "Сделайте первый вызов", + "stepCallBody": "Скопируйте пример ниже, укажите свой ключ в TAKO_KEY и запустите. Вызов появится в разделе «Использование» через несколько секунд.", + "stepInstallTitle": "Установите в кодинг-агент", + "stepInstallBody": "Научите Claude Code, Codex или OpenCode находить и вызывать агентов через TakoAPI.", + "stepInstallLink": "Открыть руководство по установке", + "stepOptional": "Необязательно", + "stepDone": "Готово", + "stepTodo": "Ещё не сделано", + "keyNameLabel": "Название ключа", + "keyNamePlaceholder": "Ключ для шлюза", + "createKeyError": "Не удалось создать ключ. Попробуйте ещё раз.", + "revokeNamed": "Отозвать {name}", + "quickstartHint": "Укажите в TAKO_KEY один из ключей выше и вызывайте любого агента по его slug.", + "creditRule": "Бесплатные агенты ничего не стоят; платные списывают предоплаченные кредиты, а вызов отклоняется с HTTP 402, как только баланс ушёл бы ниже нуля.", + "topUpUnavailable": "Самостоятельное пополнение пока недоступно — кредиты начисляет платформа. Баланс выше в любом случае актуален.", + "dismiss": "Скрыть", + "loadError": "Часть данных панели загрузить не удалось.", + "retry": "Повторить", + "recentCallsCaption": "Последние вызовы через шлюз", + "chartTitle": "Вызовы по дням за последние {days} дней", + "chartTotal": "всего {count, number}", + "chartAria": "Столбчатая диаграмма вызовов шлюза по дням за последние {days} дней, всего {count, number} вызовов.", + "chartDay": "{date}: вызовов {calls, number}, с ошибкой {errors, number}" }, "Profile": { - "title": "Мой профиль", - "signInPrompt": "Войдите, чтобы просмотреть свой профиль.", - "signIn": "Войти", - "submissions": "Заявки", + "title": "Мои публикации", + "signInPrompt": "Войдите, чтобы увидеть опубликованных агентов и навыки.", "likes": "Лайки", - "views": "Просмотры", - "statValue": "{count, number}", - "mySubmissions": "Мои заявки", - "submitNew": "Отправить новую", + "views": "просмотров", "tab_all": "Все", - "tab_approved": "Одобренные", - "tab_pending": "Ожидают", - "tab_rejected": "Отклонённые", - "tabCount": "{count, number}", - "emptyTab": "В этой вкладке нет заявок", + "emptyTab": "Здесь пока нет навыков.", "submitFirst": "Отправьте свой первый навык", - "statusApproved": "Одобрено", - "statusPending": "Ожидает проверки", - "statusRejected": "Отклонено", - "reviewerNote": "Примечание проверяющего:" + "statusApproved": "Опубликован", + "statusPending": "На проверке", + "statusRejected": "Отклонён", + "reviewerNote": "Примечание проверяющего:", + "publishAgent": "Опубликовать агента", + "submitSkill": "Отправить навык", + "statAgents": "Агенты", + "statCalls": "Вызовы через шлюз", + "statSkills": "Навыки", + "tabAgents": "Агенты", + "tabSkills": "Навыки", + "statusDisabled": "Отключён", + "kindHosted": "Вызываемый", + "kindProject": "Open-source", + "noAgents": "Здесь пока нет агентов.", + "publishFirstAgent": "Опубликуйте своего первого агента", + "myListings": "Мои публикации" }, "Submit": { "title": "Отправить навык", "subtitle": "Поделитесь своим навыком OpenClaw с сообществом", "signInPrompt": "Войдите, чтобы отправлять навыки.", - "signIn": "Войти", "skillSource": "Источник навыка", "githubUrl": "URL GitHub", "clawSkillsUrl": "URL ClawSkills.sh", @@ -556,29 +725,36 @@ "selectCategory": "Выберите категорию", "categoryOption": "{name} ({count})", "brief": "Кратко", - "briefHelper": "- Краткое описание в одну строку", - "briefPlaceholder": "Краткое описание того, что делает ваш навык", + "briefHelper": "- Описание в одну строку", + "briefPlaceholder": "Краткое описание того, что делает навык", "skillDetails": "Детали навыка", "skillDetailsHelper": "- Автозаполнение из README", "whatItDoes": "Что делает этот навык", - "whatItDoesPlaceholder": "Подробное объяснение того, что делает этот навык, его функции и возможности...", + "whatItDoesPlaceholder": "Подробное описание того, что делает навык, его функции и возможности...", "exampleWorkflow": "Пример сценария", "exampleWorkflowHelper": "- ВВОД / шаги АГЕНТА / ВЫВОД", "exampleWorkflowPlaceholder": "ВВОД\nПользователь просит: Сделай что-нибудь полезное\n\nАГЕНТ\n1\nВыполните `my-skill action`, чтобы решить задачу\n2\nПроверьте результат\n\nВЫВОД\nЗадача успешно выполнена", "requirements": "Требования", - "requirementsPlaceholder": "- Node.js 18+\n- API-ключ от сервиса X\n- и т. д.", + "requirementsPlaceholder": "- Node.js 18+\n- API-ключ сервиса X\n- и т. д.", "submitSkill": "Отправить навык", "submitting": "Отправка...", "enterUrlFirst": "Сначала введите URL", "autoFillFailed": "Автозаполнение не удалось", - "submitFailed": "Не удалось отправить", - "somethingWentWrong": "Что-то пошло не так" + "somethingWentWrong": "Что-то пошло не так", + "successTitle": "Отправлено 🎉", + "pendingReview": "{name} отправлен на проверку.", + "nextStepsTitle": "Что дальше", + "nextStepReview": "Проверяющий смотрит репозиторий и описание.", + "nextStepEmail": "Мы напишем на почту, когда проверка закончится.", + "nextStepProfile": "На странице публикаций всегда виден текущий статус и примечание проверяющего.", + "viewMyListings": "Мои публикации", + "backToSkills": "← Назад к навыкам" }, "SubmitAgent": { "title": "Опубликовать агента", "signInPrompt": "Войдите, чтобы опубликовать агента.", "signIn": "Войти", - "intro": "Разместите вызываемого агента на маркетплейсе. Отправьте URL его A2A AgentCard, и мы разберём остальное, или заполните вручную.", + "intro": "Разместите вызываемого агента на маркетплейсе. Отправьте URL его A2A AgentCard — остальное разберём сами, либо заполните поля вручную.", "tabUrl": "URL AgentCard", "tabManual": "Вручную", "cardUrlLabel": "URL AgentCard", @@ -601,7 +777,7 @@ "pricingPerToken": "За 1 тыс. токенов", "unitPriceLabel": "Цена за единицу (USD)", "unitPricePlaceholder": "0.002", - "homepageLabel": "Главная страница", + "homepageLabel": "Сайт", "optional": "необязательно", "homepagePlaceholder": "https://your-agent.com", "submit": "Опубликовать агента", @@ -609,28 +785,45 @@ "submitFailed": "Не удалось отправить", "somethingWentWrong": "Что-то пошло не так", "successTitle": "Отправлено 🎉", - "pendingReview": "{name} теперь ожидает проверки.", - "successDetail": "Мы проверим AgentCard и опубликуем её на маркетплейсе после одобрения.", - "backToMarketplace": "← Назад на маркетплейс" + "pendingReview": "{name} отправлен на проверку.", + "successDetail": "Мы проверим AgentCard и опубликуем агента на маркетплейсе после одобрения.", + "backToMarketplace": "← Назад на маркетплейс", + "checkCard": "Проверить карточку", + "checking": "Проверка…", + "enterCardUrlFirst": "Сначала введите URL AgentCard.", + "previewTitle": "Вот что будет опубликовано", + "previewNoDescription": "В карточке нет описания.", + "previewEndpoint": "Endpoint", + "previewCapabilities": "Возможности", + "previewSkillCount": "{count, plural, =0 {Возможности не заявлены} one {# возможность} few {# возможности} many {# возможностей} other {# возможности}}", + "previewSupports": "Поддерживает", + "previewStreaming": "Стриминг", + "previewPush": "Push-уведомления", + "previewHint": "Пока ничего не сохранено — опубликуйте ниже, чтобы отправить на проверку.", + "nextStepsTitle": "Что дальше", + "nextStepReview": "Проверяющий смотрит AgentCard и убеждается, что эндпоинт отвечает.", + "nextStepEmail": "Мы напишем на почту, когда проверка закончится.", + "nextStepProfile": "На странице публикаций всегда виден текущий статус и примечание проверяющего.", + "viewMyListings": "Мои публикации" }, "AgentEngagement": { "yourRating": "Ваша оценка", - "ratingsCount": "{count, number} оценок", + "ratingsCount": "оценок: {count, number}", "noRatings": "Ещё нет оценок", "bookmark": "Сохранить", "bookmarked": "Сохранено", - "signInToRate": "Войдите, чтобы оценить или сохранить агента" + "signInToRate": "Войдите, чтобы оценить или сохранить агента", + "rateStars": "Поставить {count, plural, one {# звезду} few {# звезды} many {# звёзд} other {# звезды}}" }, "Bookmarks": { "title": "Сохранённые агенты", "subtitle": "Агенты, которые вы сохранили.", - "empty": "Пока нет закладок.", - "browseAgents": "Просмотреть агентов", - "signInPrompt": "Войдите, чтобы увидеть закладки.", - "signIn": "Войти" + "empty": "Пока ничего не сохранено.", + "browseAgents": "Просмотр агентов", + "signInPrompt": "Войдите, чтобы увидеть сохранённых агентов." }, "ScenarioPage": { - "metaTitle": "ИИ-агенты для «{scenario}»", + "metaTitle": "ИИ-агенты: {scenario}", "metaDescription": "Находите и вызывайте ИИ-агентов для «{scenario}» через единый API — описанных открытыми A2A AgentCard на TakoAPI.", "heroSubtitle": "ИИ-агенты для «{scenario}», вызываемые через единый API.", "empty": "В этом сценарии пока нет агентов.", @@ -638,25 +831,42 @@ "back": "← Все сценарии", "breadcrumbScenarios": "Сценарии", "indexTitle": "ИИ-агенты по сценариям", - "indexDescription": "Найдите ИИ-агентов по задаче: фронтенд, инвестиции, маркетинг, поддержка и другое — все вызываются через один API.", + "indexDescription": "Найдите ИИ-агентов по задаче — фронтенд, инвестиции, маркетинг, поддержка и другое — все вызываются через один API.", "indexHeroTitle": "По сценариям", - "indexHeroSubtitle": "Найдите ИИ-агентов по задаче, которую нужно решить." + "indexHeroSubtitle": "Найдите ИИ-агентов по задаче, которую нужно решить.", + "indexPopularHeading": "Хорошо покрыто", + "indexPopularHint": "Сценарии, где есть большой каталог агентов и из чего выбрать.", + "indexSparseHeading": "Только начинается", + "indexSparseHint": "Пока лишь несколько агентов — легко стать первым.", + "indexSparseCta": "Опубликуйте агента в одном из них →", + "metaTitlePaged": "ИИ-агенты: {scenario} — страница {page}", + "resultsLabel": "Агенты: {scenario}", + "paginationLabel": "Постраничная навигация", + "showingRange": "Показаны {from}–{to} из {total}", + "prev": "← Назад", + "next": "Вперёд →", + "goToPage": "Перейти на страницу {page}", + "ctaTitle": "Знаете агента для «{scenario}»?", + "ctaBody": "Сценарий пока небогатый. Опубликуйте агента — он появится здесь для всех, кто смотрит «{scenario}».", + "ctaButton": "Опубликовать агента", + "relatedHeading": "Другие сценарии" }, "Badge": { "snippetTitle": "Добавить бейдж", "copy": "Копировать", "copied": "Скопировано", - "snippetHint": "Вставьте в свой README, чтобы дать ссылку на вашу страницу в TakoAPI.", + "snippetHint": "Вставьте в README, чтобы дать ссылку на свою страницу в TakoAPI.", "pageTitle": "README-бейдж TakoAPI", - "pageIntro": "Покажите, что ваш проект есть в каталоге TakoAPI — небольшой бейдж со ссылкой на вашу страницу и актуальным числом звёзд.", + "pageIntro": "Покажите, что проект есть в каталоге TakoAPI — небольшой бейдж со ссылкой на вашу страницу и актуальным числом звёзд.", "example": "Пример", "step1Title": "Найдите свою страницу", "step1": "Найдите свой проект в каталоге агентов.", "step2Title": "Скопируйте бейдж", - "step2": "На странице вашего проекта скопируйте Markdown из блока бейджа.", + "step2": "На странице проекта скопируйте Markdown из блока с бейджем.", "step3Title": "Вставьте в README", "step3": "Добавьте ближе к началу — посетители и поисковики получат ссылку на вашу страницу TakoAPI.", - "findListing": "Найти свою страницу" + "findListing": "Найти свою страницу", + "exampleAlt": "Бейдж TakoAPI" }, "Blog": { "title": "Блог", @@ -666,5 +876,149 @@ "minRead": "{min} мин чтения", "published": "Опубликовано", "faqTitle": "Частые вопросы" + }, + "NotFound": { + "title": "Не удалось найти эту страницу", + "description": "Ссылка могла устареть, или запись удалили. Попробуйте каталог или поиск в шапке.", + "home": "Главная", + "agents": "Просмотр агентов", + "skills": "Просмотр навыков" + }, + "ErrorPage": { + "title": "Что-то пошло не так", + "description": "Страница не загрузилась. Часто помогает повторная попытка; если нет — по коду ниже мы найдём её в логах.", + "reference": "Код: {id}", + "retry": "Попробовать снова", + "home": "На главную" + }, + "Consent": { + "title": "Аналитика", + "description": "Мы используем Google Analytics, чтобы видеть, какие страницы полезны. Пока вы не согласитесь, ничего не загружается и куки не ставятся.", + "accept": "Принять", + "decline": "Отклонить" + }, + "Docs": { + "metaTitle": "Документация API", + "title": "Справочник API TakoAPI", + "description": "От API-ключа до рабочего вызова агента: аутентификация, эндпоинты каталога, единый шлюз агентов, размещённый MCP-сервер, коды ошибок, лимиты и тарификация.", + "heroBadge": "Документация для разработчиков", + "heroTitle": "Справочник API TakoAPI", + "heroSubtitle": "Один ключ, все агенты. Направьте любой OpenAI-совместимый клиент на TakoAPI и вызовите агента по его slug — или изучайте весь каталог вообще без ключа.", + "heroKeyCta": "Создать API-ключ", + "heroBrowseCta": "Просмотр агентов", + "tocTitle": "На этой странице", + "tocLabel": "Содержание", + "quickstartTitle": "Быстрый старт", + "quickstartIntro": "Три шага от нуля до ответа. Каждый запрос на этой странице — реальный вызов рабочего API.", + "quickstartStep1Title": "Создайте ключ", + "quickstartStep1": "Войдите и создайте API-ключ в панели. Секрет показывается один раз, при создании.", + "quickstartStep2Title": "Экспортируйте его", + "quickstartStep2": "Все примеры на этой странице читают ключ из TAKO_KEY.", + "quickstartStep3Title": "Вызовите агента", + "quickstartStep3": "Укажите в model — или в slug в URL — любого размещённого агента из каталога.", + "quickstartSamplesLabel": "Примеры вызовов для быстрого старта", + "quickstartNote": "Slug в этих примерах — заглушка, а не реальный агент. Скопируйте настоящий со страницы любого агента; вызывать можно только агентов, отмеченных как размещённые.", + "authenticationTitle": "Аутентификация", + "authenticationIntro": "Вызовы шлюза аутентифицируются bearer-токеном. Эндпоинты каталога анонимны, ключ им не нужен.", + "authHeaderNote": "x-api-key принимается как альтернатива заголовку Authorization. Ключи всегда начинаются с tako_live_.", + "authWhereTitle": "Откуда берутся ключи", + "authWhere": "Создавайте отдельный ключ для каждого клиента или окружения в панели и отзывайте те, которыми перестали пользоваться.", + "authOnceTitle": "Секрет показывается один раз", + "authOnce": "Хранятся только SHA-256-хеш ключа и короткий префикс для отображения, поэтому потерянный секрет восстановить нельзя — создайте новый ключ и отзовите старый.", + "authRevokeTitle": "Отзыв срабатывает сразу", + "authRevoke": "Каждый вызов заново сверяет ключ с базой, ничего не кешируется, поэтому отозванный ключ отклоняется уже на следующем запросе. История прошлых вызовов сохраняется.", + "discoveryTitle": "Каталог", + "discoveryIntro": "Четыре публичных кешируемых эндпоинта, ключ не нужен. Возвращаются только одобренные агенты и навыки.", + "endpointRegistrySummary": "Весь каталог агентов — Markdown для модели, JSON для кода.", + "endpointRegistryNote": "Без format=json ответ приходит в Markdown (text/markdown): таблица размещённых агентов и таблица open-source проектов, по размеру рассчитанные так, чтобы сразу лечь в контекст модели — именно это читает установленный навык TakoAPI. Оба формата ограничены limit, поэтому проверяйте truncated и общее число записей, прежде чем считать, что получили всё. Свободный текст в параметрах обрезается до 100 символов.", + "paramRegistryFormat": "md (по умолчанию) или json.", + "paramRegistryQ": "Совпадение по названию и описанию агента.", + "paramRegistryCategory": "Slug категории.", + "paramRegistryProtocol": "Только агенты, объявившие этот протокол.", + "paramRegistryKind": "Агенты HOSTED вызываются через шлюз; записи PROJECT — это open-source репозитории, перечисленные только для ознакомления.", + "paramRegistrySort": "Порядок. По умолчанию сначала рекомендуемые, затем самые вызываемые, затем новые — а для проектов по числу звёзд.", + "paramRegistryLimit": "Сколько записей вернуть всего. Сначала заполняются размещённые агенты, затем проекты.", + "endpointAgentDetailSummary": "Полные публичные данные одного агента, включая объявленные им A2A-навыки.", + "endpointAgentDetailNote": "Разрешаются только одобренные агенты, всё остальное — 404. Поля модерации — примечания проверяющих, внутренние id и аватары издателей — в эту структуру не попадают. Кешируется на edge 60 секунд.", + "paramAgentDetailSlug": "Сегмент пути. Slug агента в том виде, в каком его возвращает реестр.", + "endpointSkillsDirectorySummary": "Каталог навыков OpenClaw, по умолчанию в Markdown.", + "endpointSkillsDirectoryNote": "Как и реестр, сначала Markdown. Поле install равно null для навыков без опубликованной команды установки — используйте ссылку github. Поле downloads — сторонняя цифра, собранная со страницы самого навыка, а не число установок через TakoAPI.", + "paramSkillsDirFormat": "md (по умолчанию) или json.", + "paramSkillsDirQ": "Совпадение по названию и описанию навыка.", + "paramSkillsDirCategory": "Slug категории.", + "paramSkillsDirPage": "Номер страницы, начиная с 1.", + "paramSkillsDirLimit": "Результатов на странице.", + "endpointSkillsSearchSummary": "Полнотекстовый поиск по каталогу навыков, только JSON.", + "endpointSkillsSearchNote": "Параметр q обязателен: без него эндпоинт возвращает пустой список, а не весь каталог. Каждый результат — полная запись каталога без readme и reviewNote; пример выше сокращён.", + "paramSkillsSearchQ": "Поисковый запрос. Ищет по названию, описанию и автору навыка.", + "paramSkillsSearchCategory": "Slug категории.", + "paramSkillsSearchPage": "Номер страницы, начиная с 1.", + "paramSkillsSearchLimit": "Результатов на странице.", + "gatewayTitle": "Шлюз", + "gatewayIntro": "Три способа вызвать одного и того же агента. Всем трём нужен ключ, все три тарифицируются одинаково и все три принимают slug агента там, где обычно указывается название модели. Вызывать можно только агентов вида HOSTED.", + "endpointChatCompletionsSummary": "Совместимо с OpenAI. Направьте любой OpenAI SDK на /v1 и укажите в model slug агента.", + "endpointChatCompletionsNote": "Пересылается вся переписка: по одной текстовой A2A-части на реплику, по порядку, с ролью OpenAI у каждой части. Поле usage присутствует всегда, но всегда нулевое — токены здесь никто не считает, да и агент не обязан быть LLM. Укажите stream: true для SSE-формы выше и stream_options.include_usage, чтобы получить ещё и завершающий чанк usage. Обычный вызов прерывается по таймауту через 30 секунд, стрим — через 120.", + "endpointA2AMessageSummary": "Нативный проброс A2A. Возвращает собственный JSON-RPC-ответ агента без изменений.", + "endpointA2AMessageNote": "Ретранслируется как вызов A2A message/send. Отправьте краткую текстовую форму или полное A2A-сообщение — с parts, taskId и contextId — чтобы продолжить многошаговую задачу или передать части с файлами и данными. Код статуса и тело ответа агента передаются без изменений, а запрос без содержимого отклоняется с 400 и не тарифицируется. Таймаут — 30 секунд.", + "endpointA2AStreamSummary": "Тот же вызов через Server-Sent Events, для агентов, которые умеют стримить.", + "endpointA2AStreamNote": "Ретранслирует кадры A2A message/stream от агента дословно, как text/event-stream. Этот эндпоинт принимает только краткую текстовую форму. Соединение обрывается через 120 секунд, а вызов тарифицируется только после того, как стрим дошёл до конца — см. «Лимиты и тарификация».", + "labelQueryParams": "Параметры", + "labelRequest": "Тело запроса", + "labelExample": "Пример", + "labelResponse": "Ответ", + "labelStreaming": "Ответ при stream: true", + "labelA2AMessageForm": "Полная форма A2A-сообщения", + "labelAuthRequired": "Нужен ключ", + "labelNoAuth": "Без ключа", + "labelBillable": "Тарифицируется", + "labelFree": "Бесплатно", + "labelParamName": "Имя", + "labelParamType": "Тип", + "labelParamDesc": "Описание", + "paramsTableCaption": "Параметры для {path}", + "mcpTitle": "MCP-сервер", + "mcpIntro": "TakoAPI держит размещённый stateless-сервер Model Context Protocol поверх Streamable HTTP по адресу {url}. Зарегистрируйте его один раз — и кодинг-агент сможет искать по каталогу и вызывать агентов нативными инструментами.", + "mcpRegisterTitle": "Регистрация", + "mcpRegisterLabel": "Команды регистрации MCP", + "mcpAuthNote": "Три инструмента чтения работают анонимно. invoke_agent тратит кредиты, поэтому ему нужен ключ: передайте его в заголовке Authorization (Claude Code), через bearer_token_env_var (Codex) или в блоке headers (OpenCode).", + "mcpToolsTitle": "Инструменты", + "mcpToolsTableCaption": "Инструменты MCP-сервера TakoAPI", + "mcpToolColumn": "Инструмент", + "mcpToolAccessColumn": "Доступ", + "mcpToolWhatColumn": "Что делает", + "mcpToolKeyRequired": "Нужен ключ", + "mcpToolAnonymous": "Анонимно", + "mcpToolSearchAgents": "Поиск по реестру: по ключевому слову, категории или протоколу. Возвращает slug, протоколы и цены.", + "mcpToolGetAgent": "Всё, что известно об одном агенте по его slug — возможности, протоколы, endpoint, цены и объявленные навыки.", + "mcpToolSearchSkills": "Поиск по каталогу навыков OpenClaw. Без запроса возвращает отобранные лучшие навыки.", + "mcpToolInvokeAgent": "Вызывает агента через шлюз и возвращает его ответ. Нужен ключ, тратит кредиты.", + "mcpRateNote": "У MCP-эндпоинта лимит 120 запросов в минуту на IP-адрес — отдельно от лимита шлюза для вашего ключа.", + "mcpInstallNote": "Предпочитаете файл навыка серверу? Установщик научит Claude Code, Codex и OpenCode обращаться к этим эндпоинтам напрямую по HTTP.", + "errorsTitle": "Ошибки", + "errorsIntro": "Любая ошибка шлюза приходит в JSON. Это статусы, которые возвращает сам шлюз.", + "errorsTableCaption": "Статусы ошибок, которые возвращает шлюз", + "errorStatusColumn": "Статус", + "errorCodeColumn": "Код", + "errorWhenColumn": "Когда", + "errorBodyColumn": "Пример тела", + "error400When": "Slug указывает на open-source проект без вызываемого эндпоинта, тело запроса не прошло валидацию или все реплики оказались пустыми.", + "error401When": "Ключ отсутствует, некорректен, неизвестен или отозван.", + "error402When": "Агент платный, и списание увело бы баланс ниже нуля. Проверяется до обращения к агенту, поэтому ничего не тратится.", + "error404When": "Одобренного агента с таким slug нет.", + "error429When": "Ключ превысил лимит за последние 60 секунд. Retry-After показывает, сколько секунд до сброса окна.", + "error502When": "Агент не ответил, вышел таймаут, не отдал стрим или сообщил об ошибке JSON-RPC внутри HTTP 200. Никогда не тарифицируется.", + "errorsOpenAiNote": "На /v1/chat/completions те же ошибки приходят в конверте OpenAI — с обязательными type, param и code — поэтому OpenAI SDK выбрасывает свои привычные типы исключений:", + "errorsPassthroughNote": "Статус, который вернул сам агент, эндпоинт A2A message передаёт без изменений, поэтому 4xx оттуда может быть ответом агента, а не нашим.", + "limitsTitle": "Лимиты и тарификация", + "limitsRateTitle": "Лимиты запросов", + "limitsRate": "Шлюз допускает 120 запросов за 60 секунд на один API-ключ. Окно общее для всех инстансов сервера и привязано к самому ключу, а не к IP-адресу, поэтому оно не расширяется под нагрузкой и не сужается за общим NAT.", + "limitsRateOverride": "У ключа может быть собственный лимит, который заменяет значение по умолчанию только для него. При превышении возвращается 429 с заголовком Retry-After в секундах.", + "limitsBillingTitle": "Тарификация", + "limitsBillingFree": "Агенты с ценой FREE бесплатны — как и любой агент, у которого цена за единицу не указана или равна нулю. Шлюз не придумывает списаний.", + "limitsBillingPriced": "Платный агент списывает предоплаченные кредиты по своей цене за единицу, один раз за вызов. Потокенного учёта нет, поэтому вызов — это одна единица, сколько бы он ни длился.", + "limitsBillingPreflight": "Баланс проверяется до обращения к агенту. Если списание увело бы его ниже нуля, вызов отклоняется с 402 и агент не вызывается — овердрафта нет.", + "limitsBillingFailures": "Неудачные вызовы не тарифицируются. 4xx или 5xx от агента, таймаут, недоступный эндпоинт или ошибка JSON-RPC — попытка записывается, но счёт не выставляется.", + "limitsBillingStreams": "Стримы тарифицируются только при успешном завершении. Стрим, который прервали, который упал по таймауту или в середине, или от которого вы отключились раньше времени, записывается, но не тарифицируется.", + "limitsBillingLedger": "Каждое списание пишет неизменяемую запись в журнал, привязанную к вызову, который его вызвал. Баланс, журнал и статистика по каждому вызову — в панели." } } diff --git a/messages/tr.json b/messages/tr.json index c643a24c7..0ed645ef2 100644 --- a/messages/tr.json +++ b/messages/tr.json @@ -1,66 +1,84 @@ { "Metadata": { "tagline": "Tüm ajanlara erişmek için tek API", - "description": "Yapay zekâ ajanlarını tek bir birleşik API üzerinden keşfedin ve çağırın — ajanlar için bir OpenRouter. Kodlama ajanınız için yüzlerce açık kaynaklı ajan projesinden ve binlerce OpenClaw becerisinden oluşan bir dizini gözatın." + "description": "Yapay zekâ ajanlarını tek bir birleşik API üzerinden keşfedin ve çağırın — ajanlar için bir OpenRouter. Yüzlerce açık kaynaklı ajan projesinden ve kodlama ajanınız için binlerce OpenClaw becerisinden oluşan dizine göz atın." }, "LocaleSwitcher": { "label": "Dil" }, "Header": { - "searchPlaceholder": "Ajan ara...", "agents": "Ajanlar", "skills": "Beceriler", - "trending": "Öne Çıkanlar", - "publish": "Yayınla", - "publishAgent": "Ajan Yayınla", + "publishAgent": "Ajan yayınla", "account": "Hesap", - "mySkills": "Becerilerim", - "myBookmarks": "Kaydedilenler", - "apiUsage": "API ve Kullanım", - "adminPanel": "Yönetim Paneli", + "myBookmarks": "Kaydedilen ajanlar", + "apiUsage": "API anahtarları ve kullanım", + "adminPanel": "Yönetim paneli", "signIn": "Giriş yap", - "signOut": "Çıkış yap" + "signOut": "Çıkış yap", + "mainNav": "Ana menü", + "skipToContent": "İçeriğe geç", + "openMenu": "Menüyü aç", + "closeMenu": "Menüyü kapat", + "projects": "Projeler", + "install": "Kurulum", + "getApiKey": "API anahtarı al", + "search": "Ara", + "searchScope": "Şurada ara", + "scopeAgents": "Ajanlar", + "scopeProjects": "Projeler", + "scopeSkills": "Beceriler", + "searchAgentsPlaceholder": "Ajanları yaptıkları işe göre arayın…", + "searchProjectsPlaceholder": "Açık kaynaklı ajan projelerinde arayın…", + "searchSkillsPlaceholder": "Kodlama ajanınız için beceri arayın…", + "myListings": "Yayınlarım", + "docs": "Dokümantasyon" }, "Footer": { - "tagline": "Tüm ajanlara erişmek için tek API — ayrıca kodlama ajanınız için binlerce OpenClaw becerisi.", - "agentRegistry": "Ajan kaydı:", + "tagline": "Tüm ajanlara erişmek için tek API — her ajanı tek bir uç nokta üzerinden keşfedin, çağırın ve ölçün.", + "agentRegistry": "Ajan kayıt defteri:", "platform": "Platform", "resources": "Kaynaklar", - "installInAgent": "Ajanınıza kurun", - "browseAgents": "Ajanlara Gözat", - "publishAnAgent": "Ajan Yayınla", - "developerDashboard": "Geliştirici Paneli", - "browseSkills": "Becerilere Gözat", - "awesomeList": "Awesome Listesi", + "installInAgent": "Kodlama ajanınıza kurun", + "browseAgents": "Ajanlara göz at", + "publishAnAgent": "Ajan yayınla", + "developerDashboard": "API anahtarları ve kullanım", + "browseSkills": "Becerilere göz at", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. OpenClaw topluluğu için geliştirildi.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. MIT lisanslı.", + "blog": "Blog", + "badge": "README rozeti", + "scenarios": "Senaryoya göre göz at", + "a2aSpec": "A2A protokolü", + "github": "GitHub", + "docs": "API dokümantasyonu" }, "Home": { - "heroTitle": "Tüm ajanlara erişmek için tek API", - "heroSubtitle": "{agents, number} yapay zekâ ajanını tek bir birleşik API üzerinden keşfedin ve çağırın — ayrıca kodlama ajanınız için {skills, number} beceri.", - "browseAgents": "Ajanlara gözat", - "publishAnAgent": "Ajan yayınla", - "agentRegistry": "Ajan kaydı:", - "browseByScenario": "Senaryoya göre gözat", - "allAgents": "Tüm ajanlar", - "featuredAgents": "Öne Çıkan Ajanlar", - "browseAll": "Tümüne gözat", + "heroTitle": "Tek API ile tüm ajanlara erişin", + "heroSubtitle": "Yapay zekâ ajanlarını keşfedin, sonra herhangi birini tek bir uç noktadan tek bir anahtarla çağırın. Yerel A2A desteği, OpenAI uyumlu, çağrı başına ölçümleme.", + "browseAgents": "Ajanlara göz at", + "browseByScenario": "Yaptırmak istediğiniz işe göre göz atın", + "featuredAgents": "Öne çıkan ajanlar", + "browseAll": "Tümüne göz at", "popularProjects": "Popüler açık kaynaklı projeler", "selfHostable": "{count, number} kendi sunucunuzda barındırılabilir", - "skillTitle": "OpenClaw için TakoAPI Becerisi", - "official": "Resmî", - "skillDescription": "OpenClaw becerilerini doğal sohbet yoluyla arayın, kurun ve yönetin — TakoAPI pazaryeri tarafından desteklenir.", - "viewOnGithub": "GitHub'da görüntüle", - "askAgent": "veya ajanınıza sorun: \"TakoAPI becerisini kur\"", - "usingCodingAgent": "Claude Code, Codex veya OpenCode mu kullanıyorsunuz?", - "oneCommandInstall": "Tek komutla kurulum", - "mustHaveSkills": "Olmazsa Olmaz Beceriler", - "viewRankings": "Sıralamaları görüntüle", - "categories": "Kategoriler", - "viewAll": "Tümünü görüntüle", + "categories": "Kategoriler:", "moreCategories": "{count, plural, one {+# kategori daha} other {+# kategori daha}}", - "latestSkills": "En Yeni Beceriler" + "getApiKey": "API anahtarı alın", + "catalogSummary": "{agents, number} çağrılabilir ajan · {projects, number} açık kaynaklı proje · {skills, number} kodlama ajanı becerisi", + "quickstartLabel": "İlk çağrınıza 30 saniye", + "quickstartNote": "Terminalden çalışmayı mı tercih edersiniz? TakoAPI'yi kodlama ajanınıza kurun.", + "allScenarios": "Tüm senaryolar", + "callableNow": "{count, number} tanesi ağ geçidi üzerinden çağrılabilir", + "installTitle": "TakoAPI'yi kodlama ajanınızdan kullanın", + "installDescription": "Tek bir komut, Claude Code, Codex veya OpenCode'a dizinde arama yapmayı ve sizin için ajan çağırmayı öğretir — ya da bunun yerine barındırılan MCP sunucusunu kaydedin.", + "installCta": "Kurulum rehberi", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Kodlama ajanınız için beceriler", + "skillsSubtitle": "{count, number} OpenClaw becerisi, en çok kurulanlar ve en yeniler.", + "quickstartTabDiscover": "1. Keşfet", + "quickstartTabCall": "2. Çağır", + "quickstartTabFromAgent": "3. Agent’ından" }, "Scenarios": { "frontend": "Ön Yüz", @@ -85,43 +103,65 @@ "notFound": "Ajan bulunamadı", "backToMarketplace": "← Ajan Pazaryeri", "openSourceProject": "Açık kaynaklı proje", - "signedCard": "İmzalı kart", - "verifiedPublisher": "Doğrulanmış yayıncı", "by": "{name} tarafından", "publisherOpenSource": "açık kaynak", "publisherUnknown": "bilinmiyor", "scenario": "Senaryo:", "stars": "{count, number} yıldız", - "selfHost": "Kendi sunucunuzda barındırın", + "selfHost": "Kendiniz barındırın", "streaming": "Akış", "pushNotifications": "Anlık bildirimler", "skills": "Beceriler", "skillsCount": "({count, number})", "noSkillsProject": "Açık kaynaklı bir proje — kodu inceleyin ve GitHub'dan kendi sunucunuzda barındırın.", - "noSkillsHosted": "Bu ajan, AgentCard'ında yapılandırılmış beceriler bildirmedi.", + "noSkillsHosted": "Bu ajan, AgentCard'ında yapılandırılmış beceri bildirmedi.", "inputMode": "giriş: {mode}", "outputMode": "çıkış: {mode}", - "openSourceProjectHeading": "Açık kaynaklı proje", "openSourceProjectNote": "Kendi sunucunuzda barındırın veya kodu inceleyin. TakoAPI ağ geçidi üzerinden çağrılamaz.", "viewOnGithub": "GitHub'da görüntüle", - "integration": "Entegrasyon", "endpoint": "Uç nokta", "agentCard": "AgentCard", "homepage": "Ana sayfa", "callThroughTakoApi": "TakoAPI üzerinden çağırın", - "gatewayPhase2": "Birleşik ağ geçidi — çağırma özelliği 2. Aşamada gelecek.", - "breadcrumbHome": "Ana Sayfa", - "breadcrumbMarketplace": "Ajan Pazaryeri" + "breadcrumbHome": "Ana sayfa", + "breadcrumbMarketplace": "Ajan Pazaryeri", + "gatewayHint": "API anahtarınızla çağırın — panelden bir tane oluşturun.", + "healthOk": "Yanıt veriyor", + "healthDegraded": "Sorunlu", + "healthDown": "Yanıt vermiyor", + "healthCheckedAt": "Son kontrol: {time}", + "atAGlance": "Bir bakışta", + "protocols": "Protokoller", + "protocolsNone": "Bildirilmedi", + "yes": "Evet", + "no": "Hayır", + "pricing": "Fiyatlandırma", + "health": "Sağlık", + "healthUnknown": "Henüz kontrol edilmedi", + "totalCalls": "Toplam çağrı", + "numberValue": "{count, number}", + "averageRating": "Ortalama puan", + "ratingValue": "{rating} / 5 · {count, number} değerlendirme", + "notRated": "Henüz değerlendirilmedi", + "callExamplesLabel": "Çağrı örnekleri", + "repository": "Depo", + "language": "Dil", + "license": "Lisans", + "licenseNone": "Belirtilmemiş", + "lastPush": "Son gönderim", + "topics": "Konular", + "readmeHeading": "README'den", + "readmeNote": "Projenin GitHub'daki README dosyasından alıntı." }, "Agents": { "metaTitle": "Ajan Pazaryeri", - "metaDescription": "Tüm ajanlara erişmek için tek API. Açık A2A AgentCard'larıyla tanımlanan yapay zekâ ajanlarını ve yüzlerce açık kaynaklı ajan projesini tek bir birleşik API üzerinden gözatın ve çağırın.", + "metaDescription": "Tüm ajanlara erişmek için tek API. Açık A2A AgentCard'larıyla tanımlanan yapay zekâ ajanlarına ve yüzlerce açık kaynaklı ajan projesine tek bir birleşik API üzerinden göz atın ve bunları çağırın.", "ogTitle": "Ajan Pazaryeri — {siteName}", "ogDescription": "Yapay zekâ ajanlarını ve açık kaynaklı ajan projelerini tek bir birleşik API üzerinden keşfedin ve çağırın.", "heroTitle": "Ajan Pazaryeri", "heroTagline": "Tüm ajanlara erişmek için tek API.", "heroSubtitle": "Açık A2A AgentCard'larıyla tanımlanan yapay zekâ ajanlarını tek bir birleşik API üzerinden keşfedin ve çağırın.", - "searchPlaceholder": "Ajanları ada veya açıklamaya göre ara…", + "searchPlaceholder": "Ajanları ada veya açıklamaya göre arayın…", "search": "Ara", "all": "Tümü", "kindAgents": "Ajanlar", @@ -131,39 +171,56 @@ "pricingFilter": "Fiyatlandırma:", "categoryFilter": "Kategori:", "sortLabel": "Sırala:", - "sortLatest": "En Yeni", - "sortStars": "Yıldızlar", + "sortLatest": "En yeni", + "sortStars": "Yıldız", "sortPopular": "Popüler", - "sortCalls": "En Çok Çağrılan", - "sortRating": "En Yüksek Puanlı", + "sortCalls": "En çok çağrılan", + "sortRating": "En yüksek puanlı", "pricingFree": "Ücretsiz", "pricingPerCall": "çağrı başına", "pricingPerTask": "görev başına", - "pricingPerToken": "1k jeton başına", + "pricingPerToken": "1k token başına", "count": "{count, plural, =0 {Ajan yok} one {# ajan} other {# ajan}}", - "emptyTitle": "Filtrelerinizle henüz eşleşen ajan yok.", + "emptyTitle": "Filtrelerinizle eşleşen ajan henüz yok.", "emptyCta": "İlkini siz yayınlayın →", "prev": "← Önceki", "next": "Sonraki →", - "pageOf": "Sayfa {page} / {total}" + "pageOf": "Sayfa {page} / {total}", + "filtersTitle": "Filtreler", + "filtersWithCount": "Filtreler ({count})", + "activeFiltersLabel": "Etkin filtreler:", + "removeFilter": "Filtreyi kaldır: {label}", + "clearAll": "Tümünü temizle", + "kindLabel": "Tür", + "scenarioGroup": "Senaryo", + "protocolGroup": "Protokol", + "pricingGroup": "Fiyatlandırma", + "categoryGroup": "Kategori", + "searchChip": "Arama: {query}", + "resultsLabel": "Sonuçlar", + "paginationLabel": "Sayfalama", + "goToPage": "{page}. sayfaya git", + "showingRange": "{total} sonuçtan {from}–{to} arası gösteriliyor" }, "Components": { - "homeSearchPlaceholder": "Becerileri ada, açıklamaya veya yazara göre ara...", - "homeSearchButton": "Ara", "agentFree": "Ücretsiz", "agentPerCall": "{price}/çağrı", "agentPerTask": "{price}/görev", - "agentPerToken": "{price}/1k jeton", + "agentPerToken": "{price}/1k tok", "agentPriceFallbackCall": "çağrı", "agentPriceFallbackTask": "görev", - "agentPriceFallbackToken": "jeton", + "agentPriceFallbackToken": "token", "agentBadgeProject": "proje", "agentBadgeStreaming": "akış", "agentOpenSource": "açık kaynak", - "agentSelfHost": "kendi sunucunuzda barındırın", + "agentSelfHost": "barındırılabilir", "agentSkills": "{count, plural, one {# beceri} other {# beceri}}", "skillBy": "{author} tarafından", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} çağrı", + "healthOk": "Yanıt veriyor", + "healthDegraded": "Sorunlu", + "healthDown": "Yanıt vermiyor" }, "Install": { "metaTitle": "Claude Code, Codex ve OpenCode'a kurun", @@ -175,20 +232,20 @@ "stepUseAgentText": "Kodlama ajanınızdan TakoAPI'deki bir yapay zekâ ajanını bulmasını veya çağırmasını isteyin.", "heroBadge": "Tek komut, üç ajan", "heroTitle": "TakoAPI'yi kodlama ajanınıza kurun", - "heroSubtitle": "TakoAPI becerisini Claude Code, Codex veya OpenCode'a ekleyin — ardından doğrudan terminalinizden, tek bir birleşik ağ geçidi üzerinden herhangi bir yapay zekâ ajanını keşfedin ve çağırın.", + "heroSubtitle": "TakoAPI becerisini Claude Code, Codex veya OpenCode'a ekleyin — sonra doğrudan terminalinizden, tek bir birleşik ağ geçidi üzerinden herhangi bir yapay zekâ ajanını keşfedin ve çağırın.", "whatTitle": "Kurulum aracı ne yapar", - "whatWritesSkill": "Her ajanın kendi beceriler dizinine tek, ad alanı belirlenmiş bir TakoAPI becerisi yazar — paylaşılan yapılandırma dosyalarınızı asla düzenlemez.", - "whatTeachesAgent": "Ajanınıza TakoAPI kaydını ve ağ geçidini HTTP üzerinden çağırmayı öğretir — ek bağımlılık yok, çalıştırılacak sunucu yok.", + "whatWritesSkill": "Her ajanın kendi beceri dizinine tek ve ad alanı belirlenmiş bir TakoAPI becerisi yazar — paylaşılan yapılandırma dosyalarınızı asla düzenlemez.", + "whatTeachesAgent": "Ajanınıza TakoAPI kayıt defterini ve ağ geçidini HTTP üzerinden çağırmayı öğretir — ek bağımlılık yok, çalıştırılacak sunucu yok.", "whatMcpServer": "Barındırılan araçları mı tercih edersiniz? Bunun yerine TakoAPI MCP sunucusunu kaydedin — claude mcp add --transport http takoapi https://takoapi.com/mcp — search_agents, get_agent, search_skills ve invoke_agent araçlarını yerel MCP araçları olarak sunar.", - "whatIdempotent": "Bağımsız çalıştırılabilirdir (yeniden çalıştırmak güvenlidir) ve --uninstall ile tamamen geri alınabilir.", - "whatApiKey": "Sayaçlı ağ geçidi için panelinizde bir API anahtarı oluşturun ve onu TAKO_KEY olarak dışa aktarın.", - "footnote": "Windows'ta mısınız veya zaten Node içinde misiniz? npx takoapi-install komutunu kullanın. Önce betiği okumayı mı tercih edersiniz? takoapi.com/install.sh adresinde düz kabuk betiği olarak bulunur." + "whatIdempotent": "İdempotenttir (yeniden çalıştırmak güvenlidir) ve --uninstall ile tamamen geri alınabilir.", + "whatApiKey": "Sayaçlı ağ geçidi için panelinizde bir API anahtarı oluşturun ve TAKO_KEY olarak dışa aktarın.", + "footnote": "Windows'ta mısınız veya zaten Node içinde misiniz? npx takoapi-install komutunu kullanın. Önce betiği okumayı mı tercih edersiniz? takoapi.com/install.sh adresinde düz kabuk betiği olarak duruyor." }, "InstallTabs": { "universalNote": "TakoAPI becerisini Claude Code, Codex ve OpenCode'a kurar — otomatik algılanır. Yeniden çalıştırmak güvenlidir, root gerektirmez. Windows'ta veya Node içinde kullanın.", "tablistLabel": "Kodlama ajanları", - "claudeCodeTagline": "Anthropic'in CLI'ı", - "codexTagline": "OpenAI'nin CLI'ı", + "claudeCodeTagline": "Anthropic'in CLI aracı", + "codexTagline": "OpenAI'nin CLI aracı", "opencodeTagline": "opencode.ai", "skillHeading": "Beceri — ajanınıza TakoAPI'yi çağırmayı öğretir", "skillUniversalHint": "…ya da yukarıdaki evrensel komutu kullanın — {name} için de geçerlidir.", @@ -196,13 +253,13 @@ "howToUseHeading": "Nasıl kullanılır", "claudeCodeUse": "Otomatik yüklenir — yalnızca Claude'dan TakoAPI'de bir ajan bulmasını isteyin.", "codexUse": "$takoapi ile çağırın veya /skills menüsünden seçin.", - "opencodeUse": "/takoapi komutunu çalıştırın veya @takoapi'ye devredin.", + "opencodeUse": "/takoapi '' komutunu çalıştırın veya işi @takoapi'ye devredin.", "mcpHeading": "MCP sunucusu — barındırılan araçlar, kurulacak bir şey yok", - "claudeCodeMcpHint": "Tek komut. invoke_agent'ı etkinleştirmek için --header \"Authorization: Bearer $TAKO_KEY\" ekleyin.", + "claudeCodeMcpHint": "Tek komut. invoke_agent aracını etkinleştirmek için --header \"Authorization: Bearer $TAKO_KEY\" ekleyin.", "codexMcpHint": "~/.codex/config.toml dosyasına ekleyin", "opencodeMcpHint": "~/.config/opencode/opencode.json dosyasına birleştirin", "mcpToolsNote": "Şu araçları ekler: search_agents, get_agent, search_skills, invoke_agent.", - "uninstallHeading": "Kaldır", + "uninstallHeading": "Kaldırma", "uninstallNote": "Yalnızca TakoAPI'nin kendi dosyalarını kaldırır — diğer ajan yapılandırmanıza asla dokunmaz.", "copy": "Kopyala", "copied": "Kopyalandı" @@ -226,25 +283,30 @@ "requirements": "Gereksinimler", "installation": "Kurulum", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClaw Chat (bunu sohbetinize yapıştırın)", "links": "Bağlantılar", "github": "GitHub", - "copyGithubUrl": "GitHub URL'sini kopyala", "clawHub": "ClawHub", - "skillNotFound": "Beceri bulunamadı" + "codingAgent": "Kodlama ajanı", + "agentPrompt": "{url} adresindeki beceriyi kur", + "installHintRegistry": "CLI komutunu çalıştırın ya da istemi Claude Code, Cursor, Codex veya kullandığınız kodlama ajanına yapıştırın.", + "installHintRepo": "Bu beceri ClawHub kayıt defterinde yayınlanmadı. İstemi kodlama ajanınıza — Claude Code, Cursor, Codex — yapıştırın; beceriyi doğrudan kaynak deposundan kuracaktır.", + "installUnavailable": "Bu beceri için henüz bir kurulum kaynağı yayınlanmadı.", + "openRepository": "Depoyu aç", + "signInToLike": "Bu beceriyi beğenmek için giriş yapın", + "downloadsSource": "İndirme sayıları clawskills.sh tarafından bildirilir" }, "Skills": { - "metaTitle": "Becerilere Gözat", - "metaDescription": "Kodlama ajanınız için binlerce OpenClaw becerisini onlarca kategoride gözatın — arama, filtreleme ve öne çıkanlar tablosuyla.", - "ogTitle": "Becerilere Gözat — {siteName}", + "metaTitle": "Becerilere göz at", + "metaDescription": "Kodlama ajanınız için binlerce OpenClaw becerisine onlarca kategoride göz atın — arama, filtreleme ve öne çıkanlar tablosuyla.", + "ogTitle": "Becerilere göz at — {siteName}", "ogDescription": "Kodlama ajanınız için binlerce OpenClaw becerisi.", - "heading": "Becerilere Gözat", + "heading": "Becerilere göz at", "searchLabel": "Arama: \"{query}\"", "count": "{count, number} beceri bulundu", - "sortLatest": "En Yeni", - "sortPopular": "En Popüler", - "sortViews": "En Çok Görüntülenen", - "sortStars": "En Çok Yıldızlı", + "sortLatest": "En yeni", + "sortPopular": "En popüler", + "sortViews": "En çok görüntülenen", + "sortStars": "En çok yıldızlı", "agentAll": "Tüm ajanlar", "agentClaudeCode": "Claude Code", "agentCursor": "Cursor", @@ -256,19 +318,38 @@ "agentGeneric": "Genel", "all": "Tümü", "emptyTitle": "Beceri bulunamadı", - "emptyHint": "Aramanızı veya filtrelerinizi ayarlamayı deneyin", - "pageOf": "Sayfa {page} / {total}" + "emptyHint": "Aramanızı veya filtrelerinizi değiştirmeyi deneyin", + "pageOf": "Sayfa {page} / {total}", + "metaTitleSearch": "\"{query}\" ile eşleşen beceriler", + "metaTitlePaged": "Becerilere göz at — sayfa {page}", + "searchInputLabel": "Becerilerde ara", + "searchPlaceholder": "Becerileri ada, açıklamaya veya yazara göre arayın", + "searchButton": "Ara", + "agentFilter": "Ajan", + "agentZed": "Zed", + "sourceFilter": "Kaynak", + "sourceGithub": "GitHub", + "sourceCommunity": "Topluluk", + "sourceCurated": "Seçilmiş", + "categoryFilter": "Kategori", + "sortLabel": "Sırala", + "sortRelevance": "En iyi eşleşme", + "clearFilters": "Filtreleri temizle", + "paginationLabel": "Beceri listesi sayfaları", + "goToPage": "{page}. sayfaya git", + "prev": "Önceki", + "next": "Sonraki" }, "Trending": { - "metaTitle": "Öne Çıkan Beceriler", - "metaDescription": "Şu anda en çok indirilen ve en çok beğenilen OpenClaw becerileri — kodlama ajanlarının neler kurduğunu gösteren canlı bir liderlik tablosu.", - "ogTitle": "Öne Çıkan Beceriler — {siteName}", - "ogDescription": "En popüler OpenClaw becerilerinin canlı bir liderlik tablosu.", - "title": "Öne Çıkan Beceriler", - "subtitle": "Topluluk tarafından sıralanan en popüler OpenClaw becerilerini keşfedin", + "metaTitle": "Öne çıkan beceriler", + "metaDescription": "En çok indirilen ve en çok beğenilen OpenClaw becerileri — clawskills.sh tarafından bildirilen indirme sayılarına dayanan, her gün yenilenen bir sıralama.", + "ogTitle": "Öne çıkan beceriler — {siteName}", + "ogDescription": "En popüler OpenClaw becerilerinin her gün güncellenen sıralaması.", + "title": "Öne çıkan beceriler", + "subtitle": "Topluluğun sıraladığı en popüler OpenClaw becerilerini keşfedin", "searchPlaceholder": "Öne çıkan becerilerde ara…", "categories": "Kategoriler", - "allCategories": "Tüm Kategoriler", + "allCategories": "Tüm kategoriler", "downloads": "İndirmeler", "stars": "Yıldızlar", "likes": "Beğeniler", @@ -277,15 +358,21 @@ "count": "{count, plural, =0 {Beceri yok} one {# beceri} other {# beceri}}", "countInCategory": "{category} kategorisinde {count, plural, =0 {beceri yok} one {# beceri} other {# beceri}}", "emptyTitle": "Beceri bulunamadı", - "emptySubtitle": "Aramanızı veya filtrelerinizi ayarlamayı deneyin", + "emptySubtitle": "Aramanızı veya filtrelerinizi değiştirmeyi deneyin", "byAuthor": "{author} tarafından", "showing": "{total, number} sonuçtan {from, number}–{to, number} arası gösteriliyor", "prev": "Önceki", - "next": "Sonraki" + "next": "Sonraki", + "provenanceNote": "İndirme ve yıldız sayıları üst kaynak katalogdan — clawskills.sh ve GitHub — gelir ve her gece yenilenir; TakoAPI üzerinden yapılan kurulumları ölçmez. Beğeniler ve görüntülenmeler burada sayılır.", + "search": "Ara", + "sortLabel": "Sıralama ölçütü", + "rank": "Sıra", + "goToPage": "{page}. sayfaya git", + "paginationLabel": "Sıralama tablosu sayfaları" }, "Admin": { "accessDenied": "Erişim Reddedildi", - "accessDeniedDescription": "Bu sayfaya erişmek için yönetici ayrıcalıklarına ihtiyacınız var.", + "accessDeniedDescription": "Bu sayfaya erişmek için yönetici yetkisine ihtiyacınız var.", "goHome": "Ana Sayfaya Git", "backToSite": "Siteye dön", "panelTitle": "Yönetim Paneli", @@ -335,8 +422,8 @@ "actionReject": "Reddet", "feature": "Öne Çıkar", "unfeature": "Öne Çıkarmayı Kaldır", - "scenariosEditorHint": "场景 Senaryolar — 点击切换 / atamak için değiştir", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Senaryolar — atamak veya kaldırmak için tıklayın", + "scenarioOption": "{name}", "selectedCount": "{count, number} seçildi", "rejectReasonPrompt": "Reddetme nedeni (isteğe bağlı):", "deleteAgentConfirm": "Bu ajan kalıcı olarak silinsin mi?", @@ -398,9 +485,9 @@ "noCategoriesFound": "Kategori bulunamadı. Yukarıdan bir tane ekleyin.", "deleting": "...", "cannotDeleteHasSkills": "Silinemiyor: becerileri var", - "categoriesTotal": "{count, number} {count, plural, one {kategori} other {kategori}} toplam", + "categoriesTotal": "toplam {count, number} {count, plural, one {kategori} other {kategori}}", "usersTitle": "Kullanıcılar", - "usersTotal": "{count, number} {count, plural, one {kullanıcı} other {kullanıcı}} toplam", + "usersTotal": "toplam {count, number} {count, plural, one {kullanıcı} other {kullanıcı}}", "searchUsersPlaceholder": "Ada veya e-postaya göre ara...", "colEmail": "E-posta", "colRole": "Rol", @@ -417,7 +504,7 @@ "demoteToUser": "Kullanıcıya düşür", "promoteToAdmin": "Yöneticiye yükselt", "logsTitle": "Yönetim Günlükleri", - "logsTotal": "{count, number} {count, plural, one {kayıt} other {kayıt}} toplam", + "logsTotal": "toplam {count, number} {count, plural, one {kayıt} other {kayıt}}", "colTimestamp": "Zaman Damgası", "colAdmin": "Yönetici", "colAction": "İşlem", @@ -436,12 +523,51 @@ "targetSkill": "beceri", "targetAgent": "ajan", "targetCategory": "kategori", - "targetUser": "kullanıcı" + "targetUser": "kullanıcı", + "adminNavLabel": "Yönetim bölümleri", + "retry": "Yeniden dene", + "close": "Kapat", + "prevPage": "Önceki sayfa", + "nextPage": "Sonraki sayfa", + "sectionAgents": "Ajanlar", + "sectionSkills": "Beceriler ve topluluk", + "statTotalAgents": "Toplam Ajan", + "statPendingAgents": "Bekleyen Ajan", + "statHostedAgents": "Barındırılan", + "statProjectAgents": "Projeler", + "pendingAgentsTitle": "İncelenmeyi bekleyen ajanlar", + "noPendingAgents": "İnceleme bekleyen ajan yok.", + "kindHosted": "Barındırılan", + "kindProject": "Proje", + "filterByStatus": "Duruma göre filtrele", + "filterDisabled": "Devre dışı", + "actionDisable": "Devre dışı bırak", + "actionEnable": "Yeniden etkinleştir", + "disableAgentTitle": "Ajanı Devre Dışı Bırak", + "disableAgentConfirm": "{name} siz yeniden etkinleştirene kadar herkese açık katalogda görünmeyecek. Hiçbir şey silinmez.", + "deleteAgentTitle": "Ajanı Sil", + "deleteAgentConfirmNamed": "{name} ajanını silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.", + "rejectAgentTitle": "Ajanı Reddet", + "rejectReasonLabel": "Neden (isteğe bağlı, yayıncıya gösterilir)", + "rejectReasonPlaceholder": "örn. uç nokta örnek görevde 500 döndürüyor", + "healthOk": "Sağlıklı", + "healthDegraded": "Sorunlu", + "healthDown": "Çalışmıyor", + "healthUnknown": "Kontrol edilmedi", + "healthCheckedAt": "Son sağlık kontrolü: {date}", + "healthNeverChecked": "Sağlık kontrolü kaydı yok", + "batchDeleteTitle": "Becerileri Sil", + "selectAllRows": "Tüm satırları seç", + "selectRow": "{name} satırını seç", + "editSkillNamed": "{name} becerisini düzenle", + "deleteSkillNamed": "{name} becerisini sil", + "editCategoryNamed": "{name} kategorisini düzenle", + "deleteCategoryNamed": "{name} kategorisini sil" }, "Auth": { - "signInTitle": "TakoAPI'ye giriş yap", - "signInSubtitle": "Beceriler panelinize erişin", - "signUpTitle": "Hesap oluştur", + "signInTitle": "TakoAPI'ye giriş yapın", + "signInSubtitle": "Panelinize erişin", + "signUpTitle": "Hesap oluşturun", "signUpSubtitle": "TakoAPI topluluğuna katılın", "continueWithGoogle": "Google ile devam et", "continueWithApple": "Apple ile devam et", @@ -462,13 +588,27 @@ "noAccount": "Hesabınız yok mu?", "signUpLink": "Kaydol", "haveAccount": "Zaten bir hesabınız var mı?", - "signInLink": "Giriş yap" + "signInLink": "Giriş yap", + "forgotPassword": "Parolanızı mı unuttunuz?", + "forgotTitle": "Parolanızı sıfırlayın", + "forgotSubtitle": "Yeni bir parola seçmeniz için size bağlantı göndereceğiz", + "sendResetLink": "Sıfırlama bağlantısı gönder", + "sendingResetLink": "Gönderiliyor...", + "resetLinkSent": "Bu adrese ait bir hesap varsa parola sıfırlama bağlantısını gönderdik. Gelen kutunuzu kontrol edin.", + "backToSignIn": "Girişe dön", + "resetTitle": "Yeni bir parola seçin", + "resetSubtitle": "Hesabınız için yeni bir parola girin", + "newPasswordHint": "Yeni parola (en az 8 karakter)", + "updatePassword": "Parolayı güncelle", + "updatingPassword": "Güncelleniyor...", + "passwordUpdated": "Parolanız güncellendi. Giriş sayfasına yönlendiriliyorsunuz...", + "resetLinkInvalid": "Bu parola sıfırlama bağlantısı geçersiz veya süresi dolmuş.", + "requestNewLink": "Yeni bağlantı iste" }, "Dashboard": { "title": "Geliştirici Paneli", - "signInPrompt": "API anahtarlarını yönetmek ve kullanımı görüntülemek için giriş yapın.", - "signIn": "Giriş yap", - "description": "API anahtarlarını yönetin ve ağ geçidi kullanımını izleyin. Tek anahtar, tek fatura, herhangi bir ajan.", + "signInPrompt": "API anahtarlarını yönetmek ve kullanımı görmek için giriş yapın.", + "description": "API anahtarlarını yönetin ve ağ geçidi kullanımını izleyin. Tek anahtar, tek fatura, her ajan.", "apiKeys": "API Anahtarları", "createKey": "Anahtar oluştur", "creating": "Oluşturuluyor…", @@ -480,9 +620,9 @@ "created": "{date} tarihinde oluşturuldu", "lastUsed": "son kullanım {date}", "neverUsed": "hiç kullanılmadı", - "revoke": "İptal Et", - "revokeConfirm": "Bu anahtar iptal edilsin mi? Onu kullanan uygulamalar çalışmayı durduracak.", - "quickstart": "Hızlı Başlangıç", + "revoke": "İptal et", + "revokeConfirm": "Bu anahtar iptal edilsin mi? Onu kullanan uygulamalar çalışmayı durdurur.", + "quickstart": "Hızlı başlangıç", "usage": "Kullanım", "totalCalls": "Toplam çağrı", "agentsUsed": "Kullanılan ajanlar", @@ -493,15 +633,14 @@ "tableLatency": "Gecikme", "tableWhen": "Zaman", "tableCost": "Maliyet", - "noCalls": "Henüz çağrı yok. Burada kullanımı görmek için bir anahtar oluşturun ve bir ajan çağırın.", + "noCalls": "Henüz çağrı yok. Kullanımı burada görmek için bir anahtar oluşturun ve bir ajan çağırın.", "billing": "Krediler ve Faturalama", "creditBalance": "Kredi bakiyesi", "totalSpend": "Toplam harcama", - "topUpComingSoon": "Krediler şimdilik platform tarafından yükleniyor; PayPal ile kendin yükle özelliği yakında geliyor.", "topUpLabel": "Kredi ekle", "topUpButton": "PayPal ile yükle", "topUpRedirecting": "Yönlendiriliyor…", - "topUpSuccess": "Kredi eklendi — bakiyen güncellendi.", + "topUpSuccess": "Kredi eklendi — bakiyeniz güncellendi.", "topUpError": "Yükleme tamamlanmadı. Lütfen tekrar deneyin.", "topUpCancel": "Yükleme iptal edildi.", "noLedger": "Henüz kredi hareketi yok.", @@ -513,35 +652,65 @@ "ledgerDebit": "Çağrı ücreti", "ledgerPayout": "Ödeme", "ledgerRefund": "İade", - "ledgerAdjust": "Düzeltme" + "ledgerAdjust": "Düzeltme", + "onboardingTitle": "İlk çağrınızı çalışır hâle getirin", + "onboardingSubtitle": "Buradan bir ajanın size yanıt vermesine üç adım.", + "stepKeyTitle": "API anahtarı oluşturun", + "stepKeyBody": "Tek anahtar TakoAPI'deki her ajanı çağırır. Gizli değer yalnızca oluşturduktan hemen sonra bir kez gösterilir.", + "stepCallTitle": "İlk çağrınızı yapın", + "stepCallBody": "Aşağıdaki örneklerden birini kopyalayın, TAKO_KEY değerini anahtarınıza ayarlayın ve çalıştırın. Çağrı saniyeler içinde Kullanım bölümünde görünür.", + "stepInstallTitle": "Kodlama ajanınıza kurun", + "stepInstallBody": "Claude Code, Codex veya OpenCode'a TakoAPI üzerinden ajan keşfetmeyi ve çağırmayı öğretin.", + "stepInstallLink": "Kurulum rehberini aç", + "stepOptional": "İsteğe bağlı", + "stepDone": "Tamam", + "stepTodo": "Henüz yapılmadı", + "keyNameLabel": "Anahtar adı", + "keyNamePlaceholder": "Ağ geçidi anahtarı", + "createKeyError": "Anahtar oluşturulamadı. Lütfen tekrar deneyin.", + "revokeNamed": "{name} anahtarını iptal et", + "quickstartHint": "TAKO_KEY değerini yukarıdaki anahtarlardan birine ayarlayın, sonra herhangi bir ajanı kısa adıyla çağırın.", + "creditRule": "Ücretsiz ajanlar hiçbir şeye mal olmaz; ücretli ajanlar ön ödemeli kredinizden düşer ve bakiye sıfırın altına inecek olduğu anda çağrı HTTP 402 ile reddedilir.", + "topUpUnavailable": "Kendi kendine yükleme henüz kullanılamıyor — krediler şimdilik platform tarafından ekleniyor. Yukarıdaki bakiyeniz her durumda günceldir.", + "dismiss": "Kapat", + "loadError": "Bazı panel verileri yüklenemedi.", + "retry": "Yeniden dene", + "recentCallsCaption": "En son ağ geçidi çağrılarınız", + "chartTitle": "Günlük çağrılar, son {days} gün", + "chartTotal": "toplam {count, number}", + "chartAria": "Son {days} gündeki günlük ağ geçidi çağrılarının çubuk grafiği, toplam {count, number} çağrı.", + "chartDay": "{date}: {calls, number} çağrı, {errors, number} başarısız" }, "Profile": { - "title": "Profilim", - "signInPrompt": "Profilinizi görüntülemek için lütfen giriş yapın.", - "signIn": "Giriş yap", - "submissions": "Gönderimler", + "title": "Yayınlarım", + "signInPrompt": "Yayınladığınız ajanları ve becerileri görmek için giriş yapın.", "likes": "Beğeniler", - "views": "Görüntülenmeler", - "statValue": "{count, number}", - "mySubmissions": "Gönderimlerim", - "submitNew": "Yeni gönder", + "views": "görüntülenme", "tab_all": "Tümü", - "tab_approved": "Onaylandı", - "tab_pending": "Beklemede", - "tab_rejected": "Reddedildi", - "tabCount": "{count, number}", - "emptyTab": "Bu sekmede gönderim yok", + "emptyTab": "Burada henüz beceri yok.", "submitFirst": "İlk becerinizi gönderin", - "statusApproved": "Onaylandı", + "statusApproved": "Yayında", "statusPending": "İncelemede", "statusRejected": "Reddedildi", - "reviewerNote": "İnceleyen notu:" + "reviewerNote": "İnceleyen notu:", + "publishAgent": "Ajan yayınla", + "submitSkill": "Beceri gönder", + "statAgents": "Ajanlar", + "statCalls": "Ağ geçidi çağrıları", + "statSkills": "Beceriler", + "tabAgents": "Ajanlar", + "tabSkills": "Beceriler", + "statusDisabled": "Devre dışı", + "kindHosted": "Çağrılabilir", + "kindProject": "Açık kaynak", + "noAgents": "Burada henüz ajan yok.", + "publishFirstAgent": "İlk ajanınızı yayınlayın", + "myListings": "Yayınlarım" }, "Submit": { "title": "Beceri Gönder", "subtitle": "OpenClaw becerinizi toplulukla paylaşın", "signInPrompt": "Beceri göndermek için lütfen giriş yapın.", - "signIn": "Giriş yap", "skillSource": "Beceri Kaynağı", "githubUrl": "GitHub URL'si", "clawSkillsUrl": "ClawSkills.sh URL'si", @@ -571,8 +740,15 @@ "submitting": "Gönderiliyor...", "enterUrlFirst": "Lütfen önce bir URL girin", "autoFillFailed": "Otomatik doldurma başarısız", - "submitFailed": "Gönderilemedi", - "somethingWentWrong": "Bir şeyler ters gitti" + "somethingWentWrong": "Bir şeyler ters gitti", + "successTitle": "Gönderildi 🎉", + "pendingReview": "{name} şu anda incelenmeyi bekliyor.", + "nextStepsTitle": "Bundan sonra ne olacak", + "nextStepReview": "Bir inceleyen, depoyu ve açıklamayı kontrol eder.", + "nextStepEmail": "İnceleme tamamlandığında size e-posta göndeririz.", + "nextStepProfile": "Yayınlarım sayfası her zaman güncel durumu ve varsa inceleyen notunu gösterir.", + "viewMyListings": "Yayınlarımı görüntüle", + "backToSkills": "← Becerilere dön" }, "SubmitAgent": { "title": "Ajan Yayınla", @@ -598,49 +774,82 @@ "pricingFree": "Ücretsiz", "pricingPerCall": "Çağrı başına", "pricingPerTask": "Görev başına", - "pricingPerToken": "1k jeton başına", + "pricingPerToken": "1k token başına", "unitPriceLabel": "Birim fiyat (USD)", "unitPricePlaceholder": "0.002", "homepageLabel": "Ana sayfa", "optional": "isteğe bağlı", "homepagePlaceholder": "https://your-agent.com", - "submit": "Ajan Yayınla", + "submit": "Ajanı Yayınla", "submitting": "Gönderiliyor…", "submitFailed": "Gönderilemedi", "somethingWentWrong": "Bir şeyler ters gitti", "successTitle": "Gönderildi 🎉", "pendingReview": "{name} şu anda incelenmeyi bekliyor.", "successDetail": "AgentCard'ı doğrulayacağız ve onaylandıktan sonra pazaryerinde yayınlayacağız.", - "backToMarketplace": "← Pazaryerine dön" + "backToMarketplace": "← Pazaryerine dön", + "checkCard": "Kartı kontrol et", + "checking": "Kontrol ediliyor…", + "enterCardUrlFirst": "Önce bir AgentCard URL'si girin.", + "previewTitle": "Listeleyeceğimiz bilgiler bunlar", + "previewNoDescription": "Kartın açıklaması yok.", + "previewEndpoint": "Uç nokta", + "previewCapabilities": "Yetenekler", + "previewSkillCount": "{count, plural, =0 {Bildirilen yetenek yok} one {# yetenek} other {# yetenek}}", + "previewSupports": "Destekler", + "previewStreaming": "Akış", + "previewPush": "Anlık bildirimler", + "previewHint": "Henüz hiçbir şey kaydedilmedi — incelemeye göndermek için aşağıdan yayınlayın.", + "nextStepsTitle": "Bundan sonra ne olacak", + "nextStepReview": "Bir inceleyen, AgentCard'ı ve uç noktanızın yanıt verdiğini kontrol eder.", + "nextStepEmail": "İnceleme tamamlandığında size e-posta göndeririz.", + "nextStepProfile": "Yayınlarım sayfası her zaman güncel durumu ve varsa inceleyen notunu gösterir.", + "viewMyListings": "Yayınlarımı görüntüle" }, "AgentEngagement": { - "yourRating": "Senin puanın", + "yourRating": "Puanınız", "ratingsCount": "{count, number} değerlendirme", "noRatings": "Henüz değerlendirilmedi", "bookmark": "Kaydet", "bookmarked": "Kaydedildi", - "signInToRate": "Bu aracıyı puanlamak veya kaydetmek için giriş yap" + "signInToRate": "Bu ajanı puanlamak veya kaydetmek için giriş yapın", + "rateStars": "{count, plural, one {# yıldız} other {# yıldız}} ver" }, "Bookmarks": { - "title": "Kaydedilen aracılar", - "subtitle": "Kaydettiğin aracılar.", - "empty": "Henüz kayıt yok.", - "browseAgents": "Aracılara göz at", - "signInPrompt": "Kayıtlarını görmek için giriş yap.", - "signIn": "Giriş yap" + "title": "Kaydedilen ajanlar", + "subtitle": "Kaydettiğiniz ajanlar.", + "empty": "Henüz kaydedilen ajan yok.", + "browseAgents": "Ajanlara göz at", + "signInPrompt": "Kaydettiklerinizi görmek için giriş yapın." }, "ScenarioPage": { - "metaTitle": "{scenario} için yapay zekâ aracıları", - "metaDescription": "{scenario} için yapay zekâ aracılarını tek bir birleşik API üzerinden keşfedin ve çağırın — TakoAPI'de açık A2A AgentCard'larıyla tanımlanır.", - "heroSubtitle": "{scenario} için yapay zekâ aracıları, tek bir API ile çağrılabilir.", - "empty": "Bu senaryoda henüz aracı yok.", - "browseAll": "Tüm aracılara göz at", + "metaTitle": "{scenario} için yapay zekâ ajanları", + "metaDescription": "{scenario} için yapay zekâ ajanlarını tek bir birleşik API üzerinden keşfedin ve çağırın — TakoAPI'de açık A2A AgentCard'larıyla tanımlanır.", + "heroSubtitle": "{scenario} için yapay zekâ ajanları, tek bir birleşik API üzerinden çağrılabilir.", + "empty": "Bu senaryoda henüz ajan yok.", + "browseAll": "Tüm ajanlara göz at", "back": "← Tüm senaryolar", "breadcrumbScenarios": "Senaryolar", - "indexTitle": "Yapay zekâ aracılarını senaryoya göre keşfedin", - "indexDescription": "Yapay zekâ aracılarını kullanım amacına göre bulun — frontend, yatırım, pazarlama, destek ve daha fazlası — hepsi tek bir API ile çağrılabilir.", - "indexHeroTitle": "Senaryoya göre keşfet", - "indexHeroSubtitle": "Yapmanız gereken işe göre yapay zekâ aracıları bulun." + "indexTitle": "Yapay zekâ ajanlarına senaryoya göre göz atın", + "indexDescription": "Yapay zekâ ajanlarını kullanım amacına göre bulun — ön yüz, yatırım, pazarlama, destek ve daha fazlası — hepsi tek bir API üzerinden çağrılabilir.", + "indexHeroTitle": "Senaryoya göre göz atın", + "indexHeroSubtitle": "Yaptırmak istediğiniz işe göre yapay zekâ ajanı bulun.", + "indexPopularHeading": "İyi kapsanan", + "indexPopularHint": "Karşılaştırabileceğiniz zengin ajan kataloğuna sahip senaryolar.", + "indexSparseHeading": "Yeni başlayan", + "indexSparseHint": "Şimdilik yalnızca birkaç ajan var — ilk olmak için uygun bir yer.", + "indexSparseCta": "Bunlardan birinde ajan yayınlayın →", + "metaTitlePaged": "{scenario} için yapay zekâ ajanları — sayfa {page}", + "resultsLabel": "{scenario} ajanları", + "paginationLabel": "Sayfalama", + "showingRange": "{total} sonuçtan {from}–{to} arası gösteriliyor", + "prev": "← Önceki", + "next": "Sonraki →", + "goToPage": "{page}. sayfaya git", + "ctaTitle": "{scenario} için bir ajan biliyor musunuz?", + "ctaBody": "Bu senaryo hâlâ zayıf. Bir ajan yayınlayın; {scenario} senaryosuna göz atan herkes için burada görünsün.", + "ctaButton": "Ajan yayınla", + "relatedHeading": "Diğer senaryoları keşfedin" }, "Badge": { "snippetTitle": "Rozet ekle", @@ -651,20 +860,165 @@ "pageIntro": "Projenizin TakoAPI'de listelendiğini gösterin — sayfanıza bağlanan ve güncel yıldız sayınızı gösteren küçük bir rozet.", "example": "Örnek", "step1Title": "Sayfanızı bulun", - "step1": "Projenizi aracı dizininde arayın.", + "step1": "Projenizi ajan dizininde arayın.", "step2Title": "Rozeti kopyalayın", - "step2": "Sayfanızda rozet kutusundan Markdown'ı kopyalayın.", + "step2": "Sayfanızda rozet kutusundaki Markdown'ı kopyalayın.", "step3Title": "README'nize yapıştırın", - "step3": "Üst kısma ekleyin — ziyaretçiler ve arama motorları TakoAPI sayfanıza bağlantı alır.", - "findListing": "Sayfanızı bulun" + "step3": "Üst kısma ekleyin — ziyaretçiler ve arama motorları TakoAPI sayfanıza bir bağlantı bulur.", + "findListing": "Sayfanızı bulun", + "exampleAlt": "TakoAPI rozeti" }, "Blog": { "title": "Blog", - "subtitle": "A2A, agent ağ geçitleri ve TakoAPI ile geliştirme üzerine rehberler.", + "subtitle": "A2A, ajan ağ geçitleri ve TakoAPI ile geliştirme üzerine rehberler.", "readMore": "Devamını oku", "backToBlog": "← Tüm yazılar", "minRead": "{min} dk okuma", "published": "Yayınlandı", "faqTitle": "SSS" + }, + "NotFound": { + "title": "Bu sayfayı bulamadık", + "description": "Bağlantı güncelliğini yitirmiş ya da kayıt kaldırılmış olabilir. Dizini deneyin veya üst menüden arama yapın.", + "home": "Ana sayfa", + "agents": "Ajanlara göz at", + "skills": "Becerilere göz at" + }, + "ErrorPage": { + "title": "Bir şeyler ters gitti", + "description": "Bu sayfa yüklenemedi. Tekrar denemek çoğu zaman işe yarar; yaramazsa aşağıdaki referans, sorunu kayıtlarda bulmamıza yardımcı olur.", + "reference": "Referans: {id}", + "retry": "Tekrar dene", + "home": "Ana sayfaya git" + }, + "Consent": { + "title": "Analitik", + "description": "İnsanların hangi sayfaları yararlı bulduğunu görmek için Google Analytics kullanıyoruz. Siz kabul etmedikçe hiçbir şey yüklenmez ve çerez yerleştirilmez.", + "accept": "Kabul et", + "decline": "Reddet" + }, + "Docs": { + "metaTitle": "API belgeleri", + "title": "TakoAPI API referansı", + "description": "API anahtarından çalışan bir ajan çağrısına: kimlik doğrulama, keşif uç noktaları, birleşik ajan ağ geçidi, barındırılan MCP sunucusu, hata kodları, hız sınırları ve faturalama.", + "heroBadge": "Geliştirici belgeleri", + "heroTitle": "TakoAPI API referansı", + "heroSubtitle": "Tek anahtar, her ajan. OpenAI uyumlu herhangi bir istemciyi TakoAPI'ye yönlendirin ve bir ajanı kısa adıyla çağırın — ya da hiç anahtar kullanmadan tüm kataloğa göz atın.", + "heroKeyCta": "API anahtarı oluştur", + "heroBrowseCta": "Ajanlara göz at", + "tocTitle": "Bu sayfada", + "tocLabel": "İçindekiler", + "quickstartTitle": "Hızlı başlangıç", + "quickstartIntro": "Sıfırdan bir yanıta üç adım. Bu sayfadaki her istek, canlı API'ye yapılan gerçek bir çağrıdır.", + "quickstartStep1Title": "Anahtar oluşturun", + "quickstartStep1": "Giriş yapın ve panelinizde bir API anahtarı oluşturun. Gizli değer yalnızca oluşturulurken bir kez gösterilir.", + "quickstartStep2Title": "Dışa aktarın", + "quickstartStep2": "Bu sayfadaki her örnek, anahtarı TAKO_KEY değişkeninden okur.", + "quickstartStep3Title": "Bir ajan çağırın", + "quickstartStep3": "Modeli — ya da URL'deki kısa adı — katalogdaki herhangi bir barındırılan ajana ayarlayın.", + "quickstartSamplesLabel": "Hızlı başlangıç çağrı örnekleri", + "quickstartNote": "Bu örneklerdeki kısa ad bir yer tutucudur, gerçek bir ajan değildir. Herhangi bir ajan sayfasından gerçeğini kopyalayın; yalnızca barındırılan olarak listelenen ajanlar çağrılabilir.", + "authenticationTitle": "Kimlik doğrulama", + "authenticationIntro": "Ağ geçidi çağrıları bir bearer token ile doğrulanır. Keşif uç noktaları anonimdir ve hiç anahtar gerektirmez.", + "authHeaderNote": "Authorization başlığına alternatif olarak x-api-key da kabul edilir. Anahtarlar her zaman tako_live_ ile başlar.", + "authWhereTitle": "Anahtarlar nereden gelir", + "authWhere": "Panelinizde her istemci veya ortam için ayrı bir anahtar oluşturun ve kullanmayı bıraktıklarınızı iptal edin.", + "authOnceTitle": "Gizli değer bir kez gösterilir", + "authOnce": "Yalnızca anahtarın SHA-256 özeti ve kısa bir görüntüleme öneki saklanır; bu yüzden kaybolan bir gizli değer kurtarılamaz — yerine yenisini oluşturun ve eskisini iptal edin.", + "authRevokeTitle": "İptal anında geçerlidir", + "authRevoke": "Her çağrı anahtarı veritabanına karşı yeniden denetler ve hiçbir şey önbelleğe alınmaz; bu yüzden iptal edilen bir anahtar bir sonraki istekte reddedilir. Geçmiş kullanım geçmişinizde kalır.", + "discoveryTitle": "Keşif", + "discoveryIntro": "Anahtar gerektirmeyen, önbelleklenebilir dört herkese açık uç nokta. Yalnızca onaylanmış ajanlar ve beceriler döndürülür.", + "endpointRegistrySummary": "Tüm ajan kataloğu — model için Markdown, kod için JSON.", + "endpointRegistryNote": "format=json olmadan yanıt Markdown'dır (text/markdown); barındırılan ajanlar ve açık kaynaklı projeler için iki tabloya bölünür ve doğrudan bir modelin bağlamına düşecek şekilde boyutlandırılır — kurulu TakoAPI becerisinin okuduğu budur. Her iki biçim de limit ile sınırlanır; bu yüzden her şeye sahip olduğunuzu varsaymadan önce truncated değerini ve toplam sayıları kontrol edin. Serbest metin parametreleri 100 karaktere kırpılır.", + "paramRegistryFormat": "md (varsayılan) veya json.", + "paramRegistryQ": "Ajan adı ve açıklamasında serbest metin eşleşmesi.", + "paramRegistryCategory": "Kategori kısa adı.", + "paramRegistryProtocol": "Yalnızca bu protokolü bildiren ajanları döndürür.", + "paramRegistryKind": "HOSTED ajanlar ağ geçidi üzerinden çağrılabilir; PROJECT girdileri yalnızca keşif için listelenen açık kaynaklı depolardır.", + "paramRegistrySort": "Sıralama. Varsayılan olarak önce öne çıkanlar, sonra en çok çağrılanlar, sonra en yeniler — projeler içinse en çok yıldız alanlar.", + "paramRegistryLimit": "Döndürülecek toplam girdi sayısı. Önce barındırılan ajanlar, sonra projeler doldurulur.", + "endpointAgentDetailSummary": "Tek bir ajanın, bildirdiği A2A becerileri dahil tüm herkese açık ayrıntısı.", + "endpointAgentDetailNote": "Yalnızca onaylanmış ajanlar çözümlenir; diğer her şey 404 döner. Denetim alanları — inceleme notları, dahili kimlikler ve yayıncı avatarları — bu şemanın hiçbir zaman parçası değildir. Uçta 60 saniye önbelleklenir.", + "paramAgentDetailSlug": "Yol parçası. Ajanın, kayıt defterinin döndürdüğü kısa adı.", + "endpointSkillsDirectorySummary": "OpenClaw beceriler kataloğu, varsayılan olarak Markdown.", + "endpointSkillsDirectoryNote": "Kayıt defteri gibi önce Markdown. Yayınlanmış kurulum komutu olmayan beceriler için install değeri null olur — github bağlantısına geri dönün. downloads, becerinin kendi listesinden alınan üçüncü taraf bir rakamdır; TakoAPI kurulum sayısı değildir.", + "paramSkillsDirFormat": "md (varsayılan) veya json.", + "paramSkillsDirQ": "Beceri adı ve açıklamasında serbest metin eşleşmesi.", + "paramSkillsDirCategory": "Kategori kısa adı.", + "paramSkillsDirPage": "1'den başlayan sayfa numarası.", + "paramSkillsDirLimit": "Sayfa başına sonuç sayısı.", + "endpointSkillsSearchSummary": "Beceriler kataloğunda tam metin arama, yalnızca JSON.", + "endpointSkillsSearchNote": "q zorunludur: bu parametre olmadan uç nokta tüm katalog yerine boş bir liste döndürür. Her sonuç, readme ve reviewNote hariç tüm katalog satırıdır; yukarıdaki örnek kısaltılmıştır.", + "paramSkillsSearchQ": "Arama terimi. Beceri adı, açıklaması ve yazarıyla eşleşir.", + "paramSkillsSearchCategory": "Kategori kısa adı.", + "paramSkillsSearchPage": "1'den başlayan sayfa numarası.", + "paramSkillsSearchLimit": "Sayfa başına sonuç sayısı.", + "gatewayTitle": "Ağ geçidi", + "gatewayIntro": "Aynı ajanı çağırmanın üç yolu. Üçü de anahtar ister, üçü de aynı şekilde ücretlendirir ve üçü de normalde model adının geçtiği yerde ajanın kısa adını kabul eder. Yalnızca HOSTED türündeki ajanlar çağrılabilir.", + "endpointChatCompletionsSummary": "OpenAI uyumlu. Herhangi bir OpenAI SDK'sını /v1 adresine yönlendirin ve model değerini bir ajan kısa adına ayarlayın.", + "endpointChatCompletionsNote": "Tüm konuşma iletilir: tur başına sırayla bir A2A metin parçası ve her parçaya iliştirilmiş OpenAI rolü. usage her zaman bulunur ama her zaman sıfırdır — bu ağ geçidinde hiçbir şey token saymaz ve bir ajanın LLM olması gerekmez. Yukarıdaki SSE biçimi için stream: true, sondaki usage parçasını da almak için stream_options.include_usage ayarlayın. Akışsız bir çağrı 30 saniye, akış ise 120 saniye sonra zaman aşımına uğrar.", + "endpointA2AMessageSummary": "Yerel A2A geçişi. Ajanın kendi JSON-RPC yanıtını değiştirmeden döndürür.", + "endpointA2AMessageNote": "A2A message/send çağrısı olarak aktarılır. Çok turlu bir göreve devam etmek ya da dosya ve veri parçalarını geçirmek için metin kısayolunu veya parts, taskId ve contextId içeren tam bir A2A mesajını gönderin. Ajanın durum kodu ve gövdesi olduğu gibi aktarılır; içerik taşımayan bir istek ücretlendirilmek yerine 400 ile reddedilir. 30 saniye sonra zaman aşımına uğrar.", + "endpointA2AStreamSummary": "Akış yapan ajanlar için aynı çağrının Server-Sent Events üzerinden hâli.", + "endpointA2AStreamNote": "Ajanın A2A message/stream çerçevelerini olduğu gibi text/event-stream olarak aktarır. Bu uç nokta yalnızca metin kısayolunu kabul eder. Bağlantı 120 saniye sonra kesilir ve çağrı yalnızca akış sonuna ulaştığında ücretlendirilir — bkz. Hız sınırları ve faturalama.", + "labelQueryParams": "Parametreler", + "labelRequest": "İstek gövdesi", + "labelExample": "Örnek", + "labelResponse": "Yanıt", + "labelStreaming": "stream: true ile yanıt", + "labelA2AMessageForm": "Tam A2A mesaj biçimi", + "labelAuthRequired": "Anahtar gerekli", + "labelNoAuth": "Anahtarsız", + "labelBillable": "Ücretli", + "labelFree": "Ücretsiz", + "labelParamName": "Ad", + "labelParamType": "Tür", + "labelParamDesc": "Açıklama", + "paramsTableCaption": "{path} için parametreler", + "mcpTitle": "MCP sunucusu", + "mcpIntro": "TakoAPI, {url} adresinde Streamable HTTP üzerinden barındırılan, durumsuz bir Model Context Protocol sunucusu çalıştırır. Bir kez kaydedin; kodlama ajanınız kataloğu yerel araçlarla arayıp çağırabilsin.", + "mcpRegisterTitle": "Kaydedin", + "mcpRegisterLabel": "MCP kayıt komutları", + "mcpAuthNote": "Üç okuma aracı anonim çalışır. invoke_agent kredi harcar, bu yüzden anahtarınızı ister: Authorization başlığı olarak (Claude Code), bearer_token_env_var olarak (Codex) ya da bir headers bloğu içinde (OpenCode) iletin.", + "mcpToolsTitle": "Araçlar", + "mcpToolsTableCaption": "TakoAPI MCP sunucusunun sunduğu araçlar", + "mcpToolColumn": "Araç", + "mcpToolAccessColumn": "Erişim", + "mcpToolWhatColumn": "Ne yapar", + "mcpToolKeyRequired": "Anahtar gerekli", + "mcpToolAnonymous": "Anonim", + "mcpToolSearchAgents": "Kayıt defterinde anahtar kelimeye, kategoriye veya protokole göre arama yapar. Kısa adları, protokolleri ve fiyatlandırmayı döndürür.", + "mcpToolGetAgent": "Kısa adına göre bir ajan hakkında bilinen her şey — yetenekler, protokoller, uç nokta, fiyatlandırma ve bildirilen beceriler.", + "mcpToolSearchSkills": "OpenClaw beceriler kataloğunda arama yapar. Sorgu verilmezse seçilmiş en iyi becerileri döndürür.", + "mcpToolInvokeAgent": "Bir ajanı ağ geçidi üzerinden çağırır ve yanıtını döndürür. Anahtar gerektirir ve kredi harcar.", + "mcpRateNote": "MCP uç noktası, anahtarınızın ağ geçidi kotasından ayrı olarak IP adresi başına dakikada 120 istekle sınırlıdır.", + "mcpInstallNote": "Sunucu yerine beceri dosyasını mı tercih edersiniz? Kurulum aracı, Claude Code, Codex ve OpenCode'a bu uç noktaları düz HTTP üzerinden çağırmayı öğretir.", + "errorsTitle": "Hatalar", + "errorsIntro": "Her ağ geçidi hatası JSON'dur. Bunlar, ağ geçidinin kendisinin döndürdüğü durumlardır.", + "errorsTableCaption": "Ağ geçidinin döndürdüğü hata durumları", + "errorStatusColumn": "Durum", + "errorCodeColumn": "Kod", + "errorWhenColumn": "Ne zaman", + "errorBodyColumn": "Örnek gövde", + "error400When": "Kısa ad, çağrılabilir uç noktası olmayan bir açık kaynaklı projeyi gösteriyor, istek gövdesi doğrulamayı geçemedi ya da her mesaj turu boştu.", + "error401When": "Anahtar eksik, bozuk, tanınmıyor veya iptal edilmiş.", + "error402When": "Ajan ücretli ve çağrının ücretlendirilmesi bakiyenizi sıfırın altına düşürecek. Ajanla iletişime geçilmeden önce denetlenir, bu yüzden hiçbir şey harcanmaz.", + "error404When": "Bu kısa ada sahip onaylanmış bir ajan yok.", + "error429When": "Anahtar son 60 saniyedeki kotasını aştı. Retry-After, pencerenin sıfırlanmasına kaç saniye kaldığını verir.", + "error502When": "Ajan yanıt vermedi, zaman aşımına uğradı, akış döndürmedi ya da HTTP 200 üzerinden bir JSON-RPC hatası bildirdi. Hiçbir zaman ücretlendirilmez.", + "errorsOpenAiNote": "/v1/chat/completions üzerinde aynı hatalar OpenAI'nin zarfıyla gelir — type, param ve code her zaman bulunur — böylece bir OpenAI SDK'sı her zamanki istisna türlerini fırlatır:", + "errorsPassthroughNote": "Ajanın kendisinin döndürdüğü bir durum, A2A message uç noktası tarafından değiştirilmeden aktarılır; bu yüzden oradaki bir 4xx bizim değil ajanın olabilir.", + "limitsTitle": "Hız sınırları ve faturalama", + "limitsRateTitle": "Hız sınırları", + "limitsRate": "Ağ geçidi API anahtarı başına 60 saniyede 120 isteğe izin verir. Pencere tüm sunucu örnekleri arasında paylaşılır ve IP adresinize değil anahtarın kendisine bağlıdır; bu yüzden yük altında gevşemez, paylaşılan bir NAT arkasında da daralmaz.", + "limitsRateOverride": "Bir anahtar kendi kotasını taşıyabilir; bu kota yalnızca o anahtar için varsayılanın yerine geçer. Sınırı aştığınızda saniye cinsinden bir Retry-After başlığıyla 429 alırsınız.", + "limitsBillingTitle": "Faturalama", + "limitsBillingFree": "FREE olarak fiyatlandırılan ajanlar hiçbir şeye mal olmaz — birim fiyatı eksik veya sıfır olan her ajan da öyle. Ağ geçidi asla kendiliğinden ücret uydurmaz.", + "limitsBillingPriced": "Ücretli bir ajan, çağrı başına bir kez kendi birim fiyatı üzerinden ön ödemeli kredinizden düşer. Token düzeyinde ölçüm yoktur; bir çağrı, ne kadar sürerse sürsün tek bir birimdir.", + "limitsBillingPreflight": "Bakiyeniz, ajanla iletişime geçilmeden önce denetlenir. Ücret sizi sıfırın altına düşürecekse çağrı 402 ile reddedilir ve ajan hiç çağrılmaz — eksi bakiye yoktur.", + "limitsBillingFailures": "Başarısız olan çağrılar ücretlendirilmez. Ajandan gelen bir 4xx veya 5xx, zaman aşımı, erişilemeyen bir uç nokta ya da JSON-RPC hatası; hepsi denemeyi kaydeder ama hiçbir ücret yansıtmaz.", + "limitsBillingStreams": "Akışlar yalnızca tamamlandıklarında ücretlendirilir. İptal edilen, zaman aşımına uğrayan, ortasında başarısız olan ya da erkenden bağlantısını kestiğiniz bir akış kaydedilir ama faturalandırılmaz.", + "limitsBillingLedger": "Her ücret, ona sebep olan çağrıya karşı değiştirilemez bir defter kaydı yazar. Bakiye, defter ve çağrı başına kullanım panelinizde bulunur." } } diff --git a/messages/vi.json b/messages/vi.json index af18fcd04..d2e8f796e 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -7,60 +7,78 @@ "label": "Ngôn ngữ" }, "Header": { - "searchPlaceholder": "Tìm kiếm agent...", "agents": "Agent", "skills": "Kỹ năng", - "trending": "Thịnh hành", - "publish": "Đăng", "publishAgent": "Đăng agent", "account": "Tài khoản", - "mySkills": "Kỹ năng của tôi", - "myBookmarks": "Đã lưu", - "apiUsage": "API & Sử dụng", + "myBookmarks": "Agent đã lưu", + "apiUsage": "API key & mức dùng", "adminPanel": "Bảng quản trị", "signIn": "Đăng nhập", - "signOut": "Đăng xuất" + "signOut": "Đăng xuất", + "mainNav": "Chính", + "skipToContent": "Bỏ qua tới nội dung", + "openMenu": "Mở menu", + "closeMenu": "Đóng menu", + "projects": "Dự án", + "install": "Cài đặt", + "getApiKey": "Lấy API key", + "search": "Tìm kiếm", + "searchScope": "Tìm trong", + "scopeAgents": "Agent", + "scopeProjects": "Dự án", + "scopeSkills": "Kỹ năng", + "searchAgentsPlaceholder": "Tìm agent theo việc chúng làm…", + "searchProjectsPlaceholder": "Tìm dự án agent mã nguồn mở…", + "searchSkillsPlaceholder": "Tìm kỹ năng cho coding agent của bạn…", + "myListings": "Mục tôi đã đăng", + "docs": "Tài liệu" }, "Footer": { - "tagline": "Một API để truy cập mọi agent — cùng hàng nghìn kỹ năng OpenClaw cho coding agent của bạn.", + "tagline": "Một API để truy cập mọi agent — khám phá, gọi và đo lường mọi agent qua một endpoint duy nhất.", "agentRegistry": "Registry agent:", "platform": "Nền tảng", "resources": "Tài nguyên", - "installInAgent": "Cài đặt vào agent của bạn", + "installInAgent": "Cài vào coding agent của bạn", "browseAgents": "Duyệt agent", "publishAnAgent": "Đăng một agent", - "developerDashboard": "Bảng điều khiển nhà phát triển", + "developerDashboard": "API key & mức dùng", "browseSkills": "Duyệt kỹ năng", - "awesomeList": "Awesome List", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI. Xây dựng cho cộng đồng OpenClaw.", - "blog": "Blog" + "copyright": "© {year} TakoAPI. Giấy phép MIT.", + "blog": "Blog", + "badge": "Huy hiệu README", + "scenarios": "Duyệt theo kịch bản", + "a2aSpec": "Giao thức A2A", + "github": "GitHub", + "docs": "Tài liệu API" }, "Home": { "heroTitle": "Một API để truy cập mọi agent", - "heroSubtitle": "Khám phá và gọi {agents, number} AI agent thông qua một API hợp nhất — cùng {skills, number} kỹ năng cho coding agent của bạn.", + "heroSubtitle": "Khám phá các AI agent, rồi gọi bất kỳ agent nào qua một endpoint với một key. Hỗ trợ A2A gốc, tương thích OpenAI, tính phí theo lượt gọi.", "browseAgents": "Duyệt agent", - "publishAnAgent": "Đăng một agent", - "agentRegistry": "Registry agent:", - "browseByScenario": "Duyệt theo kịch bản", - "allAgents": "Tất cả agent", + "browseByScenario": "Duyệt theo việc bạn cần làm", "featuredAgents": "Agent nổi bật", "browseAll": "Duyệt tất cả", "popularProjects": "Dự án mã nguồn mở phổ biến", "selfHostable": "{count, number} có thể tự host", - "skillTitle": "Kỹ năng TakoAPI cho OpenClaw", - "official": "Chính thức", - "skillDescription": "Tìm kiếm, cài đặt và quản lý kỹ năng OpenClaw qua hội thoại tự nhiên — được hỗ trợ bởi sàn TakoAPI.", - "viewOnGithub": "Xem trên GitHub", - "askAgent": "hoặc yêu cầu agent của bạn: \"Cài đặt kỹ năng TakoAPI\"", - "usingCodingAgent": "Đang dùng Claude Code, Codex hay OpenCode?", - "oneCommandInstall": "Cài đặt bằng một lệnh", - "mustHaveSkills": "Kỹ năng nên có", - "viewRankings": "Xem xếp hạng", - "categories": "Danh mục", - "viewAll": "Xem tất cả", + "categories": "Danh mục:", "moreCategories": "{count, plural, other {+# danh mục nữa}}", - "latestSkills": "Kỹ năng mới nhất" + "getApiKey": "Lấy một API key", + "catalogSummary": "{agents, number} agent gọi được · {projects, number} dự án mã nguồn mở · {skills, number} kỹ năng cho coding agent", + "quickstartLabel": "30 giây tới lượt gọi đầu tiên", + "quickstartNote": "Thích làm việc từ terminal? Cài TakoAPI vào coding agent của bạn.", + "allScenarios": "Tất cả kịch bản", + "callableNow": "{count, number} gọi được qua gateway", + "installTitle": "Dùng TakoAPI từ coding agent của bạn", + "installDescription": "Một lệnh giúp Claude Code, Codex hay OpenCode biết cách tìm trong thư mục và gọi agent thay bạn — hoặc đăng ký MCP server được host thay thế.", + "installCta": "Hướng dẫn cài đặt", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "Kỹ năng cho coding agent của bạn", + "skillsSubtitle": "{count, number} kỹ năng OpenClaw, được cài nhiều nhất và mới nhất.", + "quickstartTabDiscover": "1. Khám phá", + "quickstartTabCall": "2. Gọi", + "quickstartTabFromAgent": "3. Từ agent của bạn" }, "Scenarios": { "frontend": "Frontend", @@ -85,8 +103,6 @@ "notFound": "Không tìm thấy agent", "backToMarketplace": "← Sàn agent", "openSourceProject": "Dự án mã nguồn mở", - "signedCard": "Card đã ký", - "verifiedPublisher": "Nhà phát hành đã xác minh", "by": "bởi {name}", "publisherOpenSource": "mã nguồn mở", "publisherUnknown": "không rõ", @@ -101,17 +117,41 @@ "noSkillsHosted": "Agent này không công bố kỹ năng có cấu trúc trong AgentCard của nó.", "inputMode": "vào: {mode}", "outputMode": "ra: {mode}", - "openSourceProjectHeading": "Dự án mã nguồn mở", "openSourceProjectNote": "Tự host hoặc khám phá mã nguồn. Không thể gọi qua gateway TakoAPI.", "viewOnGithub": "Xem trên GitHub", - "integration": "Tích hợp", "endpoint": "Endpoint", "agentCard": "AgentCard", "homepage": "Trang chủ", "callThroughTakoApi": "Gọi qua TakoAPI", - "gatewayPhase2": "Gateway hợp nhất — khả năng gọi ra mắt ở Giai đoạn 2.", "breadcrumbHome": "Trang chủ", - "breadcrumbMarketplace": "Sàn agent" + "breadcrumbMarketplace": "Sàn agent", + "gatewayHint": "Gọi nó bằng API key của bạn — tạo key trong bảng điều khiển.", + "healthOk": "Đang phản hồi", + "healthDegraded": "Suy giảm", + "healthDown": "Không phản hồi", + "healthCheckedAt": "Kiểm tra lần cuối {time}", + "atAGlance": "Tổng quan nhanh", + "protocols": "Giao thức", + "protocolsNone": "Không khai báo", + "yes": "Có", + "no": "Không", + "pricing": "Giá", + "health": "Tình trạng", + "healthUnknown": "Chưa kiểm tra", + "totalCalls": "Tổng lượt gọi", + "numberValue": "{count, number}", + "averageRating": "Đánh giá trung bình", + "ratingValue": "{rating} / 5 · {count, number} đánh giá", + "notRated": "Chưa có đánh giá", + "callExamplesLabel": "Ví dụ gọi", + "repository": "Kho mã", + "language": "Ngôn ngữ", + "license": "Giấy phép", + "licenseNone": "Không ghi rõ", + "lastPush": "Lần push cuối", + "topics": "Chủ đề", + "readmeHeading": "Từ README", + "readmeNote": "Trích từ README của dự án trên GitHub." }, "Agents": { "metaTitle": "Sàn agent", @@ -140,16 +180,29 @@ "pricingPerCall": "mỗi lần gọi", "pricingPerTask": "mỗi tác vụ", "pricingPerToken": "Mỗi 1k token", - "count": "{count, plural, other {# agent}}", + "count": "{count, plural, =0 {Không có agent} other {# agent}}", "emptyTitle": "Chưa có agent nào khớp với bộ lọc của bạn.", "emptyCta": "Đăng agent đầu tiên →", "prev": "← Trước", "next": "Sau →", - "pageOf": "Trang {page} / {total}" + "pageOf": "Trang {page} / {total}", + "filtersTitle": "Bộ lọc", + "filtersWithCount": "Bộ lọc ({count})", + "activeFiltersLabel": "Bộ lọc đang dùng:", + "removeFilter": "Bỏ bộ lọc: {label}", + "clearAll": "Xóa tất cả", + "kindLabel": "Loại", + "scenarioGroup": "Kịch bản", + "protocolGroup": "Giao thức", + "pricingGroup": "Giá", + "categoryGroup": "Danh mục", + "searchChip": "Tìm kiếm: {query}", + "resultsLabel": "Kết quả", + "paginationLabel": "Phân trang", + "goToPage": "Đến trang {page}", + "showingRange": "Hiển thị {from}–{to} trên {total}" }, "Components": { - "homeSearchPlaceholder": "Tìm kỹ năng theo tên, mô tả hoặc tác giả...", - "homeSearchButton": "Tìm kiếm", "agentFree": "Miễn phí", "agentPerCall": "{price}/lần gọi", "agentPerTask": "{price}/tác vụ", @@ -163,7 +216,11 @@ "agentSelfHost": "tự host", "agentSkills": "{count, plural, other {# kỹ năng}}", "skillBy": "bởi {author}", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} lượt gọi", + "healthOk": "Đang phản hồi", + "healthDegraded": "Suy giảm", + "healthDown": "Không phản hồi" }, "Install": { "metaTitle": "Cài đặt trong Claude Code, Codex & OpenCode", @@ -196,7 +253,7 @@ "howToUseHeading": "Cách dùng", "claudeCodeUse": "Tự động tải — chỉ cần yêu cầu Claude tìm một agent trên TakoAPI.", "codexUse": "Gọi bằng $takoapi, hoặc chọn từ menu /skills.", - "opencodeUse": "Chạy /takoapi , hoặc chuyển giao cho @takoapi.", + "opencodeUse": "Chạy /takoapi '', hoặc chuyển giao cho @takoapi.", "mcpHeading": "MCP server — công cụ được host, không cần cài gì", "claudeCodeMcpHint": "Một lệnh. Thêm --header \"Authorization: Bearer $TAKO_KEY\" để bật invoke_agent.", "codexMcpHint": "Thêm vào ~/.codex/config.toml", @@ -226,12 +283,17 @@ "requirements": "Yêu cầu", "installation": "Cài đặt", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClaw Chat (dán đoạn này vào cuộc hội thoại của bạn)", "links": "Liên kết", "github": "GitHub", - "copyGithubUrl": "Sao chép URL GitHub", "clawHub": "ClawHub", - "skillNotFound": "Không tìm thấy kỹ năng" + "codingAgent": "Coding agent", + "agentPrompt": "Cài kỹ năng từ {url}", + "installHintRegistry": "Chạy lệnh CLI, hoặc dán prompt vào Claude Code, Cursor, Codex hay coding agent bạn đang dùng.", + "installHintRepo": "Kỹ năng này chưa được đăng trên registry ClawHub. Hãy dán prompt vào coding agent của bạn — Claude Code, Cursor, Codex — và nó sẽ cài kỹ năng thẳng từ kho mã nguồn.", + "installUnavailable": "Kỹ năng này chưa công bố nguồn cài đặt nào.", + "openRepository": "Mở kho mã", + "signInToLike": "Đăng nhập để thích kỹ năng này", + "downloadsSource": "Lượt tải do clawskills.sh công bố" }, "Skills": { "metaTitle": "Duyệt kỹ năng", @@ -257,13 +319,32 @@ "all": "Tất cả", "emptyTitle": "Không tìm thấy kỹ năng", "emptyHint": "Thử điều chỉnh tìm kiếm hoặc bộ lọc của bạn", - "pageOf": "Trang {page} / {total}" + "pageOf": "Trang {page} / {total}", + "metaTitleSearch": "Kỹ năng khớp với \"{query}\"", + "metaTitlePaged": "Duyệt kỹ năng — trang {page}", + "searchInputLabel": "Tìm kỹ năng", + "searchPlaceholder": "Tìm kỹ năng theo tên, mô tả hoặc tác giả", + "searchButton": "Tìm kiếm", + "agentFilter": "Agent", + "agentZed": "Zed", + "sourceFilter": "Nguồn", + "sourceGithub": "GitHub", + "sourceCommunity": "Cộng đồng", + "sourceCurated": "Tuyển chọn", + "categoryFilter": "Danh mục", + "sortLabel": "Sắp xếp", + "sortRelevance": "Khớp nhất", + "clearFilters": "Xóa bộ lọc", + "paginationLabel": "Trang danh sách kỹ năng", + "goToPage": "Đến trang {page}", + "prev": "Trước", + "next": "Sau" }, "Trending": { "metaTitle": "Kỹ năng thịnh hành", - "metaDescription": "Những kỹ năng OpenClaw được tải và thích nhiều nhất ngay lúc này — bảng xếp hạng trực tiếp về những gì coding agent đang cài đặt.", + "metaDescription": "Các kỹ năng OpenClaw được tải nhiều nhất và được thích nhiều nhất — bảng xếp hạng dựng từ số lượt tải do clawskills.sh công bố, cập nhật hằng ngày.", "ogTitle": "Kỹ năng thịnh hành — {siteName}", - "ogDescription": "Bảng xếp hạng trực tiếp các kỹ năng OpenClaw phổ biến nhất.", + "ogDescription": "Bảng xếp hạng hằng ngày các kỹ năng OpenClaw phổ biến nhất.", "title": "Kỹ năng thịnh hành", "subtitle": "Khám phá các kỹ năng OpenClaw phổ biến nhất được cộng đồng xếp hạng", "searchPlaceholder": "Tìm kỹ năng thịnh hành…", @@ -274,14 +355,20 @@ "likes": "Lượt thích", "views": "Lượt xem", "skill": "Kỹ năng", - "count": "{count, plural, other {# kỹ năng}}", - "countInCategory": "{count, plural, other {# kỹ năng}} trong {category}", + "count": "{count, plural, =0 {Không có kỹ năng} other {# kỹ năng}}", + "countInCategory": "{count, plural, =0 {Không có kỹ năng} other {# kỹ năng}} trong {category}", "emptyTitle": "Không tìm thấy kỹ năng", "emptySubtitle": "Thử điều chỉnh tìm kiếm hoặc bộ lọc của bạn", "byAuthor": "bởi {author}", "showing": "Hiển thị {from, number}–{to, number} trên {total, number}", "prev": "Trước", - "next": "Sau" + "next": "Sau", + "provenanceNote": "Số lượt tải và số sao đến từ danh mục nguồn — clawskills.sh và GitHub — và được cập nhật hằng đêm; chúng không đo số lượt cài qua TakoAPI. Lượt thích và lượt xem được đếm tại đây.", + "search": "Tìm kiếm", + "sortLabel": "Sắp xếp theo", + "rank": "Hạng", + "goToPage": "Đến trang {page}", + "paginationLabel": "Trang bảng xếp hạng" }, "Admin": { "accessDenied": "Truy cập bị từ chối", @@ -335,8 +422,8 @@ "actionReject": "Từ chối", "feature": "Đưa lên nổi bật", "unfeature": "Bỏ nổi bật", - "scenariosEditorHint": "场景 Kịch bản — 点击切换 / nhấn để gán", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "Kịch bản — nhấn để gán hoặc bỏ", + "scenarioOption": "{name}", "selectedCount": "{count, number} đã chọn", "rejectReasonPrompt": "Lý do từ chối (tùy chọn):", "deleteAgentConfirm": "Xóa vĩnh viễn agent này?", @@ -436,7 +523,46 @@ "targetSkill": "kỹ năng", "targetAgent": "agent", "targetCategory": "danh mục", - "targetUser": "người dùng" + "targetUser": "người dùng", + "adminNavLabel": "Mục quản trị", + "retry": "Thử lại", + "close": "Đóng", + "prevPage": "Trang trước", + "nextPage": "Trang sau", + "sectionAgents": "Agent", + "sectionSkills": "Kỹ năng & cộng đồng", + "statTotalAgents": "Tổng agent", + "statPendingAgents": "Agent chờ duyệt", + "statHostedAgents": "Được host", + "statProjectAgents": "Dự án", + "pendingAgentsTitle": "Agent đang chờ duyệt", + "noPendingAgents": "Không có agent nào đang chờ duyệt.", + "kindHosted": "Được host", + "kindProject": "Dự án", + "filterByStatus": "Lọc theo trạng thái", + "filterDisabled": "Đã tắt", + "actionDisable": "Tắt", + "actionEnable": "Bật lại", + "disableAgentTitle": "Tắt agent", + "disableAgentConfirm": "{name} sẽ bị ẩn khỏi danh mục công khai cho đến khi bạn bật lại. Không có gì bị xóa.", + "deleteAgentTitle": "Xóa agent", + "deleteAgentConfirmNamed": "Bạn có chắc muốn xóa {name} không? Thao tác này không thể hoàn tác.", + "rejectAgentTitle": "Từ chối agent", + "rejectReasonLabel": "Lý do (tùy chọn, hiển thị cho người đăng)", + "rejectReasonPlaceholder": "vd. endpoint trả về 500 với tác vụ mẫu", + "healthOk": "Tốt", + "healthDegraded": "Suy giảm", + "healthDown": "Ngừng hoạt động", + "healthUnknown": "Chưa kiểm tra", + "healthCheckedAt": "Lần kiểm tra tình trạng gần nhất: {date}", + "healthNeverChecked": "Chưa ghi nhận lần kiểm tra tình trạng nào", + "batchDeleteTitle": "Xóa kỹ năng", + "selectAllRows": "Chọn tất cả các dòng", + "selectRow": "Chọn {name}", + "editSkillNamed": "Sửa kỹ năng {name}", + "deleteSkillNamed": "Xóa kỹ năng {name}", + "editCategoryNamed": "Sửa danh mục {name}", + "deleteCategoryNamed": "Xóa danh mục {name}" }, "Auth": { "signInTitle": "Đăng nhập vào TakoAPI", @@ -462,14 +588,28 @@ "noAccount": "Chưa có tài khoản?", "signUpLink": "Đăng ký", "haveAccount": "Đã có tài khoản?", - "signInLink": "Đăng nhập" + "signInLink": "Đăng nhập", + "forgotPassword": "Quên mật khẩu?", + "forgotTitle": "Đặt lại mật khẩu", + "forgotSubtitle": "Chúng tôi sẽ gửi email liên kết để bạn chọn mật khẩu mới", + "sendResetLink": "Gửi liên kết đặt lại", + "sendingResetLink": "Đang gửi...", + "resetLinkSent": "Nếu địa chỉ đó có tài khoản, chúng tôi đã gửi liên kết đặt lại mật khẩu. Hãy kiểm tra hộp thư của bạn.", + "backToSignIn": "Quay lại đăng nhập", + "resetTitle": "Chọn mật khẩu mới", + "resetSubtitle": "Nhập mật khẩu mới cho tài khoản của bạn", + "newPasswordHint": "Mật khẩu mới (tối thiểu 8 ký tự)", + "updatePassword": "Cập nhật mật khẩu", + "updatingPassword": "Đang cập nhật...", + "passwordUpdated": "Mật khẩu của bạn đã được cập nhật. Đang chuyển tới trang đăng nhập...", + "resetLinkInvalid": "Liên kết đặt lại mật khẩu này không hợp lệ hoặc đã hết hạn.", + "requestNewLink": "Yêu cầu liên kết mới" }, "Dashboard": { "title": "Bảng điều khiển nhà phát triển", "signInPrompt": "Đăng nhập để quản lý API key và xem mức sử dụng.", - "signIn": "Đăng nhập", "description": "Quản lý API key và theo dõi mức sử dụng gateway. Một key, một hóa đơn, mọi agent.", - "apiKeys": "API Key", + "apiKeys": "API key", "createKey": "Tạo key", "creating": "Đang tạo…", "copyKeyWarning": "Sao chép key này ngay — nó sẽ không được hiển thị lại.", @@ -497,7 +637,6 @@ "billing": "Tín dụng & thanh toán", "creditBalance": "Số dư tín dụng", "totalSpend": "Tổng chi tiêu", - "topUpComingSoon": "Hiện tại tín dụng do nền tảng nạp; tính năng tự nạp qua PayPal sẽ sớm ra mắt.", "topUpLabel": "Thêm tín dụng", "topUpButton": "Nạp bằng PayPal", "topUpRedirecting": "Đang chuyển hướng…", @@ -513,35 +652,65 @@ "ledgerDebit": "Phí gọi", "ledgerPayout": "Chi trả", "ledgerRefund": "Hoàn tiền", - "ledgerAdjust": "Điều chỉnh" + "ledgerAdjust": "Điều chỉnh", + "onboardingTitle": "Thực hiện lượt gọi đầu tiên của bạn", + "onboardingSubtitle": "Ba bước từ đây đến khi một agent trả lời bạn.", + "stepKeyTitle": "Tạo một API key", + "stepKeyBody": "Một key gọi được mọi agent trên TakoAPI. Chuỗi bí mật chỉ hiển thị một lần, ngay sau khi bạn tạo.", + "stepCallTitle": "Thực hiện lượt gọi đầu tiên", + "stepCallBody": "Sao chép một mẫu bên dưới, đặt TAKO_KEY thành key của bạn rồi chạy. Lượt gọi sẽ xuất hiện trong mục Mức sử dụng sau vài giây.", + "stepInstallTitle": "Cài vào coding agent của bạn", + "stepInstallBody": "Hướng dẫn Claude Code, Codex hay OpenCode khám phá và gọi agent qua TakoAPI.", + "stepInstallLink": "Mở hướng dẫn cài đặt", + "stepOptional": "Tùy chọn", + "stepDone": "Xong", + "stepTodo": "Chưa xong", + "keyNameLabel": "Tên key", + "keyNamePlaceholder": "Key gateway", + "createKeyError": "Không tạo được key. Vui lòng thử lại.", + "revokeNamed": "Thu hồi {name}", + "quickstartHint": "Đặt TAKO_KEY thành một key ở trên, rồi gọi bất kỳ agent nào theo slug của nó.", + "creditRule": "Agent miễn phí không tốn gì; agent có giá sẽ trừ vào tín dụng trả trước của bạn, và lượt gọi bị từ chối với HTTP 402 ngay khi số dư sẽ xuống dưới 0.", + "topUpUnavailable": "Tự nạp tiền chưa khả dụng — hiện tại tín dụng do nền tảng thêm vào. Dù sao thì số dư ở trên vẫn là số liệu trực tiếp.", + "dismiss": "Bỏ qua", + "loadError": "Không tải được một số dữ liệu của bảng điều khiển.", + "retry": "Thử lại", + "recentCallsCaption": "Các lượt gọi gateway gần đây nhất của bạn", + "chartTitle": "Lượt gọi mỗi ngày, {days} ngày qua", + "chartTotal": "tổng {count, number}", + "chartAria": "Biểu đồ cột số lượt gọi gateway mỗi ngày trong {days} ngày qua, tổng cộng {count, number} lượt gọi.", + "chartDay": "{date}: {calls, number} lượt gọi, {errors, number} lỗi" }, "Profile": { - "title": "Hồ sơ của tôi", - "signInPrompt": "Vui lòng đăng nhập để xem hồ sơ của bạn.", - "signIn": "Đăng nhập", - "submissions": "Bài gửi", + "title": "Mục tôi đã đăng", + "signInPrompt": "Đăng nhập để xem các agent và kỹ năng bạn đã đăng.", "likes": "Lượt thích", - "views": "Lượt xem", - "statValue": "{count, number}", - "mySubmissions": "Bài gửi của tôi", - "submitNew": "Gửi mới", + "views": "lượt xem", "tab_all": "Tất cả", - "tab_approved": "Đã duyệt", - "tab_pending": "Đang chờ", - "tab_rejected": "Đã từ chối", - "tabCount": "{count, number}", - "emptyTab": "Không có bài gửi nào trong tab này", + "emptyTab": "Chưa có kỹ năng nào ở đây.", "submitFirst": "Gửi kỹ năng đầu tiên của bạn", - "statusApproved": "Đã duyệt", - "statusPending": "Chờ duyệt", + "statusApproved": "Đang hiển thị", + "statusPending": "Đang duyệt", "statusRejected": "Đã từ chối", - "reviewerNote": "Ghi chú của người duyệt:" + "reviewerNote": "Ghi chú của người duyệt:", + "publishAgent": "Đăng agent", + "submitSkill": "Gửi kỹ năng", + "statAgents": "Agent", + "statCalls": "Lượt gọi gateway", + "statSkills": "Kỹ năng", + "tabAgents": "Agent", + "tabSkills": "Kỹ năng", + "statusDisabled": "Đã tắt", + "kindHosted": "Gọi được", + "kindProject": "Mã nguồn mở", + "noAgents": "Chưa có agent nào ở đây.", + "publishFirstAgent": "Đăng agent đầu tiên của bạn", + "myListings": "Mục tôi đã đăng" }, "Submit": { "title": "Gửi kỹ năng", "subtitle": "Chia sẻ kỹ năng OpenClaw của bạn với cộng đồng", "signInPrompt": "Vui lòng đăng nhập để gửi kỹ năng.", - "signIn": "Đăng nhập", "skillSource": "Nguồn kỹ năng", "githubUrl": "URL GitHub", "clawSkillsUrl": "URL ClawSkills.sh", @@ -571,8 +740,15 @@ "submitting": "Đang gửi...", "enterUrlFirst": "Vui lòng nhập URL trước", "autoFillFailed": "Điền tự động thất bại", - "submitFailed": "Gửi thất bại", - "somethingWentWrong": "Đã xảy ra lỗi" + "somethingWentWrong": "Đã xảy ra lỗi", + "successTitle": "Đã gửi 🎉", + "pendingReview": "{name} hiện đang chờ duyệt.", + "nextStepsTitle": "Tiếp theo là gì", + "nextStepReview": "Người duyệt sẽ kiểm tra kho mã và phần mô tả.", + "nextStepEmail": "Chúng tôi sẽ gửi email cho bạn khi duyệt xong.", + "nextStepProfile": "Trang mục đã đăng của bạn luôn hiển thị trạng thái hiện tại và ghi chú của người duyệt.", + "viewMyListings": "Xem mục tôi đã đăng", + "backToSkills": "← Quay lại kỹ năng" }, "SubmitAgent": { "title": "Đăng một agent", @@ -611,7 +787,24 @@ "successTitle": "Đã gửi 🎉", "pendingReview": "{name} hiện đang chờ duyệt.", "successDetail": "Chúng tôi sẽ xác minh AgentCard và đăng nó lên sàn sau khi được duyệt.", - "backToMarketplace": "← Quay lại sàn" + "backToMarketplace": "← Quay lại sàn", + "checkCard": "Kiểm tra card", + "checking": "Đang kiểm tra…", + "enterCardUrlFirst": "Hãy nhập URL AgentCard trước.", + "previewTitle": "Đây là những gì sẽ được đăng", + "previewNoDescription": "Card không có mô tả.", + "previewEndpoint": "Endpoint", + "previewCapabilities": "Khả năng", + "previewSkillCount": "{count, plural, =0 {Không công bố khả năng nào} other {# khả năng}}", + "previewSupports": "Hỗ trợ", + "previewStreaming": "Streaming", + "previewPush": "Thông báo đẩy", + "previewHint": "Chưa có gì được lưu — nhấn đăng bên dưới để gửi đi duyệt.", + "nextStepsTitle": "Tiếp theo là gì", + "nextStepReview": "Người duyệt sẽ kiểm tra AgentCard và xem endpoint của bạn có phản hồi không.", + "nextStepEmail": "Chúng tôi sẽ gửi email cho bạn khi duyệt xong.", + "nextStepProfile": "Trang mục đã đăng của bạn luôn hiển thị trạng thái hiện tại và ghi chú của người duyệt.", + "viewMyListings": "Xem mục tôi đã đăng" }, "AgentEngagement": { "yourRating": "Đánh giá của bạn", @@ -619,28 +812,44 @@ "noRatings": "Chưa có đánh giá", "bookmark": "Lưu", "bookmarked": "Đã lưu", - "signInToRate": "Đăng nhập để đánh giá hoặc lưu agent này" + "signInToRate": "Đăng nhập để đánh giá hoặc lưu agent này", + "rateStars": "Đánh giá {count, plural, other {# sao}}" }, "Bookmarks": { "title": "Agent đã lưu", "subtitle": "Các agent bạn đã lưu.", "empty": "Chưa có mục đã lưu.", "browseAgents": "Duyệt agent", - "signInPrompt": "Đăng nhập để xem mục đã lưu.", - "signIn": "Đăng nhập" + "signInPrompt": "Đăng nhập để xem mục đã lưu của bạn." }, "ScenarioPage": { - "metaTitle": "Agent AI cho {scenario}", - "metaDescription": "Khám phá và gọi các agent AI cho {scenario} qua một API hợp nhất — được mô tả bằng A2A AgentCard mở trên TakoAPI.", - "heroSubtitle": "Agent AI cho {scenario}, gọi được qua một API.", + "metaTitle": "AI agent cho {scenario}", + "metaDescription": "Khám phá và gọi các AI agent cho {scenario} qua một API hợp nhất — được mô tả bằng AgentCard A2A mở trên TakoAPI.", + "heroSubtitle": "AI agent cho {scenario}, gọi được qua một API hợp nhất.", "empty": "Chưa có agent nào trong kịch bản này.", "browseAll": "Xem tất cả agent", "back": "← Tất cả kịch bản", "breadcrumbScenarios": "Kịch bản", - "indexTitle": "Duyệt agent AI theo kịch bản", - "indexDescription": "Tìm agent AI theo nhu cầu — frontend, đầu tư, marketing, hỗ trợ và hơn thế — tất cả gọi được qua một API.", + "indexTitle": "Duyệt AI agent theo kịch bản", + "indexDescription": "Tìm AI agent theo mục đích sử dụng — frontend, đầu tư, marketing, hỗ trợ và hơn thế — tất cả đều gọi được qua một API.", "indexHeroTitle": "Duyệt theo kịch bản", - "indexHeroSubtitle": "Tìm agent AI theo công việc bạn cần làm." + "indexHeroSubtitle": "Tìm AI agent theo công việc bạn cần làm.", + "indexPopularHeading": "Đã có nhiều lựa chọn", + "indexPopularHint": "Những kịch bản có sẵn nhiều agent để so sánh.", + "indexSparseHeading": "Mới bắt đầu", + "indexSparseHint": "Mới chỉ có vài agent — chỗ dễ để bạn là người đầu tiên.", + "indexSparseCta": "Đăng agent vào một trong số này →", + "metaTitlePaged": "AI agent cho {scenario} — trang {page}", + "resultsLabel": "Agent cho {scenario}", + "paginationLabel": "Phân trang", + "showingRange": "Hiển thị {from}–{to} trên {total}", + "prev": "← Trước", + "next": "Sau →", + "goToPage": "Đến trang {page}", + "ctaTitle": "Bạn biết agent nào cho {scenario}?", + "ctaBody": "Kịch bản này còn ít agent. Đăng một agent và nó sẽ hiện ở đây cho mọi người đang duyệt {scenario}.", + "ctaButton": "Đăng một agent", + "relatedHeading": "Khám phá các kịch bản khác" }, "Badge": { "snippetTitle": "Thêm huy hiệu", @@ -656,15 +865,160 @@ "step2": "Trên trang của bạn, sao chép Markdown từ ô huy hiệu.", "step3Title": "Dán vào README", "step3": "Thêm gần đầu trang — khách truy cập và công cụ tìm kiếm có liên kết đến trang TakoAPI của bạn.", - "findListing": "Tìm trang của bạn" + "findListing": "Tìm trang của bạn", + "exampleAlt": "Huy hiệu TakoAPI" }, "Blog": { "title": "Blog", - "subtitle": "Hướng dẫn về A2A, cổng agent và xây dựng với TakoAPI.", + "subtitle": "Hướng dẫn về A2A, gateway agent và xây dựng với TakoAPI.", "readMore": "Đọc thêm", "backToBlog": "← Tất cả bài viết", "minRead": "Đọc {min} phút", "published": "Đã đăng", "faqTitle": "Câu hỏi thường gặp" + }, + "NotFound": { + "title": "Không tìm thấy trang đó", + "description": "Liên kết có thể đã cũ, hoặc mục đăng đã bị gỡ. Hãy thử thư mục hoặc dùng ô tìm kiếm ở đầu trang.", + "home": "Trang chủ", + "agents": "Duyệt agent", + "skills": "Duyệt kỹ năng" + }, + "ErrorPage": { + "title": "Đã xảy ra lỗi", + "description": "Trang này không tải được. Thử lại thường sẽ được; nếu không, mã tham chiếu bên dưới giúp chúng tôi tìm ra nó trong nhật ký.", + "reference": "Tham chiếu: {id}", + "retry": "Thử lại", + "home": "Về trang chủ" + }, + "Consent": { + "title": "Phân tích", + "description": "Chúng tôi dùng Google Analytics để biết người dùng thấy trang nào hữu ích. Không có gì được tải và không cookie nào được đặt trừ khi bạn đồng ý.", + "accept": "Đồng ý", + "decline": "Từ chối" + }, + "Docs": { + "metaTitle": "Tài liệu API", + "title": "Tài liệu tham chiếu API TakoAPI", + "description": "Từ một API key đến một lượt gọi agent chạy được: xác thực, các endpoint khám phá, gateway agent hợp nhất, MCP server được host, mã lỗi, giới hạn tần suất và thanh toán.", + "heroBadge": "Tài liệu cho nhà phát triển", + "heroTitle": "Tài liệu tham chiếu API TakoAPI", + "heroSubtitle": "Một key, mọi agent. Trỏ bất kỳ client tương thích OpenAI nào tới TakoAPI và gọi một agent theo slug của nó — hoặc duyệt toàn bộ danh mục mà không cần key nào.", + "heroKeyCta": "Tạo một API key", + "heroBrowseCta": "Duyệt agent", + "tocTitle": "Trong trang này", + "tocLabel": "Mục lục", + "quickstartTitle": "Bắt đầu nhanh", + "quickstartIntro": "Ba bước từ con số không đến một phản hồi. Mọi request trên trang này đều là lượt gọi thật tới API đang chạy.", + "quickstartStep1Title": "Tạo một key", + "quickstartStep1": "Đăng nhập và tạo một API key trong bảng điều khiển của bạn. Chuỗi bí mật chỉ hiển thị một lần, lúc tạo.", + "quickstartStep2Title": "Export nó", + "quickstartStep2": "Mọi ví dụ trên trang này đều đọc key từ TAKO_KEY.", + "quickstartStep3Title": "Gọi một agent", + "quickstartStep3": "Đặt model — hoặc slug trong URL — thành bất kỳ agent được host nào trong danh mục.", + "quickstartSamplesLabel": "Ví dụ gọi để bắt đầu nhanh", + "quickstartNote": "Slug trong các mẫu này chỉ là chỗ giữ chỗ, không phải agent thật. Hãy sao chép một slug thật từ trang của agent bất kỳ; chỉ những agent được liệt kê là hosted mới gọi được.", + "authenticationTitle": "Xác thực", + "authenticationIntro": "Các lượt gọi gateway được xác thực bằng bearer token. Các endpoint khám phá là ẩn danh và hoàn toàn không cần key.", + "authHeaderNote": "x-api-key được chấp nhận thay cho header Authorization. Key luôn bắt đầu bằng tako_live_.", + "authWhereTitle": "Key đến từ đâu", + "authWhere": "Hãy tạo một key cho mỗi client hoặc mỗi môi trường trong bảng điều khiển của bạn, và thu hồi những key bạn không còn dùng.", + "authOnceTitle": "Chuỗi bí mật chỉ hiện một lần", + "authOnce": "Chỉ hash SHA-256 của key và một tiền tố ngắn để hiển thị được lưu lại, nên chuỗi bí mật đã mất thì không khôi phục được — hãy tạo key thay thế và thu hồi key cũ.", + "authRevokeTitle": "Thu hồi có hiệu lực ngay", + "authRevoke": "Mỗi lượt gọi đều kiểm tra lại key với cơ sở dữ liệu và không có gì được cache, nên key đã thu hồi bị từ chối ngay ở request kế tiếp. Lịch sử sử dụng trước đó vẫn được giữ nguyên.", + "discoveryTitle": "Khám phá", + "discoveryIntro": "Bốn endpoint công khai, có thể cache và không cần key. Chỉ những agent và kỹ năng đã được duyệt mới được trả về.", + "endpointRegistrySummary": "Toàn bộ danh mục agent — Markdown cho model, JSON cho code.", + "endpointRegistryNote": "Không có format=json thì phản hồi là Markdown (text/markdown), chia thành một bảng agent được host và một bảng dự án mã nguồn mở, với kích thước vừa đủ để đưa thẳng vào context của model — đó chính là thứ mà kỹ năng TakoAPI đã cài sẽ đọc. Cả hai định dạng đều bị giới hạn bởi limit, nên hãy kiểm tra truncated và các số tổng trước khi cho rằng bạn đã có đủ. Tham số dạng văn bản tự do bị cắt còn 100 ký tự.", + "paramRegistryFormat": "md (mặc định) hoặc json.", + "paramRegistryQ": "Khớp văn bản tự do trên tên và mô tả agent.", + "paramRegistryCategory": "Slug danh mục.", + "paramRegistryProtocol": "Chỉ trả về những agent có công bố giao thức này.", + "paramRegistryKind": "Agent HOSTED gọi được qua gateway; mục PROJECT là kho mã nguồn mở chỉ được liệt kê để khám phá.", + "paramRegistrySort": "Thứ tự sắp xếp. Mặc định là nổi bật trước, rồi được gọi nhiều nhất, rồi mới nhất — và nhiều sao nhất đối với dự án.", + "paramRegistryLimit": "Tổng số mục trả về. Agent được host được lấp trước, rồi mới đến dự án.", + "endpointAgentDetailSummary": "Toàn bộ thông tin công khai của một agent, gồm cả các kỹ năng A2A mà nó công bố.", + "endpointAgentDetailNote": "Chỉ agent đã duyệt mới truy xuất được; mọi thứ khác đều là 404. Các trường kiểm duyệt — ghi chú duyệt, id nội bộ và ảnh đại diện người đăng — không bao giờ nằm trong cấu trúc này. Được cache tại edge trong 60 giây.", + "paramAgentDetailSlug": "Phân đoạn đường dẫn. Slug của agent, đúng như registry trả về.", + "endpointSkillsDirectorySummary": "Danh mục kỹ năng OpenClaw, mặc định là Markdown.", + "endpointSkillsDirectoryNote": "Ưu tiên Markdown giống registry. install là null với những kỹ năng chưa công bố lệnh cài — khi đó hãy dùng liên kết github. downloads là số liệu của bên thứ ba lấy từ trang giới thiệu của chính kỹ năng đó, không phải số lượt cài qua TakoAPI.", + "paramSkillsDirFormat": "md (mặc định) hoặc json.", + "paramSkillsDirQ": "Khớp văn bản tự do trên tên và mô tả kỹ năng.", + "paramSkillsDirCategory": "Slug danh mục.", + "paramSkillsDirPage": "Số trang, bắt đầu từ 1.", + "paramSkillsDirLimit": "Số kết quả mỗi trang.", + "endpointSkillsSearchSummary": "Tìm kiếm toàn văn trong danh mục kỹ năng, chỉ trả về JSON.", + "endpointSkillsSearchNote": "q là bắt buộc: thiếu nó, endpoint trả về danh sách rỗng chứ không phải toàn bộ danh mục. Mỗi kết quả là bản ghi đầy đủ trong danh mục, trừ readme và reviewNote; ví dụ ở trên đã được rút gọn.", + "paramSkillsSearchQ": "Từ khóa tìm kiếm. Khớp với tên, mô tả và tác giả của kỹ năng.", + "paramSkillsSearchCategory": "Slug danh mục.", + "paramSkillsSearchPage": "Số trang, bắt đầu từ 1.", + "paramSkillsSearchLimit": "Số kết quả mỗi trang.", + "gatewayTitle": "Gateway", + "gatewayIntro": "Ba cách gọi cùng một agent. Cả ba đều cần key, đều tính phí như nhau, và đều nhận slug của agent ở chỗ thường điền tên model. Chỉ agent thuộc loại HOSTED mới gọi được.", + "endpointChatCompletionsSummary": "Tương thích OpenAI. Trỏ bất kỳ OpenAI SDK nào tới /v1 và đặt model thành slug của một agent.", + "endpointChatCompletionsNote": "Toàn bộ hội thoại được chuyển tiếp: mỗi lượt là một A2A text part, theo đúng thứ tự, với role OpenAI gắn vào từng part. usage luôn có mặt nhưng luôn bằng 0 — không có gì trong gateway này đếm token, và một agent cũng không nhất thiết phải là LLM. Đặt stream: true để dùng dạng SSE ở trên, và stream_options.include_usage để nhận thêm chunk usage ở cuối. Lượt gọi không stream hết thời gian chờ sau 30 giây, còn stream là sau 120 giây.", + "endpointA2AMessageSummary": "Chuyển tiếp A2A gốc. Trả về đúng phản hồi JSON-RPC của agent, không chỉnh sửa.", + "endpointA2AMessageNote": "Được chuyển tiếp dưới dạng lượt gọi A2A message/send. Gửi dạng text rút gọn, hoặc một A2A message đầy đủ — với parts, taskId và contextId — để tiếp tục một tác vụ nhiều lượt hoặc chuyển tiếp các part dạng file và data. Mã trạng thái và body của agent được chuyển tiếp nguyên vẹn, và request không có nội dung bị từ chối với mã 400 chứ không bị tính phí. Hết thời gian chờ sau 30 giây.", + "endpointA2AStreamSummary": "Vẫn lượt gọi đó nhưng qua Server-Sent Events, dành cho những agent có stream.", + "endpointA2AStreamNote": "Chuyển tiếp nguyên văn các frame A2A message/stream của agent dưới dạng text/event-stream. Endpoint này chỉ nhận dạng text rút gọn. Kết nối bị ngắt sau 120 giây, và lượt gọi chỉ bị tính phí khi stream chạy đến hết — xem phần Giới hạn tần suất & thanh toán.", + "labelQueryParams": "Tham số", + "labelRequest": "Body của request", + "labelExample": "Ví dụ", + "labelResponse": "Phản hồi", + "labelStreaming": "Phản hồi khi stream: true", + "labelA2AMessageForm": "Dạng A2A message đầy đủ", + "labelAuthRequired": "Cần key", + "labelNoAuth": "Không cần key", + "labelBillable": "Có tính phí", + "labelFree": "Miễn phí", + "labelParamName": "Tên", + "labelParamType": "Kiểu", + "labelParamDesc": "Mô tả", + "paramsTableCaption": "Tham số cho {path}", + "mcpTitle": "MCP server", + "mcpIntro": "TakoAPI vận hành một Model Context Protocol server được host, không lưu trạng thái, qua Streamable HTTP tại {url}. Đăng ký một lần là coding agent của bạn có thể tìm và gọi danh mục như những công cụ gốc.", + "mcpRegisterTitle": "Đăng ký nó", + "mcpRegisterLabel": "Các lệnh đăng ký MCP", + "mcpAuthNote": "Ba công cụ chỉ đọc hoạt động ẩn danh. invoke_agent tiêu tín dụng nên cần key của bạn: truyền qua header Authorization (Claude Code), qua bearer_token_env_var (Codex), hoặc trong khối headers (OpenCode).", + "mcpToolsTitle": "Công cụ", + "mcpToolsTableCaption": "Các công cụ do MCP server của TakoAPI cung cấp", + "mcpToolColumn": "Công cụ", + "mcpToolAccessColumn": "Truy cập", + "mcpToolWhatColumn": "Chức năng", + "mcpToolKeyRequired": "Cần key", + "mcpToolAnonymous": "Ẩn danh", + "mcpToolSearchAgents": "Tìm trong registry theo từ khóa, danh mục hoặc giao thức. Trả về slug, giao thức và giá.", + "mcpToolGetAgent": "Mọi thông tin về một agent theo slug — khả năng, giao thức, endpoint, giá và các kỹ năng được công bố.", + "mcpToolSearchSkills": "Tìm trong danh mục kỹ năng OpenClaw. Không có truy vấn thì nó trả về các kỹ năng hàng đầu được tuyển chọn.", + "mcpToolInvokeAgent": "Gọi một agent qua gateway và trả về phản hồi của nó. Cần key và tiêu tín dụng.", + "mcpRateNote": "Endpoint MCP bị giới hạn 120 request mỗi phút cho mỗi địa chỉ IP, tách biệt với hạn mức gateway của key.", + "mcpInstallNote": "Thích một tệp kỹ năng hơn là một server? Trình cài đặt sẽ hướng dẫn Claude Code, Codex và OpenCode gọi các endpoint này qua HTTP thuần.", + "errorsTitle": "Lỗi", + "errorsIntro": "Mọi lỗi của gateway đều ở dạng JSON. Đây là các mã trạng thái do chính gateway trả về.", + "errorsTableCaption": "Các mã trạng thái lỗi do gateway trả về", + "errorStatusColumn": "Trạng thái", + "errorCodeColumn": "Mã", + "errorWhenColumn": "Khi nào", + "errorBodyColumn": "Body ví dụ", + "error400When": "Slug trỏ tới một dự án mã nguồn mở không có endpoint gọi được, body của request không qua kiểm tra hợp lệ, hoặc mọi lượt tin nhắn đều rỗng.", + "error401When": "Key bị thiếu, sai định dạng, không tồn tại hoặc đã bị thu hồi.", + "error402When": "Agent có tính phí và việc trừ tiền cho lượt gọi sẽ khiến số dư của bạn xuống dưới 0. Điều này được kiểm tra trước khi liên hệ agent, nên không có gì bị tiêu.", + "error404When": "Không có agent đã duyệt nào mang slug đó.", + "error429When": "Key đã vượt hạn mức trong 60 giây vừa qua. Retry-After cho biết số giây cho tới khi cửa sổ được đặt lại.", + "error502When": "Agent không trả lời, hết thời gian chờ, không trả về stream, hoặc báo lỗi JSON-RPC kèm HTTP 200. Không bao giờ bị tính phí.", + "errorsOpenAiNote": "Trên /v1/chat/completions, cùng những lỗi đó đến trong envelope của OpenAI — luôn có type, param và code — nên OpenAI SDK vẫn ném ra đúng các kiểu exception quen thuộc:", + "errorsPassthroughNote": "Mã trạng thái do chính agent trả về được endpoint A2A message chuyển tiếp nguyên vẹn, nên một mã 4xx ở đó có thể là của agent chứ không phải của chúng tôi.", + "limitsTitle": "Giới hạn tần suất & thanh toán", + "limitsRateTitle": "Giới hạn tần suất", + "limitsRate": "Gateway cho phép 120 request mỗi 60 giây cho mỗi API key. Cửa sổ này dùng chung cho mọi instance server và được tính theo chính cái key chứ không theo địa chỉ IP của bạn, nên nó không nới ra khi tải cao cũng không siết lại khi bạn ở sau một NAT dùng chung.", + "limitsRateOverride": "Một key có thể mang hạn mức riêng, thay thế mức mặc định chỉ cho riêng key đó. Vượt giới hạn, bạn nhận 429 kèm header Retry-After, tính bằng giây.", + "limitsBillingTitle": "Thanh toán", + "limitsBillingFree": "Agent có giá FREE không tốn gì — và agent nào thiếu đơn giá hoặc có đơn giá bằng 0 cũng vậy. Gateway không bao giờ tự nghĩ ra một khoản phí.", + "limitsBillingPriced": "Agent có tính phí sẽ trừ tín dụng trả trước của bạn theo đơn giá, mỗi lượt gọi một lần. Không có đo lường ở mức token, nên một lượt gọi là một đơn vị dù nó chạy bao lâu.", + "limitsBillingPreflight": "Số dư của bạn được kiểm tra trước khi liên hệ agent. Nếu khoản phí khiến bạn xuống dưới 0, lượt gọi bị từ chối với 402 và agent không hề được gọi — không có chuyện thấu chi.", + "limitsBillingFailures": "Những lượt gọi thất bại không bị tính phí. Mã 4xx hoặc 5xx từ agent, hết thời gian chờ, endpoint không truy cập được hay lỗi JSON-RPC đều chỉ ghi nhận lần thử và không tính tiền.", + "limitsBillingStreams": "Stream chỉ bị tính phí khi chạy xong. Stream bị hủy, hết thời gian chờ, hỏng giữa chừng, hoặc bị bạn ngắt kết nối sớm đều được ghi nhận và không bị tính phí.", + "limitsBillingLedger": "Mỗi khoản phí đều ghi một mục sổ cái bất biến gắn với lượt gọi tạo ra nó. Số dư, sổ cái và mức sử dụng theo từng lượt gọi đều nằm trong bảng điều khiển của bạn." } } diff --git a/messages/zh.json b/messages/zh.json index 1823ab075..aa4fea3c1 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -1,66 +1,84 @@ { "Metadata": { "tagline": "一个 API,访问所有智能体", - "description": "通过一个统一 API 发现并调用 AI 智能体——智能体领域的 OpenRouter。浏览数百个开源智能体项目和数千个 OpenClaw 技能,为你的编程智能体所用。" + "description": "通过一个统一 API 发现并调用 AI 智能体——智能体世界的 OpenRouter。目录里收录了数百个开源智能体项目,以及数千个可用于编程智能体的 OpenClaw 技能。" }, "LocaleSwitcher": { "label": "语言" }, "Header": { - "searchPlaceholder": "搜索智能体……", "agents": "智能体", "skills": "技能", - "trending": "热门", - "publish": "发布", "publishAgent": "发布智能体", "account": "账户", - "mySkills": "我的技能", - "myBookmarks": "收藏", - "apiUsage": "API 与用量", + "myBookmarks": "收藏的智能体", + "apiUsage": "API 密钥与用量", "adminPanel": "管理后台", "signIn": "登录", - "signOut": "退出登录" + "signOut": "退出登录", + "mainNav": "主导航", + "skipToContent": "跳到正文", + "openMenu": "打开菜单", + "closeMenu": "关闭菜单", + "projects": "项目", + "install": "安装", + "getApiKey": "获取 API 密钥", + "search": "搜索", + "searchScope": "搜索范围", + "scopeAgents": "智能体", + "scopeProjects": "项目", + "scopeSkills": "技能", + "searchAgentsPlaceholder": "按用途搜索智能体……", + "searchProjectsPlaceholder": "搜索开源智能体项目……", + "searchSkillsPlaceholder": "搜索编程智能体能用的技能……", + "myListings": "我的发布", + "docs": "文档" }, "Footer": { - "tagline": "一个 API,访问所有智能体——外加数千个 OpenClaw 技能,为你的编程智能体所用。", + "tagline": "一个 API,访问所有智能体——通过单一端点发现、调用并计量任意智能体。", "agentRegistry": "智能体注册表:", "platform": "平台", "resources": "资源", - "installInAgent": "安装到你的智能体", + "installInAgent": "安装到你的编程智能体", "browseAgents": "浏览智能体", "publishAnAgent": "发布智能体", - "developerDashboard": "开发者控制台", + "developerDashboard": "API 密钥与用量", "browseSkills": "浏览技能", - "awesomeList": "精选清单", "clawHub": "ClawHub", - "copyright": "© {year} TakoAPI。为 OpenClaw 社区打造。", - "blog": "博客" + "copyright": "© {year} TakoAPI。MIT 许可证。", + "blog": "博客", + "badge": "README 徽章", + "scenarios": "按场景浏览", + "a2aSpec": "A2A 协议", + "github": "GitHub", + "docs": "API 文档" }, "Home": { "heroTitle": "一个 API,访问所有智能体", - "heroSubtitle": "通过一个统一 API 发现并调用 {agents, number} 个 AI 智能体——外加 {skills, number} 个技能,为你的编程智能体所用。", + "heroSubtitle": "先发现 AI 智能体,再用一个密钥、一个端点调用其中任意一个。原生 A2A,兼容 OpenAI,按次计量。", "browseAgents": "浏览智能体", - "publishAnAgent": "发布智能体", - "agentRegistry": "智能体注册表:", - "browseByScenario": "按场景浏览", - "allAgents": "全部智能体", + "browseByScenario": "按你要做的事浏览", "featuredAgents": "精选智能体", "browseAll": "查看全部", "popularProjects": "热门开源项目", "selfHostable": "{count, number} 个可自托管", - "skillTitle": "面向 OpenClaw 的 TakoAPI 技能", - "official": "官方", - "skillDescription": "通过自然对话搜索、安装并管理 OpenClaw 技能——由 TakoAPI 市场提供支持。", - "viewOnGithub": "在 GitHub 上查看", - "askAgent": "或者让你的智能体执行:“安装 TakoAPI 技能”", - "usingCodingAgent": "在用 Claude Code、Codex 或 OpenCode?", - "oneCommandInstall": "一条命令完成安装", - "mustHaveSkills": "必备技能", - "viewRankings": "查看排行榜", - "categories": "分类", - "viewAll": "查看全部", + "categories": "分类:", "moreCategories": "{count, plural, other {还有 # 个分类}}", - "latestSkills": "最新技能" + "getApiKey": "获取 API 密钥", + "catalogSummary": "{agents, number} 个可调用智能体 · {projects, number} 个开源项目 · {skills, number} 个编程智能体技能", + "quickstartLabel": "30 秒完成第一次调用", + "quickstartNote": "更习惯在终端里干活?把 TakoAPI 装进你的编程智能体。", + "allScenarios": "全部场景", + "callableNow": "{count, number} 个可通过网关调用", + "installTitle": "在编程智能体里使用 TakoAPI", + "installDescription": "一条命令,就能让 Claude Code、Codex 或 OpenCode 帮你检索目录并调用智能体——也可以改为注册托管的 MCP 服务器。", + "installCta": "安装指南", + "installPlatforms": "Claude Code · Codex · OpenCode · MCP", + "skillsTitle": "面向编程智能体的技能", + "skillsSubtitle": "{count, number} 个 OpenClaw 技能,按安装最多和最新排列。", + "quickstartTabDiscover": "1. 发现", + "quickstartTabCall": "2. 调用", + "quickstartTabFromAgent": "3. 从你的 agent" }, "Scenarios": { "frontend": "前端", @@ -85,8 +103,6 @@ "notFound": "未找到该智能体", "backToMarketplace": "← 智能体市场", "openSourceProject": "开源项目", - "signedCard": "已签名卡片", - "verifiedPublisher": "已验证发布者", "by": "由 {name} 提供", "publisherOpenSource": "开源", "publisherUnknown": "未知", @@ -101,17 +117,41 @@ "noSkillsHosted": "该智能体未在其 AgentCard 中声明结构化技能。", "inputMode": "输入:{mode}", "outputMode": "输出:{mode}", - "openSourceProjectHeading": "开源项目", "openSourceProjectNote": "自托管或浏览代码。无法通过 TakoAPI 网关调用。", "viewOnGithub": "在 GitHub 上查看", - "integration": "集成", "endpoint": "端点", "agentCard": "AgentCard", "homepage": "主页", "callThroughTakoApi": "通过 TakoAPI 调用", - "gatewayPhase2": "统一网关——调用功能将在第二阶段上线。", "breadcrumbHome": "首页", - "breadcrumbMarketplace": "智能体市场" + "breadcrumbMarketplace": "智能体市场", + "gatewayHint": "用你的 API 密钥调用它——在控制台创建一个。", + "healthOk": "响应正常", + "healthDegraded": "状态降级", + "healthDown": "无响应", + "healthCheckedAt": "上次检查:{time}", + "atAGlance": "概览", + "protocols": "协议", + "protocolsNone": "未声明", + "yes": "是", + "no": "否", + "pricing": "定价", + "health": "健康状态", + "healthUnknown": "尚未检查", + "totalCalls": "总调用次数", + "numberValue": "{count, number}", + "averageRating": "平均评分", + "ratingValue": "{rating} / 5 · {count, number} 条评分", + "notRated": "暂无评分", + "callExamplesLabel": "调用示例", + "repository": "仓库", + "language": "语言", + "license": "许可证", + "licenseNone": "未注明", + "lastPush": "最近推送", + "topics": "主题标签", + "readmeHeading": "README 摘录", + "readmeNote": "摘自该项目在 GitHub 上的 README。" }, "Agents": { "metaTitle": "智能体市场", @@ -140,16 +180,29 @@ "pricingPerCall": "每次调用", "pricingPerTask": "每个任务", "pricingPerToken": "每 1k tokens", - "count": "{count, plural, other {# 个智能体}}", + "count": "{count, plural, =0 {暂无智能体} other {# 个智能体}}", "emptyTitle": "暂无符合筛选条件的智能体。", "emptyCta": "成为第一个发布者 →", "prev": "← 上一页", "next": "下一页 →", - "pageOf": "第 {page} / {total} 页" + "pageOf": "第 {page} / {total} 页", + "filtersTitle": "筛选", + "filtersWithCount": "筛选({count})", + "activeFiltersLabel": "已选条件:", + "removeFilter": "移除筛选:{label}", + "clearAll": "全部清除", + "kindLabel": "类型", + "scenarioGroup": "场景", + "protocolGroup": "协议", + "pricingGroup": "定价", + "categoryGroup": "分类", + "searchChip": "搜索:{query}", + "resultsLabel": "结果", + "paginationLabel": "分页", + "goToPage": "跳到第 {page} 页", + "showingRange": "显示第 {from}–{to} 个,共 {total} 个" }, "Components": { - "homeSearchPlaceholder": "按名称、描述或作者搜索技能……", - "homeSearchButton": "搜索", "agentFree": "免费", "agentPerCall": "{price}/次", "agentPerTask": "{price}/任务", @@ -163,7 +216,11 @@ "agentSelfHost": "自托管", "agentSkills": "{count, plural, other {# 个技能}}", "skillBy": "由 {author} 提供", - "skillLikes": "{count, number}" + "skillLikes": "{count, number}", + "agentCalls": "{count, number} 次调用", + "healthOk": "响应正常", + "healthDegraded": "状态降级", + "healthDown": "无响应" }, "Install": { "metaTitle": "安装到 Claude Code、Codex 和 OpenCode", @@ -173,7 +230,7 @@ "stepRunInstallerText": "运行:curl -fsSL https://takoapi.com/install.sh | sh", "stepUseAgentName": "从你的智能体使用 TakoAPI", "stepUseAgentText": "让你的编程智能体在 TakoAPI 上查找或调用某个 AI 智能体。", - "heroBadge": "一条命令,三种智能体", + "heroBadge": "一条命令,三个编程智能体", "heroTitle": "将 TakoAPI 安装到你的编程智能体", "heroSubtitle": "把 TakoAPI 技能装进 Claude Code、Codex 或 OpenCode——然后通过一个统一网关,直接在终端里发现并调用任意 AI 智能体。", "whatTitle": "安装程序会做什么", @@ -196,7 +253,7 @@ "howToUseHeading": "如何使用", "claudeCodeUse": "自动加载——只需让 Claude 在 TakoAPI 上查找智能体。", "codexUse": "用 $takoapi 调用,或从 /skills 菜单中选择。", - "opencodeUse": "运行 /takoapi ,或交接给 @takoapi。", + "opencodeUse": "运行 /takoapi '',或交接给 @takoapi。", "mcpHeading": "MCP 服务器——托管工具,无需安装", "claudeCodeMcpHint": "一条命令。加上 --header \"Authorization: Bearer $TAKO_KEY\" 即可启用 invoke_agent。", "codexMcpHint": "添加到 ~/.codex/config.toml", @@ -226,12 +283,17 @@ "requirements": "前置要求", "installation": "安装", "clawHubCli": "ClawHub CLI", - "openClawChat": "OpenClaw Chat(将此内容粘贴到你的对话中)", "links": "链接", "github": "GitHub", - "copyGithubUrl": "复制 GitHub URL", "clawHub": "ClawHub", - "skillNotFound": "未找到该技能" + "codingAgent": "编程智能体", + "agentPrompt": "从 {url} 安装这个技能", + "installHintRegistry": "运行 CLI 命令,或把这段提示词粘贴到 Claude Code、Cursor、Codex 或你在用的任意编程智能体里。", + "installHintRepo": "这个技能没有发布到 ClawHub 注册表。把提示词粘贴到你的编程智能体——Claude Code、Cursor、Codex——它会直接从源码仓库把技能装上。", + "installUnavailable": "这个技能还没有公开任何安装来源。", + "openRepository": "打开仓库", + "signInToLike": "登录后即可为这个技能点赞", + "downloadsSource": "下载量由 clawskills.sh 提供" }, "Skills": { "metaTitle": "浏览技能", @@ -257,13 +319,32 @@ "all": "全部", "emptyTitle": "未找到技能", "emptyHint": "试试调整搜索或筛选条件", - "pageOf": "第 {page} / {total} 页" + "pageOf": "第 {page} / {total} 页", + "metaTitleSearch": "匹配“{query}”的技能", + "metaTitlePaged": "浏览技能 — 第 {page} 页", + "searchInputLabel": "搜索技能", + "searchPlaceholder": "按名称、描述或作者搜索技能", + "searchButton": "搜索", + "agentFilter": "智能体", + "agentZed": "Zed", + "sourceFilter": "来源", + "sourceGithub": "GitHub", + "sourceCommunity": "社区", + "sourceCurated": "精选", + "categoryFilter": "分类", + "sortLabel": "排序", + "sortRelevance": "最佳匹配", + "clearFilters": "清除筛选", + "paginationLabel": "技能列表分页", + "goToPage": "跳到第 {page} 页", + "prev": "上一页", + "next": "下一页" }, "Trending": { "metaTitle": "热门技能", - "metaDescription": "当下下载量和点赞量最高的 OpenClaw 技能——实时呈现编程智能体正在安装哪些技能的排行榜。", + "metaDescription": "下载量和点赞数最高的 OpenClaw 技能——榜单基于 clawskills.sh 公布的下载数据,每日刷新。", "ogTitle": "热门技能 — {siteName}", - "ogDescription": "最受欢迎 OpenClaw 技能的实时排行榜。", + "ogDescription": "每日更新的 OpenClaw 热门技能榜单。", "title": "热门技能", "subtitle": "发现社区评选出的最受欢迎 OpenClaw 技能", "searchPlaceholder": "搜索热门技能……", @@ -274,14 +355,20 @@ "likes": "点赞", "views": "浏览量", "skill": "技能", - "count": "{count, plural, other {# 个技能}}", - "countInCategory": "{category} 分类下 {count, plural, other {# 个技能}}", + "count": "{count, plural, =0 {暂无技能} other {# 个技能}}", + "countInCategory": "{category} 分类下 {count, plural, =0 {暂无技能} other {# 个技能}}", "emptyTitle": "未找到技能", "emptySubtitle": "试试调整搜索或筛选条件", "byAuthor": "由 {author} 提供", "showing": "显示第 {from, number}–{to, number} 个,共 {total, number} 个", "prev": "上一页", - "next": "下一页" + "next": "下一页", + "provenanceNote": "下载量和星标数来自上游目录——clawskills.sh 和 GitHub——每晚刷新,并不代表通过 TakoAPI 的安装量。点赞和浏览量由本站统计。", + "search": "搜索", + "sortLabel": "排序方式", + "rank": "排名", + "goToPage": "跳到第 {page} 页", + "paginationLabel": "排行榜分页" }, "Admin": { "accessDenied": "访问被拒绝", @@ -335,8 +422,8 @@ "actionReject": "拒绝", "feature": "设为精选", "unfeature": "取消精选", - "scenariosEditorHint": "场景 Scenarios — 点击切换 / toggle to assign", - "scenarioOption": "{nameZh} · {name}", + "scenariosEditorHint": "场景——点击以添加或移除", + "scenarioOption": "{name}", "selectedCount": "已选 {count, number} 个", "rejectReasonPrompt": "拒绝原因(可选):", "deleteAgentConfirm": "永久删除该智能体?", @@ -398,13 +485,13 @@ "noCategoriesFound": "未找到分类。请在上方添加一个。", "deleting": "……", "cannotDeleteHasSkills": "无法删除:存在关联技能", - "categoriesTotal": "{count, plural, other {共 {count, number} 个分类}}", + "categoriesTotal": "共 {count, number} 个分类", "usersTitle": "用户", - "usersTotal": "{count, plural, other {共 {count, number} 名用户}}", + "usersTotal": "共 {count, number} 名用户", "searchUsersPlaceholder": "按姓名或邮箱搜索……", "colEmail": "邮箱", "colRole": "角色", - "colProvider": "提供方", + "colProvider": "登录方式", "colJoined": "加入时间", "loadingUsers": "正在加载用户……", "noUsersFound": "未找到用户。", @@ -417,7 +504,7 @@ "demoteToUser": "降级为用户", "promoteToAdmin": "提升为管理员", "logsTitle": "管理日志", - "logsTotal": "{count, plural, other {共 {count, number} 条记录}}", + "logsTotal": "共 {count, number} 条记录", "colTimestamp": "时间戳", "colAdmin": "管理员", "colAction": "操作", @@ -436,7 +523,46 @@ "targetSkill": "技能", "targetAgent": "智能体", "targetCategory": "分类", - "targetUser": "用户" + "targetUser": "用户", + "adminNavLabel": "管理分区", + "retry": "重试", + "close": "关闭", + "prevPage": "上一页", + "nextPage": "下一页", + "sectionAgents": "智能体", + "sectionSkills": "技能与社区", + "statTotalAgents": "智能体总数", + "statPendingAgents": "待审核智能体", + "statHostedAgents": "托管", + "statProjectAgents": "项目", + "pendingAgentsTitle": "待审核的智能体", + "noPendingAgents": "没有等待审核的智能体。", + "kindHosted": "托管", + "kindProject": "项目", + "filterByStatus": "按状态筛选", + "filterDisabled": "已停用", + "actionDisable": "停用", + "actionEnable": "重新启用", + "disableAgentTitle": "停用智能体", + "disableAgentConfirm": "{name} 将从公开目录中隐藏,直到你重新启用它。不会删除任何数据。", + "deleteAgentTitle": "删除智能体", + "deleteAgentConfirmNamed": "确定要删除 {name} 吗?此操作无法撤销。", + "rejectAgentTitle": "拒绝智能体", + "rejectReasonLabel": "原因(可选,会展示给发布者)", + "rejectReasonPlaceholder": "例如:端点在示例任务上返回 500", + "healthOk": "健康", + "healthDegraded": "降级", + "healthDown": "不可用", + "healthUnknown": "未检查", + "healthCheckedAt": "上次健康检查:{date}", + "healthNeverChecked": "没有健康检查记录", + "batchDeleteTitle": "删除技能", + "selectAllRows": "全选所有行", + "selectRow": "选择 {name}", + "editSkillNamed": "编辑技能 {name}", + "deleteSkillNamed": "删除技能 {name}", + "editCategoryNamed": "编辑分类 {name}", + "deleteCategoryNamed": "删除分类 {name}" }, "Auth": { "signInTitle": "登录 TakoAPI", @@ -462,12 +588,26 @@ "noAccount": "还没有账户?", "signUpLink": "注册", "haveAccount": "已经有账户了?", - "signInLink": "登录" + "signInLink": "登录", + "forgotPassword": "忘记密码?", + "forgotTitle": "重置密码", + "forgotSubtitle": "我们会发一封邮件,里面有设置新密码的链接", + "sendResetLink": "发送重置链接", + "sendingResetLink": "正在发送……", + "resetLinkSent": "如果该邮箱有对应账户,我们已经发出了密码重置链接。请查收邮件。", + "backToSignIn": "返回登录", + "resetTitle": "设置新密码", + "resetSubtitle": "为你的账户输入一个新密码", + "newPasswordHint": "新密码(至少 8 个字符)", + "updatePassword": "更新密码", + "updatingPassword": "正在更新……", + "passwordUpdated": "密码已更新,正在带你去登录……", + "resetLinkInvalid": "这个密码重置链接无效或已过期。", + "requestNewLink": "重新申请链接" }, "Dashboard": { "title": "开发者控制台", "signInPrompt": "登录以管理 API 密钥并查看用量。", - "signIn": "登录", "description": "管理 API 密钥并监控网关用量。一个密钥,一份账单,任意智能体。", "apiKeys": "API 密钥", "createKey": "创建密钥", @@ -497,12 +637,11 @@ "billing": "余额与账单", "creditBalance": "余额", "totalSpend": "累计消费", - "topUpComingSoon": "目前余额由平台充值,自助 PayPal 充值即将上线。", "topUpLabel": "充值", "topUpButton": "用 PayPal 充值", - "topUpRedirecting": "正在跳转…", + "topUpRedirecting": "正在跳转……", "topUpSuccess": "充值成功——余额已更新。", - "topUpError": "充值未完成,请重试。", + "topUpError": "充值未完成,请重试。", "topUpCancel": "已取消充值。", "noLedger": "暂无余额变动记录。", "ledgerType": "类型", @@ -513,35 +652,65 @@ "ledgerDebit": "调用扣费", "ledgerPayout": "结算分成", "ledgerRefund": "退款", - "ledgerAdjust": "调整" + "ledgerAdjust": "调整", + "onboardingTitle": "让第一次调用跑通", + "onboardingSubtitle": "三步走完,就有智能体回应你。", + "stepKeyTitle": "创建 API 密钥", + "stepKeyBody": "一个密钥就能调用 TakoAPI 上的所有智能体。明文只在创建后立即显示一次。", + "stepCallTitle": "发起第一次调用", + "stepCallBody": "复制下面任意一个示例,把 TAKO_KEY 设成你的密钥再运行。几秒内这次调用就会出现在「用量」里。", + "stepInstallTitle": "安装到你的编程智能体", + "stepInstallBody": "让 Claude Code、Codex 或 OpenCode 学会通过 TakoAPI 发现并调用智能体。", + "stepInstallLink": "打开安装指南", + "stepOptional": "可选", + "stepDone": "已完成", + "stepTodo": "尚未完成", + "keyNameLabel": "密钥名称", + "keyNamePlaceholder": "网关密钥", + "createKeyError": "密钥创建失败,请重试。", + "revokeNamed": "吊销 {name}", + "quickstartHint": "把 TAKO_KEY 设成上面任意一个密钥,然后用 slug 调用任意智能体。", + "creditRule": "免费智能体不收费;收费智能体从预付余额扣款,一旦余额会变成负数,调用就会以 HTTP 402 被拒。", + "topUpUnavailable": "自助充值还没上线——目前余额由平台代充。无论如何,上面的余额都是实时的。", + "dismiss": "关闭", + "loadError": "部分控制台数据加载失败。", + "retry": "重试", + "recentCallsCaption": "你最近的网关调用", + "chartTitle": "最近 {days} 天的每日调用次数", + "chartTotal": "共 {count, number} 次", + "chartAria": "最近 {days} 天每日网关调用次数的柱状图,共 {count, number} 次调用。", + "chartDay": "{date}:{calls, number} 次调用,{errors, number} 次失败" }, "Profile": { - "title": "我的资料", - "signInPrompt": "请登录以查看你的资料。", - "signIn": "登录", - "submissions": "提交", + "title": "我的发布", + "signInPrompt": "登录后查看你发布的智能体和技能。", "likes": "点赞", "views": "浏览量", - "statValue": "{count, number}", - "mySubmissions": "我的提交", - "submitNew": "提交新内容", "tab_all": "全部", - "tab_approved": "已通过", - "tab_pending": "待审核", - "tab_rejected": "已拒绝", - "tabCount": "{count, number}", - "emptyTab": "此标签下暂无提交", + "emptyTab": "这里还没有技能。", "submitFirst": "提交你的第一个技能", - "statusApproved": "已通过", - "statusPending": "待审核", + "statusApproved": "已上线", + "statusPending": "审核中", "statusRejected": "已拒绝", - "reviewerNote": "审核员备注:" + "reviewerNote": "审核员备注:", + "publishAgent": "发布智能体", + "submitSkill": "提交技能", + "statAgents": "智能体", + "statCalls": "网关调用", + "statSkills": "技能", + "tabAgents": "智能体", + "tabSkills": "技能", + "statusDisabled": "已停用", + "kindHosted": "可调用", + "kindProject": "开源", + "noAgents": "这里还没有智能体。", + "publishFirstAgent": "发布你的第一个智能体", + "myListings": "我的发布" }, "Submit": { "title": "提交技能", "subtitle": "与社区分享你的 OpenClaw 技能", "signInPrompt": "请登录以提交技能。", - "signIn": "登录", "skillSource": "技能来源", "githubUrl": "GitHub URL", "clawSkillsUrl": "ClawSkills.sh URL", @@ -571,14 +740,21 @@ "submitting": "正在提交……", "enterUrlFirst": "请先输入一个 URL", "autoFillFailed": "自动填充失败", - "submitFailed": "提交失败", - "somethingWentWrong": "出了点问题" + "somethingWentWrong": "出了点问题", + "successTitle": "已提交 🎉", + "pendingReview": "{name} 现已进入待审核状态。", + "nextStepsTitle": "接下来会发生什么", + "nextStepReview": "审核员会检查仓库和描述。", + "nextStepEmail": "审核完成后我们会发邮件通知你。", + "nextStepProfile": "你的发布页会一直显示当前状态和审核员留言。", + "viewMyListings": "查看我的发布", + "backToSkills": "← 返回技能列表" }, "SubmitAgent": { "title": "发布智能体", "signInPrompt": "请登录以发布智能体。", "signIn": "登录", - "intro": "在市场上发布一个可调用的智能体。提交其 A2A AgentCard URL,我们会解析其余信息,或者你也可以手动填写。", + "intro": "在市场上发布一个可调用的智能体。提交它的 A2A AgentCard URL,我们会解析其余信息,也可以手动填写。", "tabUrl": "AgentCard URL", "tabManual": "手动", "cardUrlLabel": "AgentCard URL", @@ -611,7 +787,24 @@ "successTitle": "已提交 🎉", "pendingReview": "{name} 现已进入待审核状态。", "successDetail": "我们会验证 AgentCard,审核通过后将其发布到市场。", - "backToMarketplace": "← 返回市场" + "backToMarketplace": "← 返回市场", + "checkCard": "检查卡片", + "checking": "正在检查……", + "enterCardUrlFirst": "请先填写 AgentCard URL。", + "previewTitle": "收录后会是这个样子", + "previewNoDescription": "这张卡片没有描述。", + "previewEndpoint": "端点", + "previewCapabilities": "能力", + "previewSkillCount": "{count, plural, =0 {未声明任何能力} other {# 项能力}}", + "previewSupports": "支持", + "previewStreaming": "流式传输", + "previewPush": "推送通知", + "previewHint": "目前还没保存任何内容——点下面的发布才会提交审核。", + "nextStepsTitle": "接下来会发生什么", + "nextStepReview": "审核员会检查 AgentCard,并确认你的端点能正常响应。", + "nextStepEmail": "审核完成后我们会发邮件通知你。", + "nextStepProfile": "你的发布页会一直显示当前状态和审核员留言。", + "viewMyListings": "查看我的发布" }, "AgentEngagement": { "yourRating": "你的评分", @@ -619,28 +812,44 @@ "noRatings": "暂无评分", "bookmark": "收藏", "bookmarked": "已收藏", - "signInToRate": "登录后即可评分或收藏" + "signInToRate": "登录后即可给这个智能体评分或收藏", + "rateStars": "{count, plural, other {评 # 星}}" }, "Bookmarks": { "title": "收藏的智能体", - "subtitle": "你收藏的智能体。", + "subtitle": "你收藏过的智能体。", "empty": "还没有收藏。", "browseAgents": "浏览智能体", - "signInPrompt": "登录后查看你的收藏。", - "signIn": "登录" + "signInPrompt": "登录后查看你的收藏。" }, "ScenarioPage": { "metaTitle": "{scenario} AI 智能体", - "metaDescription": "通过一个统一 API 发现并调用「{scenario}」AI 智能体——以开放的 A2A AgentCard 描述。", + "metaDescription": "通过一个统一 API 发现并调用「{scenario}」AI 智能体——在 TakoAPI 上以开放的 A2A AgentCard 描述。", "heroSubtitle": "面向「{scenario}」的 AI 智能体,通过一个统一 API 调用。", "empty": "该场景下暂无智能体。", "browseAll": "浏览全部智能体", "back": "← 全部场景", "breadcrumbScenarios": "场景", "indexTitle": "按场景浏览 AI 智能体", - "indexDescription": "按用途查找 AI 智能体——前端、投资、营销、客服等——都可通过一个 API 调用。", + "indexDescription": "按用途查找 AI 智能体——前端、投资、营销、客服等等——全都可以通过一个 API 调用。", "indexHeroTitle": "按场景浏览", - "indexHeroSubtitle": "按你要完成的任务查找 AI 智能体。" + "indexHeroSubtitle": "按你要完成的任务查找 AI 智能体。", + "indexPopularHeading": "收录较多", + "indexPopularHint": "这些场景下的智能体足够多,可以横向比较。", + "indexSparseHeading": "刚刚起步", + "indexSparseHint": "目前只有寥寥几个智能体——很容易在这里做第一个。", + "indexSparseCta": "在这些场景里发布智能体 →", + "metaTitlePaged": "{scenario} AI 智能体 — 第 {page} 页", + "resultsLabel": "{scenario} 智能体", + "paginationLabel": "分页", + "showingRange": "显示第 {from}–{to} 个,共 {total} 个", + "prev": "← 上一页", + "next": "下一页 →", + "goToPage": "跳到第 {page} 页", + "ctaTitle": "知道哪个「{scenario}」智能体?", + "ctaBody": "这个场景还比较空。发布一个智能体,浏览「{scenario}」的人都能看到它。", + "ctaButton": "发布智能体", + "relatedHeading": "看看其他场景" }, "Badge": { "snippetTitle": "添加徽章", @@ -656,15 +865,160 @@ "step2": "在收录页的徽章框里复制 Markdown。", "step3Title": "粘贴到 README", "step3": "放在顶部附近——访客和搜索引擎都能链接到你的 TakoAPI 页面。", - "findListing": "找到你的收录页" + "findListing": "找到你的收录页", + "exampleAlt": "TakoAPI 徽章" }, "Blog": { "title": "博客", - "subtitle": "关于 A2A、Agent 网关,以及如何用 TakoAPI 构建的指南。", + "subtitle": "关于 A2A、智能体网关,以及如何用 TakoAPI 构建的指南。", "readMore": "阅读全文", "backToBlog": "← 所有文章", "minRead": "约 {min} 分钟阅读", "published": "发布于", "faqTitle": "常见问题" + }, + "NotFound": { + "title": "找不到这个页面", + "description": "链接可能已经过期,或者这条收录已被移除。可以去目录看看,或者用顶部的搜索框找找。", + "home": "首页", + "agents": "浏览智能体", + "skills": "浏览技能" + }, + "ErrorPage": { + "title": "出了点问题", + "description": "这个页面没能加载出来。重试一次通常就好了,如果还不行,下面的编号能帮我们在日志里定位。", + "reference": "编号:{id}", + "retry": "重试", + "home": "回到首页" + }, + "Consent": { + "title": "分析统计", + "description": "我们用 Google Analytics 了解哪些页面对访客有用。在你同意之前,不会加载任何脚本,也不会写入任何 cookie。", + "accept": "同意", + "decline": "拒绝" + }, + "Docs": { + "metaTitle": "API 文档", + "title": "TakoAPI API 参考", + "description": "从一个 API 密钥到一次成功的智能体调用:鉴权、发现端点、统一智能体网关、托管 MCP 服务器、错误码、速率限制与计费。", + "heroBadge": "开发者文档", + "heroTitle": "TakoAPI API 参考", + "heroSubtitle": "一个密钥,调用所有智能体。把任意 OpenAI 兼容客户端指向 TakoAPI,用 slug 调用智能体——或者完全不用密钥,直接浏览整个目录。", + "heroKeyCta": "创建 API 密钥", + "heroBrowseCta": "浏览智能体", + "tocTitle": "本页内容", + "tocLabel": "目录", + "quickstartTitle": "快速上手", + "quickstartIntro": "三步就能拿到第一个回复。本页的每个请求都是对线上 API 的真实调用。", + "quickstartStep1Title": "创建密钥", + "quickstartStep1": "登录后在控制台创建 API 密钥。明文只在创建时显示一次。", + "quickstartStep2Title": "导出密钥", + "quickstartStep2": "本页所有示例都从 TAKO_KEY 读取密钥。", + "quickstartStep3Title": "调用智能体", + "quickstartStep3": "把 model——或 URL 里的 slug——设成目录中任意一个托管智能体。", + "quickstartSamplesLabel": "快速上手调用示例", + "quickstartNote": "示例里的 slug 只是占位符,不是真实智能体。请从任意智能体页面复制真实的那个,并且只有标注为托管的智能体才能被调用。", + "authenticationTitle": "鉴权", + "authenticationIntro": "网关调用使用 bearer token 鉴权。发现类端点是匿名的,完全不需要密钥。", + "authHeaderNote": "除 Authorization 头外,也接受 x-api-key。密钥一律以 tako_live_ 开头。", + "authWhereTitle": "密钥从哪来", + "authWhere": "在控制台为每个客户端或环境各建一个密钥,不再使用的及时吊销。", + "authOnceTitle": "明文只显示一次", + "authOnce": "服务端只保存密钥的 SHA-256 哈希和一小段用于展示的前缀,所以明文丢了就找不回来——重新建一个,再把旧密钥吊销。", + "authRevokeTitle": "吊销立即生效", + "authRevoke": "每次调用都会重新查库校验密钥,不做任何缓存,所以被吊销的密钥在下一个请求就会被拒。历史用量仍然保留。", + "discoveryTitle": "发现", + "discoveryIntro": "四个公开、可缓存且无需密钥的端点。返回的只会是已审核通过的智能体和技能。", + "endpointRegistrySummary": "完整的智能体目录——给模型看的是 Markdown,给代码用的是 JSON。", + "endpointRegistryNote": "不带 format=json 时返回 Markdown(text/markdown),分成托管智能体和开源项目两张表,长度控制在可以直接塞进模型上下文的范围——安装后的 TakoAPI 技能读的就是它。两种格式都受 limit 限制,所以在认定拿全之前,先看 truncated 和各项总数。自由文本参数会被截断到 100 个字符。", + "paramRegistryFormat": "md(默认)或 json。", + "paramRegistryQ": "对智能体名称和描述做自由文本匹配。", + "paramRegistryCategory": "分类 slug。", + "paramRegistryProtocol": "只返回声明了该协议的智能体。", + "paramRegistryKind": "HOSTED 是可通过网关调用的智能体,PROJECT 是仅供发现的开源仓库。", + "paramRegistrySort": "排序方式。默认精选优先,其次调用最多,再次最新——项目则默认按星标最多。", + "paramRegistryLimit": "返回的条目总数。先填托管智能体,再填项目。", + "endpointAgentDetailSummary": "单个智能体的完整公开信息,包括它声明的 A2A 技能。", + "endpointAgentDetailNote": "只有审核通过的智能体能解析成功,其余一律 404。审核相关字段——审核备注、内部 id、发布者头像——不会出现在这个结构里。边缘缓存 60 秒。", + "paramAgentDetailSlug": "路径段。智能体的 slug,与注册表返回的一致。", + "endpointSkillsDirectorySummary": "OpenClaw 技能目录,默认返回 Markdown。", + "endpointSkillsDirectoryNote": "和注册表一样以 Markdown 为先。没有公开安装命令的技能,install 为 null——这时改用 github 链接。downloads 是从技能自己的收录页抓来的第三方数字,不是 TakoAPI 的安装量。", + "paramSkillsDirFormat": "md(默认)或 json。", + "paramSkillsDirQ": "对技能名称和描述做自由文本匹配。", + "paramSkillsDirCategory": "分类 slug。", + "paramSkillsDirPage": "页码,从 1 开始。", + "paramSkillsDirLimit": "每页结果数。", + "endpointSkillsSearchSummary": "对技能目录做全文搜索,只返回 JSON。", + "endpointSkillsSearchNote": "q 是必填的:不带它时端点返回空列表,而不是整个目录。每条结果都是完整的目录行,只去掉 readme 和 reviewNote,上面的示例做了删节。", + "paramSkillsSearchQ": "搜索词。匹配技能名称、描述和作者。", + "paramSkillsSearchCategory": "分类 slug。", + "paramSkillsSearchPage": "页码,从 1 开始。", + "paramSkillsSearchLimit": "每页结果数。", + "gatewayTitle": "网关", + "gatewayIntro": "调用同一个智能体的三种方式。三者都要密钥,计费方式完全一致,并且都在本该写模型名的位置接受智能体的 slug。只有 kind 为 HOSTED 的智能体可以被调用。", + "endpointChatCompletionsSummary": "OpenAI 兼容。把任意 OpenAI SDK 指向 /v1,并把 model 设成智能体 slug。", + "endpointChatCompletionsNote": "整段对话都会转发过去:每一轮对应一个 A2A text part,按顺序排列,并在每个 part 上带上 OpenAI 的 role。usage 字段一定存在,但恒为零——这个网关不统计 token,而且智能体也未必是 LLM。设置 stream: true 得到上面的 SSE 形式,再加 stream_options.include_usage 还能拿到末尾的 usage 块。非流式调用 30 秒超时,流式 120 秒。", + "endpointA2AMessageSummary": "原生 A2A 透传。原样返回智能体自己的 JSON-RPC 响应。", + "endpointA2AMessageNote": "以 A2A message/send 调用的形式转发。可以发 text 简写形式,也可以发完整的 A2A message——带 parts、taskId 和 contextId——用来延续多轮任务,或透传 file、data 类型的 part。智能体的状态码和响应体原样转发,内容为空的请求会以 400 拒绝,不计费。30 秒超时。", + "endpointA2AStreamSummary": "同样的调用,改走 Server-Sent Events,适用于支持流式的智能体。", + "endpointA2AStreamNote": "以 text/event-stream 原样转发智能体的 A2A message/stream 帧。这个端点只接受 text 简写形式。连接在 120 秒后断开,并且只有流走到结束才会计费——见「速率限制与计费」。", + "labelQueryParams": "参数", + "labelRequest": "请求体", + "labelExample": "示例", + "labelResponse": "响应", + "labelStreaming": "stream: true 时的响应", + "labelA2AMessageForm": "完整 A2A message 形式", + "labelAuthRequired": "需要密钥", + "labelNoAuth": "无需密钥", + "labelBillable": "计费", + "labelFree": "免费", + "labelParamName": "名称", + "labelParamType": "类型", + "labelParamDesc": "说明", + "paramsTableCaption": "{path} 的参数", + "mcpTitle": "MCP 服务器", + "mcpIntro": "TakoAPI 在 {url} 上运行一个托管的无状态 Model Context Protocol 服务器,走 Streamable HTTP。注册一次,你的编程智能体就能把检索和调用目录当作原生工具来用。", + "mcpRegisterTitle": "注册", + "mcpRegisterLabel": "MCP 注册命令", + "mcpAuthNote": "三个只读工具可以匿名使用。invoke_agent 会消耗余额,所以需要你的密钥:通过 Authorization 头传入(Claude Code)、用 bearer_token_env_var 指定(Codex),或者写在 headers 块里(OpenCode)。", + "mcpToolsTitle": "工具", + "mcpToolsTableCaption": "TakoAPI MCP 服务器提供的工具", + "mcpToolColumn": "工具", + "mcpToolAccessColumn": "访问方式", + "mcpToolWhatColumn": "作用", + "mcpToolKeyRequired": "需要密钥", + "mcpToolAnonymous": "匿名", + "mcpToolSearchAgents": "按关键词、分类或协议检索注册表。返回 slug、协议和定价。", + "mcpToolGetAgent": "按 slug 返回某个智能体的全部已知信息——能力、协议、端点、定价和声明的技能。", + "mcpToolSearchSkills": "检索 OpenClaw 技能目录。不带查询词时返回精选的头部技能。", + "mcpToolInvokeAgent": "通过网关调用智能体并返回它的回复。需要密钥,并会消耗余额。", + "mcpRateNote": "MCP 端点按 IP 地址限制为每分钟 120 个请求,与密钥在网关上的额度分开计算。", + "mcpInstallNote": "比起服务器更想要一个技能文件?安装程序会教 Claude Code、Codex 和 OpenCode 直接用普通 HTTP 调用这些端点。", + "errorsTitle": "错误", + "errorsIntro": "网关的每一次失败都返回 JSON。以下是网关自身会返回的状态码。", + "errorsTableCaption": "网关返回的错误状态码", + "errorStatusColumn": "状态码", + "errorCodeColumn": "错误码", + "errorWhenColumn": "触发条件", + "errorBodyColumn": "示例响应体", + "error400When": "slug 指向的是没有可调用端点的开源项目、请求体校验失败,或者所有消息轮次都是空的。", + "error401When": "密钥缺失、格式错误、不存在或已被吊销。", + "error402When": "该智能体是收费的,扣这笔费用会让余额变成负数。这个检查在联系智能体之前完成,所以不会产生任何消耗。", + "error404When": "没有已审核通过的智能体使用这个 slug。", + "error429When": "该密钥在过去 60 秒内超出了额度。Retry-After 给出窗口重置前还要等待的秒数。", + "error502When": "智能体没有响应、超时、没有返回流,或者在 HTTP 200 里报了 JSON-RPC 错误。这些情况一律不计费。", + "errorsOpenAiNote": "在 /v1/chat/completions 上,同样的错误会以 OpenAI 的信封格式返回——type、param 和 code 始终存在——所以 OpenAI SDK 会抛出它惯常的异常类型:", + "errorsPassthroughNote": "智能体自己返回的状态码,会被 A2A message 端点原样转发,所以那里的 4xx 可能来自智能体,而不是我们。", + "limitsTitle": "速率限制与计费", + "limitsRateTitle": "速率限制", + "limitsRate": "网关允许每个 API 密钥每 60 秒 120 个请求。这个窗口在所有服务实例之间共享,并且按密钥而不是 IP 地址计数,因此既不会在高负载下变松,也不会因为共用 NAT 而变紧。", + "limitsRateOverride": "单个密钥可以带自己的额度,只对该密钥覆盖默认值。超限时会收到 429,并附带以秒为单位的 Retry-After 头。", + "limitsBillingTitle": "计费", + "limitsBillingFree": "定价为 FREE 的智能体不收费——单价缺失或为零的同样不收费。网关不会凭空造出一笔扣费。", + "limitsBillingPriced": "收费智能体按单价从你的预付余额扣款,每次调用扣一次。没有 token 级计量,所以一次调用不管跑多久都只算一份。", + "limitsBillingPreflight": "余额检查发生在联系智能体之前。如果扣费会让余额变成负数,调用直接以 402 拒绝,智能体根本不会被调用——不存在透支。", + "limitsBillingFailures": "失败的调用不计费。智能体返回 4xx 或 5xx、超时、端点不可达或 JSON-RPC 错误,都只记录这次尝试,不产生费用。", + "limitsBillingStreams": "流式调用只有正常结束才计费。中断、超时、中途失败,或者你提前断开的流,都会被记录但不计费。", + "limitsBillingLedger": "每一笔扣费都会针对触发它的那次调用写入一条不可变的账目记录。余额、账目和逐次调用的用量都在控制台里。" } } diff --git a/next.config.ts b/next.config.ts index 5c5c27c28..cedbeae4e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -3,8 +3,42 @@ import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts"); +// Baseline hardening headers for every response. A full Content-Security-Policy is +// deliberately not set here: Next's inline runtime scripts and the GA4 loader would +// need a per-request nonce, and a broken CSP takes the whole site down. Clickjacking +// is covered by frame-ancestors + X-Frame-Options, which need no nonce. +const SECURITY_HEADERS = [ + { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" }, + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "X-Frame-Options", value: "DENY" }, + { key: "Content-Security-Policy", value: "frame-ancestors 'none'" }, + { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, + { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=()" }, + { key: "X-DNS-Prefetch-Control", value: "on" }, +]; + +// Public, token-authenticated machine surfaces. Browsers can only call them +// cross-origin (an OpenAI SDK in a web app, a docs "Try it" widget) if CORS allows +// it. `*` is correct here: the API key travels in the Authorization header, never in +// cookies, so there is no ambient credential for a foreign origin to ride on. +const CORS_HEADERS = [ + { key: "Access-Control-Allow-Origin", value: "*" }, + { key: "Access-Control-Allow-Methods", value: "GET, POST, DELETE, OPTIONS" }, + { key: "Access-Control-Allow-Headers", value: "Content-Type, Authorization, X-Api-Key" }, + { key: "Access-Control-Max-Age", value: "86400" }, +]; + const nextConfig: NextConfig = { output: "standalone", + poweredByHeader: false, + async headers() { + return [ + { source: "/:path*", headers: SECURITY_HEADERS }, + ...["/v1/:path*", "/mcp", "/api/registry", "/api/agent", "/api/agents/:path*", "/api/skills/:path*", "/api/categories", "/api/badge/:path*"].map( + (source) => ({ source, headers: CORS_HEADERS }) + ), + ]; + }, }; export default withNextIntl(nextConfig); diff --git a/package-lock.json b/package-lock.json index 82fc791c3..9c71bdce5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "takoapi-temp", + "name": "takoapi", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "takoapi-temp", + "name": "takoapi", "version": "0.1.0", "dependencies": { "@auth/prisma-adapter": "^2.11.1", @@ -37,13 +37,18 @@ "puppeteer-core": "^24.40.0", "tailwindcss": "^4", "tsx": "^4.21.0", - "typescript": "^5" + "typescript": "^5", + "vitest": "^3.2.7" + }, + "engines": { + "node": ">=20 <25", + "npm": ">=10" } }, "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz", + "integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==", "dev": true, "license": "MIT", "engines": { @@ -335,6 +340,55 @@ "node": ">=6.9.0" } }, + "node_modules/@eloqnt/config": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eloqnt/config/-/config-0.1.0.tgz", + "integrity": "sha512-SLR6ZSHxu6XdZtmOz3xRmC3gsY/sCzqtLMD1gxbW2I3XXPBbupwL5RZbkLU/NcZm74AKZrIUPfz3CtEJfxUKqQ==", + "license": "MIT" + }, + "node_modules/@eloqnt/format-json": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eloqnt/format-json/-/format-json-0.1.0.tgz", + "integrity": "sha512-4Pkb+HyzgWNJXoU48aJhUyNZSjZvMGooPCNG1ZFmBm55xTiNNPQCNXH66Ob6sXS6RvbWZrRpCf+Uo/Jz9K9g+A==", + "license": "MIT", + "dependencies": { + "@eloqnt/config": "^0.1.0" + } + }, + "node_modules/@eloqnt/format-po": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eloqnt/format-po/-/format-po-0.1.0.tgz", + "integrity": "sha512-odgtuICWs67GdkNqY51/d2vaIukuOdlGalKASUGkI5c4cyWOmv/WIVyk5gZyn3xr614RzwWp57s9WB40lRLlBw==", + "license": "MIT", + "dependencies": { + "@eloqnt/config": "^0.1.0", + "po-parser": "^2.2.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -347,9 +401,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -364,9 +418,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -381,9 +435,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -398,9 +452,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -415,9 +469,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -432,9 +486,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -449,9 +503,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -466,9 +520,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -483,9 +537,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -500,9 +554,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -517,9 +571,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -534,9 +588,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -551,9 +605,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -568,9 +622,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -585,9 +639,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -602,9 +656,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -619,9 +673,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -636,9 +690,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -653,9 +707,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -670,9 +724,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -687,9 +741,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -704,9 +758,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -721,9 +775,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -738,9 +792,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -755,9 +809,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -772,9 +826,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -872,9 +926,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", - "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", "dev": true, "license": "MIT", "dependencies": { @@ -884,7 +938,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.3.0", + "js-yaml": "^4.3.2", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -939,9 +993,9 @@ "license": "MIT" }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.16.tgz", - "integrity": "sha512-kl6b/4D56gjGZi4ZewSmvXbalHwjOUI5ogEHPZqw42goeXTTrL7/yuPzvdrvr0QigDtvaOeb+UeMf62jks43Yg==", + "version": "3.5.17", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.17.tgz", + "integrity": "sha512-cN9jhVqT7u0K9tix43fhjoUwL0nazyW6zsNIXs2QdPADr+nurPfYyssUiMqcSCGlPcCiqnYVxgSn7zBSuI+5Bg==", "license": "MIT", "dependencies": { "@formatjs/icu-skeleton-parser": "2.1.11" @@ -1527,9 +1581,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -1544,10 +1598,27 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", - "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", "dev": true, "license": "MIT", "optional": true, @@ -1562,8 +1633,8 @@ "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { - "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", - "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" } }, "node_modules/@next/env": { @@ -2042,9 +2113,9 @@ } }, "node_modules/@parcel/watcher/node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "license": "MIT", "engines": { "node": ">=12" @@ -2168,6 +2239,356 @@ "node": ">=10" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", + "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", + "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", + "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", + "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", + "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", + "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", + "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", + "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", + "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", + "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", + "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", + "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", + "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", + "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", + "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", + "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", + "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", + "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", + "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", + "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", + "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", + "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", + "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", + "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", + "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -2194,9 +2615,9 @@ "license": "MIT" }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.47.tgz", - "integrity": "sha512-GsoMtan3ojGGMGFbl31mmRu5ctZ56re8grGE8mO/OHJ8O+JRkzod02fe7X6ZQ8JvamA3imkEkx/h3u+vsOgPgA==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.16.2.tgz", + "integrity": "sha512-i/j0HNbnn79qnTVPicvay92Nark8fW8NQqn1e2mGERjUXNpBV0+SwQxlRpk2zBhn6laJ8PDI6Kn1nHZhnz3LCA==", "cpu": [ "arm64" ], @@ -2210,9 +2631,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.47.tgz", - "integrity": "sha512-leTi7Rx3KF4zcC637iqWgk9SoV8VXAD8ppQYXsep63px5A/UftOcxLN1pmr8Z1si/YvX90ompP/rHgpYkgwXWg==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.16.2.tgz", + "integrity": "sha512-HrwqHyEyHVXO3qTk8EkNK7/b6sOZSEoNh+pot6RdE5x0LbNqfo8LtJUvi3UTXr+5ja/o5HbJdW80eCXo+NjbiA==", "cpu": [ "x64" ], @@ -2226,9 +2647,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.47.tgz", - "integrity": "sha512-hBqHuoWKKIsKmDBn9qVeWqj5GWZhtlcczVaqQmNRXsDfq+voR5CxKRfamA367QjJXtceYuliLFfEL8QsskRM2g==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.16.2.tgz", + "integrity": "sha512-MdXi83Z/gGp1LIrg+h7HKxiul/z/Bty/ZJSvYAFqDl9zteC1XLSAZdScquKtXPp50rdyXqritTDCqQBhwVfZKA==", "cpu": [ "arm" ], @@ -2242,9 +2663,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.47.tgz", - "integrity": "sha512-TBxvRz+B4K205TWHHZxWVxkC2RFNP/Mz3PNcECBos5PsKwxjg3QSJzdoebr0VCf0Bfh8HOPldKxAP/8XkFe9gA==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.16.2.tgz", + "integrity": "sha512-/jcTmK6Ktz3owM3YtiKvjofV6p3VpHnYzTIrOGwDIOsDigRAAVuZ8east33wYO/7UTdKYFlyHNnJNT0WJqOA3Q==", "cpu": [ "arm64" ], @@ -2258,9 +2679,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.47.tgz", - "integrity": "sha512-3Yu3Uq/VgytqsPjTMbkPU1ExADytbdWbruJYhA584E9jrpE2Ki+R6VVPoZCeAVk1Cb7QxcRTgblw6bSa6a/R+w==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.16.2.tgz", + "integrity": "sha512-4gFarKaFnlJTSlJYKmMhV4u+3YE4uYfiydpBoYjmgQhCf9lAieOq+WilZaK9vVSHeqLuQpTEiGULZqAdsRX5Dw==", "cpu": [ "arm64" ], @@ -2274,9 +2695,9 @@ } }, "node_modules/@swc/core-linux-ppc64-gnu": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.47.tgz", - "integrity": "sha512-wfdMi5IaOaNtmh2/6geRoxIdNfqylUZFdtzTKS655y1axWfIWyx7As74vv0wVdjeCIZ3WmCI9odDd4rUttXOSQ==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.16.2.tgz", + "integrity": "sha512-syqSLGd6KlZ1PciNzs6bIUlhOuFztZufebOHaERjc4N4SqNZxyqYd4I+jj/EfOYnpe0kNjccn9HJLN1p5dz3+w==", "cpu": [ "ppc64" ], @@ -2290,9 +2711,9 @@ } }, "node_modules/@swc/core-linux-s390x-gnu": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.47.tgz", - "integrity": "sha512-3hHYBY0yx8Ez7GMRrkhXHQzMdR5IZA6Wq5Ee4svlgwvSECLpnAJ9+0AimEGUFDvuLwE7nV/2+PYe8+Nm4rvNcQ==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.16.2.tgz", + "integrity": "sha512-ZBBLK+ewGyXLzWeMS7wbKtWBdnif6etn7xvPY/iOfbdsjX/+bgkp1pQt2lWF2wlu2hXYZuhJ/tHZE/QR8/apzg==", "cpu": [ "s390x" ], @@ -2306,9 +2727,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.47.tgz", - "integrity": "sha512-TjfhjgP/jGCfFHYC3JQPhJA1HwErbIJ9JfREDc1KNkvY6P0LodCgKVIlQ5deeTbkG7ih3bF5PHJLuLpaZjdRyQ==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.16.2.tgz", + "integrity": "sha512-LyHJgxCA4Tje0ysBMbEb0tt/ie8kgUKoFE3JAKFhpevmTmhYEoC0H9s47WuDsqiFckF1ITUguZIXJG6K5e0dvg==", "cpu": [ "x64" ], @@ -2322,9 +2743,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.47.tgz", - "integrity": "sha512-CQpS8Ge/avfjZd0UEwG/sds83Uu32deQXcV1Jo3jD0mmvQQqtYAjpsDZXugmheeAwmt+YIuoVtVHro8LMYHqsQ==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.16.2.tgz", + "integrity": "sha512-PghXJlVM1cgtLfNUR1vxFo1z+PDRAe8cWAJlZZ7spmeiN7BospGXg/MHUg7oNSgwSX7Zo//YKv9P5yD9apsFJQ==", "cpu": [ "x64" ], @@ -2338,9 +2759,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.47.tgz", - "integrity": "sha512-0W8IKHsUTYiT7G2RqtOoVWk+89yzZikIiDUb/sCK6BmQDBhN91hQSfyUtW12jhEWLzYgcfmisfsZrmZE+84U1A==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.16.2.tgz", + "integrity": "sha512-StTOSefYBxemvNYYUI3UmO1a8y+hSPjjfHogC2TEHL+Z1PlEBim/XtLas5rS04jAzT9RrNmbtX911SZ42H9jSQ==", "cpu": [ "arm64" ], @@ -2354,9 +2775,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.47.tgz", - "integrity": "sha512-ZIp49d2Z4/ka2jO9otOg4hDvTdPmp86kVOgS2M5FCPI7eKKZ1W0boxWn+8XeZrfERtFGW0AlMRm4JhlJa7l3NA==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.16.2.tgz", + "integrity": "sha512-fycER209DYIzsibpTMC+chND05OfOjgztWL9U8OE6/uUlsOUZH3eh98isBLEnOymYUhlJLEt5++W1+KL/FOh5Q==", "cpu": [ "ia32" ], @@ -2370,9 +2791,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.47.tgz", - "integrity": "sha512-2h8Iek95vnixkBRCo+H8p09+Q5ll2NgSMFrWTy0iKt7+/t+8/T5mBpiT6c0ZxSS7wcWjwZ9sGZkK70tTSYHdDw==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.16.2.tgz", + "integrity": "sha512-cSd1z6ivSrJPVr+moVwOHWjeKy6TpO4/Shwcv5KCrKYXCccxwh4pRy1C3fDioNx2PF1jPZWHKZjtXt+Be9VbaQ==", "cpu": [ "x64" ], @@ -2632,6 +3053,29 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "dev": true, @@ -2750,6 +3194,24 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", @@ -2811,9 +3273,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-I8bPpDLcHBv1qiIiXDCy71Rt8eQDKJP0sMSWJphDdAcdqiJ1sGpZamavoEIRZmYzjia9LuEb2HlYdDpmoENpvQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2832,17 +3294,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", - "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.69.0.tgz", + "integrity": "sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/type-utils": "8.65.0", - "@typescript-eslint/utils": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/type-utils": "8.69.0", + "@typescript-eslint/utils": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2855,15 +3317,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.65.0", + "@typescript-eslint/parser": "^8.69.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", - "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", + "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", "dev": true, "license": "MIT", "engines": { @@ -2871,17 +3333,17 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", - "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.69.0.tgz", + "integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", "debug": "^4.4.3" }, "engines": { @@ -2897,14 +3359,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", - "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.69.0.tgz", + "integrity": "sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.65.0", - "@typescript-eslint/types": "^8.65.0", + "@typescript-eslint/tsconfig-utils": "^8.69.0", + "@typescript-eslint/types": "^8.69.0", "debug": "^4.4.3" }, "engines": { @@ -2919,14 +3381,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", - "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.69.0.tgz", + "integrity": "sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0" + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2937,9 +3399,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", - "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.69.0.tgz", + "integrity": "sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==", "dev": true, "license": "MIT", "engines": { @@ -2954,15 +3416,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", - "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.69.0.tgz", + "integrity": "sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0", + "@typescript-eslint/utils": "8.69.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -2979,9 +3441,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", - "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.69.0.tgz", + "integrity": "sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==", "dev": true, "license": "MIT", "engines": { @@ -2993,16 +3455,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", - "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.69.0.tgz", + "integrity": "sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.65.0", - "@typescript-eslint/tsconfig-utils": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/visitor-keys": "8.65.0", + "@typescript-eslint/project-service": "8.69.0", + "@typescript-eslint/tsconfig-utils": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3073,16 +3535,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", - "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.69.0.tgz", + "integrity": "sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.65.0", - "@typescript-eslint/types": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0" + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3097,13 +3559,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", - "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.69.0.tgz", + "integrity": "sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/types": "8.69.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3451,6 +3913,121 @@ "win32" ] }, + "node_modules/@vitest/expect": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", @@ -3705,6 +4282,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -3752,9 +4339,9 @@ } }, "node_modules/axe-core": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz", - "integrity": "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.13.0.tgz", + "integrity": "sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==", "dev": true, "license": "MPL-2.0", "engines": { @@ -3794,9 +4381,9 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", - "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", "dev": true, "license": "Apache-2.0", "peerDependencies": { @@ -3809,9 +4396,9 @@ } }, "node_modules/bare-fs": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.4.tgz", - "integrity": "sha512-y1kC+ffIx/tPLdTE693uNjHfzTfr+ravR5tvWlMXe25nELbkqV400S71qHDwbkAQ1FVEZobB1NFRzFbCCcyBCQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.1.tgz", + "integrity": "sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3822,7 +4409,7 @@ "fast-fifo": "^1.3.2" }, "engines": { - "bare": ">=1.16.0" + "bare": ">=1.28.0" }, "peerDependencies": { "bare-buffer": "*" @@ -3834,16 +4421,16 @@ } }, "node_modules/bare-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.1.tgz", - "integrity": "sha512-JprUlveX3QjApC1cTpsUOiscADftCGVWkzitbHsRqv84hzYwYHw2mbluddsq5TvI8mH/8Ov1f4BiMAdcB0oYnQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", + "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", "dev": true, "license": "Apache-2.0" }, "node_modules/bare-stream": { - "version": "2.13.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", - "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", + "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3869,9 +4456,9 @@ } }, "node_modules/bare-url": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.6.tgz", - "integrity": "sha512-iQxPClE07hETVpbRoX7JXX3v/ZQViCxe/SYCxylRLzdEx1xJAufPptfiOqR8tqiCtmbtMDANKWszzjLu1PMAZQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", + "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3879,9 +4466,9 @@ } }, "node_modules/baseline-browser-mapping": { - "version": "2.11.9", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.9.tgz", - "integrity": "sha512-cp447VUsGS07+n1Dqf7YSQ8maeJrjEhaDxTm1ZefbqDtypHBC5GzGMQbklR6IPR13Y8OAJRHZWEMtZipJLCttg==", + "version": "2.11.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz", + "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -3934,9 +4521,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", - "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "version": "4.28.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz", + "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==", "dev": true, "funding": [ { @@ -3955,11 +4542,11 @@ "license": "MIT", "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.10.44", - "caniuse-lite": "^1.0.30001806", - "electron-to-chromium": "^1.5.393", - "node-releases": "^2.0.51", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.20", + "caniuse-lite": "^1.0.30001810", + "electron-to-chromium": "^1.5.420", + "node-releases": "^2.0.54", + "update-browserslist-db": "^1.3.2" }, "bin": { "browserslist": "cli.js" @@ -4012,6 +4599,16 @@ } } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -4073,9 +4670,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001806", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", - "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "funding": [ { "type": "opencollective", @@ -4092,6 +4689,23 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4109,6 +4723,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -4241,6 +4865,19 @@ "node": "^14.18.0 || >=16.10.0" } }, + "node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4359,6 +4996,16 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4515,9 +5162,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.399", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.399.tgz", - "integrity": "sha512-lEcqhErbHjXRvd41rnWLpzbyU/IXfIYo7QwaFWmxGeLiLyY2TBCdHnWY88vB+p3ubnihRypDm66panXl7TylLA==", + "version": "1.5.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz", + "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==", "dev": true, "license": "ISC" }, @@ -4697,6 +5344,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", @@ -4761,9 +5415,9 @@ } }, "node_modules/esbuild": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4774,32 +5428,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.1", - "@esbuild/android-arm": "0.28.1", - "@esbuild/android-arm64": "0.28.1", - "@esbuild/android-x64": "0.28.1", - "@esbuild/darwin-arm64": "0.28.1", - "@esbuild/darwin-x64": "0.28.1", - "@esbuild/freebsd-arm64": "0.28.1", - "@esbuild/freebsd-x64": "0.28.1", - "@esbuild/linux-arm": "0.28.1", - "@esbuild/linux-arm64": "0.28.1", - "@esbuild/linux-ia32": "0.28.1", - "@esbuild/linux-loong64": "0.28.1", - "@esbuild/linux-mips64el": "0.28.1", - "@esbuild/linux-ppc64": "0.28.1", - "@esbuild/linux-riscv64": "0.28.1", - "@esbuild/linux-s390x": "0.28.1", - "@esbuild/linux-x64": "0.28.1", - "@esbuild/netbsd-arm64": "0.28.1", - "@esbuild/netbsd-x64": "0.28.1", - "@esbuild/openbsd-arm64": "0.28.1", - "@esbuild/openbsd-x64": "0.28.1", - "@esbuild/openharmony-arm64": "0.28.1", - "@esbuild/sunos-x64": "0.28.1", - "@esbuild/win32-arm64": "0.28.1", - "@esbuild/win32-ia32": "0.28.1", - "@esbuild/win32-x64": "0.28.1" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/escalade": { @@ -4851,6 +5505,7 @@ "version": "9.39.5", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "peer": true, @@ -5039,7 +5694,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -5259,6 +5913,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -5279,6 +5943,16 @@ "bare-events": "^2.7.0" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/exsolve": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", @@ -5393,9 +6067,9 @@ "license": "Unlicense" }, "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", "dev": true, "license": "ISC", "dependencies": { @@ -5655,9 +6329,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", - "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", "dev": true, "license": "MIT", "dependencies": { @@ -5902,9 +6576,9 @@ } }, "node_modules/icu-minify": { - "version": "4.13.4", - "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.13.4.tgz", - "integrity": "sha512-yK6HyPLGlQjqm8fTKtnBpM77z7vl7JdDBN2EXLvmgAu/b7XaOHWZb73M3ISl9ahBTehBv7RYeqqWSHfk1v2YcA==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.14.2.tgz", + "integrity": "sha512-2eJ9ooR8xXWbe0IivG58TFL96AUp5ts8FJ4qSMzn8YRJXpLmtPNWcUkIZ8t3iz0+eaVzG+mN+rdNIigHDvYa2Q==", "funding": [ { "type": "individual", @@ -5969,19 +6643,19 @@ } }, "node_modules/intl-messageformat": { - "version": "11.2.13", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.13.tgz", - "integrity": "sha512-JaPaE6TIX+TAS5XLhDUh41geLw4QfBHX4s5pW8Km+L9fVC8HzB9yOuhbh4EMR/F1+8C6b9qk4763Cv+LdOG1kg==", + "version": "11.2.14", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.14.tgz", + "integrity": "sha512-9f2VD1HFuxUvMw0RxsaP8WmMns6JRTnsNB/zghTFrp11ZktiXWwVDeZBPQchBKEmo+Gx/ZhxI7Qht7YglFD4PA==", "license": "BSD-3-Clause", "dependencies": { "@formatjs/fast-memoize": "3.1.7", - "@formatjs/icu-messageformat-parser": "3.5.16" + "@formatjs/icu-messageformat-parser": "3.5.17" } }, "node_modules/ip-address": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", - "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", + "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", "dev": true, "license": "MIT", "engines": { @@ -6469,9 +7143,9 @@ } }, "node_modules/jose": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.6.tgz", - "integrity": "sha512-HwMtbJjMw8rC8dUTwCNilHJD+fxTeKM3JV1eprSmTjS41qwXSSt6exJXgyPK1QOu0jB9eDYLESRDkB3qaT3jnw==", + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -6485,9 +7159,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", - "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "funding": [ { @@ -7008,6 +7682,13 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7019,9 +7700,9 @@ } }, "node_modules/lucide-react": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.28.0.tgz", - "integrity": "sha512-fARAFJULsGuDDydjp6+6blekG/sBIM29TerzLjc9bQUKAcEfrSc4ZQKb25KRz4OMKd87cZTb5dgq0w/T6KufVg==", + "version": "1.41.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.41.0.tgz", + "integrity": "sha512-6lksP35l6KszDKUeRTi4LV7i6DEe0Yzl2ALJm9j4c5xEYN91GdW1xGsawGMOg2mgjF5GHBVX8pKX9kP+cWsP3Q==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -7108,9 +7789,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", - "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "funding": [ { "type": "github", @@ -7149,12 +7830,19 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/netmask": { @@ -7249,9 +7937,9 @@ } }, "node_modules/next-intl": { - "version": "4.13.4", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.13.4.tgz", - "integrity": "sha512-jhPAT0u0lahIK6E4gVdZAehugWCosBhLG8sV7xMzgSVoJpxHObP+Fiu+z2FfkEW0XPPtr7uEXoUlLEfhxhNMTg==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.14.2.tgz", + "integrity": "sha512-rIq8me1uOa2qXnttJ6dUGYnHHL63Rh1FAIIDTJP5av5KSBmUzNBS17QAi07QDrUMY6KxFNeoCTRzSZRPUc97lg==", "funding": [ { "type": "individual", @@ -7260,40 +7948,37 @@ ], "license": "MIT", "dependencies": { + "@eloqnt/config": "^0.1.0", + "@eloqnt/format-json": "^0.1.0", + "@eloqnt/format-po": "^0.1.0", "@formatjs/intl-localematcher": "^0.8.1", "@parcel/watcher": "^2.4.1", - "@swc/core": "^1.15.2", - "icu-minify": "^4.13.4", + "@swc/core": "~1.16.0", + "icu-minify": "^4.14.2", "negotiator": "^1.0.0", - "next-intl-swc-plugin-extractor": "^4.13.4", - "po-parser": "^2.1.1", - "use-intl": "^4.13.4" + "next-intl-swc-plugin-extractor": "4.14.2", + "use-intl": "^4.14.2" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } } }, "node_modules/next-intl-swc-plugin-extractor": { - "version": "4.13.4", - "resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.13.4.tgz", - "integrity": "sha512-uN1+NMUYbG6YkO3q+rjc2bvAPX9nQ23owemvHJAyW0pRbQjVDwvNhmrV5qaak0oQc/9okbK17KLT49AoMGhVEQ==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.14.2.tgz", + "integrity": "sha512-R+i9Xw6XKyn1QVx+I5IDZQNGQBwT79ugGwzbuJudgejob/IYnXXrRKMlLhmMxLdKH8jUWh92nqwNnp+5rtSVJA==", "license": "MIT" }, "node_modules/next-intl/node_modules/@swc/core": { - "version": "1.15.47", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.47.tgz", - "integrity": "sha512-FbsO5JcfOjfH38W/rohBRBweJeERsAuIP4f377lmkmxTcq9exjtx4SkRuZY5CdfhR2CBVwDIJegBpJDffwNsOg==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.16.2.tgz", + "integrity": "sha512-95I4kiSMeveI/Mhi+tE4fiWcWLUMfzfKrk0jtr8LRMqHgOgq+xHS+zExkDqoO4b5OeeuXHMWVdD5MeP3X6sULw==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.27" + "@swc/types": "^0.1.28" }, "engines": { "node": ">=10" @@ -7303,18 +7988,18 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.47", - "@swc/core-darwin-x64": "1.15.47", - "@swc/core-linux-arm-gnueabihf": "1.15.47", - "@swc/core-linux-arm64-gnu": "1.15.47", - "@swc/core-linux-arm64-musl": "1.15.47", - "@swc/core-linux-ppc64-gnu": "1.15.47", - "@swc/core-linux-s390x-gnu": "1.15.47", - "@swc/core-linux-x64-gnu": "1.15.47", - "@swc/core-linux-x64-musl": "1.15.47", - "@swc/core-win32-arm64-msvc": "1.15.47", - "@swc/core-win32-ia32-msvc": "1.15.47", - "@swc/core-win32-x64-msvc": "1.15.47" + "@swc/core-darwin-arm64": "1.16.2", + "@swc/core-darwin-x64": "1.16.2", + "@swc/core-linux-arm-gnueabihf": "1.16.2", + "@swc/core-linux-arm64-gnu": "1.16.2", + "@swc/core-linux-arm64-musl": "1.16.2", + "@swc/core-linux-ppc64-gnu": "1.16.2", + "@swc/core-linux-s390x-gnu": "1.16.2", + "@swc/core-linux-x64-gnu": "1.16.2", + "@swc/core-linux-x64-musl": "1.16.2", + "@swc/core-win32-arm64-msvc": "1.16.2", + "@swc/core-win32-ia32-msvc": "1.16.2", + "@swc/core-win32-x64-msvc": "1.16.2" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -7325,6 +8010,17 @@ } } }, + "node_modules/next-intl/node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -7385,9 +8081,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.51", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", - "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", "dev": true, "license": "MIT", "engines": { @@ -7418,9 +8114,9 @@ "license": "MIT" }, "node_modules/oauth4webapi": { - "version": "3.8.6", - "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.6.tgz", - "integrity": "sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==", + "version": "3.8.8", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.8.tgz", + "integrity": "sha512-8N28E+a/oxfXWBgOMt+ZP/JUf/XR+IFbvkAEPP3gznXOMv9BpAAwiIj0TFNz3tGTPc0ZQ8zmWBNgN1nAys0gng==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" @@ -7550,9 +8246,9 @@ } }, "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", "license": "MIT" }, "node_modules/once": { @@ -7714,6 +8410,16 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -7747,20 +8453,26 @@ } }, "node_modules/pkg-types": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", - "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.2.tgz", + "integrity": "sha512-v0sVXzj7oPGysr543YYZLYbcJNJsKikSsp/fFzoxQ12ewY3ZZr7oCPC8y7OlmxfYB3QPvriXmuPD8KZggE1vqg==", "license": "MIT", "dependencies": { - "confbox": "^0.2.4", - "exsolve": "^1.0.8", + "confbox": "^0.3.0", + "exsolve": "^1.1.1", "pathe": "^2.0.3" } }, + "node_modules/pkg-types/node_modules/confbox": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.3.1.tgz", + "integrity": "sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==", + "license": "MIT" + }, "node_modules/po-parser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.1.1.tgz", - "integrity": "sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.2.0.tgz", + "integrity": "sha512-NdTrKgh0oO7+y+RFX8KKhOB438x/j94UGXEFzl/7pHH0JjWSn42iJ9tgmPksIO6n1JKDHmNDcejPJfKspljB9g==", "license": "MIT" }, "node_modules/possible-typed-array-names": { @@ -7780,9 +8492,9 @@ "license": "MIT-0" }, "node_modules/postcss": { - "version": "8.5.25", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", - "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { @@ -7800,7 +8512,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.16", + "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8108,9 +8820,9 @@ } }, "node_modules/resend": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/resend/-/resend-6.18.1.tgz", - "integrity": "sha512-XN8XIaDdKF+ziSQ3K23ndUcyhP7U3ze2gky6SPgYkuAOq54mH4Wdhwm7QylEQ3zlz0NzdX7/l1AgmJUZbdPI/Q==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/resend/-/resend-6.26.0.tgz", + "integrity": "sha512-vkrULozV5nGF8o7tyun/t0+qFgtPpn+cyRhVqeorhOsPUNDiSc/9p/pn8AprdHqJpYaTvftpnQgWurW++FkCdw==", "license": "MIT", "dependencies": { "postal-mime": "2.7.5", @@ -8183,6 +8895,52 @@ "node": ">=0.10.0" } }, + "node_modules/rollup": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", + "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.1", + "@rollup/rollup-android-arm64": "4.63.1", + "@rollup/rollup-darwin-arm64": "4.63.1", + "@rollup/rollup-darwin-x64": "4.63.1", + "@rollup/rollup-freebsd-arm64": "4.63.1", + "@rollup/rollup-freebsd-x64": "4.63.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", + "@rollup/rollup-linux-arm-musleabihf": "4.63.1", + "@rollup/rollup-linux-arm64-gnu": "4.63.1", + "@rollup/rollup-linux-arm64-musl": "4.63.1", + "@rollup/rollup-linux-loong64-gnu": "4.63.1", + "@rollup/rollup-linux-loong64-musl": "4.63.1", + "@rollup/rollup-linux-ppc64-gnu": "4.63.1", + "@rollup/rollup-linux-ppc64-musl": "4.63.1", + "@rollup/rollup-linux-riscv64-gnu": "4.63.1", + "@rollup/rollup-linux-riscv64-musl": "4.63.1", + "@rollup/rollup-linux-s390x-gnu": "4.63.1", + "@rollup/rollup-linux-x64-gnu": "4.63.1", + "@rollup/rollup-linux-x64-musl": "4.63.1", + "@rollup/rollup-openbsd-x64": "4.63.1", + "@rollup/rollup-openharmony-arm64": "4.63.1", + "@rollup/rollup-win32-arm64-msvc": "4.63.1", + "@rollup/rollup-win32-ia32-msvc": "4.63.1", + "@rollup/rollup-win32-x64-gnu": "4.63.1", + "@rollup/rollup-win32-x64-msvc": "4.63.1", + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8504,6 +9262,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -8516,9 +9281,9 @@ } }, "node_modules/socks": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", - "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.10.tgz", + "integrity": "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8572,6 +9337,13 @@ "dev": true, "license": "MIT" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/standardwebhooks": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", @@ -8582,6 +9354,13 @@ "fast-sha256": "^1.3.0" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -8597,9 +9376,9 @@ } }, "node_modules/streamx": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", - "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", "dev": true, "license": "MIT", "dependencies": { @@ -8646,25 +9425,25 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.1.0.tgz", + "integrity": "sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", + "es-abstract": "^1.24.2", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", + "es-object-atoms": "^1.1.2", + "get-intrinsic": "^1.3.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" + "side-channel": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -8780,6 +9559,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -8876,9 +9675,9 @@ } }, "node_modules/tar-stream": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", - "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8908,10 +9707,17 @@ "b4a": "^1.6.4" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "license": "MIT", "engines": { "node": ">=18" @@ -8953,9 +9759,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "peer": true, @@ -8966,6 +9772,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.6.tgz", + "integrity": "sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -9025,11 +9861,12 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.23.11", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.11.tgz", - "integrity": "sha512-Ry2oTEUnhBdeEdWIztY8kf3/nBGnPnjMLVGL0YfdRXMORuPER5NlKmayqxtxRxwB1xBN+RivRaJfe7PM1rtiyw==", + "version": "4.23.13", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.13.tgz", + "integrity": "sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.28.0" }, @@ -9157,16 +9994,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.65.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", - "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.69.0.tgz", + "integrity": "sha512-B3MltX0VqjUBNEe3b3sSuiRbfa6XrfHFtBiPamjT5AsW/dfq+y+bc0wyuS9DxAS1LyzCxRp2+rxzpLUvqM2BvA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.65.0", - "@typescript-eslint/parser": "8.65.0", - "@typescript-eslint/typescript-estree": "8.65.0", - "@typescript-eslint/utils": "8.65.0" + "@typescript-eslint/eslint-plugin": "8.69.0", + "@typescript-eslint/parser": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0", + "@typescript-eslint/utils": "8.69.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9245,9 +10082,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "dev": true, "funding": [ { @@ -9286,9 +10123,9 @@ } }, "node_modules/use-intl": { - "version": "4.13.4", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.13.4.tgz", - "integrity": "sha512-wRhU5zyPNgu845++EJ8ckQsi89b22QUop7NlGxNXpsnKSwEJr7WErAkdAYeVQgFTmDWsa8e2NI1e14XbWz9Ecw==", + "version": "4.14.2", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.14.2.tgz", + "integrity": "sha512-XffvbD4q4uZjRbuO8CgK6Ei8hvYn7aegoSad2YPho1ZRjYEs+k4erhYq0vfm5ttF4lnja3riXCeJrtUdNdQG3g==", "funding": [ { "type": "individual", @@ -9299,13 +10136,236 @@ "dependencies": { "@formatjs/fast-memoize": "^3.1.0", "@schummar/icu-type-parser": "1.21.5", - "icu-minify": "^4.13.4", + "icu-minify": "^4.14.2", "intl-messageformat": "^11.1.0" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" } }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/webdriver-bidi-protocol": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", @@ -9418,6 +10478,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -9454,9 +10531,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.21.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", - "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "dev": true, "license": "MIT", "engines": { @@ -9546,9 +10623,9 @@ } }, "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz", + "integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==", "license": "MIT", "peer": true, "funding": { diff --git a/package.json b/package.json index 456da6b34..bc0122fb6 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "takoapi-temp", + "name": "takoapi", "version": "0.1.0", "private": true, "scripts": { @@ -7,7 +7,15 @@ "build": "next build", "start": "next start", "lint": "eslint", - "check:i18n": "node scripts/check-i18n.mjs" + "typecheck": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest", + "check:i18n": "node scripts/check-i18n.mjs", + "check:secrets": "node scripts/check-secrets.mjs", + "check:skill-assets": "node scripts/build-skill-assets.mjs --check", + "build:skill-assets": "node scripts/build-skill-assets.mjs", + "db:push": "prisma db push", + "db:seed": "tsx prisma/seed.ts" }, "dependencies": { "@auth/prisma-adapter": "^2.11.1", @@ -39,6 +47,11 @@ "puppeteer-core": "^24.40.0", "tailwindcss": "^4", "tsx": "^4.21.0", - "typescript": "^5" + "typescript": "^5", + "vitest": "^3.2.7" + }, + "engines": { + "node": ">=20 <25", + "npm": ">=10" } } diff --git a/prisma/migrations/010_requestlog_ip_hash/migration.sql b/prisma/migrations/010_requestlog_ip_hash/migration.sql new file mode 100644 index 000000000..d351e4fd2 --- /dev/null +++ b/prisma/migrations/010_requestlog_ip_hash/migration.sql @@ -0,0 +1,34 @@ +-- Replace RequestLog's plaintext client IP with a salted, day-rotating hash. +-- +-- DESTRUCTIVE, DELIBERATELY: the "ip" column is DROPPED rather than renamed. The +-- addresses already stored there are plaintext personal data with no retention +-- policy behind them, and they are not even reliable — they come from the first +-- element of a caller-supplied X-Forwarded-For (see extractClientIp), so they are +-- a privacy liability that is not usable as evidence either way. Keeping them +-- under a new name would preserve exactly the problem this change exists to +-- remove, and would leave "ipHash" holding values that are not hashes. +-- +-- Nothing reads the column: `grep -rn '\.ip\b' src/` finds no reader, only the +-- single write in src/lib/requestLog.ts. Dropping it is code-safe. +-- +-- If those rows are wanted for a one-off investigation, snapshot them BEFORE +-- applying this migration — afterwards they are gone. +-- +-- HOW TO APPLY IN PRODUCTION: `npx prisma db execute --url "$PROD_URL" --file +-- prisma/migrations/010_requestlog_ip_hash/migration.sql`, per +-- docs/agent-marketplace/HANDOFF.md §5. Do NOT run `prisma migrate deploy` +-- against production: the `0_init` baseline directory sorts *after* every +-- `0NN_`/`0NNN_` directory ('1' < '_'), so deploy would replay the whole history +-- and finish by trying to re-run the baseline, and HANDOFF.md §6 records that the +-- production `_prisma_migrations` table is not a reliable record of what is +-- already applied. Both statements below are idempotent, so a re-run is safe. +-- +-- ORDERING vs the code deploy: `logRequest` catches its own write failure and +-- never throws, so either order only costs RequestLog rows, never a request (the +-- failures do show up as `requestLog failed:` lines). Applying +-- this before the new revision means the old code's `ip` write fails until the +-- deploy lands; applying it after means the new code's `ipHash` write fails until +-- the migration lands. Keep the gap short. + +ALTER TABLE "RequestLog" DROP COLUMN IF EXISTS "ip"; +ALTER TABLE "RequestLog" ADD COLUMN IF NOT EXISTS "ipHash" TEXT; diff --git a/prisma/migrations/011_agent_health_history/migration.sql b/prisma/migrations/011_agent_health_history/migration.sql new file mode 100644 index 000000000..13b2e02bf --- /dev/null +++ b/prisma/migrations/011_agent_health_history/migration.sql @@ -0,0 +1,33 @@ +-- Health-transition history for agents. +-- +-- Agent.healthStatus / healthCheckedAt are overwritten in place by the hourly cron, +-- so the only answerable question was "what is it right now". This table records a +-- row each time the probed status *differs* from the stored one, which makes "when +-- did it go down", "how long was it down", and "what was its uptime last week" +-- answerable, while keeping the table proportional to flips rather than to +-- (agent count x hours). +-- +-- HOW TO APPLY IN PRODUCTION: `npx prisma db execute --url "$PROD_URL" --file +-- prisma/migrations/011_agent_health_history/migration.sql`, per +-- docs/agent-marketplace/HANDOFF.md §5. Do NOT run `prisma migrate deploy` +-- against production — see the same note at the top of +-- prisma/migrations/010_requestlog_ip_hash/migration.sql. Every statement below +-- is idempotent, so a re-run is safe. +-- +-- ORDERING vs the code deploy: apply this BEFORE deploying the new revision. +-- runHealthChecks() inserts into this table inside the same transaction as the +-- Agent update, so if the new code runs first, every status flip in that window +-- fails the whole transaction and is counted as a writeError — the agent's +-- healthStatus is not updated either. + +CREATE TABLE IF NOT EXISTS "AgentHealthCheck" ( + "id" TEXT PRIMARY KEY, + "agentId" TEXT NOT NULL, + "status" TEXT NOT NULL, + "previousStatus" TEXT, + "checkedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "AgentHealthCheck_agentId_fkey" FOREIGN KEY ("agentId") REFERENCES "Agent"("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX IF NOT EXISTS "AgentHealthCheck_agentId_checkedAt_idx" ON "AgentHealthCheck"("agentId", "checkedAt"); +CREATE INDEX IF NOT EXISTS "AgentHealthCheck_checkedAt_idx" ON "AgentHealthCheck"("checkedAt"); diff --git a/prisma/migrations/012_installcmd_cleanup/migration.sql b/prisma/migrations/012_installcmd_cleanup/migration.sql new file mode 100644 index 000000000..2d63a14f3 --- /dev/null +++ b/prisma/migrations/012_installcmd_cleanup/migration.sql @@ -0,0 +1,20 @@ +-- Remove fabricated install commands. +-- +-- Every skill row was written with installCmd = 'clawhub install ', +-- including skills scraped from GitHub or submitted with only a GitHub URL — +-- packages that do not exist on ClawHub, so the command fails for anyone who +-- copies it from the detail page. Only skills with a clawskills.sh listing have a +-- real install command; keep those, null the rest. The detail page now shows +-- GitHub install guidance when installCmd is null. +-- +-- HOW TO APPLY IN PRODUCTION: `npx prisma db execute --url "$PROD_URL" --file +-- prisma/migrations/012_installcmd_cleanup/migration.sql` (see +-- docs/agent-marketplace/HANDOFF.md §5; do NOT run `prisma migrate deploy`). +-- Idempotent — safe to re-run. Pure data change, no schema change, so it can be +-- applied before or after the code deploy. + +UPDATE "Skill" +SET "installCmd" = NULL +WHERE "installCmd" LIKE 'clawhub install %' + AND "clawSkillsUrl" IS NULL + AND "clawHubUrl" IS NULL; diff --git a/prisma/migrations/013_password_reset/migration.sql b/prisma/migrations/013_password_reset/migration.sql new file mode 100644 index 000000000..a75f089ad --- /dev/null +++ b/prisma/migrations/013_password_reset/migration.sql @@ -0,0 +1,33 @@ +-- Storage for password-reset and email-verification tokens. +-- +-- No new table: both flows in src/lib/password-reset.ts reuse "VerificationToken", +-- which has existed since 0_init with nothing reading or writing it. Rows hold a +-- SHA-256 digest of the token, never the token itself, and "identifier" carries the +-- kind plus the subject ("reset:" / "verify:"). +-- +-- What was missing is the index the expiry sweep needs. Nothing has ever deleted +-- from this table and there is no cron for it, so password-reset.ts purges expired +-- rows on every request; without an index that DELETE seq-scans the whole table on +-- each reset. +-- +-- NO INDEX ON "identifier" IS ADDED, deliberately. The existing +-- "VerificationToken_identifier_token_key" unique index leads with "identifier", so +-- the by-user lookups and deletes in password-reset.ts already use it — a standalone +-- copy would add write cost on every token issued and buy nothing. +-- +-- DRIFT NOTE: prisma/schema.prisma has no matching `@@index([expires])` on +-- VerificationToken (that file is outside this change), so `prisma migrate diff` +-- reports this index as drift until the line is added there. +-- +-- HOW TO APPLY IN PRODUCTION: `npx prisma db execute --url "$PROD_URL" --file +-- prisma/migrations/013_password_reset/migration.sql`, per +-- docs/agent-marketplace/HANDOFF.md §5. Do NOT run `prisma migrate deploy` against +-- production — see the note at the top of +-- prisma/migrations/010_requestlog_ip_hash/migration.sql. The statement below is +-- idempotent, so a re-run is safe. +-- +-- ORDERING vs the code deploy: either order is safe. This is purely additive and no +-- shipped revision touches the table; applying it after the deploy only means the +-- first reset requests pay a seq scan over an almost-empty table. + +CREATE INDEX IF NOT EXISTS "VerificationToken_expires_idx" ON "VerificationToken"("expires"); diff --git a/prisma/migrations/014_search_indexes/migration.sql b/prisma/migrations/014_search_indexes/migration.sql new file mode 100644 index 000000000..23bbc09a6 --- /dev/null +++ b/prisma/migrations/014_search_indexes/migration.sql @@ -0,0 +1,78 @@ +-- Make catalogue search use an index instead of scanning the table. +-- +-- Every search path in the app — /api/skills/search, /api/skills, /api/agents, +-- /api/registry, /api/agent and the two marketplace pages — filters with +-- `contains` + `mode: "insensitive"`, which Prisma emits as `ILIKE '%q%'`. A +-- leading wildcard makes the existing btree indexes on Skill(name) and +-- Agent(slug) useless, so each of those queries is a sequential scan: ~5.4k +-- Skill rows and ~1.6k Agent rows today, on a db-f1-micro with one shared vCPU, +-- for every keystroke-driven search and every MCP `search_agents` call. +-- +-- Trigram GIN indexes are the fix that needs no query rewrite: pg_trgm can serve +-- `ILIKE '%…%'` directly, so the application code stays exactly as it is and the +-- planner starts using an index. (A tsvector column would rank better but would +-- require rewriting every call site in raw SQL and keeping the column in sync — +-- worth doing later, not needed to stop the scans.) +-- +-- Also adds a GIN index on Agent.scenarios: migration 007 deliberately skipped it +-- ("the catalog is small and `has` scans are cheap"), which was true at ~500 +-- agents. It is now the filter behind every scenario tile on the home page, the +-- /scenarios index and every /agents?scenario= view, over 1.6k rows. +-- +-- HOW TO APPLY IN PRODUCTION: `npx prisma db execute --url "$PROD_URL" --file +-- prisma/migrations/014_search_indexes/migration.sql`, per +-- docs/agent-marketplace/HANDOFF.md §5. Do NOT run `prisma migrate deploy` — see +-- the note in prisma/migrations/010_requestlog_ip_hash/migration.sql. +-- +-- Safe to re-run. CREATE INDEX (without CONCURRENTLY) takes a brief ACCESS +-- EXCLUSIVE lock on each table; at this row count that is well under a second. +-- Prisma's `db execute` runs statements in one implicit transaction, which is why +-- CONCURRENTLY is not used here — it cannot run inside a transaction block. +-- +-- These indexes are intentionally absent from schema.prisma: Prisma has no +-- representation for a GIN/gin_trgm_ops index, and introspecting them back would +-- produce an unrepresentable-index warning. They are managed here. + +CREATE EXTENSION IF NOT EXISTS pg_trgm; + +-- Skill: name and description are both searched, always together in an OR. +CREATE INDEX IF NOT EXISTS "Skill_name_trgm_idx" + ON "Skill" USING gin ("name" gin_trgm_ops); +CREATE INDEX IF NOT EXISTS "Skill_description_trgm_idx" + ON "Skill" USING gin ("description" gin_trgm_ops); +-- `author` is searched by /api/skills/search only. +CREATE INDEX IF NOT EXISTS "Skill_author_trgm_idx" + ON "Skill" USING gin ("author" gin_trgm_ops); + +-- Agent: same pair, hit by the marketplace, the registry and the MCP tool. +CREATE INDEX IF NOT EXISTS "Agent_name_trgm_idx" + ON "Agent" USING gin ("name" gin_trgm_ops); +CREATE INDEX IF NOT EXISTS "Agent_description_trgm_idx" + ON "Agent" USING gin ("description" gin_trgm_ops); + +-- Array containment for `scenarios: { has: … }`. +CREATE INDEX IF NOT EXISTS "Agent_scenarios_gin_idx" + ON "Agent" USING gin ("scenarios"); + +-- Sorting the public catalogue: every listing filters on status and then orders by +-- one of these, and the existing composite indexes do not cover the combination. +CREATE INDEX IF NOT EXISTS "Agent_status_kind_stars_idx" + ON "Agent" ("status", "kind", "stars" DESC NULLS LAST); +CREATE INDEX IF NOT EXISTS "Agent_status_kind_callsCount_idx" + ON "Agent" ("status", "kind", "callsCount" DESC); +CREATE INDEX IF NOT EXISTS "Skill_status_createdAt_idx" + ON "Skill" ("status", "createdAt" DESC); +CREATE INDEX IF NOT EXISTS "Skill_status_likesCount_idx" + ON "Skill" ("status", "likesCount" DESC); + +-- The submissions list on /profile and /api/user/skills. +CREATE INDEX IF NOT EXISTS "Skill_submitterId_createdAt_idx" + ON "Skill" ("submitterId", "createdAt" DESC); + +-- SkillEvent is now written on every deduplicated page view; the dedupe probe +-- looks up (skillId, type, referrer) and the sweep deletes by (type, createdAt). +CREATE INDEX IF NOT EXISTS "SkillEvent_skillId_type_referrer_idx" + ON "SkillEvent" ("skillId", "type", "referrer"); +-- …and the retention sweep deletes by (type, createdAt). +CREATE INDEX IF NOT EXISTS "SkillEvent_type_createdAt_idx" + ON "SkillEvent" ("type", "createdAt"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8ee23a028..59e928cc1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -125,6 +125,13 @@ model Skill { updatedAt DateTime @updatedAt brief String? clawSkillsUrl String? + /// THIRD-PARTY COUNT, NOT OURS. Scraped from the skill's clawskills.sh page + /// (scripts/scrape-clawskills.ts, and overwritten nightly by scripts/daily-sync.ts + /// via the takoapi-daily-sync Scheduler job). Nothing in this codebase ever + /// increments it: neither public/install.sh, packages/takoapi-install nor + /// takoapi_skill/ report an install back, so this says nothing about downloads + /// through TakoAPI. Values are also rounded at the source ("1.2k" -> 1200). + /// Do not sum it and present the total as a site metric. downloads Int @default(0) readme String? stars Int @default(0) @@ -190,7 +197,9 @@ model RequestLog { status Int durationMs Int userId String? - ip String? + /// Salted, day-rotating SHA-256 of the client IP — never the address itself. + /// See hashClientIp() in src/lib/requestLog.ts. Null when TAKO_IP_SALT is unset. + ipHash String? userAgent String? errorCode String? createdAt DateTime @default(now()) @@ -428,6 +437,7 @@ model Agent { cardSignatureVerified Boolean @default(false) namespaceVerified Boolean @default(false) cardFetchedAt DateTime? + /// Latest probe result only. History of changes lives in AgentHealthCheck. healthStatus String? healthCheckedAt DateTime? @@ -453,13 +463,14 @@ model Agent { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - publisher User @relation(fields: [publisherId], references: [id]) - category Category? @relation(fields: [categoryId], references: [id]) - skills AgentSkillDef[] - tags AgentTag[] - invocations Invocation[] - ratings AgentRating[] - bookmarks AgentBookmark[] + publisher User @relation(fields: [publisherId], references: [id]) + category Category? @relation(fields: [categoryId], references: [id]) + skills AgentSkillDef[] + tags AgentTag[] + invocations Invocation[] + ratings AgentRating[] + bookmarks AgentBookmark[] + healthChecks AgentHealthCheck[] @@index([status, categoryId]) @@index([featured, callsCount(sort: Desc)]) @@ -470,6 +481,30 @@ model Agent { @@index([kind, stars(sort: Desc)]) } +/// Health *transitions* for an agent — one row each time a probe returns a +/// different status than the one on record, not one row per probe. `Agent.healthStatus` +/// is overwritten in place every hour, so on its own it can say "this agent is down +/// right now" but never "since when", "for how long", or "what was last week's uptime". +/// +/// Recording only changes keeps this table proportional to how often agents flip +/// rather than to (agents x hours) — the hourly cron would otherwise add a row per +/// agent per hour forever, on a 10 GB shared-core instance. It also makes the +/// question that prompted this table a single lookup: when an agent went down is the +/// `checkedAt` of its most recent row with status='down'. +model AgentHealthCheck { + id String @id @default(cuid()) + agentId String + /// ok | degraded | down + status String + /// Status this replaced; null for the first observation of an agent. + previousStatus String? + checkedAt DateTime @default(now()) + agent Agent @relation(fields: [agentId], references: [id], onDelete: Cascade) + + @@index([agentId, checkedAt]) + @@index([checkedAt]) +} + model AgentSkillDef { id String @id @default(cuid()) agentId String @@ -537,8 +572,12 @@ model ApiKey { name String? hashedKey String @unique prefix String + /// NOT ENFORCED YET — no code reads this; every key can reach every route. scopes String[] @default([]) + /// Per-key gateway allowance, requests per 60s window. Null → the shared + /// default (see gatewayRateLimit in src/lib/apikey.ts). rateLimit Int? + /// NOT ENFORCED YET — no code reads this; there is no monthly cap. monthlyQuota Int? lastUsedAt DateTime? revokedAt DateTime? diff --git a/prisma/seed.ts b/prisma/seed.ts index 023eb905c..c38efa52c 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -49,7 +49,7 @@ async function main() { } // Use the clawskills slug as our slug - let slug = skill.slug || slugify(skill.name); + const slug = skill.slug || slugify(skill.name); // Construct clawHub URL from author and skill name const clawHubUrl = skill.clawHubUrl || skill.clawSkillsUrl || null; diff --git a/scripts/build-skill-assets.mjs b/scripts/build-skill-assets.mjs new file mode 100644 index 000000000..6fc80d2d0 --- /dev/null +++ b/scripts/build-skill-assets.mjs @@ -0,0 +1,93 @@ +#!/usr/bin/env node +// Single source of truth for the TakoAPI skill body. +// +// The same instructions ship in five places — the Claude Code plugin, the npx +// installer's three assets, and the heredoc inside public/install.sh — and they +// had already drifted from each other. This script regenerates all of them from +// the body in takoapi_skill/SKILL.md (everything after its frontmatter, up to the +// "Install this skill" divider that only the GitHub-facing copy carries). +// +// node scripts/build-skill-assets.mjs # rewrite the generated copies +// node scripts/build-skill-assets.mjs --check # exit 1 if any copy is stale (CI) +import { readFileSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const ROOT = resolve(new URL("..", import.meta.url).pathname); +const CHECK = process.argv.includes("--check"); + +const SOURCE = "takoapi_skill/SKILL.md"; +const src = readFileSync(resolve(ROOT, SOURCE), "utf8"); + +// Body = after the closing frontmatter fence, before the standalone "---" that +// precedes "## Install this skill into your coding agent". +const fm = src.match(/^---\n[\s\S]*?\n---\n/); +if (!fm) throw new Error(`${SOURCE}: frontmatter not found`); +let body = src.slice(fm[0].length); +const divider = body.search(/\n---\n\n## Install this skill/); +if (divider !== -1) body = body.slice(0, divider); +body = body.replace(/\s+$/, "") + "\n"; + +const SKILL_FM = `--- +name: takoapi +description: Discover and invoke AI agents through TakoAPI's unified gateway (takoapi.com) — one API key, any agent — and search the OpenClaw skills catalog. Use when the user wants to find, call, or publish an agent, or browse coding-agent skills. +--- +`; +const OPENCODE_AGENT_FM = `--- +description: Discover and invoke AI agents through TakoAPI's unified gateway (takoapi.com), and search the OpenClaw skills catalog. +mode: subagent +permission: + webfetch: allow + bash: allow +--- +`; +const OPENCODE_COMMAND_FM = `--- +description: Discover/invoke agents and search skills via TakoAPI (takoapi.com) +agent: takoapi +--- +`; + +const targets = [ + { file: "plugins/takoapi/skills/takoapi/SKILL.md", content: SKILL_FM + body }, + { file: "packages/takoapi-install/assets/skill.md", content: SKILL_FM + body }, + { file: "packages/takoapi-install/assets/opencode-agent.md", content: OPENCODE_AGENT_FM + body }, + { + file: "packages/takoapi-install/assets/opencode-command.md", + content: OPENCODE_COMMAND_FM + body + "\n---\nUser request: $ARGUMENTS\n", + }, + { + file: "public/install.sh", + content: (() => { + const sh = readFileSync(resolve(ROOT, "public/install.sh"), "utf8"); + const start = sh.indexOf(" cat <<'BODY'\n"); + const end = sh.indexOf("\nBODY\n", start); + if (start === -1 || end === -1) throw new Error("public/install.sh: BODY heredoc not found"); + return sh.slice(0, start) + " cat <<'BODY'\n" + body + sh.slice(end + 1); + })(), + }, +]; + +let stale = 0; +for (const t of targets) { + const path = resolve(ROOT, t.file); + let current = ""; + try { + current = readFileSync(path, "utf8"); + } catch { + /* missing → will be written / reported */ + } + if (current === t.content) continue; + stale++; + if (CHECK) { + console.error(`stale: ${t.file} (regenerate with: npm run build:skill-assets)`); + } else { + writeFileSync(path, t.content); + console.log(`wrote ${t.file}`); + } +} + +if (CHECK) { + if (stale) process.exit(1); + console.log(`skill assets: ${targets.length} generated copies match ${SOURCE} ✓`); +} else if (!stale) { + console.log("skill assets already up to date"); +} diff --git a/scripts/check-secrets.mjs b/scripts/check-secrets.mjs new file mode 100644 index 000000000..5162719bd --- /dev/null +++ b/scripts/check-secrets.mjs @@ -0,0 +1,73 @@ +#!/usr/bin/env node +// Fails when a tracked file contains something that looks like a live credential. +// A production database password once shipped in scripts/deploy-sync-job.sh; this +// is the cheap, always-on guard against a repeat. Runs in CI (npm run check:secrets) +// and can be run locally before committing. +// +// Deliberately narrow patterns — it looks for *values*, not for words like +// "password" in prose or variable names. Add a pattern when a new class of secret +// enters the stack; add a path to IGNORE only for files that cannot contain a value. +import { execSync } from "node:child_process"; +import { readFileSync } from "node:fs"; + +const PATTERNS = [ + // connection strings with an inline password (postgres://user:pass@host) + { name: "database URL with inline password", re: /\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis):\/\/[^\s:@/'"`]+:[^\s@/'"`]{4,}@/ }, + { name: "AWS access key id", re: /\bAKIA[0-9A-Z]{16}\b/ }, + { name: "GitHub token", re: /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{36,}\b/ }, + { name: "GitHub fine-grained token", re: /\bgithub_pat_[A-Za-z0-9_]{60,}\b/ }, + { name: "Slack token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/ }, + { name: "Stripe live key", re: /\b(?:sk|rk)_live_[A-Za-z0-9]{16,}\b/ }, + { name: "Google API key", re: /\bAIza[0-9A-Za-z_-]{35}\b/ }, + { name: "private key block", re: /-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/ }, + { name: "TakoAPI gateway key", re: /\btako_live_[A-Za-z0-9_-]{24,}\b/ }, + { name: "JWT", re: /\beyJ[A-Za-z0-9_-]{20,}\.eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\b/ }, +]; + +// Files that legitimately contain matching-looking strings (placeholders, examples). +const SELF = "scripts/check-secrets.mjs"; +// The scanner's own patterns look exactly like the values it hunts for. +const IGNORE = [new RegExp(`^${SELF}$`), /^package-lock\.json$/, /^scripts\/notify-ledger\.json$/, /^prisma\/seed-data\.json$/, /\.(png|jpg|jpeg|gif|svg|ico|woff2?)$/]; + +const files = execSync("git ls-files -z", { encoding: "utf8" }).split("\0").filter(Boolean); +const hits = []; +for (const file of files) { + if (IGNORE.some((re) => re.test(file))) continue; + let text; + try { + text = readFileSync(file, "utf8"); + } catch { + continue; // binary / unreadable + } + const lines = text.split("\n"); + lines.forEach((line, i) => { + for (const { name, re } of PATTERNS) { + if (re.test(line)) { + // Allow obvious placeholders so docs can show the *shape* of a value, and + // the local docker-compose defaults (a throwaway password on localhost). + if (/<[^>]*(redacted|password|secret|token|your-[a-z-]+)[^>]*>|\*{3,}|example\.com/i.test(line)) continue; + // Documentation placeholders: postgres://user:pass@host, USER:PASSWORD@… + if (/:\/\/(user|username|usuario|<[^>]+>|\$\{?\w+\}?)[^\s@]*:(pass(word)?|secret|<[^>]+>|\$\{?\w+\}?)[^\s@]*@/i.test(line)) continue; + // Local development targets — a throwaway password on a loopback host. + // NOT applied when the line also carries a unix-socket host override: Cloud + // SQL connection strings are written `…@localhost/db?host=/cloudsql/`, + // which is a *production* credential wearing a loopback hostname. That is + // the exact shape of the password this scanner exists to stop recurring, + // so exempting it would make the guard blind to its own founding incident. + if (/@(localhost|127\.0\.0\.1|db)(:\d+)?[\/?]/.test(line) && !/[?&]host=\//.test(line)) continue; + // A "secret" made of one repeated character is a shape, not a value — + // docs write tako_live_xxxxxxxx… and AKIA0000… to show the format. Real + // credentials are random, so this cannot mask one. + if (/(.)\1{7,}/.test(line.match(re)?.[0] ?? "")) continue; + hits.push(`${file}:${i + 1}: ${name}`); + } + } + }); +} + +if (hits.length) { + console.error("Possible credentials found in tracked files:\n" + hits.map((h) => ` ${h}`).join("\n")); + console.error("\nRemove the value (bind it from Secret Manager / env instead), then rotate it."); + process.exit(1); +} +console.log(`check-secrets: ${files.length} tracked files scanned, nothing found ✓`); diff --git a/scripts/daily-sync.ts b/scripts/daily-sync.ts index 99abcc3dc..a977a3f58 100644 --- a/scripts/daily-sync.ts +++ b/scripts/daily-sync.ts @@ -4,16 +4,62 @@ * 2. Deduplicate skills (keep highest downloads per skill name) * 3. Recount category skill counts * - * Run: DATABASE_URL=... npx tsx scripts/daily-sync.ts + * Run: DATABASE_URL=... npx tsx scripts/daily-sync.ts + * Dry run: DATABASE_URL=... npx tsx scripts/daily-sync.ts --dry + * + * WHY THIS SCRIPT SHOUTS + * ---------------------- + * This job ran green every night from 2026-07-04 to 2026-09-05 and imported + * nothing. It drives clawskills.sh with Puppeteer and finds the category chips by + * matching a regex against button text; the site changed its markup, the filter + * matched zero buttons, the import loop never ran, and the process still exited 0. + * A pipeline that reports success while importing nothing is worse than one that + * crashes, so every "the selector matched nothing" path below is now a hard + * failure that names the selector that came up empty, and the run summary goes out + * as a structured line (src/lib/ingestion-log.ts) that Cloud Logging can alert on. + * + * `--dry` scrapes and reports without writing, so a selector change can be + * confirmed against production data before anything touches the database. */ import puppeteer from "puppeteer-core"; import { PrismaClient } from "@prisma/client"; +import { logIngestionRun } from "../src/lib/ingestion-log"; const CHROME_PATH = process.env.CHROME_PATH || "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; +const JOB = "daily-sync"; +const SOURCE_URL = "https://clawskills.sh/"; + +/** + * The selectors this scrape depends on, in one place, because every failure + * message has to name the one that came up empty — "found 0 skills" sent nobody + * to the right line of code for two months. + * + * `categoryLabelPattern` is passed into the page as a string and rebuilt with + * `new RegExp` there: a RegExp cannot cross the page.evaluate boundary, and + * keeping a second copy inline is how these two drift apart. + */ +const SELECTORS = { + /** Skill detail links; also the readiness probe for the initial page load. */ + skillLink: 'a[href*="/skills/"]', + /** Category chips are + ) : agents.length === 0 ? ( -

{t("noAgentsInView")}

+

{t("noAgentsInView")}

) : (
- {agents.map((a) => ( -
-
-
-
- {a.name} - {a.featured && } - - {STATUS_LABEL_KEY[a.status] ? t(STATUS_LABEL_KEY[a.status]) : a.status} - -
-

- {a.publisher?.email || a.publisher?.name || t("unknownPublisher")} · {a.category?.name || t("uncategorized")} ·{" "} - {t("agentMetaSkills", { count: a._count.skills })} · {a.pricingModel} · {a.endpointUrl} -

- {a.scenarios.length > 0 ? ( -
- {a.scenarios.map((slug) => { - const sc = findScenario(slug); - return sc ? ( - - {sc.emoji} - {tScenario(slug)} - - ) : null; - })} + {agents.map((a) => { + const healthKey = a.healthStatus ? HEALTH_LABEL_KEY[a.healthStatus] : undefined; + return ( +
+
+
+
+ {a.name} + {a.featured && } + + {STATUS_LABEL_KEY[a.status] ? t(STATUS_LABEL_KEY[a.status]) : a.status} + + + {a.kind === "PROJECT" ? t("kindProject") : t("kindHosted")} + + {a.kind !== "PROJECT" && ( + + {healthKey ? t(healthKey) : t("healthUnknown")} + + )}
- ) : ( -

{t("noScenarios")}

- )} - {a.reviewNote &&

{t("reviewNoteLabel", { note: a.reviewNote })}

} -
-
- - - - - {a.status !== "APPROVED" && ( -
+
+ - {t("actionApprove")} - - )} - {a.status !== "REJECTED" && ( + + - )} - - -
-
- - {editingId === a.id && ( -
-

{t("scenariosEditorHint")}

-
- {SCENARIOS.map((s) => { - const on = draft.includes(s.slug); - return ( + {a.status === "DISABLED" ? ( + + ) : ( + a.status !== "APPROVED" && ( - ); - })} -
-
+ ) + )} + {a.status === "APPROVED" && ( + + )} + {a.status !== "REJECTED" && ( + + )} - {t("selectedCount", { count: draft.length })}
- )} -
- ))} + + {editingId === a.id && ( +
+

{t("scenariosEditorHint")}

+
+ {SCENARIOS.map((s) => { + const on = draft.includes(s.slug); + return ( + + ); + })} +
+
+ + + {t("selectedCount", { count: draft.length })} +
+
+ )} +
+ ); + })}
)} + + {pagination.totalPages > 1 && ( +
+ + {t("showingRange", { + from: (pagination.page - 1) * pagination.limit + 1, + to: Math.min(pagination.page * pagination.limit, pagination.total), + total: pagination.total, + })} + +
+ + +
+
+ )} + + {rejectTarget && ( + + + + + } + > +

{rejectTarget.name}

+ +