byterover-cli 1.5.0 → 1.6.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/README.md +132 -11
- package/dist/core/domain/errors/headless-prompt-error.d.ts +11 -0
- package/dist/core/domain/errors/headless-prompt-error.js +18 -0
- package/dist/core/interfaces/i-cogit-pull-service.d.ts +0 -1
- package/dist/core/interfaces/i-memory-retrieval-service.d.ts +0 -1
- package/dist/core/interfaces/i-memory-storage-service.d.ts +0 -2
- package/dist/core/interfaces/i-space-service.d.ts +1 -2
- package/dist/core/interfaces/i-team-service.d.ts +1 -2
- package/dist/core/interfaces/i-user-service.d.ts +1 -2
- package/dist/core/interfaces/usecase/i-curate-use-case.d.ts +2 -0
- package/dist/core/interfaces/usecase/i-init-use-case.d.ts +9 -3
- package/dist/core/interfaces/usecase/i-login-use-case.d.ts +4 -1
- package/dist/core/interfaces/usecase/i-pull-use-case.d.ts +5 -3
- package/dist/core/interfaces/usecase/i-push-use-case.d.ts +6 -4
- package/dist/core/interfaces/usecase/i-query-use-case.d.ts +2 -0
- package/dist/core/interfaces/usecase/i-status-use-case.d.ts +1 -0
- package/dist/infra/cipher/agent/service-initializer.d.ts +1 -1
- package/dist/infra/cipher/agent/service-initializer.js +0 -1
- package/dist/infra/cipher/http/internal-llm-http-service.d.ts +0 -1
- package/dist/infra/cipher/http/internal-llm-http-service.js +1 -2
- package/dist/infra/cogit/http-cogit-pull-service.js +1 -1
- package/dist/infra/cogit/http-cogit-push-service.js +0 -1
- package/dist/infra/http/authenticated-http-client.d.ts +1 -3
- package/dist/infra/http/authenticated-http-client.js +1 -5
- package/dist/infra/memory/http-memory-retrieval-service.js +1 -1
- package/dist/infra/memory/http-memory-storage-service.js +2 -2
- package/dist/infra/process/inline-agent-executor.d.ts +32 -0
- package/dist/infra/process/inline-agent-executor.js +259 -0
- package/dist/infra/space/http-space-service.d.ts +1 -1
- package/dist/infra/space/http-space-service.js +2 -2
- package/dist/infra/storage/token-store.d.ts +4 -3
- package/dist/infra/storage/token-store.js +6 -5
- package/dist/infra/team/http-team-service.d.ts +1 -1
- package/dist/infra/team/http-team-service.js +2 -2
- package/dist/infra/terminal/headless-terminal.d.ts +91 -0
- package/dist/infra/terminal/headless-terminal.js +211 -0
- package/dist/infra/usecase/curate-use-case.d.ts +40 -1
- package/dist/infra/usecase/curate-use-case.js +176 -15
- package/dist/infra/usecase/init-use-case.d.ts +27 -5
- package/dist/infra/usecase/init-use-case.js +200 -34
- package/dist/infra/usecase/login-use-case.d.ts +10 -8
- package/dist/infra/usecase/login-use-case.js +35 -2
- package/dist/infra/usecase/pull-use-case.d.ts +19 -5
- package/dist/infra/usecase/pull-use-case.js +71 -13
- package/dist/infra/usecase/push-use-case.d.ts +18 -5
- package/dist/infra/usecase/push-use-case.js +81 -14
- package/dist/infra/usecase/query-use-case.d.ts +21 -0
- package/dist/infra/usecase/query-use-case.js +114 -29
- package/dist/infra/usecase/space-list-use-case.js +1 -1
- package/dist/infra/usecase/space-switch-use-case.js +2 -2
- package/dist/infra/usecase/status-use-case.d.ts +36 -0
- package/dist/infra/usecase/status-use-case.js +185 -48
- package/dist/infra/user/http-user-service.d.ts +1 -1
- package/dist/infra/user/http-user-service.js +2 -2
- package/dist/oclif/commands/curate.d.ts +6 -1
- package/dist/oclif/commands/curate.js +24 -3
- package/dist/oclif/commands/init.d.ts +18 -0
- package/dist/oclif/commands/init.js +129 -0
- package/dist/oclif/commands/login.d.ts +9 -0
- package/dist/oclif/commands/login.js +45 -0
- package/dist/oclif/commands/pull.d.ts +16 -0
- package/dist/oclif/commands/pull.js +78 -0
- package/dist/oclif/commands/push.d.ts +17 -0
- package/dist/oclif/commands/push.js +87 -0
- package/dist/oclif/commands/query.d.ts +6 -1
- package/dist/oclif/commands/query.js +29 -4
- package/dist/oclif/commands/status.d.ts +5 -1
- package/dist/oclif/commands/status.js +17 -5
- package/dist/tui/hooks/use-auth-polling.js +1 -1
- package/dist/utils/environment-detector.d.ts +15 -0
- package/dist/utils/environment-detector.js +62 -1
- package/oclif.manifest.json +287 -5
- package/package.json +1 -1
|
@@ -5,24 +5,26 @@ import type { ITerminal } from '../../core/interfaces/i-terminal.js';
|
|
|
5
5
|
import type { ITokenStore } from '../../core/interfaces/i-token-store.js';
|
|
6
6
|
import type { ITrackingService } from '../../core/interfaces/i-tracking-service.js';
|
|
7
7
|
import type { IUserService } from '../../core/interfaces/i-user-service.js';
|
|
8
|
-
import type { ILoginUseCase } from '../../core/interfaces/usecase/i-login-use-case.js';
|
|
8
|
+
import type { ILoginUseCase, LoginUseCaseRunOptions } from '../../core/interfaces/usecase/i-login-use-case.js';
|
|
9
9
|
export interface LoginUseCaseOptions {
|
|
10
|
-
authService
|
|
11
|
-
browserLauncher
|
|
12
|
-
callbackHandler
|
|
10
|
+
authService?: IAuthService;
|
|
11
|
+
browserLauncher?: IBrowserLauncher;
|
|
12
|
+
callbackHandler?: ICallbackHandler;
|
|
13
13
|
terminal: ITerminal;
|
|
14
14
|
tokenStore: ITokenStore;
|
|
15
15
|
trackingService: ITrackingService;
|
|
16
16
|
userService: IUserService;
|
|
17
17
|
}
|
|
18
18
|
export declare class LoginUseCase implements ILoginUseCase {
|
|
19
|
-
private readonly authService
|
|
20
|
-
private readonly browserLauncher
|
|
21
|
-
private readonly callbackHandler
|
|
19
|
+
private readonly authService?;
|
|
20
|
+
private readonly browserLauncher?;
|
|
21
|
+
private readonly callbackHandler?;
|
|
22
22
|
private readonly terminal;
|
|
23
23
|
private readonly tokenStore;
|
|
24
24
|
private readonly trackingService;
|
|
25
25
|
private readonly userService;
|
|
26
26
|
constructor(options: LoginUseCaseOptions);
|
|
27
|
-
run(): Promise<void>;
|
|
27
|
+
run({ apiKey }?: LoginUseCaseRunOptions): Promise<void>;
|
|
28
|
+
runLoginManually(): Promise<void>;
|
|
29
|
+
runLoginWithApiKey(apiKey: string): Promise<void>;
|
|
28
30
|
}
|
|
@@ -17,7 +17,13 @@ export class LoginUseCase {
|
|
|
17
17
|
this.trackingService = options.trackingService;
|
|
18
18
|
this.userService = options.userService;
|
|
19
19
|
}
|
|
20
|
-
async run() {
|
|
20
|
+
async run({ apiKey } = {}) {
|
|
21
|
+
return apiKey ? this.runLoginWithApiKey(apiKey) : this.runLoginManually();
|
|
22
|
+
}
|
|
23
|
+
async runLoginManually() {
|
|
24
|
+
if (!this.authService || !this.browserLauncher || !this.callbackHandler) {
|
|
25
|
+
throw new Error('OAuth services are required for manual login');
|
|
26
|
+
}
|
|
21
27
|
try {
|
|
22
28
|
await this.trackingService.track('auth:sign_in', { status: 'started' });
|
|
23
29
|
this.terminal.log('Starting authentication process...');
|
|
@@ -53,7 +59,7 @@ export class LoginUseCase {
|
|
|
53
59
|
// Wait for callback with 5 minute timeout
|
|
54
60
|
const { code } = await this.callbackHandler.waitForCallback(authContext.state, 5 * 60 * 1000);
|
|
55
61
|
const authTokenData = await this.authService.exchangeCodeForToken(code, authContext, redirectUri);
|
|
56
|
-
const user = await this.userService.getCurrentUser(authTokenData.
|
|
62
|
+
const user = await this.userService.getCurrentUser(authTokenData.sessionKey);
|
|
57
63
|
const authToken = new AuthToken({
|
|
58
64
|
accessToken: authTokenData.accessToken,
|
|
59
65
|
expiresAt: authTokenData.expiresAt,
|
|
@@ -91,4 +97,31 @@ export class LoginUseCase {
|
|
|
91
97
|
await this.callbackHandler.stop();
|
|
92
98
|
}
|
|
93
99
|
}
|
|
100
|
+
async runLoginWithApiKey(apiKey) {
|
|
101
|
+
try {
|
|
102
|
+
await this.trackingService.track('auth:sign_in', { status: 'started' });
|
|
103
|
+
this.terminal.log('Logging in...');
|
|
104
|
+
const user = await this.userService.getCurrentUser(apiKey);
|
|
105
|
+
// eslint-disable-next-line no-warning-comments
|
|
106
|
+
// TODO: accessToken, refreshToken, and tokenType do not appear to be necessary; consider removing them.
|
|
107
|
+
const authToken = new AuthToken({
|
|
108
|
+
accessToken: 'unnecessary',
|
|
109
|
+
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days
|
|
110
|
+
refreshToken: 'unnecessary',
|
|
111
|
+
sessionKey: apiKey,
|
|
112
|
+
tokenType: 'unnecessary',
|
|
113
|
+
userEmail: user.email,
|
|
114
|
+
userId: user.id,
|
|
115
|
+
});
|
|
116
|
+
await this.tokenStore.save(authToken);
|
|
117
|
+
await this.trackingService.track('auth:sign_in', { status: 'finished' });
|
|
118
|
+
this.terminal.log(`Logged in as ${user.email}`);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
// Throw error to let oclif handle display
|
|
122
|
+
const errorMessage = error instanceof Error ? error.message : 'Authentication failed';
|
|
123
|
+
await this.trackingService.track('auth:sign_in', { message: errorMessage, status: 'error' });
|
|
124
|
+
this.terminal.log(errorMessage);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
94
127
|
}
|
|
@@ -7,7 +7,19 @@ import type { IProjectConfigStore } from '../../core/interfaces/i-project-config
|
|
|
7
7
|
import type { ITerminal } from '../../core/interfaces/i-terminal.js';
|
|
8
8
|
import type { ITokenStore } from '../../core/interfaces/i-token-store.js';
|
|
9
9
|
import type { ITrackingService } from '../../core/interfaces/i-tracking-service.js';
|
|
10
|
-
import type { IPullUseCase } from '../../core/interfaces/usecase/i-pull-use-case.js';
|
|
10
|
+
import type { IPullUseCase, PullUseCaseRunOptions } from '../../core/interfaces/usecase/i-pull-use-case.js';
|
|
11
|
+
/**
|
|
12
|
+
* Structured pull result for JSON output.
|
|
13
|
+
*/
|
|
14
|
+
export interface PullResult {
|
|
15
|
+
added?: number;
|
|
16
|
+
branch?: string;
|
|
17
|
+
commitSha?: string;
|
|
18
|
+
deleted?: number;
|
|
19
|
+
edited?: number;
|
|
20
|
+
error?: string;
|
|
21
|
+
status: 'error' | 'local_changes' | 'success';
|
|
22
|
+
}
|
|
11
23
|
export interface PullUseCaseOptions {
|
|
12
24
|
cogitPullService: ICogitPullService;
|
|
13
25
|
contextTreeSnapshotService: IContextTreeSnapshotService;
|
|
@@ -28,8 +40,10 @@ export declare class PullUseCase implements IPullUseCase {
|
|
|
28
40
|
constructor(options: PullUseCaseOptions);
|
|
29
41
|
protected checkLocalChanges(): Promise<boolean>;
|
|
30
42
|
protected checkProjectInit(): Promise<BrvConfig>;
|
|
31
|
-
run(options:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
run(options: PullUseCaseRunOptions): Promise<void>;
|
|
44
|
+
protected validateAuth(format: 'json' | 'text'): Promise<AuthToken | undefined>;
|
|
45
|
+
/**
|
|
46
|
+
* Output JSON result for headless mode.
|
|
47
|
+
*/
|
|
48
|
+
private outputJsonResult;
|
|
35
49
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WorkspaceNotInitializedError } from '../cipher/validation/workspace-validator.js';
|
|
2
|
+
import { HeadlessTerminal } from '../terminal/headless-terminal.js';
|
|
2
3
|
export class PullUseCase {
|
|
3
4
|
cogitPullService;
|
|
4
5
|
contextTreeSnapshotService;
|
|
@@ -28,9 +29,10 @@ export class PullUseCase {
|
|
|
28
29
|
return projectConfig;
|
|
29
30
|
}
|
|
30
31
|
async run(options) {
|
|
32
|
+
const format = options.format ?? 'text';
|
|
31
33
|
try {
|
|
32
34
|
await this.trackingService.track('mem:pull');
|
|
33
|
-
const token = await this.validateAuth();
|
|
35
|
+
const token = await this.validateAuth(format);
|
|
34
36
|
if (!token)
|
|
35
37
|
return;
|
|
36
38
|
const projectConfig = await this.checkProjectInit();
|
|
@@ -39,13 +41,20 @@ export class PullUseCase {
|
|
|
39
41
|
const hasLocalChanges = await this.checkLocalChanges();
|
|
40
42
|
this.terminal.actionStop();
|
|
41
43
|
if (hasLocalChanges) {
|
|
42
|
-
|
|
44
|
+
if (format === 'json') {
|
|
45
|
+
this.outputJsonResult({
|
|
46
|
+
error: 'You have local changes that have not been pushed. Run push first.',
|
|
47
|
+
status: 'local_changes',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.terminal.log('You have local changes that have not been pushed. Run "/push" first.');
|
|
52
|
+
}
|
|
43
53
|
return;
|
|
44
54
|
}
|
|
45
55
|
// Pull from CoGit
|
|
46
56
|
this.terminal.log('Pulling from ByteRover...');
|
|
47
57
|
const snapshot = await this.cogitPullService.pull({
|
|
48
|
-
accessToken: token.accessToken,
|
|
49
58
|
branch: options.branch,
|
|
50
59
|
sessionKey: token.sessionKey,
|
|
51
60
|
spaceId: projectConfig.spaceId,
|
|
@@ -57,33 +66,82 @@ export class PullUseCase {
|
|
|
57
66
|
this.terminal.actionStop();
|
|
58
67
|
// Update snapshot ONLY after successful sync
|
|
59
68
|
await this.contextTreeSnapshotService.saveSnapshot();
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
if (format === 'json') {
|
|
70
|
+
this.outputJsonResult({
|
|
71
|
+
added: syncResult.added.length,
|
|
72
|
+
branch: options.branch,
|
|
73
|
+
commitSha: snapshot.commitSha,
|
|
74
|
+
deleted: syncResult.deleted.length,
|
|
75
|
+
edited: syncResult.edited.length,
|
|
76
|
+
status: 'success',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Success message
|
|
81
|
+
this.terminal.log('\n✓ Successfully pulled context tree from ByteRover memory storage!');
|
|
82
|
+
this.terminal.log(` Branch: ${options.branch}`);
|
|
83
|
+
this.terminal.log(` Commit: ${snapshot.commitSha.slice(0, 7)}`);
|
|
84
|
+
this.terminal.log(` Added: ${syncResult.added.length}, Edited: ${syncResult.edited.length}, Deleted: ${syncResult.deleted.length}`);
|
|
85
|
+
}
|
|
65
86
|
}
|
|
66
87
|
catch (error) {
|
|
67
88
|
// Stop action if it's in progress
|
|
68
89
|
this.terminal.actionStop();
|
|
69
90
|
if (error instanceof WorkspaceNotInitializedError) {
|
|
70
|
-
|
|
91
|
+
if (format === 'json') {
|
|
92
|
+
this.outputJsonResult({ error: 'Project not initialized. Run init first.', status: 'error' });
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
this.terminal.log('Project not initialized. Please run "/init" to select your team and workspace.');
|
|
96
|
+
}
|
|
71
97
|
return;
|
|
72
98
|
}
|
|
73
99
|
const message = error instanceof Error ? error.message : 'Pull failed';
|
|
74
|
-
|
|
100
|
+
if (format === 'json') {
|
|
101
|
+
this.outputJsonResult({ error: message, status: 'error' });
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
this.terminal.error(`Failed to pull: ${message}`);
|
|
105
|
+
}
|
|
75
106
|
}
|
|
76
107
|
}
|
|
77
|
-
async validateAuth() {
|
|
108
|
+
async validateAuth(format) {
|
|
78
109
|
const token = await this.tokenStore.load();
|
|
79
110
|
if (token === undefined) {
|
|
80
|
-
|
|
111
|
+
if (format === 'json') {
|
|
112
|
+
this.outputJsonResult({ error: 'Not authenticated. Run login first.', status: 'error' });
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
this.terminal.error('Not authenticated. Run "/login" first.');
|
|
116
|
+
}
|
|
81
117
|
return undefined;
|
|
82
118
|
}
|
|
83
119
|
if (!token.isValid()) {
|
|
84
|
-
|
|
120
|
+
if (format === 'json') {
|
|
121
|
+
this.outputJsonResult({ error: 'Authentication token expired. Run login again.', status: 'error' });
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
this.terminal.error('Authentication token expired. Run "/login" again.');
|
|
125
|
+
}
|
|
85
126
|
return undefined;
|
|
86
127
|
}
|
|
87
128
|
return token;
|
|
88
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Output JSON result for headless mode.
|
|
132
|
+
*/
|
|
133
|
+
outputJsonResult(result) {
|
|
134
|
+
const response = {
|
|
135
|
+
command: 'pull',
|
|
136
|
+
data: result,
|
|
137
|
+
success: result.status === 'success',
|
|
138
|
+
timestamp: new Date().toISOString(),
|
|
139
|
+
};
|
|
140
|
+
if (this.terminal instanceof HeadlessTerminal) {
|
|
141
|
+
this.terminal.writeFinalResponse(response);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
this.terminal.log(JSON.stringify(response));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
89
147
|
}
|
|
@@ -5,7 +5,19 @@ import type { IProjectConfigStore } from '../../core/interfaces/i-project-config
|
|
|
5
5
|
import type { ITerminal } from '../../core/interfaces/i-terminal.js';
|
|
6
6
|
import type { ITokenStore } from '../../core/interfaces/i-token-store.js';
|
|
7
7
|
import type { ITrackingService } from '../../core/interfaces/i-tracking-service.js';
|
|
8
|
-
import type { IPushUseCase } from '../../core/interfaces/usecase/i-push-use-case.js';
|
|
8
|
+
import type { IPushUseCase, PushUseCaseRunOptions } from '../../core/interfaces/usecase/i-push-use-case.js';
|
|
9
|
+
/**
|
|
10
|
+
* Structured push result for JSON output.
|
|
11
|
+
*/
|
|
12
|
+
export interface PushResult {
|
|
13
|
+
added?: number;
|
|
14
|
+
branch?: string;
|
|
15
|
+
deleted?: number;
|
|
16
|
+
edited?: number;
|
|
17
|
+
error?: string;
|
|
18
|
+
status: 'cancelled' | 'error' | 'no_changes' | 'success';
|
|
19
|
+
url?: string;
|
|
20
|
+
}
|
|
9
21
|
export interface PushUseCaseOptions {
|
|
10
22
|
cogitPushService: ICogitPushService;
|
|
11
23
|
contextFileReader: IContextFileReader;
|
|
@@ -26,12 +38,13 @@ export declare class PushUseCase implements IPushUseCase {
|
|
|
26
38
|
private readonly trackingService;
|
|
27
39
|
private readonly webAppUrl;
|
|
28
40
|
constructor(options: PushUseCaseOptions);
|
|
29
|
-
run(options:
|
|
30
|
-
branch: string;
|
|
31
|
-
skipConfirmation: boolean;
|
|
32
|
-
}): Promise<void>;
|
|
41
|
+
run(options: PushUseCaseRunOptions): Promise<void>;
|
|
33
42
|
private buildSpaceUrl;
|
|
34
43
|
private checkProjectInit;
|
|
35
44
|
private confirmPush;
|
|
45
|
+
/**
|
|
46
|
+
* Output JSON result for headless mode.
|
|
47
|
+
*/
|
|
48
|
+
private outputJsonResult;
|
|
36
49
|
private validateAuth;
|
|
37
50
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { WorkspaceNotInitializedError } from '../cipher/validation/workspace-validator.js';
|
|
2
2
|
import { mapToPushContexts } from '../cogit/context-tree-to-push-context-mapper.js';
|
|
3
|
+
import { HeadlessTerminal } from '../terminal/headless-terminal.js';
|
|
3
4
|
export class PushUseCase {
|
|
4
5
|
cogitPushService;
|
|
5
6
|
contextFileReader;
|
|
@@ -20,9 +21,10 @@ export class PushUseCase {
|
|
|
20
21
|
this.webAppUrl = options.webAppUrl;
|
|
21
22
|
}
|
|
22
23
|
async run(options) {
|
|
24
|
+
const format = options.format ?? 'text';
|
|
23
25
|
try {
|
|
24
26
|
await this.trackingService.track('mem:push');
|
|
25
|
-
const token = await this.validateAuth();
|
|
27
|
+
const token = await this.validateAuth(format);
|
|
26
28
|
if (!token)
|
|
27
29
|
return;
|
|
28
30
|
const projectConfig = await this.checkProjectInit();
|
|
@@ -33,14 +35,24 @@ export class PushUseCase {
|
|
|
33
35
|
if (contextTreeChanges.added.length === 0 &&
|
|
34
36
|
contextTreeChanges.modified.length === 0 &&
|
|
35
37
|
contextTreeChanges.deleted.length === 0) {
|
|
36
|
-
|
|
38
|
+
if (format === 'json') {
|
|
39
|
+
this.outputJsonResult({ status: 'no_changes' });
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
this.terminal.log('No context changes to push.');
|
|
43
|
+
}
|
|
37
44
|
return;
|
|
38
45
|
}
|
|
39
46
|
// Prompt for confirmation unless skipConfirmation is true
|
|
40
47
|
if (!options.skipConfirmation) {
|
|
41
48
|
const confirmed = await this.confirmPush(projectConfig, options.branch);
|
|
42
49
|
if (!confirmed) {
|
|
43
|
-
|
|
50
|
+
if (format === 'json') {
|
|
51
|
+
this.outputJsonResult({ status: 'cancelled' });
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.terminal.log('Push cancelled.');
|
|
55
|
+
}
|
|
44
56
|
return;
|
|
45
57
|
}
|
|
46
58
|
}
|
|
@@ -57,7 +69,12 @@ export class PushUseCase {
|
|
|
57
69
|
modifiedFiles,
|
|
58
70
|
});
|
|
59
71
|
if (pushContexts.length === 0) {
|
|
60
|
-
|
|
72
|
+
if (format === 'json') {
|
|
73
|
+
this.outputJsonResult({ status: 'no_changes' });
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.terminal.log('\nNo valid context files to push.');
|
|
77
|
+
}
|
|
61
78
|
return;
|
|
62
79
|
}
|
|
63
80
|
// Push to CoGit (with two-request SHA flow)
|
|
@@ -72,22 +89,45 @@ export class PushUseCase {
|
|
|
72
89
|
});
|
|
73
90
|
// Update snapshot ONLY after successful push
|
|
74
91
|
await this.contextTreeSnapshotService.saveSnapshot();
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
92
|
+
const url = this.buildSpaceUrl(projectConfig.teamName, projectConfig.spaceName);
|
|
93
|
+
if (format === 'json') {
|
|
94
|
+
this.outputJsonResult({
|
|
95
|
+
added: addedFiles.length,
|
|
96
|
+
branch: options.branch,
|
|
97
|
+
deleted: contextTreeChanges.deleted.length,
|
|
98
|
+
edited: modifiedFiles.length,
|
|
99
|
+
status: 'success',
|
|
100
|
+
url,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Success message
|
|
105
|
+
this.terminal.log('\n✓ Successfully pushed context tree to ByteRover memory storage!');
|
|
106
|
+
this.terminal.log(` Branch: ${options.branch}`);
|
|
107
|
+
this.terminal.log(` Added: ${addedFiles.length}, Edited: ${modifiedFiles.length}, Deleted: ${contextTreeChanges.deleted.length}`);
|
|
108
|
+
this.terminal.log(` View: ${url}`);
|
|
109
|
+
}
|
|
80
110
|
}
|
|
81
111
|
catch (error) {
|
|
82
112
|
// Stop action if it's in progress
|
|
83
113
|
this.terminal.actionStop();
|
|
84
114
|
if (error instanceof WorkspaceNotInitializedError) {
|
|
85
|
-
|
|
115
|
+
if (format === 'json') {
|
|
116
|
+
this.outputJsonResult({ error: 'Project not initialized. Run init first.', status: 'error' });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
this.terminal.log('Project not initialized. Please run "/init" to select your team and workspace.');
|
|
120
|
+
}
|
|
86
121
|
return;
|
|
87
122
|
}
|
|
88
123
|
// For other errors, to properly display error before exit
|
|
89
124
|
const message = error instanceof Error ? error.message : 'Push failed';
|
|
90
|
-
|
|
125
|
+
if (format === 'json') {
|
|
126
|
+
this.outputJsonResult({ error: message, status: 'error' });
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
this.terminal.error(`Failed to push: ${message}`);
|
|
130
|
+
}
|
|
91
131
|
}
|
|
92
132
|
}
|
|
93
133
|
buildSpaceUrl(teamName, spaceName) {
|
|
@@ -109,14 +149,41 @@ export class PushUseCase {
|
|
|
109
149
|
message: 'Push to ByteRover',
|
|
110
150
|
});
|
|
111
151
|
}
|
|
112
|
-
|
|
152
|
+
/**
|
|
153
|
+
* Output JSON result for headless mode.
|
|
154
|
+
*/
|
|
155
|
+
outputJsonResult(result) {
|
|
156
|
+
const response = {
|
|
157
|
+
command: 'push',
|
|
158
|
+
data: result,
|
|
159
|
+
success: result.status === 'success',
|
|
160
|
+
timestamp: new Date().toISOString(),
|
|
161
|
+
};
|
|
162
|
+
if (this.terminal instanceof HeadlessTerminal) {
|
|
163
|
+
this.terminal.writeFinalResponse(response);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.terminal.log(JSON.stringify(response));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async validateAuth(format) {
|
|
113
170
|
const token = await this.tokenStore.load();
|
|
114
171
|
if (token === undefined) {
|
|
115
|
-
|
|
172
|
+
if (format === 'json') {
|
|
173
|
+
this.outputJsonResult({ error: 'Not authenticated. Run login first.', status: 'error' });
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
this.terminal.error('Not authenticated. Run "/login" first.');
|
|
177
|
+
}
|
|
116
178
|
return undefined;
|
|
117
179
|
}
|
|
118
180
|
if (!token.isValid()) {
|
|
119
|
-
|
|
181
|
+
if (format === 'json') {
|
|
182
|
+
this.outputJsonResult({ error: 'Authentication token expired. Run login again.', status: 'error' });
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
this.terminal.error('Authentication token expired. Run "/login" again.');
|
|
186
|
+
}
|
|
120
187
|
return undefined;
|
|
121
188
|
}
|
|
122
189
|
return token;
|
|
@@ -4,6 +4,19 @@ import type { ITrackingService } from '../../core/interfaces/i-tracking-service.
|
|
|
4
4
|
import type { IQueryUseCase, QueryUseCaseRunOptions } from '../../core/interfaces/usecase/i-query-use-case.js';
|
|
5
5
|
import { CipherAgent } from '../cipher/agent/index.js';
|
|
6
6
|
import { type TransportClientFactory } from '../transport/transport-client-factory.js';
|
|
7
|
+
/**
|
|
8
|
+
* Structured query result for JSON output.
|
|
9
|
+
*/
|
|
10
|
+
export interface QueryResult {
|
|
11
|
+
error?: string;
|
|
12
|
+
result?: string;
|
|
13
|
+
status: 'completed' | 'error';
|
|
14
|
+
toolCalls?: Array<{
|
|
15
|
+
status: string;
|
|
16
|
+
summary: string;
|
|
17
|
+
tool: string;
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
7
20
|
export type TransportClientFactoryCreator = () => TransportClientFactory;
|
|
8
21
|
export interface QueryUseCaseOptions {
|
|
9
22
|
terminal: ITerminal;
|
|
@@ -44,6 +57,14 @@ export declare class QueryUseCase implements IQueryUseCase {
|
|
|
44
57
|
* Handle connection-related errors with user-friendly messages.
|
|
45
58
|
*/
|
|
46
59
|
private handleConnectionError;
|
|
60
|
+
/**
|
|
61
|
+
* Handle connection errors with JSON output.
|
|
62
|
+
*/
|
|
63
|
+
private handleConnectionErrorJson;
|
|
64
|
+
/**
|
|
65
|
+
* Output JSON result for headless mode.
|
|
66
|
+
*/
|
|
67
|
+
private outputJsonResult;
|
|
47
68
|
/**
|
|
48
69
|
* Stream task results from the connected instance.
|
|
49
70
|
*/
|