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.
- package/.env.production +2 -1
- package/dist/agent/infra/tools/implementations/curate-tool.js +18 -8
- 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/constants.d.ts +6 -0
- package/dist/server/constants.js +11 -0
- package/dist/server/core/domain/entities/task-history-entry.d.ts +775 -0
- package/dist/server/core/domain/entities/task-history-entry.js +88 -0
- package/dist/server/core/domain/transport/schemas.d.ts +1420 -11
- package/dist/server/core/domain/transport/schemas.js +160 -6
- package/dist/server/core/domain/transport/task-info.d.ts +18 -0
- package/dist/server/core/interfaces/process/i-task-lifecycle-hook.d.ts +7 -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/core/interfaces/storage/i-task-history-store.d.ts +62 -0
- package/dist/server/core/interfaces/storage/i-task-history-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 +48 -18
- package/dist/server/infra/dream/dream-response-schemas.d.ts +24 -0
- package/dist/server/infra/dream/dream-response-schemas.js +7 -0
- package/dist/server/infra/dream/operations/consolidate.js +21 -8
- package/dist/server/infra/dream/operations/synthesize.js +35 -8
- 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/process/task-history-entry-builder.d.ts +36 -0
- package/dist/server/infra/process/task-history-entry-builder.js +101 -0
- package/dist/server/infra/process/task-history-hook.d.ts +37 -0
- package/dist/server/infra/process/task-history-hook.js +70 -0
- package/dist/server/infra/process/task-history-store-cache.d.ts +25 -0
- package/dist/server/infra/process/task-history-store-cache.js +106 -0
- package/dist/server/infra/process/task-router.d.ts +72 -0
- package/dist/server/infra/process/task-router.js +690 -15
- package/dist/server/infra/process/transport-handlers.d.ts +8 -0
- package/dist/server/infra/process/transport-handlers.js +2 -0
- 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/storage/file-task-history-store.d.ts +294 -0
- package/dist/server/infra/storage/file-task-history-store.js +912 -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 +16 -0
- package/dist/shared/transport/events/index.js +6 -0
- package/dist/shared/transport/events/task-events.d.ts +204 -1
- package/dist/shared/transport/events/task-events.js +11 -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/tui/features/tasks/hooks/use-task-subscriptions.js +7 -0
- package/dist/tui/features/tasks/stores/tasks-store.d.ts +4 -16
- package/dist/tui/features/tasks/stores/tasks-store.js +7 -0
- package/dist/tui/types/messages.d.ts +2 -9
- 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 +653 -645
- package/package.json +1 -1
- package/dist/webui/assets/index--sXE__bc.css +0 -1
- package/dist/webui/assets/index-Bkkx961b.js +0 -130
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { AGENT_CONNECTOR_CONFIG } from '../../../core/domain/entities/agent.js';
|
|
3
|
-
import { hasMcpToolsInBrvSection } from '../shared/constants.js';
|
|
3
|
+
import { extractInstalledAgentFromBrvSection, hasMcpToolsInBrvSection } from '../shared/constants.js';
|
|
4
4
|
import { RuleFileManager } from '../shared/rule-file-manager.js';
|
|
5
5
|
import { RULES_CONNECTOR_CONFIGS } from './rules-connector-config.js';
|
|
6
6
|
/**
|
|
@@ -99,7 +99,12 @@ export class RulesConnector {
|
|
|
99
99
|
}
|
|
100
100
|
const content = await this.fileService.read(fullPath);
|
|
101
101
|
const hasMcpTools = hasMcpToolsInBrvSection(content);
|
|
102
|
-
const
|
|
102
|
+
const footerAgent = extractInstalledAgentFromBrvSection(content);
|
|
103
|
+
// Footer present: only the agent named in the footer owns this rule file.
|
|
104
|
+
// Footer absent (legacy file pre-footer): fall back to marker presence so
|
|
105
|
+
// existing installs keep reporting installed until the next reinstall.
|
|
106
|
+
const matchesFooter = footerAgent === undefined ? true : footerAgent === agent;
|
|
107
|
+
const installed = hasMarkers && !hasMcpTools && matchesFooter;
|
|
103
108
|
return {
|
|
104
109
|
configExists: true,
|
|
105
110
|
configPath: config.filePath,
|
|
@@ -15,3 +15,12 @@ export declare const BRV_RULE_MARKERS: {
|
|
|
15
15
|
* Only checks within the markers section to avoid false positives from user content.
|
|
16
16
|
*/
|
|
17
17
|
export declare const hasMcpToolsInBrvSection: (content: string) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Extracts the agent name from a `Generated by ByteRover CLI for X` footer
|
|
20
|
+
* inside the BRV markers section. Used to disambiguate which agent owns a
|
|
21
|
+
* shared rule file (Amp / Codex / OpenCode all map to AGENTS.md).
|
|
22
|
+
*
|
|
23
|
+
* Returns undefined when markers are missing, when the footer is absent
|
|
24
|
+
* (legacy pre-footer installs), or when the footer line is empty.
|
|
25
|
+
*/
|
|
26
|
+
export declare const extractInstalledAgentFromBrvSection: (content: string) => string | undefined;
|
|
@@ -10,16 +10,42 @@ export const BRV_RULE_MARKERS = {
|
|
|
10
10
|
END: '<!-- END BYTEROVER RULES -->',
|
|
11
11
|
START: '<!-- BEGIN BYTEROVER RULES -->',
|
|
12
12
|
};
|
|
13
|
+
const sliceBrvSection = (content) => {
|
|
14
|
+
const startIdx = content.indexOf(BRV_RULE_MARKERS.START);
|
|
15
|
+
const endIdx = content.indexOf(BRV_RULE_MARKERS.END);
|
|
16
|
+
if (startIdx === -1 || endIdx === -1 || endIdx < startIdx)
|
|
17
|
+
return undefined;
|
|
18
|
+
return content.slice(startIdx, endIdx);
|
|
19
|
+
};
|
|
13
20
|
/**
|
|
14
21
|
* Checks if the BRV markers section contains MCP tool references (brv-query/brv-curate).
|
|
15
22
|
* Only checks within the markers section to avoid false positives from user content.
|
|
16
23
|
*/
|
|
17
24
|
export const hasMcpToolsInBrvSection = (content) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
if (startIdx === -1 || endIdx === -1)
|
|
25
|
+
const brvSection = sliceBrvSection(content);
|
|
26
|
+
if (brvSection === undefined)
|
|
21
27
|
return false;
|
|
22
|
-
// eslint-disable-next-line unicorn/prefer-set-has
|
|
23
|
-
const brvSection = content.slice(startIdx, endIdx);
|
|
24
28
|
return brvSection.includes('brv-query') || brvSection.includes('brv-curate');
|
|
25
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Extracts the agent name from a `Generated by ByteRover CLI for X` footer
|
|
32
|
+
* inside the BRV markers section. Used to disambiguate which agent owns a
|
|
33
|
+
* shared rule file (Amp / Codex / OpenCode all map to AGENTS.md).
|
|
34
|
+
*
|
|
35
|
+
* Returns undefined when markers are missing, when the footer is absent
|
|
36
|
+
* (legacy pre-footer installs), or when the footer line is empty.
|
|
37
|
+
*/
|
|
38
|
+
export const extractInstalledAgentFromBrvSection = (content) => {
|
|
39
|
+
const brvSection = sliceBrvSection(content);
|
|
40
|
+
if (brvSection === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
const tagWithDelimiter = `${BRV_RULE_TAG} `;
|
|
43
|
+
const tagIdx = brvSection.indexOf(tagWithDelimiter);
|
|
44
|
+
if (tagIdx === -1)
|
|
45
|
+
return undefined;
|
|
46
|
+
const afterTag = brvSection.slice(tagIdx + tagWithDelimiter.length);
|
|
47
|
+
const newlineIdx = afterTag.indexOf('\n');
|
|
48
|
+
const agentLine = newlineIdx === -1 ? afterTag : afterTag.slice(0, newlineIdx);
|
|
49
|
+
const agent = agentLine.trim();
|
|
50
|
+
return agent.length === 0 ? undefined : agent;
|
|
51
|
+
};
|
|
@@ -133,7 +133,7 @@ async function activateExistingSession(sessionId, providerId) {
|
|
|
133
133
|
*/
|
|
134
134
|
let cachedSessionKey = '';
|
|
135
135
|
let cachedBrvConfig;
|
|
136
|
-
let
|
|
136
|
+
let cachedPinnedOrgId;
|
|
137
137
|
let cachedSpaceId = '';
|
|
138
138
|
let cachedActiveProvider = '';
|
|
139
139
|
let cachedActiveModel = '';
|
|
@@ -177,15 +177,16 @@ async function start() {
|
|
|
177
177
|
transport.on('connect_error', (err) => {
|
|
178
178
|
agentLog(`Transport connect_error: ${err?.message ?? 'unknown'}`);
|
|
179
179
|
});
|
|
180
|
-
const [configResult, authResult, providerResult] = await Promise.all([
|
|
180
|
+
const [configResult, authResult, providerResult, billingResult] = await Promise.all([
|
|
181
181
|
transport.requestWithAck(TransportStateEventNames.GET_PROJECT_CONFIG, { projectPath }),
|
|
182
182
|
transport.requestWithAck(TransportStateEventNames.GET_AUTH),
|
|
183
183
|
transport.requestWithAck(TransportStateEventNames.GET_PROVIDER_CONFIG),
|
|
184
|
+
transport.requestWithAck(TransportStateEventNames.GET_BILLING_CONFIG, { projectPath }),
|
|
184
185
|
]);
|
|
185
186
|
cachedBrvConfig = configResult.brvConfig;
|
|
186
|
-
cachedTeamId = configResult.teamId ?? '';
|
|
187
187
|
cachedSpaceId = configResult.spaceId ?? '';
|
|
188
188
|
cachedSessionKey = authResult.sessionKey ?? '';
|
|
189
|
+
cachedPinnedOrgId = billingResult.pinnedTeamId;
|
|
189
190
|
agentLog('Initial config loaded from state server');
|
|
190
191
|
// 3. Listen for config/auth/provider updates from daemon
|
|
191
192
|
transport.on('config:updated', (data) => {
|
|
@@ -193,8 +194,6 @@ async function start() {
|
|
|
193
194
|
return;
|
|
194
195
|
if (data.brvConfig)
|
|
195
196
|
cachedBrvConfig = data.brvConfig;
|
|
196
|
-
if (data.teamId !== undefined)
|
|
197
|
-
cachedTeamId = data.teamId;
|
|
198
197
|
if (data.spaceId !== undefined)
|
|
199
198
|
cachedSpaceId = data.spaceId;
|
|
200
199
|
});
|
|
@@ -206,6 +205,11 @@ async function start() {
|
|
|
206
205
|
providerConfigDirty = true;
|
|
207
206
|
providerFetchRetries = 0;
|
|
208
207
|
});
|
|
208
|
+
transport.on(TransportDaemonEventNames.BILLING_PIN_CHANGED, (data) => {
|
|
209
|
+
if (data.projectPath !== projectPath)
|
|
210
|
+
return;
|
|
211
|
+
cachedPinnedOrgId = data.teamId;
|
|
212
|
+
});
|
|
209
213
|
// 4. Provider config resolved by daemon (API key, base URL, headers, etc.)
|
|
210
214
|
const { activeModel, activeProvider } = providerResult;
|
|
211
215
|
cachedActiveProvider = activeProvider;
|
|
@@ -244,7 +248,7 @@ async function start() {
|
|
|
244
248
|
projectIdProvider: () => PROJECT,
|
|
245
249
|
sessionKeyProvider: () => cachedSessionKey,
|
|
246
250
|
spaceIdProvider: () => cachedSpaceId,
|
|
247
|
-
teamIdProvider: () =>
|
|
251
|
+
teamIdProvider: () => cachedPinnedOrgId ?? '',
|
|
248
252
|
transportClient: transport,
|
|
249
253
|
});
|
|
250
254
|
await agent.start();
|
|
@@ -393,8 +397,6 @@ async function executeTask(task, curateExecutor, folderPackExecutor, queryExecut
|
|
|
393
397
|
const configResult = await transport.requestWithAck(TransportStateEventNames.GET_PROJECT_CONFIG, { projectPath });
|
|
394
398
|
if (configResult.brvConfig)
|
|
395
399
|
cachedBrvConfig = configResult.brvConfig;
|
|
396
|
-
if (configResult.teamId !== undefined)
|
|
397
|
-
cachedTeamId = configResult.teamId;
|
|
398
400
|
if (configResult.spaceId !== undefined)
|
|
399
401
|
cachedSpaceId = configResult.spaceId;
|
|
400
402
|
}
|
|
@@ -34,6 +34,7 @@ import { TransportStateEventNames, TransportTaskEventNames, } from '../../core/d
|
|
|
34
34
|
import { getGlobalDataDir } from '../../utils/global-data-path.js';
|
|
35
35
|
import { getProjectDataDir } from '../../utils/path-utils.js';
|
|
36
36
|
import { crashLog, processLog } from '../../utils/process-logger.js';
|
|
37
|
+
import { createBillingStateHandler } from '../billing/billing-state-endpoint.js';
|
|
37
38
|
import { ClientManager } from '../client/client-manager.js';
|
|
38
39
|
import { ProjectConfigStore } from '../config/file-config-store.js';
|
|
39
40
|
import { readContextTreeRemoteUrl } from '../context-tree/read-context-tree-remote.js';
|
|
@@ -44,6 +45,8 @@ import { broadcastToProjectRoom } from '../process/broadcast-utils.js';
|
|
|
44
45
|
import { CurateLogHandler } from '../process/curate-log-handler.js';
|
|
45
46
|
import { setupFeatureHandlers } from '../process/feature-handlers.js';
|
|
46
47
|
import { QueryLogHandler } from '../process/query-log-handler.js';
|
|
48
|
+
import { TaskHistoryHook } from '../process/task-history-hook.js';
|
|
49
|
+
import { getStore as getTaskHistoryStore } from '../process/task-history-store-cache.js';
|
|
47
50
|
import { TransportHandlers } from '../process/transport-handlers.js';
|
|
48
51
|
import { ProjectRegistry } from '../project/project-registry.js';
|
|
49
52
|
import { createProviderOAuthTokenStore } from '../provider-oauth/provider-oauth-token-store.js';
|
|
@@ -52,6 +55,7 @@ import { clearStaleProviderConfig, resolveProviderConfig } from '../provider/pro
|
|
|
52
55
|
import { ProjectRouter } from '../routing/project-router.js';
|
|
53
56
|
import { AuthStateStore } from '../state/auth-state-store.js';
|
|
54
57
|
import { ProjectStateLoader } from '../state/project-state-loader.js';
|
|
58
|
+
import { FileBillingConfigStore } from '../storage/file-billing-config-store.js';
|
|
55
59
|
import { FileCurateLogStore } from '../storage/file-curate-log-store.js';
|
|
56
60
|
import { FileProviderConfigStore } from '../storage/file-provider-config-store.js';
|
|
57
61
|
import { FileReviewBackupStore } from '../storage/file-review-backup-store.js';
|
|
@@ -334,6 +338,32 @@ async function main() {
|
|
|
334
338
|
broadcastToProjectRoom(projectRegistry, projectRouter, info.projectPath, ReviewEvents.NOTIFY, payload, info.clientId);
|
|
335
339
|
});
|
|
336
340
|
const queryLogHandler = new QueryLogHandler();
|
|
341
|
+
// Task-history hook — persists every lifecycle transition + accumulated
|
|
342
|
+
// llmservice events to a per-project FileTaskHistoryStore. The store
|
|
343
|
+
// factory is module-scoped so M2.09 wire handlers can read from the
|
|
344
|
+
// same instances this hook writes to.
|
|
345
|
+
const taskHistoryHook = new TaskHistoryHook({ getStore: getTaskHistoryStore });
|
|
346
|
+
// Provider config/keychain stores — shared between feature handlers and state endpoint.
|
|
347
|
+
// Hoisted ahead of `new TransportHandlers` so the resolveActiveProvider callback below
|
|
348
|
+
// can close over them and call resolveProviderConfig synchronously at task-create time.
|
|
349
|
+
const providerConfigStore = new FileProviderConfigStore();
|
|
350
|
+
const providerKeychainStore = createProviderKeychainStore();
|
|
351
|
+
const providerOAuthTokenStore = createProviderOAuthTokenStore();
|
|
352
|
+
// Token refresh manager — transparently refreshes OAuth tokens before they expire
|
|
353
|
+
const tokenRefreshManager = new TokenRefreshManager({
|
|
354
|
+
providerConfigStore,
|
|
355
|
+
providerKeychainStore,
|
|
356
|
+
providerOAuthTokenStore,
|
|
357
|
+
transport: transportServer,
|
|
358
|
+
});
|
|
359
|
+
// Clear stale provider config on startup (e.g. migration from v1 system keychain to v2 file keystore).
|
|
360
|
+
// If a provider is configured but its API key is no longer accessible, disconnect it so the user
|
|
361
|
+
// is returned to the onboarding flow rather than hitting a cryptic API key error mid-task.
|
|
362
|
+
await clearStaleProviderConfig(providerConfigStore, providerKeychainStore, providerOAuthTokenStore);
|
|
363
|
+
// State endpoint: provider config — agents request this on startup and after provider:updated
|
|
364
|
+
transportServer.onRequest(TransportStateEventNames.GET_PROVIDER_CONFIG, async () => resolveProviderConfig({ authStateStore, providerConfigStore, providerKeychainStore, tokenRefreshManager }));
|
|
365
|
+
const billingConfigStoreFactory = (projectPath) => new FileBillingConfigStore({ baseDir: join(projectPath, BRV_DIR) });
|
|
366
|
+
transportServer.onRequest(TransportStateEventNames.GET_BILLING_CONFIG, createBillingStateHandler(billingConfigStoreFactory));
|
|
337
367
|
const transportHandlers = new TransportHandlers({
|
|
338
368
|
agentPool,
|
|
339
369
|
clientManager,
|
|
@@ -341,6 +371,7 @@ async function main() {
|
|
|
341
371
|
// so peer clients (TUI / MCP) can render drift indicators without an
|
|
342
372
|
// extra round-trip.
|
|
343
373
|
daemonVersion: version,
|
|
374
|
+
getTaskHistoryStore,
|
|
344
375
|
// Resolves the project's review-disabled flag once at task-create. The result
|
|
345
376
|
// is stamped onto TaskInfo + TaskExecute so daemon hooks (CurateLogHandler) and
|
|
346
377
|
// the agent process (curate-tool backups, dream review entries) all observe a
|
|
@@ -348,7 +379,7 @@ async function main() {
|
|
|
348
379
|
// idle-dream dispatch above so review semantics are identical regardless of
|
|
349
380
|
// dispatch source (CLI task:create vs agent-idle trigger).
|
|
350
381
|
isReviewDisabled: resolveReviewDisabled,
|
|
351
|
-
lifecycleHooks: [curateLogHandler, queryLogHandler],
|
|
382
|
+
lifecycleHooks: [curateLogHandler, queryLogHandler, taskHistoryHook],
|
|
352
383
|
// Daemon-side gate for dream task:create — mirrors the idle-trigger pre-check
|
|
353
384
|
// in this file so the CLI path (brv dream without --force) actually honors
|
|
354
385
|
// gate 3 (queue). The agent-side check kept gate 3 hardcoded to skip,
|
|
@@ -369,6 +400,21 @@ async function main() {
|
|
|
369
400
|
},
|
|
370
401
|
projectRegistry,
|
|
371
402
|
projectRouter,
|
|
403
|
+
// Stamp the active provider/model snapshot onto every created task so the
|
|
404
|
+
// Web UI can display which provider handled which task. Failures are
|
|
405
|
+
// swallowed by TaskRouter's safeResolveActiveProvider — never blocks dispatch.
|
|
406
|
+
async resolveActiveProvider() {
|
|
407
|
+
const config = await resolveProviderConfig({
|
|
408
|
+
authStateStore,
|
|
409
|
+
providerConfigStore,
|
|
410
|
+
providerKeychainStore,
|
|
411
|
+
tokenRefreshManager,
|
|
412
|
+
});
|
|
413
|
+
return {
|
|
414
|
+
...(config.activeModel ? { model: config.activeModel } : {}),
|
|
415
|
+
...(config.activeProvider ? { provider: config.activeProvider } : {}),
|
|
416
|
+
};
|
|
417
|
+
},
|
|
372
418
|
transport: transportServer,
|
|
373
419
|
});
|
|
374
420
|
transportHandlers.setup();
|
|
@@ -526,28 +572,12 @@ async function main() {
|
|
|
526
572
|
running: transportServer.isRunning(),
|
|
527
573
|
},
|
|
528
574
|
}));
|
|
529
|
-
// Provider config/keychain stores — shared between feature handlers and state endpoint
|
|
530
|
-
const providerConfigStore = new FileProviderConfigStore();
|
|
531
|
-
const providerKeychainStore = createProviderKeychainStore();
|
|
532
|
-
const providerOAuthTokenStore = createProviderOAuthTokenStore();
|
|
533
|
-
// Token refresh manager — transparently refreshes OAuth tokens before they expire
|
|
534
|
-
const tokenRefreshManager = new TokenRefreshManager({
|
|
535
|
-
providerConfigStore,
|
|
536
|
-
providerKeychainStore,
|
|
537
|
-
providerOAuthTokenStore,
|
|
538
|
-
transport: transportServer,
|
|
539
|
-
});
|
|
540
|
-
// Clear stale provider config on startup (e.g. migration from v1 system keychain to v2 file keystore).
|
|
541
|
-
// If a provider is configured but its API key is no longer accessible, disconnect it so the user
|
|
542
|
-
// is returned to the onboarding flow rather than hitting a cryptic API key error mid-task.
|
|
543
|
-
await clearStaleProviderConfig(providerConfigStore, providerKeychainStore, providerOAuthTokenStore);
|
|
544
|
-
// State endpoint: provider config — agents request this on startup and after provider:updated
|
|
545
|
-
transportServer.onRequest(TransportStateEventNames.GET_PROVIDER_CONFIG, async () => resolveProviderConfig({ authStateStore, providerConfigStore, providerKeychainStore, tokenRefreshManager }));
|
|
546
575
|
// Feature handlers (auth, init, status, push, pull, etc.) require async OIDC discovery.
|
|
547
576
|
// Placed after daemon:getState so the debug endpoint is available immediately,
|
|
548
577
|
// without waiting for OIDC discovery (~400ms).
|
|
549
578
|
await setupFeatureHandlers({
|
|
550
579
|
authStateStore,
|
|
580
|
+
billingConfigStoreFactory,
|
|
551
581
|
broadcastToProject(projectPath, event, data) {
|
|
552
582
|
broadcastToProjectRoom(projectRegistry, projectRouter, projectPath, event, data);
|
|
553
583
|
},
|
|
@@ -86,10 +86,16 @@ export declare const SynthesisCandidateSchema: z.ZodObject<{
|
|
|
86
86
|
domain: string;
|
|
87
87
|
fact: string;
|
|
88
88
|
}>, "many">;
|
|
89
|
+
keywords: z.ZodArray<z.ZodString, "many">;
|
|
89
90
|
placement: z.ZodString;
|
|
91
|
+
summary: z.ZodString;
|
|
92
|
+
tags: z.ZodArray<z.ZodString, "many">;
|
|
90
93
|
title: z.ZodString;
|
|
91
94
|
}, "strip", z.ZodTypeAny, {
|
|
95
|
+
summary: string;
|
|
96
|
+
tags: string[];
|
|
92
97
|
title: string;
|
|
98
|
+
keywords: string[];
|
|
93
99
|
confidence: number;
|
|
94
100
|
claim: string;
|
|
95
101
|
evidence: {
|
|
@@ -98,7 +104,10 @@ export declare const SynthesisCandidateSchema: z.ZodObject<{
|
|
|
98
104
|
}[];
|
|
99
105
|
placement: string;
|
|
100
106
|
}, {
|
|
107
|
+
summary: string;
|
|
108
|
+
tags: string[];
|
|
101
109
|
title: string;
|
|
110
|
+
keywords: string[];
|
|
102
111
|
confidence: number;
|
|
103
112
|
claim: string;
|
|
104
113
|
evidence: {
|
|
@@ -121,10 +130,16 @@ export declare const SynthesizeResponseSchema: z.ZodObject<{
|
|
|
121
130
|
domain: string;
|
|
122
131
|
fact: string;
|
|
123
132
|
}>, "many">;
|
|
133
|
+
keywords: z.ZodArray<z.ZodString, "many">;
|
|
124
134
|
placement: z.ZodString;
|
|
135
|
+
summary: z.ZodString;
|
|
136
|
+
tags: z.ZodArray<z.ZodString, "many">;
|
|
125
137
|
title: z.ZodString;
|
|
126
138
|
}, "strip", z.ZodTypeAny, {
|
|
139
|
+
summary: string;
|
|
140
|
+
tags: string[];
|
|
127
141
|
title: string;
|
|
142
|
+
keywords: string[];
|
|
128
143
|
confidence: number;
|
|
129
144
|
claim: string;
|
|
130
145
|
evidence: {
|
|
@@ -133,7 +148,10 @@ export declare const SynthesizeResponseSchema: z.ZodObject<{
|
|
|
133
148
|
}[];
|
|
134
149
|
placement: string;
|
|
135
150
|
}, {
|
|
151
|
+
summary: string;
|
|
152
|
+
tags: string[];
|
|
136
153
|
title: string;
|
|
154
|
+
keywords: string[];
|
|
137
155
|
confidence: number;
|
|
138
156
|
claim: string;
|
|
139
157
|
evidence: {
|
|
@@ -144,7 +162,10 @@ export declare const SynthesizeResponseSchema: z.ZodObject<{
|
|
|
144
162
|
}>, "many">;
|
|
145
163
|
}, "strip", z.ZodTypeAny, {
|
|
146
164
|
syntheses: {
|
|
165
|
+
summary: string;
|
|
166
|
+
tags: string[];
|
|
147
167
|
title: string;
|
|
168
|
+
keywords: string[];
|
|
148
169
|
confidence: number;
|
|
149
170
|
claim: string;
|
|
150
171
|
evidence: {
|
|
@@ -155,7 +176,10 @@ export declare const SynthesizeResponseSchema: z.ZodObject<{
|
|
|
155
176
|
}[];
|
|
156
177
|
}, {
|
|
157
178
|
syntheses: {
|
|
179
|
+
summary: string;
|
|
180
|
+
tags: string[];
|
|
158
181
|
title: string;
|
|
182
|
+
keywords: string[];
|
|
159
183
|
confidence: number;
|
|
160
184
|
claim: string;
|
|
161
185
|
evidence: {
|
|
@@ -13,6 +13,10 @@ export const ConsolidateResponseSchema = z.object({
|
|
|
13
13
|
actions: z.array(ConsolidationActionSchema),
|
|
14
14
|
});
|
|
15
15
|
// ── Synthesize ───────────────────────────────────────────────────────────────
|
|
16
|
+
// Bounds are slightly above the prompt's soft targets (200 chars / 3-5 tags /
|
|
17
|
+
// 5-10 keywords) so a model that goes a little over still produces a usable
|
|
18
|
+
// synthesis instead of being rejected outright; the caps still prevent a
|
|
19
|
+
// runaway model from landing oversized text directly in card-mode YAML.
|
|
16
20
|
export const SynthesisCandidateSchema = z.object({
|
|
17
21
|
claim: z.string(),
|
|
18
22
|
confidence: z.number().min(0).max(1),
|
|
@@ -20,7 +24,10 @@ export const SynthesisCandidateSchema = z.object({
|
|
|
20
24
|
domain: z.string(),
|
|
21
25
|
fact: z.string(),
|
|
22
26
|
})),
|
|
27
|
+
keywords: z.array(z.string()).max(15),
|
|
23
28
|
placement: z.string(),
|
|
29
|
+
summary: z.string().max(500),
|
|
30
|
+
tags: z.array(z.string()).max(8),
|
|
24
31
|
title: z.string(),
|
|
25
32
|
});
|
|
26
33
|
export const SynthesizeResponseSchema = z.object({
|
|
@@ -15,6 +15,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
15
15
|
import { access, mkdir, readdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
|
|
16
16
|
import { dirname, join } from 'node:path';
|
|
17
17
|
import { warnSidecarFailure } from '../../../core/domain/knowledge/sidecar-logging.js';
|
|
18
|
+
import { isExcludedFromSync } from '../../context-tree/derived-artifact.js';
|
|
18
19
|
import { ConsolidateResponseSchema } from '../dream-response-schemas.js';
|
|
19
20
|
import { parseDreamResponse } from '../parse-dream-response.js';
|
|
20
21
|
/**
|
|
@@ -226,7 +227,7 @@ function addFrontmatterFields(content, fields) {
|
|
|
226
227
|
if (parsed && typeof parsed === 'object') {
|
|
227
228
|
// Spread preserves existing key order; new fields are appended at end.
|
|
228
229
|
const merged = { ...parsed, ...fields };
|
|
229
|
-
const newYaml = yamlDump(merged, { flowLevel:
|
|
230
|
+
const newYaml = yamlDump(merged, { flowLevel: 1, lineWidth: -1, sortKeys: false }).trimEnd();
|
|
230
231
|
return `---\n${newYaml}\n---\n${body}`;
|
|
231
232
|
}
|
|
232
233
|
}
|
|
@@ -236,7 +237,7 @@ function addFrontmatterFields(content, fields) {
|
|
|
236
237
|
}
|
|
237
238
|
}
|
|
238
239
|
// No valid frontmatter — prepend
|
|
239
|
-
const yaml = yamlDump(fields, { flowLevel:
|
|
240
|
+
const yaml = yamlDump(fields, { flowLevel: 1, lineWidth: -1, sortKeys: false }).trimEnd();
|
|
240
241
|
return `---\n${yaml}\n---\n${content}`;
|
|
241
242
|
}
|
|
242
243
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
@@ -458,8 +459,10 @@ async function executeCrossReference(action, ctx) {
|
|
|
458
459
|
await Promise.all(Object.entries(previousTexts).map(([file, content]) => reviewBackupStore.save(file, content).catch(() => { })));
|
|
459
460
|
}
|
|
460
461
|
// For each file, add the other files to its related frontmatter
|
|
461
|
-
|
|
462
|
-
|
|
462
|
+
// Skip derived-artifact targets so we never write related: onto them.
|
|
463
|
+
const eligibleFiles = action.files.filter((f) => !isExcludedFromSync(f));
|
|
464
|
+
await Promise.all(eligibleFiles.map((file) => {
|
|
465
|
+
const otherFiles = eligibleFiles.filter((f) => f !== file);
|
|
463
466
|
return addRelatedLinks(join(contextTreeDir, file), otherFiles);
|
|
464
467
|
}));
|
|
465
468
|
return {
|
|
@@ -472,6 +475,8 @@ async function executeCrossReference(action, ctx) {
|
|
|
472
475
|
};
|
|
473
476
|
}
|
|
474
477
|
async function addRelatedLinks(filePath, relatedPaths) {
|
|
478
|
+
// Skip paths that won't be pushed — they'd be dangling refs on remote.
|
|
479
|
+
const incoming = relatedPaths.filter((p) => !isExcludedFromSync(p));
|
|
475
480
|
let content;
|
|
476
481
|
try {
|
|
477
482
|
content = await readFile(filePath, 'utf8');
|
|
@@ -491,8 +496,14 @@ async function addRelatedLinks(filePath, relatedPaths) {
|
|
|
491
496
|
try {
|
|
492
497
|
const parsed = yamlLoad(yamlBlock);
|
|
493
498
|
if (parsed && typeof parsed === 'object') {
|
|
494
|
-
const
|
|
495
|
-
parsed.related
|
|
499
|
+
const hadRelated = Array.isArray(parsed.related);
|
|
500
|
+
const existing = (Array.isArray(parsed.related) ? parsed.related : [])
|
|
501
|
+
.filter((p) => !isExcludedFromSync(p));
|
|
502
|
+
const merged = [...new Set([...existing, ...incoming])];
|
|
503
|
+
// Don't introduce a related: [] key into a file that didn't have one.
|
|
504
|
+
if (!hadRelated && merged.length === 0)
|
|
505
|
+
return;
|
|
506
|
+
parsed.related = merged;
|
|
496
507
|
const newYaml = yamlDump(parsed, { flowLevel: 1, lineWidth: -1, sortKeys: false }).trimEnd();
|
|
497
508
|
await atomicWrite(filePath, `---\n${newYaml}\n---\n${body}`);
|
|
498
509
|
return;
|
|
@@ -503,8 +514,10 @@ async function addRelatedLinks(filePath, relatedPaths) {
|
|
|
503
514
|
}
|
|
504
515
|
}
|
|
505
516
|
}
|
|
506
|
-
// No existing frontmatter — add one with related field
|
|
507
|
-
|
|
517
|
+
// No existing frontmatter — add one with related field, unless filter left nothing to add.
|
|
518
|
+
if (incoming.length === 0)
|
|
519
|
+
return;
|
|
520
|
+
const yaml = yamlDump({ related: incoming }, { flowLevel: 1, lineWidth: -1, sortKeys: false }).trimEnd();
|
|
508
521
|
await atomicWrite(filePath, `---\n${yaml}\n---\n${content}`);
|
|
509
522
|
}
|
|
510
523
|
async function determineNeedsReview(actionType, files, opts) {
|
|
@@ -200,15 +200,36 @@ async function writeSynthesisFile(candidate, contextTreeDir, runtimeSignalStore,
|
|
|
200
200
|
// ENOENT — good, proceed
|
|
201
201
|
}
|
|
202
202
|
const sources = candidate.evidence.map((e) => `${e.domain}/_index.md`);
|
|
203
|
+
// Normalize tags to lowercase kebab-case so card chips and BM25 search see
|
|
204
|
+
// a consistent label regardless of whether the model honored the prompt's
|
|
205
|
+
// formatting rule. Empty entries (post-trim) are dropped.
|
|
206
|
+
const normalizedTags = candidate.tags
|
|
207
|
+
.map((t) => t.toLowerCase().trim().replaceAll(/\s+/g, '-'))
|
|
208
|
+
.filter((t) => t.length > 0);
|
|
209
|
+
const now = new Date().toISOString();
|
|
210
|
+
// Field order is enforced by insertion order (yamlDump uses sortKeys:false).
|
|
211
|
+
// Synthesis markers (confidence, sources, synthesized_at, type) come first
|
|
212
|
+
// in the order pre-existing synthesized files use on disk, so re-generating
|
|
213
|
+
// an old file does not produce a mechanical reorder diff. The seven
|
|
214
|
+
// semantic fields below mirror the order in markdown-writer.ts's
|
|
215
|
+
// generateFrontmatter so the on-disk shape matches regular `brv save`
|
|
216
|
+
// files; cogit then exposes them in DtoV3MemoryCardResource for card-mode
|
|
217
|
+
// display in the web UI.
|
|
203
218
|
/* eslint-disable camelcase */
|
|
204
|
-
const frontmatter = {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
219
|
+
const frontmatter = {};
|
|
220
|
+
frontmatter.confidence = candidate.confidence;
|
|
221
|
+
frontmatter.sources = sources;
|
|
222
|
+
frontmatter.synthesized_at = now;
|
|
223
|
+
frontmatter.type = 'synthesis';
|
|
224
|
+
frontmatter.title = candidate.title;
|
|
225
|
+
frontmatter.summary = candidate.summary;
|
|
226
|
+
frontmatter.tags = normalizedTags;
|
|
227
|
+
frontmatter.related = [];
|
|
228
|
+
frontmatter.keywords = candidate.keywords;
|
|
229
|
+
frontmatter.createdAt = now;
|
|
230
|
+
frontmatter.updatedAt = now;
|
|
210
231
|
/* eslint-enable camelcase */
|
|
211
|
-
const yaml = yamlDump(frontmatter, { lineWidth: -1, sortKeys: false }).trimEnd();
|
|
232
|
+
const yaml = yamlDump(frontmatter, { flowLevel: 1, lineWidth: -1, sortKeys: false }).trimEnd();
|
|
212
233
|
const body = [
|
|
213
234
|
`# ${candidate.title}`,
|
|
214
235
|
'',
|
|
@@ -280,11 +301,17 @@ function buildPrompt(domains, existingSyntheses) {
|
|
|
280
301
|
'- Do NOT report trivial or obvious connections (e.g., "both domains use TypeScript").',
|
|
281
302
|
'- Each synthesis must reference at least 2 domains with specific evidence.',
|
|
282
303
|
'- For "placement", choose the domain where this insight is MOST actionable.',
|
|
304
|
+
'- "summary" is one sentence (≤ 200 chars) describing the insight; this is what the UI shows as a card preview.',
|
|
305
|
+
'- "tags" are 3-5 short topical labels drawn from the source domains (e.g., "auth", "caching"). Lowercase, kebab-case.',
|
|
306
|
+
'- "keywords" are 5-10 single words a developer would search for to surface this synthesis.',
|
|
283
307
|
'- If nothing meaningful is found, return an empty array. That is fine — but missing a clear cross-domain pattern is a failure.',
|
|
284
308
|
'',
|
|
309
|
+
// Keep the JSON shape below in sync with SynthesisCandidateSchema in
|
|
310
|
+
// dream-response-schemas.ts; the schema rejects responses that omit any
|
|
311
|
+
// listed field, so adding a field there requires updating this example.
|
|
285
312
|
'Respond with JSON:',
|
|
286
313
|
'```',
|
|
287
|
-
'{ "syntheses": [{ "title": "...", "claim": "...", "evidence": [{"domain": "...", "fact": "..."}], "confidence": 0.0-1.0, "placement": "..." }] }',
|
|
314
|
+
'{ "syntheses": [{ "title": "...", "summary": "...", "claim": "...", "evidence": [{"domain": "...", "fact": "..."}], "tags": ["..."], "keywords": ["..."], "confidence": 0.0-1.0, "placement": "..." }] }',
|
|
288
315
|
'```',
|
|
289
316
|
].join('\n');
|
|
290
317
|
}
|
|
@@ -361,17 +361,23 @@ export class OpenAICompatibleModelFetcher {
|
|
|
361
361
|
const modelList = Array.isArray(responseData)
|
|
362
362
|
? responseData
|
|
363
363
|
: (responseData.data ?? responseData.models ?? []);
|
|
364
|
-
const
|
|
364
|
+
const uniqueModels = new Map();
|
|
365
|
+
for (const model of modelList) {
|
|
365
366
|
const id = String(model.id ?? model.name ?? '');
|
|
366
|
-
|
|
367
|
+
if (!id)
|
|
368
|
+
continue;
|
|
369
|
+
if (uniqueModels.has(id))
|
|
370
|
+
continue;
|
|
371
|
+
uniqueModels.set(id, {
|
|
367
372
|
contextLength: typeof model.context_length === 'number' ? model.context_length : 128_000,
|
|
368
373
|
id,
|
|
369
374
|
isFree: false,
|
|
370
375
|
name: id,
|
|
371
376
|
pricing: { inputPerM: 0, outputPerM: 0 },
|
|
372
377
|
provider: this.providerName,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
const models = [...uniqueModels.values()];
|
|
375
381
|
// Sort by ID
|
|
376
382
|
models.sort((a, b) => a.id.localeCompare(b.id));
|
|
377
383
|
this.cache = { models, timestamp: Date.now() };
|
|
@@ -9,10 +9,12 @@ import type { IProviderKeychainStore } from '../../core/interfaces/i-provider-ke
|
|
|
9
9
|
import type { IProviderOAuthTokenStore } from '../../core/interfaces/i-provider-oauth-token-store.js';
|
|
10
10
|
import type { IProjectRegistry } from '../../core/interfaces/project/i-project-registry.js';
|
|
11
11
|
import type { IAuthStateStore } from '../../core/interfaces/state/i-auth-state-store.js';
|
|
12
|
+
import type { IBillingConfigStore } from '../../core/interfaces/storage/i-billing-config-store.js';
|
|
12
13
|
import type { ITransportServer } from '../../core/interfaces/transport/i-transport-server.js';
|
|
13
14
|
import type { ProjectBroadcaster, ProjectPathResolver } from '../transport/handlers/handler-types.js';
|
|
14
15
|
export interface FeatureHandlersOptions {
|
|
15
16
|
authStateStore: IAuthStateStore;
|
|
17
|
+
billingConfigStoreFactory: (projectPath: string) => IBillingConfigStore;
|
|
16
18
|
broadcastToProject: ProjectBroadcaster;
|
|
17
19
|
getActiveProjectPaths: () => string[];
|
|
18
20
|
log: (msg: string) => void;
|
|
@@ -28,4 +30,4 @@ export interface FeatureHandlersOptions {
|
|
|
28
30
|
* Setup all feature handlers on the transport server.
|
|
29
31
|
* These handlers implement the TUI ↔ Server event contract (auth:*, config:*, status:*, etc.).
|
|
30
32
|
*/
|
|
31
|
-
export declare function setupFeatureHandlers({ authStateStore, broadcastToProject, getActiveProjectPaths, log, projectRegistry, providerConfigStore, providerKeychainStore, providerOAuthTokenStore, resolveProjectPath, transport, webuiPort, }: FeatureHandlersOptions): Promise<void>;
|
|
33
|
+
export declare function setupFeatureHandlers({ authStateStore, billingConfigStoreFactory, broadcastToProject, getActiveProjectPaths, log, projectRegistry, providerConfigStore, providerKeychainStore, providerOAuthTokenStore, resolveProjectPath, transport, webuiPort, }: FeatureHandlersOptions): Promise<void>;
|
|
@@ -10,9 +10,12 @@ import { ReviewEvents } from '../../../shared/transport/events/review-events.js'
|
|
|
10
10
|
import { getAuthConfig } from '../../config/auth.config.js';
|
|
11
11
|
import { getCurrentConfig } from '../../config/environment.js';
|
|
12
12
|
import { API_V1_PATH, BRV_DIR } from '../../constants.js';
|
|
13
|
+
import { TransportStateEventNames } from '../../core/domain/transport/schemas.js';
|
|
13
14
|
import { getProjectDataDir } from '../../utils/path-utils.js';
|
|
14
15
|
import { OAuthService } from '../auth/oauth-service.js';
|
|
15
16
|
import { OidcDiscoveryService } from '../auth/oidc-discovery-service.js';
|
|
17
|
+
import { HttpBillingService } from '../billing/http-billing-service.js';
|
|
18
|
+
import { createPaidOrganizationsHandler } from '../billing/paid-organizations-endpoint.js';
|
|
16
19
|
import { SystemBrowserLauncher } from '../browser/system-browser-launcher.js';
|
|
17
20
|
import { HttpCogitPullService } from '../cogit/http-cogit-pull-service.js';
|
|
18
21
|
import { HttpCogitPushService } from '../cogit/http-cogit-push-service.js';
|
|
@@ -37,23 +40,25 @@ import { FileReviewBackupStore } from '../storage/file-review-backup-store.js';
|
|
|
37
40
|
import { createTokenStore } from '../storage/token-store.js';
|
|
38
41
|
import { HttpTeamService } from '../team/http-team-service.js';
|
|
39
42
|
import { FsTemplateLoader } from '../template/fs-template-loader.js';
|
|
40
|
-
import { AuthHandler, ConfigHandler, ConnectorsHandler, ContextTreeHandler, HubHandler, InitHandler, LocationsHandler, ModelHandler, ProviderHandler, PullHandler, PushHandler, ResetHandler, ReviewHandler, SourceHandler, SpaceHandler, StatusHandler, VcHandler, WorktreeHandler, } from '../transport/handlers/index.js';
|
|
43
|
+
import { AuthHandler, BillingHandler, ConfigHandler, ConnectorsHandler, ContextTreeHandler, HubHandler, InitHandler, LocationsHandler, ModelHandler, ProviderHandler, PullHandler, PushHandler, ResetHandler, ReviewHandler, SourceHandler, SpaceHandler, StatusHandler, TeamHandler, VcHandler, WorktreeHandler, } from '../transport/handlers/index.js';
|
|
41
44
|
import { HttpUserService } from '../user/http-user-service.js';
|
|
42
45
|
import { FileVcGitConfigStore } from '../vc/file-vc-git-config-store.js';
|
|
43
46
|
/**
|
|
44
47
|
* Setup all feature handlers on the transport server.
|
|
45
48
|
* These handlers implement the TUI ↔ Server event contract (auth:*, config:*, status:*, etc.).
|
|
46
49
|
*/
|
|
47
|
-
export async function setupFeatureHandlers({ authStateStore, broadcastToProject, getActiveProjectPaths, log, projectRegistry, providerConfigStore, providerKeychainStore, providerOAuthTokenStore, resolveProjectPath, transport, webuiPort, }) {
|
|
50
|
+
export async function setupFeatureHandlers({ authStateStore, billingConfigStoreFactory, broadcastToProject, getActiveProjectPaths, log, projectRegistry, providerConfigStore, providerKeychainStore, providerOAuthTokenStore, resolveProjectPath, transport, webuiPort, }) {
|
|
48
51
|
const envConfig = getCurrentConfig();
|
|
49
52
|
const tokenStore = createTokenStore();
|
|
50
53
|
const projectConfigStore = new ProjectConfigStore();
|
|
51
54
|
// API version paths appended at point of use.
|
|
52
55
|
// Note: IAM and Cogit currently share this version path, but may version independently in the future.
|
|
53
56
|
const iamApiV1 = `${envConfig.iamBaseUrl}${API_V1_PATH}`;
|
|
57
|
+
const billingApiV1 = `${envConfig.billingBaseUrl}${API_V1_PATH}`;
|
|
54
58
|
const userService = new HttpUserService({ apiBaseUrl: iamApiV1 });
|
|
55
59
|
const teamService = new HttpTeamService({ apiBaseUrl: iamApiV1 });
|
|
56
60
|
const spaceService = new HttpSpaceService({ apiBaseUrl: iamApiV1 });
|
|
61
|
+
const billingService = new HttpBillingService({ apiBaseUrl: billingApiV1 });
|
|
57
62
|
// Auth handler requires async OIDC discovery
|
|
58
63
|
const discoveryService = new OidcDiscoveryService();
|
|
59
64
|
const authConfig = await getAuthConfig(discoveryService);
|
|
@@ -65,6 +70,7 @@ export async function setupFeatureHandlers({ authStateStore, broadcastToProject,
|
|
|
65
70
|
browserLauncher: new SystemBrowserLauncher(),
|
|
66
71
|
callbackHandler: new CallbackHandler(),
|
|
67
72
|
projectConfigStore,
|
|
73
|
+
providerConfigStore,
|
|
68
74
|
resolveProjectPath,
|
|
69
75
|
tokenStore,
|
|
70
76
|
transport,
|
|
@@ -78,6 +84,20 @@ export async function setupFeatureHandlers({ authStateStore, broadcastToProject,
|
|
|
78
84
|
providerOAuthTokenStore,
|
|
79
85
|
transport,
|
|
80
86
|
}).setup();
|
|
87
|
+
new BillingHandler({
|
|
88
|
+
authStateStore,
|
|
89
|
+
billingConfigStoreFactory,
|
|
90
|
+
billingService,
|
|
91
|
+
providerConfigStore,
|
|
92
|
+
resolveProjectPath,
|
|
93
|
+
transport,
|
|
94
|
+
}).setup();
|
|
95
|
+
transport.onRequest(TransportStateEventNames.GET_PAID_ORGANIZATIONS, createPaidOrganizationsHandler({ authStateStore, billingService }));
|
|
96
|
+
new TeamHandler({
|
|
97
|
+
authStateStore,
|
|
98
|
+
teamService,
|
|
99
|
+
transport,
|
|
100
|
+
}).setup();
|
|
81
101
|
new ModelHandler({
|
|
82
102
|
providerConfigStore,
|
|
83
103
|
providerKeychainStore,
|
|
@@ -100,10 +120,14 @@ export async function setupFeatureHandlers({ authStateStore, broadcastToProject,
|
|
|
100
120
|
// Project-scoped handlers (receive resolveProjectPath for client → project resolution)
|
|
101
121
|
const gitService = new IsomorphicGitService(authStateStore);
|
|
102
122
|
new StatusHandler({
|
|
123
|
+
authStateStore,
|
|
124
|
+
billingConfigStoreFactory,
|
|
125
|
+
billingService,
|
|
103
126
|
contextTreeService,
|
|
104
127
|
contextTreeSnapshotService,
|
|
105
128
|
curateLogStoreFactory: (projectPath) => new FileCurateLogStore({ baseDir: getProjectDataDir(projectPath) }),
|
|
106
129
|
projectConfigStore,
|
|
130
|
+
providerConfigStore,
|
|
107
131
|
resolveProjectPath,
|
|
108
132
|
tokenStore,
|
|
109
133
|
transport,
|