rovecode 0.3.3 → 0.4.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +512 -0
- package/README.md +33 -48
- package/bin/rovecode.js +24 -0
- package/dist/cli/app-dybnr56b.js +2 -0
- package/dist/cli/ask-user-p8hq4xgj.js +2 -0
- package/dist/cli/auth-login-ewpgw5sm.js +2 -0
- package/dist/cli/auth-m8p9grty.js +2 -0
- package/dist/cli/bench-xv3ypwev.js +9 -0
- package/dist/cli/catalog-737wb2s0.js +2 -0
- package/dist/cli/cli-arhg40m0.js +2 -0
- package/dist/cli/client-cf2pxx8q.js +2 -0
- package/dist/cli/commands-3p7e4xxs.js +2 -0
- package/dist/cli/connect-3q93d7cb.js +2 -0
- package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
- package/dist/cli/context-report-hbw9zfes.js +2 -0
- package/dist/cli/count-remote-mby98cd0.js +2 -0
- package/dist/cli/design-122y0axd.js +2 -0
- package/dist/cli/dispatch-b4egzvvh.js +2 -0
- package/dist/cli/doctor-x4jkv72e.js +3 -0
- package/dist/cli/executor-ftvg6tsy.js +2 -0
- package/dist/cli/export-pdgdhkch.js +2 -0
- package/dist/cli/files-cez9a96p.js +2 -0
- package/dist/cli/gauntlet-r3xxaszc.js +2 -0
- package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
- package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
- package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
- package/dist/cli/hashline-ewg5hbe3.js +2 -0
- package/dist/cli/http-n0kehsk8.js +5 -0
- package/dist/cli/index-z5qt1s76.js +2 -0
- package/dist/cli/install-80mp63kx.js +2 -0
- package/dist/cli/loop-12twjcat.js +2 -0
- package/dist/cli/main-01pv9206.js +4 -0
- package/dist/cli/main-0jys2ccn.js +3 -0
- package/dist/cli/main-1ztz6fkj.js +10 -0
- package/dist/cli/main-23q7cmww.js +9 -0
- package/dist/cli/main-2rzbexn2.js +3 -0
- package/dist/cli/main-2wyax8k9.js +9 -0
- package/dist/cli/main-2yeveeve.js +6 -0
- package/dist/cli/main-2z3dek0b.js +3 -0
- package/dist/cli/main-2zgsknth.js +3 -0
- package/dist/cli/main-45ejth3a.js +4 -0
- package/dist/cli/main-45rn3trk.js +22 -0
- package/dist/cli/main-4p4e2w7x.js +4 -0
- package/dist/cli/main-4y0tnfpa.js +16 -0
- package/dist/cli/main-5py0rkmc.js +4 -0
- package/dist/cli/main-6dtqmbt6.js +7 -0
- package/dist/cli/main-6h9x282m.js +4 -0
- package/dist/cli/main-6vjeds42.js +3 -0
- package/dist/cli/main-78gq4bt9.js +6 -0
- package/dist/cli/main-7jd5vh3x.js +4 -0
- package/dist/cli/main-7kt6r53y.js +4 -0
- package/dist/cli/main-8c1tbazx.js +58 -0
- package/dist/cli/main-9a9rnh47.js +19 -0
- package/dist/cli/main-9ht36z12.js +3 -0
- package/dist/cli/main-a2yfvcy9.js +7 -0
- package/dist/cli/main-a3f51n0x.js +5 -0
- package/dist/cli/main-b8zq261k.js +3 -0
- package/dist/cli/main-bxtvnf6d.js +13 -0
- package/dist/cli/main-edxc3yzt.js +4 -0
- package/dist/cli/main-evgz4mp5.js +21 -0
- package/dist/cli/main-f33fc5je.js +9 -0
- package/dist/cli/main-fvnpq46y.js +12 -0
- package/dist/cli/main-gbbty4d4.js +3 -0
- package/dist/cli/main-gth53dnt.js +25 -0
- package/dist/cli/main-hqbz10aw.js +9 -0
- package/dist/cli/main-hrrvcfan.js +38 -0
- package/dist/cli/main-hzwtsb2m.js +5 -0
- package/dist/cli/main-j7ttv0sd.js +34 -0
- package/dist/cli/main-jak598k9.js +5 -0
- package/dist/cli/main-kba6zeyd.js +6 -0
- package/dist/cli/main-kwwsz6rq.js +3 -0
- package/dist/cli/main-m8vm17zq.js +3 -0
- package/dist/cli/main-mg4f96e1.js +3 -0
- package/dist/cli/main-mg9b20ac.js +18 -0
- package/dist/cli/main-mgb9ccnx.js +3 -0
- package/dist/cli/main-mjt2p7aj.js +3 -0
- package/dist/cli/main-n6qrdbmy.js +3 -0
- package/dist/cli/main-na7wse0x.js +5 -0
- package/dist/cli/main-nqveez48.js +4 -0
- package/dist/cli/main-ntqef02r.js +10 -0
- package/dist/cli/main-nvc3yjay.js +136 -0
- package/dist/cli/main-p0cfn6nr.js +16 -0
- package/dist/cli/main-qj2djy17.js +19 -0
- package/dist/cli/main-qsevpgsv.js +3 -0
- package/dist/cli/main-qvarybsp.js +3 -0
- package/dist/cli/main-rebtt91r.js +5 -0
- package/dist/cli/main-rpg7h8mb.js +3 -0
- package/dist/cli/main-rsy72qmw.js +15 -0
- package/dist/cli/main-rvetps99.js +18 -0
- package/dist/cli/main-s4bb0jav.js +3 -0
- package/dist/cli/main-s9v8k74e.js +3 -0
- package/dist/cli/main-tjvwmscs.js +3 -0
- package/dist/cli/main-tkgarpjj.js +4 -0
- package/dist/cli/main-v8y60bb2.js +3 -0
- package/dist/cli/main-vhrrq337.js +3 -0
- package/dist/cli/main-vp2dfb7s.js +4 -0
- package/dist/cli/main-vqbr22sz.js +8 -0
- package/dist/cli/main-vxnwe5xx.js +18 -0
- package/dist/cli/main-wgph00xf.js +5 -0
- package/dist/cli/main-wk2csfnj.js +5 -0
- package/dist/cli/main-wm997zjx.js +3 -0
- package/dist/cli/main-wpkyraxh.js +3 -0
- package/dist/cli/main-wqt32p5x.js +4 -0
- package/dist/cli/main-x9ct6y1a.js +3 -0
- package/dist/cli/main-xfekqh9m.js +7 -0
- package/dist/cli/main-xt9zc3n6.js +7 -0
- package/dist/cli/main-xx2z3zh5.js +4 -0
- package/dist/cli/main-y5c82rxr.js +3 -0
- package/dist/cli/main-yrjt2sqt.js +14 -0
- package/dist/cli/main-ys6zj3yr.js +3 -0
- package/dist/cli/main-ywbxshqc.js +8 -0
- package/dist/cli/main-z13755t8.js +25 -0
- package/dist/cli/main-zc7pyrbj.js +4 -0
- package/dist/cli/main.js +279 -0
- package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
- package/dist/cli/mcp-login-bthtfpt7.js +2 -0
- package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
- package/dist/cli/notify-54v5z9dz.js +2 -0
- package/dist/cli/oauth-g5gme95c.js +2 -0
- package/dist/cli/output-satndjap.js +16 -0
- package/dist/cli/profiles-sfhpbq3m.js +2 -0
- package/dist/cli/provider-config-hv3xtdt4.js +2 -0
- package/dist/cli/provider-kwzq6g84.js +2 -0
- package/dist/cli/registry-fh0hdnyn.js +2 -0
- package/dist/cli/registry-y1y8e94r.js +2 -0
- package/dist/cli/repl-t4z03mqq.js +11 -0
- package/dist/cli/resume-fqt4chg8.js +2 -0
- package/dist/cli/run-flags-rysbag9t.js +2 -0
- package/dist/cli/runtime-j19fjbsa.js +2 -0
- package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
- package/dist/cli/server-r0b6bksk.js +5 -0
- package/dist/cli/session-arg-txmn5g4x.js +2 -0
- package/dist/cli/session-ed250d9j.js +2 -0
- package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
- package/dist/cli/settings-y9rzcqx8.js +2 -0
- package/dist/cli/setup-jmbr11j0.js +2 -0
- package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
- package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
- package/dist/cli/smoke-1bg937kx.js +8 -0
- package/dist/cli/start-chat-p01cdks3.js +12 -0
- package/dist/cli/stream-4wmyaypz.js +2 -0
- package/dist/cli/task-eg4s093s.js +2 -0
- package/dist/cli/tasks-12v9rr9k.js +2 -0
- package/dist/cli/thinking-a5ngvqyh.js +2 -0
- package/dist/cli/todo-1wxpcecx.js +2 -0
- package/dist/cli/tools-2ftsya7w.js +2 -0
- package/dist/cli/tools-x1tj4fxm.js +2 -0
- package/dist/cli/trust-cmd-hccxehzb.js +2 -0
- package/dist/cli/update-check-ygt3vd7m.js +2 -0
- package/dist/cli/update-cmd-v23qhr8c.js +2 -0
- package/dist/cli/voice-g1gtck92.js +2 -0
- package/dist/cli/webfetch-0nnrjgb5.js +2 -0
- package/dist/cli/websearch-f0vr2p7d.js +2 -0
- package/dist/cli/workspace-9rq1w4ta.js +2 -0
- package/dist/lib/index.js +62 -0
- package/dist/lib/models-index.json +1 -0
- package/dist/lib/plugins.js +6 -0
- package/dist/lib/providers.js +17 -0
- package/dist/lib/public-api.js +20 -0
- package/dist/rovecode.exe +4 -0
- package/package.json +83 -72
- package/bin/rovecode.ts +0 -21
- package/src/acp/server.ts +0 -374
- package/src/cli/auth-login.ts +0 -122
- package/src/cli/connect.ts +0 -244
- package/src/cli/context-cmd.ts +0 -199
- package/src/cli/dispatch.ts +0 -82
- package/src/cli/doctor.ts +0 -362
- package/src/cli/export.ts +0 -276
- package/src/cli/help.ts +0 -293
- package/src/cli/is-tui-invocation.ts +0 -8
- package/src/cli/main.ts +0 -583
- package/src/cli/market-cmd.ts +0 -658
- package/src/cli/mcp-login.ts +0 -141
- package/src/cli/mcp-market-cmd.ts +0 -302
- package/src/cli/output.ts +0 -382
- package/src/cli/repl.ts +0 -250
- package/src/cli/repomap-root.ts +0 -14
- package/src/cli/resume.ts +0 -57
- package/src/cli/run-flags.ts +0 -43
- package/src/cli/run-limits.ts +0 -78
- package/src/cli/runtime.ts +0 -931
- package/src/cli/session-arg.ts +0 -30
- package/src/cli/sessions-cmd.ts +0 -145
- package/src/cli/setup.ts +0 -153
- package/src/cli/skills-cmd.ts +0 -194
- package/src/cli/start-chat.ts +0 -65
- package/src/cli/trust-cmd.ts +0 -52
- package/src/coding/bash.ts +0 -148
- package/src/coding/checkpoints.ts +0 -327
- package/src/coding/diff.ts +0 -138
- package/src/coding/files.ts +0 -341
- package/src/coding/hashline.ts +0 -274
- package/src/coding/lsp-gate.ts +0 -254
- package/src/coding/lsp-servers.ts +0 -147
- package/src/coding/lsp.ts +0 -283
- package/src/coding/repomap-cache.ts +0 -99
- package/src/coding/repomap-files.ts +0 -192
- package/src/coding/repomap.ts +0 -481
- package/src/core/agents.ts +0 -255
- package/src/core/compaction.ts +0 -259
- package/src/core/config.ts +0 -289
- package/src/core/context-report.ts +0 -228
- package/src/core/context.ts +0 -60
- package/src/core/count-remote.ts +0 -107
- package/src/core/execpolicy-rules.ts +0 -196
- package/src/core/execpolicy.ts +0 -385
- package/src/core/executor.ts +0 -454
- package/src/core/guardrails.ts +0 -400
- package/src/core/hooks.ts +0 -411
- package/src/core/images.ts +0 -230
- package/src/core/intro.ts +0 -266
- package/src/core/loop.ts +0 -567
- package/src/core/modes.ts +0 -372
- package/src/core/orchestrator.ts +0 -245
- package/src/core/proc-group.ts +0 -48
- package/src/core/project-trust.ts +0 -98
- package/src/core/reflection.ts +0 -165
- package/src/core/sandbox-config.ts +0 -186
- package/src/core/session-id.ts +0 -24
- package/src/core/session-images.ts +0 -73
- package/src/core/session-ops.ts +0 -183
- package/src/core/session-text.ts +0 -29
- package/src/core/session.ts +0 -469
- package/src/core/settings.ts +0 -170
- package/src/core/tasks.ts +0 -646
- package/src/core/token-scale.ts +0 -108
- package/src/core/tools.ts +0 -309
- package/src/core/trust.ts +0 -104
- package/src/core/types.ts +0 -330
- package/src/core/update-check.ts +0 -171
- package/src/core/usage.ts +0 -204
- package/src/core/validate.ts +0 -121
- package/src/core/verify-gate.ts +0 -159
- package/src/core/verify.ts +0 -236
- package/src/core/voice.ts +0 -158
- package/src/core/win-job.ts +0 -183
- package/src/core/workspace.ts +0 -184
- package/src/design/audit.ts +0 -797
- package/src/design/direction.ts +0 -190
- package/src/design/rules.ts +0 -157
- package/src/eval/bench.ts +0 -150
- package/src/eval/gauntlet-runner.ts +0 -215
- package/src/eval/gauntlet-support.ts +0 -84
- package/src/eval/gauntlet-wave3.ts +0 -269
- package/src/eval/gauntlet-wave4.ts +0 -217
- package/src/eval/gauntlet.ts +0 -253
- package/src/index.ts +0 -17
- package/src/lanes/agy.ts +0 -95
- package/src/lanes/approval.ts +0 -24
- package/src/lanes/claude.ts +0 -129
- package/src/lanes/codex.ts +0 -127
- package/src/lanes/events.ts +0 -130
- package/src/lanes/job.ts +0 -142
- package/src/lanes/opencode.ts +0 -122
- package/src/lanes/process.ts +0 -184
- package/src/lanes/progress.ts +0 -183
- package/src/lanes/registry.ts +0 -178
- package/src/lanes/runner.ts +0 -124
- package/src/lanes/types.ts +0 -112
- package/src/market/catalogs/mcp-docs.json +0 -111
- package/src/market/catalogs/plugins.json +0 -111
- package/src/market/catalogs/skills.json +0 -478
- package/src/market/clone.ts +0 -72
- package/src/market/context-cost.ts +0 -121
- package/src/market/digest.ts +0 -106
- package/src/market/index.ts +0 -22
- package/src/market/install.ts +0 -578
- package/src/market/manifest.ts +0 -187
- package/src/market/prereq.ts +0 -145
- package/src/market/registry.ts +0 -363
- package/src/market/resolve.ts +0 -111
- package/src/market/types.ts +0 -236
- package/src/market/validate.ts +0 -227
- package/src/mcp/client.ts +0 -449
- package/src/mcp/config.ts +0 -252
- package/src/mcp/local-package.ts +0 -211
- package/src/mcp/market-catalog.ts +0 -84
- package/src/mcp/market-install.ts +0 -289
- package/src/mcp/market.ts +0 -362
- package/src/mcp/oauth.ts +0 -251
- package/src/mcp/prompts-resources.ts +0 -249
- package/src/mcp/shared.ts +0 -149
- package/src/mcp/status.ts +0 -67
- package/src/mcp/tools.ts +0 -275
- package/src/mcp/transport.ts +0 -122
- package/src/mcp/trust.ts +0 -25
- package/src/memory/blocks.ts +0 -278
- package/src/memory/recall.ts +0 -355
- package/src/memory/scope.ts +0 -182
- package/src/memory/store.ts +0 -105
- package/src/memory/tools.ts +0 -99
- package/src/plugins/cli.ts +0 -119
- package/src/plugins/discover.ts +0 -108
- package/src/plugins/index.ts +0 -50
- package/src/plugins/install.ts +0 -184
- package/src/plugins/load.ts +0 -124
- package/src/plugins/manifest.ts +0 -92
- package/src/plugins/state.ts +0 -83
- package/src/providers/auth.ts +0 -408
- package/src/providers/cache.ts +0 -223
- package/src/providers/catalog-local.ts +0 -160
- package/src/providers/catalog.ts +0 -421
- package/src/providers/middleware-context.ts +0 -86
- package/src/providers/middleware.ts +0 -373
- package/src/providers/model-list.ts +0 -23
- package/src/providers/oauth/common.ts +0 -105
- package/src/providers/oauth/device-code.ts +0 -107
- package/src/providers/oauth/github-copilot.ts +0 -146
- package/src/providers/oauth/loopback.ts +0 -158
- package/src/providers/oauth/openai.ts +0 -163
- package/src/providers/oauth/openrouter.ts +0 -89
- package/src/providers/oauth/pkce.ts +0 -45
- package/src/providers/oauth/registry.ts +0 -39
- package/src/providers/oauth/seam.ts +0 -89
- package/src/providers/profile-glm53.ts +0 -111
- package/src/providers/profile-sonnet5-persona.ts +0 -65
- package/src/providers/profile-sonnet5-voice.ts +0 -23
- package/src/providers/profiles.ts +0 -156
- package/src/providers/provider-config.ts +0 -311
- package/src/providers/registry.ts +0 -333
- package/src/providers/responses.ts +0 -209
- package/src/providers/retry.ts +0 -234
- package/src/providers/router.ts +0 -294
- package/src/providers/sse.ts +0 -26
- package/src/providers/stream-errors.ts +0 -117
- package/src/providers/stream.ts +0 -566
- package/src/providers/thinking.ts +0 -189
- package/src/providers/wire-messages.ts +0 -129
- package/src/providers/wire-responses.ts +0 -79
- package/src/providers/wire-select.ts +0 -53
- package/src/server/http.ts +0 -291
- package/src/server/openapi.ts +0 -246
- package/src/sextant/card-hits.ts +0 -102
- package/src/sextant/card-keys.ts +0 -55
- package/src/sextant/context-source.ts +0 -157
- package/src/sextant/crew-cards.ts +0 -350
- package/src/sextant/draw-agents.ts +0 -273
- package/src/sextant/draw-code.ts +0 -388
- package/src/sextant/draw-context.ts +0 -222
- package/src/sextant/draw-frame.ts +0 -164
- package/src/sextant/draw-market.ts +0 -573
- package/src/sextant/draw-messages.ts +0 -386
- package/src/sextant/draw-pet.ts +0 -230
- package/src/sextant/draw-plan.ts +0 -187
- package/src/sextant/draw-tabs.ts +0 -85
- package/src/sextant/draw-util.ts +0 -65
- package/src/sextant/draw-wizard.ts +0 -378
- package/src/sextant/engine.ts +0 -230
- package/src/sextant/frame-hits.ts +0 -25
- package/src/sextant/frame.ts +0 -101
- package/src/sextant/git-status.ts +0 -197
- package/src/sextant/grid.ts +0 -59
- package/src/sextant/input.ts +0 -119
- package/src/sextant/keys.ts +0 -521
- package/src/sextant/layout.ts +0 -86
- package/src/sextant/local-commands.ts +0 -169
- package/src/sextant/market-source.ts +0 -287
- package/src/sextant/mentions.ts +0 -200
- package/src/sextant/message-hits.ts +0 -26
- package/src/sextant/model.ts +0 -387
- package/src/sextant/overlays.ts +0 -456
- package/src/sextant/panel-hits.ts +0 -38
- package/src/sextant/pet.ts +0 -399
- package/src/sextant/screen.ts +0 -324
- package/src/sextant/scroll-hits.ts +0 -66
- package/src/sextant/scrollbar.ts +0 -82
- package/src/sextant/sextant-bridge.ts +0 -174
- package/src/sextant/sextant-cards.ts +0 -142
- package/src/sextant/sextant-diff-base.ts +0 -63
- package/src/sextant/sextant-files.ts +0 -154
- package/src/sextant/sextant-frame-loop.ts +0 -335
- package/src/sextant/sextant-renderer.ts +0 -574
- package/src/sextant/sextant-repo.ts +0 -140
- package/src/sextant/theme.ts +0 -66
- package/src/sextant/tool-rows.ts +0 -189
- package/src/sextant/types.ts +0 -493
- package/src/skills/index.ts +0 -387
- package/src/skills/pack.ts +0 -220
- package/src/skills/spec.ts +0 -162
- package/src/skills/tools.ts +0 -69
- package/src/skills/versioned.ts +0 -227
- package/src/telemetry/otel-export.ts +0 -122
- package/src/telemetry/otel-lanes.ts +0 -89
- package/src/telemetry/otel-logs.ts +0 -131
- package/src/telemetry/otel-metrics.ts +0 -136
- package/src/telemetry/otel.ts +0 -397
- package/src/telemetry/otlp.ts +0 -76
- package/src/tools/ask-user.ts +0 -156
- package/src/tools/bash-bg.ts +0 -94
- package/src/tools/bash-jobs.ts +0 -237
- package/src/tools/design.ts +0 -151
- package/src/tools/evalcell.ts +0 -338
- package/src/tools/html-text.ts +0 -139
- package/src/tools/provider.ts +0 -149
- package/src/tools/task.ts +0 -250
- package/src/tools/todo.ts +0 -320
- package/src/tools/webfetch.ts +0 -332
- package/src/tools/websearch.ts +0 -359
- package/src/tui/agents-cmd.ts +0 -41
- package/src/tui/app.ts +0 -749
- package/src/tui/attach.ts +0 -127
- package/src/tui/boot-notes.ts +0 -41
- package/src/tui/builtin-prompts.ts +0 -59
- package/src/tui/checkpoints-cmd.ts +0 -70
- package/src/tui/clipboard-image.ts +0 -81
- package/src/tui/clipboard.ts +0 -78
- package/src/tui/commands.ts +0 -283
- package/src/tui/config-view.ts +0 -53
- package/src/tui/context-cmds.ts +0 -282
- package/src/tui/cost.ts +0 -108
- package/src/tui/crash-guard.ts +0 -173
- package/src/tui/focus-terminal.ts +0 -34
- package/src/tui/git-cmds.ts +0 -273
- package/src/tui/git-plain.ts +0 -58
- package/src/tui/info-cmd.ts +0 -150
- package/src/tui/input-plain.ts +0 -76
- package/src/tui/mcp-cmd.ts +0 -128
- package/src/tui/memory-note.ts +0 -77
- package/src/tui/modes-cmd.ts +0 -45
- package/src/tui/notify-seq.ts +0 -100
- package/src/tui/notify.ts +0 -318
- package/src/tui/overlays.ts +0 -97
- package/src/tui/pi-renderer.ts +0 -428
- package/src/tui/providers-cmd.ts +0 -377
- package/src/tui/reasoning-view.ts +0 -56
- package/src/tui/renderer.ts +0 -128
- package/src/tui/replay-marker.ts +0 -29
- package/src/tui/session-cmd.ts +0 -148
- package/src/tui/session-manage.ts +0 -95
- package/src/tui/sextant-attach.ts +0 -102
- package/src/tui/sextant-io.ts +0 -202
- package/src/tui/sextant-smoke.ts +0 -110
- package/src/tui/shell-cmd.ts +0 -158
- package/src/tui/smoke.ts +0 -72
- package/src/tui/staged-terminal.ts +0 -50
- package/src/tui/startup.ts +0 -12
- package/src/tui/theme.ts +0 -59
- package/src/tui/todo-label.ts +0 -7
- package/src/tui/trust-card.ts +0 -107
- package/src/tui/tui-commands.ts +0 -87
- package/tsconfig.json +0 -30
- package/vendor/pi-tui/LICENSE +0 -21
- package/vendor/pi-tui/PATCHES.md +0 -12
- package/vendor/pi-tui/PROVENANCE.md +0 -12
- package/vendor/pi-tui/README.upstream.md +0 -854
- package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
- package/vendor/pi-tui/src/autocomplete.ts +0 -827
- package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
- package/vendor/pi-tui/src/components/box.ts +0 -138
- package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
- package/vendor/pi-tui/src/components/editor.ts +0 -2364
- package/vendor/pi-tui/src/components/h-stack.ts +0 -45
- package/vendor/pi-tui/src/components/image.ts +0 -128
- package/vendor/pi-tui/src/components/input.ts +0 -448
- package/vendor/pi-tui/src/components/loader.ts +0 -93
- package/vendor/pi-tui/src/components/markdown.ts +0 -1016
- package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
- package/vendor/pi-tui/src/components/select-list.ts +0 -230
- package/vendor/pi-tui/src/components/settings-list.ts +0 -277
- package/vendor/pi-tui/src/components/spacer.ts +0 -29
- package/vendor/pi-tui/src/components/stack.ts +0 -155
- package/vendor/pi-tui/src/components/text.ts +0 -108
- package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
- package/vendor/pi-tui/src/components/v-stack.ts +0 -34
- package/vendor/pi-tui/src/editor-component.ts +0 -75
- package/vendor/pi-tui/src/fuzzy.ts +0 -138
- package/vendor/pi-tui/src/index.ts +0 -149
- package/vendor/pi-tui/src/keybindings.ts +0 -321
- package/vendor/pi-tui/src/keys.ts +0 -1402
- package/vendor/pi-tui/src/kill-ring.ts +0 -47
- package/vendor/pi-tui/src/latex.ts +0 -1381
- package/vendor/pi-tui/src/layout-node.ts +0 -52
- package/vendor/pi-tui/src/layout.ts +0 -411
- package/vendor/pi-tui/src/native-modifiers.ts +0 -60
- package/vendor/pi-tui/src/native-module-path.ts +0 -32
- package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
- package/vendor/pi-tui/src/terminal-colors.ts +0 -74
- package/vendor/pi-tui/src/terminal-image.ts +0 -701
- package/vendor/pi-tui/src/terminal.ts +0 -554
- package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
- package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
- package/vendor/pi-tui/src/tui.ts +0 -1264
- package/vendor/pi-tui/src/undo-stack.ts +0 -29
- package/vendor/pi-tui/src/utils.ts +0 -1327
- package/vendor/pi-tui/src/word-navigation.ts +0 -118
- package/vendor/pi-tui/test/test-themes.ts +0 -39
- package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
- /package/{src/providers → dist/cli}/models-index.json +0 -0
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/** OAuth 2.0 device-authorization polling (RFC 8628 §3.4–3.5) — VENDORED from pi.
|
|
2
|
-
*
|
|
3
|
-
* Source: earendil-works/pi, packages/ai/src/auth/oauth/device-code.ts (snapshot under
|
|
4
|
-
* research/source_snapshots/earendil-works-pi, @earendil-works/ai 0.84.4). License: MIT —
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2025 Mario Zechner
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
9
|
-
* associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
10
|
-
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
11
|
-
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
|
12
|
-
* furnished to do so, subject to the following conditions: The above copyright notice and this
|
|
13
|
-
* permission notice shall be included in all copies or substantial portions of the Software.
|
|
14
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
|
15
|
-
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
16
|
-
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
17
|
-
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
19
|
-
*
|
|
20
|
-
* Local changes (port #66): `sleep` and `now` are injectable (the tests drive pending → slow_down →
|
|
21
|
-
* complete with a recording sleep and a fixed clock, and pin the interval arithmetic); the default
|
|
22
|
-
* sleep is pi's abortableSleep; the messages lose the WSL clock-drift paragraph's second sentence.
|
|
23
|
-
* Semantics unchanged: a missing `interval` means 5 s (§3.2), `slow_down` adds 5 s (§3.5) unless the
|
|
24
|
-
* server states a new interval (GitHub does), never faster than 1 s, deadline = `expires_in`. */
|
|
25
|
-
|
|
26
|
-
const CANCEL_MESSAGE = "login cancelled";
|
|
27
|
-
const TIMEOUT_MESSAGE = "device flow timed out";
|
|
28
|
-
const SLOW_DOWN_TIMEOUT_MESSAGE = "device flow timed out after one or more slow_down responses (often clock drift in a WSL/VM — sync the clock and retry)";
|
|
29
|
-
const MINIMUM_INTERVAL_MS = 1000;
|
|
30
|
-
/** RFC 8628 §3.2: when the server omits `interval` the client polls every 5 s */
|
|
31
|
-
const DEFAULT_POLL_INTERVAL_SECONDS = 5;
|
|
32
|
-
/** RFC 8628 §3.5: `slow_down` means the interval grows by 5 s */
|
|
33
|
-
const SLOW_DOWN_INTERVAL_INCREMENT_MS = 5000;
|
|
34
|
-
|
|
35
|
-
type DeviceCodeIncompletePollResult =
|
|
36
|
-
| { status: "pending" }
|
|
37
|
-
| { status: "slow_down"; intervalSeconds?: number }
|
|
38
|
-
| { status: "failed"; message: string };
|
|
39
|
-
|
|
40
|
-
export type DeviceCodePollResult<T> = DeviceCodeIncompletePollResult | { status: "complete"; value: T };
|
|
41
|
-
|
|
42
|
-
export interface DeviceCodePollOptions<T> {
|
|
43
|
-
intervalSeconds?: number;
|
|
44
|
-
expiresInSeconds?: number;
|
|
45
|
-
/** wait one interval before the first poll (GitHub's flow; the user has not even seen the code yet) */
|
|
46
|
-
waitBeforeFirstPoll?: boolean;
|
|
47
|
-
poll: () => Promise<DeviceCodePollResult<T>>;
|
|
48
|
-
signal: AbortSignal;
|
|
49
|
-
/** injectable timer (default abortableSleep) */
|
|
50
|
-
sleep?: (ms: number, signal: AbortSignal) => Promise<void>;
|
|
51
|
-
/** injectable clock (default Date.now) */
|
|
52
|
-
now?: () => number;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function abortableSleep(ms: number, signal: AbortSignal, cancelMessage: string = CANCEL_MESSAGE): Promise<void> {
|
|
56
|
-
return new Promise((resolve, reject) => {
|
|
57
|
-
if (signal.aborted) {
|
|
58
|
-
reject(new Error(cancelMessage));
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
const onAbort = () => {
|
|
62
|
-
clearTimeout(timeout);
|
|
63
|
-
reject(new Error(cancelMessage));
|
|
64
|
-
};
|
|
65
|
-
const timeout = setTimeout(() => {
|
|
66
|
-
signal.removeEventListener("abort", onAbort);
|
|
67
|
-
resolve();
|
|
68
|
-
}, ms);
|
|
69
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export async function pollDeviceCodeFlow<T>(options: DeviceCodePollOptions<T>): Promise<T> {
|
|
74
|
-
const now = options.now ?? Date.now;
|
|
75
|
-
const sleep = options.sleep ?? abortableSleep;
|
|
76
|
-
const deadline = typeof options.expiresInSeconds === "number" ? now() + options.expiresInSeconds * 1000 : Number.POSITIVE_INFINITY;
|
|
77
|
-
let intervalMs = Math.max(MINIMUM_INTERVAL_MS, Math.floor((options.intervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS) * 1000));
|
|
78
|
-
|
|
79
|
-
let slowDownResponses = 0;
|
|
80
|
-
if (options.waitBeforeFirstPoll) {
|
|
81
|
-
const remainingMs = deadline - now();
|
|
82
|
-
if (remainingMs > 0) await sleep(Math.min(intervalMs, remainingMs), options.signal);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
while (now() < deadline) {
|
|
86
|
-
if (options.signal.aborted) throw new Error(CANCEL_MESSAGE);
|
|
87
|
-
|
|
88
|
-
const result = await options.poll();
|
|
89
|
-
if (result.status === "complete") return result.value;
|
|
90
|
-
if (result.status === "failed") throw new Error(result.message);
|
|
91
|
-
if (result.status === "slow_down") {
|
|
92
|
-
slowDownResponses += 1;
|
|
93
|
-
// Use the server-provided interval when given (GitHub reports the new required minimum in
|
|
94
|
-
// `interval`); trusting only a client-tracked value risks polling early forever under WSL/VM
|
|
95
|
-
// clock drift. Otherwise apply RFC 8628 §3.5: increase by 5 seconds.
|
|
96
|
-
intervalMs = typeof result.intervalSeconds === "number" && Number.isFinite(result.intervalSeconds) && result.intervalSeconds > 0
|
|
97
|
-
? Math.max(MINIMUM_INTERVAL_MS, Math.floor(result.intervalSeconds * 1000))
|
|
98
|
-
: Math.max(MINIMUM_INTERVAL_MS, intervalMs + SLOW_DOWN_INTERVAL_INCREMENT_MS);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const remainingMs = deadline - now();
|
|
102
|
-
if (remainingMs <= 0) break;
|
|
103
|
-
await sleep(Math.min(intervalMs, remainingMs), options.signal);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
throw new Error(slowDownResponses > 0 ? SLOW_DOWN_TIMEOUT_MESSAGE : TIMEOUT_MESSAGE);
|
|
107
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/** GitHub Copilot OAuth (port #66): the GitHub device-code flow, then the Copilot token exchange.
|
|
2
|
-
*
|
|
3
|
-
* Ported from pi packages/ai/src/auth/oauth/github-copilot.ts (earendil-works/pi, MIT — Copyright (c)
|
|
4
|
-
* 2025 Mario Zechner; snapshot under research/source_snapshots/earendil-works-pi). Kept as pi has it:
|
|
5
|
-
* - POST /login/device/code with the Copilot Chat client id + scope read:user → user_code +
|
|
6
|
-
* verification_uri + interval + expires_in; the verification URI must parse as http(s) (never handed
|
|
7
|
-
* to an opener otherwise);
|
|
8
|
-
* - poll POST /login/oauth/access_token (grant urn:ietf:params:oauth:grant-type:device_code) through
|
|
9
|
-
* pollDeviceCodeFlow, waiting one interval before the first poll; `authorization_pending` → pending,
|
|
10
|
-
* `slow_down` → the server's new `interval`; a 200 body with `error` is how GitHub reports both;
|
|
11
|
-
* - the GitHub OAuth token is the long-lived REFRESH credential; GET api.github.com/copilot_internal/
|
|
12
|
-
* v2/token (Bearer <github token> + the Copilot editor headers) mints the short-lived Copilot
|
|
13
|
-
* token (`token`, `expires_at` seconds) — stored as `access` with a 5-minute early expiry;
|
|
14
|
-
* - the chat base URL comes from the Copilot token's `proxy-ep=` field (proxy.X → https://api.X),
|
|
15
|
-
* default https://api.individual.githubcopilot.com; requests carry the editor headers.
|
|
16
|
-
* Deviations: enterprise domains are not prompted for (github.com only — follow-up); pi's
|
|
17
|
-
* /models catalog fetch + per-model policy enabling is not ported (rovecode fetches /models itself
|
|
18
|
-
* through providers/stream.ts fetchModels); endpoints are injectable for the fake-server tests.
|
|
19
|
-
* Honesty: the client id is the one the GitHub Copilot Chat VS Code extension uses (pi, opencode and
|
|
20
|
-
* others sign in with it); the token endpoint is GitHub's internal Copilot API. Not live-verified
|
|
21
|
-
* on this box (no Copilot account) — see README. */
|
|
22
|
-
|
|
23
|
-
import { fetchOrCancel, httpFailure, jsonObject, type OAuthCredential, type OAuthDeps, type OAuthLoginIO, type OAuthProvider } from "./common.ts";
|
|
24
|
-
import { pollDeviceCodeFlow } from "./device-code.ts";
|
|
25
|
-
|
|
26
|
-
export interface GitHubCopilotEndpoints {
|
|
27
|
-
deviceCodeUrl: string;
|
|
28
|
-
accessTokenUrl: string;
|
|
29
|
-
copilotTokenUrl: string;
|
|
30
|
-
/** fixed chat base URL (tests; enterprise proxies). Default: derived from the token's proxy-ep. */
|
|
31
|
-
chatBaseUrl?: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export const GITHUB_COPILOT_ENDPOINTS: GitHubCopilotEndpoints = {
|
|
35
|
-
deviceCodeUrl: "https://github.com/login/device/code",
|
|
36
|
-
accessTokenUrl: "https://github.com/login/oauth/access_token",
|
|
37
|
-
copilotTokenUrl: "https://api.github.com/copilot_internal/v2/token",
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** pi: the GitHub Copilot Chat (VS Code extension) OAuth app id, base64 as upstream keeps it */
|
|
41
|
-
const CLIENT_ID = atob("SXYxLmI1MDdhMDhjODdlY2ZlOTg=");
|
|
42
|
-
const USER_AGENT = "GitHubCopilotChat/0.35.0";
|
|
43
|
-
/** the editor headers the Copilot API expects on every request (pi COPILOT_HEADERS) */
|
|
44
|
-
export const COPILOT_HEADERS: Readonly<Record<string, string>> = {
|
|
45
|
-
"User-Agent": USER_AGENT,
|
|
46
|
-
"Editor-Version": "vscode/1.107.0",
|
|
47
|
-
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
48
|
-
"Copilot-Integration-Id": "vscode-chat",
|
|
49
|
-
};
|
|
50
|
-
const DEFAULT_BASE_URL = "https://api.individual.githubcopilot.com";
|
|
51
|
-
/** the stored expiry leads the server's by this much, so a request never races the real expiry */
|
|
52
|
-
const EXPIRY_SKEW_MS = 5 * 60_000;
|
|
53
|
-
export const GITHUB_COPILOT_DEFAULT_MODEL = "gpt-4o";
|
|
54
|
-
|
|
55
|
-
interface DeviceCode { deviceCode: string; userCode: string; verificationUri: string; interval?: number; expiresIn: number }
|
|
56
|
-
|
|
57
|
-
/** `tid=…;exp=…;proxy-ep=proxy.individual.githubcopilot.com;…` → https://api.individual.githubcopilot.com */
|
|
58
|
-
export function baseUrlFromCopilotToken(token: string): string | null {
|
|
59
|
-
const match = /proxy-ep=([^;]+)/.exec(token);
|
|
60
|
-
if (!match || !match[1]) return null;
|
|
61
|
-
return `https://${match[1].replace(/^proxy\./, "api.")}`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function postForm(deps: OAuthDeps, url: string, fields: Record<string, string>, signal: AbortSignal | undefined, what: string): Promise<Record<string, unknown>> {
|
|
65
|
-
const res = await fetchOrCancel(deps, url, {
|
|
66
|
-
method: "POST",
|
|
67
|
-
headers: { Accept: "application/json", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": USER_AGENT },
|
|
68
|
-
body: new URLSearchParams(fields).toString(),
|
|
69
|
-
}, signal);
|
|
70
|
-
if (!res.ok) throw await httpFailure(what, res);
|
|
71
|
-
return jsonObject(res, what);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function startDeviceFlow(ep: GitHubCopilotEndpoints, deps: OAuthDeps, signal: AbortSignal): Promise<DeviceCode> {
|
|
75
|
-
const data = await postForm(deps, ep.deviceCodeUrl, { client_id: CLIENT_ID, scope: "read:user" }, signal, "GitHub device code request");
|
|
76
|
-
const { device_code: deviceCode, user_code: userCode, verification_uri: verificationUri, interval, expires_in: expiresIn } = data;
|
|
77
|
-
if (typeof deviceCode !== "string" || typeof userCode !== "string" || typeof verificationUri !== "string"
|
|
78
|
-
|| (interval !== undefined && typeof interval !== "number") || typeof expiresIn !== "number") {
|
|
79
|
-
throw new Error("GitHub device code response is missing fields");
|
|
80
|
-
}
|
|
81
|
-
// the verification URI is shown for the user to open — force it to be an http(s) URL (pi)
|
|
82
|
-
let parsed: URL;
|
|
83
|
-
try {
|
|
84
|
-
parsed = new URL(verificationUri);
|
|
85
|
-
} catch {
|
|
86
|
-
throw new Error("GitHub device code response carries an untrusted verification_uri");
|
|
87
|
-
}
|
|
88
|
-
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error("GitHub device code response carries an untrusted verification_uri");
|
|
89
|
-
return { deviceCode, userCode, verificationUri: parsed.href, interval: interval as number | undefined, expiresIn };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function pollAccessToken(ep: GitHubCopilotEndpoints, device: DeviceCode, deps: OAuthDeps, signal: AbortSignal): Promise<string> {
|
|
93
|
-
return pollDeviceCodeFlow<string>({
|
|
94
|
-
intervalSeconds: device.interval,
|
|
95
|
-
expiresInSeconds: device.expiresIn,
|
|
96
|
-
waitBeforeFirstPoll: true,
|
|
97
|
-
signal,
|
|
98
|
-
sleep: deps.sleep,
|
|
99
|
-
now: deps.now,
|
|
100
|
-
poll: async () => {
|
|
101
|
-
const raw = await postForm(deps, ep.accessTokenUrl, {
|
|
102
|
-
client_id: CLIENT_ID, device_code: device.deviceCode, grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
103
|
-
}, signal, "GitHub device token poll");
|
|
104
|
-
if (typeof raw.access_token === "string" && raw.access_token.length > 0) return { status: "complete", value: raw.access_token };
|
|
105
|
-
if (typeof raw.error === "string") {
|
|
106
|
-
if (raw.error === "authorization_pending") return { status: "pending" };
|
|
107
|
-
if (raw.error === "slow_down") return { status: "slow_down", intervalSeconds: typeof raw.interval === "number" ? raw.interval : undefined };
|
|
108
|
-
return { status: "failed", message: `GitHub device flow failed: ${raw.error}${typeof raw.error_description === "string" ? ` — ${raw.error_description.slice(0, 160)}` : ""}` };
|
|
109
|
-
}
|
|
110
|
-
return { status: "failed", message: "GitHub device token response is missing fields" };
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/** Bearer <github token> → the Copilot token (`access`) + expiry; the GitHub token stays `refresh`. */
|
|
116
|
-
async function exchangeCopilotToken(ep: GitHubCopilotEndpoints, githubToken: string, deps: OAuthDeps, signal: AbortSignal | undefined): Promise<OAuthCredential> {
|
|
117
|
-
const res = await fetchOrCancel(deps, ep.copilotTokenUrl, {
|
|
118
|
-
headers: { Accept: "application/json", Authorization: `Bearer ${githubToken}`, ...COPILOT_HEADERS },
|
|
119
|
-
}, signal);
|
|
120
|
-
if (!res.ok) throw await httpFailure("Copilot token exchange", res);
|
|
121
|
-
const raw = await jsonObject(res, "Copilot token exchange");
|
|
122
|
-
const { token, expires_at: expiresAt } = raw;
|
|
123
|
-
if (typeof token !== "string" || token.length === 0 || typeof expiresAt !== "number") throw new Error("Copilot token response is missing fields");
|
|
124
|
-
return { type: "oauth", access: token, refresh: githubToken, expires: expiresAt * 1000 - EXPIRY_SKEW_MS };
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function githubCopilotOAuth(endpoints: GitHubCopilotEndpoints = GITHUB_COPILOT_ENDPOINTS): OAuthProvider {
|
|
128
|
-
return {
|
|
129
|
-
id: "github-copilot",
|
|
130
|
-
label: "GitHub Copilot (device code)",
|
|
131
|
-
defaultModel: GITHUB_COPILOT_DEFAULT_MODEL,
|
|
132
|
-
async login(io: OAuthLoginIO, deps: OAuthDeps): Promise<OAuthCredential> {
|
|
133
|
-
const device = await startDeviceFlow(endpoints, deps, io.signal);
|
|
134
|
-
io.notify({ type: "device_code", userCode: device.userCode, verificationUri: device.verificationUri, intervalSeconds: device.interval ?? 5, expiresInSeconds: device.expiresIn });
|
|
135
|
-
const githubToken = await pollAccessToken(endpoints, device, deps, io.signal);
|
|
136
|
-
io.notify({ type: "progress", message: "authorized — exchanging for a Copilot token" });
|
|
137
|
-
return exchangeCopilotToken(endpoints, githubToken, deps, io.signal);
|
|
138
|
-
},
|
|
139
|
-
refresh(cred, signal, deps) {
|
|
140
|
-
return exchangeCopilotToken(endpoints, cred.refresh, deps, signal);
|
|
141
|
-
},
|
|
142
|
-
toAuth(cred) {
|
|
143
|
-
return { apiKey: cred.access, baseUrl: endpoints.chatBaseUrl ?? baseUrlFromCopilotToken(cred.access) ?? DEFAULT_BASE_URL, headers: { ...COPILOT_HEADERS } };
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
|
-
}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/** One-shot loopback OAuth callback server (port #66, extracted for port #76): binds 127.0.0.1 on an ephemeral
|
|
2
|
-
* port, waits for ONE browser GET on `/callback/<nonce>` and hands its `?code=` to the caller's `onCode`.
|
|
3
|
-
*
|
|
4
|
-
* Extracted — behaviour unchanged — from the OpenRouter flow of port #66 (openrouter.ts, itself ported from pi
|
|
5
|
-
* packages/ai/src/auth/oauth/openrouter.ts: earendil-works/pi, MIT, Copyright (c) 2025 Mario Zechner; snapshot under
|
|
6
|
-
* research/source_snapshots/earendil-works-pi) so the MCP login (mcp/oauth.ts) shares every rule:
|
|
7
|
-
* - Bun.serve on CALLBACK_HOST (127.0.0.1) port 0 — never 0.0.0.0, never a fixed port (opencode's 19876 is the
|
|
8
|
-
* anti-pattern); the callback URL is built from the BOUND server (hostname + port), so a bind regression shows up
|
|
9
|
-
* in the URL the tests pin;
|
|
10
|
-
* - anything but GET is a 404 (pi: `request.method !== "GET"` → 404), so is a request whose Host is not the loopback
|
|
11
|
-
* host (a DNS-rebinding page reaching the port) and a request off the `/callback/` prefix (a favicon probe) —
|
|
12
|
-
* all three keep waiting;
|
|
13
|
-
* - the callback PATH carries a fresh 32-byte nonce (pi randomizes the path with a UUID): a GET on
|
|
14
|
-
* `/callback/<anything else>` is a STATE MISMATCH — 400 and the whole login rejects, not merely ignored, because a
|
|
15
|
-
* forged redirect is the one thing the nonce exists to catch;
|
|
16
|
-
* - `requireStateParam` (port #76; the MCP SDK appends `state` to the authorize URL): `?state=` must equal the nonce
|
|
17
|
-
* too, else the same 400 + reject. OpenRouter passes none — whether it preserves a query string on the redirect is
|
|
18
|
-
* not live-verified, and a wrong guess would abort every real login;
|
|
19
|
-
* - `?error=` (RFC 6749 §4.1.2.1) rejects with the provider's reason; a GET without `code` is a 400 that keeps waiting;
|
|
20
|
-
* - single use: once a callback is being exchanged (`claimed`) or the outcome is decided (`closing`, flipped
|
|
21
|
-
* synchronously; `settled` follows SETTLE_DELAY_MS later so the browser gets its page first) every later request —
|
|
22
|
-
* genuine or forged — is a 409 that never reaches `onCode`. The ONE accepted code runs `onCode` BEFORE the 200 is
|
|
23
|
-
* sent (exchange-before-200); a throwing `onCode` answers 502 and rejects the login;
|
|
24
|
-
* - the server closes when the login settles — success, mismatch, denial, `onCode` failure, abort (`signal`),
|
|
25
|
-
* `stop()` or the timeout (5 minutes by default) — and refuses connections afterwards.
|
|
26
|
-
* Every message is built from `what` ("OpenRouter", `MCP server "x"`) so OpenRouter's texts stay verbatim. */
|
|
27
|
-
|
|
28
|
-
import { loginCancelled } from "./common.ts";
|
|
29
|
-
|
|
30
|
-
/** the callback server binds loopback only — the browser must run on this machine */
|
|
31
|
-
export const CALLBACK_HOST = "127.0.0.1";
|
|
32
|
-
export const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60_000;
|
|
33
|
-
/** the browser's response is flushed before the server goes away */
|
|
34
|
-
const SETTLE_DELAY_MS = 20;
|
|
35
|
-
|
|
36
|
-
export interface LoopbackOptions<T> {
|
|
37
|
-
/** the per-login nonce (pkce.ts randomState): the callback path is `/callback/<nonce>` */
|
|
38
|
-
nonce: string;
|
|
39
|
-
/** Ctrl-C — aborting rejects `result` with loginCancelled() */
|
|
40
|
-
signal: AbortSignal;
|
|
41
|
-
/** who is being signed in to; prefixes every message and page ("OpenRouter", `MCP server "x"`) */
|
|
42
|
-
what: string;
|
|
43
|
-
/** the sentence the 400 mismatch page ends with ("Run rovecode auth login openrouter again.") */
|
|
44
|
-
retryHint: string;
|
|
45
|
-
/** what `onCode` does, for the 502 page (default "key exchange") */
|
|
46
|
-
exchange?: string;
|
|
47
|
-
/** also require `?state=` to equal the nonce (the MCP SDK sends it; OpenRouter does not) */
|
|
48
|
-
requireStateParam?: boolean;
|
|
49
|
-
timeoutMs?: number;
|
|
50
|
-
/** runs with the accepted code BEFORE the browser gets its 200 page; a throw answers 502 and rejects `result` */
|
|
51
|
-
onCode: (code: string) => Promise<T>;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface LoopbackCallback<T> {
|
|
55
|
-
/** `http://127.0.0.1:<port>/callback/<nonce>` from the BOUND server */
|
|
56
|
-
callbackUrl: string;
|
|
57
|
-
/** onCode's value; rejects on mismatch, denial, onCode failure, abort, stop() or timeout — never with a token */
|
|
58
|
-
result: Promise<T>;
|
|
59
|
-
/** stop the server now (idempotent; runs on its own once `result` settles) */
|
|
60
|
-
stop(): void;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** the thrown text for a callback on a wrong path (or, with requireStateParam, a wrong `?state=`) */
|
|
64
|
-
export function stateMismatchMessage(what: string): string {
|
|
65
|
-
return `${what} OAuth: state mismatch on the callback — login aborted`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function html(status: number, text: string): Response {
|
|
69
|
-
return new Response(`<!doctype html><meta charset="utf-8"><title>rovecode</title><p>${text}</p>`, {
|
|
70
|
-
status, headers: { "content-type": "text/html; charset=utf-8", "cache-control": "no-store" },
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function minutes(ms: number): string {
|
|
75
|
-
return ms >= 60_000 ? `${Math.round(ms / 60_000)} minutes` : `${ms} ms`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export function startLoopbackCallback<T>(opts: LoopbackOptions<T>): LoopbackCallback<T> {
|
|
79
|
-
type Outcome = { value: T } | { error: Error };
|
|
80
|
-
const callbackPath = `/callback/${opts.nonce}`;
|
|
81
|
-
const exchange = opts.exchange ?? "key exchange";
|
|
82
|
-
let settle: (o: Outcome) => void = () => {};
|
|
83
|
-
const outcome = new Promise<Outcome>((resolve) => { settle = resolve; });
|
|
84
|
-
let settled = false;
|
|
85
|
-
let claimed = false;
|
|
86
|
-
/** the outcome is decided but `settled` waits SETTLE_DELAY_MS — flipped synchronously so no callback that lands in
|
|
87
|
-
* that window is exchanged (a forged mismatch first, the genuine redirect 5 ms later) */
|
|
88
|
-
let closing = false;
|
|
89
|
-
const finish = (o: Outcome): void => {
|
|
90
|
-
if (settled) return;
|
|
91
|
-
settled = true;
|
|
92
|
-
settle(o);
|
|
93
|
-
};
|
|
94
|
-
// the browser gets its page before the login settles and the server stops
|
|
95
|
-
const finishAfterResponse = (o: Outcome): void => { closing = true; setTimeout(() => finish(o), SETTLE_DELAY_MS); };
|
|
96
|
-
const mismatch = (): Response => {
|
|
97
|
-
finishAfterResponse({ error: new Error(stateMismatchMessage(opts.what)) });
|
|
98
|
-
return html(400, `State mismatch — this login was aborted. ${opts.retryHint}`);
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const server = Bun.serve({
|
|
102
|
-
port: 0,
|
|
103
|
-
hostname: CALLBACK_HOST,
|
|
104
|
-
async fetch(req) {
|
|
105
|
-
// pi: the redirect is a browser GET — anything else is not a callback (404, keep waiting)
|
|
106
|
-
if (req.method !== "GET") return html(404, "Not an OAuth callback.");
|
|
107
|
-
const url = new URL(req.url);
|
|
108
|
-
// a foreign Host header (a page at evil.example resolving to 127.0.0.1) is never the redirect we issued
|
|
109
|
-
if (url.hostname !== CALLBACK_HOST) return html(404, "Not an OAuth callback.");
|
|
110
|
-
if (!url.pathname.startsWith("/callback/")) return html(404, "Not an OAuth callback.");
|
|
111
|
-
// single use: once a callback is being exchanged (claimed) or the outcome is decided (closing / settled),
|
|
112
|
-
// every later request — genuine or forged — is a 409 that never reaches onCode
|
|
113
|
-
if (settled || claimed || closing) return html(409, "This OAuth callback has already been used.");
|
|
114
|
-
if (url.pathname !== callbackPath) return mismatch();
|
|
115
|
-
if (opts.requireStateParam === true && url.searchParams.get("state") !== opts.nonce) return mismatch();
|
|
116
|
-
const denied = url.searchParams.get("error");
|
|
117
|
-
if (denied) {
|
|
118
|
-
finishAfterResponse({ error: new Error(`${opts.what} authorization denied: ${(url.searchParams.get("error_description") ?? denied).slice(0, 160)}`) });
|
|
119
|
-
return html(400, `${opts.what} authorization was denied.`);
|
|
120
|
-
}
|
|
121
|
-
const code = url.searchParams.get("code");
|
|
122
|
-
if (!code) return html(400, `${opts.what} returned no authorization code.`);
|
|
123
|
-
claimed = true;
|
|
124
|
-
try {
|
|
125
|
-
const value = await opts.onCode(code);
|
|
126
|
-
finishAfterResponse({ value });
|
|
127
|
-
return html(200, `Signed in to ${opts.what} — you can close this tab and return to rovecode.`);
|
|
128
|
-
} catch (e) {
|
|
129
|
-
finishAfterResponse({ error: e instanceof Error ? e : new Error(String(e)) });
|
|
130
|
-
return html(502, `${opts.what} ${exchange} failed — see the rovecode terminal.`);
|
|
131
|
-
}
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// from the BOUND server, not the constant: a bind regression (0.0.0.0) shows up in the printed URL the tests pin
|
|
136
|
-
const callbackUrl = `http://${server.hostname}:${server.port}${callbackPath}`;
|
|
137
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS;
|
|
138
|
-
const onAbort = (): void => finish({ error: loginCancelled() });
|
|
139
|
-
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
140
|
-
const timer = setTimeout(() => finish({ error: new Error(`${opts.what} OAuth login timed out (${minutes(timeoutMs)})`) }), timeoutMs);
|
|
141
|
-
let stopped = false;
|
|
142
|
-
const stop = (): void => {
|
|
143
|
-
if (stopped) return;
|
|
144
|
-
stopped = true;
|
|
145
|
-
clearTimeout(timer);
|
|
146
|
-
opts.signal.removeEventListener("abort", onAbort);
|
|
147
|
-
server.stop(true);
|
|
148
|
-
finish({ error: new Error(`${opts.what} OAuth login aborted`) }); // no-op when the outcome is already in
|
|
149
|
-
};
|
|
150
|
-
if (opts.signal.aborted) onAbort();
|
|
151
|
-
const result = outcome.then((o) => {
|
|
152
|
-
stop();
|
|
153
|
-
if ("error" in o) throw o.error;
|
|
154
|
-
return o.value;
|
|
155
|
-
});
|
|
156
|
-
result.catch(() => {}); // observed by the caller; a caller that stopped early must not leave it unhandled
|
|
157
|
-
return { callbackUrl, result, stop };
|
|
158
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/** OpenAI (ChatGPT) OAuth device-code flow (port #66).
|
|
2
|
-
*
|
|
3
|
-
* Ported from pi packages/ai/src/auth/oauth/openai-codex.ts (earendil-works/pi, MIT — Copyright (c)
|
|
4
|
-
* 2025 Mario Zechner; snapshot under research/source_snapshots/earendil-works-pi), device-code half
|
|
5
|
-
* only (pi's browser PKCE half on the fixed port 1455 is not ported — follow-up). Kept as pi has it:
|
|
6
|
-
* - POST auth.openai.com/api/accounts/deviceauth/usercode {client_id} → device_auth_id + user_code +
|
|
7
|
-
* interval (number or numeric string); the user opens https://auth.openai.com/codex/device;
|
|
8
|
-
* - poll POST …/deviceauth/token {device_auth_id, user_code}: 200 → authorization_code + the
|
|
9
|
-
* server-issued code_verifier; 403/404 or error code deviceauth_authorization_pending → pending;
|
|
10
|
-
* slow_down → slow_down; anything else fails; 15-minute deadline;
|
|
11
|
-
* - exchange at POST auth.openai.com/oauth/token (grant authorization_code, redirect_uri
|
|
12
|
-
* …/deviceauth/callback) → access_token + refresh_token + expires_in; refresh = grant
|
|
13
|
-
* refresh_token at the same URL; the ChatGPT account id is read from the access token's JWT claim
|
|
14
|
-
* `https://api.openai.com/auth`.chatgpt_account_id and sent as the `chatgpt-account-id` header.
|
|
15
|
-
* Deviations: a token without the account claim is accepted (pi throws) — accountId is optional;
|
|
16
|
-
* a 404 on the usercode request is reported as "device code login not enabled" like pi.
|
|
17
|
-
* Honesty: the client id is the Codex CLI's (pi and opencode sign in with it). The resulting token is a
|
|
18
|
-
* ChatGPT-subscription token accepted by the Codex Responses backend ONLY, so `toAuth` (port #75) hands the
|
|
19
|
-
* seam `baseUrl` https://chatgpt.com/backend-api/codex (codex model-provider-info/src/lib.rs:40
|
|
20
|
-
* CHATGPT_CODEX_BASE_URL; pi openai-codex-responses.ts:45 + resolveCodexUrl :633-639), `wire: "responses"`
|
|
21
|
-
* (providers/wire-select.ts sends every request through providers/responses.ts — never /chat/completions)
|
|
22
|
-
* and the headers that backend requires: `chatgpt-account-id` (when the JWT carries the claim),
|
|
23
|
-
* `OpenAI-Beta: responses=experimental` and `originator: rovecode` (pi openai-codex-responses.ts:1607-1609,
|
|
24
|
-
* :1622; codex core/src/client.rs:688 adds its own originator). Not live-verified on this box (no ChatGPT
|
|
25
|
-
* account): the path is pinned against loopback fakes (test/unit/oauth-openai-responses.test.ts) — README. */
|
|
26
|
-
|
|
27
|
-
import { fetchOrCancel, httpFailure, jsonObject, type OAuthCredential, type OAuthDeps, type OAuthLoginIO, type OAuthProvider } from "./common.ts";
|
|
28
|
-
import { pollDeviceCodeFlow } from "./device-code.ts";
|
|
29
|
-
|
|
30
|
-
export interface OpenAIEndpoints {
|
|
31
|
-
deviceUserCodeUrl: string;
|
|
32
|
-
deviceTokenUrl: string;
|
|
33
|
-
tokenUrl: string;
|
|
34
|
-
deviceVerificationUri: string;
|
|
35
|
-
deviceRedirectUri: string;
|
|
36
|
-
/** the Responses base the token is sent to (…/responses is appended by providers/responses.ts) */
|
|
37
|
-
responsesBaseUrl: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const AUTH_BASE_URL = "https://auth.openai.com";
|
|
41
|
-
export const OPENAI_ENDPOINTS: OpenAIEndpoints = {
|
|
42
|
-
deviceUserCodeUrl: `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`,
|
|
43
|
-
deviceTokenUrl: `${AUTH_BASE_URL}/api/accounts/deviceauth/token`,
|
|
44
|
-
tokenUrl: `${AUTH_BASE_URL}/oauth/token`,
|
|
45
|
-
deviceVerificationUri: `${AUTH_BASE_URL}/codex/device`,
|
|
46
|
-
deviceRedirectUri: `${AUTH_BASE_URL}/deviceauth/callback`,
|
|
47
|
-
responsesBaseUrl: "https://chatgpt.com/backend-api/codex",
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/** the two headers the Codex Responses backend expects beside the bearer + account id (pi :1607-1609, :1622) */
|
|
51
|
-
export const CODEX_RESPONSES_HEADERS: Readonly<Record<string, string>> = { "OpenAI-Beta": "responses=experimental", originator: "rovecode" };
|
|
52
|
-
|
|
53
|
-
/** pi: the Codex CLI OAuth client id */
|
|
54
|
-
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
55
|
-
const DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;
|
|
56
|
-
const JWT_CLAIM = "https://api.openai.com/auth";
|
|
57
|
-
|
|
58
|
-
interface DeviceAuth { deviceAuthId: string; userCode: string; intervalSeconds: number }
|
|
59
|
-
interface DeviceGrant { authorizationCode: string; codeVerifier: string }
|
|
60
|
-
|
|
61
|
-
/** The ChatGPT account id claim of an access-token JWT; undefined when absent or not a JWT. */
|
|
62
|
-
export function accountIdFromJwt(token: string): string | undefined {
|
|
63
|
-
try {
|
|
64
|
-
const parts = token.split(".");
|
|
65
|
-
if (parts.length !== 3 || !parts[1]) return undefined;
|
|
66
|
-
const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
67
|
-
const payload = JSON.parse(atob(b64 + "=".repeat((4 - (b64.length % 4)) % 4))) as Record<string, unknown>;
|
|
68
|
-
const claim = payload[JWT_CLAIM];
|
|
69
|
-
const id = typeof claim === "object" && claim !== null ? (claim as { chatgpt_account_id?: unknown }).chatgpt_account_id : undefined;
|
|
70
|
-
return typeof id === "string" && id.length > 0 ? id : undefined;
|
|
71
|
-
} catch {
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async function readTokenResponse(res: Response, what: string, now: () => number): Promise<OAuthCredential> {
|
|
77
|
-
if (!res.ok) throw await httpFailure(what, res);
|
|
78
|
-
const json = await jsonObject(res, what);
|
|
79
|
-
const { access_token: access, refresh_token: refresh, expires_in: expiresIn } = json;
|
|
80
|
-
if (typeof access !== "string" || access.length === 0 || typeof refresh !== "string" || typeof expiresIn !== "number") throw new Error(`${what}: response is missing fields`);
|
|
81
|
-
const accountId = accountIdFromJwt(access);
|
|
82
|
-
return { type: "oauth", access, refresh, expires: now() + expiresIn * 1000, ...(accountId !== undefined ? { accountId } : {}) };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function startDeviceAuth(ep: OpenAIEndpoints, deps: OAuthDeps, signal: AbortSignal): Promise<DeviceAuth> {
|
|
86
|
-
const res = await fetchOrCancel(deps, ep.deviceUserCodeUrl, {
|
|
87
|
-
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ client_id: CLIENT_ID }),
|
|
88
|
-
}, signal);
|
|
89
|
-
if (res.status === 404) throw new Error("OpenAI device code login is not enabled for this server (HTTP 404)");
|
|
90
|
-
if (!res.ok) throw await httpFailure("OpenAI device code request", res);
|
|
91
|
-
const json = await jsonObject(res, "OpenAI device code request");
|
|
92
|
-
const { device_auth_id: deviceAuthId, user_code: userCode, interval } = json;
|
|
93
|
-
const intervalSeconds = typeof interval === "string" ? Number(interval.trim()) : interval;
|
|
94
|
-
if (typeof deviceAuthId !== "string" || !deviceAuthId || typeof userCode !== "string" || !userCode
|
|
95
|
-
|| typeof intervalSeconds !== "number" || !Number.isFinite(intervalSeconds) || intervalSeconds < 0) {
|
|
96
|
-
throw new Error("OpenAI device code response is missing fields");
|
|
97
|
-
}
|
|
98
|
-
return { deviceAuthId, userCode, intervalSeconds };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function pollDeviceAuth(ep: OpenAIEndpoints, device: DeviceAuth, deps: OAuthDeps, signal: AbortSignal): Promise<DeviceGrant> {
|
|
102
|
-
return pollDeviceCodeFlow<DeviceGrant>({
|
|
103
|
-
intervalSeconds: device.intervalSeconds,
|
|
104
|
-
expiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS,
|
|
105
|
-
signal,
|
|
106
|
-
sleep: deps.sleep,
|
|
107
|
-
now: deps.now,
|
|
108
|
-
poll: async () => {
|
|
109
|
-
const res = await fetchOrCancel(deps, ep.deviceTokenUrl, {
|
|
110
|
-
method: "POST", headers: { "Content-Type": "application/json" },
|
|
111
|
-
body: JSON.stringify({ device_auth_id: device.deviceAuthId, user_code: device.userCode }),
|
|
112
|
-
}, signal);
|
|
113
|
-
if (res.ok) {
|
|
114
|
-
const json = await jsonObject(res, "OpenAI device token poll");
|
|
115
|
-
const { authorization_code: authorizationCode, code_verifier: codeVerifier } = json;
|
|
116
|
-
if (typeof authorizationCode !== "string" || !authorizationCode || typeof codeVerifier !== "string" || !codeVerifier) {
|
|
117
|
-
return { status: "failed", message: "OpenAI device token response is missing fields" };
|
|
118
|
-
}
|
|
119
|
-
return { status: "complete", value: { authorizationCode, codeVerifier } };
|
|
120
|
-
}
|
|
121
|
-
if (res.status === 403 || res.status === 404) return { status: "pending" };
|
|
122
|
-
let errorCode: unknown;
|
|
123
|
-
try {
|
|
124
|
-
const json = (await res.json()) as { error?: string | { code?: string } } | null;
|
|
125
|
-
errorCode = typeof json?.error === "object" ? json.error?.code : json?.error;
|
|
126
|
-
} catch {
|
|
127
|
-
// non-JSON error body: reported by status below
|
|
128
|
-
}
|
|
129
|
-
if (errorCode === "deviceauth_authorization_pending") return { status: "pending" };
|
|
130
|
-
if (errorCode === "slow_down") return { status: "slow_down" };
|
|
131
|
-
return { status: "failed", message: `OpenAI device authorization failed (HTTP ${res.status})${typeof errorCode === "string" ? `: ${errorCode.slice(0, 80)}` : ""}` };
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async function postToken(ep: OpenAIEndpoints, fields: Record<string, string>, deps: OAuthDeps, signal: AbortSignal | undefined, what: string): Promise<OAuthCredential> {
|
|
137
|
-
const res = await fetchOrCancel(deps, ep.tokenUrl, {
|
|
138
|
-
method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams(fields).toString(),
|
|
139
|
-
}, signal);
|
|
140
|
-
return readTokenResponse(res, what, deps.now);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export function openAIOAuth(endpoints: OpenAIEndpoints = OPENAI_ENDPOINTS): OAuthProvider {
|
|
144
|
-
return {
|
|
145
|
-
id: "openai",
|
|
146
|
-
label: "OpenAI / ChatGPT (device code)",
|
|
147
|
-
async login(io: OAuthLoginIO, deps: OAuthDeps): Promise<OAuthCredential> {
|
|
148
|
-
const device = await startDeviceAuth(endpoints, deps, io.signal);
|
|
149
|
-
io.notify({ type: "device_code", userCode: device.userCode, verificationUri: endpoints.deviceVerificationUri, intervalSeconds: device.intervalSeconds, expiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS });
|
|
150
|
-
const grant = await pollDeviceAuth(endpoints, device, deps, io.signal);
|
|
151
|
-
io.notify({ type: "progress", message: "authorized — exchanging the authorization code" });
|
|
152
|
-
return postToken(endpoints, {
|
|
153
|
-
grant_type: "authorization_code", client_id: CLIENT_ID, code: grant.authorizationCode, code_verifier: grant.codeVerifier, redirect_uri: endpoints.deviceRedirectUri,
|
|
154
|
-
}, deps, io.signal, "OpenAI token exchange");
|
|
155
|
-
},
|
|
156
|
-
refresh(cred, signal, deps) {
|
|
157
|
-
return postToken(endpoints, { grant_type: "refresh_token", refresh_token: cred.refresh, client_id: CLIENT_ID }, deps, signal, "OpenAI token refresh");
|
|
158
|
-
},
|
|
159
|
-
toAuth(cred) {
|
|
160
|
-
return { apiKey: cred.access, baseUrl: endpoints.responsesBaseUrl, wire: "responses", headers: { ...(cred.accountId ? { "chatgpt-account-id": cred.accountId } : {}), ...CODEX_RESPONSES_HEADERS } };
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
}
|