@clinebot/core 0.0.27 → 0.0.29
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/README.md +7 -0
- package/dist/ClineCore.d.ts +28 -2
- package/dist/ClineCore.d.ts.map +1 -1
- package/dist/account/cline-account-service.d.ts +1 -1
- package/dist/account/cline-account-service.d.ts.map +1 -1
- package/dist/account/index.d.ts +1 -1
- package/dist/account/index.d.ts.map +1 -1
- package/dist/account/types.d.ts +5 -0
- package/dist/account/types.d.ts.map +1 -1
- package/dist/auth/bounded-ttl-cache.d.ts +14 -0
- package/dist/auth/bounded-ttl-cache.d.ts.map +1 -0
- package/dist/auth/cline.d.ts +27 -2
- package/dist/auth/cline.d.ts.map +1 -1
- package/dist/auth/oca.d.ts.map +1 -1
- package/dist/chat/chat-schema.d.ts +8 -8
- package/dist/extensions/config/agent-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/agent-config-parser.d.ts +2 -2
- package/dist/extensions/config/agent-config-parser.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/hooks-config-loader.d.ts +1 -1
- package/dist/extensions/config/hooks-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/index.d.ts +2 -4
- package/dist/extensions/config/index.d.ts.map +1 -0
- package/dist/{runtime/commands.d.ts → extensions/config/runtime-commands.d.ts} +2 -3
- package/dist/extensions/config/runtime-commands.d.ts.map +1 -0
- package/dist/extensions/config/unified-config-file-watcher.d.ts.map +1 -0
- package/dist/extensions/config/user-instruction-config-loader.d.ts.map +1 -0
- package/dist/extensions/context/agentic-compaction.d.ts +13 -0
- package/dist/extensions/context/agentic-compaction.d.ts.map +1 -0
- package/dist/extensions/context/basic-compaction.d.ts +9 -0
- package/dist/extensions/context/basic-compaction.d.ts.map +1 -0
- package/dist/extensions/context/compaction-shared.d.ts +60 -0
- package/dist/extensions/context/compaction-shared.d.ts.map +1 -0
- package/dist/extensions/context/compaction.d.ts +20 -0
- package/dist/extensions/context/compaction.d.ts.map +1 -0
- package/dist/extensions/index.d.ts +5 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/mcp/client.d.ts +3 -0
- package/dist/extensions/mcp/client.d.ts.map +1 -0
- package/dist/extensions/mcp/config-loader.d.ts.map +1 -0
- package/dist/extensions/mcp/index.d.ts +9 -0
- package/dist/extensions/mcp/index.d.ts.map +1 -0
- package/dist/{mcp → extensions/mcp}/manager.d.ts +1 -2
- package/dist/extensions/mcp/manager.d.ts.map +1 -0
- package/dist/extensions/mcp/name-transform.d.ts +3 -0
- package/dist/extensions/mcp/name-transform.d.ts.map +1 -0
- package/dist/extensions/mcp/policies.d.ts +15 -0
- package/dist/extensions/mcp/policies.d.ts.map +1 -0
- package/dist/extensions/mcp/tools.d.ts +4 -0
- package/dist/extensions/mcp/tools.d.ts.map +1 -0
- package/dist/{mcp → extensions/mcp}/types.d.ts +29 -1
- package/dist/extensions/mcp/types.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-config-loader.d.ts +1 -1
- package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-loader.d.ts +1 -1
- package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -0
- package/dist/extensions/plugin/plugin-module-import.d.ts +5 -0
- package/dist/extensions/plugin/plugin-module-import.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-sandbox.d.ts +1 -1
- package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -0
- package/dist/extensions/plugin-sandbox-bootstrap.js +485 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/persistent.d.ts +64 -0
- package/dist/hooks/persistent.d.ts.map +1 -0
- package/dist/hooks/subprocess-runner.d.ts +22 -0
- package/dist/hooks/subprocess-runner.d.ts.map +1 -0
- package/dist/hooks/subprocess.d.ts +189 -0
- package/dist/hooks/subprocess.d.ts.map +1 -0
- package/dist/index.d.ts +22 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +560 -447
- package/dist/prompt/default-system.d.ts +2 -0
- package/dist/prompt/default-system.d.ts.map +1 -0
- package/dist/providers/local-provider-service.d.ts +1 -1
- package/dist/providers/local-provider-service.d.ts.map +1 -1
- package/dist/runtime/checkpoint-hooks.d.ts +21 -0
- package/dist/runtime/checkpoint-hooks.d.ts.map +1 -0
- package/dist/runtime/hook-file-hooks.d.ts +1 -1
- package/dist/runtime/hook-file-hooks.d.ts.map +1 -1
- package/dist/runtime/rules.d.ts +1 -1
- package/dist/runtime/rules.d.ts.map +1 -1
- package/dist/runtime/runtime-builder.d.ts +1 -1
- package/dist/runtime/runtime-builder.d.ts.map +1 -1
- package/dist/runtime/session-runtime.d.ts +25 -5
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/runtime/subprocess-sandbox.d.ts.map +1 -0
- package/dist/runtime/team-runtime-registry.d.ts +1 -1
- package/dist/runtime/team-runtime-registry.d.ts.map +1 -1
- package/dist/runtime/tool-approval.d.ts +1 -1
- package/dist/session/default-session-manager.d.ts +4 -3
- package/dist/session/default-session-manager.d.ts.map +1 -1
- package/dist/session/file-session-service.d.ts +1 -1
- package/dist/session/file-session-service.d.ts.map +1 -1
- package/dist/session/{unified-session-persistence-service.d.ts → persistence-service.d.ts} +11 -42
- package/dist/session/persistence-service.d.ts.map +1 -0
- package/dist/session/rpc-session-service.d.ts +1 -1
- package/dist/session/rpc-session-service.d.ts.map +1 -1
- package/dist/session/session-agent-events.d.ts +1 -1
- package/dist/session/session-artifacts.d.ts.map +1 -1
- package/dist/session/session-config-builder.d.ts.map +1 -1
- package/dist/session/session-graph.d.ts +1 -1
- package/dist/session/session-graph.d.ts.map +1 -1
- package/dist/session/session-host.d.ts.map +1 -1
- package/dist/session/session-manager.d.ts +6 -5
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manifest.d.ts +1 -1
- package/dist/session/session-service.d.ts +3 -2
- package/dist/session/session-service.d.ts.map +1 -1
- package/dist/session/session-team-coordination.d.ts +2 -1
- package/dist/session/session-team-coordination.d.ts.map +1 -1
- package/dist/session/utils/helpers.d.ts +51 -3
- package/dist/session/utils/helpers.d.ts.map +1 -1
- package/dist/session/utils/types.d.ts +41 -7
- package/dist/session/utils/types.d.ts.map +1 -1
- package/dist/session/workspace-manager.d.ts +1 -2
- package/dist/session/workspace-manager.d.ts.map +1 -1
- package/dist/session/workspace-manifest.d.ts +1 -22
- package/dist/session/workspace-manifest.d.ts.map +1 -1
- package/dist/storage/file-team-store.d.ts +2 -1
- package/dist/storage/file-team-store.d.ts.map +1 -1
- package/dist/storage/sqlite-team-store.d.ts +4 -1
- package/dist/storage/sqlite-team-store.d.ts.map +1 -1
- package/dist/storage/team-store.d.ts.map +1 -1
- package/dist/team/delegated-agent.d.ts +44 -0
- package/dist/team/delegated-agent.d.ts.map +1 -0
- package/dist/team/index.d.ts +1 -0
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/multi-agent.d.ts +229 -0
- package/dist/team/multi-agent.d.ts.map +1 -0
- package/dist/team/projections.d.ts +2 -2
- package/dist/team/projections.d.ts.map +1 -1
- package/dist/team/runtime.d.ts +5 -0
- package/dist/team/runtime.d.ts.map +1 -0
- package/dist/team/spawn-agent-tool.d.ts +85 -0
- package/dist/team/spawn-agent-tool.d.ts.map +1 -0
- package/dist/team/subagent-prompts.d.ts +4 -0
- package/dist/team/subagent-prompts.d.ts.map +1 -0
- package/dist/team/team-tools.d.ts +35 -0
- package/dist/team/team-tools.d.ts.map +1 -0
- package/dist/telemetry/OpenTelemetryProvider.d.ts +11 -1
- package/dist/telemetry/OpenTelemetryProvider.d.ts.map +1 -1
- package/dist/telemetry/{LoggerTelemetryAdapter.d.ts → TelemetryLoggerSink.d.ts} +10 -4
- package/dist/telemetry/TelemetryLoggerSink.d.ts.map +1 -0
- package/dist/telemetry/TelemetryService.d.ts.map +1 -1
- package/dist/telemetry/index.js +15 -28
- package/dist/tools/definitions.d.ts +4 -3
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/index.d.ts +5 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/model-tool-routing.d.ts.map +1 -1
- package/dist/tools/presets.d.ts +26 -0
- package/dist/tools/presets.d.ts.map +1 -1
- package/dist/tools/schemas.d.ts +8 -0
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/types.d.ts +23 -2
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/config.d.ts +47 -3
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/events.d.ts +1 -1
- package/dist/types/provider-settings.d.ts +1 -1
- package/dist/types/provider-settings.d.ts.map +1 -1
- package/dist/types/storage.d.ts +2 -1
- package/dist/types/storage.d.ts.map +1 -1
- package/dist/types.d.ts +7 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +16 -13
- package/src/ClineCore.test.ts +150 -0
- package/src/ClineCore.ts +114 -8
- package/src/account/cline-account-service.test.ts +84 -0
- package/src/account/cline-account-service.ts +2 -2
- package/src/account/index.ts +1 -0
- package/src/account/types.ts +6 -0
- package/src/auth/bounded-ttl-cache.test.ts +38 -0
- package/src/auth/bounded-ttl-cache.ts +53 -0
- package/src/auth/cline.test.ts +173 -36
- package/src/auth/cline.ts +395 -93
- package/src/auth/oca.test.ts +125 -0
- package/src/auth/oca.ts +17 -4
- package/src/{agents → extensions/config}/agent-config-loader.test.ts +1 -1
- package/src/{agents → extensions/config}/agent-config-parser.ts +2 -2
- package/src/{agents → extensions/config}/hooks-config-loader.ts +1 -1
- package/src/{agents → extensions/config}/index.ts +7 -11
- package/src/{runtime/commands.test.ts → extensions/config/runtime-commands.test.ts} +20 -3
- package/src/{runtime/commands.ts → extensions/config/runtime-commands.ts} +1 -8
- package/src/{agents → extensions/config}/unified-config-file-watcher.ts +15 -2
- package/src/{agents → extensions/config}/user-instruction-config-loader.test.ts +90 -2
- package/src/{agents → extensions/config}/user-instruction-config-loader.ts +126 -12
- package/src/extensions/context/agentic-compaction.ts +119 -0
- package/src/extensions/context/basic-compaction.ts +275 -0
- package/src/extensions/context/compaction-shared.ts +458 -0
- package/src/extensions/context/compaction.test.ts +477 -0
- package/src/extensions/context/compaction.ts +203 -0
- package/src/extensions/index.ts +12 -0
- package/src/extensions/mcp/client.ts +420 -0
- package/src/{mcp → extensions/mcp}/index.ts +16 -0
- package/src/{mcp → extensions/mcp}/manager.test.ts +1 -2
- package/src/{mcp → extensions/mcp}/manager.ts +3 -5
- package/src/extensions/mcp/name-transform.ts +33 -0
- package/src/extensions/mcp/policies.ts +47 -0
- package/src/extensions/mcp/tools.ts +47 -0
- package/src/{mcp → extensions/mcp}/types.ts +35 -7
- package/src/{agents → extensions/plugin}/plugin-config-loader.test.ts +18 -13
- package/src/{agents → extensions/plugin}/plugin-config-loader.ts +1 -1
- package/src/{agents → extensions/plugin}/plugin-loader.test.ts +41 -4
- package/src/extensions/plugin/plugin-loader.ts +106 -0
- package/src/extensions/plugin/plugin-module-import.ts +278 -0
- package/src/{agents → extensions/plugin}/plugin-sandbox-bootstrap.ts +30 -92
- package/src/{agents → extensions/plugin}/plugin-sandbox.test.ts +60 -3
- package/src/{agents → extensions/plugin}/plugin-sandbox.ts +146 -56
- package/src/hooks/index.ts +25 -0
- package/src/hooks/persistent.ts +661 -0
- package/src/hooks/subprocess-runner.ts +196 -0
- package/src/hooks/subprocess.ts +669 -0
- package/src/index.ts +200 -118
- package/src/prompt/default-system.ts +21 -0
- package/src/providers/local-provider-registry.ts +1 -1
- package/src/providers/local-provider-service.test.ts +23 -2
- package/src/providers/local-provider-service.ts +2 -2
- package/src/runtime/checkpoint-hooks.test.ts +167 -0
- package/src/runtime/checkpoint-hooks.ts +186 -0
- package/src/runtime/hook-file-hooks.test.ts +40 -1
- package/src/runtime/hook-file-hooks.ts +35 -16
- package/src/runtime/index.ts +4 -19
- package/src/runtime/rules.ts +4 -1
- package/src/runtime/runtime-builder.team-persistence.test.ts +3 -6
- package/src/runtime/runtime-builder.test.ts +266 -160
- package/src/runtime/runtime-builder.ts +120 -47
- package/src/runtime/runtime-parity.test.ts +22 -22
- package/src/runtime/session-runtime.ts +36 -6
- package/src/runtime/{sandbox/subprocess-sandbox.ts → subprocess-sandbox.ts} +24 -3
- package/src/runtime/team-runtime-registry.ts +1 -4
- package/src/runtime/tool-approval.ts +1 -1
- package/src/session/default-session-manager.e2e.test.ts +2 -2
- package/src/session/default-session-manager.test.ts +553 -9
- package/src/session/default-session-manager.ts +140 -46
- package/src/session/file-session-service.ts +3 -3
- package/src/session/index.ts +6 -6
- package/src/session/persistence-service.test.ts +212 -0
- package/src/session/{unified-session-persistence-service.ts → persistence-service.ts} +106 -172
- package/src/session/rpc-session-service.ts +3 -3
- package/src/session/runtime-oauth-token-manager.ts +1 -1
- package/src/session/session-agent-events.ts +1 -1
- package/src/session/session-artifacts.ts +32 -4
- package/src/session/session-config-builder.ts +22 -9
- package/src/session/session-graph.ts +1 -1
- package/src/session/session-host.ts +19 -11
- package/src/session/session-manager.ts +11 -6
- package/src/session/session-service.team-persistence.test.ts +1 -1
- package/src/session/session-service.ts +6 -9
- package/src/session/session-team-coordination.ts +7 -3
- package/src/session/session-telemetry.ts +1 -1
- package/src/session/utils/helpers.test.ts +160 -0
- package/src/session/utils/helpers.ts +289 -42
- package/src/session/utils/types.ts +47 -7
- package/src/session/workspace-manager.ts +5 -3
- package/src/session/workspace-manifest.ts +3 -49
- package/src/storage/file-team-store.ts +2 -5
- package/src/storage/provider-settings-legacy-migration.ts +2 -2
- package/src/storage/provider-settings-manager.test.ts +1 -1
- package/src/storage/sqlite-team-store.ts +212 -125
- package/src/storage/team-store.ts +1 -5
- package/src/team/delegated-agent.ts +131 -0
- package/src/team/index.ts +1 -0
- package/src/team/multi-agent.lifecycle.test.ts +201 -0
- package/src/team/multi-agent.ts +1666 -0
- package/src/team/projections.ts +2 -4
- package/src/team/runtime.ts +54 -0
- package/src/team/spawn-agent-tool.test.ts +387 -0
- package/src/team/spawn-agent-tool.ts +207 -0
- package/src/team/subagent-prompts.ts +41 -0
- package/src/team/team-tools.test.ts +802 -0
- package/src/team/team-tools.ts +792 -0
- package/src/telemetry/OpenTelemetryProvider.test.ts +25 -3
- package/src/telemetry/OpenTelemetryProvider.ts +108 -18
- package/src/telemetry/TelemetryLoggerSink.test.ts +42 -0
- package/src/telemetry/{LoggerTelemetryAdapter.ts → TelemetryLoggerSink.ts} +21 -14
- package/src/telemetry/TelemetryService.test.ts +7 -7
- package/src/telemetry/TelemetryService.ts +2 -4
- package/src/tools/definitions.test.ts +76 -0
- package/src/tools/definitions.ts +41 -2
- package/src/tools/executors/apply-patch.ts +1 -1
- package/src/tools/executors/editor.ts +1 -1
- package/src/tools/executors/file-read.ts +1 -1
- package/src/tools/executors/search.ts +1 -1
- package/src/tools/executors/web-fetch.ts +1 -1
- package/src/tools/index.ts +6 -1
- package/src/tools/model-tool-routing.ts +2 -0
- package/src/tools/presets.test.ts +8 -0
- package/src/tools/presets.ts +40 -2
- package/src/tools/schemas.ts +19 -0
- package/src/tools/types.ts +31 -2
- package/src/types/config.ts +61 -7
- package/src/types/events.ts +1 -1
- package/src/types/index.ts +0 -1
- package/src/types/provider-settings.ts +1 -1
- package/src/types/storage.ts +2 -5
- package/src/types.ts +32 -44
- package/dist/agents/agent-config-loader.d.ts.map +0 -1
- package/dist/agents/agent-config-parser.d.ts.map +0 -1
- package/dist/agents/hooks-config-loader.d.ts.map +0 -1
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/plugin-config-loader.d.ts.map +0 -1
- package/dist/agents/plugin-loader.d.ts.map +0 -1
- package/dist/agents/plugin-sandbox-bootstrap.js +0 -446
- package/dist/agents/plugin-sandbox.d.ts.map +0 -1
- package/dist/agents/unified-config-file-watcher.d.ts.map +0 -1
- package/dist/agents/user-instruction-config-loader.d.ts.map +0 -1
- package/dist/mcp/config-loader.d.ts.map +0 -1
- package/dist/mcp/index.d.ts +0 -5
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/manager.d.ts.map +0 -1
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/runtime/commands.d.ts.map +0 -1
- package/dist/runtime/sandbox/subprocess-sandbox.d.ts.map +0 -1
- package/dist/runtime/skills.d.ts +0 -14
- package/dist/runtime/skills.d.ts.map +0 -1
- package/dist/runtime/workflows.d.ts +0 -14
- package/dist/runtime/workflows.d.ts.map +0 -1
- package/dist/session/unified-session-persistence-service.d.ts.map +0 -1
- package/dist/telemetry/LoggerTelemetryAdapter.d.ts.map +0 -1
- package/dist/types/workspace.d.ts +0 -8
- package/dist/types/workspace.d.ts.map +0 -1
- package/src/agents/plugin-loader.ts +0 -175
- package/src/runtime/skills.ts +0 -44
- package/src/runtime/workflows.test.ts +0 -119
- package/src/runtime/workflows.ts +0 -45
- package/src/session/unified-session-persistence-service.test.ts +0 -85
- package/src/telemetry/LoggerTelemetryAdapter.test.ts +0 -42
- package/src/types/workspace.ts +0 -7
- /package/dist/{agents → extensions/config}/agent-config-loader.d.ts +0 -0
- /package/dist/{agents → extensions/config}/unified-config-file-watcher.d.ts +0 -0
- /package/dist/{agents → extensions/config}/user-instruction-config-loader.d.ts +0 -0
- /package/dist/{mcp → extensions/mcp}/config-loader.d.ts +0 -0
- /package/dist/runtime/{sandbox/subprocess-sandbox.d.ts → subprocess-sandbox.d.ts} +0 -0
- /package/src/{agents → extensions/config}/agent-config-loader.ts +0 -0
- /package/src/{agents → extensions/config}/hooks-config-loader.test.ts +0 -0
- /package/src/{agents → extensions/config}/unified-config-file-watcher.test.ts +0 -0
- /package/src/{mcp → extensions/mcp}/config-loader.test.ts +0 -0
- /package/src/{mcp → extensions/mcp}/config-loader.ts +0 -0
package/src/auth/cline.ts
CHANGED
|
@@ -23,9 +23,17 @@ import {
|
|
|
23
23
|
const DEFAULT_AUTH_ENDPOINTS = {
|
|
24
24
|
authorize: "/api/v1/auth/authorize",
|
|
25
25
|
token: "/api/v1/auth/token",
|
|
26
|
+
register: "/api/v1/auth/register",
|
|
26
27
|
refresh: "/api/v1/auth/refresh",
|
|
27
28
|
} as const;
|
|
28
29
|
|
|
30
|
+
const DEFAULT_WORKOS_ENDPOINTS = {
|
|
31
|
+
deviceAuthorization: "/user_management/authorize/device",
|
|
32
|
+
authenticate: "/user_management/authenticate",
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
const DEFAULT_WORKOS_API_BASE_URL = "https://api.workos.com";
|
|
36
|
+
const DEFAULT_WORKOS_CLIENT_ID = "client_01K3A541FN8TA3EPPHTD2325AR";
|
|
29
37
|
const DEFAULT_CALLBACK_PATH = "/auth";
|
|
30
38
|
const DEFAULT_CALLBACK_PORTS = Array.from(
|
|
31
39
|
{ length: 11 },
|
|
@@ -34,6 +42,8 @@ const DEFAULT_CALLBACK_PORTS = Array.from(
|
|
|
34
42
|
const DEFAULT_REFRESH_BUFFER_MS = 5 * 60 * 1000;
|
|
35
43
|
const DEFAULT_RETRYABLE_TOKEN_GRACE_MS = 30 * 1000;
|
|
36
44
|
const DEFAULT_HTTP_TIMEOUT_MS = 30 * 1000;
|
|
45
|
+
const DEFAULT_DEVICE_AUTH_EXPIRES_IN_SECONDS = 300;
|
|
46
|
+
const DEFAULT_DEVICE_AUTH_INTERVAL_SECONDS = 5;
|
|
37
47
|
|
|
38
48
|
export type ClineTokenResolution = {
|
|
39
49
|
forceRefresh?: boolean;
|
|
@@ -68,10 +78,15 @@ type HeaderInput = HeaderMap | (() => Promise<HeaderMap> | HeaderMap);
|
|
|
68
78
|
export interface ClineOAuthProviderOptions {
|
|
69
79
|
apiBaseUrl: string;
|
|
70
80
|
headers?: HeaderInput;
|
|
71
|
-
callbackPath?: string;
|
|
72
|
-
callbackPorts?: number[];
|
|
73
81
|
requestTimeoutMs?: number;
|
|
74
82
|
telemetry?: ITelemetryService;
|
|
83
|
+
/**
|
|
84
|
+
* Feature flag for WorkOS Device Authorization.
|
|
85
|
+
* Defaults to false to preserve legacy callback-based OAuth behavior.
|
|
86
|
+
*/
|
|
87
|
+
useWorkOSDeviceAuth?: boolean;
|
|
88
|
+
callbackPath?: string;
|
|
89
|
+
callbackPorts?: number[];
|
|
75
90
|
/**
|
|
76
91
|
* Optional identity provider name for token exchange.
|
|
77
92
|
*/
|
|
@@ -112,18 +127,6 @@ class ClineOAuthTokenError extends Error {
|
|
|
112
127
|
}
|
|
113
128
|
}
|
|
114
129
|
|
|
115
|
-
function createState(): string {
|
|
116
|
-
const cryptoApi = globalThis.crypto;
|
|
117
|
-
if (!cryptoApi) {
|
|
118
|
-
return Math.random().toString(16).slice(2);
|
|
119
|
-
}
|
|
120
|
-
const bytes = new Uint8Array(16);
|
|
121
|
-
cryptoApi.getRandomValues(bytes);
|
|
122
|
-
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join(
|
|
123
|
-
"",
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
130
|
function toEpochMs(isoDateTime: string): number {
|
|
128
131
|
const epoch = Date.parse(isoDateTime);
|
|
129
132
|
if (Number.isNaN(epoch)) {
|
|
@@ -165,22 +168,232 @@ async function resolveHeaders(input?: HeaderInput): Promise<HeaderMap> {
|
|
|
165
168
|
return typeof input === "function" ? await input() : input;
|
|
166
169
|
}
|
|
167
170
|
|
|
168
|
-
|
|
171
|
+
function toSeconds(value: unknown, fallback: number): number {
|
|
172
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
173
|
+
return fallback;
|
|
174
|
+
}
|
|
175
|
+
return Math.floor(value);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function sleep(ms: number): Promise<void> {
|
|
179
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
type WorkOSDeviceAuthorizationResponse = {
|
|
183
|
+
device_code?: string;
|
|
184
|
+
user_code?: string;
|
|
185
|
+
verification_uri?: string;
|
|
186
|
+
verification_uri_complete?: string;
|
|
187
|
+
expires_in?: number;
|
|
188
|
+
interval?: number;
|
|
189
|
+
error?: string;
|
|
190
|
+
error_description?: string;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
type WorkOSTokenResponse = {
|
|
194
|
+
access_token?: string;
|
|
195
|
+
refresh_token?: string;
|
|
196
|
+
token_type?: string;
|
|
197
|
+
expires_in?: number;
|
|
198
|
+
error?: string;
|
|
199
|
+
error_description?: string;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
type WorkOSTokenSuccess = {
|
|
203
|
+
accessToken: string;
|
|
204
|
+
refreshToken: string;
|
|
205
|
+
tokenType: string;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
function requireClineTokenResponse(
|
|
209
|
+
payload: ClineTokenResponse,
|
|
210
|
+
message: string,
|
|
211
|
+
): ClineAuthResponseData {
|
|
212
|
+
if (!payload.success || !payload.data?.accessToken) {
|
|
213
|
+
throw new Error(message);
|
|
214
|
+
}
|
|
215
|
+
return payload.data;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function requestWorkOSDeviceAuthorization(
|
|
219
|
+
clientId: string,
|
|
220
|
+
options?: { requestTimeoutMs?: number },
|
|
221
|
+
): Promise<{
|
|
222
|
+
deviceCode: string;
|
|
223
|
+
userCode: string;
|
|
224
|
+
verificationUri: string;
|
|
225
|
+
verificationUriComplete?: string;
|
|
226
|
+
expiresInSeconds: number;
|
|
227
|
+
pollIntervalSeconds: number;
|
|
228
|
+
}> {
|
|
229
|
+
const response = await fetch(
|
|
230
|
+
resolveUrl(
|
|
231
|
+
DEFAULT_WORKOS_API_BASE_URL,
|
|
232
|
+
DEFAULT_WORKOS_ENDPOINTS.deviceAuthorization,
|
|
233
|
+
),
|
|
234
|
+
{
|
|
235
|
+
method: "POST",
|
|
236
|
+
headers: {
|
|
237
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
238
|
+
},
|
|
239
|
+
body: new URLSearchParams({ client_id: clientId }),
|
|
240
|
+
signal: AbortSignal.timeout(
|
|
241
|
+
options?.requestTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS,
|
|
242
|
+
),
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const json = (await response
|
|
247
|
+
.json()
|
|
248
|
+
.catch(() => ({}))) as WorkOSDeviceAuthorizationResponse;
|
|
249
|
+
if (!response.ok) {
|
|
250
|
+
throw new ClineOAuthTokenError(
|
|
251
|
+
`Device authorization failed: ${response.status}${json.error_description ? ` - ${json.error_description}` : ""}`,
|
|
252
|
+
{ status: response.status, errorCode: json.error },
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
if (!json.device_code || !json.user_code || !json.verification_uri) {
|
|
256
|
+
throw new Error("Invalid WorkOS device authorization response");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
deviceCode: json.device_code,
|
|
261
|
+
userCode: json.user_code,
|
|
262
|
+
verificationUri: json.verification_uri,
|
|
263
|
+
verificationUriComplete: json.verification_uri_complete,
|
|
264
|
+
expiresInSeconds: toSeconds(
|
|
265
|
+
json.expires_in,
|
|
266
|
+
DEFAULT_DEVICE_AUTH_EXPIRES_IN_SECONDS,
|
|
267
|
+
),
|
|
268
|
+
pollIntervalSeconds: toSeconds(
|
|
269
|
+
json.interval,
|
|
270
|
+
DEFAULT_DEVICE_AUTH_INTERVAL_SECONDS,
|
|
271
|
+
),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function pollWorkOSTokens(options: {
|
|
276
|
+
clientId: string;
|
|
277
|
+
deviceCode: string;
|
|
278
|
+
expiresInSeconds: number;
|
|
279
|
+
initialPollIntervalSeconds: number;
|
|
280
|
+
requestTimeoutMs: number;
|
|
281
|
+
workosApiBaseUrl: string;
|
|
282
|
+
onProgress?: OAuthLoginCallbacks["onProgress"];
|
|
283
|
+
}): Promise<WorkOSTokenSuccess> {
|
|
284
|
+
const deadline = Date.now() + options.expiresInSeconds * 1000;
|
|
285
|
+
let intervalSeconds = Math.max(1, options.initialPollIntervalSeconds);
|
|
286
|
+
|
|
287
|
+
while (Date.now() <= deadline) {
|
|
288
|
+
const response = await fetch(
|
|
289
|
+
resolveUrl(
|
|
290
|
+
options.workosApiBaseUrl,
|
|
291
|
+
DEFAULT_WORKOS_ENDPOINTS.authenticate,
|
|
292
|
+
),
|
|
293
|
+
{
|
|
294
|
+
method: "POST",
|
|
295
|
+
headers: {
|
|
296
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
297
|
+
},
|
|
298
|
+
body: new URLSearchParams({
|
|
299
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
300
|
+
device_code: options.deviceCode,
|
|
301
|
+
client_id: options.clientId,
|
|
302
|
+
}),
|
|
303
|
+
signal: AbortSignal.timeout(options.requestTimeoutMs),
|
|
304
|
+
},
|
|
305
|
+
);
|
|
306
|
+
const payload = (await response
|
|
307
|
+
.json()
|
|
308
|
+
.catch(() => ({}))) as WorkOSTokenResponse;
|
|
309
|
+
if (response.ok) {
|
|
310
|
+
if (!payload.access_token || !payload.refresh_token) {
|
|
311
|
+
throw new Error("Invalid WorkOS token response");
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
accessToken: payload.access_token,
|
|
315
|
+
refreshToken: payload.refresh_token,
|
|
316
|
+
tokenType: payload.token_type ?? "Bearer",
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
switch (payload.error) {
|
|
321
|
+
case "authorization_pending": {
|
|
322
|
+
await sleep(intervalSeconds * 1000);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
case "slow_down": {
|
|
326
|
+
intervalSeconds += 1;
|
|
327
|
+
await sleep(intervalSeconds * 1000);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
case "access_denied":
|
|
331
|
+
case "expired_token":
|
|
332
|
+
case "invalid_grant": {
|
|
333
|
+
throw new ClineOAuthTokenError(
|
|
334
|
+
payload.error_description || "WorkOS authorization failed",
|
|
335
|
+
{
|
|
336
|
+
status: response.status,
|
|
337
|
+
errorCode: payload.error,
|
|
338
|
+
},
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
default: {
|
|
342
|
+
throw new ClineOAuthTokenError(
|
|
343
|
+
`WorkOS token polling failed: ${response.status}${payload.error_description ? ` - ${payload.error_description}` : ""}`,
|
|
344
|
+
{
|
|
345
|
+
status: response.status,
|
|
346
|
+
errorCode: payload.error,
|
|
347
|
+
},
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
options.onProgress?.("Waiting for browser authentication confirmation...");
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
throw new Error("WorkOS device authorization timed out");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async function registerWorkOSTokens(
|
|
359
|
+
workosTokens: WorkOSTokenSuccess,
|
|
169
360
|
options: ClineOAuthProviderOptions,
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
361
|
+
provider?: string,
|
|
362
|
+
): Promise<ClineOAuthCredentials> {
|
|
363
|
+
const body = {
|
|
364
|
+
accessToken: workosTokens.accessToken,
|
|
365
|
+
refreshToken: workosTokens.refreshToken,
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const response = await fetch(
|
|
369
|
+
resolveUrl(options.apiBaseUrl, DEFAULT_AUTH_ENDPOINTS.register),
|
|
370
|
+
{
|
|
371
|
+
method: "POST",
|
|
372
|
+
headers: {
|
|
373
|
+
"Content-Type": "application/json",
|
|
374
|
+
...(await resolveHeaders(options.headers)),
|
|
375
|
+
},
|
|
376
|
+
body: JSON.stringify(body),
|
|
377
|
+
signal: AbortSignal.timeout(
|
|
378
|
+
options.requestTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS,
|
|
379
|
+
),
|
|
380
|
+
},
|
|
177
381
|
);
|
|
178
|
-
authUrl.searchParams.set("client_type", "extension");
|
|
179
|
-
authUrl.searchParams.set("callback_url", params.callbackUrl);
|
|
180
|
-
authUrl.searchParams.set("redirect_uri", params.callbackUrl);
|
|
181
|
-
authUrl.searchParams.set("state", params.state);
|
|
182
382
|
|
|
183
|
-
|
|
383
|
+
if (!response.ok) {
|
|
384
|
+
const text = await response.text().catch(() => "");
|
|
385
|
+
const details = parseOAuthError(text);
|
|
386
|
+
throw new ClineOAuthTokenError(
|
|
387
|
+
`Token registration failed: ${response.status}${details.message ? ` - ${details.message}` : ""}`,
|
|
388
|
+
{ status: response.status, errorCode: details.code },
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const json = (await response.json()) as ClineTokenResponse;
|
|
393
|
+
return toClineCredentials(
|
|
394
|
+
requireClineTokenResponse(json, "Invalid token exchange response"),
|
|
395
|
+
provider ?? options.provider,
|
|
396
|
+
);
|
|
184
397
|
}
|
|
185
398
|
|
|
186
399
|
async function exchangeAuthorizationCode(
|
|
@@ -222,11 +435,10 @@ async function exchangeAuthorizationCode(
|
|
|
222
435
|
}
|
|
223
436
|
|
|
224
437
|
const json = (await response.json()) as ClineTokenResponse;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return toClineCredentials(json.data, provider ?? options.provider);
|
|
438
|
+
return toClineCredentials(
|
|
439
|
+
requireClineTokenResponse(json, "Invalid token exchange response"),
|
|
440
|
+
provider ?? options.provider,
|
|
441
|
+
);
|
|
230
442
|
}
|
|
231
443
|
|
|
232
444
|
export async function loginClineOAuth(
|
|
@@ -235,76 +447,102 @@ export async function loginClineOAuth(
|
|
|
235
447
|
},
|
|
236
448
|
): Promise<ClineOAuthCredentials> {
|
|
237
449
|
captureAuthStarted(options.telemetry, options.provider ?? "cline");
|
|
450
|
+
const useWorkOSDeviceAuth = options.useWorkOSDeviceAuth;
|
|
238
451
|
const callbackPorts = options.callbackPorts?.length
|
|
239
452
|
? options.callbackPorts
|
|
240
453
|
: DEFAULT_CALLBACK_PORTS;
|
|
241
454
|
const callbackPath = options.callbackPath ?? DEFAULT_CALLBACK_PATH;
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
455
|
+
const localServer = useWorkOSDeviceAuth
|
|
456
|
+
? null
|
|
457
|
+
: await startLocalOAuthServer({
|
|
458
|
+
ports: callbackPorts,
|
|
459
|
+
callbackPath,
|
|
460
|
+
});
|
|
249
461
|
const callbackUrl =
|
|
250
|
-
localServer
|
|
462
|
+
localServer?.callbackUrl ||
|
|
251
463
|
`http://127.0.0.1:${callbackPorts[0] ?? DEFAULT_CALLBACK_PORTS[0]}${callbackPath}`;
|
|
252
464
|
|
|
253
|
-
const authUrl = await requestAuthorizationUrl(options, {
|
|
254
|
-
callbackUrl,
|
|
255
|
-
state,
|
|
256
|
-
});
|
|
257
|
-
options.callbacks.onAuth({
|
|
258
|
-
url: authUrl,
|
|
259
|
-
instructions: "Continue the authentication process in your browser.",
|
|
260
|
-
});
|
|
261
|
-
|
|
262
465
|
try {
|
|
263
|
-
let
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
throw new Error("State mismatch");
|
|
277
|
-
}
|
|
278
|
-
code = authResult.code;
|
|
279
|
-
provider = authResult.provider ?? provider;
|
|
466
|
+
let credentials: ClineOAuthCredentials;
|
|
467
|
+
if (useWorkOSDeviceAuth) {
|
|
468
|
+
const clientId = DEFAULT_WORKOS_CLIENT_ID;
|
|
469
|
+
const deviceAuthorization = await requestWorkOSDeviceAuthorization(
|
|
470
|
+
clientId,
|
|
471
|
+
options,
|
|
472
|
+
);
|
|
473
|
+
options.callbacks.onAuth({
|
|
474
|
+
url:
|
|
475
|
+
deviceAuthorization.verificationUriComplete ??
|
|
476
|
+
deviceAuthorization.verificationUri,
|
|
477
|
+
instructions: `Enter this code in your browser: ${deviceAuthorization.userCode}`,
|
|
478
|
+
});
|
|
280
479
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
480
|
+
const workosTokens = await pollWorkOSTokens({
|
|
481
|
+
clientId,
|
|
482
|
+
deviceCode: deviceAuthorization.deviceCode,
|
|
483
|
+
expiresInSeconds: deviceAuthorization.expiresInSeconds,
|
|
484
|
+
initialPollIntervalSeconds: deviceAuthorization.pollIntervalSeconds,
|
|
485
|
+
requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS,
|
|
486
|
+
workosApiBaseUrl: DEFAULT_WORKOS_API_BASE_URL,
|
|
487
|
+
onProgress: options.callbacks.onProgress,
|
|
284
488
|
});
|
|
285
|
-
const parsed = parseAuthorizationInput(input, { includeProvider: true });
|
|
286
|
-
if (parsed.state && parsed.state !== state) {
|
|
287
|
-
throw new Error("State mismatch");
|
|
288
|
-
}
|
|
289
|
-
code = parsed.code;
|
|
290
|
-
provider = parsed.provider ?? provider;
|
|
291
|
-
}
|
|
292
489
|
|
|
293
|
-
|
|
294
|
-
|
|
490
|
+
credentials = await registerWorkOSTokens(
|
|
491
|
+
workosTokens,
|
|
492
|
+
options,
|
|
493
|
+
options.provider,
|
|
494
|
+
);
|
|
495
|
+
} else {
|
|
496
|
+
const authUrl = new URL(
|
|
497
|
+
resolveUrl(options.apiBaseUrl, DEFAULT_AUTH_ENDPOINTS.authorize),
|
|
498
|
+
);
|
|
499
|
+
authUrl.searchParams.set("client_type", "extension");
|
|
500
|
+
authUrl.searchParams.set("callback_url", callbackUrl);
|
|
501
|
+
authUrl.searchParams.set("redirect_uri", callbackUrl);
|
|
502
|
+
options.callbacks.onAuth({
|
|
503
|
+
url: authUrl.toString(),
|
|
504
|
+
instructions: "Continue the authentication process in your browser.",
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
let code: string | undefined;
|
|
508
|
+
let provider = options.provider;
|
|
509
|
+
const authResult = await resolveAuthorizationCodeInput({
|
|
510
|
+
waitForCallback: localServer?.waitForCallback ?? (async () => null),
|
|
511
|
+
cancelWait: localServer?.cancelWait ?? (() => {}),
|
|
512
|
+
onManualCodeInput: options.callbacks.onManualCodeInput,
|
|
513
|
+
parseOptions: { includeProvider: true },
|
|
514
|
+
});
|
|
515
|
+
if (authResult.error) {
|
|
516
|
+
throw new Error(`OAuth error: ${authResult.error}`);
|
|
517
|
+
}
|
|
518
|
+
code = authResult.code;
|
|
519
|
+
provider = authResult.provider ?? provider;
|
|
520
|
+
if (!code) {
|
|
521
|
+
const input = await options.callbacks.onPrompt({
|
|
522
|
+
message: "Paste the authorization code (or full redirect URL):",
|
|
523
|
+
});
|
|
524
|
+
const parsed = parseAuthorizationInput(input, {
|
|
525
|
+
includeProvider: true,
|
|
526
|
+
});
|
|
527
|
+
code = parsed.code;
|
|
528
|
+
provider = parsed.provider ?? provider;
|
|
529
|
+
}
|
|
530
|
+
if (!code) {
|
|
531
|
+
throw new Error("Missing authorization code");
|
|
532
|
+
}
|
|
533
|
+
credentials = await exchangeAuthorizationCode(
|
|
534
|
+
code,
|
|
535
|
+
callbackUrl,
|
|
536
|
+
options,
|
|
537
|
+
provider,
|
|
538
|
+
);
|
|
295
539
|
}
|
|
296
540
|
|
|
297
|
-
|
|
298
|
-
code,
|
|
299
|
-
callbackUrl,
|
|
300
|
-
options,
|
|
301
|
-
provider,
|
|
302
|
-
);
|
|
303
|
-
captureAuthSucceeded(options.telemetry, provider ?? "cline");
|
|
541
|
+
captureAuthSucceeded(options.telemetry, options.provider ?? "cline");
|
|
304
542
|
identifyAccount(options.telemetry, {
|
|
305
543
|
id: credentials.accountId,
|
|
306
544
|
email: credentials.email,
|
|
307
|
-
provider: provider ?? "cline",
|
|
545
|
+
provider: options.provider ?? "cline",
|
|
308
546
|
});
|
|
309
547
|
return credentials;
|
|
310
548
|
} catch (error) {
|
|
@@ -315,7 +553,71 @@ export async function loginClineOAuth(
|
|
|
315
553
|
);
|
|
316
554
|
throw error;
|
|
317
555
|
} finally {
|
|
318
|
-
localServer
|
|
556
|
+
localServer?.close();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
export async function startClineDeviceAuth(options?: {
|
|
561
|
+
requestTimeoutMs?: number;
|
|
562
|
+
}): Promise<{
|
|
563
|
+
deviceCode: string;
|
|
564
|
+
userCode: string;
|
|
565
|
+
verificationUri: string;
|
|
566
|
+
verificationUriComplete?: string;
|
|
567
|
+
expiresInSeconds: number;
|
|
568
|
+
pollIntervalSeconds: number;
|
|
569
|
+
}> {
|
|
570
|
+
return await requestWorkOSDeviceAuthorization(
|
|
571
|
+
DEFAULT_WORKOS_CLIENT_ID,
|
|
572
|
+
options,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
export async function completeClineDeviceAuth(options: {
|
|
577
|
+
deviceCode: string;
|
|
578
|
+
expiresInSeconds: number;
|
|
579
|
+
pollIntervalSeconds: number;
|
|
580
|
+
apiBaseUrl: string;
|
|
581
|
+
provider?: string;
|
|
582
|
+
headers?: HeaderInput;
|
|
583
|
+
requestTimeoutMs?: number;
|
|
584
|
+
telemetry?: ITelemetryService;
|
|
585
|
+
}): Promise<ClineOAuthCredentials> {
|
|
586
|
+
const providerName = options.provider ?? "cline";
|
|
587
|
+
captureAuthStarted(options.telemetry, providerName);
|
|
588
|
+
try {
|
|
589
|
+
const workosTokens = await pollWorkOSTokens({
|
|
590
|
+
clientId: DEFAULT_WORKOS_CLIENT_ID,
|
|
591
|
+
deviceCode: options.deviceCode,
|
|
592
|
+
expiresInSeconds: options.expiresInSeconds,
|
|
593
|
+
initialPollIntervalSeconds: options.pollIntervalSeconds,
|
|
594
|
+
requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_HTTP_TIMEOUT_MS,
|
|
595
|
+
workosApiBaseUrl: DEFAULT_WORKOS_API_BASE_URL,
|
|
596
|
+
});
|
|
597
|
+
const credentials = await registerWorkOSTokens(
|
|
598
|
+
workosTokens,
|
|
599
|
+
{
|
|
600
|
+
apiBaseUrl: options.apiBaseUrl,
|
|
601
|
+
headers: options.headers,
|
|
602
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
603
|
+
provider: options.provider,
|
|
604
|
+
},
|
|
605
|
+
options.provider,
|
|
606
|
+
);
|
|
607
|
+
captureAuthSucceeded(options.telemetry, providerName);
|
|
608
|
+
identifyAccount(options.telemetry, {
|
|
609
|
+
id: credentials.accountId,
|
|
610
|
+
email: credentials.email,
|
|
611
|
+
provider: providerName,
|
|
612
|
+
});
|
|
613
|
+
return credentials;
|
|
614
|
+
} catch (error) {
|
|
615
|
+
captureAuthFailed(
|
|
616
|
+
options.telemetry,
|
|
617
|
+
providerName,
|
|
618
|
+
error instanceof Error ? error.message : String(error),
|
|
619
|
+
);
|
|
620
|
+
throw error;
|
|
319
621
|
}
|
|
320
622
|
}
|
|
321
623
|
|
|
@@ -351,13 +653,13 @@ export async function refreshClineToken(
|
|
|
351
653
|
}
|
|
352
654
|
|
|
353
655
|
const json = (await response.json()) as ClineTokenResponse;
|
|
354
|
-
if (!json.success || !json.data?.accessToken) {
|
|
355
|
-
throw new Error("Invalid token refresh response");
|
|
356
|
-
}
|
|
357
|
-
|
|
358
656
|
const provider =
|
|
359
657
|
(current.metadata?.provider as string | undefined) ?? options.provider;
|
|
360
|
-
return toClineCredentials(
|
|
658
|
+
return toClineCredentials(
|
|
659
|
+
requireClineTokenResponse(json, "Invalid token refresh response"),
|
|
660
|
+
provider,
|
|
661
|
+
current,
|
|
662
|
+
);
|
|
361
663
|
}
|
|
362
664
|
|
|
363
665
|
export async function getValidClineCredentials(
|
|
@@ -406,7 +708,7 @@ export function createClineOAuthProvider(
|
|
|
406
708
|
return {
|
|
407
709
|
id: "cline",
|
|
408
710
|
name: "Cline Account",
|
|
409
|
-
usesCallbackServer:
|
|
711
|
+
usesCallbackServer: !options.useWorkOSDeviceAuth,
|
|
410
712
|
async login(callbacks) {
|
|
411
713
|
return loginClineOAuth({ ...options, callbacks });
|
|
412
714
|
},
|
package/src/auth/oca.test.ts
CHANGED
|
@@ -212,4 +212,129 @@ describe("auth/oca getValidOcaCredentials", () => {
|
|
|
212
212
|
expect(result).toBe(current);
|
|
213
213
|
nowSpy.mockRestore();
|
|
214
214
|
});
|
|
215
|
+
|
|
216
|
+
it("re-discovers token endpoint shortly after discovery fallback errors", async () => {
|
|
217
|
+
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(100_000);
|
|
218
|
+
const fetchMock = vi
|
|
219
|
+
.fn()
|
|
220
|
+
// first refresh: discovery fails and fallback endpoint is used
|
|
221
|
+
.mockImplementationOnce(async () => new Response(null, { status: 503 }))
|
|
222
|
+
.mockImplementationOnce(
|
|
223
|
+
async () =>
|
|
224
|
+
new Response(
|
|
225
|
+
JSON.stringify({
|
|
226
|
+
error: "server_error",
|
|
227
|
+
error_description: "temporary issue",
|
|
228
|
+
}),
|
|
229
|
+
{
|
|
230
|
+
status: 500,
|
|
231
|
+
headers: { "Content-Type": "application/json" },
|
|
232
|
+
},
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
// second refresh within fallback TTL: discovery should still be cached
|
|
236
|
+
.mockImplementationOnce(
|
|
237
|
+
async () =>
|
|
238
|
+
new Response(
|
|
239
|
+
JSON.stringify({
|
|
240
|
+
error: "server_error",
|
|
241
|
+
error_description: "temporary issue",
|
|
242
|
+
}),
|
|
243
|
+
{
|
|
244
|
+
status: 500,
|
|
245
|
+
headers: { "Content-Type": "application/json" },
|
|
246
|
+
},
|
|
247
|
+
),
|
|
248
|
+
)
|
|
249
|
+
// third refresh after fallback TTL: discovery should be retried
|
|
250
|
+
.mockImplementationOnce(
|
|
251
|
+
async () =>
|
|
252
|
+
new Response(
|
|
253
|
+
JSON.stringify({
|
|
254
|
+
token_endpoint: "https://idcs.fallback/oauth2/v2/token",
|
|
255
|
+
}),
|
|
256
|
+
{
|
|
257
|
+
status: 200,
|
|
258
|
+
headers: { "Content-Type": "application/json" },
|
|
259
|
+
},
|
|
260
|
+
),
|
|
261
|
+
)
|
|
262
|
+
.mockImplementationOnce(
|
|
263
|
+
async () =>
|
|
264
|
+
new Response(
|
|
265
|
+
JSON.stringify({
|
|
266
|
+
error: "server_error",
|
|
267
|
+
error_description: "temporary issue",
|
|
268
|
+
}),
|
|
269
|
+
{
|
|
270
|
+
status: 500,
|
|
271
|
+
headers: { "Content-Type": "application/json" },
|
|
272
|
+
},
|
|
273
|
+
),
|
|
274
|
+
);
|
|
275
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
276
|
+
|
|
277
|
+
const current = createCredentials({ expires: 10_000_000 });
|
|
278
|
+
const providerOptions = {
|
|
279
|
+
config: {
|
|
280
|
+
internal: {
|
|
281
|
+
clientId: "client-fallback-ttl",
|
|
282
|
+
idcsUrl: "https://idcs.fallback",
|
|
283
|
+
scopes: "openid offline_access",
|
|
284
|
+
baseUrl: "https://oca.example.com",
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const first = await getValidOcaCredentials(
|
|
290
|
+
current,
|
|
291
|
+
{ forceRefresh: true },
|
|
292
|
+
providerOptions,
|
|
293
|
+
);
|
|
294
|
+
expect(first).toBe(current);
|
|
295
|
+
|
|
296
|
+
nowSpy.mockReturnValue(200_000);
|
|
297
|
+
const second = await getValidOcaCredentials(
|
|
298
|
+
current,
|
|
299
|
+
{ forceRefresh: true },
|
|
300
|
+
providerOptions,
|
|
301
|
+
);
|
|
302
|
+
expect(second).toBe(current);
|
|
303
|
+
|
|
304
|
+
nowSpy.mockReturnValue(500_001);
|
|
305
|
+
const third = await getValidOcaCredentials(
|
|
306
|
+
current,
|
|
307
|
+
{ forceRefresh: true },
|
|
308
|
+
providerOptions,
|
|
309
|
+
);
|
|
310
|
+
expect(third).toBe(current);
|
|
311
|
+
|
|
312
|
+
expect(fetchMock).toHaveBeenCalledTimes(5);
|
|
313
|
+
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
314
|
+
1,
|
|
315
|
+
"https://idcs.fallback/.well-known/openid-configuration",
|
|
316
|
+
expect.objectContaining({ method: "GET" }),
|
|
317
|
+
);
|
|
318
|
+
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
319
|
+
2,
|
|
320
|
+
"https://idcs.fallback/oauth2/v1/token",
|
|
321
|
+
expect.objectContaining({ method: "POST" }),
|
|
322
|
+
);
|
|
323
|
+
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
324
|
+
3,
|
|
325
|
+
"https://idcs.fallback/oauth2/v1/token",
|
|
326
|
+
expect.objectContaining({ method: "POST" }),
|
|
327
|
+
);
|
|
328
|
+
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
329
|
+
4,
|
|
330
|
+
"https://idcs.fallback/.well-known/openid-configuration",
|
|
331
|
+
expect.objectContaining({ method: "GET" }),
|
|
332
|
+
);
|
|
333
|
+
expect(fetchMock).toHaveBeenNthCalledWith(
|
|
334
|
+
5,
|
|
335
|
+
"https://idcs.fallback/oauth2/v2/token",
|
|
336
|
+
expect.objectContaining({ method: "POST" }),
|
|
337
|
+
);
|
|
338
|
+
nowSpy.mockRestore();
|
|
339
|
+
});
|
|
215
340
|
});
|