byterover-cli 3.12.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.
- package/.env.production +2 -1
- package/dist/oclif/commands/curate/index.js +6 -0
- package/dist/oclif/commands/providers/connect.d.ts +26 -1
- package/dist/oclif/commands/providers/connect.js +95 -17
- package/dist/oclif/commands/providers/list.d.ts +10 -1
- package/dist/oclif/commands/providers/list.js +35 -3
- package/dist/oclif/commands/query.js +6 -0
- package/dist/oclif/commands/status.js +4 -0
- package/dist/oclif/lib/billing-line.d.ts +8 -0
- package/dist/oclif/lib/billing-line.js +45 -0
- package/dist/oclif/lib/format-billing-line.d.ts +2 -0
- package/dist/oclif/lib/format-billing-line.js +19 -0
- package/dist/oclif/lib/insufficient-credits.d.ts +11 -0
- package/dist/oclif/lib/insufficient-credits.js +36 -0
- package/dist/server/config/environment.d.ts +1 -0
- package/dist/server/config/environment.js +3 -0
- package/dist/server/core/domain/transport/schemas.d.ts +17 -0
- package/dist/server/core/domain/transport/schemas.js +3 -0
- package/dist/server/core/interfaces/services/i-billing-service.d.ts +26 -0
- package/dist/server/core/interfaces/services/i-billing-service.js +1 -0
- package/dist/server/core/interfaces/storage/i-billing-config-store.d.ts +4 -0
- package/dist/server/core/interfaces/storage/i-billing-config-store.js +1 -0
- package/dist/server/infra/billing/billing-state-endpoint.d.ts +4 -0
- package/dist/server/infra/billing/billing-state-endpoint.js +7 -0
- package/dist/server/infra/billing/build-status-billing.d.ts +9 -0
- package/dist/server/infra/billing/build-status-billing.js +36 -0
- package/dist/server/infra/billing/http-billing-service.d.ts +19 -0
- package/dist/server/infra/billing/http-billing-service.js +57 -0
- package/dist/server/infra/billing/paid-organizations-endpoint.d.ts +8 -0
- package/dist/server/infra/billing/paid-organizations-endpoint.js +18 -0
- package/dist/server/infra/billing/resolve-billing-source.d.ts +13 -0
- package/dist/server/infra/billing/resolve-billing-source.js +36 -0
- package/dist/server/infra/billing/resolve-billing-team.d.ts +5 -0
- package/dist/server/infra/billing/resolve-billing-team.js +8 -0
- package/dist/server/infra/connectors/rules/rules-connector.js +7 -2
- package/dist/server/infra/connectors/shared/constants.d.ts +9 -0
- package/dist/server/infra/connectors/shared/constants.js +31 -5
- package/dist/server/infra/daemon/agent-process.js +10 -8
- package/dist/server/infra/daemon/brv-server.js +5 -0
- package/dist/server/infra/http/provider-model-fetchers.js +10 -4
- package/dist/server/infra/process/feature-handlers.d.ts +3 -1
- package/dist/server/infra/process/feature-handlers.js +26 -2
- package/dist/server/infra/storage/file-billing-config-store.d.ts +13 -0
- package/dist/server/infra/storage/file-billing-config-store.js +55 -0
- package/dist/server/infra/transport/handlers/auth-handler.d.ts +4 -0
- package/dist/server/infra/transport/handlers/auth-handler.js +20 -2
- package/dist/server/infra/transport/handlers/billing-handler.d.ts +30 -0
- package/dist/server/infra/transport/handlers/billing-handler.js +132 -0
- package/dist/server/infra/transport/handlers/index.d.ts +4 -0
- package/dist/server/infra/transport/handlers/index.js +2 -0
- package/dist/server/infra/transport/handlers/init-handler.js +2 -0
- package/dist/server/infra/transport/handlers/status-handler.d.ts +14 -0
- package/dist/server/infra/transport/handlers/status-handler.js +16 -0
- package/dist/server/infra/transport/handlers/team-handler.d.ts +19 -0
- package/dist/server/infra/transport/handlers/team-handler.js +40 -0
- package/dist/shared/transport/events/auth-events.d.ts +3 -0
- package/dist/shared/transport/events/billing-events.d.ts +48 -0
- package/dist/shared/transport/events/billing-events.js +8 -0
- package/dist/shared/transport/events/index.d.ts +11 -0
- package/dist/shared/transport/events/index.js +6 -0
- package/dist/shared/transport/events/team-events.d.ts +8 -0
- package/dist/shared/transport/events/team-events.js +3 -0
- package/dist/shared/transport/types/dto.d.ts +80 -0
- package/dist/webui/assets/index-B9JmEFOK.js +130 -0
- package/dist/webui/assets/index-CMIKsBMr.css +1 -0
- package/dist/webui/index.html +2 -2
- package/dist/webui/sw.js +1 -1
- package/oclif.manifest.json +1280 -1272
- package/package.json +1 -1
- package/dist/webui/assets/index-DyVvFoM6.css +0 -1
- package/dist/webui/assets/index-lr0byHh9.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 (
|
|
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 =
|
|
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
|
}, {
|
|
@@ -157,6 +166,8 @@ export declare const AllEventGroups: readonly [{
|
|
|
157
166
|
readonly GET: "task:get";
|
|
158
167
|
readonly LIST: "task:list";
|
|
159
168
|
readonly STARTED: "task:started";
|
|
169
|
+
}, {
|
|
170
|
+
readonly LIST: "team:list";
|
|
160
171
|
}, {
|
|
161
172
|
readonly ADD: "vc:add";
|
|
162
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
|
];
|