byterover-cli 2.1.5 → 2.3.0

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.
Files changed (110) hide show
  1. package/dist/agent/infra/llm/providers/openai.d.ts +12 -0
  2. package/dist/agent/infra/llm/providers/openai.js +52 -1
  3. package/dist/oclif/commands/curate/index.js +2 -2
  4. package/dist/oclif/commands/locations.d.ts +14 -0
  5. package/dist/oclif/commands/locations.js +68 -0
  6. package/dist/oclif/commands/model/switch.js +14 -3
  7. package/dist/oclif/commands/providers/connect.d.ts +9 -0
  8. package/dist/oclif/commands/providers/connect.js +110 -14
  9. package/dist/oclif/commands/providers/list.js +3 -5
  10. package/dist/oclif/commands/query.js +2 -2
  11. package/dist/oclif/commands/status.js +3 -3
  12. package/dist/oclif/lib/daemon-client.d.ts +4 -0
  13. package/dist/oclif/lib/daemon-client.js +13 -3
  14. package/dist/server/core/domain/entities/provider-config.d.ts +6 -0
  15. package/dist/server/core/domain/entities/provider-config.js +4 -3
  16. package/dist/server/core/domain/entities/provider-registry.d.ts +41 -1
  17. package/dist/server/core/domain/entities/provider-registry.js +24 -1
  18. package/dist/server/core/domain/errors/task-error.d.ts +2 -0
  19. package/dist/server/core/domain/errors/task-error.js +6 -1
  20. package/dist/server/core/domain/transport/schemas.d.ts +2 -0
  21. package/dist/server/core/interfaces/i-provider-config-store.d.ts +2 -0
  22. package/dist/server/core/interfaces/i-provider-model-fetcher.d.ts +12 -3
  23. package/dist/server/core/interfaces/i-provider-oauth-token-store.d.ts +24 -0
  24. package/dist/server/core/interfaces/i-provider-oauth-token-store.js +1 -0
  25. package/dist/server/core/interfaces/i-token-refresh-manager.d.ts +7 -0
  26. package/dist/server/core/interfaces/i-token-refresh-manager.js +1 -0
  27. package/dist/server/infra/daemon/agent-process.js +22 -4
  28. package/dist/server/infra/daemon/brv-server.js +15 -2
  29. package/dist/server/infra/http/models-dev-client.d.ts +29 -0
  30. package/dist/server/infra/http/models-dev-client.js +133 -0
  31. package/dist/server/infra/http/provider-model-fetcher-registry.d.ts +2 -1
  32. package/dist/server/infra/http/provider-model-fetcher-registry.js +6 -1
  33. package/dist/server/infra/http/provider-model-fetchers.d.ts +33 -8
  34. package/dist/server/infra/http/provider-model-fetchers.js +88 -10
  35. package/dist/server/infra/process/feature-handlers.d.ts +6 -1
  36. package/dist/server/infra/process/feature-handlers.js +11 -2
  37. package/dist/server/infra/provider/provider-config-resolver.d.ts +4 -2
  38. package/dist/server/infra/provider/provider-config-resolver.js +59 -4
  39. package/dist/server/infra/provider-oauth/callback-server.d.ts +24 -0
  40. package/dist/server/infra/provider-oauth/callback-server.js +203 -0
  41. package/dist/server/infra/provider-oauth/errors.d.ts +39 -0
  42. package/dist/server/infra/provider-oauth/errors.js +76 -0
  43. package/dist/server/infra/provider-oauth/index.d.ts +9 -0
  44. package/dist/server/infra/provider-oauth/index.js +9 -0
  45. package/dist/server/infra/provider-oauth/jwt-utils.d.ts +17 -0
  46. package/dist/server/infra/provider-oauth/jwt-utils.js +51 -0
  47. package/dist/server/infra/provider-oauth/pkce-service.d.ts +22 -0
  48. package/dist/server/infra/provider-oauth/pkce-service.js +33 -0
  49. package/dist/server/infra/provider-oauth/provider-oauth-token-store.d.ts +48 -0
  50. package/dist/server/infra/provider-oauth/provider-oauth-token-store.js +155 -0
  51. package/dist/server/infra/provider-oauth/refresh-token-exchange.d.ts +8 -0
  52. package/dist/server/infra/provider-oauth/refresh-token-exchange.js +39 -0
  53. package/dist/server/infra/provider-oauth/token-exchange.d.ts +8 -0
  54. package/dist/server/infra/provider-oauth/token-exchange.js +44 -0
  55. package/dist/server/infra/provider-oauth/token-refresh-manager.d.ts +32 -0
  56. package/dist/server/infra/provider-oauth/token-refresh-manager.js +96 -0
  57. package/dist/server/infra/provider-oauth/types.d.ts +55 -0
  58. package/dist/server/infra/provider-oauth/types.js +22 -0
  59. package/dist/server/infra/storage/file-provider-config-store.d.ts +2 -0
  60. package/dist/server/infra/storage/file-provider-config-store.js +1 -3
  61. package/dist/server/infra/transport/handlers/index.d.ts +2 -0
  62. package/dist/server/infra/transport/handlers/index.js +1 -0
  63. package/dist/server/infra/transport/handlers/locations-handler.d.ts +25 -0
  64. package/dist/server/infra/transport/handlers/locations-handler.js +64 -0
  65. package/dist/server/infra/transport/handlers/model-handler.d.ts +3 -0
  66. package/dist/server/infra/transport/handlers/model-handler.js +53 -11
  67. package/dist/server/infra/transport/handlers/provider-handler.d.ts +26 -0
  68. package/dist/server/infra/transport/handlers/provider-handler.js +215 -13
  69. package/dist/server/templates/skill/SKILL.md +19 -1
  70. package/dist/shared/constants/oauth.d.ts +14 -0
  71. package/dist/shared/constants/oauth.js +14 -0
  72. package/dist/shared/transport/events/index.d.ts +8 -0
  73. package/dist/shared/transport/events/index.js +3 -0
  74. package/dist/shared/transport/events/locations-events.d.ts +7 -0
  75. package/dist/shared/transport/events/locations-events.js +3 -0
  76. package/dist/shared/transport/events/model-events.d.ts +2 -0
  77. package/dist/shared/transport/events/provider-events.d.ts +36 -0
  78. package/dist/shared/transport/events/provider-events.js +5 -0
  79. package/dist/shared/transport/types/dto.d.ts +15 -0
  80. package/dist/tui/features/commands/definitions/index.js +2 -0
  81. package/dist/tui/features/commands/definitions/locations.d.ts +2 -0
  82. package/dist/tui/features/commands/definitions/locations.js +11 -0
  83. package/dist/tui/features/locations/api/get-locations.d.ts +16 -0
  84. package/dist/tui/features/locations/api/get-locations.js +17 -0
  85. package/dist/tui/features/locations/components/locations-view.d.ts +3 -0
  86. package/dist/tui/features/locations/components/locations-view.js +25 -0
  87. package/dist/tui/features/locations/utils/format-locations.d.ts +2 -0
  88. package/dist/tui/features/locations/utils/format-locations.js +26 -0
  89. package/dist/tui/features/model/api/set-active-model.d.ts +1 -1
  90. package/dist/tui/features/model/api/set-active-model.js +12 -4
  91. package/dist/tui/features/provider/api/await-oauth-callback.d.ts +11 -0
  92. package/dist/tui/features/provider/api/await-oauth-callback.js +25 -0
  93. package/dist/tui/features/provider/api/cancel-oauth.d.ts +5 -0
  94. package/dist/tui/features/provider/api/cancel-oauth.js +10 -0
  95. package/dist/tui/features/provider/api/start-oauth.d.ts +11 -0
  96. package/dist/tui/features/provider/api/start-oauth.js +15 -0
  97. package/dist/tui/features/provider/components/auth-method-dialog.d.ts +9 -0
  98. package/dist/tui/features/provider/components/auth-method-dialog.js +20 -0
  99. package/dist/tui/features/provider/components/oauth-dialog.d.ts +9 -0
  100. package/dist/tui/features/provider/components/oauth-dialog.js +96 -0
  101. package/dist/tui/features/provider/components/provider-dialog.js +1 -1
  102. package/dist/tui/features/provider/components/provider-flow.js +54 -4
  103. package/dist/tui/features/provider/components/provider-subscription-initializer.d.ts +1 -0
  104. package/dist/tui/features/provider/components/provider-subscription-initializer.js +5 -0
  105. package/dist/tui/features/provider/hooks/use-provider-subscriptions.d.ts +6 -0
  106. package/dist/tui/features/provider/hooks/use-provider-subscriptions.js +24 -0
  107. package/dist/tui/providers/app-providers.js +2 -1
  108. package/dist/tui/utils/error-messages.js +6 -1
  109. package/oclif.manifest.json +56 -1
  110. package/package.json +1 -1
@@ -0,0 +1,55 @@
1
+ import { z } from 'zod';
2
+ export type ProviderCallbackResult = {
3
+ code: string;
4
+ state: string;
5
+ };
6
+ export type PkceParameters = {
7
+ codeChallenge: string;
8
+ codeVerifier: string;
9
+ state: string;
10
+ };
11
+ export type TokenRequestContentType = 'application/json' | 'application/x-www-form-urlencoded';
12
+ /**
13
+ * Raw token response from an OAuth provider.
14
+ * Fields are snake_case per OAuth 2.0 spec (RFC 6749).
15
+ */
16
+ export declare const ProviderTokenResponseSchema: z.ZodObject<{
17
+ access_token: z.ZodString;
18
+ expires_in: z.ZodOptional<z.ZodNumber>;
19
+ id_token: z.ZodOptional<z.ZodString>;
20
+ refresh_token: z.ZodOptional<z.ZodString>;
21
+ scope: z.ZodOptional<z.ZodString>;
22
+ token_type: z.ZodOptional<z.ZodString>;
23
+ }, "strip", z.ZodTypeAny, {
24
+ access_token: string;
25
+ scope?: string | undefined;
26
+ refresh_token?: string | undefined;
27
+ expires_in?: number | undefined;
28
+ id_token?: string | undefined;
29
+ token_type?: string | undefined;
30
+ }, {
31
+ access_token: string;
32
+ scope?: string | undefined;
33
+ refresh_token?: string | undefined;
34
+ expires_in?: number | undefined;
35
+ id_token?: string | undefined;
36
+ token_type?: string | undefined;
37
+ }>;
38
+ export type ProviderTokenResponse = z.infer<typeof ProviderTokenResponseSchema>;
39
+ export type RefreshTokenExchangeParams = {
40
+ clientId: string;
41
+ contentType: TokenRequestContentType;
42
+ refreshToken: string;
43
+ tokenUrl: string;
44
+ };
45
+ export type TokenExchangeParams = {
46
+ clientId: string;
47
+ clientSecret?: string;
48
+ code: string;
49
+ codeVerifier: string;
50
+ contentType: TokenRequestContentType;
51
+ redirectUri: string;
52
+ tokenUrl: string;
53
+ };
54
+ /** Compute an ISO 8601 expiry timestamp from an OAuth expires_in value (seconds). */
55
+ export declare function computeExpiresAt(expiresInSeconds: number): string;
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Raw token response from an OAuth provider.
4
+ * Fields are snake_case per OAuth 2.0 spec (RFC 6749).
5
+ */
6
+ export const ProviderTokenResponseSchema = z.object({
7
+ // eslint-disable-next-line camelcase
8
+ access_token: z.string().min(1),
9
+ // eslint-disable-next-line camelcase
10
+ expires_in: z.number().optional(),
11
+ // eslint-disable-next-line camelcase
12
+ id_token: z.string().optional(),
13
+ // eslint-disable-next-line camelcase
14
+ refresh_token: z.string().optional(),
15
+ scope: z.string().optional(),
16
+ // eslint-disable-next-line camelcase
17
+ token_type: z.string().optional(),
18
+ });
19
+ /** Compute an ISO 8601 expiry timestamp from an OAuth expires_in value (seconds). */
20
+ export function computeExpiresAt(expiresInSeconds) {
21
+ return new Date(Date.now() + expiresInSeconds * 1000).toISOString();
22
+ }
@@ -31,7 +31,9 @@ export declare class FileProviderConfigStore implements IProviderConfigStore {
31
31
  */
32
32
  connectProvider(providerId: string, options?: {
33
33
  activeModel?: string;
34
+ authMethod?: 'api-key' | 'oauth';
34
35
  baseUrl?: string;
36
+ oauthAccountId?: string;
35
37
  }): Promise<void>;
36
38
  /**
37
39
  * Removes a provider connection.
@@ -38,9 +38,7 @@ export class FileProviderConfigStore {
38
38
  */
39
39
  async connectProvider(providerId, options) {
40
40
  const config = await this.read();
41
- const newConfig = config
42
- .withProviderConnected(providerId, options)
43
- .withActiveProvider(providerId);
41
+ const newConfig = config.withProviderConnected(providerId, options).withActiveProvider(providerId);
44
42
  await this.write(newConfig);
45
43
  }
46
44
  /**
@@ -10,6 +10,8 @@ export { HubHandler } from './hub-handler.js';
10
10
  export type { HubHandlerDeps } from './hub-handler.js';
11
11
  export { InitHandler } from './init-handler.js';
12
12
  export type { InitHandlerDeps } from './init-handler.js';
13
+ export { LocationsHandler } from './locations-handler.js';
14
+ export type { LocationsHandlerDeps } from './locations-handler.js';
13
15
  export { ModelHandler } from './model-handler.js';
14
16
  export type { ModelHandlerDeps } from './model-handler.js';
15
17
  export { ProviderHandler } from './provider-handler.js';
@@ -4,6 +4,7 @@ export { ConnectorsHandler } from './connectors-handler.js';
4
4
  export { resolveRequiredProjectPath } from './handler-types.js';
5
5
  export { HubHandler } from './hub-handler.js';
6
6
  export { InitHandler } from './init-handler.js';
7
+ export { LocationsHandler } from './locations-handler.js';
7
8
  export { ModelHandler } from './model-handler.js';
8
9
  export { ProviderHandler } from './provider-handler.js';
9
10
  export { PullHandler } from './pull-handler.js';
@@ -0,0 +1,25 @@
1
+ import type { IContextTreeService } from '../../../core/interfaces/context-tree/i-context-tree-service.js';
2
+ import type { IProjectRegistry } from '../../../core/interfaces/project/i-project-registry.js';
3
+ import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
4
+ import { type ProjectPathResolver } from './handler-types.js';
5
+ export interface LocationsHandlerDeps {
6
+ contextTreeService: IContextTreeService;
7
+ getActiveProjectPaths: () => string[];
8
+ projectRegistry: IProjectRegistry;
9
+ resolveProjectPath: ProjectPathResolver;
10
+ transport: ITransportServer;
11
+ }
12
+ /**
13
+ * Handles locations:get event.
14
+ * Returns all registered project locations with context tree status.
15
+ */
16
+ export declare class LocationsHandler {
17
+ private readonly contextTreeService;
18
+ private readonly getActiveProjectPaths;
19
+ private readonly projectRegistry;
20
+ private readonly resolveProjectPath;
21
+ private readonly transport;
22
+ constructor(deps: LocationsHandlerDeps);
23
+ setup(): void;
24
+ private buildLocations;
25
+ }
@@ -0,0 +1,64 @@
1
+ import { join } from 'node:path';
2
+ import { LocationsEvents } from '../../../../shared/transport/events/locations-events.js';
3
+ import { BRV_DIR, CONTEXT_TREE_DIR } from '../../../constants.js';
4
+ import { resolveRequiredProjectPath } from './handler-types.js';
5
+ /**
6
+ * Handles locations:get event.
7
+ * Returns all registered project locations with context tree status.
8
+ */
9
+ export class LocationsHandler {
10
+ contextTreeService;
11
+ getActiveProjectPaths;
12
+ projectRegistry;
13
+ resolveProjectPath;
14
+ transport;
15
+ constructor(deps) {
16
+ this.contextTreeService = deps.contextTreeService;
17
+ this.getActiveProjectPaths = deps.getActiveProjectPaths;
18
+ this.projectRegistry = deps.projectRegistry;
19
+ this.resolveProjectPath = deps.resolveProjectPath;
20
+ this.transport = deps.transport;
21
+ }
22
+ setup() {
23
+ this.transport.onRequest(LocationsEvents.GET, async (_data, clientId) => {
24
+ const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
25
+ try {
26
+ const locations = await this.buildLocations(projectPath);
27
+ return { locations };
28
+ }
29
+ catch {
30
+ return { locations: [] };
31
+ }
32
+ });
33
+ }
34
+ async buildLocations(currentProjectPath) {
35
+ const all = this.projectRegistry.getAll();
36
+ const activeSet = new Set(this.getActiveProjectPaths());
37
+ const results = await Promise.all([...all.entries()].map(async ([path]) => {
38
+ let isInitialized = false;
39
+ try {
40
+ isInitialized = await this.contextTreeService.exists(path);
41
+ }
42
+ catch {
43
+ // FS error — treat as not initialized
44
+ }
45
+ return {
46
+ contextTreePath: join(path, BRV_DIR, CONTEXT_TREE_DIR),
47
+ isActive: activeSet.has(path) || path === currentProjectPath,
48
+ isCurrent: path === currentProjectPath,
49
+ isInitialized,
50
+ projectPath: path,
51
+ };
52
+ }));
53
+ // Sort: current first → active (has clients) → initialized → rest, all by registeredAt desc
54
+ return results.sort((a, b) => {
55
+ if (a.isCurrent !== b.isCurrent)
56
+ return a.isCurrent ? -1 : 1;
57
+ if (a.isActive !== b.isActive)
58
+ return a.isActive ? -1 : 1;
59
+ if (a.isInitialized !== b.isInitialized)
60
+ return a.isInitialized ? -1 : 1;
61
+ return (all.get(b.projectPath)?.registeredAt ?? 0) - (all.get(a.projectPath)?.registeredAt ?? 0);
62
+ });
63
+ }
64
+ }
@@ -1,7 +1,9 @@
1
1
  import type { IProviderConfigStore } from '../../../core/interfaces/i-provider-config-store.js';
2
2
  import type { IProviderKeychainStore } from '../../../core/interfaces/i-provider-keychain-store.js';
3
+ import type { IProviderModelFetcher } from '../../../core/interfaces/i-provider-model-fetcher.js';
3
4
  import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
4
5
  export interface ModelHandlerDeps {
6
+ getModelFetcher?: (providerId: string) => Promise<IProviderModelFetcher | undefined>;
5
7
  providerConfigStore: IProviderConfigStore;
6
8
  providerKeychainStore: IProviderKeychainStore;
7
9
  transport: ITransportServer;
@@ -11,6 +13,7 @@ export interface ModelHandlerDeps {
11
13
  * Business logic for model listing and selection — no terminal/UI calls.
12
14
  */
13
15
  export declare class ModelHandler {
16
+ private readonly getModelFetcher;
14
17
  private readonly providerConfigStore;
15
18
  private readonly providerKeychainStore;
16
19
  private readonly transport;
@@ -1,15 +1,17 @@
1
1
  import { ModelEvents, } from '../../../../shared/transport/events/model-events.js';
2
2
  import { TransportDaemonEventNames } from '../../../core/domain/transport/schemas.js';
3
- import { getModelFetcher } from '../../http/provider-model-fetcher-registry.js';
3
+ import { getModelFetcher as getModelFetcherDefault } from '../../http/provider-model-fetcher-registry.js';
4
4
  /**
5
5
  * Handles model:* events.
6
6
  * Business logic for model listing and selection — no terminal/UI calls.
7
7
  */
8
8
  export class ModelHandler {
9
+ getModelFetcher;
9
10
  providerConfigStore;
10
11
  providerKeychainStore;
11
12
  transport;
12
13
  constructor(deps) {
14
+ this.getModelFetcher = deps.getModelFetcher ?? getModelFetcherDefault;
13
15
  this.providerConfigStore = deps.providerConfigStore;
14
16
  this.providerKeychainStore = deps.providerKeychainStore;
15
17
  this.transport = deps.transport;
@@ -22,18 +24,21 @@ export class ModelHandler {
22
24
  setupList() {
23
25
  this.transport.onRequest(ModelEvents.LIST, async (data) => {
24
26
  const { providerId } = data;
25
- const fetcher = await getModelFetcher(providerId);
27
+ const fetcher = await this.getModelFetcher(providerId);
26
28
  if (!fetcher) {
27
29
  return { favorites: [], models: [], recent: [] };
28
30
  }
29
31
  // Fetch models from provider API using the correct per-provider fetcher
30
32
  let fetchedModels;
31
33
  try {
34
+ const config = await this.providerConfigStore.read();
35
+ const authMethod = config.providers[providerId]?.authMethod;
32
36
  const apiKey = await this.providerKeychainStore.getApiKey(providerId);
33
- fetchedModels = await fetcher.fetchModels(apiKey ?? '');
37
+ fetchedModels = await fetcher.fetchModels(apiKey ?? '', { authMethod });
34
38
  }
35
- catch {
36
- return { favorites: [], models: [], recent: [] };
39
+ catch (error) {
40
+ const message = error instanceof Error ? error.message : 'Failed to load models';
41
+ return { error: message, favorites: [], models: [], recent: [] };
37
42
  }
38
43
  const models = fetchedModels.map((m) => ({
39
44
  contextLength: m.contextLength,
@@ -61,12 +66,14 @@ export class ModelHandler {
61
66
  setupListByProviders() {
62
67
  this.transport.onRequest(ModelEvents.LIST_BY_PROVIDERS, async (data) => {
63
68
  const { providerIds } = data;
69
+ const config = await this.providerConfigStore.read();
64
70
  const results = await Promise.allSettled(providerIds.map(async (providerId) => {
65
- const fetcher = await getModelFetcher(providerId);
71
+ const fetcher = await this.getModelFetcher(providerId);
66
72
  if (!fetcher)
67
73
  return [];
74
+ const authMethod = config.providers[providerId]?.authMethod;
68
75
  const apiKey = await this.providerKeychainStore.getApiKey(providerId);
69
- const fetchedModels = await fetcher.fetchModels(apiKey ?? '');
76
+ const fetchedModels = await fetcher.fetchModels(apiKey ?? '', { authMethod });
70
77
  return fetchedModels.map((model) => ({
71
78
  contextLength: model.contextLength,
72
79
  description: model.description,
@@ -98,10 +105,45 @@ export class ModelHandler {
98
105
  }
99
106
  setupSetActive() {
100
107
  this.transport.onRequest(ModelEvents.SET_ACTIVE, async (data) => {
101
- await this.providerConfigStore.setActiveProvider(data.providerId);
102
- await this.providerConfigStore.setActiveModel(data.providerId, data.modelId, data.contextLength);
103
- this.transport.broadcast(TransportDaemonEventNames.PROVIDER_UPDATED, {});
104
- return { success: true };
108
+ try {
109
+ const config = await this.providerConfigStore.read();
110
+ const providerConfig = config.providers[data.providerId];
111
+ if (!providerConfig) {
112
+ return {
113
+ error: `Provider "${data.providerId}" is not connected`,
114
+ success: false,
115
+ };
116
+ }
117
+ let matchedModel;
118
+ // Validate model against allowed list for OAuth providers
119
+ if (providerConfig.authMethod === 'oauth') {
120
+ const fetcher = await this.getModelFetcher(data.providerId);
121
+ if (!fetcher) {
122
+ return {
123
+ error: `Cannot validate model for OAuth-connected ${data.providerId}: model fetcher unavailable`,
124
+ success: false,
125
+ };
126
+ }
127
+ const allowedModels = await fetcher.fetchModels('', { authMethod: 'oauth' });
128
+ matchedModel = allowedModels.find((m) => m.id === data.modelId);
129
+ if (!matchedModel) {
130
+ const allowedIds = allowedModels.map((m) => m.id).join(', ');
131
+ return {
132
+ error: `Model "${data.modelId}" is not available for OAuth-connected ${data.providerId}. Allowed models: ${allowedIds}`,
133
+ success: false,
134
+ };
135
+ }
136
+ }
137
+ const contextLength = data.contextLength ?? matchedModel?.contextLength;
138
+ await this.providerConfigStore.setActiveProvider(data.providerId);
139
+ await this.providerConfigStore.setActiveModel(data.providerId, data.modelId, contextLength);
140
+ this.transport.broadcast(TransportDaemonEventNames.PROVIDER_UPDATED, {});
141
+ return { success: true };
142
+ }
143
+ catch (error) {
144
+ const message = error instanceof Error ? error.message : 'Failed to set active model';
145
+ return { error: message, success: false };
146
+ }
105
147
  });
106
148
  }
107
149
  }
@@ -1,9 +1,24 @@
1
1
  import type { IProviderConfigStore } from '../../../core/interfaces/i-provider-config-store.js';
2
2
  import type { IProviderKeychainStore } from '../../../core/interfaces/i-provider-keychain-store.js';
3
+ import type { IProviderOAuthTokenStore } from '../../../core/interfaces/i-provider-oauth-token-store.js';
4
+ import type { IBrowserLauncher } from '../../../core/interfaces/services/i-browser-launcher.js';
3
5
  import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
6
+ import type { PkceParameters, ProviderTokenResponse, TokenExchangeParams } from '../../provider-oauth/index.js';
7
+ import { ProviderCallbackServer } from '../../provider-oauth/index.js';
4
8
  export interface ProviderHandlerDeps {
9
+ browserLauncher: IBrowserLauncher;
10
+ /** Factory for creating callback servers (injectable for testing) */
11
+ createCallbackServer?: (options: {
12
+ callbackPath?: string;
13
+ port: number;
14
+ }) => ProviderCallbackServer;
15
+ /** Token exchange function (injectable for testing) */
16
+ exchangeCodeForTokens?: (params: TokenExchangeParams) => Promise<ProviderTokenResponse>;
17
+ /** PKCE generator function (injectable for testing) */
18
+ generatePkce?: () => PkceParameters;
5
19
  providerConfigStore: IProviderConfigStore;
6
20
  providerKeychainStore: IProviderKeychainStore;
21
+ providerOAuthTokenStore: IProviderOAuthTokenStore;
7
22
  transport: ITransportServer;
8
23
  }
9
24
  /**
@@ -11,15 +26,26 @@ export interface ProviderHandlerDeps {
11
26
  * Business logic for provider management — no terminal/UI calls.
12
27
  */
13
28
  export declare class ProviderHandler {
29
+ private readonly browserLauncher;
30
+ private readonly createCallbackServer;
31
+ private readonly exchangeCodeForTokens;
32
+ private readonly generatePkce;
33
+ private readonly oauthFlows;
14
34
  private readonly providerConfigStore;
15
35
  private readonly providerKeychainStore;
36
+ private readonly providerOAuthTokenStore;
16
37
  private readonly transport;
17
38
  constructor(deps: ProviderHandlerDeps);
18
39
  setup(): void;
40
+ private cleanupFlowsForClient;
41
+ private setupAwaitOAuthCallback;
42
+ private setupCancelOAuth;
19
43
  private setupConnect;
20
44
  private setupDisconnect;
21
45
  private setupGetActive;
22
46
  private setupList;
23
47
  private setupSetActive;
48
+ private setupStartOAuth;
49
+ private setupSubmitOAuthCode;
24
50
  private setupValidateApiKey;
25
51
  }