@axhub/genie 0.2.2 → 0.2.3

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 (72) hide show
  1. package/dist/assets/App-Ddoy7BIl.js +484 -0
  2. package/dist/assets/{ReviewApp-Bp_y3xff.js → ReviewApp-DOAHmxVe.js} +1 -1
  3. package/dist/assets/{_basePickBy-C5f221Kr.js → _basePickBy-CAs6HGVw.js} +1 -1
  4. package/dist/assets/{_baseUniq-CeEXFlBh.js → _baseUniq-qvm36g_v.js} +1 -1
  5. package/dist/assets/{arc-CZVQXROF.js → arc-60yKSsVt.js} +1 -1
  6. package/dist/assets/{architectureDiagram-2XIMDMQ5-D91MBXeh.js → architectureDiagram-2XIMDMQ5-C5Ik86xM.js} +1 -1
  7. package/dist/assets/{blockDiagram-WCTKOSBZ-CsHP3zT2.js → blockDiagram-WCTKOSBZ-CGqfB5z7.js} +1 -1
  8. package/dist/assets/{c4Diagram-IC4MRINW-DakKlk21.js → c4Diagram-IC4MRINW-C02h4qfL.js} +1 -1
  9. package/dist/assets/channel-B3JUshOm.js +1 -0
  10. package/dist/assets/{chunk-4BX2VUAB-BXZoxrtv.js → chunk-4BX2VUAB-BVVwu3tU.js} +1 -1
  11. package/dist/assets/{chunk-55IACEB6-V9_WXk3w.js → chunk-55IACEB6-Dyehrd1R.js} +1 -1
  12. package/dist/assets/{chunk-FMBD7UC4-IgdHo6Dd.js → chunk-FMBD7UC4-ByEz0wOl.js} +1 -1
  13. package/dist/assets/{chunk-JSJVCQXG-CnaAsDTd.js → chunk-JSJVCQXG-BXHZvm_e.js} +1 -1
  14. package/dist/assets/{chunk-KX2RTZJC-D0qksU2H.js → chunk-KX2RTZJC-DJPATr22.js} +1 -1
  15. package/dist/assets/{chunk-NQ4KR5QH-rd6KG4-c.js → chunk-NQ4KR5QH-RgZbucc4.js} +1 -1
  16. package/dist/assets/{chunk-QZHKN3VN-Cyltgv4l.js → chunk-QZHKN3VN-DMIXCLGh.js} +1 -1
  17. package/dist/assets/{chunk-WL4C6EOR-DkNtSo86.js → chunk-WL4C6EOR-Bg4bzqqP.js} +1 -1
  18. package/dist/assets/classDiagram-VBA2DB6C-DZEYhtwP.js +1 -0
  19. package/dist/assets/classDiagram-v2-RAHNMMFH-DZEYhtwP.js +1 -0
  20. package/dist/assets/clone-B2sPtF_O.js +1 -0
  21. package/dist/assets/{cose-bilkent-S5V4N54A-D_Wsd3iv.js → cose-bilkent-S5V4N54A-BZeDiX7Y.js} +1 -1
  22. package/dist/assets/{dagre-KLK3FWXG-CrrmZeGu.js → dagre-KLK3FWXG-BrwI_1JI.js} +1 -1
  23. package/dist/assets/{diagram-E7M64L7V-WzO26pGn.js → diagram-E7M64L7V-CxAlKNFG.js} +1 -1
  24. package/dist/assets/{diagram-IFDJBPK2-COynsdO3.js → diagram-IFDJBPK2-VClYIYo3.js} +1 -1
  25. package/dist/assets/{diagram-P4PSJMXO-CSqD5HJx.js → diagram-P4PSJMXO-CYRL57cP.js} +1 -1
  26. package/dist/assets/{erDiagram-INFDFZHY-BiFhS6xi.js → erDiagram-INFDFZHY-ZySJDqNG.js} +1 -1
  27. package/dist/assets/{flowDiagram-PKNHOUZH-9jdAJSs0.js → flowDiagram-PKNHOUZH-Ct9-q2zS.js} +1 -1
  28. package/dist/assets/{ganttDiagram-A5KZAMGK-pGyMKWCo.js → ganttDiagram-A5KZAMGK-Qn4VUvVR.js} +1 -1
  29. package/dist/assets/{gitGraphDiagram-K3NZZRJ6-D4jvwoW1.js → gitGraphDiagram-K3NZZRJ6-CaveIlg3.js} +1 -1
  30. package/dist/assets/{graph-DMknFkkX.js → graph-B1tvcjpq.js} +1 -1
  31. package/dist/assets/{highlighted-body-TPN3WLV5-DhMGh1O7.js → highlighted-body-TPN3WLV5-Cx4aCKGT.js} +1 -1
  32. package/dist/assets/index-BFX9lxRB.css +1 -0
  33. package/dist/assets/index-Q-dfaLIv.js +2 -0
  34. package/dist/assets/{infoDiagram-LFFYTUFH-CP5zYiVP.js → infoDiagram-LFFYTUFH-FNMZEktA.js} +1 -1
  35. package/dist/assets/{ishikawaDiagram-PHBUUO56-BplLZAQ5.js → ishikawaDiagram-PHBUUO56-D9db2Pov.js} +1 -1
  36. package/dist/assets/{journeyDiagram-4ABVD52K-CYVgkl-y.js → journeyDiagram-4ABVD52K-BzYKMYOM.js} +1 -1
  37. package/dist/assets/{kanban-definition-K7BYSVSG-D3on0q66.js → kanban-definition-K7BYSVSG-qC9Xmf0j.js} +1 -1
  38. package/dist/assets/{layout-DoKWZNVk.js → layout-dbzqH_4Q.js} +1 -1
  39. package/dist/assets/{linear-D4YTLdon.js → linear-HOAktq-3.js} +1 -1
  40. package/dist/assets/{mermaid-O7DHMXV3-KLW3VWsF.js → mermaid-O7DHMXV3-CazKksAH.js} +5 -5
  41. package/dist/assets/{mindmap-definition-YRQLILUH-EEAggxM3.js → mindmap-definition-YRQLILUH-DlfKip6Z.js} +1 -1
  42. package/dist/assets/{pieDiagram-SKSYHLDU-Da-_fmYg.js → pieDiagram-SKSYHLDU-BYv9DU8d.js} +1 -1
  43. package/dist/assets/{quadrantDiagram-337W2JSQ-Dq4gr7Sw.js → quadrantDiagram-337W2JSQ-l21TFCGi.js} +1 -1
  44. package/dist/assets/{requirementDiagram-Z7DCOOCP-DNSXyCNU.js → requirementDiagram-Z7DCOOCP-DwM1YiVc.js} +1 -1
  45. package/dist/assets/{sankeyDiagram-WA2Y5GQK-CT4ST2HW.js → sankeyDiagram-WA2Y5GQK-DaSSXYkb.js} +1 -1
  46. package/dist/assets/{sequenceDiagram-2WXFIKYE-CshVYjrF.js → sequenceDiagram-2WXFIKYE-BPBhAefs.js} +1 -1
  47. package/dist/assets/{stateDiagram-RAJIS63D-hsG5Yi2A.js → stateDiagram-RAJIS63D-Cs4xILlc.js} +1 -1
  48. package/dist/assets/stateDiagram-v2-FVOUBMTO-DeUj7MLk.js +1 -0
  49. package/dist/assets/{timeline-definition-YZTLITO2-EztFFK5F.js → timeline-definition-YZTLITO2-BPnQPJ2R.js} +1 -1
  50. package/dist/assets/{treemap-KZPCXAKY-D5UZCcq_.js → treemap-KZPCXAKY-BRWkr5mn.js} +1 -1
  51. package/dist/assets/{vennDiagram-LZ73GAT5-9Qwv8UTR.js → vennDiagram-LZ73GAT5-DioBg_XD.js} +1 -1
  52. package/dist/assets/{xychartDiagram-JWTSCODW-BBjpC1Qv.js → xychartDiagram-JWTSCODW-Bh2R2Uzh.js} +1 -1
  53. package/dist/index.html +2 -2
  54. package/package.json +2 -2
  55. package/server/database/db.js +0 -45
  56. package/server/external-agent/service.js +39 -490
  57. package/server/external-agent/service.test.js +12 -0
  58. package/server/index.js +21 -43
  59. package/server/middleware/auth.js +15 -1
  60. package/server/routes/agent.js +15 -1136
  61. package/server/routes/cli-auth.js +60 -52
  62. package/server/routes/projects.js +10 -304
  63. package/server/routes/settings.js +4 -0
  64. package/server/routes/user.js +0 -102
  65. package/dist/assets/App-Cay5kE3A.js +0 -504
  66. package/dist/assets/channel-CnzaP2H9.js +0 -1
  67. package/dist/assets/classDiagram-VBA2DB6C-CUcYxMZ8.js +0 -1
  68. package/dist/assets/classDiagram-v2-RAHNMMFH-CUcYxMZ8.js +0 -1
  69. package/dist/assets/clone-DJXJGSg2.js +0 -1
  70. package/dist/assets/index-2198VgsK.css +0 -1
  71. package/dist/assets/index-EMGPq9Uy.js +0 -2
  72. package/dist/assets/stateDiagram-v2-FVOUBMTO-BQa1Ppoo.js +0 -1
@@ -1,11 +1,5 @@
1
- import { spawn } from 'child_process';
2
- import path from 'path';
3
- import os from 'os';
4
1
  import { promises as fs } from 'fs';
5
- import crypto from 'crypto';
6
- import { Octokit } from '@octokit/rest';
7
2
 
8
- import { githubTokensDb } from '../database/db.js';
9
3
  import { addProjectManually } from '../projects.js';
10
4
  import { queryClaudeSDK } from '../claude-sdk.js';
11
5
  import { queryCodex } from '../openai-codex.js';
@@ -35,9 +29,15 @@ function parseBoolean(value, fallback) {
35
29
  if (value === undefined) {
36
30
  return fallback;
37
31
  }
32
+
38
33
  return value === true || value === 'true';
39
34
  }
40
35
 
36
+ function getDeprecatedGitHubFields(body) {
37
+ const deprecatedFields = ['githubUrl', 'githubToken', 'branchName', 'createBranch', 'createPR'];
38
+ return deprecatedFields.filter((field) => body[field] !== undefined);
39
+ }
40
+
41
41
  export function buildSessionNavigation(sessionId) {
42
42
  const normalizedSessionId = typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : null;
43
43
  const sessionPath = normalizedSessionId ? `/session/${normalizedSessionId}` : null;
@@ -55,19 +55,25 @@ export function buildSessionNavigation(sessionId) {
55
55
  }
56
56
 
57
57
  export function normalizeExternalAgentRunRequest(body = {}) {
58
- const { githubUrl, projectPath, message, model, githubToken, branchName, sessionId, openOnly } = body;
58
+ const {
59
+ projectPath,
60
+ message,
61
+ model,
62
+ sessionId,
63
+ openOnly
64
+ } = body;
65
+
59
66
  const provider = typeof body.provider === 'string' ? body.provider.trim() : 'claude';
60
67
  const stream = parseBoolean(body.stream, true);
61
68
  const requestedCleanup = parseBoolean(body.cleanup, true);
62
- const createBranch = branchName ? true : parseBoolean(body.createBranch, false);
63
- const createPR = parseBoolean(body.createPR, false);
64
69
  const normalizedSessionId = typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : null;
65
70
  const normalizedMessage = typeof message === 'string' ? message.trim() : '';
66
71
  const requestedOpenOnly = openOnly === true || openOnly === 'true';
67
72
  const isOpenOnly = requestedOpenOnly || (!normalizedMessage && !!normalizedSessionId);
68
73
  const cleanup = isOpenOnly ? false : requestedCleanup;
69
74
  const images = normalizeExternalImages(body.images);
70
- const resolvedProjectPath = githubUrl ? projectPath : resolveWorkingDirectory({ projectPath });
75
+ const resolvedProjectPath = resolveWorkingDirectory({ projectPath });
76
+ const deprecatedGitHubFields = getDeprecatedGitHubFields(body);
71
77
 
72
78
  let callbackConfig = null;
73
79
  try {
@@ -84,6 +90,13 @@ export function normalizeExternalAgentRunRequest(body = {}) {
84
90
  throw createRequestError('sessionId must be a non-empty string when provided', 400);
85
91
  }
86
92
 
93
+ if (deprecatedGitHubFields.length > 0) {
94
+ throw createRequestError(
95
+ `Unsupported parameters: ${deprecatedGitHubFields.join(', ')}. GitHub repository cloning and automatic branch/PR creation have been removed from the external agent API.`,
96
+ 400
97
+ );
98
+ }
99
+
87
100
  if (isOpenOnly && !normalizedSessionId) {
88
101
  throw createRequestError('sessionId is required when message is empty or openOnly=true', 400);
89
102
  }
@@ -92,14 +105,6 @@ export function normalizeExternalAgentRunRequest(body = {}) {
92
105
  throw createRequestError('message is required unless openOnly=true', 400);
93
106
  }
94
107
 
95
- if (isOpenOnly && githubUrl) {
96
- throw createRequestError('githubUrl is not supported when openOnly=true', 400);
97
- }
98
-
99
- if (isOpenOnly && (createBranch || createPR)) {
100
- throw createRequestError('createBranch and createPR are not supported when openOnly=true', 400);
101
- }
102
-
103
108
  if (isOpenOnly && callbackConfig) {
104
109
  throw createRequestError('callback is not supported when openOnly=true', 400);
105
110
  }
@@ -113,21 +118,16 @@ export function normalizeExternalAgentRunRequest(body = {}) {
113
118
  }
114
119
 
115
120
  return {
116
- githubUrl,
117
121
  projectPath: resolvedProjectPath,
118
122
  message,
119
123
  normalizedMessage,
120
124
  provider,
121
125
  model,
122
- githubToken,
123
- branchName,
124
126
  sessionId,
125
127
  normalizedSessionId,
126
128
  openOnly,
127
129
  requestedOpenOnly,
128
130
  isOpenOnly,
129
- createBranch,
130
- createPR,
131
131
  stream,
132
132
  requestedCleanup,
133
133
  cleanup,
@@ -156,262 +156,6 @@ export function normalizeExternalAgentAbortRequest(body = {}) {
156
156
  };
157
157
  }
158
158
 
159
- async function getGitRemoteUrl(repoPath) {
160
- return new Promise((resolve, reject) => {
161
- const gitProcess = spawn('git', ['config', '--get', 'remote.origin.url'], {
162
- cwd: repoPath,
163
- stdio: ['pipe', 'pipe', 'pipe']
164
- });
165
-
166
- let stdout = '';
167
- let stderr = '';
168
-
169
- gitProcess.stdout.on('data', (data) => {
170
- stdout += data.toString();
171
- });
172
-
173
- gitProcess.stderr.on('data', (data) => {
174
- stderr += data.toString();
175
- });
176
-
177
- gitProcess.on('close', (code) => {
178
- if (code === 0) {
179
- resolve(stdout.trim());
180
- } else {
181
- reject(new Error(`Failed to get git remote: ${stderr}`));
182
- }
183
- });
184
-
185
- gitProcess.on('error', (error) => {
186
- reject(new Error(`Failed to execute git: ${error.message}`));
187
- });
188
- });
189
- }
190
-
191
- function normalizeGitHubUrl(url) {
192
- let normalized = url.replace(/\.git$/, '');
193
- normalized = normalized.replace(/^git@github\.com:/, 'https://github.com/');
194
- normalized = normalized.replace(/\/$/, '');
195
- return normalized.toLowerCase();
196
- }
197
-
198
- function parseGitHubUrl(url) {
199
- const match = url.match(/github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/);
200
- if (!match) {
201
- throw new Error('Invalid GitHub URL format');
202
- }
203
-
204
- return {
205
- owner: match[1],
206
- repo: match[2].replace(/\.git$/, '')
207
- };
208
- }
209
-
210
- function autogenerateBranchName(message) {
211
- let branchName = message
212
- .toLowerCase()
213
- .replace(/[^a-z0-9\s-]/g, '')
214
- .replace(/\s+/g, '-')
215
- .replace(/-+/g, '-')
216
- .replace(/^-|-$/g, '');
217
-
218
- if (!branchName) {
219
- branchName = 'task';
220
- }
221
-
222
- const timestamp = Date.now().toString(36).slice(-6);
223
- const suffix = `-${timestamp}`;
224
- const maxBaseLength = 50 - suffix.length;
225
-
226
- if (branchName.length > maxBaseLength) {
227
- branchName = branchName.substring(0, maxBaseLength);
228
- }
229
-
230
- branchName = branchName.replace(/-$/, '').replace(/^-+/, '');
231
-
232
- if (!branchName || branchName.startsWith('-')) {
233
- branchName = 'task';
234
- }
235
-
236
- branchName = `${branchName}${suffix}`;
237
-
238
- if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(branchName)) {
239
- return `branch-${timestamp}`;
240
- }
241
-
242
- return branchName;
243
- }
244
-
245
- function validateBranchName(branchName) {
246
- if (!branchName || branchName.trim() === '') {
247
- return { valid: false, error: 'Branch name cannot be empty' };
248
- }
249
-
250
- const invalidPatterns = [
251
- { pattern: /^\./, message: 'Branch name cannot start with a dot' },
252
- { pattern: /\.$/, message: 'Branch name cannot end with a dot' },
253
- { pattern: /\.\./, message: 'Branch name cannot contain consecutive dots (..)' },
254
- { pattern: /\s/, message: 'Branch name cannot contain spaces' },
255
- { pattern: /[~^:?*\[\\]/, message: 'Branch name cannot contain special characters: ~ ^ : ? * [ \\' },
256
- { pattern: /@{/, message: 'Branch name cannot contain @{' },
257
- { pattern: /\/$/, message: 'Branch name cannot end with a slash' },
258
- { pattern: /^\//, message: 'Branch name cannot start with a slash' },
259
- { pattern: /\/\//, message: 'Branch name cannot contain consecutive slashes' },
260
- { pattern: /\.lock$/, message: 'Branch name cannot end with .lock' }
261
- ];
262
-
263
- for (const { pattern, message } of invalidPatterns) {
264
- if (pattern.test(branchName)) {
265
- return { valid: false, error: message };
266
- }
267
- }
268
-
269
- if (/[\x00-\x1F\x7F]/.test(branchName)) {
270
- return { valid: false, error: 'Branch name cannot contain control characters' };
271
- }
272
-
273
- return { valid: true };
274
- }
275
-
276
- async function getCommitMessages(projectPath, limit = 5) {
277
- return new Promise((resolve, reject) => {
278
- const gitProcess = spawn('git', ['log', `-${limit}`, '--pretty=format:%s'], {
279
- cwd: projectPath,
280
- stdio: ['pipe', 'pipe', 'pipe']
281
- });
282
-
283
- let stdout = '';
284
- let stderr = '';
285
-
286
- gitProcess.stdout.on('data', (data) => {
287
- stdout += data.toString();
288
- });
289
-
290
- gitProcess.stderr.on('data', (data) => {
291
- stderr += data.toString();
292
- });
293
-
294
- gitProcess.on('close', (code) => {
295
- if (code === 0) {
296
- resolve(stdout.trim().split('\n').filter((msg) => msg.length > 0));
297
- } else {
298
- reject(new Error(`Failed to get commit messages: ${stderr}`));
299
- }
300
- });
301
-
302
- gitProcess.on('error', (error) => {
303
- reject(new Error(`Failed to execute git: ${error.message}`));
304
- });
305
- });
306
- }
307
-
308
- async function createGitHubPR(octokit, owner, repo, branchName, title, body, baseBranch = 'main') {
309
- const { data: pr } = await octokit.pulls.create({
310
- owner,
311
- repo,
312
- title,
313
- head: branchName,
314
- base: baseBranch,
315
- body
316
- });
317
-
318
- return {
319
- number: pr.number,
320
- url: pr.html_url
321
- };
322
- }
323
-
324
- async function cloneGitHubRepo(githubUrl, githubToken = null, projectPath) {
325
- return new Promise(async (resolve, reject) => {
326
- try {
327
- if (!githubUrl || !githubUrl.includes('github.com')) {
328
- throw new Error('Invalid GitHub URL');
329
- }
330
-
331
- const cloneDir = path.resolve(projectPath);
332
- let directoryExists = false;
333
- try {
334
- await fs.access(cloneDir);
335
- directoryExists = true;
336
- } catch {
337
- directoryExists = false;
338
- }
339
-
340
- if (directoryExists) {
341
- try {
342
- const existingUrl = await getGitRemoteUrl(cloneDir);
343
- const normalizedExisting = normalizeGitHubUrl(existingUrl);
344
- const normalizedRequested = normalizeGitHubUrl(githubUrl);
345
-
346
- if (normalizedExisting === normalizedRequested) {
347
- return resolve(cloneDir);
348
- }
349
-
350
- throw new Error(`Directory ${cloneDir} already exists with a different repository (${existingUrl}). Expected: ${githubUrl}`);
351
- } catch (error) {
352
- if (error.message && error.message.includes('different repository')) {
353
- throw error;
354
- }
355
- throw new Error(`Directory ${cloneDir} already exists but is not a valid git repository or git command failed`);
356
- }
357
- }
358
-
359
- await fs.mkdir(path.dirname(cloneDir), { recursive: true });
360
-
361
- let cloneUrl = githubUrl;
362
- if (githubToken) {
363
- cloneUrl = githubUrl.replace('https://github.com', `https://${githubToken}@github.com`);
364
- }
365
-
366
- const gitProcess = spawn('git', ['clone', '--depth', '1', cloneUrl, cloneDir], {
367
- stdio: ['pipe', 'pipe', 'pipe']
368
- });
369
-
370
- let stderr = '';
371
-
372
- gitProcess.stderr.on('data', (data) => {
373
- stderr += data.toString();
374
- });
375
-
376
- gitProcess.on('close', (code) => {
377
- if (code === 0) {
378
- resolve(cloneDir);
379
- } else {
380
- reject(new Error(`Git clone failed: ${stderr}`));
381
- }
382
- });
383
-
384
- gitProcess.on('error', (error) => {
385
- reject(new Error(`Failed to execute git: ${error.message}`));
386
- });
387
- } catch (error) {
388
- reject(error);
389
- }
390
- });
391
- }
392
-
393
- async function cleanupProject(projectPath, sessionId = null) {
394
- try {
395
- if (!projectPath.includes('.claude/external-projects')) {
396
- console.warn('Refusing to clean up non-external project:', projectPath);
397
- return;
398
- }
399
-
400
- await fs.rm(projectPath, { recursive: true, force: true });
401
-
402
- if (sessionId) {
403
- try {
404
- const sessionPath = path.join(os.homedir(), '.claude', 'sessions', sessionId);
405
- await fs.rm(sessionPath, { recursive: true, force: true });
406
- } catch (error) {
407
- console.error('Failed to clean up session directory:', error.message);
408
- }
409
- }
410
- } catch (error) {
411
- console.error('Failed to clean up project:', error);
412
- }
413
- }
414
-
415
159
  export class SSEStreamWriter {
416
160
  constructor(res) {
417
161
  this.res = res;
@@ -607,7 +351,7 @@ export class CallbackCaptureWriter {
607
351
  }
608
352
  }
609
353
 
610
- export class TeeWriter {
354
+ class TeeWriter {
611
355
  constructor(primaryWriter, secondaryWriter) {
612
356
  this.primaryWriter = primaryWriter;
613
357
  this.secondaryWriter = secondaryWriter;
@@ -641,6 +385,14 @@ export class NoopWriter {
641
385
  end() {}
642
386
  }
643
387
 
388
+ async function ensureProjectPathExists(projectPath) {
389
+ try {
390
+ await fs.access(projectPath);
391
+ } catch {
392
+ throw new Error(`Project path does not exist: ${projectPath}`);
393
+ }
394
+ }
395
+
644
396
  async function runProviderSession({ provider, message, images, finalProjectPath, sessionId, model, writer }) {
645
397
  if (provider === 'claude') {
646
398
  await queryClaudeSDK(message, {
@@ -690,159 +442,14 @@ async function runProviderSession({ provider, message, images, finalProjectPath,
690
442
  }
691
443
  }
692
444
 
693
- async function maybeCreateBranchAndPR({
694
- user,
695
- githubToken,
696
- githubUrl,
697
- finalProjectPath,
698
- branchName,
699
- message,
700
- createBranch,
701
- createPR,
702
- transportWriter
703
- }) {
704
- let branchInfo = null;
705
- let prInfo = null;
706
-
707
- if (!createBranch && !createPR) {
708
- return { branchInfo, prInfo };
709
- }
710
-
711
- try {
712
- const tokenToUse = githubToken || githubTokensDb.getActiveGithubToken(user?.id);
713
- if (!tokenToUse) {
714
- throw new Error('GitHub token required for branch/PR creation. Please configure a GitHub token in settings.');
715
- }
716
-
717
- const octokit = new Octokit({ auth: tokenToUse });
718
- let repoUrl = githubUrl;
719
-
720
- if (!repoUrl) {
721
- repoUrl = await getGitRemoteUrl(finalProjectPath);
722
- if (!repoUrl.includes('github.com')) {
723
- throw new Error('Project does not have a GitHub remote configured');
724
- }
725
- }
726
-
727
- const { owner, repo } = parseGitHubUrl(repoUrl);
728
- const finalBranchName = branchName || autogenerateBranchName(message);
729
-
730
- if (branchName) {
731
- const validation = validateBranchName(finalBranchName);
732
- if (!validation.valid) {
733
- throw new Error(`Invalid branch name: ${validation.error}`);
734
- }
735
- }
736
-
737
- if (createBranch) {
738
- await new Promise((resolve, reject) => {
739
- const checkoutProcess = spawn('git', ['checkout', '-b', finalBranchName], {
740
- cwd: finalProjectPath,
741
- stdio: 'pipe'
742
- });
743
-
744
- let stderr = '';
745
- checkoutProcess.stderr.on('data', (data) => {
746
- stderr += data.toString();
747
- });
748
-
749
- checkoutProcess.on('close', (code) => {
750
- if (code === 0) {
751
- resolve();
752
- return;
753
- }
754
-
755
- if (stderr.includes('already exists')) {
756
- const checkoutExisting = spawn('git', ['checkout', finalBranchName], {
757
- cwd: finalProjectPath,
758
- stdio: 'pipe'
759
- });
760
- checkoutExisting.on('close', (checkoutCode) => {
761
- if (checkoutCode === 0) {
762
- resolve();
763
- } else {
764
- reject(new Error(`Failed to checkout existing branch: ${stderr}`));
765
- }
766
- });
767
- return;
768
- }
769
-
770
- reject(new Error(`Failed to create branch: ${stderr}`));
771
- });
772
- });
773
-
774
- await new Promise((resolve, reject) => {
775
- const pushProcess = spawn('git', ['push', '-u', 'origin', finalBranchName], {
776
- cwd: finalProjectPath,
777
- stdio: 'pipe'
778
- });
779
-
780
- let stderr = '';
781
- pushProcess.stderr.on('data', (data) => {
782
- stderr += data.toString();
783
- });
784
-
785
- pushProcess.on('close', (code) => {
786
- if (code === 0 || stderr.includes('already exists') || stderr.includes('up-to-date')) {
787
- resolve();
788
- } else {
789
- reject(new Error(`Failed to push branch: ${stderr}`));
790
- }
791
- });
792
- });
793
-
794
- branchInfo = {
795
- name: finalBranchName,
796
- url: `https://github.com/${owner}/${repo}/tree/${finalBranchName}`
797
- };
798
- }
799
-
800
- if (createPR) {
801
- const commitMessages = await getCommitMessages(finalProjectPath, 5);
802
- const prTitle = commitMessages.length > 0 ? commitMessages[0] : message;
803
- let prBody = '## Changes\n\n';
804
- if (commitMessages.length > 0) {
805
- prBody += commitMessages.map((msg) => `- ${msg}`).join('\n');
806
- } else {
807
- prBody += `Agent task: ${message}`;
808
- }
809
- prBody += '\n\n---\n*This pull request was automatically created by Axhub Genie Agent.*';
810
-
811
- prInfo = await createGitHubPR(octokit, owner, repo, finalBranchName, prTitle, prBody, 'main');
812
- }
813
-
814
- if (branchInfo) {
815
- transportWriter?.send?.({
816
- type: 'github-branch',
817
- branch: branchInfo
818
- });
819
- }
820
-
821
- if (prInfo) {
822
- transportWriter?.send?.({
823
- type: 'github-pr',
824
- pullRequest: prInfo
825
- });
826
- }
827
- } catch (error) {
828
- transportWriter?.send?.({
829
- type: 'github-error',
830
- error: error.message
831
- });
832
-
833
- branchInfo = { error: error.message };
834
- prInfo = { error: error.message };
835
- }
836
-
837
- return { branchInfo, prInfo };
838
- }
839
-
840
445
  export async function runExternalAgentRequest({
841
446
  user,
842
447
  request,
843
448
  transportWriter,
844
449
  endTransportOnFinish = false
845
450
  }) {
451
+ void user;
452
+
846
453
  const normalized = request?.normalizedMessage !== undefined
847
454
  ? request
848
455
  : normalizeExternalAgentRunRequest(request);
@@ -870,32 +477,14 @@ export async function runExternalAgentRequest({
870
477
  return response;
871
478
  }
872
479
 
873
- let finalProjectPath = null;
874
- let branchInfo = null;
875
- let prInfo = null;
480
+ const finalProjectPath = resolveWorkingDirectory({ projectPath: normalized.projectPath });
876
481
  const callbackCaptureWriter = new CallbackCaptureWriter();
877
482
  const writer = new TeeWriter(transportWriter, callbackCaptureWriter);
878
483
  let callbackSessionId = normalized.normalizedSessionId;
879
484
  let callbackResult = null;
880
485
 
881
486
  try {
882
- if (normalized.githubUrl) {
883
- const tokenToUse = normalized.githubToken || githubTokensDb.getActiveGithubToken(user?.id);
884
- let targetPath = normalized.projectPath;
885
- if (!targetPath) {
886
- const repoHash = crypto.createHash('md5').update(normalized.githubUrl + Date.now()).digest('hex');
887
- targetPath = path.join(os.homedir(), '.claude', 'external-projects', repoHash);
888
- }
889
-
890
- finalProjectPath = await cloneGitHubRepo(normalized.githubUrl.trim(), tokenToUse, targetPath);
891
- } else {
892
- finalProjectPath = resolveWorkingDirectory({ projectPath: normalized.projectPath });
893
- try {
894
- await fs.access(finalProjectPath);
895
- } catch {
896
- throw new Error(`Project path does not exist: ${finalProjectPath}`);
897
- }
898
- }
487
+ await ensureProjectPathExists(finalProjectPath);
899
488
 
900
489
  try {
901
490
  await addProjectManually(finalProjectPath);
@@ -905,13 +494,9 @@ export async function runExternalAgentRequest({
905
494
  }
906
495
  }
907
496
 
908
- const statusMessage = normalized.normalizedSessionId
909
- ? (normalized.githubUrl ? 'Repository cloned and session resumed' : 'Session resumed')
910
- : (normalized.githubUrl ? 'Repository cloned and session started' : 'Session started');
911
-
912
497
  transportWriter?.send?.({
913
498
  type: 'status',
914
- message: statusMessage,
499
+ message: normalized.normalizedSessionId ? 'Session resumed' : 'Session started',
915
500
  projectPath: finalProjectPath
916
501
  });
917
502
 
@@ -936,19 +521,6 @@ export async function runExternalAgentRequest({
936
521
  throw new AgentSessionAbortedError();
937
522
  }
938
523
 
939
- ({ branchInfo, prInfo } = await maybeCreateBranchAndPR({
940
- user,
941
- githubToken: normalized.githubToken,
942
- githubUrl: normalized.githubUrl,
943
- finalProjectPath,
944
- branchName: normalized.branchName,
945
- message: normalized.message,
946
- createBranch: normalized.createBranch,
947
- createPR: normalized.createPR,
948
- transportWriter
949
- }));
950
-
951
- callbackSessionId = writer.getSessionId() || callbackCaptureWriter.getSessionId() || normalized.normalizedSessionId;
952
524
  const navigation = buildSessionNavigation(callbackSessionId);
953
525
  const response = {
954
526
  success: true,
@@ -960,20 +532,11 @@ export async function runExternalAgentRequest({
960
532
  projectPath: finalProjectPath
961
533
  };
962
534
 
963
- if (branchInfo) {
964
- response.branch = branchInfo;
965
- }
966
- if (prInfo) {
967
- response.pullRequest = prInfo;
968
- }
969
-
970
535
  callbackResult = {
971
536
  sessionPath: navigation.sessionPath,
972
537
  sessionUrl: navigation.sessionUrl,
973
538
  messages: response.messages,
974
- tokens: response.tokens,
975
- branch: branchInfo,
976
- pullRequest: prInfo
539
+ tokens: response.tokens
977
540
  };
978
541
 
979
542
  if (normalized.callbackConfig && shouldDeliverAgentCallback(normalized.callbackConfig, 'completed')) {
@@ -995,13 +558,6 @@ export async function runExternalAgentRequest({
995
558
  });
996
559
  }
997
560
 
998
- if (normalized.cleanup && normalized.githubUrl) {
999
- const sessionIdForCleanup = writer.getSessionId();
1000
- setTimeout(() => {
1001
- cleanupProject(finalProjectPath, sessionIdForCleanup);
1002
- }, 5000);
1003
- }
1004
-
1005
561
  if (endTransportOnFinish) {
1006
562
  transportWriter?.end?.();
1007
563
  }
@@ -1012,16 +568,9 @@ export async function runExternalAgentRequest({
1012
568
  callbackResult = {
1013
569
  ...buildSessionNavigation(callbackSessionId),
1014
570
  messages: callbackCaptureWriter.getAssistantMessages(),
1015
- tokens: callbackCaptureWriter.getTotalTokens(),
1016
- branch: branchInfo,
1017
- pullRequest: prInfo
571
+ tokens: callbackCaptureWriter.getTotalTokens()
1018
572
  };
1019
573
 
1020
- if (finalProjectPath && normalized.cleanup && normalized.githubUrl) {
1021
- const sessionIdForCleanup = writer.getSessionId();
1022
- await cleanupProject(finalProjectPath, sessionIdForCleanup);
1023
- }
1024
-
1025
574
  const terminalEvent = error instanceof AgentSessionAbortedError ? 'aborted' : 'errored';
1026
575
  if (normalized.callbackConfig && shouldDeliverAgentCallback(normalized.callbackConfig, terminalEvent)) {
1027
576
  const callbackError = terminalEvent === 'aborted'
@@ -39,3 +39,15 @@ test('normalizeExternalAgentRunRequest falls back to process cwd when no project
39
39
 
40
40
  assert.equal(normalized.projectPath, process.cwd());
41
41
  });
42
+
43
+ test('normalizeExternalAgentRunRequest rejects deprecated GitHub parameters', () => {
44
+ assert.throws(() => {
45
+ normalizeExternalAgentRunRequest({
46
+ provider: 'codex',
47
+ message: 'Inspect the repository',
48
+ githubUrl: 'https://github.com/example/repo'
49
+ });
50
+ }, {
51
+ message: /Unsupported parameters: githubUrl/
52
+ });
53
+ });