-
Notifications
You must be signed in to change notification settings - Fork 42
feat(github): GitHub repo picker with auto-sync instructions and runtime detection #3095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
tlgimenes
wants to merge
110
commits into
main
Choose a base branch
from
tlgimenes/github-repo-picker
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
110 commits
Select commit
Hold shift + click to select a range
5d7fa7c
feat(schema): add githubRepo to virtual MCP metadata
tlgimenes 95c80b0
feat(tools): add GITHUB_LIST_INSTALLATIONS app-only tool
tlgimenes 06396c7
feat(tools): add GITHUB_LIST_REPOS app-only tool
tlgimenes 6a7d832
feat(tools): register GitHub tools in tool registry
tlgimenes 3f6dc5f
feat(ui): add GitHubRepoDialog for repo selection flow
tlgimenes 6913612
feat(ui): add GitHubRepoButton component
tlgimenes 21b986d
feat(ui): add GitHub button to shell header
tlgimenes 9e5e8e2
fix(ui): use KEYS constants for query keys, fix TS error in dialog
tlgimenes 0e182d7
fix(github): address code review findings
tlgimenes d59d01b
refactor(github): switch to device flow, remove Better Auth dependency
tlgimenes db35d0a
fix(github): use correct Deco CMS GitHub App client ID, remove debug …
tlgimenes f95aecf
feat(ui): start device flow immediately on button click, skip interme…
tlgimenes cbca5f0
feat(ui): auto-copy device code to clipboard, add copy button
tlgimenes 1086edf
feat(github): auto-sync instructions and detect runtime on repo connect
tlgimenes c2fcaf0
feat(vm): add Freestyle VM preview with terminal and dev server iframe
tlgimenes 47019f8
fix(vm): remove web terminal integration, fix error handling and stat…
tlgimenes fe1a327
fix(vm): add w-full to preview component for proper centering
tlgimenes 70e3b0a
fix(vm): deduplicate VMs by (virtualMcpId, userId) pair
tlgimenes 50bcb64
feat(vm): add Deno support, configurable port, detect port from scripts
tlgimenes 1844be7
fix(vm): add open-in-new-tab button, fix domain fallback, guard empty…
tlgimenes a8bbca2
fix(vm): use style.dev domains instead of ports config
tlgimenes 0e88b2d
fix(vm): add socat port proxy for localhost-bound dev servers
tlgimenes 70277d9
fix(vm): use deco.studio domain for VM preview URLs
tlgimenes 602d338
fix(vm): fix systemd exec quoting — split bash -c into separate args
tlgimenes 4862e87
fix(vm): use wrapper shell scripts instead of inline bash -c commands
tlgimenes a603c34
fix(vm): install deno/bun via curl instead of Freestyle integrations
tlgimenes 415bcc9
fix(vm): install runtimes to /usr/local and remove socat proxy
tlgimenes b285cb2
fix(vm): add iframe proxy to strip X-Frame-Options headers
tlgimenes a7f0a05
feat(preview): add visual editor mode with element selection and AI p…
tlgimenes a0968a6
fix(preview): purple highlight color + open chat on visual editor send
tlgimenes 1da98b5
feat(vm): add shared VmEntry/VmMetadata types and patchActiveVms helper
tlgimenes 3649a08
refactor(vm): use named VirtualMCPUpdateData type in patchActiveVms cast
tlgimenes cffb906
feat(vm): read/write active VM entries from Virtual MCP metadata in s…
tlgimenes 2bc2897
test(vm): add VM_START unit tests for cached-VM and new-VM paths
tlgimenes a0a3409
feat(vm): derive vmId from metadata in VM_STOP, fix deletion order
tlgimenes e3efd62
test(vm): add VM_STOP unit tests
tlgimenes 37c4a81
chore(vm): delete in-memory registry (replaced by DB-backed metadata)
tlgimenes 36f6b9d
feat(vm): update VM_STOP call to pass only virtualMcpId
tlgimenes 15eb88c
fix(vm): cast mock.calls to fix Bun type definition limitation in tests
tlgimenes 333c402
feat(vm): add web terminal to VM creation via VmWebTerminal
tlgimenes b233b13
feat(preview): split view with resizable terminal panel
tlgimenes 24b94c5
fix(vm): use pre-installed ttyd instead of VmWebTerminal to avoid ins…
tlgimenes 8669783
fix(vm): manually install ttyd with retries instead of relying on VmW…
tlgimenes b7c477c
fix(vm): use base image ttyd on port 7681 instead of installing manually
tlgimenes 338a748
style(preview): vscode-style thin separator between preview and termi…
tlgimenes 4abb09e
fix(vm): install ttyd to /opt/ instead of /usr/local/bin/ to avoid re…
tlgimenes 7e30115
fix(vm): install ttyd to /tmp/ to avoid overlay fs write restrictions
tlgimenes ffbd896
fix(vm): make terminal read-only and show dev-server logs instead of …
tlgimenes 7a1f1de
fix(preview): detect stale VM (503) and reset to idle state instead o…
tlgimenes 7f26098
fix(vm): server-side liveness probe clears stale VMs returning 503
tlgimenes eaf807e
fix(preview): force-reload iframe when dev server becomes ready to cl…
tlgimenes 509e223
docs: add VM preview UX redesign spec
tlgimenes e63816d
docs: add suspended VM handling to UX redesign spec
tlgimenes 1dc8f37
docs: update VM preview UX spec with critique feedback — fix blockers…
tlgimenes 11d25ec
docs: restore suspended state + smart preview detection with concrete…
tlgimenes 81f3454
docs: add VM_PROBE tool — frontend owns state, backend is proxy only
tlgimenes 7e6f654
docs: add VM preview UX redesign implementation plan
tlgimenes a3bbfdd
feat(vm): add shared requireVmEntry and resolveRuntimeConfig helpers
tlgimenes e6235ff
feat(vm): add VM_PROBE tool for backend-proxied HEAD requests
tlgimenes c8fa84d
feat(vm): add VM_EXEC tool for running install/dev commands in a Free…
tlgimenes 25a97d2
refactor(vm): strip VM_START to infrastructure-only systemd, add isNe…
tlgimenes bc2cb0a
refactor(vm): use requireVmEntry helper in VM_STOP
tlgimenes 599120e
feat(preview): new state machine with terminal-first flow, preview de…
tlgimenes e3857ec
feat(preview): add terminal dropdown with reinstall/restart actions
tlgimenes 67db7ff
feat(preview): show vmId as tooltip on stop button for debugging
tlgimenes 2e1877d
style(preview): remove Terminal label from dropdown, keep icon only
tlgimenes bc169fa
style(preview): use shadcn Tooltip for vmId on stop button
tlgimenes 171b463
fix(preview): add shadcn Tooltip to running-state stop button too
tlgimenes fc88837
style(preview): tooltip text 'Stop VM {vmId}' for clarity
tlgimenes 507e260
refactor(preview): single stable React tree — iframes stay mounted, C…
tlgimenes 4129b20
style(preview): show vmId as badge in address bar, remove tooltip fro…
tlgimenes f64d780
style(preview): add 'Stop VM' tooltip back on stop button
tlgimenes 1888e7a
fix(preview): context-aware status labels — Installing/Resuming/Resta…
tlgimenes 4720046
fix(preview): use 'Connecting...' label for creating state — works fo…
tlgimenes f2fb230
fix(vm): use freestyle vm.start() to resume VMs, vm.stop() for gracef…
tlgimenes 6db3cd3
fix(vm): improve reinstall UX — inline errors, remove installing stat…
tlgimenes 2532e70
refactor(vm): async VM_EXEC, replace ttyd with Node.js log viewer
tlgimenes 842d596
fix(vm): deploy log viewer on resumed VMs via ensureLogViewer
tlgimenes 74ad05d
fix(vm): use base64 encoding for ensureLogViewer, deduplicate log vie…
tlgimenes 9760396
fix(vm): fix \n literal in log separators, use echo for newlines
tlgimenes 09433a5
refactor(vm): use Freestyle runtime integrations, fix systemd exec an…
tlgimenes c7ad9e1
refactor(vm): replace custom log viewer with @freestyle-sh/with-web-t…
tlgimenes 951d59a
refactor(vm): flatten plugins, move constants to module scope, use Sy…
tlgimenes 1bb9740
fix(vm): use md5(virtualMcpId:userId) for VM domain generation
tlgimenes a4937b3
fix(vm): use VmSpec.with() for runtime/terminal integrations
tlgimenes 9867a36
fix(vm): always include VmNodeJs in spec; fix terminal iframe height
tlgimenes 1da6478
fix(vm): prepend runtime bin path for deno/bun exec commands
tlgimenes 1164bb1
fix(vm): clear storage entry before freestyle delete; skip await on v…
tlgimenes 8a5965f
fix(vm): log vm.delete() errors instead of swallowing them
tlgimenes 7492def
refactor(vm): use VmSpec fluent API for repo, files, and systemd config
tlgimenes 54ea974
fix(vm): restart dev server on VM resume instead of assuming it's hea…
tlgimenes 112adb1
fix(vm): fix iframe-proxy startup and exec timeouts
tlgimenes 252e8e1
fix(vm): fix iframe-proxy systemd service and VM deletion
tlgimenes 9684fa5
fix(vm): source NVM from /etc/profile.d/nvm.sh in iframe-proxy wrapper
tlgimenes 5568fae
fix(preview): reuse current thread in visual editor prompt and gate v…
tlgimenes da3cdec
refactor(preview): consolidate duplicate preview iframes into single …
tlgimenes 972eba8
fix(vm): update start.test.ts mock to match VmSpec fluent API refactor
tlgimenes 9bd5744
feat(github): paginate installations and repos list tools; fix test f…
tlgimenes b93091a
fix(vm): clear activeVms on repo change; use sticky+recreate; comment…
tlgimenes 1a8abdc
refactor(vm): rename iframe-proxy to daemon; add SSE endpoint, log ta…
tlgimenes e08f291
test(vm): update start tests for daemon rename; fix stale terminalUrl…
tlgimenes 1702f15
feat(vm): add useVmEvents SSE hook for daemon event stream
tlgimenes f1f4516
feat(vm): add custom VmTerminal component for log rendering
tlgimenes 01c422b
feat(vm): replace polling with daemon SSE; add custom terminal; SSE-b…
tlgimenes e527833
chore(vm): remove VM_PROBE tool; daemon SSE replaces frontend polling
tlgimenes 4fc6a46
fix(vm): deactivate visual editor overlays when switching to interact…
tlgimenes 84e6e14
chore(vm): add ansi-to-html type declarations
tlgimenes 12134c1
feat(vm): add 30-minute idle timeout and fix stop test mock
tlgimenes 7c639d9
feat(vm): use Freestyle Git + GitHub Sync for private repo cloning
tlgimenes 5efc2e8
fix(vm): handle oneshot service exit code in install chain and improv…
tlgimenes File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| /** | ||
| * GITHUB_DEVICE_FLOW_POLL Tool | ||
| * | ||
| * Polls GitHub Device Flow for access token. | ||
| * App-only tool — not visible to AI models. | ||
| */ | ||
|
|
||
| import { z } from "zod"; | ||
| import { defineTool } from "../../core/define-tool"; | ||
| import { requireAuth } from "../../core/mesh-context"; | ||
|
|
||
| const GITHUB_CLIENT_ID = "Iv23liLNDj260RBdPV7p"; | ||
|
|
||
| export const GITHUB_DEVICE_FLOW_POLL = defineTool({ | ||
| name: "GITHUB_DEVICE_FLOW_POLL", | ||
| description: "Poll GitHub Device Flow for the access token.", | ||
| annotations: { | ||
| title: "Poll GitHub Device Flow", | ||
| readOnlyHint: true, | ||
| destructiveHint: false, | ||
| idempotentHint: true, | ||
| openWorldHint: true, | ||
| }, | ||
| _meta: { ui: { visibility: "app" } }, | ||
| inputSchema: z.object({ | ||
| deviceCode: z.string().describe("Device code from the start step"), | ||
| }), | ||
| outputSchema: z.object({ | ||
| status: z.enum(["pending", "success", "expired", "error"]), | ||
| token: z.string().nullable(), | ||
| error: z.string().nullable(), | ||
| }), | ||
|
|
||
| handler: async (input, ctx) => { | ||
| requireAuth(ctx); | ||
| await ctx.access.check(); | ||
|
|
||
| const response = await fetch( | ||
| "https://github.com/login/oauth/access_token", | ||
| { | ||
| method: "POST", | ||
| headers: { | ||
| Accept: "application/json", | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ | ||
| client_id: GITHUB_CLIENT_ID, | ||
| device_code: input.deviceCode, | ||
| grant_type: "urn:ietf:params:oauth:grant-type:device_code", | ||
| }), | ||
| }, | ||
| ); | ||
|
|
||
| if (!response.ok) { | ||
| return { | ||
| status: "error" as const, | ||
| token: null, | ||
| error: `HTTP ${response.status}`, | ||
| }; | ||
| } | ||
|
|
||
| const data = (await response.json()) as { | ||
| access_token?: string; | ||
| error?: string; | ||
| error_description?: string; | ||
| }; | ||
|
|
||
| if (data.access_token) { | ||
| return { | ||
| status: "success" as const, | ||
| token: data.access_token, | ||
| error: null, | ||
| }; | ||
| } | ||
|
|
||
| if (data.error === "authorization_pending" || data.error === "slow_down") { | ||
| return { status: "pending" as const, token: null, error: null }; | ||
| } | ||
|
|
||
| if (data.error === "expired_token") { | ||
| return { | ||
| status: "expired" as const, | ||
| token: null, | ||
| error: "Device code expired", | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| status: "error" as const, | ||
| token: null, | ||
| error: data.error_description ?? data.error ?? "Unknown error", | ||
| }; | ||
| }, | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| /** | ||
| * GITHUB_DEVICE_FLOW_START Tool | ||
| * | ||
| * Starts GitHub Device Flow authentication. | ||
| * App-only tool — not visible to AI models. | ||
| */ | ||
|
|
||
| import { z } from "zod"; | ||
| import { defineTool } from "../../core/define-tool"; | ||
| import { requireAuth } from "../../core/mesh-context"; | ||
|
|
||
| // Deco CMS GitHub App client ID (public, safe to hardcode) | ||
| const GITHUB_CLIENT_ID = "Iv23liLNDj260RBdPV7p"; | ||
|
|
||
| export const GITHUB_DEVICE_FLOW_START = defineTool({ | ||
| name: "GITHUB_DEVICE_FLOW_START", | ||
| description: "Start GitHub Device Flow authentication to get a user code.", | ||
| annotations: { | ||
| title: "Start GitHub Device Flow", | ||
| readOnlyHint: false, | ||
| destructiveHint: false, | ||
| idempotentHint: false, | ||
| openWorldHint: true, | ||
| }, | ||
| _meta: { ui: { visibility: "app" } }, | ||
| inputSchema: z.object({}), | ||
| outputSchema: z.object({ | ||
| userCode: z.string(), | ||
| verificationUri: z.string(), | ||
| deviceCode: z.string(), | ||
| expiresIn: z.number(), | ||
| interval: z.number(), | ||
| }), | ||
|
|
||
| handler: async (_input, ctx) => { | ||
| requireAuth(ctx); | ||
| await ctx.access.check(); | ||
|
|
||
| const response = await fetch("https://github.com/login/device/code", { | ||
| method: "POST", | ||
| headers: { | ||
| Accept: "application/json", | ||
| "Content-Type": "application/json", | ||
| }, | ||
| body: JSON.stringify({ | ||
| client_id: GITHUB_CLIENT_ID, | ||
| scope: "", | ||
| }), | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`GitHub Device Flow error: ${response.status}`); | ||
| } | ||
|
|
||
| const data = (await response.json()) as { | ||
| device_code: string; | ||
| user_code: string; | ||
| verification_uri: string; | ||
| expires_in: number; | ||
| interval: number; | ||
| }; | ||
|
|
||
| return { | ||
| userCode: data.user_code, | ||
| verificationUri: data.verification_uri, | ||
| deviceCode: data.device_code, | ||
| expiresIn: data.expires_in, | ||
| interval: data.interval, | ||
| }; | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| /** | ||
| * GITHUB_GET_FILE_CONTENT Tool | ||
| * | ||
| * Fetches raw file content from a GitHub repository. | ||
| * App-only tool — not visible to AI models. | ||
| */ | ||
|
|
||
| import { z } from "zod"; | ||
| import { defineTool } from "../../core/define-tool"; | ||
| import { requireAuth } from "../../core/mesh-context"; | ||
|
|
||
| export const GITHUB_GET_FILE_CONTENT = defineTool({ | ||
| name: "GITHUB_GET_FILE_CONTENT", | ||
| description: "Fetch raw file content from a GitHub repository by path.", | ||
| annotations: { | ||
| title: "Get GitHub File Content", | ||
| readOnlyHint: true, | ||
| destructiveHint: false, | ||
| idempotentHint: true, | ||
| openWorldHint: true, | ||
| }, | ||
| _meta: { ui: { visibility: "app" } }, | ||
| inputSchema: z.object({ | ||
| token: z.string().describe("GitHub access token"), | ||
| owner: z.string().describe("Repository owner"), | ||
| repo: z.string().describe("Repository name"), | ||
| path: z.string().describe("File path within the repository"), | ||
| }), | ||
| outputSchema: z.object({ | ||
| content: z.string().nullable(), | ||
| found: z.boolean(), | ||
| }), | ||
|
|
||
| handler: async (input, ctx) => { | ||
| requireAuth(ctx); | ||
| await ctx.access.check(); | ||
|
|
||
| const url = `https://raw.githubusercontent.com/${input.owner}/${input.repo}/HEAD/${input.path}`; | ||
| const response = await fetch(url, { | ||
| headers: { | ||
| Authorization: `Bearer ${input.token}`, | ||
| }, | ||
| }); | ||
|
|
||
| if (response.status === 404) { | ||
| return { content: null, found: false }; | ||
| } | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`GitHub API error: ${response.status}`); | ||
| } | ||
|
|
||
| const content = await response.text(); | ||
| return { content, found: true }; | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /** | ||
| * GitHub Tools | ||
| * | ||
| * Tools for GitHub integration (app-only, not visible to AI models). | ||
| */ | ||
|
|
||
| export { GITHUB_LIST_INSTALLATIONS } from "./list-installations"; | ||
| export { GITHUB_LIST_REPOS } from "./list-repos"; | ||
| export { GITHUB_DEVICE_FLOW_START } from "./device-flow-start"; | ||
| export { GITHUB_DEVICE_FLOW_POLL } from "./device-flow-poll"; | ||
| export { GITHUB_GET_FILE_CONTENT } from "./get-file-content"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| /** | ||
| * GITHUB_LIST_INSTALLATIONS Tool | ||
| * | ||
| * Lists GitHub App installations of the Deco CMS app for the current user. | ||
| * App-only tool — not visible to AI models. | ||
| */ | ||
|
|
||
| import { z } from "zod"; | ||
| import { defineTool } from "../../core/define-tool"; | ||
| import { requireAuth } from "../../core/mesh-context"; | ||
|
|
||
| const InstallationSchema = z.object({ | ||
| installationId: z.number(), | ||
| orgName: z.string(), | ||
| avatarUrl: z.string().nullable(), | ||
| }); | ||
|
|
||
| export const GITHUB_LIST_INSTALLATIONS = defineTool({ | ||
| name: "GITHUB_LIST_INSTALLATIONS", | ||
| description: | ||
| "List GitHub App installations of the Deco CMS app for the current user.", | ||
| annotations: { | ||
| title: "List GitHub Installations", | ||
| readOnlyHint: true, | ||
| destructiveHint: false, | ||
| idempotentHint: true, | ||
| openWorldHint: true, | ||
| }, | ||
| _meta: { ui: { visibility: "app" } }, | ||
| inputSchema: z.object({ | ||
| token: z.string().describe("GitHub access token"), | ||
| }), | ||
| outputSchema: z.object({ | ||
| installations: z.array(InstallationSchema), | ||
| }), | ||
|
|
||
| handler: async (input, ctx) => { | ||
| requireAuth(ctx); | ||
| await ctx.access.check(); | ||
|
|
||
| const headers = { | ||
| Authorization: `Bearer ${input.token}`, | ||
| Accept: "application/vnd.github+json", | ||
| "X-GitHub-Api-Version": "2022-11-28", | ||
| }; | ||
|
|
||
| type GitHubInstallation = { | ||
| id: number; | ||
| account: { | ||
| login: string; | ||
| avatar_url: string; | ||
| }; | ||
| }; | ||
|
|
||
| const allInstallations: GitHubInstallation[] = []; | ||
| let nextUrl: string | undefined = | ||
| "https://api.github.com/user/installations?per_page=100"; | ||
|
|
||
| while (nextUrl) { | ||
| const response: Response = await fetch(nextUrl, { headers }); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`GitHub API error: ${response.status}`); | ||
| } | ||
|
|
||
| const data = (await response.json()) as { | ||
| installations: GitHubInstallation[]; | ||
| }; | ||
| allInstallations.push(...data.installations); | ||
|
|
||
| // Parse Link header for next page | ||
| nextUrl = undefined; | ||
| const link = response.headers.get("link"); | ||
| if (link) { | ||
| const next = link | ||
| .split(",") | ||
| .find((part: string) => part.includes('rel="next"')); | ||
| if (next) { | ||
| const match = next.match(/<([^>]+)>/); | ||
| if (match?.[1]) nextUrl = match[1]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const installations = allInstallations.map((inst) => ({ | ||
| installationId: inst.id, | ||
| orgName: inst.account.login, | ||
| avatarUrl: inst.account.avatar_url, | ||
| })); | ||
|
|
||
| return { installations }; | ||
| }, | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1:
slow_downis treated the same asauthorization_pending, so callers cannot apply the required poll backoff and may keep hitting device-flow rate limits.Prompt for AI agents