byterover-cli 1.0.2 → 1.0.4

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 (128) hide show
  1. package/README.md +62 -10
  2. package/dist/commands/curate.js +2 -2
  3. package/dist/commands/main.js +2 -2
  4. package/dist/commands/query.js +2 -2
  5. package/dist/commands/status.js +2 -2
  6. package/dist/config/context-tree-domains.d.ts +14 -2
  7. package/dist/config/context-tree-domains.js +22 -27
  8. package/dist/constants.d.ts +1 -0
  9. package/dist/constants.js +3 -0
  10. package/dist/core/domain/cipher/file-system/types.d.ts +2 -0
  11. package/dist/core/domain/entities/auth-token.js +6 -3
  12. package/dist/core/domain/entities/event.d.ts +1 -1
  13. package/dist/core/domain/entities/event.js +2 -1
  14. package/dist/core/domain/knowledge/relation-parser.d.ts +16 -1
  15. package/dist/core/domain/knowledge/relation-parser.js +19 -2
  16. package/dist/core/domain/transport/schemas.d.ts +17 -1
  17. package/dist/core/domain/transport/schemas.js +9 -1
  18. package/dist/core/interfaces/cipher/i-blob-storage.d.ts +6 -0
  19. package/dist/core/interfaces/cipher/index.d.ts +0 -1
  20. package/dist/core/interfaces/executor/i-curate-executor.d.ts +2 -0
  21. package/dist/infra/cipher/agent/cipher-agent.js +4 -0
  22. package/dist/infra/cipher/file-system/file-system-service.d.ts +4 -0
  23. package/dist/infra/cipher/file-system/file-system-service.js +5 -0
  24. package/dist/infra/cipher/system-prompt/contributors/context-tree-structure-contributor.js +4 -2
  25. package/dist/infra/cipher/tools/implementations/create-knowledge-topic-tool.js +24 -17
  26. package/dist/infra/cipher/tools/implementations/curate-tool.js +28 -33
  27. package/dist/infra/cipher/tools/implementations/read-file-tool.js +3 -12
  28. package/dist/infra/cipher/tools/implementations/spec-analyze-tool.js +18 -15
  29. package/dist/infra/cipher/tools/implementations/task-tool.js +53 -7
  30. package/dist/infra/context-tree/file-context-tree-service.js +4 -15
  31. package/dist/infra/core/executors/curate-executor.d.ts +2 -7
  32. package/dist/infra/core/executors/curate-executor.js +18 -53
  33. package/dist/infra/core/executors/query-executor.d.ts +1 -7
  34. package/dist/infra/core/executors/query-executor.js +10 -35
  35. package/dist/infra/core/task-processor.d.ts +2 -0
  36. package/dist/infra/core/task-processor.js +1 -0
  37. package/dist/infra/http/authenticated-http-client.js +5 -0
  38. package/dist/infra/process/agent-worker.js +113 -6
  39. package/dist/infra/process/constants.d.ts +1 -0
  40. package/dist/infra/process/constants.js +1 -0
  41. package/dist/infra/process/task-queue-manager.js +2 -1
  42. package/dist/infra/process/transport-handlers.js +4 -0
  43. package/dist/infra/process/transport-worker.js +89 -1
  44. package/dist/infra/repl/commands/curate-command.js +2 -2
  45. package/dist/infra/repl/commands/gen-rules-command.js +2 -2
  46. package/dist/infra/repl/commands/init-command.js +2 -2
  47. package/dist/infra/repl/commands/login-command.js +2 -2
  48. package/dist/infra/repl/commands/logout-command.js +2 -2
  49. package/dist/infra/repl/commands/pull-command.js +2 -2
  50. package/dist/infra/repl/commands/push-command.js +2 -2
  51. package/dist/infra/repl/commands/query-command.js +2 -2
  52. package/dist/infra/repl/commands/space/list-command.js +2 -2
  53. package/dist/infra/repl/commands/space/switch-command.js +2 -2
  54. package/dist/infra/repl/commands/status-command.js +2 -2
  55. package/dist/infra/repl/repl-startup.js +0 -2
  56. package/dist/infra/storage/file-token-store.d.ts +31 -0
  57. package/dist/infra/storage/file-token-store.js +98 -0
  58. package/dist/infra/storage/keychain-token-store.d.ts +4 -1
  59. package/dist/infra/storage/keychain-token-store.js +6 -4
  60. package/dist/infra/storage/token-store.d.ts +10 -0
  61. package/dist/infra/storage/token-store.js +14 -0
  62. package/dist/infra/usecase/curate-use-case.js +1 -1
  63. package/dist/infra/usecase/init-use-case.js +2 -4
  64. package/dist/infra/user/http-user-service.js +6 -11
  65. package/dist/resources/prompts/curate.yml +14 -5
  66. package/dist/resources/prompts/plan.yml +6 -0
  67. package/dist/tui/app.js +1 -1
  68. package/dist/tui/components/execution/log-item.js +2 -5
  69. package/dist/tui/components/header.d.ts +1 -1
  70. package/dist/tui/components/header.js +25 -4
  71. package/dist/tui/components/index.d.ts +5 -1
  72. package/dist/tui/components/index.js +3 -1
  73. package/dist/tui/components/init.d.ts +33 -0
  74. package/dist/tui/components/init.js +253 -0
  75. package/dist/tui/components/onboarding/index.d.ts +1 -0
  76. package/dist/tui/components/onboarding/index.js +1 -0
  77. package/dist/tui/components/onboarding/onboarding-flow.d.ts +2 -0
  78. package/dist/tui/components/onboarding/onboarding-flow.js +8 -229
  79. package/dist/tui/components/onboarding/onboarding-step.js +1 -1
  80. package/dist/tui/components/onboarding/welcome-box.d.ts +14 -0
  81. package/dist/tui/components/onboarding/welcome-box.js +23 -0
  82. package/dist/tui/components/status-badge.d.ts +22 -0
  83. package/dist/tui/components/status-badge.js +32 -0
  84. package/dist/tui/contexts/auth-context.js +2 -1
  85. package/dist/tui/contexts/index.d.ts +1 -0
  86. package/dist/tui/contexts/index.js +1 -0
  87. package/dist/tui/contexts/onboarding-context.d.ts +14 -0
  88. package/dist/tui/contexts/onboarding-context.js +17 -22
  89. package/dist/tui/contexts/status-context.d.ts +33 -0
  90. package/dist/tui/contexts/status-context.js +159 -0
  91. package/dist/tui/hooks/use-auth-polling.d.ts +4 -1
  92. package/dist/tui/hooks/use-auth-polling.js +21 -7
  93. package/dist/tui/hooks/use-tab-navigation.js +0 -2
  94. package/dist/tui/providers/app-providers.js +2 -2
  95. package/dist/tui/types/index.d.ts +2 -0
  96. package/dist/tui/types/index.js +2 -0
  97. package/dist/tui/types/status.d.ts +46 -0
  98. package/dist/tui/types/status.js +13 -0
  99. package/dist/tui/utils/index.d.ts +6 -0
  100. package/dist/tui/utils/index.js +6 -0
  101. package/dist/tui/utils/time.d.ts +10 -0
  102. package/dist/tui/utils/time.js +15 -0
  103. package/dist/tui/views/command-view.js +0 -2
  104. package/dist/tui/views/index.d.ts +1 -0
  105. package/dist/tui/views/index.js +1 -0
  106. package/dist/tui/views/init-view.d.ts +15 -0
  107. package/dist/tui/views/init-view.js +29 -0
  108. package/dist/tui/views/logs-view.js +22 -8
  109. package/dist/utils/environment-detector.d.ts +5 -0
  110. package/dist/utils/environment-detector.js +31 -0
  111. package/dist/utils/global-data-path.d.ts +11 -0
  112. package/dist/utils/global-data-path.js +32 -0
  113. package/oclif.manifest.json +1 -1
  114. package/package.json +1 -1
  115. package/dist/core/interfaces/cipher/i-agent-storage.d.ts +0 -152
  116. package/dist/core/interfaces/cipher/i-agent-storage.js +0 -1
  117. package/dist/infra/cipher/consumer/consumer-lock.d.ts +0 -20
  118. package/dist/infra/cipher/consumer/consumer-lock.js +0 -41
  119. package/dist/infra/cipher/consumer/consumer-service.d.ts +0 -99
  120. package/dist/infra/cipher/consumer/consumer-service.js +0 -166
  121. package/dist/infra/cipher/consumer/execution-consumer.d.ts +0 -126
  122. package/dist/infra/cipher/consumer/execution-consumer.js +0 -561
  123. package/dist/infra/cipher/consumer/index.d.ts +0 -33
  124. package/dist/infra/cipher/consumer/index.js +0 -34
  125. package/dist/infra/cipher/consumer/queue-polling-service.d.ts +0 -120
  126. package/dist/infra/cipher/consumer/queue-polling-service.js +0 -249
  127. package/dist/infra/cipher/storage/agent-storage.d.ts +0 -246
  128. package/dist/infra/cipher/storage/agent-storage.js +0 -956
@@ -1,23 +1,31 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  /**
3
3
  * Logs View
4
4
  *
5
5
  * Activity log display using ScrollableList with dynamic height calculation
6
6
  */
7
- import { Box, Text } from 'ink';
8
- import { useCallback } from 'react';
9
- import { LogItem, OnboardingFlow, ScrollableList } from '../components/index.js';
7
+ import { Box } from 'ink';
8
+ import { useCallback, useState } from 'react';
9
+ import { LogItem, OnboardingFlow, ScrollableList, WelcomeBox } from '../components/index.js';
10
+ import { useAuth } from '../contexts/index.js';
10
11
  import { useActivityLogs, useMode, useTheme, useUIHeights } from '../hooks/index.js';
11
12
  import { useOnboarding } from '../hooks/use-onboarding.js';
12
13
  import { calculateActualLogHeight, calculateLogContentLimit } from '../utils/log.js';
14
+ import { InitView } from './init-view.js';
13
15
  export const LogsView = ({ availableHeight }) => {
14
16
  const { theme: { colors }, } = useTheme();
15
17
  const { mode } = useMode();
16
18
  const { logs } = useActivityLogs();
17
- const { shouldShowOnboarding } = useOnboarding();
19
+ const { isLoadingDismissed, shouldShowOnboarding } = useOnboarding();
18
20
  const { messageItem } = useUIHeights();
21
+ const { brvConfig } = useAuth();
22
+ // Track if user has completed init flow
23
+ const [initFlowCompleted, setInitFlowCompleted] = useState(Boolean(brvConfig));
19
24
  // Calculate scrollable height for dynamic per-log calculations
20
25
  const scrollableHeight = Math.max(1, availableHeight);
26
+ const handleInitEnd = () => {
27
+ setInitFlowCompleted(true);
28
+ };
21
29
  const renderLogItem = useCallback((log) => {
22
30
  // Calculate dynamic content limit for this specific log
23
31
  const parts = calculateLogContentLimit(log, scrollableHeight, messageItem);
@@ -40,9 +48,15 @@ export const LogsView = ({ availableHeight }) => {
40
48
  maxContentLines: maxContentLine,
41
49
  });
42
50
  }, [messageItem, scrollableHeight]);
43
- // Show onboarding when project is not initialized
51
+ if (isLoadingDismissed) {
52
+ return null;
53
+ }
44
54
  if (shouldShowOnboarding) {
45
- return _jsx(OnboardingFlow, { availableHeight: availableHeight });
55
+ return _jsx(OnboardingFlow, { availableHeight: availableHeight, onInitComplete: handleInitEnd });
56
+ }
57
+ // Show init view if config doesn't exist and user hasn't completed init flow
58
+ if (!initFlowCompleted) {
59
+ return _jsx(InitView, { availableHeight: availableHeight, onInitComplete: handleInitEnd });
46
60
  }
47
- return (_jsx(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", borderTop: false, flexDirection: "column", height: "100%", width: "100%", children: logs.length > 0 ? (_jsx(Box, { flexDirection: "column", height: "100%", paddingX: 2, children: _jsx(ScrollableList, { autoScrollToBottom: true, availableHeight: scrollableHeight, estimateItemHeight: heightEstimator, isActive: mode === 'activity', items: logs, keyExtractor: keyExtractor, renderItem: renderLogItem }) })) : (_jsxs(_Fragment, { children: [_jsx(Text, { color: colors.primary, children: "Welcome to ByteRover!" }), _jsx(Text, { color: colors.text, children: "Start by telling your AI Agent what to save or retrieve." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.text, dimColor: true, children: "Press [Tab] to switch to commands view" }) })] })) }));
61
+ return (_jsx(Box, { borderColor: colors.border, borderLeft: false, borderRight: false, borderStyle: "single", borderTop: false, flexDirection: "column", height: "100%", width: "100%", children: logs.length > 0 ? (_jsx(Box, { flexDirection: "column", height: "100%", paddingX: 2, children: _jsx(ScrollableList, { autoScrollToBottom: true, availableHeight: scrollableHeight, estimateItemHeight: heightEstimator, isActive: mode === 'activity', items: logs, keyExtractor: keyExtractor, renderItem: renderLogItem }) })) : (_jsx(WelcomeBox, { isCopyActive: mode === 'activity' && logs.length === 0 })) }));
48
62
  };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Check if running in WSL environment (cached).
3
+ * Detects both WSL1 and WSL2. Result is cached after first call for performance.
4
+ */
5
+ export declare function isWsl(): boolean;
@@ -0,0 +1,31 @@
1
+ import { readFileSync } from 'node:fs';
2
+ let wslCached;
3
+ /**
4
+ * Detect if running in WSL (Windows Subsystem for Linux) environment.
5
+ * Checks WSL-specific environment variables first, then falls back to /proc/version.
6
+ */
7
+ function detectWsl() {
8
+ if (process.platform !== 'linux') {
9
+ return false;
10
+ }
11
+ if (process.env.WSL_DISTRO_NAME !== undefined || process.env.WSLENV !== undefined) {
12
+ return true;
13
+ }
14
+ try {
15
+ const version = readFileSync('/proc/version', 'utf8');
16
+ return /microsoft|wsl/i.test(version);
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ /**
23
+ * Check if running in WSL environment (cached).
24
+ * Detects both WSL1 and WSL2. Result is cached after first call for performance.
25
+ */
26
+ export function isWsl() {
27
+ if (wslCached === undefined) {
28
+ wslCached = detectWsl();
29
+ }
30
+ return wslCached;
31
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Returns the global data directory path following XDG spec:
3
+ * - Linux: $XDG_DATA_HOME/brv (defaults to ~/.local/share/brv)
4
+ * - macOS: ~/.local/share/brv
5
+ * - Windows: %LOCALAPPDATA%/brv
6
+ *
7
+ * Use this for user data and secrets (not config files).
8
+ *
9
+ * @returns Absolute path to the global data directory
10
+ */
11
+ export declare const getGlobalDataDir: () => string;
@@ -0,0 +1,32 @@
1
+ import { homedir, platform } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { GLOBAL_DATA_DIR } from '../constants.js';
4
+ /**
5
+ * Returns the global data directory path following XDG spec:
6
+ * - Linux: $XDG_DATA_HOME/brv (defaults to ~/.local/share/brv)
7
+ * - macOS: ~/.local/share/brv
8
+ * - Windows: %LOCALAPPDATA%/brv
9
+ *
10
+ * Use this for user data and secrets (not config files).
11
+ *
12
+ * @returns Absolute path to the global data directory
13
+ */
14
+ export const getGlobalDataDir = () => {
15
+ const currentPlatform = platform();
16
+ if (currentPlatform === 'win32') {
17
+ const localAppData = process.env.LOCALAPPDATA;
18
+ if (localAppData !== undefined) {
19
+ return join(localAppData, GLOBAL_DATA_DIR);
20
+ }
21
+ return join(homedir(), 'AppData', 'Local', GLOBAL_DATA_DIR);
22
+ }
23
+ // Linux: respect XDG_DATA_HOME if set
24
+ if (currentPlatform === 'linux') {
25
+ const xdgDataHome = process.env.XDG_DATA_HOME;
26
+ if (xdgDataHome !== undefined) {
27
+ return join(xdgDataHome, GLOBAL_DATA_DIR);
28
+ }
29
+ }
30
+ // Linux (default) and macOS: use ~/.local/share/brv
31
+ return join(homedir(), '.local', 'share', GLOBAL_DATA_DIR);
32
+ };
@@ -198,5 +198,5 @@
198
198
  ]
199
199
  }
200
200
  },
201
- "version": "1.0.2"
201
+ "version": "1.0.4"
202
202
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "byterover-cli",
3
3
  "description": "ByteRover's CLI",
4
- "version": "1.0.2",
4
+ "version": "1.0.4",
5
5
  "author": "ByteRover",
6
6
  "bin": {
7
7
  "brv": "./bin/run.js"
@@ -1,152 +0,0 @@
1
- import type { Execution, ExecutionStatus, ExecutionType, ToolCall, ToolCallInfo, ToolCallStatus, ToolCallUpdateOptions } from '../../domain/cipher/queue/types.js';
2
- /**
3
- * Interface for agent execution storage.
4
- *
5
- * Manages the execution queue, tool call tracking, and consumer locks.
6
- * Implementations can use different storage backends (SQLite, in-memory, etc.)
7
- */
8
- export interface IAgentStorage {
9
- /**
10
- * Acquire consumer lock (register this consumer).
11
- * Only ONE consumer can run at a time.
12
- * @param consumerId - Unique ID for this consumer
13
- * @returns true if lock acquired, false if another consumer is already running
14
- */
15
- acquireConsumerLock(consumerId: string): boolean;
16
- /**
17
- * Add a tool call record.
18
- * @returns tool call id
19
- */
20
- addToolCall(executionId: string, info: ToolCallInfo): string;
21
- /**
22
- * Cleanup old executions, keep only maxKeep most recent completed/failed.
23
- * @returns number of deleted executions
24
- */
25
- cleanupOldExecutions(maxKeep?: number): number;
26
- /**
27
- * Cleanup orphaned executions (status='running') from previous session crash.
28
- * @returns number of orphaned executions
29
- */
30
- cleanupOrphanedExecutions(): number;
31
- /**
32
- * Cleanup stale consumers and orphan their executions.
33
- * @param timeoutMs - heartbeat timeout (default 30 seconds)
34
- * @returns number of orphaned executions
35
- */
36
- cleanupStaleConsumers(timeoutMs?: number): number;
37
- /**
38
- * Close storage connection.
39
- */
40
- close(): void;
41
- /**
42
- * Create a new execution.
43
- * @param type - 'curate' or 'query'
44
- * @param input - content (curate) or query string (query)
45
- * @returns execution id
46
- */
47
- createExecution(type: ExecutionType, input: string): string;
48
- /**
49
- * Dequeue multiple executions at once (atomic batch SELECT + UPDATE).
50
- * @param limit - max number of executions to dequeue
51
- * @param consumerId - ID of the consumer claiming these executions
52
- * @returns array of executions (may be empty if queue is empty)
53
- */
54
- dequeueBatch(limit: number, consumerId?: string): Execution[];
55
- /**
56
- * Dequeue next queued execution (atomic SELECT + UPDATE).
57
- * @param consumerId - ID of the consumer claiming this execution
58
- * @returns execution or null if queue is empty
59
- */
60
- dequeueExecution(consumerId?: string): Execution | null;
61
- /**
62
- * Get execution by id.
63
- */
64
- getExecution(id: string): Execution | null;
65
- /**
66
- * Get executions updated since timestamp (for incremental polling).
67
- */
68
- getExecutionsSince(timestamp: number): Execution[];
69
- /**
70
- * Get execution with all its tool calls (for UI display).
71
- */
72
- getExecutionWithToolCalls(id: string): null | {
73
- execution: Execution;
74
- toolCalls: ToolCall[];
75
- };
76
- /**
77
- * Get all queued executions.
78
- */
79
- getQueuedExecutions(): Execution[];
80
- /**
81
- * Get recent executions (for UI display).
82
- */
83
- getRecentExecutions(limit?: number): Execution[];
84
- /**
85
- * Get all running executions.
86
- */
87
- getRunningExecutions(): Execution[];
88
- /**
89
- * Get all executions belonging to a specific consumer session.
90
- * Returns executions ordered by created_at ASC (oldest first, newest last).
91
- * @param consumerId - The consumer ID to filter by
92
- */
93
- getSessionExecutions(consumerId: string): Execution[];
94
- /**
95
- * Get queue statistics (queries DB directly for accurate counts).
96
- */
97
- getStats(): {
98
- completed: number;
99
- failed: number;
100
- queued: number;
101
- running: number;
102
- total: number;
103
- };
104
- /**
105
- * Get all tool calls for an execution.
106
- */
107
- getToolCalls(executionId: string): ToolCall[];
108
- /**
109
- * Check if any consumer is currently active (has recent heartbeat).
110
- * @param timeoutMs - heartbeat timeout (default 30 seconds)
111
- */
112
- hasActiveConsumer(timeoutMs?: number): boolean;
113
- /**
114
- * Check if a specific consumer lock exists in the database.
115
- * Used by Consumer to verify its lock is still valid after DB reconnection.
116
- */
117
- hasConsumerLock(consumerId: string): boolean;
118
- /**
119
- * Initialize storage.
120
- */
121
- initialize(options?: {
122
- cleanupOrphans?: boolean;
123
- }): Promise<void>;
124
- /** Whether the storage has been initialized */
125
- readonly initialized: boolean;
126
- /**
127
- * Check if the DB file has been replaced (different inode).
128
- * Returns true if DB needs reconnection.
129
- */
130
- isDbFileChanged(): boolean;
131
- /**
132
- * Reconnect to the database (close and reinitialize).
133
- * Use when DB file has been replaced by another process (e.g., brv init).
134
- */
135
- reconnect(): Promise<void>;
136
- /**
137
- * Release consumer lock (unregister this consumer).
138
- */
139
- releaseConsumerLock(consumerId: string): void;
140
- /**
141
- * Update consumer heartbeat.
142
- */
143
- updateConsumerHeartbeat(consumerId: string): void;
144
- /**
145
- * Update execution status.
146
- */
147
- updateExecutionStatus(id: string, status: ExecutionStatus, result?: string, error?: string): void;
148
- /**
149
- * Update tool call status and result.
150
- */
151
- updateToolCall(id: string, status: ToolCallStatus, options?: ToolCallUpdateOptions): void;
152
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,20 +0,0 @@
1
- /**
2
- * DB-based consumer lock utilities
3
- *
4
- * The actual locking is now handled by AgentStorage methods:
5
- * - acquireConsumerLock(consumerId)
6
- * - releaseConsumerLock(consumerId)
7
- * - hasActiveConsumer(timeoutMs)
8
- *
9
- * These utility functions provide a simple interface for checking consumer status.
10
- */
11
- /**
12
- * Check if a consumer is currently running (has active heartbeat in DB)
13
- * Auto-detects .brv/blobs path from cwd
14
- */
15
- export declare function isConsumerRunning(): Promise<boolean>;
16
- /**
17
- * Check if a consumer is currently running (sync version)
18
- * Assumes storage is already initialized
19
- */
20
- export declare function isConsumerRunningSync(): boolean;
@@ -1,41 +0,0 @@
1
- // TODO(v0.5.0): Remove this file. Consumer locking is no longer needed (one CoreProcess per TUI).
2
- import { getAgentStorage, getAgentStorageSync } from '../storage/agent-storage.js';
3
- // Consumer is considered stale after 30 seconds without heartbeat
4
- const STALE_TIMEOUT_MS = 30_000;
5
- /**
6
- * DB-based consumer lock utilities
7
- *
8
- * The actual locking is now handled by AgentStorage methods:
9
- * - acquireConsumerLock(consumerId)
10
- * - releaseConsumerLock(consumerId)
11
- * - hasActiveConsumer(timeoutMs)
12
- *
13
- * These utility functions provide a simple interface for checking consumer status.
14
- */
15
- /**
16
- * Check if a consumer is currently running (has active heartbeat in DB)
17
- * Auto-detects .brv/blobs path from cwd
18
- */
19
- export async function isConsumerRunning() {
20
- try {
21
- const storage = await getAgentStorage();
22
- return storage.hasActiveConsumer(STALE_TIMEOUT_MS);
23
- }
24
- catch {
25
- // If we can't check, assume not running
26
- return false;
27
- }
28
- }
29
- /**
30
- * Check if a consumer is currently running (sync version)
31
- * Assumes storage is already initialized
32
- */
33
- export function isConsumerRunningSync() {
34
- try {
35
- const storage = getAgentStorageSync();
36
- return storage.hasActiveConsumer(STALE_TIMEOUT_MS);
37
- }
38
- catch {
39
- return false;
40
- }
41
- }
@@ -1,99 +0,0 @@
1
- export interface ConsumerServiceOptions {
2
- /** Max concurrent jobs (default: 5) */
3
- concurrency?: number;
4
- /** Poll interval in ms (default: 1000) */
5
- pollInterval?: number;
6
- }
7
- /**
8
- * ConsumerService - Singleton background worker that processes the execution queue
9
- *
10
- * Architecture:
11
- * ```
12
- * ┌─────────────────┐
13
- * │ ConsumerService │ ← Singleton, start once in main
14
- * │ (process jobs) │
15
- * └────────┬────────┘
16
- * │ writes
17
- * ▼
18
- * ┌─────────────────┐
19
- * │ AgentStorage │ ← SQLite DB (source of truth)
20
- * │ (agent.db) │
21
- * └────────┬────────┘
22
- * │ polls
23
- * ▼
24
- * ┌─────────────────┐
25
- * │QueuePollingServ │ ← UI subscribes here for updates
26
- * └─────────────────┘
27
- * ```
28
- *
29
- * Usage:
30
- * ```typescript
31
- * // Main - start consumer singleton (once)
32
- * const consumer = getConsumerService({ concurrency: 5 })
33
- * await consumer.start()
34
- *
35
- * // UI components - use QueuePollingService for monitoring
36
- * import { getQueuePollingService } from './queue-polling-service'
37
- * const poller = getQueuePollingService({ pollInterval: 500 })
38
- * poller.on('snapshot', (snapshot) => renderUI(snapshot))
39
- * await poller.start()
40
- *
41
- * // Cleanup
42
- * consumer.dispose()
43
- * ```
44
- *
45
- * Features:
46
- * - Auto-loads auth token from Keychain
47
- * - Auto-loads project config from .brv/config.json
48
- * - Handles lock acquisition/release
49
- * - Single dispose() for full cleanup
50
- */
51
- export declare class ConsumerService {
52
- private consumer;
53
- private readonly options;
54
- private running;
55
- constructor(options?: ConsumerServiceOptions);
56
- /**
57
- * Stop the consumer and cleanup all resources
58
- */
59
- dispose(): void;
60
- /**
61
- * Get the consumer ID (unique per session)
62
- * Returns null if consumer hasn't started yet
63
- */
64
- getConsumerId(): null | string;
65
- /**
66
- * Check if consumer is running
67
- */
68
- isRunning(): boolean;
69
- /**
70
- * Start the consumer
71
- *
72
- * - Loads auth token from Keychain
73
- * - Loads project config from .brv/config.json
74
- * - Acquires consumer lock
75
- * - Starts processing queue
76
- *
77
- * @throws Error if not authenticated
78
- * @throws Error if another consumer is already running
79
- */
80
- start(): Promise<void>;
81
- stop(): void;
82
- private setupSignalHandlers;
83
- }
84
- /**
85
- * Get or create singleton ConsumerService
86
- *
87
- * Usage:
88
- * ```typescript
89
- * const consumer = getConsumerService()
90
- * await consumer.start()
91
- * // later...
92
- * consumer.dispose()
93
- * ```
94
- */
95
- export declare function getConsumerService(options?: ConsumerServiceOptions): ConsumerService;
96
- /**
97
- * Dispose singleton and clear reference
98
- */
99
- export declare function disposeConsumerService(): void;
@@ -1,166 +0,0 @@
1
- // TODO(v0.5.0): Remove this file. ConsumerService is replaced by CoreProcess → TaskProcessor.
2
- import { ProjectConfigStore } from '../../config/file-config-store.js';
3
- import { KeychainTokenStore } from '../../storage/keychain-token-store.js';
4
- import { closeAgentStorage } from '../storage/agent-storage.js';
5
- import { createExecutionConsumer } from './execution-consumer.js';
6
- // ==================== SERVICE ====================
7
- /**
8
- * ConsumerService - Singleton background worker that processes the execution queue
9
- *
10
- * Architecture:
11
- * ```
12
- * ┌─────────────────┐
13
- * │ ConsumerService │ ← Singleton, start once in main
14
- * │ (process jobs) │
15
- * └────────┬────────┘
16
- * │ writes
17
- * ▼
18
- * ┌─────────────────┐
19
- * │ AgentStorage │ ← SQLite DB (source of truth)
20
- * │ (agent.db) │
21
- * └────────┬────────┘
22
- * │ polls
23
- * ▼
24
- * ┌─────────────────┐
25
- * │QueuePollingServ │ ← UI subscribes here for updates
26
- * └─────────────────┘
27
- * ```
28
- *
29
- * Usage:
30
- * ```typescript
31
- * // Main - start consumer singleton (once)
32
- * const consumer = getConsumerService({ concurrency: 5 })
33
- * await consumer.start()
34
- *
35
- * // UI components - use QueuePollingService for monitoring
36
- * import { getQueuePollingService } from './queue-polling-service'
37
- * const poller = getQueuePollingService({ pollInterval: 500 })
38
- * poller.on('snapshot', (snapshot) => renderUI(snapshot))
39
- * await poller.start()
40
- *
41
- * // Cleanup
42
- * consumer.dispose()
43
- * ```
44
- *
45
- * Features:
46
- * - Auto-loads auth token from Keychain
47
- * - Auto-loads project config from .brv/config.json
48
- * - Handles lock acquisition/release
49
- * - Single dispose() for full cleanup
50
- */
51
- export class ConsumerService {
52
- consumer = null;
53
- options;
54
- running = false;
55
- constructor(options) {
56
- this.options = options ?? {};
57
- }
58
- /**
59
- * Stop the consumer and cleanup all resources
60
- */
61
- dispose() {
62
- if (!this.running)
63
- return;
64
- this.running = false;
65
- if (this.consumer) {
66
- this.consumer.stop();
67
- this.consumer = null;
68
- }
69
- closeAgentStorage();
70
- }
71
- /**
72
- * Get the consumer ID (unique per session)
73
- * Returns null if consumer hasn't started yet
74
- */
75
- getConsumerId() {
76
- return this.consumer?.getConsumerId() ?? null;
77
- }
78
- /**
79
- * Check if consumer is running
80
- */
81
- isRunning() {
82
- return this.running && this.consumer !== null;
83
- }
84
- /**
85
- * Start the consumer
86
- *
87
- * - Loads auth token from Keychain
88
- * - Loads project config from .brv/config.json
89
- * - Acquires consumer lock
90
- * - Starts processing queue
91
- *
92
- * @throws Error if not authenticated
93
- * @throws Error if another consumer is already running
94
- */
95
- async start() {
96
- if (this.running) {
97
- throw new Error('Consumer already running');
98
- }
99
- // Load auth token
100
- const tokenStore = new KeychainTokenStore();
101
- const token = await tokenStore.load();
102
- if (!token) {
103
- throw new Error('Not authenticated. Please run "brv login" first.');
104
- }
105
- // Load project config (optional)
106
- const configStore = new ProjectConfigStore();
107
- const brvConfig = await configStore.read();
108
- // Create consumer (auto-detects .brv/blobs from cwd)
109
- this.consumer = createExecutionConsumer({
110
- authToken: { accessToken: token.accessToken, sessionKey: token.sessionKey },
111
- brvConfig: brvConfig ?? undefined,
112
- maxConcurrency: this.options.concurrency ?? 5,
113
- pollInterval: this.options.pollInterval ?? 1000,
114
- });
115
- // Start consumer
116
- const started = await this.consumer.start();
117
- if (!started) {
118
- this.consumer = null;
119
- throw new Error('Another consumer is already running in this directory');
120
- }
121
- this.running = true;
122
- this.setupSignalHandlers();
123
- }
124
- // Alias for dispose
125
- stop() {
126
- this.dispose();
127
- }
128
- // ==================== PRIVATE ====================
129
- setupSignalHandlers() {
130
- // eslint-disable-next-line unicorn/consistent-function-scoping -- needs 'this' context
131
- const cleanup = () => {
132
- this.dispose();
133
- };
134
- process.on('SIGTERM', cleanup);
135
- process.on('SIGINT', cleanup);
136
- process.on('exit', cleanup);
137
- }
138
- }
139
- // ==================== SINGLETON HELPER ====================
140
- let instance = null;
141
- /**
142
- * Get or create singleton ConsumerService
143
- *
144
- * Usage:
145
- * ```typescript
146
- * const consumer = getConsumerService()
147
- * await consumer.start()
148
- * // later...
149
- * consumer.dispose()
150
- * ```
151
- */
152
- export function getConsumerService(options) {
153
- if (!instance) {
154
- instance = new ConsumerService(options);
155
- }
156
- return instance;
157
- }
158
- /**
159
- * Dispose singleton and clear reference
160
- */
161
- export function disposeConsumerService() {
162
- if (instance) {
163
- instance.dispose();
164
- instance = null;
165
- }
166
- }