@axhub/genie 0.1.3 → 0.1.5
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/assets/index-Bue8nA1L.js +1249 -0
- package/dist/assets/index-CtRxrKDm.css +32 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/server/cli.js +11 -6
- package/server/database/auth.db +0 -0
- package/server/index.js +8 -18
- package/server/projects.js +3 -72
- package/server/routes/agent.js +9 -54
- package/server/routes/commands.js +1 -1
- package/shared/modelConstants.js +0 -14
- package/dist/assets/index-BYKlB9hp.css +0 -32
- package/dist/assets/index-YzZ559FA.js +0 -1249
- package/dist/icons/opencode-white.svg +0 -4
- package/dist/icons/opencode.svg +0 -10
- package/server/opencode-manager.js +0 -605
- package/server/opencode-sdk.js +0 -474
- package/server/routes/opencode.js +0 -99
package/server/projects.js
CHANGED
|
@@ -66,7 +66,6 @@ import sqlite3 from 'sqlite3';
|
|
|
66
66
|
import { open } from 'sqlite';
|
|
67
67
|
import os from 'os';
|
|
68
68
|
import { parseCodexTokenCountInfo } from './utils/codexTokenUsage.js';
|
|
69
|
-
import { listOpencodeSessions, getOpencodeSessionMessages, deleteOpencodeSession } from './opencode-manager.js';
|
|
70
69
|
|
|
71
70
|
// Import TaskMaster detection functions
|
|
72
71
|
async function detectTaskMasterFolder(projectPath) {
|
|
@@ -468,14 +467,6 @@ async function getProjects(progressCallback = null) {
|
|
|
468
467
|
project.codexSessions = [];
|
|
469
468
|
}
|
|
470
469
|
|
|
471
|
-
// Also fetch OpenCode sessions for this project
|
|
472
|
-
try {
|
|
473
|
-
project.opencodeSessions = await getOpencodeSessions(actualProjectDir);
|
|
474
|
-
} catch (e) {
|
|
475
|
-
console.warn(`Could not load OpenCode sessions for project ${entry.name}:`, e.message);
|
|
476
|
-
project.opencodeSessions = [];
|
|
477
|
-
}
|
|
478
|
-
|
|
479
470
|
// Add TaskMaster detection
|
|
480
471
|
try {
|
|
481
472
|
const taskMasterResult = await detectTaskMasterFolder(actualProjectDir);
|
|
@@ -544,8 +535,7 @@ async function getProjects(progressCallback = null) {
|
|
|
544
535
|
isManuallyAdded: true,
|
|
545
536
|
sessions: [],
|
|
546
537
|
cursorSessions: [],
|
|
547
|
-
codexSessions: []
|
|
548
|
-
opencodeSessions: []
|
|
538
|
+
codexSessions: []
|
|
549
539
|
};
|
|
550
540
|
|
|
551
541
|
// Try to fetch Cursor sessions for manual projects too
|
|
@@ -562,13 +552,6 @@ async function getProjects(progressCallback = null) {
|
|
|
562
552
|
console.warn(`Could not load Codex sessions for manual project ${projectName}:`, e.message);
|
|
563
553
|
}
|
|
564
554
|
|
|
565
|
-
// Try to fetch OpenCode sessions for manual projects too
|
|
566
|
-
try {
|
|
567
|
-
project.opencodeSessions = await getOpencodeSessions(actualProjectDir);
|
|
568
|
-
} catch (e) {
|
|
569
|
-
console.warn(`Could not load OpenCode sessions for manual project ${projectName}:`, e.message);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
555
|
// Add TaskMaster detection for manual projects
|
|
573
556
|
try {
|
|
574
557
|
const taskMasterResult = await detectTaskMasterFolder(actualProjectDir);
|
|
@@ -1080,20 +1063,6 @@ async function deleteProject(projectName, force = false) {
|
|
|
1080
1063
|
console.warn('Failed to delete Codex sessions:', err.message);
|
|
1081
1064
|
}
|
|
1082
1065
|
|
|
1083
|
-
// Delete all OpenCode sessions associated with this project
|
|
1084
|
-
try {
|
|
1085
|
-
const opencodeSessions = await getOpencodeSessions(projectPath, { limit: 0 });
|
|
1086
|
-
for (const session of opencodeSessions) {
|
|
1087
|
-
try {
|
|
1088
|
-
await deleteOpencodeSession(session.id, { directory: projectPath });
|
|
1089
|
-
} catch (err) {
|
|
1090
|
-
console.warn(`Failed to delete OpenCode session ${session.id}:`, err.message);
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
} catch (err) {
|
|
1094
|
-
console.warn('Failed to delete OpenCode sessions:', err.message);
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
1066
|
// Delete Cursor sessions directory if it exists
|
|
1098
1067
|
try {
|
|
1099
1068
|
const hash = crypto.createHash('md5').update(projectPath).digest('hex');
|
|
@@ -1160,9 +1129,7 @@ async function addProjectManually(projectPath, displayName = null) {
|
|
|
1160
1129
|
displayName: displayName || await generateDisplayName(projectName, absolutePath),
|
|
1161
1130
|
isManuallyAdded: true,
|
|
1162
1131
|
sessions: [],
|
|
1163
|
-
cursorSessions: []
|
|
1164
|
-
codexSessions: [],
|
|
1165
|
-
opencodeSessions: []
|
|
1132
|
+
cursorSessions: []
|
|
1166
1133
|
};
|
|
1167
1134
|
}
|
|
1168
1135
|
|
|
@@ -1354,28 +1321,6 @@ async function getCodexSessions(projectPath, options = {}) {
|
|
|
1354
1321
|
}
|
|
1355
1322
|
}
|
|
1356
1323
|
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
let opencodeDiscoveryUnavailable = false;
|
|
1360
|
-
|
|
1361
|
-
async function getOpencodeSessions(projectPath, options = {}) {
|
|
1362
|
-
const { limit = 5 } = options;
|
|
1363
|
-
|
|
1364
|
-
if (opencodeDiscoveryUnavailable) {
|
|
1365
|
-
return [];
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
try {
|
|
1369
|
-
const sessions = await listOpencodeSessions(projectPath, { limit });
|
|
1370
|
-
opencodeDiscoveryUnavailable = false;
|
|
1371
|
-
return sessions;
|
|
1372
|
-
} catch (error) {
|
|
1373
|
-
opencodeDiscoveryUnavailable = true;
|
|
1374
|
-
console.error('Error fetching OpenCode sessions:', error.message);
|
|
1375
|
-
return [];
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
1324
|
// Parse a Codex session JSONL file to extract metadata
|
|
1380
1325
|
async function parseCodexSessionFile(filePath) {
|
|
1381
1326
|
try {
|
|
@@ -1677,17 +1622,6 @@ async function getCodexSessionMessages(sessionId, limit = null, offset = 0) {
|
|
|
1677
1622
|
}
|
|
1678
1623
|
}
|
|
1679
1624
|
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
async function getOpencodeSessionMessagesFromManager(sessionId, options = {}) {
|
|
1683
|
-
try {
|
|
1684
|
-
return await getOpencodeSessionMessages(sessionId, options);
|
|
1685
|
-
} catch (error) {
|
|
1686
|
-
console.error(`Error reading OpenCode session messages for ${sessionId}:`, error);
|
|
1687
|
-
return { messages: [], total: 0, hasMore: false };
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
1625
|
async function deleteCodexSession(sessionId) {
|
|
1692
1626
|
try {
|
|
1693
1627
|
const codexSessionsDir = path.join(os.homedir(), '.codex', 'sessions');
|
|
@@ -1741,8 +1675,5 @@ export {
|
|
|
1741
1675
|
clearProjectDirectoryCache,
|
|
1742
1676
|
getCodexSessions,
|
|
1743
1677
|
getCodexSessionMessages,
|
|
1744
|
-
deleteCodexSession
|
|
1745
|
-
getOpencodeSessions,
|
|
1746
|
-
getOpencodeSessionMessagesFromManager,
|
|
1747
|
-
deleteOpencodeSession
|
|
1678
|
+
deleteCodexSession
|
|
1748
1679
|
};
|
package/server/routes/agent.js
CHANGED
|
@@ -9,52 +9,17 @@ import { addProjectManually } from '../projects.js';
|
|
|
9
9
|
import { queryClaudeSDK } from '../claude-sdk.js';
|
|
10
10
|
import { spawnCursor } from '../cursor-cli.js';
|
|
11
11
|
import { queryCodex } from '../openai-codex.js';
|
|
12
|
-
import { queryOpencode } from '../opencode-sdk.js';
|
|
13
12
|
import { Octokit } from '@octokit/rest';
|
|
14
|
-
import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS
|
|
13
|
+
import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS } from '../../shared/modelConstants.js';
|
|
15
14
|
import { IS_PLATFORM } from '../constants/config.js';
|
|
16
15
|
|
|
17
16
|
const router = express.Router();
|
|
18
17
|
|
|
19
|
-
function
|
|
20
|
-
if (Array.isArray(value)) {
|
|
21
|
-
return value[0]?.split(',')[0]?.trim() || null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (typeof value === 'string') {
|
|
25
|
-
return value.split(',')[0].trim() || null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function getRequestOrigin(req) {
|
|
32
|
-
if (!req) {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const forwardedProto = getForwardedHeaderValue(req.headers['x-forwarded-proto']);
|
|
37
|
-
const forwardedHost = getForwardedHeaderValue(req.headers['x-forwarded-host']);
|
|
38
|
-
if (forwardedProto && forwardedHost) {
|
|
39
|
-
return `${forwardedProto}://${forwardedHost}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const host = req.get('host');
|
|
43
|
-
if (host) {
|
|
44
|
-
return `${req.protocol || 'http'}://${host}`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function buildSessionNavigation(sessionId, req) {
|
|
18
|
+
function buildSessionNavigation(sessionId) {
|
|
51
19
|
const normalizedSessionId = typeof sessionId === 'string' && sessionId.trim() ? sessionId.trim() : null;
|
|
52
20
|
const sessionPath = normalizedSessionId ? `/session/${normalizedSessionId}` : null;
|
|
53
|
-
const configuredFrontendUrl = (process.env.FRONTEND_URL || '').trim().replace(/\/+$/, '');
|
|
54
|
-
const requestOrigin = getRequestOrigin(req);
|
|
55
21
|
const frontendPort = process.env.VITE_PORT || '5173';
|
|
56
|
-
const
|
|
57
|
-
const sessionUrl = normalizedSessionId ? `${frontendBaseUrl}${sessionPath}` : null;
|
|
22
|
+
const sessionUrl = normalizedSessionId ? `http://localhost:${frontendPort}${sessionPath}` : null;
|
|
58
23
|
|
|
59
24
|
return {
|
|
60
25
|
sessionPath,
|
|
@@ -696,7 +661,7 @@ class ResponseCollector {
|
|
|
696
661
|
* @param {string} sessionId - (Optional) Existing session ID to resume.
|
|
697
662
|
* If provided, the request continues that session instead of creating a new one.
|
|
698
663
|
*
|
|
699
|
-
* @param {string} provider - (Optional) AI provider to use. Options: 'claude' | 'cursor'
|
|
664
|
+
* @param {string} provider - (Optional) AI provider to use. Options: 'claude' | 'cursor'
|
|
700
665
|
* Default: 'claude'
|
|
701
666
|
*
|
|
702
667
|
* @param {boolean} stream - (Optional) Enable Server-Sent Events (SSE) streaming for real-time updates.
|
|
@@ -814,7 +779,7 @@ class ResponseCollector {
|
|
|
814
779
|
* Input Validations (400 Bad Request):
|
|
815
780
|
* - Either githubUrl OR projectPath must be provided (not neither)
|
|
816
781
|
* - message must be non-empty string
|
|
817
|
-
* - provider must be 'claude'
|
|
782
|
+
* - provider must be 'claude' or 'cursor'
|
|
818
783
|
* - createBranch/createPR requires githubUrl OR projectPath (not neither)
|
|
819
784
|
* - branchName must pass Git naming rules (if provided)
|
|
820
785
|
*
|
|
@@ -918,8 +883,8 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
918
883
|
const isOpenOnly = requestedOpenOnly || (!normalizedMessage && !!normalizedSessionId);
|
|
919
884
|
|
|
920
885
|
// Validate inputs
|
|
921
|
-
if (!['claude', 'cursor', 'codex'
|
|
922
|
-
return res.status(400).json({ error: 'provider must be "claude", "cursor",
|
|
886
|
+
if (!['claude', 'cursor', 'codex'].includes(provider)) {
|
|
887
|
+
return res.status(400).json({ error: 'provider must be "claude", "cursor", or "codex"' });
|
|
923
888
|
}
|
|
924
889
|
|
|
925
890
|
if (sessionId !== undefined && (typeof sessionId !== 'string' || !sessionId.trim())) {
|
|
@@ -957,7 +922,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
957
922
|
}
|
|
958
923
|
|
|
959
924
|
if (isOpenOnly) {
|
|
960
|
-
const { sessionPath, sessionUrl } = buildSessionNavigation(normalizedSessionId
|
|
925
|
+
const { sessionPath, sessionUrl } = buildSessionNavigation(normalizedSessionId);
|
|
961
926
|
const response = {
|
|
962
927
|
success: true,
|
|
963
928
|
openOnly: true,
|
|
@@ -1097,16 +1062,6 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
1097
1062
|
model: model || CODEX_MODELS.DEFAULT,
|
|
1098
1063
|
permissionMode: 'bypassPermissions'
|
|
1099
1064
|
}, writer);
|
|
1100
|
-
} else if (provider === 'opencode') {
|
|
1101
|
-
console.log('🧠 Starting OpenCode session');
|
|
1102
|
-
|
|
1103
|
-
await queryOpencode(normalizedMessage, {
|
|
1104
|
-
projectPath: finalProjectPath,
|
|
1105
|
-
cwd: finalProjectPath,
|
|
1106
|
-
sessionId: normalizedSessionId,
|
|
1107
|
-
model: model || OPENCODE_MODELS.DEFAULT,
|
|
1108
|
-
permissionMode: 'bypassPermissions'
|
|
1109
|
-
}, writer);
|
|
1110
1065
|
}
|
|
1111
1066
|
|
|
1112
1067
|
// Handle GitHub branch and PR creation after successful agent completion
|
|
@@ -1299,7 +1254,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
1299
1254
|
const assistantMessages = writer.getAssistantMessages();
|
|
1300
1255
|
const tokenSummary = writer.getTotalTokens();
|
|
1301
1256
|
const resolvedSessionId = writer.getSessionId() || normalizedSessionId;
|
|
1302
|
-
const { sessionPath, sessionUrl } = buildSessionNavigation(resolvedSessionId
|
|
1257
|
+
const { sessionPath, sessionUrl } = buildSessionNavigation(resolvedSessionId);
|
|
1303
1258
|
|
|
1304
1259
|
const response = {
|
|
1305
1260
|
success: true,
|
|
@@ -213,7 +213,7 @@ Custom commands can be created in:
|
|
|
213
213
|
// Read version from package.json
|
|
214
214
|
const packageJsonPath = path.join(path.dirname(__dirname), '..', 'package.json');
|
|
215
215
|
let version = 'unknown';
|
|
216
|
-
let packageName = '
|
|
216
|
+
let packageName = '@axhub/genie';
|
|
217
217
|
|
|
218
218
|
try {
|
|
219
219
|
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
package/shared/modelConstants.js
CHANGED
|
@@ -63,17 +63,3 @@ export const CODEX_MODELS = {
|
|
|
63
63
|
|
|
64
64
|
DEFAULT: 'gpt-5.2-codex'
|
|
65
65
|
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* OpenCode Models
|
|
70
|
-
*/
|
|
71
|
-
export const OPENCODE_MODELS = {
|
|
72
|
-
OPTIONS: [
|
|
73
|
-
{ value: 'opencode/gpt-5-nano', label: 'GPT-5 Nano (OpenCode)' },
|
|
74
|
-
{ value: 'openai/gpt-5.2', label: 'GPT-5.2 (OpenAI)' },
|
|
75
|
-
{ value: 'anthropic/claude-sonnet-4.5', label: 'Claude Sonnet 4.5 (Anthropic)' }
|
|
76
|
-
],
|
|
77
|
-
|
|
78
|
-
DEFAULT: 'opencode/gpt-5-nano'
|
|
79
|
-
};
|