[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CFVAHZFCP6SCB74KZ7ZZRB":3,"skill-same:01M6CFVAHZFCP6SCB74KZ7ZZRB":140},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":31,"publishedAt":51,"linkOnly":52,"markdownContent":53,"markdownBody":54,"frontMatter":55,"refName":65,"commitSha":66,"skillDirectory":67,"forksCount":68,"repositoryHtmlUrl":69,"repositoryHomepageUrl":70,"ownerHtmlUrl":71,"ownerBlogUrl":72,"ownerDescription":6,"updatedAt":73,"install":74,"resources":87},"01M6CFVAHZFCP6SCB74KZ7ZZRB","runpod-migrate",null,"Migrate a codebase from the Runpod GraphQL API or REST v1 to REST v2 — inventory which parts use which API version, rewrite the call sites, flag breaking changes, and verify. Use when someone asks to move to v2, asks what v2 would change, or asks which Runpod API their code is on. For managing infrastructure rather than migrating code, use runpod-mcp or runpodctl.","runpod\u002Frunpod-plugins-official","runpod","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F95939477?v=4","01M6CFSV7PCCKKMKEV6AM8XC7H","Runpod","01M6CFSVC1QMHFR2P1HF14EZ08",49,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002FSKILL.md",[18,23,26],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[32,9,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50],"migrate","runpod migrate","when","api version","infrastructure","endpoint","macos","network","scripts","move","volume","which","template","change","api","code","Bash","MCP","inventory","2026-11-10 10:12:30",false,"---\nname: runpod-migrate\ndescription: >-\n  Migrate a codebase from the Runpod GraphQL API or REST v1 to REST v2 — inventory\n  which parts use which API version, rewrite the call sites, flag breaking changes,\n  and verify. Use when someone asks to move to v2, asks what v2 would change, or\n  asks which Runpod API their code is on. For managing infrastructure rather than\n  migrating code, use runpod-mcp or runpodctl.\nallowed-tools: Bash(python3 ${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Frunpod-migrate\u002Fscripts\u002Frp_api_inventory.py:*) Bash(rg:*)\ncompatibility: Linux, macOS, Windows\nmetadata:\n  author: runpod\n  version: \"2.0.0\" # x-release-please-version\n  concepts: [runpod-platform, pod, serverless-endpoint, template, network-volume]\nlicense: Apache-2.0\n---\n\n# Migrate to Runpod REST v2\n\nMoves a codebase off the **GraphQL API** (`api.runpod.io\u002Fgraphql`) and **REST v1**\n(`rest.runpod.io\u002Fv1`) onto **REST v2** (`api.runpod.io\u002Fv2`).\n\n**The payoff, in one line each** — you deliver these to the user at step 6, matched to\ntheir actual code. Don't recite them now:\n\n- **See stock before you rent** — `GET \u002Fv2\u002Fcatalog\u002Fgpus?include=AVAILABILITY&product=POD`. v1 had no\n  catalog at all, so every capacity retry loop was blind.\n- **Endpoints return their own job URLs** — `requestUrls.run`, no more string-building.\n- **Real lifecycle states** — `PROVISIONING`\u002F`STARTING`\u002F`ERROR` and an `actions` list, so\n  wait-loops fail fast instead of timing out.\n- **Mistakes fail loudly** — unknown request fields are rejected by name, with structured\n  errors and honest status codes.\n\nThe full set, organized as *if their code does X → v2 offers Y*:\n**[reference\u002Funlocks.md](reference\u002Funlocks.md)** — open it at step 6.\n\n## Before you touch any code\n\n**Infer the scope, state it, and move** — do not open with a questionnaire:\n\n| The user says | Scope |\n| --- | --- |\n| \"migrate to v2\" \u002F nothing specific | `all` — REST v1 **and** GraphQL |\n| \"just the REST stuff\", \"leave GraphQL alone\" | `rest` — REST v1 only |\n| \"get us off GraphQL\" | `graphql` — GraphQL only |\n\nThe table resolves every phrasing, so scope is not the thing to interrupt for. Say which\nrow you matched and carry on. **The question that does need asking comes later** — at\nstep 3, when the inventory shows the code depends on a capability v2 removed. That one\nis a real fork and you cannot answer it for them.\n\nSome things **have no v2 equivalent and must stay on GraphQL regardless of scope**:\naccount\u002Fbilling identity (`myself`) and spot\u002Finterruptible pods. A \"full\" migration still\nleaves those calls in place — say so up front rather than letting the user discover it at\nthe end.\n\n**Never rewrite the serverless job API.** `https:\u002F\u002Fapi.runpod.ai\u002Fv2\u002F\u003CendpointId>\u002Frun`,\n`\u002Frunsync`, `\u002Fstatus`, `\u002Fstream`, `\u002Fcancel` is a *different API* that happens to have\n`v2` in its path. It is unchanged and out of scope. The inventory reports it separately\nso you do not touch it.\n\n## The workflow\n\n### 1. Inventory — never migrate what you have not counted\n\nThe scanner ships **beside this file**, in the installed skill directory — not in the\nuser's repo. Resolve its path first; your working directory is their project:\n\n```bash\n# 1. Claude Code plugin installs expose the plugin root:\nSCAN=\"$CLAUDE_PLUGIN_ROOT\u002Fskills\u002Frunpod-migrate\u002Fscripts\u002Frp_api_inventory.py\"\n# 2. Otherwise substitute the directory you loaded this SKILL.md from — you know it:\n[ -f \"$SCAN\" ] || SCAN=\"\u003Cdirectory containing this SKILL.md>\u002Fscripts\u002Frp_api_inventory.py\"\n# 3. Last resort, search the usual install roots:\n[ -f \"$SCAN\" ] || SCAN=$(find ~\u002F.claude ~\u002F.agents ~\u002F.codex ~\u002F.config -name rp_api_inventory.py 2>\u002Fdev\u002Fnull | head -1)\npython3 \"$SCAN\" --help >\u002Fdev\u002Fnull || echo \"scanner not found — resolve it before continuing\"\n```\n\nThen, from the root of the user's repo:\n\n```bash\npython3 \"$SCAN\" . > runpod-api-inventory.md\npython3 \"$SCAN\" . --json > runpod-api-inventory.json   # if you want to drive edits from it\npython3 \"$SCAN\" . --scope rest                          # REST-only migrations\n```\n\n`runpod-api-inventory.md` lands in the user's repo — mention it, and remove it or\ngitignore it before you hand the migration back.\n\nStdlib-only Python, no install. It reports every call site bucketed by generation —\nGraphQL, REST v1, v1\u002FGraphQL **field names**, REST v2 **already**, serverless job API,\nSDK\u002FCLI wrappers — plus a suggested file-by-file order.\n\n**Show the user the inventory table before editing anything.** Users routinely do not\nknow what they are on: an agent picked a version for them months ago and wrote it down\nnowhere. \"3 files on v1, 2 on GraphQL, 1 already on v2, 2 on the job API — leave those\nalone\" is often the single most useful output of this whole skill.\n\n#### What it detects, and what it cannot\n\nIt is regex line-scanning, but the classification is what makes it usable — plain\n`grep -r runpod` gets two things actively wrong:\n\n- **`api.runpod.ai\u002Fv2` vs `api.runpod.io\u002Fv2`.** One letter apart. `.ai` is the serverless\n  job API and must not be touched; `.io` is the control plane you are migrating to.\n  Grepping for `v2` tells you the codebase is \"already migrated\" when it is not.\n- **Names legal in both versions.** `\u002Fpods` is a v1 path *and* a v2 path; `[\"pods\"]` is\n  v2 envelope-unwrapping; `idleTimeout` is top-level in v1 and nested under `workers` in\n  v2. The scanner suppresses a hit when the same line carries v2 context, so it reports\n  work that remains rather than every occurrence of a word.\n\nIt also looks for **field names, not just URLs**, which is what catches the files that\nnever spell \"runpod\": a module reading `p[\"costPerHr\"]` off a wrapper's return value has\nno URL, no import, no operation name — and is exactly what a v2 rename breaks silently.\n\nFour things it genuinely cannot resolve. Check them by hand, every time:\n\n| Blind spot | How to close it |\n| --- | --- |\n| **Base URL lives in config**, not code (`settings.yaml`, `.env`, a ConfigMap, Terraform) | The scanner does read those files, so the URL surfaces — but the *call sites* using it are elsewhere. Grep for whoever reads that config key. |\n| **Paths assembled by a helper** — `_url(\"pods\", pod_id, \"stop\")` | Reported under *possible indirect call sites*. Advisory, because `resp.json()[\"pods\"]` looks identical. Open each one. |\n| **SDK wrappers** (`import runpod`) | The API generation is a property of the installed *version*, not the code. Check `requirements.txt` \u002F lockfile and the SDK's own release notes. |\n| **Generated clients** | The OpenAPI\u002FGraphQL document is the real source. Regenerate from the v2 spec instead of editing generated files. |\n\nThen read the code the scanner flagged. It finds call sites; it does not understand your\nwrappers. Trace who calls them — a renamed response field like `costPerHr → cost` breaks\nevery caller, not just the request builder. This is the one step where a code-graph or\nLSP index earns its keep, if one is already available.\n\n### 2. Brief the breaking changes — before the diff, not after\n\nRead **[reference\u002Fbreaking-changes.md](reference\u002Fbreaking-changes.md)** and tell the\nuser which ones actually apply to *their* code. Two classes, and the second is the one\nthey are afraid of:\n\n1. **Renames and moves** — loud. v2 rejects unknown request fields with `422` listing\n   them by name, so a missed rename cannot slip into production silently.\n2. **Same name, different meaning** — quiet, and the reason a green test suite is not\n   proof. The reference enumerates every one of them; the two that bite hardest:\n   `flashboot` went from boolean to a three-value enum, and v1's `\u002Fbilling\u002Fendpoints`\n   (serverless spend) is v2's `\u002Fbilling\u002Fserverless` — v2's `\u002Fbilling\u002Fendpoints` bills a\n   *different product* (public endpoints) and answers `200` with a correct total for\n   that product, which is not the one the caller asked for.\n\n### 3. Plan, split into required vs cleanup\n\nWrite the plan down before editing, and keep these buckets separate all the way through\nto the final summary:\n\n- **Required** — it does not work on v2 without this.\n- **Cleanup** — it works either way, but v2 lets you delete code (hand-built job URLs,\n  hand-rolled availability retry, polling loops that can now be SSE).\n- **Decisions the user must make** — the code depends on something v2 removed outright:\n  spot\u002Finterruptible pods, savings plans, placement constraints\n  (`countryCodes`, `minDownloadMbps`, …), pod `reset`, per-pod GPU fallback. See\n  [breaking-changes.md](reference\u002Fbreaking-changes.md) Class 3 — and check it rather\n  than working from memory, because things leave this bucket as v2 grows. CUDA pinning,\n  `templateId` and CPU endpoint writes all used to be here and are not any more.\n\n**Stop and ask before writing code in that third bucket** — but bring the replacement\nwith you. Some of these have a working rebuild and some genuinely have nothing, and the\ndifference decides what you are asking. Where a rebuild exists (`countryCodes` →\n[catalog filter + `dataCenterIds` + a placement\nassert](reference\u002Fbreaking-changes.md#replacing-countrycodes-and-the-rest-of-the-placement-constraints),\nper-pod GPU fallback → [an availability-ordered\nloop](reference\u002Fbreaking-changes.md#replacing-the-gpu-fallback-list-pods-only)), show it\nand ask the one question that changes it — for `countryCodes`, whether the restriction\nwas a preference or a compliance requirement. Where nothing exists, the options are\naccept the behavior change, keep that call on v1\u002FGraphQL, or redesign around it, and only\nthe user can pick.\n\nEither way, do not present a removal as a dead end when a rebuild exists — that pushes\nthe user into keeping a v1 call they did not need to keep. And never drop the field with\na `# no v2 equivalent` comment: that is the failure mode this bucket exists to prevent,\nbecause it silently changes what their infrastructure does. If the bucket is empty, say\nso — that is reassuring and takes one line.\n\n### 4. Migrate, one file per commit\n\nWork in the scanner's suggested order (fewest call sites first) — **with one override:\nif several call sites share a transport helper, migrate the helper first**, whatever its\ncount. The scanner orders by call-site count and cannot see imports, so it will happily\nput a consumer ahead of the module it imports its client from. Migrating a consumer\nfirst means writing against an interface you are about to change.\n\nPer file:\n\n- Map paths and fields with **[reference\u002Frest-v1-to-v2.md](reference\u002Frest-v1-to-v2.md)**\n  or **[reference\u002Fgraphql-to-v2.md](reference\u002Fgraphql-to-v2.md)**. If a field isn't in\n  the tables, or the API disagrees with them, check the spec directly —\n  [Ground truth](#ground-truth-check-the-spec-yourself).\n- **`gpuTypeIds` + `gpuTypePriority` on a *pod* means you are writing new code, not\n  renaming fields.** A v2 pod takes one GPU type, so the server-side fallback becomes a\n  client-side loop over the catalog. Working implementation:\n  [breaking-changes.md → Replacing the GPU fallback list](reference\u002Fbreaking-changes.md#replacing-the-gpu-fallback-list-pods-only).\n  **Endpoints need no loop** — `gpu.pools` is already a list and workers land on\n  whichever listed pool has capacity.\n- **Always request availability on catalog reads — with `product`.** Any\n  `GET \u002Fv2\u002Fcatalog\u002Fgpus`, `\u002Fcatalog\u002Fcpus`, or `\u002Fcatalog\u002Fdatacenters` this migration\n  introduces gets `include=AVAILABILITY` (`GPU_AVAILABILITY`\u002F`CPU_AVAILABILITY` for\n  datacenters). Availability is the top-of-mind question for every Runpod user and the\n  call costs the same. Do not omit it because the current code did not ask for it — v1\n  could not.\n\n  **`include=AVAILABILITY` alone is a `400`.** On `\u002Fcatalog\u002Fgpus` and `\u002Fcatalog\u002Fcpus`,\n  `product` is required with it and invalid without it — `400` either way. There is no\n  default, deliberately: the same GPU can be scarce for `POD` and plentiful for\n  `SERVERLESS`, so the context has to be stated. Pick the one matching what you are\n  creating (`POD`, `SERVERLESS`, or `CLUSTER`; CPUs take `POD` or `SERVERLESS`):\n\n  ```\n  GET \u002Fv2\u002Fcatalog\u002Fgpus?include=AVAILABILITY&product=POD\n  ```\n- Offer the **rollback flag** (`RUNPOD_API_V1=1`) while v2 is new to them:\n  **[reference\u002Frollback-flag.md](reference\u002Frollback-flag.md)**. Worth it for a service\n  in production; skip it for a one-off script.\n- **Never change behavior and API version in the same commit** — including the\n  improvements v2 makes possible. Failing fast on `status == \"ERROR\"` instead of timing\n  out is a genuine win and it belongs in the *next* commit; folding it into the\n  migration commit means a rollback has to give up both. Land those in the **cleanup**\n  bucket, separately.\n\nA full before\u002Fafter of a real client — pod create with GPU fallback, endpoint create\nwith the container config inlined, GraphQL dashboard — is in\n**[reference\u002Fworked-example.md](reference\u002Fworked-example.md)**.\n\n### 5. Verify against the live API\n\nStatic review is not enough; v2's validator is strict and its errors are precise.\nRe-run the scanner to prove the call sites are gone, then exercise the real paths:\n\n```bash\npython3 \"$SCAN\" . --scope rest --fail-on-legacy   # exit 1 if v1 remains\n```\n\nTwo markers, and they mean different things — do not reach for the wrong one:\n\n| Marker | Use it for |\n| --- | --- |\n| `rp-migrate: keep-v1` | legacy code kept **on purpose** — a `RUNPOD_API_V1` rollback branch, or a GraphQL call with no v2 equivalent. Reported under *kept on purpose*. |\n| `rp-migrate: ignore` | a **false positive** on code that is already correct. Says \"this isn't legacy\", not \"this is legacy I'm keeping\". Reported under *marked false positives*. |\n\nBoth accept `line`, `start`\u002F`end` region, or `file` scope, and both drop out of the plan\nand out of `--fail-on-legacy`. Using `keep-v1` to silence a false positive records a lie\nin the report — reach for `ignore` there.\n\n**Where the gate can still be wrong.** The same blind spots from step 1 invert after a\nmigration: before, they hide v1 code; after, they can flag correct v2 code. The scanner\nhandles the two common cases — trailing `# was imageName` annotations, and a base URL\nheld in a constant (`f\"{BASE}\u002Fpods\"` where `BASE` is a v2 URL defined anywhere in the\nfile). Beyond those — a base URL imported from another module, or built at runtime from\nconfig — it can still misread correct code as legacy. Read the flagged lines before\nbelieving the exit code, and mark true false positives with `ignore` rather than\nweakening the gate.\n\n- **Reads** are free — list pods, endpoints, volumes, catalog. Confirm you unwrap the\n  new envelope (`{\"pods\": [...]}`, not a bare array).\n- **Writes** cost money. Create → assert → delete, on the smallest thing that proves the\n  shape. Never test against resources the user already has.\n- Decode `422`s with the table in\n  [reference\u002Fbreaking-changes.md](reference\u002Fbreaking-changes.md#reading-a-422) — including\n  the confusing one where a *missing required field* makes the validator report your\n  *valid* fields as \"additional properties not allowed\".\n\n### 6. Summarize — this is the artifact they will actually read\n\nMost users read the summary and not the diff. Structure it exactly like this:\n\n```\n## Required for the migration\n\u003Cfile:line> — what changed and why it had to\n\n## Cleanup enabled by v2\n\u003Cfile:line> — what got deleted or simplified\n\n## Behavior changes to watch\nthe same-name-different-meaning items that applied\n\n## Still on GraphQL (no v2 equivalent)\nmyself \u002F spot pods — and why\n\n## Unlocks: what you can build now\ntied to what this codebase already does\n```\n\nThat last section is the highest-value part. Do not paste a generic feature list —\nlook at what this user has been building and struggling with, including anything you\nalready know from the session, and name where v2 changes it. \"Your `wait_until_running`\nloop times out on failed pods; v2's `ERROR` status lets it fail in seconds\" beats \"v2 has\nricher status values\". [reference\u002Funlocks.md](reference\u002Funlocks.md) is organized as\n*if the code does X → v2 offers Y* for exactly this.\n\n## Ground truth: check the spec yourself\n\nThe mapping tables in `reference\u002F` were verified against the live API on **2026-08-10**.\nv2 is actively developed, so treat them as a fast path, not as the authority. Both specs\nare public and need no auth:\n\n```bash\ncurl -s https:\u002F\u002Fapi.runpod.io\u002Fv2\u002Fopenapi.json  -o \u002Ftmp\u002Frp-v2.json\ncurl -s https:\u002F\u002Frest.runpod.io\u002Fv1\u002Fopenapi.json -o \u002Ftmp\u002Frp-v1.json\n```\n\n**What a request body actually accepts, and what is required** (`*`). Worth running\nbefore writing any create call — it resolves `allOf` composition, which a naive read of\nthe raw JSON misses:\n\n```bash\npython3 - CreateEndpointRequest \u003C\u003C'PY'\nimport json, sys\nS = json.load(open(\"\u002Ftmp\u002Frp-v2.json\"))[\"components\"][\"schemas\"]\ndef merge(n, acc=None):\n    acc = acc if acc is not None else {\"props\": {}, \"req\": set()}\n    if \"$ref\" in n: return merge(S[n[\"$ref\"].split(\"\u002F\")[-1]], acc)\n    for sub in n.get(\"allOf\", []): merge(sub, acc)\n    acc[\"props\"].update(n.get(\"properties\", {})); acc[\"req\"].update(n.get(\"required\", []))\n    return acc\ndef kind(v):\n    if \"$ref\" in v: return v[\"$ref\"].split(\"\u002F\")[-1]\n    if \"allOf\" in v: return kind(v[\"allOf\"][0])\n    return v.get(\"type\", \"?\")\nm = merge(S[sys.argv[1]])\nfor k, v in sorted(m[\"props\"].items()):\n    print(f\"  {'*' if k in m['req'] else ' '} {k:16} {kind(v)}\")\nPY\n```\n\nSwap the argument for `CreatePodRequest`, `UpdatePodRequest`, `CreateTemplateRequest`,\n`CreateNetworkVolumeRequest`, … **Which schemas mention a field** — useful when a `422`\nnames something you cannot place:\n\n```bash\npython3 -c 'import json,sys; S=json.load(open(\"\u002Ftmp\u002Frp-v2.json\"))[\"components\"][\"schemas\"]; [print(\" \",n) for n,s in S.items() if sys.argv[1] in json.dumps(s)]' flashboot\n```\n\n### Precedence when sources disagree\n\n**observed live behavior > the spec > these tables.** The spec is not always right, and\nthe reference docs say so where it is known to be wrong — `timeout` is documented to\ndefault to `300000` ms but comes back `0`. If you hit a case where the running API\ncontradicts the spec, trust the API, and **say so in your summary** so the user knows a\ndocumented default cannot be relied on.\n\nIf you find a mapping in `reference\u002F` that no longer matches the spec, fix the call and\nflag the drift — the tables carry a verification date precisely so staleness is\ndetectable rather than silent.\n\nFor GraphQL there is no machine-readable schema (introspection is disabled), so that\nside cannot be checked this way — see the caveat in\n[reference\u002Fgraphql-to-v2.md](reference\u002Fgraphql-to-v2.md).\n\n## Tooling notes\n\nThe inventory scanner is deliberately a **grep-class script, not a code-graph index**:\nAPI generation is a property of URL strings and field names, it must work on any\nlanguage in an arbitrary customer repo, and it has to give the same answer for every\nuser with zero setup. A code-intelligence index (LSP, or an MCP graph server if one is\nalready running) earns its keep at a different step — step 1's *blast radius* question,\n\"who calls this wrapper whose response field just got renamed\" — not at detection.\nUse one there if it is already available; do not stand one up just for this.\n","# Migrate to Runpod REST v2\n\nMoves a codebase off the **GraphQL API** (`api.runpod.io\u002Fgraphql`) and **REST v1**\n(`rest.runpod.io\u002Fv1`) onto **REST v2** (`api.runpod.io\u002Fv2`).\n\n**The payoff, in one line each** — you deliver these to the user at step 6, matched to\ntheir actual code. Don't recite them now:\n\n- **See stock before you rent** — `GET \u002Fv2\u002Fcatalog\u002Fgpus?include=AVAILABILITY&product=POD`. v1 had no\n  catalog at all, so every capacity retry loop was blind.\n- **Endpoints return their own job URLs** — `requestUrls.run`, no more string-building.\n- **Real lifecycle states** — `PROVISIONING`\u002F`STARTING`\u002F`ERROR` and an `actions` list, so\n  wait-loops fail fast instead of timing out.\n- **Mistakes fail loudly** — unknown request fields are rejected by name, with structured\n  errors and honest status codes.\n\nThe full set, organized as *if their code does X → v2 offers Y*:\n**[reference\u002Funlocks.md](reference\u002Funlocks.md)** — open it at step 6.\n\n## Before you touch any code\n\n**Infer the scope, state it, and move** — do not open with a questionnaire:\n\n| The user says | Scope |\n| --- | --- |\n| \"migrate to v2\" \u002F nothing specific | `all` — REST v1 **and** GraphQL |\n| \"just the REST stuff\", \"leave GraphQL alone\" | `rest` — REST v1 only |\n| \"get us off GraphQL\" | `graphql` — GraphQL only |\n\nThe table resolves every phrasing, so scope is not the thing to interrupt for. Say which\nrow you matched and carry on. **The question that does need asking comes later** — at\nstep 3, when the inventory shows the code depends on a capability v2 removed. That one\nis a real fork and you cannot answer it for them.\n\nSome things **have no v2 equivalent and must stay on GraphQL regardless of scope**:\naccount\u002Fbilling identity (`myself`) and spot\u002Finterruptible pods. A \"full\" migration still\nleaves those calls in place — say so up front rather than letting the user discover it at\nthe end.\n\n**Never rewrite the serverless job API.** `https:\u002F\u002Fapi.runpod.ai\u002Fv2\u002F\u003CendpointId>\u002Frun`,\n`\u002Frunsync`, `\u002Fstatus`, `\u002Fstream`, `\u002Fcancel` is a *different API* that happens to have\n`v2` in its path. It is unchanged and out of scope. The inventory reports it separately\nso you do not touch it.\n\n## The workflow\n\n### 1. Inventory — never migrate what you have not counted\n\nThe scanner ships **beside this file**, in the installed skill directory — not in the\nuser's repo. Resolve its path first; your working directory is their project:\n\n```bash\n# 1. Claude Code plugin installs expose the plugin root:\nSCAN=\"$CLAUDE_PLUGIN_ROOT\u002Fskills\u002Frunpod-migrate\u002Fscripts\u002Frp_api_inventory.py\"\n# 2. Otherwise substitute the directory you loaded this SKILL.md from — you know it:\n[ -f \"$SCAN\" ] || SCAN=\"\u003Cdirectory containing this SKILL.md>\u002Fscripts\u002Frp_api_inventory.py\"\n# 3. Last resort, search the usual install roots:\n[ -f \"$SCAN\" ] || SCAN=$(find ~\u002F.claude ~\u002F.agents ~\u002F.codex ~\u002F.config -name rp_api_inventory.py 2>\u002Fdev\u002Fnull | head -1)\npython3 \"$SCAN\" --help >\u002Fdev\u002Fnull || echo \"scanner not found — resolve it before continuing\"\n```\n\nThen, from the root of the user's repo:\n\n```bash\npython3 \"$SCAN\" . > runpod-api-inventory.md\npython3 \"$SCAN\" . --json > runpod-api-inventory.json   # if you want to drive edits from it\npython3 \"$SCAN\" . --scope rest                          # REST-only migrations\n```\n\n`runpod-api-inventory.md` lands in the user's repo — mention it, and remove it or\ngitignore it before you hand the migration back.\n\nStdlib-only Python, no install. It reports every call site bucketed by generation —\nGraphQL, REST v1, v1\u002FGraphQL **field names**, REST v2 **already**, serverless job API,\nSDK\u002FCLI wrappers — plus a suggested file-by-file order.\n\n**Show the user the inventory table before editing anything.** Users routinely do not\nknow what they are on: an agent picked a version for them months ago and wrote it down\nnowhere. \"3 files on v1, 2 on GraphQL, 1 already on v2, 2 on the job API — leave those\nalone\" is often the single most useful output of this whole skill.\n\n#### What it detects, and what it cannot\n\nIt is regex line-scanning, but the classification is what makes it usable — plain\n`grep -r runpod` gets two things actively wrong:\n\n- **`api.runpod.ai\u002Fv2` vs `api.runpod.io\u002Fv2`.** One letter apart. `.ai` is the serverless\n  job API and must not be touched; `.io` is the control plane you are migrating to.\n  Grepping for `v2` tells you the codebase is \"already migrated\" when it is not.\n- **Names legal in both versions.** `\u002Fpods` is a v1 path *and* a v2 path; `[\"pods\"]` is\n  v2 envelope-unwrapping; `idleTimeout` is top-level in v1 and nested under `workers` in\n  v2. The scanner suppresses a hit when the same line carries v2 context, so it reports\n  work that remains rather than every occurrence of a word.\n\nIt also looks for **field names, not just URLs**, which is what catches the files that\nnever spell \"runpod\": a module reading `p[\"costPerHr\"]` off a wrapper's return value has\nno URL, no import, no operation name — and is exactly what a v2 rename breaks silently.\n\nFour things it genuinely cannot resolve. Check them by hand, every time:\n\n| Blind spot | How to close it |\n| --- | --- |\n| **Base URL lives in config**, not code (`settings.yaml`, `.env`, a ConfigMap, Terraform) | The scanner does read those files, so the URL surfaces — but the *call sites* using it are elsewhere. Grep for whoever reads that config key. |\n| **Paths assembled by a helper** — `_url(\"pods\", pod_id, \"stop\")` | Reported under *possible indirect call sites*. Advisory, because `resp.json()[\"pods\"]` looks identical. Open each one. |\n| **SDK wrappers** (`import runpod`) | The API generation is a property of the installed *version*, not the code. Check `requirements.txt` \u002F lockfile and the SDK's own release notes. |\n| **Generated clients** | The OpenAPI\u002FGraphQL document is the real source. Regenerate from the v2 spec instead of editing generated files. |\n\nThen read the code the scanner flagged. It finds call sites; it does not understand your\nwrappers. Trace who calls them — a renamed response field like `costPerHr → cost` breaks\nevery caller, not just the request builder. This is the one step where a code-graph or\nLSP index earns its keep, if one is already available.\n\n### 2. Brief the breaking changes — before the diff, not after\n\nRead **[reference\u002Fbreaking-changes.md](reference\u002Fbreaking-changes.md)** and tell the\nuser which ones actually apply to *their* code. Two classes, and the second is the one\nthey are afraid of:\n\n1. **Renames and moves** — loud. v2 rejects unknown request fields with `422` listing\n   them by name, so a missed rename cannot slip into production silently.\n2. **Same name, different meaning** — quiet, and the reason a green test suite is not\n   proof. The reference enumerates every one of them; the two that bite hardest:\n   `flashboot` went from boolean to a three-value enum, and v1's `\u002Fbilling\u002Fendpoints`\n   (serverless spend) is v2's `\u002Fbilling\u002Fserverless` — v2's `\u002Fbilling\u002Fendpoints` bills a\n   *different product* (public endpoints) and answers `200` with a correct total for\n   that product, which is not the one the caller asked for.\n\n### 3. Plan, split into required vs cleanup\n\nWrite the plan down before editing, and keep these buckets separate all the way through\nto the final summary:\n\n- **Required** — it does not work on v2 without this.\n- **Cleanup** — it works either way, but v2 lets you delete code (hand-built job URLs,\n  hand-rolled availability retry, polling loops that can now be SSE).\n- **Decisions the user must make** — the code depends on something v2 removed outright:\n  spot\u002Finterruptible pods, savings plans, placement constraints\n  (`countryCodes`, `minDownloadMbps`, …), pod `reset`, per-pod GPU fallback. See\n  [breaking-changes.md](reference\u002Fbreaking-changes.md) Class 3 — and check it rather\n  than working from memory, because things leave this bucket as v2 grows. CUDA pinning,\n  `templateId` and CPU endpoint writes all used to be here and are not any more.\n\n**Stop and ask before writing code in that third bucket** — but bring the replacement\nwith you. Some of these have a working rebuild and some genuinely have nothing, and the\ndifference decides what you are asking. Where a rebuild exists (`countryCodes` →\n[catalog filter + `dataCenterIds` + a placement\nassert](reference\u002Fbreaking-changes.md#replacing-countrycodes-and-the-rest-of-the-placement-constraints),\nper-pod GPU fallback → [an availability-ordered\nloop](reference\u002Fbreaking-changes.md#replacing-the-gpu-fallback-list-pods-only)), show it\nand ask the one question that changes it — for `countryCodes`, whether the restriction\nwas a preference or a compliance requirement. Where nothing exists, the options are\naccept the behavior change, keep that call on v1\u002FGraphQL, or redesign around it, and only\nthe user can pick.\n\nEither way, do not present a removal as a dead end when a rebuild exists — that pushes\nthe user into keeping a v1 call they did not need to keep. And never drop the field with\na `# no v2 equivalent` comment: that is the failure mode this bucket exists to prevent,\nbecause it silently changes what their infrastructure does. If the bucket is empty, say\nso — that is reassuring and takes one line.\n\n### 4. Migrate, one file per commit\n\nWork in the scanner's suggested order (fewest call sites first) — **with one override:\nif several call sites share a transport helper, migrate the helper first**, whatever its\ncount. The scanner orders by call-site count and cannot see imports, so it will happily\nput a consumer ahead of the module it imports its client from. Migrating a consumer\nfirst means writing against an interface you are about to change.\n\nPer file:\n\n- Map paths and fields with **[reference\u002Frest-v1-to-v2.md](reference\u002Frest-v1-to-v2.md)**\n  or **[reference\u002Fgraphql-to-v2.md](reference\u002Fgraphql-to-v2.md)**. If a field isn't in\n  the tables, or the API disagrees with them, check the spec directly —\n  [Ground truth](#ground-truth-check-the-spec-yourself).\n- **`gpuTypeIds` + `gpuTypePriority` on a *pod* means you are writing new code, not\n  renaming fields.** A v2 pod takes one GPU type, so the server-side fallback becomes a\n  client-side loop over the catalog. Working implementation:\n  [breaking-changes.md → Replacing the GPU fallback list](reference\u002Fbreaking-changes.md#replacing-the-gpu-fallback-list-pods-only).\n  **Endpoints need no loop** — `gpu.pools` is already a list and workers land on\n  whichever listed pool has capacity.\n- **Always request availability on catalog reads — with `product`.** Any\n  `GET \u002Fv2\u002Fcatalog\u002Fgpus`, `\u002Fcatalog\u002Fcpus`, or `\u002Fcatalog\u002Fdatacenters` this migration\n  introduces gets `include=AVAILABILITY` (`GPU_AVAILABILITY`\u002F`CPU_AVAILABILITY` for\n  datacenters). Availability is the top-of-mind question for every Runpod user and the\n  call costs the same. Do not omit it because the current code did not ask for it — v1\n  could not.\n\n  **`include=AVAILABILITY` alone is a `400`.** On `\u002Fcatalog\u002Fgpus` and `\u002Fcatalog\u002Fcpus`,\n  `product` is required with it and invalid without it — `400` either way. There is no\n  default, deliberately: the same GPU can be scarce for `POD` and plentiful for\n  `SERVERLESS`, so the context has to be stated. Pick the one matching what you are\n  creating (`POD`, `SERVERLESS`, or `CLUSTER`; CPUs take `POD` or `SERVERLESS`):\n\n  ```\n  GET \u002Fv2\u002Fcatalog\u002Fgpus?include=AVAILABILITY&product=POD\n  ```\n- Offer the **rollback flag** (`RUNPOD_API_V1=1`) while v2 is new to them:\n  **[reference\u002Frollback-flag.md](reference\u002Frollback-flag.md)**. Worth it for a service\n  in production; skip it for a one-off script.\n- **Never change behavior and API version in the same commit** — including the\n  improvements v2 makes possible. Failing fast on `status == \"ERROR\"` instead of timing\n  out is a genuine win and it belongs in the *next* commit; folding it into the\n  migration commit means a rollback has to give up both. Land those in the **cleanup**\n  bucket, separately.\n\nA full before\u002Fafter of a real client — pod create with GPU fallback, endpoint create\nwith the container config inlined, GraphQL dashboard — is in\n**[reference\u002Fworked-example.md](reference\u002Fworked-example.md)**.\n\n### 5. Verify against the live API\n\nStatic review is not enough; v2's validator is strict and its errors are precise.\nRe-run the scanner to prove the call sites are gone, then exercise the real paths:\n\n```bash\npython3 \"$SCAN\" . --scope rest --fail-on-legacy   # exit 1 if v1 remains\n```\n\nTwo markers, and they mean different things — do not reach for the wrong one:\n\n| Marker | Use it for |\n| --- | --- |\n| `rp-migrate: keep-v1` | legacy code kept **on purpose** — a `RUNPOD_API_V1` rollback branch, or a GraphQL call with no v2 equivalent. Reported under *kept on purpose*. |\n| `rp-migrate: ignore` | a **false positive** on code that is already correct. Says \"this isn't legacy\", not \"this is legacy I'm keeping\". Reported under *marked false positives*. |\n\nBoth accept `line`, `start`\u002F`end` region, or `file` scope, and both drop out of the plan\nand out of `--fail-on-legacy`. Using `keep-v1` to silence a false positive records a lie\nin the report — reach for `ignore` there.\n\n**Where the gate can still be wrong.** The same blind spots from step 1 invert after a\nmigration: before, they hide v1 code; after, they can flag correct v2 code. The scanner\nhandles the two common cases — trailing `# was imageName` annotations, and a base URL\nheld in a constant (`f\"{BASE}\u002Fpods\"` where `BASE` is a v2 URL defined anywhere in the\nfile). Beyond those — a base URL imported from another module, or built at runtime from\nconfig — it can still misread correct code as legacy. Read the flagged lines before\nbelieving the exit code, and mark true false positives with `ignore` rather than\nweakening the gate.\n\n- **Reads** are free — list pods, endpoints, volumes, catalog. Confirm you unwrap the\n  new envelope (`{\"pods\": [...]}`, not a bare array).\n- **Writes** cost money. Create → assert → delete, on the smallest thing that proves the\n  shape. Never test against resources the user already has.\n- Decode `422`s with the table in\n  [reference\u002Fbreaking-changes.md](reference\u002Fbreaking-changes.md#reading-a-422) — including\n  the confusing one where a *missing required field* makes the validator report your\n  *valid* fields as \"additional properties not allowed\".\n\n### 6. Summarize — this is the artifact they will actually read\n\nMost users read the summary and not the diff. Structure it exactly like this:\n\n```\n## Required for the migration\n\u003Cfile:line> — what changed and why it had to\n\n## Cleanup enabled by v2\n\u003Cfile:line> — what got deleted or simplified\n\n## Behavior changes to watch\nthe same-name-different-meaning items that applied\n\n## Still on GraphQL (no v2 equivalent)\nmyself \u002F spot pods — and why\n\n## Unlocks: what you can build now\ntied to what this codebase already does\n```\n\nThat last section is the highest-value part. Do not paste a generic feature list —\nlook at what this user has been building and struggling with, including anything you\nalready know from the session, and name where v2 changes it. \"Your `wait_until_running`\nloop times out on failed pods; v2's `ERROR` status lets it fail in seconds\" beats \"v2 has\nricher status values\". [reference\u002Funlocks.md](reference\u002Funlocks.md) is organized as\n*if the code does X → v2 offers Y* for exactly this.\n\n## Ground truth: check the spec yourself\n\nThe mapping tables in `reference\u002F` were verified against the live API on **2026-08-10**.\nv2 is actively developed, so treat them as a fast path, not as the authority. Both specs\nare public and need no auth:\n\n```bash\ncurl -s https:\u002F\u002Fapi.runpod.io\u002Fv2\u002Fopenapi.json  -o \u002Ftmp\u002Frp-v2.json\ncurl -s https:\u002F\u002Frest.runpod.io\u002Fv1\u002Fopenapi.json -o \u002Ftmp\u002Frp-v1.json\n```\n\n**What a request body actually accepts, and what is required** (`*`). Worth running\nbefore writing any create call — it resolves `allOf` composition, which a naive read of\nthe raw JSON misses:\n\n```bash\npython3 - CreateEndpointRequest \u003C\u003C'PY'\nimport json, sys\nS = json.load(open(\"\u002Ftmp\u002Frp-v2.json\"))[\"components\"][\"schemas\"]\ndef merge(n, acc=None):\n    acc = acc if acc is not None else {\"props\": {}, \"req\": set()}\n    if \"$ref\" in n: return merge(S[n[\"$ref\"].split(\"\u002F\")[-1]], acc)\n    for sub in n.get(\"allOf\", []): merge(sub, acc)\n    acc[\"props\"].update(n.get(\"properties\", {})); acc[\"req\"].update(n.get(\"required\", []))\n    return acc\ndef kind(v):\n    if \"$ref\" in v: return v[\"$ref\"].split(\"\u002F\")[-1]\n    if \"allOf\" in v: return kind(v[\"allOf\"][0])\n    return v.get(\"type\", \"?\")\nm = merge(S[sys.argv[1]])\nfor k, v in sorted(m[\"props\"].items()):\n    print(f\"  {'*' if k in m['req'] else ' '} {k:16} {kind(v)}\")\nPY\n```\n\nSwap the argument for `CreatePodRequest`, `UpdatePodRequest`, `CreateTemplateRequest`,\n`CreateNetworkVolumeRequest`, … **Which schemas mention a field** — useful when a `422`\nnames something you cannot place:\n\n```bash\npython3 -c 'import json,sys; S=json.load(open(\"\u002Ftmp\u002Frp-v2.json\"))[\"components\"][\"schemas\"]; [print(\" \",n) for n,s in S.items() if sys.argv[1] in json.dumps(s)]' flashboot\n```\n\n### Precedence when sources disagree\n\n**observed live behavior > the spec > these tables.** The spec is not always right, and\nthe reference docs say so where it is known to be wrong — `timeout` is documented to\ndefault to `300000` ms but comes back `0`. If you hit a case where the running API\ncontradicts the spec, trust the API, and **say so in your summary** so the user knows a\ndocumented default cannot be relied on.\n\nIf you find a mapping in `reference\u002F` that no longer matches the spec, fix the call and\nflag the drift — the tables carry a verification date precisely so staleness is\ndetectable rather than silent.\n\nFor GraphQL there is no machine-readable schema (introspection is disabled), so that\nside cannot be checked this way — see the caveat in\n[reference\u002Fgraphql-to-v2.md](reference\u002Fgraphql-to-v2.md).\n\n## Tooling notes\n\nThe inventory scanner is deliberately a **grep-class script, not a code-graph index**:\nAPI generation is a property of URL strings and field names, it must work on any\nlanguage in an arbitrary customer repo, and it has to give the same answer for every\nuser with zero setup. A code-intelligence index (LSP, or an MCP graph server if one is\nalready running) earns its keep at a different step — step 1's *blast radius* question,\n\"who calls this wrapper whose response field just got renamed\" — not at detection.\nUse one there if it is already available; do not stand one up just for this.\n",{"name":5,"license":15,"metadata":56,"description":7,"allowed-tools":63,"compatibility":64},{"author":9,"version":57,"concepts":58},"2.0.0",[59,60,61,44,62],"runpod-platform","pod","serverless-endpoint","network-volume","Bash(python3 ${CLAUDE_PLUGIN_ROOT}\u002Fskills\u002Frunpod-migrate\u002Fscripts\u002Frp_api_inventory.py:*) Bash(rg:*)","Linux, macOS, Windows","main","91b9f9671af0f15186420ec466fe6e80c1b13f25","plugins\u002Frunpod\u002Fskills\u002Frunpod-migrate",10,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official","https:\u002F\u002Fdocs.runpod.io\u002Fagent-setup.md","https:\u002F\u002Fgithub.com\u002Frunpod","https:\u002F\u002Fwww.runpod.io","2026-11-10 06:32:38",{"source":8,"skillName":5,"commands":75,"prompt":85,"zipAvailable":86},[76,79,82],{"tool":77,"command":78},"npx","npx skills add runpod\u002Frunpod-plugins-official --skill runpod-migrate",{"tool":80,"command":81},"bunx","bunx skills add runpod\u002Frunpod-plugins-official --skill runpod-migrate",{"tool":83,"command":84},"pnpm","pnpm dlx skills add runpod\u002Frunpod-plugins-official --skill runpod-migrate","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002FSKILL.md",true,[88,94,98,102,106,110,114,118,122,126,130,136],{"path":89,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":92,"sourceHtmlUrl":93},"evals\u002Favailability-and-fallback.eval.md","REFERENCE","参考文档",2675,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Fevals\u002Favailability-and-fallback.eval.md",{"path":95,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":96,"sourceHtmlUrl":97},"evals\u002Finventory-before-editing.eval.md",1646,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Fevals\u002Finventory-before-editing.eval.md",{"path":99,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":100,"sourceHtmlUrl":101},"evals\u002Fnetwork-volume-pins-the-data-center.eval.md",2350,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Fevals\u002Fnetwork-volume-pins-the-data-center.eval.md",{"path":103,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":104,"sourceHtmlUrl":105},"evals\u002Frest-only-scope.eval.md",1383,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Fevals\u002Frest-only-scope.eval.md",{"path":107,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":108,"sourceHtmlUrl":109},"reference\u002Fbreaking-changes.md",20944,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Freference\u002Fbreaking-changes.md",{"path":111,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":112,"sourceHtmlUrl":113},"reference\u002Fgraphql-to-v2.md",11796,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Freference\u002Fgraphql-to-v2.md",{"path":115,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":116,"sourceHtmlUrl":117},"reference\u002Frest-v1-to-v2.md",24194,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Freference\u002Frest-v1-to-v2.md",{"path":119,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":120,"sourceHtmlUrl":121},"reference\u002Frollback-flag.md",4146,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Freference\u002Frollback-flag.md",{"path":123,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":124,"sourceHtmlUrl":125},"reference\u002Funlocks.md",5284,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Freference\u002Funlocks.md",{"path":127,"resourceType":90,"resourceTypeDesc":91,"sizeBytes":128,"sourceHtmlUrl":129},"reference\u002Fworked-example.md",11256,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Freference\u002Fworked-example.md",{"path":131,"resourceType":132,"resourceTypeDesc":133,"sizeBytes":134,"sourceHtmlUrl":135},"scripts\u002Frp_api_inventory.py","SCRIPT","脚本",41796,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Fscripts\u002Frp_api_inventory.py",{"path":137,"resourceType":132,"resourceTypeDesc":133,"sizeBytes":138,"sourceHtmlUrl":139},"tests\u002Ftest_rp_api_inventory.py",17480,"https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-migrate\u002Ftests\u002Ftest_rp_api_inventory.py",[141,170,187,203,223,240],{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":153,"publishedAt":51},"01M717A4HFW93XQC7PTXV1QFFH","runpod-vllm","Serve a Hugging Face model with vLLM on Runpod (Hub worker-vllm queue endpoint, load-balancing endpoint on a vllm\u002Fvllm-openai image, or a Pod), sized, CUDA-pinned and verified with a real OpenAI client. Use for any \"deploy\u002Fserve\u002Ftest this LLM with vLLM\" request or a vLLM deployment that will not boot; defer to runpod-serverless-deploy for non-vLLM engines and runpod-endpoint-ops for an endpoint that already works.","https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-vllm\u002FSKILL.md",[147,150],{"categoryNo":148,"categoryName":149,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000011","运维与部署",{"categoryNo":151,"categoryName":152,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",[154,9,155,42,156,157,158,159,160,161,162,39,163,164,165,166,37,167,168,169],"runpod vllm","vLLM","model","worker","image","log","port","request","release","verified","hugging face","openai","face","availability","load-balancing","for",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":177,"publishedAt":51},"01M6CFW2PPRQZ006AVQXGMWPH1","runpodctl","Runpod CLI for managing GPU\u002FCPU workloads from the terminal — pods, serverless endpoints, templates, network volumes, Hub deploys, models, SSH, and file transfer (send\u002Freceive, network-volume upload\u002Fdownload). Use for terminal\u002FCI\u002Fscripting, SSH setup, `doctor`, or when the Runpod MCP tools are not connected; prefer runpod-mcp for structured tool calls in an MCP-enabled session.","https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpodctl\u002FSKILL.md",[176],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[172,178,156,179,49,48,180,44,42,181,182,39,9,183,184,38,37,185,186,34],"doctor","ci","cli","CPU","terminal","ssh setup","Tools","send","windows",{"skillNo":188,"name":9,"nameZh":6,"description":189,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":190,"categories":191,"keywords":193,"publishedAt":51},"01M6CFVTW32TE82Y5NA85H83CK","Start here when the Runpod lane is unclear: picks the skill for a task (runpod-mcp and the journey playbooks it routes to, such as runpod-serverless-deploy, runpod-pod-deploy, runpod-pod-doctor and runpod-cost-audit; runpodctl, runpod-flash, runpod-companion-clis, runpod-usage, runpod-templates, runpod-build-template, runpod-migrate) and indexes two dozen end-to-end worked examples (golden paths). Use it before any multi-step or provisioning task, even when the lane is clear, to check for a matching golden path; for the Runpod MCP tools, runpod-mcp owns the answer contract, connection and tool rules.","https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod\u002FSKILL.md",[192],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[9,49,32,44,39,194,42,195,196,197,198,199,37,200,201,184,202,172,178,34],"start","end","companion","paths","flash","golden","journey","audit","path",{"skillNo":204,"name":205,"nameZh":6,"description":206,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":207,"categories":208,"keywords":210,"publishedAt":51},"01M6CFVK41CSZ4HM1N7TKM3WWY","runpod-usage","Explain Runpod architecture and design choices: Pods versus Serverless, GPU\u002FVRAM, image construction, storage, networking, and workload development. For creating or improving a complete template project use runpod-build-template; execute infrastructure with runpodctl\u002Frunpod-mcp and code-first work with runpod-flash.","https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-usage\u002FSKILL.md",[209],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[211,9,212,172,213,214,215,49,216,158,47,217,218,219,42,169,220,221,157,222],"runpod usage","usage","development","container","design","use","explain","architecture","disk","project","complete","vram",{"skillNo":224,"name":225,"nameZh":6,"description":226,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":227,"categories":228,"keywords":230,"publishedAt":51},"01M6CFVEPPADDF61NC3MC4KCE5","runpod-templates","Find, use, and troubleshoot official Runpod Pod templates, including ComfyUI workflow model metadata repair. For creating or optimizing Dockerfiles and application template projects use runpod-build-template; deploy through runpod-mcp or runpodctl.","https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-templates\u002FSKILL.md",[229],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[231,232,9,172,233,234,235,156,50,49,236,158,48,44,160,40,237,238,239,169],"runpod templates","templates","projects","python3","official","workflow","troubleshoot","png","repair",{"skillNo":241,"name":242,"nameZh":6,"description":243,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":244,"categories":245,"keywords":249,"publishedAt":51},"01M6PP9W1XMN6PT4J4H0XEMTKW","runpod-serverless-deploy","Bring any source (a HuggingFace repo, a ComfyUI workflow, a Hub release, a custom container image, a GGUF repo, a Civitai asset, or a community template) to a proven Runpod Serverless endpoint: pick the engine for the modality and model format, size the GPU from the model's real metadata, provision, warm, prove it with one real job, and hand back the URL with what it bills. Deploys an existing source through the Runpod MCP tools; code-first Python deploys (the Flash dev loop) are out of scope. Use when the user wants a model or worker served on Runpod Serverless, whatever the modality: text generation, speech-to-text or text-to-speech, image or video generation, embeddings, document processing, or their own handler image. For an LLM served with vLLM (Hub worker-vllm, a vllm\u002Fvllm-openai image, or a vLLM Pod), defer to runpod-vllm.","https:\u002F\u002Fgithub.com\u002Frunpod\u002Frunpod-plugins-official\u002Fblob\u002Fmain\u002Fplugins\u002Frunpod\u002Fskills\u002Frunpod-serverless-deploy\u002FSKILL.md",[246,247,248],{"categoryNo":148,"categoryName":149,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":151,"categoryName":152,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[250,9,251,252,253,254,255,256,42,257,258,156,259,157,49,260,261,236,158,34],"runpod serverless deploy","serverless deploy","deploy","serverless","back","Video","community","processing","scope","pick","loop","URL"]