@neurcode-ai/cli 0.4.0 → 0.4.1
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/dist/api-client.d.ts +105 -17
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +388 -85
- package/dist/api-client.js.map +1 -1
- package/dist/commands/allow.d.ts.map +1 -1
- package/dist/commands/allow.js +6 -33
- package/dist/commands/allow.js.map +1 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +56 -13
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +134 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +365 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +8 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +209 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +7 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +70 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/plan.d.ts +2 -0
- package/dist/commands/plan.d.ts.map +1 -1
- package/dist/commands/plan.js +210 -57
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/prompt.d.ts +6 -0
- package/dist/commands/prompt.d.ts.map +1 -0
- package/dist/commands/prompt.js +254 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/revert.d.ts.map +1 -1
- package/dist/commands/revert.js +10 -0
- package/dist/commands/revert.js.map +1 -1
- package/dist/commands/session.d.ts +29 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +382 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +127 -13
- package/dist/commands/verify.js.map +1 -1
- package/dist/commands/watch.d.ts +8 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +78 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/config.d.ts +29 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +186 -21
- package/dist/config.js.map +1 -1
- package/dist/index.js +120 -3
- package/dist/index.js.map +1 -1
- package/dist/services/integrations/TicketService.d.ts +68 -0
- package/dist/services/integrations/TicketService.d.ts.map +1 -0
- package/dist/services/integrations/TicketService.js +151 -0
- package/dist/services/integrations/TicketService.js.map +1 -0
- package/dist/services/security/SecurityGuard.d.ts +80 -0
- package/dist/services/security/SecurityGuard.d.ts.map +1 -0
- package/dist/services/security/SecurityGuard.js +410 -0
- package/dist/services/security/SecurityGuard.js.map +1 -0
- package/dist/services/watch/BlobStore.d.ts +33 -0
- package/dist/services/watch/BlobStore.d.ts.map +1 -0
- package/dist/services/watch/BlobStore.js +108 -0
- package/dist/services/watch/BlobStore.js.map +1 -0
- package/dist/services/watch/CommandPoller.d.ts +76 -0
- package/dist/services/watch/CommandPoller.d.ts.map +1 -0
- package/dist/services/watch/CommandPoller.js +298 -0
- package/dist/services/watch/CommandPoller.js.map +1 -0
- package/dist/services/watch/Journal.d.ts +58 -0
- package/dist/services/watch/Journal.d.ts.map +1 -0
- package/dist/services/watch/Journal.js +144 -0
- package/dist/services/watch/Journal.js.map +1 -0
- package/dist/services/watch/Sentinel.d.ts +49 -0
- package/dist/services/watch/Sentinel.d.ts.map +1 -0
- package/dist/services/watch/Sentinel.js +205 -0
- package/dist/services/watch/Sentinel.js.map +1 -0
- package/dist/services/watch/Syncer.d.ts +55 -0
- package/dist/services/watch/Syncer.d.ts.map +1 -0
- package/dist/services/watch/Syncer.js +231 -0
- package/dist/services/watch/Syncer.js.map +1 -0
- package/dist/utils/ROILogger.d.ts +16 -0
- package/dist/utils/ROILogger.d.ts.map +1 -0
- package/dist/utils/ROILogger.js +45 -0
- package/dist/utils/ROILogger.js.map +1 -0
- package/dist/utils/box.d.ts +16 -0
- package/dist/utils/box.d.ts.map +1 -0
- package/dist/utils/box.js +85 -0
- package/dist/utils/box.js.map +1 -0
- package/dist/utils/gitignore.d.ts +10 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +34 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/dist/utils/messages.d.ts +81 -0
- package/dist/utils/messages.d.ts.map +1 -0
- package/dist/utils/messages.js +306 -0
- package/dist/utils/messages.js.map +1 -0
- package/dist/utils/restore.d.ts +14 -0
- package/dist/utils/restore.d.ts.map +1 -0
- package/dist/utils/restore.js +89 -0
- package/dist/utils/restore.js.map +1 -0
- package/dist/utils/state.d.ts +69 -0
- package/dist/utils/state.d.ts.map +1 -0
- package/dist/utils/state.js +151 -0
- package/dist/utils/state.js.map +1 -0
- package/dist/utils/user-context.d.ts +28 -0
- package/dist/utils/user-context.d.ts.map +1 -0
- package/dist/utils/user-context.js +68 -0
- package/dist/utils/user-context.js.map +1 -0
- package/package.json +11 -4
package/dist/api-client.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
36
|
exports.ApiClient = void 0;
|
|
4
37
|
const config_1 = require("./config");
|
|
@@ -6,12 +39,19 @@ class ApiClient {
|
|
|
6
39
|
apiUrl;
|
|
7
40
|
apiKey;
|
|
8
41
|
requestTimeout = 300000; // 5 minutes (300,000ms)
|
|
42
|
+
isRetryingAuth = false; // Flag to prevent infinite retry loops
|
|
9
43
|
constructor(config) {
|
|
10
44
|
// API URL will always be set (defaults to production)
|
|
11
45
|
// This check is no longer needed, but kept for safety
|
|
12
46
|
this.apiUrl = (config.apiUrl || 'https://api.neurcode.com').replace(/\/$/, ''); // Remove trailing slash
|
|
13
47
|
this.apiKey = config.apiKey;
|
|
14
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Update API key after re-login
|
|
51
|
+
*/
|
|
52
|
+
updateApiKey(newApiKey) {
|
|
53
|
+
this.apiKey = newApiKey;
|
|
54
|
+
}
|
|
15
55
|
/**
|
|
16
56
|
* Get API key, requiring it if not set
|
|
17
57
|
* Shows helpful error message if missing
|
|
@@ -46,17 +86,166 @@ class ApiClient {
|
|
|
46
86
|
throw error;
|
|
47
87
|
}
|
|
48
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Wrapper for fetch with debug logging on error
|
|
91
|
+
* Logs the exact URL attempted when fetch fails
|
|
92
|
+
*/
|
|
93
|
+
async fetchWithDebug(url, options = {}) {
|
|
94
|
+
try {
|
|
95
|
+
return await this.fetchWithTimeout(url, options);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Debug logging: Show the exact URL that failed
|
|
99
|
+
console.error(`\n🔍 [DEBUG] Fetch failed for URL: ${url}`);
|
|
100
|
+
console.error(`🔍 [DEBUG] API Base URL: ${this.apiUrl}`);
|
|
101
|
+
console.error(`🔍 [DEBUG] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
102
|
+
if (error instanceof Error && 'code' in error) {
|
|
103
|
+
console.error(`🔍 [DEBUG] Error code: ${error.code}`);
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Central request handler with 401 recovery
|
|
110
|
+
* Handles authentication failures gracefully by prompting for re-login
|
|
111
|
+
*/
|
|
112
|
+
async makeRequest(url, options, retryOnAuth = true) {
|
|
113
|
+
// Get API key for authorization
|
|
114
|
+
const apiKey = this.getApiKey();
|
|
115
|
+
const authHeader = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
116
|
+
// Ensure headers exist
|
|
117
|
+
if (!options.headers) {
|
|
118
|
+
options.headers = {};
|
|
119
|
+
}
|
|
120
|
+
const headers = options.headers;
|
|
121
|
+
headers['Authorization'] = authHeader;
|
|
122
|
+
try {
|
|
123
|
+
const response = await this.fetchWithDebug(url, options);
|
|
124
|
+
// Check for 401 Unauthorized
|
|
125
|
+
if (response.status === 401) {
|
|
126
|
+
const errorText = await response.text().catch(() => '');
|
|
127
|
+
let errorJson = null;
|
|
128
|
+
try {
|
|
129
|
+
errorJson = JSON.parse(errorText);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Error body is not JSON, use default message
|
|
133
|
+
}
|
|
134
|
+
// If we're already retrying or this is a retry attempt, don't loop
|
|
135
|
+
if (this.isRetryingAuth || !retryOnAuth) {
|
|
136
|
+
const errorMessage = errorJson?.message || errorJson?.error || 'Authentication failed';
|
|
137
|
+
throw new Error(`Error: Authentication failed. Please run 'neurcode login'.`);
|
|
138
|
+
}
|
|
139
|
+
// Check if terminal is interactive
|
|
140
|
+
if (process.stdout.isTTY && !process.env.CI) {
|
|
141
|
+
// Import readline for interactive prompt
|
|
142
|
+
const { createInterface } = await Promise.resolve().then(() => __importStar(require('readline/promises')));
|
|
143
|
+
const { stdin, stdout } = await Promise.resolve().then(() => __importStar(require('process')));
|
|
144
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
145
|
+
try {
|
|
146
|
+
const answer = await rl.question('❌ Session expired or invalid. Would you like to log in again? (Y/n) ');
|
|
147
|
+
rl.close();
|
|
148
|
+
const shouldRelogin = answer.trim().toLowerCase() !== 'n' && answer.trim().toLowerCase() !== 'no';
|
|
149
|
+
if (shouldRelogin) {
|
|
150
|
+
// Set flag to prevent infinite loops
|
|
151
|
+
this.isRetryingAuth = true;
|
|
152
|
+
try {
|
|
153
|
+
// Import and call login command
|
|
154
|
+
const { loginCommand } = await Promise.resolve().then(() => __importStar(require('./commands/login')));
|
|
155
|
+
await loginCommand();
|
|
156
|
+
// Reload config to get new API key
|
|
157
|
+
const { loadConfig } = await Promise.resolve().then(() => __importStar(require('./config')));
|
|
158
|
+
const newConfig = loadConfig();
|
|
159
|
+
if (newConfig.apiKey) {
|
|
160
|
+
this.updateApiKey(newConfig.apiKey);
|
|
161
|
+
// Retry the request once with new auth
|
|
162
|
+
// Create new options object with updated authorization header
|
|
163
|
+
const newAuthHeader = newConfig.apiKey.startsWith('Bearer ')
|
|
164
|
+
? newConfig.apiKey
|
|
165
|
+
: `Bearer ${newConfig.apiKey}`;
|
|
166
|
+
const retryOptions = {
|
|
167
|
+
...options,
|
|
168
|
+
headers: {
|
|
169
|
+
...headers,
|
|
170
|
+
'Authorization': newAuthHeader,
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
// Retry with retry flag set to false to prevent loops
|
|
174
|
+
const retryResponse = await this.fetchWithDebug(url, retryOptions);
|
|
175
|
+
if (retryResponse.status === 401) {
|
|
176
|
+
// Still 401 after login - something is wrong
|
|
177
|
+
throw new Error(`Error: Authentication failed. Please run 'neurcode login'.`);
|
|
178
|
+
}
|
|
179
|
+
if (!retryResponse.ok) {
|
|
180
|
+
const retryErrorText = await retryResponse.text();
|
|
181
|
+
throw new Error(`API request failed with status ${retryResponse.status}: ${retryErrorText}`);
|
|
182
|
+
}
|
|
183
|
+
return retryResponse.json();
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
throw new Error(`Error: Authentication failed. Please run 'neurcode login'.`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (loginError) {
|
|
190
|
+
// Login failed or was cancelled
|
|
191
|
+
if (loginError instanceof Error && loginError.message.includes('Authentication failed')) {
|
|
192
|
+
throw loginError;
|
|
193
|
+
}
|
|
194
|
+
throw new Error(`Error: Authentication failed. Please run 'neurcode login'.`);
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
// Reset flag
|
|
198
|
+
this.isRetryingAuth = false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// User declined to login
|
|
203
|
+
throw new Error(`Error: Authentication failed. Please run 'neurcode login'.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (promptError) {
|
|
207
|
+
rl.close();
|
|
208
|
+
throw promptError;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
// Non-interactive terminal (CI, scripts, etc.)
|
|
213
|
+
const errorMessage = errorJson?.message || errorJson?.error || 'Authentication failed';
|
|
214
|
+
throw new Error(`Error: Authentication failed. Please run 'neurcode login'.`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (!response.ok) {
|
|
218
|
+
const errorText = await response.text();
|
|
219
|
+
let errorMessage = `API request failed with status ${response.status}`;
|
|
220
|
+
try {
|
|
221
|
+
const errorJson = JSON.parse(errorText);
|
|
222
|
+
errorMessage = errorJson.error || errorMessage;
|
|
223
|
+
if (errorJson.message) {
|
|
224
|
+
errorMessage += `: ${errorJson.message}`;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
errorMessage += `: ${errorText}`;
|
|
229
|
+
}
|
|
230
|
+
throw new Error(errorMessage);
|
|
231
|
+
}
|
|
232
|
+
return response.json();
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
// Re-throw if it's already our formatted error
|
|
236
|
+
if (error instanceof Error && error.message.startsWith('Error: Authentication failed')) {
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
// For other errors, wrap them appropriately
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
49
243
|
async analyzeDiff(diff, projectId) {
|
|
50
244
|
const url = `${this.apiUrl}/api/v1/analyze-diff`;
|
|
51
245
|
const headers = {
|
|
52
246
|
'Content-Type': 'application/json'
|
|
53
247
|
};
|
|
54
|
-
|
|
55
|
-
const apiKey = this.getApiKey();
|
|
56
|
-
// Support both "Bearer nk_live_..." and just "nk_live_..."
|
|
57
|
-
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
58
|
-
headers['Authorization'] = key;
|
|
59
|
-
const response = await fetch(url, {
|
|
248
|
+
return this.makeRequest(url, {
|
|
60
249
|
method: 'POST',
|
|
61
250
|
headers,
|
|
62
251
|
body: JSON.stringify({
|
|
@@ -64,34 +253,13 @@ class ApiClient {
|
|
|
64
253
|
projectId
|
|
65
254
|
})
|
|
66
255
|
});
|
|
67
|
-
if (!response.ok) {
|
|
68
|
-
const errorText = await response.text();
|
|
69
|
-
let errorMessage = `API request failed with status ${response.status}`;
|
|
70
|
-
try {
|
|
71
|
-
const errorJson = JSON.parse(errorText);
|
|
72
|
-
errorMessage = errorJson.error || errorMessage;
|
|
73
|
-
if (errorJson.message) {
|
|
74
|
-
errorMessage += `: ${errorJson.message}`;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
errorMessage += `: ${errorText}`;
|
|
79
|
-
}
|
|
80
|
-
throw new Error(errorMessage);
|
|
81
|
-
}
|
|
82
|
-
return response.json();
|
|
83
256
|
}
|
|
84
257
|
async analyzeBloat(diff, intent, projectId, sessionId, fileContents) {
|
|
85
258
|
const url = `${this.apiUrl}/api/v1/analyze-bloat`;
|
|
86
259
|
const headers = {
|
|
87
260
|
'Content-Type': 'application/json'
|
|
88
261
|
};
|
|
89
|
-
|
|
90
|
-
const apiKey = this.getApiKey();
|
|
91
|
-
// Support both "Bearer nk_live_..." and just "nk_live_..."
|
|
92
|
-
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
93
|
-
headers['Authorization'] = key;
|
|
94
|
-
const response = await fetch(url, {
|
|
262
|
+
return this.makeRequest(url, {
|
|
95
263
|
method: 'POST',
|
|
96
264
|
headers,
|
|
97
265
|
body: JSON.stringify({
|
|
@@ -102,22 +270,6 @@ class ApiClient {
|
|
|
102
270
|
fileContents
|
|
103
271
|
})
|
|
104
272
|
});
|
|
105
|
-
if (!response.ok) {
|
|
106
|
-
const errorText = await response.text();
|
|
107
|
-
let errorMessage = `API request failed with status ${response.status}`;
|
|
108
|
-
try {
|
|
109
|
-
const errorJson = JSON.parse(errorText);
|
|
110
|
-
errorMessage = errorJson.error || errorMessage;
|
|
111
|
-
if (errorJson.message) {
|
|
112
|
-
errorMessage += `: ${errorJson.message}`;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
errorMessage += `: ${errorText}`;
|
|
117
|
-
}
|
|
118
|
-
throw new Error(errorMessage);
|
|
119
|
-
}
|
|
120
|
-
return response.json();
|
|
121
273
|
}
|
|
122
274
|
async getFileVersions(filePath, projectId, limit = 50) {
|
|
123
275
|
const url = `${this.apiUrl}/api/v1/revert/versions`;
|
|
@@ -132,7 +284,8 @@ class ApiClient {
|
|
|
132
284
|
const apiKey = this.getApiKey();
|
|
133
285
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
134
286
|
headers['Authorization'] = key;
|
|
135
|
-
const
|
|
287
|
+
const fullUrl = `${url}?${params.toString()}`;
|
|
288
|
+
const response = await this.fetchWithDebug(fullUrl, {
|
|
136
289
|
method: 'GET',
|
|
137
290
|
headers,
|
|
138
291
|
});
|
|
@@ -167,7 +320,8 @@ class ApiClient {
|
|
|
167
320
|
const apiKey = this.getApiKey();
|
|
168
321
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
169
322
|
headers['Authorization'] = key;
|
|
170
|
-
const
|
|
323
|
+
const fullUrl = `${url}?${params.toString()}`;
|
|
324
|
+
const response = await this.fetchWithDebug(fullUrl, {
|
|
171
325
|
method: 'GET',
|
|
172
326
|
headers,
|
|
173
327
|
});
|
|
@@ -199,7 +353,7 @@ class ApiClient {
|
|
|
199
353
|
const apiKey = this.getApiKey();
|
|
200
354
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
201
355
|
headers['Authorization'] = key;
|
|
202
|
-
const response = await
|
|
356
|
+
const response = await this.fetchWithDebug(url, {
|
|
203
357
|
method: 'POST',
|
|
204
358
|
headers,
|
|
205
359
|
body: JSON.stringify({
|
|
@@ -237,7 +391,7 @@ class ApiClient {
|
|
|
237
391
|
const apiKey = this.getApiKey();
|
|
238
392
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
239
393
|
headers['Authorization'] = key;
|
|
240
|
-
const response = await
|
|
394
|
+
const response = await this.fetchWithDebug(url, {
|
|
241
395
|
method: 'POST',
|
|
242
396
|
headers,
|
|
243
397
|
body: JSON.stringify({
|
|
@@ -272,7 +426,7 @@ class ApiClient {
|
|
|
272
426
|
const apiKey = this.getApiKey();
|
|
273
427
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
274
428
|
headers['Authorization'] = key;
|
|
275
|
-
const response = await
|
|
429
|
+
const response = await this.fetchWithDebug(url, {
|
|
276
430
|
method: 'POST',
|
|
277
431
|
headers,
|
|
278
432
|
body: JSON.stringify({
|
|
@@ -308,7 +462,7 @@ class ApiClient {
|
|
|
308
462
|
const apiKey = this.getApiKey();
|
|
309
463
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
310
464
|
headers['Authorization'] = key;
|
|
311
|
-
const response = await
|
|
465
|
+
const response = await this.fetchWithDebug(url, {
|
|
312
466
|
method: 'POST',
|
|
313
467
|
headers,
|
|
314
468
|
body: JSON.stringify({
|
|
@@ -336,6 +490,13 @@ class ApiClient {
|
|
|
336
490
|
/**
|
|
337
491
|
* Connect or ensure project exists
|
|
338
492
|
* Automatically detects Git URL and creates/links project
|
|
493
|
+
*
|
|
494
|
+
* Note: organizationId is automatically extracted from the auth token by the backend,
|
|
495
|
+
* so it does not need to be passed in the request body.
|
|
496
|
+
*
|
|
497
|
+
* Backend Issue: The /api/v1/projects/connect endpoint currently requires a non-empty gitUrl.
|
|
498
|
+
* When creating name-only projects (without Git), this will fail with "gitUrl is required".
|
|
499
|
+
* The backend should be updated to allow empty gitUrl when name is provided.
|
|
339
500
|
*/
|
|
340
501
|
async ensureProject(gitUrl, name) {
|
|
341
502
|
const url = `${this.apiUrl}/api/v1/projects/connect`;
|
|
@@ -343,10 +504,11 @@ class ApiClient {
|
|
|
343
504
|
'Content-Type': 'application/json',
|
|
344
505
|
};
|
|
345
506
|
// Get API key (will show helpful error if missing)
|
|
507
|
+
// Note: organizationId is extracted from the auth token by requireAuth middleware on the backend
|
|
346
508
|
const apiKey = this.getApiKey();
|
|
347
509
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
348
510
|
headers['Authorization'] = key;
|
|
349
|
-
const response = await
|
|
511
|
+
const response = await this.fetchWithDebug(url, {
|
|
350
512
|
method: 'POST',
|
|
351
513
|
headers,
|
|
352
514
|
body: JSON.stringify({
|
|
@@ -357,53 +519,47 @@ class ApiClient {
|
|
|
357
519
|
if (!response.ok) {
|
|
358
520
|
const errorText = await response.text();
|
|
359
521
|
let errorMessage = `HTTP ${response.status}`;
|
|
522
|
+
let fullErrorDetails = null;
|
|
360
523
|
try {
|
|
361
524
|
const errorJson = JSON.parse(errorText);
|
|
362
525
|
errorMessage = errorJson.error || errorJson.message || errorMessage;
|
|
526
|
+
fullErrorDetails = errorJson;
|
|
363
527
|
}
|
|
364
528
|
catch {
|
|
365
529
|
errorMessage = errorText || errorMessage;
|
|
530
|
+
fullErrorDetails = errorText;
|
|
531
|
+
}
|
|
532
|
+
// Enhanced error logging: Log full API error details for debugging
|
|
533
|
+
console.error('\n🔍 API Error Details:');
|
|
534
|
+
console.error(` Status: ${response.status}`);
|
|
535
|
+
console.error(` Error: ${errorMessage}`);
|
|
536
|
+
if (fullErrorDetails && typeof fullErrorDetails === 'object') {
|
|
537
|
+
console.error(` Full Response:`, JSON.stringify(fullErrorDetails, null, 2));
|
|
366
538
|
}
|
|
539
|
+
else {
|
|
540
|
+
console.error(` Full Response: ${fullErrorDetails}`);
|
|
541
|
+
}
|
|
542
|
+
console.error('');
|
|
367
543
|
throw new Error(`Failed to connect project: ${errorMessage}`);
|
|
368
544
|
}
|
|
369
545
|
const result = await response.json();
|
|
370
546
|
return { id: result.id, name: result.name };
|
|
371
547
|
}
|
|
372
|
-
async generatePlan(intent, files, projectId) {
|
|
548
|
+
async generatePlan(intent, files, projectId, ticketMetadata) {
|
|
373
549
|
const url = `${this.apiUrl}/api/v1/plan`;
|
|
374
550
|
const headers = {
|
|
375
551
|
'Content-Type': 'application/json'
|
|
376
552
|
};
|
|
377
|
-
|
|
378
|
-
const apiKey = this.getApiKey();
|
|
379
|
-
// Support both "Bearer nk_live_..." and just "nk_live_..."
|
|
380
|
-
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
381
|
-
headers['Authorization'] = key;
|
|
382
|
-
const response = await this.fetchWithTimeout(url, {
|
|
553
|
+
return this.makeRequest(url, {
|
|
383
554
|
method: 'POST',
|
|
384
555
|
headers,
|
|
385
556
|
body: JSON.stringify({
|
|
386
557
|
intent,
|
|
387
558
|
files,
|
|
388
559
|
projectId,
|
|
560
|
+
ticketMetadata,
|
|
389
561
|
})
|
|
390
562
|
});
|
|
391
|
-
if (!response.ok) {
|
|
392
|
-
const errorText = await response.text();
|
|
393
|
-
let errorMessage = `API request failed with status ${response.status}`;
|
|
394
|
-
try {
|
|
395
|
-
const errorJson = JSON.parse(errorText);
|
|
396
|
-
errorMessage = errorJson.error || errorMessage;
|
|
397
|
-
if (errorJson.message) {
|
|
398
|
-
errorMessage += `: ${errorJson.message}`;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
catch {
|
|
402
|
-
errorMessage += `: ${errorText}`;
|
|
403
|
-
}
|
|
404
|
-
throw new Error(errorMessage);
|
|
405
|
-
}
|
|
406
|
-
return response.json();
|
|
407
563
|
}
|
|
408
564
|
async applyPlan(planId, snapshots) {
|
|
409
565
|
const url = `${this.apiUrl}/api/v1/apply`;
|
|
@@ -415,7 +571,7 @@ class ApiClient {
|
|
|
415
571
|
// Support both "Bearer nk_live_..." and just "nk_live_..."
|
|
416
572
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
417
573
|
headers['Authorization'] = key;
|
|
418
|
-
const response = await
|
|
574
|
+
const response = await this.fetchWithDebug(url, {
|
|
419
575
|
method: 'POST',
|
|
420
576
|
headers,
|
|
421
577
|
body: JSON.stringify({
|
|
@@ -448,7 +604,7 @@ class ApiClient {
|
|
|
448
604
|
const apiKey = this.getApiKey();
|
|
449
605
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
450
606
|
headers['Authorization'] = key;
|
|
451
|
-
const response = await
|
|
607
|
+
const response = await this.fetchWithDebug(url, {
|
|
452
608
|
method: 'POST',
|
|
453
609
|
headers,
|
|
454
610
|
body: JSON.stringify({
|
|
@@ -486,7 +642,7 @@ class ApiClient {
|
|
|
486
642
|
const apiKey = this.getApiKey();
|
|
487
643
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
488
644
|
headers['Authorization'] = key;
|
|
489
|
-
const response = await
|
|
645
|
+
const response = await this.fetchWithDebug(url, {
|
|
490
646
|
method: 'POST',
|
|
491
647
|
headers,
|
|
492
648
|
body: JSON.stringify({ filePath }),
|
|
@@ -509,17 +665,17 @@ class ApiClient {
|
|
|
509
665
|
return response.json();
|
|
510
666
|
}
|
|
511
667
|
/**
|
|
512
|
-
* Get
|
|
668
|
+
* Get plan by ID
|
|
513
669
|
*/
|
|
514
|
-
async
|
|
515
|
-
const url = `${this.apiUrl}/api/v1/
|
|
670
|
+
async getPlan(planId) {
|
|
671
|
+
const url = `${this.apiUrl}/api/v1/plan/${planId}`;
|
|
516
672
|
const headers = {
|
|
517
673
|
'Content-Type': 'application/json'
|
|
518
674
|
};
|
|
519
675
|
const apiKey = this.getApiKey();
|
|
520
676
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
521
677
|
headers['Authorization'] = key;
|
|
522
|
-
const response = await
|
|
678
|
+
const response = await this.fetchWithDebug(url, {
|
|
523
679
|
method: 'GET',
|
|
524
680
|
headers,
|
|
525
681
|
});
|
|
@@ -541,17 +697,49 @@ class ApiClient {
|
|
|
541
697
|
return response.json();
|
|
542
698
|
}
|
|
543
699
|
/**
|
|
544
|
-
* Get
|
|
700
|
+
* Get Cursor prompt for a plan
|
|
545
701
|
*/
|
|
546
|
-
async
|
|
547
|
-
const url = `${this.apiUrl}/api/v1/plan/${planId}`;
|
|
702
|
+
async getPlanPrompt(planId) {
|
|
703
|
+
const url = `${this.apiUrl}/api/v1/architect/plan/${planId}/prompt`;
|
|
704
|
+
const headers = {
|
|
705
|
+
'Content-Type': 'application/json'
|
|
706
|
+
};
|
|
707
|
+
const apiKey = this.getApiKey();
|
|
708
|
+
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
709
|
+
headers['Authorization'] = key;
|
|
710
|
+
const response = await this.fetchWithTimeout(url, {
|
|
711
|
+
method: 'GET',
|
|
712
|
+
headers,
|
|
713
|
+
});
|
|
714
|
+
if (!response.ok) {
|
|
715
|
+
const errorText = await response.text();
|
|
716
|
+
let errorMessage = `API request failed with status ${response.status}`;
|
|
717
|
+
try {
|
|
718
|
+
const errorJson = JSON.parse(errorText);
|
|
719
|
+
errorMessage = errorJson.error || errorMessage;
|
|
720
|
+
if (errorJson.message) {
|
|
721
|
+
errorMessage += `: ${errorJson.message}`;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
catch {
|
|
725
|
+
errorMessage += `: ${errorText}`;
|
|
726
|
+
}
|
|
727
|
+
throw new Error(errorMessage);
|
|
728
|
+
}
|
|
729
|
+
return response.json();
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Get list of projects for the authenticated user
|
|
733
|
+
*/
|
|
734
|
+
async getProjects() {
|
|
735
|
+
const url = `${this.apiUrl}/api/v1/projects`;
|
|
548
736
|
const headers = {
|
|
549
737
|
'Content-Type': 'application/json'
|
|
550
738
|
};
|
|
551
739
|
const apiKey = this.getApiKey();
|
|
552
740
|
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
553
741
|
headers['Authorization'] = key;
|
|
554
|
-
const response = await
|
|
742
|
+
const response = await this.fetchWithDebug(url, {
|
|
555
743
|
method: 'GET',
|
|
556
744
|
headers,
|
|
557
745
|
});
|
|
@@ -572,6 +760,121 @@ class ApiClient {
|
|
|
572
760
|
}
|
|
573
761
|
return response.json();
|
|
574
762
|
}
|
|
763
|
+
/**
|
|
764
|
+
* Get project by name (for CLI auto-discovery)
|
|
765
|
+
*/
|
|
766
|
+
async getProjectByName(name) {
|
|
767
|
+
const url = `${this.apiUrl}/api/v1/projects/by-name?name=${encodeURIComponent(name)}`;
|
|
768
|
+
const headers = {
|
|
769
|
+
'Content-Type': 'application/json'
|
|
770
|
+
};
|
|
771
|
+
const apiKey = this.getApiKey();
|
|
772
|
+
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
773
|
+
headers['Authorization'] = key;
|
|
774
|
+
const response = await this.fetchWithDebug(url, {
|
|
775
|
+
method: 'GET',
|
|
776
|
+
headers,
|
|
777
|
+
});
|
|
778
|
+
if (!response.ok) {
|
|
779
|
+
const errorText = await response.text();
|
|
780
|
+
let errorMessage = `API request failed with status ${response.status}`;
|
|
781
|
+
try {
|
|
782
|
+
const errorJson = JSON.parse(errorText);
|
|
783
|
+
errorMessage = errorJson.error || errorMessage;
|
|
784
|
+
if (errorJson.message) {
|
|
785
|
+
errorMessage += `: ${errorJson.message}`;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
catch {
|
|
789
|
+
errorMessage += `: ${errorText}`;
|
|
790
|
+
}
|
|
791
|
+
throw new Error(errorMessage);
|
|
792
|
+
}
|
|
793
|
+
const result = await response.json();
|
|
794
|
+
return result;
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Get current user information
|
|
798
|
+
* Works with both API keys and Clerk JWT tokens
|
|
799
|
+
*/
|
|
800
|
+
async getCurrentUser() {
|
|
801
|
+
const url = `${this.apiUrl}/api/v1/users/me`;
|
|
802
|
+
const headers = {
|
|
803
|
+
'Content-Type': 'application/json'
|
|
804
|
+
};
|
|
805
|
+
return this.makeRequest(url, {
|
|
806
|
+
method: 'GET',
|
|
807
|
+
headers,
|
|
808
|
+
}, false); // Don't retry on auth for getCurrentUser (used to check login status)
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Get sessions for a project
|
|
812
|
+
*/
|
|
813
|
+
async getSessions(projectId, limit = 5) {
|
|
814
|
+
const params = new URLSearchParams();
|
|
815
|
+
if (projectId)
|
|
816
|
+
params.set('projectId', projectId);
|
|
817
|
+
params.set('limit', limit.toString());
|
|
818
|
+
const url = `${this.apiUrl}/api/v1/sessions?${params.toString()}`;
|
|
819
|
+
const headers = {
|
|
820
|
+
'Content-Type': 'application/json'
|
|
821
|
+
};
|
|
822
|
+
const apiKey = this.getApiKey();
|
|
823
|
+
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
824
|
+
headers['Authorization'] = key;
|
|
825
|
+
const response = await this.fetchWithDebug(url, {
|
|
826
|
+
method: 'GET',
|
|
827
|
+
headers,
|
|
828
|
+
});
|
|
829
|
+
if (!response.ok) {
|
|
830
|
+
const errorText = await response.text();
|
|
831
|
+
throw new Error(`API request failed: ${errorText}`);
|
|
832
|
+
}
|
|
833
|
+
const sessions = await response.json();
|
|
834
|
+
return sessions;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* End a session (mark as completed)
|
|
838
|
+
*/
|
|
839
|
+
async endSession(sessionId) {
|
|
840
|
+
const url = `${this.apiUrl}/api/v1/sessions/${sessionId}/end`;
|
|
841
|
+
const headers = {
|
|
842
|
+
'Content-Type': 'application/json'
|
|
843
|
+
};
|
|
844
|
+
const apiKey = this.getApiKey();
|
|
845
|
+
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
846
|
+
headers['Authorization'] = key;
|
|
847
|
+
const response = await this.fetchWithDebug(url, {
|
|
848
|
+
method: 'POST',
|
|
849
|
+
headers,
|
|
850
|
+
});
|
|
851
|
+
if (!response.ok) {
|
|
852
|
+
const errorText = await response.text();
|
|
853
|
+
throw new Error(`API request failed: ${errorText}`);
|
|
854
|
+
}
|
|
855
|
+
return await response.json();
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Get a specific session by ID
|
|
859
|
+
*/
|
|
860
|
+
async getSession(sessionId) {
|
|
861
|
+
const url = `${this.apiUrl}/api/v1/sessions/${sessionId}`;
|
|
862
|
+
const headers = {
|
|
863
|
+
'Content-Type': 'application/json'
|
|
864
|
+
};
|
|
865
|
+
const apiKey = this.getApiKey();
|
|
866
|
+
const key = apiKey.startsWith('Bearer ') ? apiKey : `Bearer ${apiKey}`;
|
|
867
|
+
headers['Authorization'] = key;
|
|
868
|
+
const response = await this.fetchWithDebug(url, {
|
|
869
|
+
method: 'GET',
|
|
870
|
+
headers,
|
|
871
|
+
});
|
|
872
|
+
if (!response.ok) {
|
|
873
|
+
const errorText = await response.text();
|
|
874
|
+
throw new Error(`API request failed: ${errorText}`);
|
|
875
|
+
}
|
|
876
|
+
return await response.json();
|
|
877
|
+
}
|
|
575
878
|
}
|
|
576
879
|
exports.ApiClient = ApiClient;
|
|
577
880
|
//# sourceMappingURL=api-client.js.map
|