@code-yeongyu/senpi 2026.9.10 → 2026.9.11
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 +83 -0
- package/dist/core/agent-session.d.ts +57 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +130 -20
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +2 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +49 -17
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/brand.d.ts +5 -0
- package/dist/core/brand.d.ts.map +1 -1
- package/dist/core/brand.js +13 -0
- package/dist/core/brand.js.map +1 -1
- package/dist/core/changelog-source.d.ts +8 -0
- package/dist/core/changelog-source.d.ts.map +1 -0
- package/dist/core/changelog-source.js +19 -0
- package/dist/core/changelog-source.js.map +1 -0
- package/dist/core/credential-accounts.d.ts +3 -0
- package/dist/core/credential-accounts.d.ts.map +1 -1
- package/dist/core/credential-accounts.js +20 -1
- package/dist/core/credential-accounts.js.map +1 -1
- package/dist/core/credential-pool/classify.d.ts.map +1 -1
- package/dist/core/credential-pool/classify.js +8 -0
- package/dist/core/credential-pool/classify.js.map +1 -1
- package/dist/core/extensions/builtin/account/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/account/index.js +7 -3
- package/dist/core/extensions/builtin/account/index.js.map +1 -1
- package/dist/core/extensions/builtin/account-display-name.d.ts +7 -0
- package/dist/core/extensions/builtin/account-display-name.d.ts.map +1 -0
- package/dist/core/extensions/builtin/account-display-name.js +40 -0
- package/dist/core/extensions/builtin/account-display-name.js.map +1 -0
- package/dist/core/extensions/builtin/ask-user/format.d.ts.map +1 -1
- package/dist/core/extensions/builtin/ask-user/format.js +7 -2
- package/dist/core/extensions/builtin/ask-user/format.js.map +1 -1
- package/dist/core/extensions/builtin/ask-user/pending.d.ts.map +1 -1
- package/dist/core/extensions/builtin/ask-user/pending.js +3 -0
- package/dist/core/extensions/builtin/ask-user/pending.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.js +21 -9
- package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/account-management.d.ts +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/account-management.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/account-management.js +11 -6
- package/dist/core/extensions/builtin/claude-sdk-oauth/account-management.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/accounts.d.ts +9 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/accounts.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/accounts.js +10 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/accounts.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +3 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js +19 -10
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/settings.js +3 -9
- package/dist/core/extensions/builtin/claude-sdk-oauth/settings.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/routine-settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/routine-settings.js +1 -0
- package/dist/core/extensions/builtin/config-reload/routine-settings.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.js +5 -3
- package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.d.ts +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/settings.js +3 -9
- package/dist/core/extensions/builtin/cursor-cli-oauth/settings.js.map +1 -1
- package/dist/core/extensions/builtin/goal/agent-end-continuation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/agent-end-continuation.js +13 -1
- package/dist/core/extensions/builtin/goal/agent-end-continuation.js.map +1 -1
- package/dist/core/extensions/builtin/goal/prompt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/prompt.js +11 -8
- package/dist/core/extensions/builtin/goal/prompt.js.map +1 -1
- package/dist/core/extensions/builtin/goal/terminal-provider-error.d.ts +1 -0
- package/dist/core/extensions/builtin/goal/terminal-provider-error.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/terminal-provider-error.js +28 -0
- package/dist/core/extensions/builtin/goal/terminal-provider-error.js.map +1 -1
- package/dist/core/extensions/builtin/goal/todo-gate.js +2 -2
- package/dist/core/extensions/builtin/goal/todo-gate.js.map +1 -1
- package/dist/core/extensions/builtin/goal/tool-registration.js +2 -2
- package/dist/core/extensions/builtin/goal/tool-registration.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-account.d.ts.map +1 -1
- package/dist/core/extensions/builtin/gpt-account.js +19 -8
- package/dist/core/extensions/builtin/gpt-account.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.js +19 -0
- package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +6 -6
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +26 -7
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.js +29 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
- package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
- package/dist/core/high-reasoning-warning.d.ts.map +1 -1
- package/dist/core/high-reasoning-warning.js +5 -1
- package/dist/core/high-reasoning-warning.js.map +1 -1
- package/dist/core/manual-continue.d.ts +14 -0
- package/dist/core/manual-continue.d.ts.map +1 -1
- package/dist/core/manual-continue.js +13 -0
- package/dist/core/manual-continue.js.map +1 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +2 -2
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/remote-catalog-merge.d.ts +16 -0
- package/dist/core/remote-catalog-merge.d.ts.map +1 -0
- package/dist/core/remote-catalog-merge.js +75 -0
- package/dist/core/remote-catalog-merge.js.map +1 -0
- package/dist/core/remote-catalog-provider.d.ts +3 -0
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +12 -40
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/session-entry-materializer.d.ts +12 -0
- package/dist/core/session-entry-materializer.d.ts.map +1 -0
- package/dist/core/session-entry-materializer.js +26 -0
- package/dist/core/session-entry-materializer.js.map +1 -0
- package/dist/core/session-manager.d.ts +36 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +38 -8
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +10 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/modes/app-server/daemon/process.d.ts +6 -1
- package/dist/modes/app-server/daemon/process.d.ts.map +1 -1
- package/dist/modes/app-server/daemon/process.js +23 -4
- package/dist/modes/app-server/daemon/process.js.map +1 -1
- package/dist/modes/app-server/protocol/account.d.ts +2 -0
- package/dist/modes/app-server/protocol/account.d.ts.map +1 -1
- package/dist/modes/app-server/protocol/account.js.map +1 -1
- package/dist/modes/interactive/components/ask-user-question-keys.d.ts +1 -1
- package/dist/modes/interactive/components/ask-user-question-keys.d.ts.map +1 -1
- package/dist/modes/interactive/components/ask-user-question-keys.js +65 -17
- package/dist/modes/interactive/components/ask-user-question-keys.js.map +1 -1
- package/dist/modes/interactive/components/ask-user-question-render.d.ts +2 -1
- package/dist/modes/interactive/components/ask-user-question-render.d.ts.map +1 -1
- package/dist/modes/interactive/components/ask-user-question-render.js +39 -9
- package/dist/modes/interactive/components/ask-user-question-render.js.map +1 -1
- package/dist/modes/interactive/components/ask-user-question-state.d.ts +14 -4
- package/dist/modes/interactive/components/ask-user-question-state.d.ts.map +1 -1
- package/dist/modes/interactive/components/ask-user-question-state.js +63 -6
- package/dist/modes/interactive/components/ask-user-question-state.js.map +1 -1
- package/dist/modes/interactive/components/ask-user-question.d.ts +2 -0
- package/dist/modes/interactive/components/ask-user-question.d.ts.map +1 -1
- package/dist/modes/interactive/components/ask-user-question.js +27 -14
- package/dist/modes/interactive/components/ask-user-question.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +51 -19
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +11 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +29 -9
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +7 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/help-content.d.ts.map +1 -1
- package/dist/modes/interactive/help-content.js +4 -0
- package/dist/modes/interactive/help-content.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +13 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +44 -15
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tips/catalog/input-tips.js +1 -1
- package/dist/modes/interactive/tips/catalog/input-tips.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +3 -1
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/connection-question-bridge.js +1 -1
- package/dist/modes/rpc/connection-question-bridge.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +33 -11
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts +14 -0
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +31 -5
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.js +2 -0
- package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +2 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/changelog.d.ts +3 -1
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +89 -23
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/paths.d.ts +5 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -1
- package/dist/utils/paths.js.map +1 -1
- package/docs/app-server.md +6 -0
- package/docs/extensions.md +19 -3
- package/docs/rpc.md +10 -0
- package/docs/settings.md +2 -1
- package/docs/tui.md +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +28 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +5 -4
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/output/streaming-output-buffer.ts +58 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/output/streaming-output.ts +9 -64
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt-template.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.d.ts +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.js +90 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/frames.d.ts +23 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/frames.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/frames.js +71 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/frames.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.d.ts +873 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.js +303 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.d.ts +12 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.js +35 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.d.ts +23 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.js +23 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.d.ts +24 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.js +116 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.d.ts +20 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.js +153 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/types.d.ts +12 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/types.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/types.js +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/types.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.d.ts +6 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.js +6 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.d.ts +14 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.js +162 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.lazy.d.ts +5 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.lazy.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.lazy.js +23 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.lazy.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin-callback.d.ts +25 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin-callback.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin-callback.js +124 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin-callback.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin-token.d.ts +18 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin-token.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin-token.js +101 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin-token.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.d.ts +14 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.js +111 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js +5 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-identity.d.ts +24 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-identity.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-identity.js +86 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-identity.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.js +5 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.d.ts +51 -4
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.js +196 -7
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.d.ts +8 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/types.d.ts +9 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.js +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.js +14 -5
- package/node_modules/@earendil-works/pi-ai/dist/models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/venice.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.d.ts +12 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.js +62 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.d.ts +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.js +28 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +3 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.js +55 -16
- package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
import { lazyApi } from "./lazy.js";
|
|
10
|
+
/**
|
|
11
|
+
* Loads the devin-agent implementation through a variable specifier so bundlers
|
|
12
|
+
* (browser smoke, Bun compile) cannot follow the import into the Node-only
|
|
13
|
+
* Connect transport. The `.ts`/`.js` rewrite keeps the trick working from both
|
|
14
|
+
* source and built output.
|
|
15
|
+
*/
|
|
16
|
+
const importNodeOnlyApi = (specifier) => {
|
|
17
|
+
const runtimeSpecifier = import.meta.url.endsWith(".js") ? specifier.replace(/\.ts$/, ".js") : specifier;
|
|
18
|
+
return import(__rewriteRelativeImportExtension(runtimeSpecifier));
|
|
19
|
+
};
|
|
20
|
+
const loadDevinAgentModule = () => importNodeOnlyApi("./devin-agent.ts");
|
|
21
|
+
export const devinAgentApi = () => lazyApi(loadDevinAgentModule);
|
|
22
|
+
export { loadDevinAgentModule };
|
|
23
|
+
//# sourceMappingURL=devin-agent.lazy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-agent.lazy.js","sourceRoot":"","sources":["../../src/api/devin-agent.lazy.ts"],"names":[],"mappings":";;;;;;;;AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,CAAC,SAAiB,EAAoB,EAAE;IACjE,MAAM,gBAAgB,GAAG,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACzG,OAAO,MAAM,kCAAC,gBAAgB,EAAC,CAAC;AACjC,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,GAA6B,EAAE,CAC3D,iBAAiB,CAAC,kBAAkB,CAA6B,CAAC;AAEnE,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,CAAC","sourcesContent":["import type { ProviderStreams } from \"../types.ts\";\nimport { lazyApi } from \"./lazy.ts\";\n\n/**\n * Loads the devin-agent implementation through a variable specifier so bundlers\n * (browser smoke, Bun compile) cannot follow the import into the Node-only\n * Connect transport. The `.ts`/`.js` rewrite keeps the trick working from both\n * source and built output.\n */\nconst importNodeOnlyApi = (specifier: string): Promise<unknown> => {\n\tconst runtimeSpecifier = import.meta.url.endsWith(\".js\") ? specifier.replace(/\\.ts$/, \".js\") : specifier;\n\treturn import(runtimeSpecifier);\n};\n\nconst loadDevinAgentModule = (): Promise<ProviderStreams> =>\n\timportNodeOnlyApi(\"./devin-agent.ts\") as Promise<ProviderStreams>;\n\nexport const devinAgentApi = () => lazyApi(loadDevinAgentModule);\nexport { loadDevinAgentModule };\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin CLI OAuth loopback callback.
|
|
3
|
+
*
|
|
4
|
+
* Devin registers ONE redirect URI for the CLI, so the callback binds the fixed
|
|
5
|
+
* loopback port 59653 instead of an ephemeral one: a different port is rejected
|
|
6
|
+
* by the authorize page. The server is one-shot, validates the issued state
|
|
7
|
+
* before spending the code, and renders the shared OAuth result pages.
|
|
8
|
+
*/
|
|
9
|
+
import type { OAuthCredential } from "../types.ts";
|
|
10
|
+
export declare const DEVIN_CALLBACK_PORT = 59653;
|
|
11
|
+
export declare const DEVIN_CALLBACK_PATH = "/callback";
|
|
12
|
+
export type DevinCallbackServer = {
|
|
13
|
+
callbackUrl: string;
|
|
14
|
+
close: () => void;
|
|
15
|
+
/** Hand the login to manual code entry unless a callback already claimed it. */
|
|
16
|
+
cancelWait: () => void;
|
|
17
|
+
waitForCredential: () => Promise<OAuthCredential | null>;
|
|
18
|
+
};
|
|
19
|
+
export declare function startDevinCallbackServer(input: {
|
|
20
|
+
state: string;
|
|
21
|
+
verifier: string;
|
|
22
|
+
signal: AbortSignal;
|
|
23
|
+
loginTimeoutMs: number;
|
|
24
|
+
}): Promise<DevinCallbackServer>;
|
|
25
|
+
//# sourceMappingURL=devin-callback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-callback.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/devin-callback.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAInD,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AACzC,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAE/C,MAAM,MAAM,mBAAmB,GAAG;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,gFAAgF;IAChF,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,iBAAiB,EAAE,MAAM,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;CACzD,CAAC;AAaF,wBAAsB,wBAAwB,CAAC,KAAK,EAAE;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsG/B"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin CLI OAuth loopback callback.
|
|
3
|
+
*
|
|
4
|
+
* Devin registers ONE redirect URI for the CLI, so the callback binds the fixed
|
|
5
|
+
* loopback port 59653 instead of an ephemeral one: a different port is rejected
|
|
6
|
+
* by the authorize page. The server is one-shot, validates the issued state
|
|
7
|
+
* before spending the code, and renders the shared OAuth result pages.
|
|
8
|
+
*/
|
|
9
|
+
import { createServer } from "node:http";
|
|
10
|
+
import { getProviderEnvValue } from "../../utils/provider-env.js";
|
|
11
|
+
import { exchangeDevinAuthorizationCode } from "./devin-token.js";
|
|
12
|
+
import { oauthErrorHtml, oauthSuccessHtml } from "./oauth-page.js";
|
|
13
|
+
export const DEVIN_CALLBACK_PORT = 59653;
|
|
14
|
+
export const DEVIN_CALLBACK_PATH = "/callback";
|
|
15
|
+
function callbackHost() {
|
|
16
|
+
return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
|
|
17
|
+
}
|
|
18
|
+
function sendHtml(response, status, html) {
|
|
19
|
+
response.statusCode = status;
|
|
20
|
+
response.setHeader("content-type", "text/html; charset=utf-8");
|
|
21
|
+
response.setHeader("cache-control", "no-store");
|
|
22
|
+
response.end(html);
|
|
23
|
+
}
|
|
24
|
+
export async function startDevinCallbackServer(input) {
|
|
25
|
+
if (input.signal.aborted)
|
|
26
|
+
throw new Error("Login cancelled");
|
|
27
|
+
const host = callbackHost();
|
|
28
|
+
let resolveCredential = () => { };
|
|
29
|
+
let rejectCredential = () => { };
|
|
30
|
+
const credential = new Promise((resolve, reject) => {
|
|
31
|
+
resolveCredential = resolve;
|
|
32
|
+
rejectCredential = reject;
|
|
33
|
+
});
|
|
34
|
+
let claimed = false;
|
|
35
|
+
let settled = false;
|
|
36
|
+
let timeout;
|
|
37
|
+
let onAbort;
|
|
38
|
+
const close = () => {
|
|
39
|
+
if (timeout)
|
|
40
|
+
clearTimeout(timeout);
|
|
41
|
+
if (onAbort)
|
|
42
|
+
input.signal.removeEventListener("abort", onAbort);
|
|
43
|
+
server.close();
|
|
44
|
+
};
|
|
45
|
+
const finish = (result) => {
|
|
46
|
+
if (settled)
|
|
47
|
+
return;
|
|
48
|
+
settled = true;
|
|
49
|
+
close();
|
|
50
|
+
if ("credential" in result)
|
|
51
|
+
resolveCredential(result.credential);
|
|
52
|
+
else
|
|
53
|
+
rejectCredential(result.error);
|
|
54
|
+
};
|
|
55
|
+
const server = createServer((request, response) => {
|
|
56
|
+
void (async () => {
|
|
57
|
+
const requestUrl = new URL(request.url ?? "/", `http://${host}`);
|
|
58
|
+
if (request.method !== "GET" || requestUrl.pathname !== DEVIN_CALLBACK_PATH) {
|
|
59
|
+
sendHtml(response, 404, oauthErrorHtml("OAuth callback route not found."));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (claimed || settled) {
|
|
63
|
+
sendHtml(response, 409, oauthErrorHtml("This OAuth callback has already been used."));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const oauthError = requestUrl.searchParams.get("error");
|
|
67
|
+
if (oauthError) {
|
|
68
|
+
const description = requestUrl.searchParams.get("error_description") ?? oauthError;
|
|
69
|
+
sendHtml(response, 400, oauthErrorHtml("Devin authorization was denied.", description));
|
|
70
|
+
finish({ error: new Error(`Devin authorization failed: ${description}`) });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// The state guard runs before the code is spent: a forged callback must
|
|
74
|
+
// never reach the token endpoint.
|
|
75
|
+
const state = requestUrl.searchParams.get("state") ?? "";
|
|
76
|
+
if (state !== input.state) {
|
|
77
|
+
sendHtml(response, 400, oauthErrorHtml("Devin authorization state mismatch."));
|
|
78
|
+
finish({ error: new Error("Devin OAuth callback state mismatch") });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const code = requestUrl.searchParams.get("code");
|
|
82
|
+
if (!code) {
|
|
83
|
+
sendHtml(response, 400, oauthErrorHtml("Devin returned no authorization code."));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
claimed = true;
|
|
87
|
+
try {
|
|
88
|
+
const result = await exchangeDevinAuthorizationCode(code, input.verifier, input.signal);
|
|
89
|
+
sendHtml(response, 200, oauthSuccessHtml("Signed in to Devin. You may now close this page."));
|
|
90
|
+
finish({ credential: result });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const message = error instanceof Error ? error.message : "Unknown token exchange error";
|
|
94
|
+
sendHtml(response, 502, oauthErrorHtml("Devin token exchange failed.", message));
|
|
95
|
+
finish({ error: error instanceof Error ? error : new Error(message) });
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
});
|
|
99
|
+
await new Promise((resolve, reject) => {
|
|
100
|
+
server.once("error", reject);
|
|
101
|
+
server.listen(DEVIN_CALLBACK_PORT, host, () => {
|
|
102
|
+
server.removeListener("error", reject);
|
|
103
|
+
resolve();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
server.on("error", (error) => finish({ error }));
|
|
107
|
+
onAbort = () => finish({ error: new Error("Login cancelled") });
|
|
108
|
+
input.signal.addEventListener("abort", onAbort, { once: true });
|
|
109
|
+
if (input.signal.aborted) {
|
|
110
|
+
close();
|
|
111
|
+
throw new Error("Login cancelled");
|
|
112
|
+
}
|
|
113
|
+
timeout = setTimeout(() => finish({ error: new Error("Devin OAuth login timed out") }), input.loginTimeoutMs);
|
|
114
|
+
return {
|
|
115
|
+
callbackUrl: `http://${host}:${DEVIN_CALLBACK_PORT}${DEVIN_CALLBACK_PATH}`,
|
|
116
|
+
close,
|
|
117
|
+
cancelWait: () => {
|
|
118
|
+
if (!claimed)
|
|
119
|
+
finish({ credential: null });
|
|
120
|
+
},
|
|
121
|
+
waitForCredential: () => credential,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=devin-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-callback.js","sourceRoot":"","sources":["../../../src/auth/oauth/devin-callback.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Devin CLI OAuth loopback callback.\n *\n * Devin registers ONE redirect URI for the CLI, so the callback binds the fixed\n * loopback port 59653 instead of an ephemeral one: a different port is rejected\n * by the authorize page. The server is one-shot, validates the issued state\n * before spending the code, and renders the shared OAuth result pages.\n */\n\nimport { createServer, type Server, type ServerResponse } from \"node:http\";\nimport { getProviderEnvValue } from \"../../utils/provider-env.ts\";\nimport type { OAuthCredential } from \"../types.ts\";\nimport { exchangeDevinAuthorizationCode } from \"./devin-token.ts\";\nimport { oauthErrorHtml, oauthSuccessHtml } from \"./oauth-page.ts\";\n\nexport const DEVIN_CALLBACK_PORT = 59653;\nexport const DEVIN_CALLBACK_PATH = \"/callback\";\n\nexport type DevinCallbackServer = {\n\tcallbackUrl: string;\n\tclose: () => void;\n\t/** Hand the login to manual code entry unless a callback already claimed it. */\n\tcancelWait: () => void;\n\twaitForCredential: () => Promise<OAuthCredential | null>;\n};\n\nfunction callbackHost(): string {\n\treturn getProviderEnvValue(\"PI_OAUTH_CALLBACK_HOST\") || \"127.0.0.1\";\n}\n\nfunction sendHtml(response: ServerResponse, status: number, html: string): void {\n\tresponse.statusCode = status;\n\tresponse.setHeader(\"content-type\", \"text/html; charset=utf-8\");\n\tresponse.setHeader(\"cache-control\", \"no-store\");\n\tresponse.end(html);\n}\n\nexport async function startDevinCallbackServer(input: {\n\tstate: string;\n\tverifier: string;\n\tsignal: AbortSignal;\n\tloginTimeoutMs: number;\n}): Promise<DevinCallbackServer> {\n\tif (input.signal.aborted) throw new Error(\"Login cancelled\");\n\tconst host = callbackHost();\n\tlet resolveCredential: (credential: OAuthCredential | null) => void = () => {};\n\tlet rejectCredential: (error: Error) => void = () => {};\n\tconst credential = new Promise<OAuthCredential | null>((resolve, reject) => {\n\t\tresolveCredential = resolve;\n\t\trejectCredential = reject;\n\t});\n\n\tlet claimed = false;\n\tlet settled = false;\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\tlet onAbort: (() => void) | undefined;\n\n\tconst close = (): void => {\n\t\tif (timeout) clearTimeout(timeout);\n\t\tif (onAbort) input.signal.removeEventListener(\"abort\", onAbort);\n\t\tserver.close();\n\t};\n\n\tconst finish = (result: { credential: OAuthCredential | null } | { error: Error }): void => {\n\t\tif (settled) return;\n\t\tsettled = true;\n\t\tclose();\n\t\tif (\"credential\" in result) resolveCredential(result.credential);\n\t\telse rejectCredential(result.error);\n\t};\n\n\tconst server: Server = createServer((request, response) => {\n\t\tvoid (async () => {\n\t\t\tconst requestUrl = new URL(request.url ?? \"/\", `http://${host}`);\n\t\t\tif (request.method !== \"GET\" || requestUrl.pathname !== DEVIN_CALLBACK_PATH) {\n\t\t\t\tsendHtml(response, 404, oauthErrorHtml(\"OAuth callback route not found.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (claimed || settled) {\n\t\t\t\tsendHtml(response, 409, oauthErrorHtml(\"This OAuth callback has already been used.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst oauthError = requestUrl.searchParams.get(\"error\");\n\t\t\tif (oauthError) {\n\t\t\t\tconst description = requestUrl.searchParams.get(\"error_description\") ?? oauthError;\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"Devin authorization was denied.\", description));\n\t\t\t\tfinish({ error: new Error(`Devin authorization failed: ${description}`) });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// The state guard runs before the code is spent: a forged callback must\n\t\t\t// never reach the token endpoint.\n\t\t\tconst state = requestUrl.searchParams.get(\"state\") ?? \"\";\n\t\t\tif (state !== input.state) {\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"Devin authorization state mismatch.\"));\n\t\t\t\tfinish({ error: new Error(\"Devin OAuth callback state mismatch\") });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst code = requestUrl.searchParams.get(\"code\");\n\t\t\tif (!code) {\n\t\t\t\tsendHtml(response, 400, oauthErrorHtml(\"Devin returned no authorization code.\"));\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tclaimed = true;\n\n\t\t\ttry {\n\t\t\t\tconst result = await exchangeDevinAuthorizationCode(code, input.verifier, input.signal);\n\t\t\t\tsendHtml(response, 200, oauthSuccessHtml(\"Signed in to Devin. You may now close this page.\"));\n\t\t\t\tfinish({ credential: result });\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"Unknown token exchange error\";\n\t\t\t\tsendHtml(response, 502, oauthErrorHtml(\"Devin token exchange failed.\", message));\n\t\t\t\tfinish({ error: error instanceof Error ? error : new Error(message) });\n\t\t\t}\n\t\t})();\n\t});\n\n\tawait new Promise<void>((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(DEVIN_CALLBACK_PORT, host, () => {\n\t\t\tserver.removeListener(\"error\", reject);\n\t\t\tresolve();\n\t\t});\n\t});\n\n\tserver.on(\"error\", (error) => finish({ error }));\n\tonAbort = () => finish({ error: new Error(\"Login cancelled\") });\n\tinput.signal.addEventListener(\"abort\", onAbort, { once: true });\n\tif (input.signal.aborted) {\n\t\tclose();\n\t\tthrow new Error(\"Login cancelled\");\n\t}\n\ttimeout = setTimeout(() => finish({ error: new Error(\"Devin OAuth login timed out\") }), input.loginTimeoutMs);\n\n\treturn {\n\t\tcallbackUrl: `http://${host}:${DEVIN_CALLBACK_PORT}${DEVIN_CALLBACK_PATH}`,\n\t\tclose,\n\t\tcancelWait: () => {\n\t\t\tif (!claimed) finish({ credential: null });\n\t\t},\n\t\twaitForCredential: () => credential,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin (Cognition) CLI token exchange.
|
|
3
|
+
*
|
|
4
|
+
* Devin's CLI grant is deliberately non-standard: the authorization code is
|
|
5
|
+
* redeemed with a bare JSON body carrying only the code and the PKCE verifier
|
|
6
|
+
* (no client_id, no grant_type), and the response returns ONE opaque JWT in
|
|
7
|
+
* "token" that serves as both the access and the refresh credential. Expiry is
|
|
8
|
+
* read from the JWT itself because the response carries no expires_in.
|
|
9
|
+
*/
|
|
10
|
+
import type { OAuthCredential } from "../types.ts";
|
|
11
|
+
export declare const DEVIN_TOKEN_URL = "https://api.devin.ai/auth/cli/token";
|
|
12
|
+
export declare const DEVIN_API_ENDPOINT = "https://api.devin.ai";
|
|
13
|
+
/** Devin issues long-lived CLI tokens; used when the JWT carries no usable exp. */
|
|
14
|
+
export declare const DEVIN_FALLBACK_EXPIRY_MS = 31536000000;
|
|
15
|
+
/** Builds the credential Devin's CLI stores: one token for access and refresh. */
|
|
16
|
+
export declare function devinCredential(token: string): OAuthCredential;
|
|
17
|
+
export declare function exchangeDevinAuthorizationCode(code: string, verifier: string, signal: AbortSignal): Promise<OAuthCredential>;
|
|
18
|
+
//# sourceMappingURL=devin-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-token.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/devin-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,eAAO,MAAM,eAAe,wCAAwC,CAAC;AACrE,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AAEzD,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,cAAiB,CAAC;AAgCvD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAO9D;AAED,wBAAsB,8BAA8B,CACnD,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GACjB,OAAO,CAAC,eAAe,CAAC,CA4C1B"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin (Cognition) CLI token exchange.
|
|
3
|
+
*
|
|
4
|
+
* Devin's CLI grant is deliberately non-standard: the authorization code is
|
|
5
|
+
* redeemed with a bare JSON body carrying only the code and the PKCE verifier
|
|
6
|
+
* (no client_id, no grant_type), and the response returns ONE opaque JWT in
|
|
7
|
+
* "token" that serves as both the access and the refresh credential. Expiry is
|
|
8
|
+
* read from the JWT itself because the response carries no expires_in.
|
|
9
|
+
*/
|
|
10
|
+
export const DEVIN_TOKEN_URL = "https://api.devin.ai/auth/cli/token";
|
|
11
|
+
export const DEVIN_API_ENDPOINT = "https://api.devin.ai";
|
|
12
|
+
/** Devin issues long-lived CLI tokens; used when the JWT carries no usable exp. */
|
|
13
|
+
export const DEVIN_FALLBACK_EXPIRY_MS = 31_536_000_000;
|
|
14
|
+
const TOKEN_EXCHANGE_TIMEOUT_MS = 30_000;
|
|
15
|
+
function decodeJwtExpiry(token) {
|
|
16
|
+
const payload = token.split(".")[1];
|
|
17
|
+
if (!payload)
|
|
18
|
+
return undefined;
|
|
19
|
+
const base64 = payload.replaceAll("-", "+").replaceAll("_", "/");
|
|
20
|
+
const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), "=");
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(atob(padded));
|
|
23
|
+
const exp = parsed.exp;
|
|
24
|
+
if (typeof exp === "number" && Number.isFinite(exp) && exp > 0)
|
|
25
|
+
return Math.trunc(exp * 1000);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// A token whose payload is not readable JSON falls back to the fixed window.
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
function errorDetail(body) {
|
|
33
|
+
if (typeof body.error_description === "string")
|
|
34
|
+
return body.error_description;
|
|
35
|
+
if (typeof body.message === "string")
|
|
36
|
+
return body.message;
|
|
37
|
+
if (typeof body.error === "string")
|
|
38
|
+
return body.error;
|
|
39
|
+
if (body.error && typeof body.error === "object" && !Array.isArray(body.error)) {
|
|
40
|
+
const message = body.error.message;
|
|
41
|
+
if (typeof message === "string")
|
|
42
|
+
return message;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
/** Builds the credential Devin's CLI stores: one token for access and refresh. */
|
|
47
|
+
export function devinCredential(token) {
|
|
48
|
+
return {
|
|
49
|
+
type: "oauth",
|
|
50
|
+
access: token,
|
|
51
|
+
refresh: token,
|
|
52
|
+
expires: decodeJwtExpiry(token) ?? Date.now() + DEVIN_FALLBACK_EXPIRY_MS,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export async function exchangeDevinAuthorizationCode(code, verifier, signal) {
|
|
56
|
+
if (signal.aborted)
|
|
57
|
+
throw new Error("Login cancelled");
|
|
58
|
+
const controller = new AbortController();
|
|
59
|
+
const onAbort = () => controller.abort(signal.reason);
|
|
60
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
61
|
+
const timeout = setTimeout(() => controller.abort(new Error("Devin OAuth token exchange timed out")), TOKEN_EXCHANGE_TIMEOUT_MS);
|
|
62
|
+
let response;
|
|
63
|
+
let body = {};
|
|
64
|
+
try {
|
|
65
|
+
response = await fetch(DEVIN_TOKEN_URL, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { accept: "application/json", "content-type": "application/json" },
|
|
68
|
+
body: JSON.stringify({ code, code_verifier: verifier }),
|
|
69
|
+
signal: controller.signal,
|
|
70
|
+
});
|
|
71
|
+
try {
|
|
72
|
+
const parsed = (await response.json());
|
|
73
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
74
|
+
body = parsed;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
if (response.ok)
|
|
78
|
+
throw new Error("Devin OAuth returned invalid JSON");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
if (signal.aborted)
|
|
83
|
+
throw new Error("Login cancelled");
|
|
84
|
+
if (controller.signal.aborted)
|
|
85
|
+
throw new Error("Devin OAuth token exchange timed out");
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
clearTimeout(timeout);
|
|
90
|
+
signal.removeEventListener("abort", onAbort);
|
|
91
|
+
}
|
|
92
|
+
if (!response.ok) {
|
|
93
|
+
const detail = errorDetail(body);
|
|
94
|
+
throw new Error(`Devin OAuth token exchange failed (HTTP ${response.status})${detail ? `: ${detail}` : ""}`);
|
|
95
|
+
}
|
|
96
|
+
if (typeof body.token !== "string" || body.token.length === 0) {
|
|
97
|
+
throw new Error('Devin OAuth response carries no "token"');
|
|
98
|
+
}
|
|
99
|
+
return devinCredential(body.token);
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=devin-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin-token.js","sourceRoot":"","sources":["../../../src/auth/oauth/devin-token.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Devin (Cognition) CLI token exchange.\n *\n * Devin's CLI grant is deliberately non-standard: the authorization code is\n * redeemed with a bare JSON body carrying only the code and the PKCE verifier\n * (no client_id, no grant_type), and the response returns ONE opaque JWT in\n * \"token\" that serves as both the access and the refresh credential. Expiry is\n * read from the JWT itself because the response carries no expires_in.\n */\n\nimport type { OAuthCredential } from \"../types.ts\";\n\nexport const DEVIN_TOKEN_URL = \"https://api.devin.ai/auth/cli/token\";\nexport const DEVIN_API_ENDPOINT = \"https://api.devin.ai\";\n\n/** Devin issues long-lived CLI tokens; used when the JWT carries no usable exp. */\nexport const DEVIN_FALLBACK_EXPIRY_MS = 31_536_000_000;\n\nconst TOKEN_EXCHANGE_TIMEOUT_MS = 30_000;\n\ntype JsonObject = Record<string, unknown>;\n\nfunction decodeJwtExpiry(token: string): number | undefined {\n\tconst payload = token.split(\".\")[1];\n\tif (!payload) return undefined;\n\tconst base64 = payload.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n\tconst padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), \"=\");\n\ttry {\n\t\tconst parsed = JSON.parse(atob(padded)) as JsonObject;\n\t\tconst exp = parsed.exp;\n\t\tif (typeof exp === \"number\" && Number.isFinite(exp) && exp > 0) return Math.trunc(exp * 1000);\n\t} catch {\n\t\t// A token whose payload is not readable JSON falls back to the fixed window.\n\t}\n\treturn undefined;\n}\n\nfunction errorDetail(body: JsonObject): string | undefined {\n\tif (typeof body.error_description === \"string\") return body.error_description;\n\tif (typeof body.message === \"string\") return body.message;\n\tif (typeof body.error === \"string\") return body.error;\n\tif (body.error && typeof body.error === \"object\" && !Array.isArray(body.error)) {\n\t\tconst message = (body.error as JsonObject).message;\n\t\tif (typeof message === \"string\") return message;\n\t}\n\treturn undefined;\n}\n\n/** Builds the credential Devin's CLI stores: one token for access and refresh. */\nexport function devinCredential(token: string): OAuthCredential {\n\treturn {\n\t\ttype: \"oauth\",\n\t\taccess: token,\n\t\trefresh: token,\n\t\texpires: decodeJwtExpiry(token) ?? Date.now() + DEVIN_FALLBACK_EXPIRY_MS,\n\t};\n}\n\nexport async function exchangeDevinAuthorizationCode(\n\tcode: string,\n\tverifier: string,\n\tsignal: AbortSignal,\n): Promise<OAuthCredential> {\n\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\tconst controller = new AbortController();\n\tconst onAbort = () => controller.abort(signal.reason);\n\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\tconst timeout = setTimeout(\n\t\t() => controller.abort(new Error(\"Devin OAuth token exchange timed out\")),\n\t\tTOKEN_EXCHANGE_TIMEOUT_MS,\n\t);\n\n\tlet response: Response;\n\tlet body: JsonObject = {};\n\ttry {\n\t\tresponse = await fetch(DEVIN_TOKEN_URL, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { accept: \"application/json\", \"content-type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ code, code_verifier: verifier }),\n\t\t\tsignal: controller.signal,\n\t\t});\n\t\ttry {\n\t\t\tconst parsed = (await response.json()) as unknown;\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) body = parsed as JsonObject;\n\t\t} catch {\n\t\t\tif (response.ok) throw new Error(\"Devin OAuth returned invalid JSON\");\n\t\t}\n\t} catch (error) {\n\t\tif (signal.aborted) throw new Error(\"Login cancelled\");\n\t\tif (controller.signal.aborted) throw new Error(\"Devin OAuth token exchange timed out\");\n\t\tthrow error;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t}\n\n\tif (!response.ok) {\n\t\tconst detail = errorDetail(body);\n\t\tthrow new Error(`Devin OAuth token exchange failed (HTTP ${response.status})${detail ? `: ${detail}` : \"\"}`);\n\t}\n\n\tif (typeof body.token !== \"string\" || body.token.length === 0) {\n\t\tthrow new Error('Devin OAuth response carries no \"token\"');\n\t}\n\n\treturn devinCredential(body.token);\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin (Cognition) CLI OAuth flow.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Devin CLI login: the authorize page at app.devin.ai redeems a
|
|
5
|
+
* PKCE S256 challenge against the fixed loopback redirect, and the resulting
|
|
6
|
+
* code is exchanged for a single long-lived CLI token. There is no refresh
|
|
7
|
+
* grant - Devin re-issues the token through a fresh login - so refresh returns
|
|
8
|
+
* the stored credential untouched.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: the callback module uses node:http and is CLI-only, never browser.
|
|
11
|
+
*/
|
|
12
|
+
import type { OAuthAuth } from "../types.ts";
|
|
13
|
+
export declare const devinOAuth: OAuthAuth;
|
|
14
|
+
//# sourceMappingURL=devin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/devin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AA0FvF,eAAO,MAAM,UAAU,EAAE,SAWxB,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Devin (Cognition) CLI OAuth flow.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Devin CLI login: the authorize page at app.devin.ai redeems a
|
|
5
|
+
* PKCE S256 challenge against the fixed loopback redirect, and the resulting
|
|
6
|
+
* code is exchanged for a single long-lived CLI token. There is no refresh
|
|
7
|
+
* grant - Devin re-issues the token through a fresh login - so refresh returns
|
|
8
|
+
* the stored credential untouched.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: the callback module uses node:http and is CLI-only, never browser.
|
|
11
|
+
*/
|
|
12
|
+
import { startDevinCallbackServer } from "./devin-callback.js";
|
|
13
|
+
import { DEVIN_API_ENDPOINT, exchangeDevinAuthorizationCode } from "./devin-token.js";
|
|
14
|
+
import { generatePKCE } from "./pkce.js";
|
|
15
|
+
const AUTHORIZE_URL = "https://app.devin.ai/auth/cli/continue";
|
|
16
|
+
const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
17
|
+
function parseAuthorizationInput(input) {
|
|
18
|
+
const value = input.trim();
|
|
19
|
+
if (!value)
|
|
20
|
+
return undefined;
|
|
21
|
+
try {
|
|
22
|
+
return new URL(value).searchParams.get("code") ?? undefined;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// not a URL
|
|
26
|
+
}
|
|
27
|
+
if (value.includes("code="))
|
|
28
|
+
return new URLSearchParams(value).get("code") ?? undefined;
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function authorizeUrl(input) {
|
|
32
|
+
const url = new URL(AUTHORIZE_URL);
|
|
33
|
+
url.search = new URLSearchParams({
|
|
34
|
+
response_type: "code",
|
|
35
|
+
redirect_uri: input.redirectUri,
|
|
36
|
+
code_challenge: input.challenge,
|
|
37
|
+
code_challenge_method: "S256",
|
|
38
|
+
state: input.state,
|
|
39
|
+
prompt: "select_account",
|
|
40
|
+
}).toString();
|
|
41
|
+
return url.toString();
|
|
42
|
+
}
|
|
43
|
+
async function loginDevin(interaction) {
|
|
44
|
+
const { verifier, challenge } = await generatePKCE();
|
|
45
|
+
const state = crypto.randomUUID();
|
|
46
|
+
const callback = await startDevinCallbackServer({
|
|
47
|
+
state,
|
|
48
|
+
verifier,
|
|
49
|
+
signal: interaction.signal,
|
|
50
|
+
loginTimeoutMs: LOGIN_TIMEOUT_MS,
|
|
51
|
+
});
|
|
52
|
+
const manualAbort = new AbortController();
|
|
53
|
+
let manualInput;
|
|
54
|
+
let manualError;
|
|
55
|
+
try {
|
|
56
|
+
interaction.notify({
|
|
57
|
+
type: "progress",
|
|
58
|
+
message: `Listening for the Devin OAuth callback on ${callback.callbackUrl}`,
|
|
59
|
+
});
|
|
60
|
+
interaction.notify({
|
|
61
|
+
type: "auth_url",
|
|
62
|
+
url: authorizeUrl({ redirectUri: callback.callbackUrl, challenge, state }),
|
|
63
|
+
instructions: "Sign in to Devin in your browser. If the browser is on another machine, paste the final redirect URL here.",
|
|
64
|
+
});
|
|
65
|
+
const manualPromise = interaction
|
|
66
|
+
.prompt({
|
|
67
|
+
type: "manual_code",
|
|
68
|
+
message: "Complete sign-in in your browser, or paste the authorization code / redirect URL here:",
|
|
69
|
+
placeholder: callback.callbackUrl,
|
|
70
|
+
signal: manualAbort.signal,
|
|
71
|
+
})
|
|
72
|
+
.then((input) => {
|
|
73
|
+
manualInput = input;
|
|
74
|
+
callback.cancelWait();
|
|
75
|
+
})
|
|
76
|
+
.catch((error) => {
|
|
77
|
+
manualError = error instanceof Error ? error : new Error(String(error));
|
|
78
|
+
callback.cancelWait();
|
|
79
|
+
});
|
|
80
|
+
const credential = await callback.waitForCredential();
|
|
81
|
+
if (manualError)
|
|
82
|
+
throw manualError;
|
|
83
|
+
if (credential)
|
|
84
|
+
return credential;
|
|
85
|
+
await manualPromise;
|
|
86
|
+
if (manualError)
|
|
87
|
+
throw manualError;
|
|
88
|
+
const code = manualInput ? parseAuthorizationInput(manualInput) : undefined;
|
|
89
|
+
if (!code)
|
|
90
|
+
throw new Error("Missing authorization code");
|
|
91
|
+
interaction.notify({ type: "progress", message: "Exchanging the authorization code for a Devin token..." });
|
|
92
|
+
return await exchangeDevinAuthorizationCode(code, verifier, interaction.signal);
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
manualAbort.abort();
|
|
96
|
+
callback.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export const devinOAuth = {
|
|
100
|
+
name: "Devin",
|
|
101
|
+
isSubscription: true,
|
|
102
|
+
loginLabel: "Sign in with Devin",
|
|
103
|
+
login: loginDevin,
|
|
104
|
+
async refresh(credential, _signal) {
|
|
105
|
+
return credential;
|
|
106
|
+
},
|
|
107
|
+
async toAuth(credential) {
|
|
108
|
+
return { apiKey: credential.access, baseUrl: DEVIN_API_ENDPOINT };
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=devin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devin.js","sourceRoot":"","sources":["../../../src/auth/oauth/devin.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Devin (Cognition) CLI OAuth flow.\n *\n * Mirrors the Devin CLI login: the authorize page at app.devin.ai redeems a\n * PKCE S256 challenge against the fixed loopback redirect, and the resulting\n * code is exchanged for a single long-lived CLI token. There is no refresh\n * grant - Devin re-issues the token through a fresh login - so refresh returns\n * the stored credential untouched.\n *\n * NOTE: the callback module uses node:http and is CLI-only, never browser.\n */\n\nimport type { OAuthAuth, OAuthCredential, ProviderAuthInteraction } from \"../types.ts\";\nimport { startDevinCallbackServer } from \"./devin-callback.ts\";\nimport { DEVIN_API_ENDPOINT, exchangeDevinAuthorizationCode } from \"./devin-token.ts\";\nimport { generatePKCE } from \"./pkce.ts\";\n\nconst AUTHORIZE_URL = \"https://app.devin.ai/auth/cli/continue\";\nconst LOGIN_TIMEOUT_MS = 5 * 60 * 1000;\n\nfunction parseAuthorizationInput(input: string): string | undefined {\n\tconst value = input.trim();\n\tif (!value) return undefined;\n\ttry {\n\t\treturn new URL(value).searchParams.get(\"code\") ?? undefined;\n\t} catch {\n\t\t// not a URL\n\t}\n\tif (value.includes(\"code=\")) return new URLSearchParams(value).get(\"code\") ?? undefined;\n\treturn value;\n}\n\nfunction authorizeUrl(input: { redirectUri: string; challenge: string; state: string }): string {\n\tconst url = new URL(AUTHORIZE_URL);\n\turl.search = new URLSearchParams({\n\t\tresponse_type: \"code\",\n\t\tredirect_uri: input.redirectUri,\n\t\tcode_challenge: input.challenge,\n\t\tcode_challenge_method: \"S256\",\n\t\tstate: input.state,\n\t\tprompt: \"select_account\",\n\t}).toString();\n\treturn url.toString();\n}\n\nasync function loginDevin(interaction: ProviderAuthInteraction): Promise<OAuthCredential> {\n\tconst { verifier, challenge } = await generatePKCE();\n\tconst state = crypto.randomUUID();\n\tconst callback = await startDevinCallbackServer({\n\t\tstate,\n\t\tverifier,\n\t\tsignal: interaction.signal,\n\t\tloginTimeoutMs: LOGIN_TIMEOUT_MS,\n\t});\n\tconst manualAbort = new AbortController();\n\tlet manualInput: string | undefined;\n\tlet manualError: Error | undefined;\n\n\ttry {\n\t\tinteraction.notify({\n\t\t\ttype: \"progress\",\n\t\t\tmessage: `Listening for the Devin OAuth callback on ${callback.callbackUrl}`,\n\t\t});\n\t\tinteraction.notify({\n\t\t\ttype: \"auth_url\",\n\t\t\turl: authorizeUrl({ redirectUri: callback.callbackUrl, challenge, state }),\n\t\t\tinstructions:\n\t\t\t\t\"Sign in to Devin in your browser. If the browser is on another machine, paste the final redirect URL here.\",\n\t\t});\n\n\t\tconst manualPromise = interaction\n\t\t\t.prompt({\n\t\t\t\ttype: \"manual_code\",\n\t\t\t\tmessage: \"Complete sign-in in your browser, or paste the authorization code / redirect URL here:\",\n\t\t\t\tplaceholder: callback.callbackUrl,\n\t\t\t\tsignal: manualAbort.signal,\n\t\t\t})\n\t\t\t.then((input) => {\n\t\t\t\tmanualInput = input;\n\t\t\t\tcallback.cancelWait();\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\tmanualError = error instanceof Error ? error : new Error(String(error));\n\t\t\t\tcallback.cancelWait();\n\t\t\t});\n\n\t\tconst credential = await callback.waitForCredential();\n\t\tif (manualError) throw manualError;\n\t\tif (credential) return credential;\n\n\t\tawait manualPromise;\n\t\tif (manualError) throw manualError;\n\t\tconst code = manualInput ? parseAuthorizationInput(manualInput) : undefined;\n\t\tif (!code) throw new Error(\"Missing authorization code\");\n\t\tinteraction.notify({ type: \"progress\", message: \"Exchanging the authorization code for a Devin token...\" });\n\t\treturn await exchangeDevinAuthorizationCode(code, verifier, interaction.signal);\n\t} finally {\n\t\tmanualAbort.abort();\n\t\tcallback.close();\n\t}\n}\n\nexport const devinOAuth: OAuthAuth = {\n\tname: \"Devin\",\n\tisSubscription: true,\n\tloginLabel: \"Sign in with Devin\",\n\tlogin: loginDevin,\n\tasync refresh(credential, _signal) {\n\t\treturn credential;\n\t},\n\tasync toAuth(credential) {\n\t\treturn { apiKey: credential.access, baseUrl: DEVIN_API_ENDPOINT };\n\t},\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kimi-coding.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/kimi-coding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"kimi-coding.d.ts","sourceRoot":"","sources":["../../../src/auth/oauth/kimi-coding.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,SAAS,EAA4C,MAAM,aAAa,CAAC;AAkRvF,eAAO,MAAM,eAAe,EAAE,SAe7B,CAAC"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { getProviderEnvValue } from "../../utils/provider-env.js";
|
|
9
9
|
import { sleep } from "../../utils/sleep.js";
|
|
10
10
|
import { pollOAuthDeviceCodeFlow } from "./device-code.js";
|
|
11
|
+
import { kimiCodeIdentityHeaders } from "./kimi-identity.js";
|
|
11
12
|
const CLIENT_ID = "17e5f671-d194-4dfb-9706-5516cb48c098";
|
|
12
13
|
const DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
|
|
13
14
|
const DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;
|
|
@@ -51,6 +52,7 @@ async function startDeviceAuthorization(oauthHost, signal) {
|
|
|
51
52
|
const response = await fetch(`${oauthHost}/api/oauth/device_authorization`, {
|
|
52
53
|
method: "POST",
|
|
53
54
|
headers: {
|
|
55
|
+
...kimiCodeIdentityHeaders(),
|
|
54
56
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
55
57
|
Accept: "application/json",
|
|
56
58
|
},
|
|
@@ -118,6 +120,7 @@ async function pollForToken(oauthHost, device, signal) {
|
|
|
118
120
|
const response = await fetch(`${oauthHost}/api/oauth/token`, {
|
|
119
121
|
method: "POST",
|
|
120
122
|
headers: {
|
|
123
|
+
...kimiCodeIdentityHeaders(),
|
|
121
124
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
122
125
|
Accept: "application/json",
|
|
123
126
|
},
|
|
@@ -186,6 +189,7 @@ async function refreshToken(oauthHost, refreshTokenValue, signal) {
|
|
|
186
189
|
response = await fetch(`${oauthHost}/api/oauth/token`, {
|
|
187
190
|
method: "POST",
|
|
188
191
|
headers: {
|
|
192
|
+
...kimiCodeIdentityHeaders(),
|
|
189
193
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
190
194
|
Accept: "application/json",
|
|
191
195
|
},
|
|
@@ -242,7 +246,7 @@ export const kimiCodingOAuth = {
|
|
|
242
246
|
return { type: "oauth", access: token.access, refresh: token.refresh, expires: token.expires };
|
|
243
247
|
},
|
|
244
248
|
async toAuth(credential) {
|
|
245
|
-
return { headers: { Authorization: `Bearer ${credential.access}` } };
|
|
249
|
+
return { headers: { ...kimiCodeIdentityHeaders(), Authorization: `Bearer ${credential.access}` } };
|
|
246
250
|
},
|
|
247
251
|
};
|
|
248
252
|
//# sourceMappingURL=kimi-coding.js.map
|