byterover-cli 3.11.0 → 3.13.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 (102) hide show
  1. package/.env.production +2 -1
  2. package/dist/agent/infra/tools/implementations/curate-tool.js +18 -8
  3. package/dist/oclif/commands/curate/index.js +6 -0
  4. package/dist/oclif/commands/providers/connect.d.ts +26 -1
  5. package/dist/oclif/commands/providers/connect.js +95 -17
  6. package/dist/oclif/commands/providers/list.d.ts +10 -1
  7. package/dist/oclif/commands/providers/list.js +35 -3
  8. package/dist/oclif/commands/query.js +6 -0
  9. package/dist/oclif/commands/status.js +4 -0
  10. package/dist/oclif/lib/billing-line.d.ts +8 -0
  11. package/dist/oclif/lib/billing-line.js +45 -0
  12. package/dist/oclif/lib/format-billing-line.d.ts +2 -0
  13. package/dist/oclif/lib/format-billing-line.js +19 -0
  14. package/dist/oclif/lib/insufficient-credits.d.ts +11 -0
  15. package/dist/oclif/lib/insufficient-credits.js +36 -0
  16. package/dist/server/config/environment.d.ts +1 -0
  17. package/dist/server/config/environment.js +3 -0
  18. package/dist/server/constants.d.ts +6 -0
  19. package/dist/server/constants.js +11 -0
  20. package/dist/server/core/domain/entities/task-history-entry.d.ts +775 -0
  21. package/dist/server/core/domain/entities/task-history-entry.js +88 -0
  22. package/dist/server/core/domain/transport/schemas.d.ts +1420 -11
  23. package/dist/server/core/domain/transport/schemas.js +160 -6
  24. package/dist/server/core/domain/transport/task-info.d.ts +18 -0
  25. package/dist/server/core/interfaces/process/i-task-lifecycle-hook.d.ts +7 -0
  26. package/dist/server/core/interfaces/services/i-billing-service.d.ts +26 -0
  27. package/dist/server/core/interfaces/services/i-billing-service.js +1 -0
  28. package/dist/server/core/interfaces/storage/i-billing-config-store.d.ts +4 -0
  29. package/dist/server/core/interfaces/storage/i-billing-config-store.js +1 -0
  30. package/dist/server/core/interfaces/storage/i-task-history-store.d.ts +62 -0
  31. package/dist/server/core/interfaces/storage/i-task-history-store.js +1 -0
  32. package/dist/server/infra/billing/billing-state-endpoint.d.ts +4 -0
  33. package/dist/server/infra/billing/billing-state-endpoint.js +7 -0
  34. package/dist/server/infra/billing/build-status-billing.d.ts +9 -0
  35. package/dist/server/infra/billing/build-status-billing.js +36 -0
  36. package/dist/server/infra/billing/http-billing-service.d.ts +19 -0
  37. package/dist/server/infra/billing/http-billing-service.js +57 -0
  38. package/dist/server/infra/billing/paid-organizations-endpoint.d.ts +8 -0
  39. package/dist/server/infra/billing/paid-organizations-endpoint.js +18 -0
  40. package/dist/server/infra/billing/resolve-billing-source.d.ts +13 -0
  41. package/dist/server/infra/billing/resolve-billing-source.js +36 -0
  42. package/dist/server/infra/billing/resolve-billing-team.d.ts +5 -0
  43. package/dist/server/infra/billing/resolve-billing-team.js +8 -0
  44. package/dist/server/infra/connectors/rules/rules-connector.js +7 -2
  45. package/dist/server/infra/connectors/shared/constants.d.ts +9 -0
  46. package/dist/server/infra/connectors/shared/constants.js +31 -5
  47. package/dist/server/infra/daemon/agent-process.js +10 -8
  48. package/dist/server/infra/daemon/brv-server.js +48 -18
  49. package/dist/server/infra/dream/dream-response-schemas.d.ts +24 -0
  50. package/dist/server/infra/dream/dream-response-schemas.js +7 -0
  51. package/dist/server/infra/dream/operations/consolidate.js +21 -8
  52. package/dist/server/infra/dream/operations/synthesize.js +35 -8
  53. package/dist/server/infra/http/provider-model-fetchers.js +10 -4
  54. package/dist/server/infra/process/feature-handlers.d.ts +3 -1
  55. package/dist/server/infra/process/feature-handlers.js +26 -2
  56. package/dist/server/infra/process/task-history-entry-builder.d.ts +36 -0
  57. package/dist/server/infra/process/task-history-entry-builder.js +101 -0
  58. package/dist/server/infra/process/task-history-hook.d.ts +37 -0
  59. package/dist/server/infra/process/task-history-hook.js +70 -0
  60. package/dist/server/infra/process/task-history-store-cache.d.ts +25 -0
  61. package/dist/server/infra/process/task-history-store-cache.js +106 -0
  62. package/dist/server/infra/process/task-router.d.ts +72 -0
  63. package/dist/server/infra/process/task-router.js +690 -15
  64. package/dist/server/infra/process/transport-handlers.d.ts +8 -0
  65. package/dist/server/infra/process/transport-handlers.js +2 -0
  66. package/dist/server/infra/storage/file-billing-config-store.d.ts +13 -0
  67. package/dist/server/infra/storage/file-billing-config-store.js +55 -0
  68. package/dist/server/infra/storage/file-task-history-store.d.ts +294 -0
  69. package/dist/server/infra/storage/file-task-history-store.js +912 -0
  70. package/dist/server/infra/transport/handlers/auth-handler.d.ts +4 -0
  71. package/dist/server/infra/transport/handlers/auth-handler.js +20 -2
  72. package/dist/server/infra/transport/handlers/billing-handler.d.ts +30 -0
  73. package/dist/server/infra/transport/handlers/billing-handler.js +132 -0
  74. package/dist/server/infra/transport/handlers/index.d.ts +4 -0
  75. package/dist/server/infra/transport/handlers/index.js +2 -0
  76. package/dist/server/infra/transport/handlers/init-handler.js +2 -0
  77. package/dist/server/infra/transport/handlers/status-handler.d.ts +14 -0
  78. package/dist/server/infra/transport/handlers/status-handler.js +16 -0
  79. package/dist/server/infra/transport/handlers/team-handler.d.ts +19 -0
  80. package/dist/server/infra/transport/handlers/team-handler.js +40 -0
  81. package/dist/shared/transport/events/auth-events.d.ts +3 -0
  82. package/dist/shared/transport/events/billing-events.d.ts +48 -0
  83. package/dist/shared/transport/events/billing-events.js +8 -0
  84. package/dist/shared/transport/events/index.d.ts +16 -0
  85. package/dist/shared/transport/events/index.js +6 -0
  86. package/dist/shared/transport/events/task-events.d.ts +204 -1
  87. package/dist/shared/transport/events/task-events.js +11 -0
  88. package/dist/shared/transport/events/team-events.d.ts +8 -0
  89. package/dist/shared/transport/events/team-events.js +3 -0
  90. package/dist/shared/transport/types/dto.d.ts +80 -0
  91. package/dist/tui/features/tasks/hooks/use-task-subscriptions.js +7 -0
  92. package/dist/tui/features/tasks/stores/tasks-store.d.ts +4 -16
  93. package/dist/tui/features/tasks/stores/tasks-store.js +7 -0
  94. package/dist/tui/types/messages.d.ts +2 -9
  95. package/dist/webui/assets/index-B9JmEFOK.js +130 -0
  96. package/dist/webui/assets/index-CMIKsBMr.css +1 -0
  97. package/dist/webui/index.html +2 -2
  98. package/dist/webui/sw.js +1 -1
  99. package/oclif.manifest.json +653 -645
  100. package/package.json +1 -1
  101. package/dist/webui/assets/index--sXE__bc.css +0 -1
  102. package/dist/webui/assets/index-Bkkx961b.js +0 -130
@@ -1,6 +1,7 @@
1
1
  import type { IAuthService } from '../../../core/interfaces/auth/i-auth-service.js';
2
2
  import type { ICallbackHandler } from '../../../core/interfaces/auth/i-callback-handler.js';
3
3
  import type { ITokenStore } from '../../../core/interfaces/auth/i-token-store.js';
4
+ import type { IProviderConfigStore } from '../../../core/interfaces/i-provider-config-store.js';
4
5
  import type { IBrowserLauncher } from '../../../core/interfaces/services/i-browser-launcher.js';
5
6
  import type { IUserService } from '../../../core/interfaces/services/i-user-service.js';
6
7
  import type { IAuthStateStore } from '../../../core/interfaces/state/i-auth-state-store.js';
@@ -13,6 +14,7 @@ export interface AuthHandlerDeps {
13
14
  browserLauncher: IBrowserLauncher;
14
15
  callbackHandler: ICallbackHandler;
15
16
  projectConfigStore: IProjectConfigStore;
17
+ providerConfigStore: IProviderConfigStore;
16
18
  resolveProjectPath: ProjectPathResolver;
17
19
  tokenStore: ITokenStore;
18
20
  transport: ITransportServer;
@@ -28,6 +30,7 @@ export declare class AuthHandler {
28
30
  private readonly browserLauncher;
29
31
  private readonly callbackHandler;
30
32
  private readonly projectConfigStore;
33
+ private readonly providerConfigStore;
31
34
  private readonly resolveProjectPath;
32
35
  private readonly tokenStore;
33
36
  private readonly transport;
@@ -41,6 +44,7 @@ export declare class AuthHandler {
41
44
  * On network error, skips broadcast silently (next poll cycle retries in 5s).
42
45
  */
43
46
  private broadcastAuthStateChanged;
47
+ private disconnectByteRoverProvider;
44
48
  private processLoginCallback;
45
49
  /**
46
50
  * Registers callbacks on AuthStateStore to broadcast auth events when
@@ -1,7 +1,9 @@
1
1
  import { AuthEvents, } from '../../../../shared/transport/events/auth-events.js';
2
2
  import { AuthToken } from '../../../core/domain/entities/auth-token.js';
3
+ import { TransportDaemonEventNames } from '../../../core/domain/transport/schemas.js';
3
4
  import { getErrorMessage } from '../../../utils/error-helpers.js';
4
5
  import { processLog } from '../../../utils/process-logger.js';
6
+ const BYTEROVER_PROVIDER_ID = 'byterover';
5
7
  function toUserDTO(user) {
6
8
  const dto = {
7
9
  email: user.email,
@@ -24,6 +26,7 @@ export class AuthHandler {
24
26
  browserLauncher;
25
27
  callbackHandler;
26
28
  projectConfigStore;
29
+ providerConfigStore;
27
30
  resolveProjectPath;
28
31
  tokenStore;
29
32
  transport;
@@ -34,6 +37,7 @@ export class AuthHandler {
34
37
  this.browserLauncher = deps.browserLauncher;
35
38
  this.callbackHandler = deps.callbackHandler;
36
39
  this.projectConfigStore = deps.projectConfigStore;
40
+ this.providerConfigStore = deps.providerConfigStore;
37
41
  this.resolveProjectPath = deps.resolveProjectPath;
38
42
  this.tokenStore = deps.tokenStore;
39
43
  this.transport = deps.transport;
@@ -72,6 +76,18 @@ export class AuthHandler {
72
76
  this.transport.broadcast(AuthEvents.STATE_CHANGED, { isAuthorized: true });
73
77
  }
74
78
  }
79
+ async disconnectByteRoverProvider() {
80
+ try {
81
+ const isConnected = await this.providerConfigStore.isProviderConnected(BYTEROVER_PROVIDER_ID);
82
+ if (!isConnected)
83
+ return;
84
+ await this.providerConfigStore.disconnectProvider(BYTEROVER_PROVIDER_ID);
85
+ this.transport.broadcast(TransportDaemonEventNames.PROVIDER_UPDATED, {});
86
+ }
87
+ catch (error) {
88
+ processLog(`[Auth] Failed to disconnect byterover on auth clear: ${error instanceof Error ? error.message : String(error)}`);
89
+ }
90
+ }
75
91
  async processLoginCallback(authContext, redirectUri) {
76
92
  try {
77
93
  const { code } = await this.callbackHandler.waitForCallback(authContext.state, 5 * 60 * 1000);
@@ -136,16 +152,17 @@ export class AuthHandler {
136
152
  this.authStateStore.onAuthExpired(() => {
137
153
  this.transport.broadcast(AuthEvents.EXPIRED, {});
138
154
  this.transport.broadcast(AuthEvents.STATE_CHANGED, { isAuthorized: false });
155
+ this.disconnectByteRoverProvider();
139
156
  });
140
157
  }
141
158
  setupGetState() {
142
- this.transport.onRequest(AuthEvents.GET_STATE, async (_data, clientId) => {
159
+ this.transport.onRequest(AuthEvents.GET_STATE, async (data) => {
143
160
  try {
144
161
  const token = await this.tokenStore.load();
145
162
  if (token === undefined || !token.isValid()) {
146
163
  return { isAuthorized: false };
147
164
  }
148
- const projectPath = this.resolveProjectPath(clientId);
165
+ const { projectPath } = data;
149
166
  const [user, brvConfig] = await Promise.all([
150
167
  this.userService.getCurrentUser(token.sessionKey),
151
168
  projectPath ? this.projectConfigStore.read(projectPath) : Promise.resolve(),
@@ -204,6 +221,7 @@ export class AuthHandler {
204
221
  this.transport.onRequest(AuthEvents.LOGOUT, async () => {
205
222
  try {
206
223
  await this.tokenStore.clear();
224
+ await this.disconnectByteRoverProvider();
207
225
  await this.authStateStore.loadToken();
208
226
  this.transport.broadcast(AuthEvents.STATE_CHANGED, { isAuthorized: false });
209
227
  return { success: true };
@@ -0,0 +1,30 @@
1
+ import type { IProviderConfigStore } from '../../../core/interfaces/i-provider-config-store.js';
2
+ import type { IBillingService } from '../../../core/interfaces/services/i-billing-service.js';
3
+ import type { IAuthStateStore } from '../../../core/interfaces/state/i-auth-state-store.js';
4
+ import type { IBillingConfigStore } from '../../../core/interfaces/storage/i-billing-config-store.js';
5
+ import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
6
+ import type { ProjectPathResolver } from './handler-types.js';
7
+ export interface BillingHandlerDeps {
8
+ authStateStore: IAuthStateStore;
9
+ billingConfigStoreFactory: (projectPath: string) => IBillingConfigStore;
10
+ billingService: IBillingService;
11
+ providerConfigStore: IProviderConfigStore;
12
+ resolveProjectPath: ProjectPathResolver;
13
+ transport: ITransportServer;
14
+ }
15
+ export declare class BillingHandler {
16
+ private readonly authStateStore;
17
+ private readonly billingConfigStoreFactory;
18
+ private readonly billingService;
19
+ private readonly providerConfigStore;
20
+ private readonly resolveProjectPath;
21
+ private readonly transport;
22
+ constructor(deps: BillingHandlerDeps);
23
+ setup(): void;
24
+ private setupGetFreeUserLimit;
25
+ private setupGetPinnedTeam;
26
+ private setupGetUsage;
27
+ private setupListUsage;
28
+ private setupResolve;
29
+ private setupSetPinnedTeam;
30
+ }
@@ -0,0 +1,132 @@
1
+ import { BillingEvents, } from '../../../../shared/transport/events/billing-events.js';
2
+ import { TransportDaemonEventNames } from '../../../core/domain/transport/schemas.js';
3
+ import { getErrorMessage } from '../../../utils/error-helpers.js';
4
+ import { resolveBillingForProject } from '../../billing/resolve-billing-source.js';
5
+ import { resolveRequiredProjectPath } from './handler-types.js';
6
+ const NOT_AUTHENTICATED_ERROR = 'Billing data requires sign-in. Run /login or brv login to sign in.';
7
+ export class BillingHandler {
8
+ authStateStore;
9
+ billingConfigStoreFactory;
10
+ billingService;
11
+ providerConfigStore;
12
+ resolveProjectPath;
13
+ transport;
14
+ constructor(deps) {
15
+ this.authStateStore = deps.authStateStore;
16
+ this.billingConfigStoreFactory = deps.billingConfigStoreFactory;
17
+ this.billingService = deps.billingService;
18
+ this.providerConfigStore = deps.providerConfigStore;
19
+ this.resolveProjectPath = deps.resolveProjectPath;
20
+ this.transport = deps.transport;
21
+ }
22
+ setup() {
23
+ this.setupGetFreeUserLimit();
24
+ this.setupGetPinnedTeam();
25
+ this.setupGetUsage();
26
+ this.setupListUsage();
27
+ this.setupResolve();
28
+ this.setupSetPinnedTeam();
29
+ }
30
+ setupGetFreeUserLimit() {
31
+ this.transport.onRequest(BillingEvents.GET_FREE_USER_LIMIT, async () => {
32
+ const token = this.authStateStore.getToken();
33
+ if (!token?.isValid()) {
34
+ return { error: NOT_AUTHENTICATED_ERROR };
35
+ }
36
+ try {
37
+ const limit = await this.billingService.getFreeUserLimit(token.sessionKey);
38
+ return { limit };
39
+ }
40
+ catch (error) {
41
+ return { error: getErrorMessage(error) };
42
+ }
43
+ });
44
+ }
45
+ setupGetPinnedTeam() {
46
+ this.transport.onRequest(BillingEvents.GET_PINNED_TEAM, async (data) => {
47
+ if (!data.projectPath)
48
+ return { error: 'projectPath is required' };
49
+ try {
50
+ const store = this.billingConfigStoreFactory(data.projectPath);
51
+ const teamId = await store.getPinnedTeamId();
52
+ return teamId === undefined ? {} : { teamId };
53
+ }
54
+ catch (error) {
55
+ return { error: getErrorMessage(error) };
56
+ }
57
+ });
58
+ }
59
+ setupGetUsage() {
60
+ this.transport.onRequest(BillingEvents.GET_USAGE, async (data) => {
61
+ const token = this.authStateStore.getToken();
62
+ if (!token?.isValid()) {
63
+ return { error: NOT_AUTHENTICATED_ERROR };
64
+ }
65
+ try {
66
+ const usages = await this.billingService.getUsages(token.sessionKey);
67
+ const usage = usages.find((u) => u.organizationId === data.organizationId);
68
+ if (!usage) {
69
+ return { error: `No billing usage found for organization ${data.organizationId}` };
70
+ }
71
+ return { usage };
72
+ }
73
+ catch (error) {
74
+ return { error: getErrorMessage(error) };
75
+ }
76
+ });
77
+ }
78
+ setupListUsage() {
79
+ this.transport.onRequest(BillingEvents.LIST_USAGE, async () => {
80
+ const token = this.authStateStore.getToken();
81
+ if (!token?.isValid()) {
82
+ return { error: NOT_AUTHENTICATED_ERROR };
83
+ }
84
+ try {
85
+ const usages = await this.billingService.getUsages(token.sessionKey);
86
+ const usage = {};
87
+ for (const entry of usages) {
88
+ usage[entry.organizationId] = entry;
89
+ }
90
+ return { usage };
91
+ }
92
+ catch (error) {
93
+ return { error: getErrorMessage(error) };
94
+ }
95
+ });
96
+ }
97
+ setupResolve() {
98
+ this.transport.onRequest(BillingEvents.RESOLVE, async (_, clientId) => {
99
+ try {
100
+ const projectPath = resolveRequiredProjectPath(this.resolveProjectPath, clientId);
101
+ const billing = await resolveBillingForProject({
102
+ authStateStore: this.authStateStore,
103
+ billingConfigStoreFactory: this.billingConfigStoreFactory,
104
+ billingService: this.billingService,
105
+ projectPath,
106
+ providerConfigStore: this.providerConfigStore,
107
+ });
108
+ return { billing };
109
+ }
110
+ catch (error) {
111
+ return { error: getErrorMessage(error) };
112
+ }
113
+ });
114
+ }
115
+ setupSetPinnedTeam() {
116
+ this.transport.onRequest(BillingEvents.SET_PINNED_TEAM, async (data) => {
117
+ if (!data.projectPath)
118
+ return { error: 'projectPath is required', success: false };
119
+ try {
120
+ const { projectPath } = data;
121
+ const store = this.billingConfigStoreFactory(projectPath);
122
+ await store.setPinnedTeamId(data.teamId);
123
+ const payload = data.teamId === undefined ? { projectPath } : { projectPath, teamId: data.teamId };
124
+ this.transport.broadcast(TransportDaemonEventNames.BILLING_PIN_CHANGED, payload);
125
+ return { success: true };
126
+ }
127
+ catch (error) {
128
+ return { error: getErrorMessage(error), success: false };
129
+ }
130
+ });
131
+ }
132
+ }
@@ -1,5 +1,7 @@
1
1
  export { AuthHandler } from './auth-handler.js';
2
2
  export type { AuthHandlerDeps } from './auth-handler.js';
3
+ export { BillingHandler } from './billing-handler.js';
4
+ export type { BillingHandlerDeps } from './billing-handler.js';
3
5
  export { ConfigHandler } from './config-handler.js';
4
6
  export type { ConfigHandlerDeps } from './config-handler.js';
5
7
  export { ConnectorsHandler } from './connectors-handler.js';
@@ -32,6 +34,8 @@ export { SpaceHandler } from './space-handler.js';
32
34
  export type { SpaceHandlerDeps } from './space-handler.js';
33
35
  export { StatusHandler } from './status-handler.js';
34
36
  export type { StatusHandlerDeps } from './status-handler.js';
37
+ export { TeamHandler } from './team-handler.js';
38
+ export type { TeamHandlerDeps } from './team-handler.js';
35
39
  export { VcHandler } from './vc-handler.js';
36
40
  export type { IVcHandlerDeps } from './vc-handler.js';
37
41
  export { WorktreeHandler } from './worktree-handler.js';
@@ -1,4 +1,5 @@
1
1
  export { AuthHandler } from './auth-handler.js';
2
+ export { BillingHandler } from './billing-handler.js';
2
3
  export { ConfigHandler } from './config-handler.js';
3
4
  export { ConnectorsHandler } from './connectors-handler.js';
4
5
  export { ContextTreeHandler } from './context-tree-handler.js';
@@ -15,5 +16,6 @@ export { ReviewHandler } from './review-handler.js';
15
16
  export { SourceHandler } from './source-handler.js';
16
17
  export { SpaceHandler } from './space-handler.js';
17
18
  export { StatusHandler } from './status-handler.js';
19
+ export { TeamHandler } from './team-handler.js';
18
20
  export { VcHandler } from './vc-handler.js';
19
21
  export { WorktreeHandler } from './worktree-handler.js';
@@ -150,10 +150,12 @@ export class InitHandler {
150
150
  const { teams } = await this.teamService.getTeams(token.sessionKey, { fetchAll: true });
151
151
  return {
152
152
  teams: teams.map((t) => ({
153
+ avatarUrl: t.avatarUrl,
153
154
  displayName: t.displayName,
154
155
  id: t.id,
155
156
  isDefault: t.isDefault,
156
157
  name: t.name,
158
+ slug: t.slug,
157
159
  })),
158
160
  };
159
161
  }
@@ -1,17 +1,27 @@
1
1
  import type { ITokenStore } from '../../../core/interfaces/auth/i-token-store.js';
2
2
  import type { IContextTreeService } from '../../../core/interfaces/context-tree/i-context-tree-service.js';
3
3
  import type { IContextTreeSnapshotService } from '../../../core/interfaces/context-tree/i-context-tree-snapshot-service.js';
4
+ import type { IProviderConfigStore } from '../../../core/interfaces/i-provider-config-store.js';
5
+ import type { IBillingService } from '../../../core/interfaces/services/i-billing-service.js';
6
+ import type { IAuthStateStore } from '../../../core/interfaces/state/i-auth-state-store.js';
7
+ import type { IBillingConfigStore } from '../../../core/interfaces/storage/i-billing-config-store.js';
4
8
  import type { ICurateLogStore } from '../../../core/interfaces/storage/i-curate-log-store.js';
5
9
  import type { IProjectConfigStore } from '../../../core/interfaces/storage/i-project-config-store.js';
6
10
  import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
7
11
  import { type ProjectPathResolver } from './handler-types.js';
8
12
  /** Factory that creates a curate log store scoped to a project directory. */
9
13
  export type CurateLogStoreFactory = (projectPath: string) => ICurateLogStore;
14
+ /** Factory that creates a billing config store scoped to a project directory. */
15
+ export type BillingConfigStoreFactory = (projectPath: string) => IBillingConfigStore;
10
16
  export interface StatusHandlerDeps {
17
+ authStateStore: IAuthStateStore;
18
+ billingConfigStoreFactory: BillingConfigStoreFactory;
19
+ billingService: IBillingService;
11
20
  contextTreeService: IContextTreeService;
12
21
  contextTreeSnapshotService: IContextTreeSnapshotService;
13
22
  curateLogStoreFactory: CurateLogStoreFactory;
14
23
  projectConfigStore: IProjectConfigStore;
24
+ providerConfigStore: IProviderConfigStore;
15
25
  resolveProjectPath: ProjectPathResolver;
16
26
  tokenStore: ITokenStore;
17
27
  transport: ITransportServer;
@@ -22,10 +32,14 @@ export interface StatusHandlerDeps {
22
32
  * Collects auth, project, and context tree status — pure data, no terminal output.
23
33
  */
24
34
  export declare class StatusHandler {
35
+ private readonly authStateStore;
36
+ private readonly billingConfigStoreFactory;
37
+ private readonly billingService;
25
38
  private readonly contextTreeService;
26
39
  private readonly contextTreeSnapshotService;
27
40
  private readonly curateLogStoreFactory;
28
41
  private readonly projectConfigStore;
42
+ private readonly providerConfigStore;
29
43
  private readonly resolveProjectPath;
30
44
  private readonly tokenStore;
31
45
  private readonly transport;
@@ -3,6 +3,7 @@ import { join } from 'node:path';
3
3
  import { StatusEvents } from '../../../../shared/transport/events/status-events.js';
4
4
  import { BRV_DIR, CONTEXT_TREE_DIR } from '../../../constants.js';
5
5
  import { listSourceStatuses } from '../../../core/domain/source/source-operations.js';
6
+ import { resolveBillingForProject } from '../../billing/resolve-billing-source.js';
6
7
  import { BrokenWorktreePointerError, MalformedWorktreePointerError, resolveProject } from '../../project/resolve-project.js';
7
8
  import { resolveRequiredProjectPath } from './handler-types.js';
8
9
  /**
@@ -10,19 +11,27 @@ import { resolveRequiredProjectPath } from './handler-types.js';
10
11
  * Collects auth, project, and context tree status — pure data, no terminal output.
11
12
  */
12
13
  export class StatusHandler {
14
+ authStateStore;
15
+ billingConfigStoreFactory;
16
+ billingService;
13
17
  contextTreeService;
14
18
  contextTreeSnapshotService;
15
19
  curateLogStoreFactory;
16
20
  projectConfigStore;
21
+ providerConfigStore;
17
22
  resolveProjectPath;
18
23
  tokenStore;
19
24
  transport;
20
25
  webuiPort;
21
26
  constructor(deps) {
27
+ this.authStateStore = deps.authStateStore;
28
+ this.billingConfigStoreFactory = deps.billingConfigStoreFactory;
29
+ this.billingService = deps.billingService;
22
30
  this.contextTreeService = deps.contextTreeService;
23
31
  this.contextTreeSnapshotService = deps.contextTreeSnapshotService;
24
32
  this.curateLogStoreFactory = deps.curateLogStoreFactory;
25
33
  this.projectConfigStore = deps.projectConfigStore;
34
+ this.providerConfigStore = deps.providerConfigStore;
26
35
  this.resolveProjectPath = deps.resolveProjectPath;
27
36
  this.tokenStore = deps.tokenStore;
28
37
  this.transport = deps.transport;
@@ -98,6 +107,13 @@ export class StatusHandler {
98
107
  }
99
108
  }
100
109
  catch { }
110
+ result.billing = await resolveBillingForProject({
111
+ authStateStore: this.authStateStore,
112
+ billingConfigStoreFactory: this.billingConfigStoreFactory,
113
+ billingService: this.billingService,
114
+ projectPath: effectiveProjectPath,
115
+ providerConfigStore: this.providerConfigStore,
116
+ });
101
117
  // Abstract generation queue status (written by agent process via abstract-queue.ts)
102
118
  try {
103
119
  const queueStatusPath = join(projectPath, BRV_DIR, '_queue_status.json');
@@ -0,0 +1,19 @@
1
+ import type { ITeamService } from '../../../core/interfaces/services/i-team-service.js';
2
+ import type { IAuthStateStore } from '../../../core/interfaces/state/i-auth-state-store.js';
3
+ import type { ITransportServer } from '../../../core/interfaces/transport/i-transport-server.js';
4
+ export interface TeamHandlerDeps {
5
+ authStateStore: IAuthStateStore;
6
+ teamService: ITeamService;
7
+ transport: ITransportServer;
8
+ }
9
+ /**
10
+ * Handles team:* events. Exposes the user's teams to clients (webui) so they
11
+ * can render team pickers without each client re-implementing the IAM call.
12
+ */
13
+ export declare class TeamHandler {
14
+ private readonly authStateStore;
15
+ private readonly teamService;
16
+ private readonly transport;
17
+ constructor(deps: TeamHandlerDeps);
18
+ setup(): void;
19
+ }
@@ -0,0 +1,40 @@
1
+ import { TeamEvents } from '../../../../shared/transport/events/team-events.js';
2
+ import { getErrorMessage } from '../../../utils/error-helpers.js';
3
+ const NOT_AUTHENTICATED_ERROR = 'Listing teams requires sign-in. Run /login or brv login to sign in.';
4
+ /**
5
+ * Handles team:* events. Exposes the user's teams to clients (webui) so they
6
+ * can render team pickers without each client re-implementing the IAM call.
7
+ */
8
+ export class TeamHandler {
9
+ authStateStore;
10
+ teamService;
11
+ transport;
12
+ constructor(deps) {
13
+ this.authStateStore = deps.authStateStore;
14
+ this.teamService = deps.teamService;
15
+ this.transport = deps.transport;
16
+ }
17
+ setup() {
18
+ this.transport.onRequest(TeamEvents.LIST, async () => {
19
+ const token = this.authStateStore.getToken();
20
+ if (!token?.isValid()) {
21
+ return { error: NOT_AUTHENTICATED_ERROR };
22
+ }
23
+ try {
24
+ const { teams } = await this.teamService.getTeams(token.sessionKey, { fetchAll: true });
25
+ const dtos = teams.map((team) => ({
26
+ avatarUrl: team.avatarUrl,
27
+ displayName: team.displayName,
28
+ id: team.id,
29
+ isDefault: team.isDefault,
30
+ name: team.name,
31
+ slug: team.slug,
32
+ }));
33
+ return { teams: dtos };
34
+ }
35
+ catch (error) {
36
+ return { error: getErrorMessage(error) };
37
+ }
38
+ });
39
+ }
40
+ }
@@ -10,6 +10,9 @@ export declare const AuthEvents: {
10
10
  readonly STATE_CHANGED: "auth:stateChanged";
11
11
  readonly UPDATED: "auth:updated";
12
12
  };
13
+ export interface AuthGetStateRequest {
14
+ projectPath: string;
15
+ }
13
16
  export interface AuthGetStateResponse {
14
17
  authToken?: AuthTokenDTO;
15
18
  brvConfig?: BrvConfigDTO;
@@ -0,0 +1,48 @@
1
+ import type { BillingFreeUserLimitDTO, BillingUsageDTO, StatusBillingDTO } from '../types/dto.js';
2
+ export declare const BillingEvents: {
3
+ readonly GET_FREE_USER_LIMIT: "billing:getFreeUserLimit";
4
+ readonly GET_PINNED_TEAM: "billing:getPinnedTeam";
5
+ readonly GET_USAGE: "billing:getUsage";
6
+ readonly LIST_USAGE: "billing:listUsage";
7
+ readonly RESOLVE: "billing:resolve";
8
+ readonly SET_PINNED_TEAM: "billing:setPinnedTeam";
9
+ };
10
+ export interface BillingResolveResponse {
11
+ billing?: StatusBillingDTO;
12
+ error?: string;
13
+ }
14
+ export interface BillingGetUsageRequest {
15
+ /** Organization (team) whose usage should be reported. */
16
+ organizationId: string;
17
+ }
18
+ export interface BillingGetUsageResponse {
19
+ error?: string;
20
+ usage?: BillingUsageDTO;
21
+ }
22
+ export interface BillingListUsageResponse {
23
+ /** Top-level error (auth/transport). When present, `usage` is omitted. */
24
+ error?: string;
25
+ /** Every organization the user belongs to, keyed by organizationId. */
26
+ usage?: Record<string, BillingUsageDTO>;
27
+ }
28
+ export interface BillingGetFreeUserLimitResponse {
29
+ error?: string;
30
+ limit?: BillingFreeUserLimitDTO;
31
+ }
32
+ export interface BillingGetPinnedTeamRequest {
33
+ projectPath: string;
34
+ }
35
+ export interface BillingGetPinnedTeamResponse {
36
+ error?: string;
37
+ /** When undefined, no pin is set and the consumer should fall back to its workspace default. */
38
+ teamId?: string;
39
+ }
40
+ export interface BillingSetPinnedTeamRequest {
41
+ projectPath: string;
42
+ /** Pass `undefined` (or omit) to clear the pin. */
43
+ teamId?: string;
44
+ }
45
+ export interface BillingSetPinnedTeamResponse {
46
+ error?: string;
47
+ success: boolean;
48
+ }
@@ -0,0 +1,8 @@
1
+ export const BillingEvents = {
2
+ GET_FREE_USER_LIMIT: 'billing:getFreeUserLimit',
3
+ GET_PINNED_TEAM: 'billing:getPinnedTeam',
4
+ GET_USAGE: 'billing:getUsage',
5
+ LIST_USAGE: 'billing:listUsage',
6
+ RESOLVE: 'billing:resolve',
7
+ SET_PINNED_TEAM: 'billing:setPinnedTeam',
8
+ };
@@ -1,6 +1,7 @@
1
1
  export * from '../types/dto.js';
2
2
  export * from './agent-events.js';
3
3
  export * from './auth-events.js';
4
+ export * from './billing-events.js';
4
5
  export * from './client-events.js';
5
6
  export * from './config-events.js';
6
7
  export * from './connector-events.js';
@@ -21,6 +22,7 @@ export * from './source-events.js';
21
22
  export * from './space-events.js';
22
23
  export * from './status-events.js';
23
24
  export * from './task-events.js';
25
+ export * from './team-events.js';
24
26
  export * from './vc-events.js';
25
27
  export * from './worktree-events.js';
26
28
  /**
@@ -46,6 +48,13 @@ export declare const AllEventGroups: readonly [{
46
48
  readonly START_LOGIN: "auth:startLogin";
47
49
  readonly STATE_CHANGED: "auth:stateChanged";
48
50
  readonly UPDATED: "auth:updated";
51
+ }, {
52
+ readonly GET_FREE_USER_LIMIT: "billing:getFreeUserLimit";
53
+ readonly GET_PINNED_TEAM: "billing:getPinnedTeam";
54
+ readonly GET_USAGE: "billing:getUsage";
55
+ readonly LIST_USAGE: "billing:listUsage";
56
+ readonly RESOLVE: "billing:resolve";
57
+ readonly SET_PINNED_TEAM: "billing:setPinnedTeam";
49
58
  }, {
50
59
  readonly ASSOCIATE_PROJECT: "client:associateProject";
51
60
  }, {
@@ -146,12 +155,19 @@ export declare const AllEventGroups: readonly [{
146
155
  readonly ACK: "task:ack";
147
156
  readonly CANCEL: "task:cancel";
148
157
  readonly CANCELLED: "task:cancelled";
158
+ readonly CLEAR_COMPLETED: "task:clearCompleted";
149
159
  readonly COMPLETED: "task:completed";
150
160
  readonly CREATE: "task:create";
151
161
  readonly CREATED: "task:created";
162
+ readonly DELETE: "task:delete";
163
+ readonly DELETE_BULK: "task:deleteBulk";
164
+ readonly DELETED: "task:deleted";
152
165
  readonly ERROR: "task:error";
166
+ readonly GET: "task:get";
153
167
  readonly LIST: "task:list";
154
168
  readonly STARTED: "task:started";
169
+ }, {
170
+ readonly LIST: "team:list";
155
171
  }, {
156
172
  readonly ADD: "vc:add";
157
173
  readonly BRANCH: "vc:branch";
@@ -3,6 +3,7 @@ export * from '../types/dto.js';
3
3
  // Event constants and types
4
4
  export * from './agent-events.js';
5
5
  export * from './auth-events.js';
6
+ export * from './billing-events.js';
6
7
  export * from './client-events.js';
7
8
  export * from './config-events.js';
8
9
  export * from './connector-events.js';
@@ -23,11 +24,13 @@ export * from './source-events.js';
23
24
  export * from './space-events.js';
24
25
  export * from './status-events.js';
25
26
  export * from './task-events.js';
27
+ export * from './team-events.js';
26
28
  export * from './vc-events.js';
27
29
  export * from './worktree-events.js';
28
30
  // Utility exports
29
31
  import { AgentEvents } from './agent-events.js';
30
32
  import { AuthEvents } from './auth-events.js';
33
+ import { BillingEvents } from './billing-events.js';
31
34
  import { ClientEvents } from './client-events.js';
32
35
  import { ConfigEvents } from './config-events.js';
33
36
  import { ConnectorEvents } from './connector-events.js';
@@ -48,6 +51,7 @@ import { SourceEvents } from './source-events.js';
48
51
  import { SpaceEvents } from './space-events.js';
49
52
  import { StatusEvents } from './status-events.js';
50
53
  import { TaskEvents } from './task-events.js';
54
+ import { TeamEvents } from './team-events.js';
51
55
  import { VcEvents } from './vc-events.js';
52
56
  import { WorktreeEvents } from './worktree-events.js';
53
57
  /**
@@ -57,6 +61,7 @@ import { WorktreeEvents } from './worktree-events.js';
57
61
  export const AllEventGroups = [
58
62
  AgentEvents,
59
63
  AuthEvents,
64
+ BillingEvents,
60
65
  ClientEvents,
61
66
  ConfigEvents,
62
67
  ConnectorEvents,
@@ -77,6 +82,7 @@ export const AllEventGroups = [
77
82
  SpaceEvents,
78
83
  StatusEvents,
79
84
  TaskEvents,
85
+ TeamEvents,
80
86
  VcEvents,
81
87
  WorktreeEvents,
82
88
  ];