@ian2018cs/agenthub 0.1.25 → 0.1.27
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-1dh35QcB.js → index-CdP2lKlj.js} +14 -14
- package/dist/index.html +1 -1
- package/package.json +2 -2
- package/server/claude-sdk.js +37 -25
- package/server/database/db.js +47 -0
- package/server/index.js +165 -76
- package/server/routes/skills.js +133 -81
package/dist/index.html
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
<!-- Prevent zoom on iOS -->
|
|
27
27
|
<meta name="format-detection" content="telephone=no" />
|
|
28
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/assets/index-CdP2lKlj.js"></script>
|
|
29
29
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-BeVl62c0.js">
|
|
30
30
|
<link rel="modulepreload" crossorigin href="/assets/vendor-codemirror-C_VWDoZS.js">
|
|
31
31
|
<link rel="modulepreload" crossorigin href="/assets/vendor-utils-00TdZexr.js">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ian2018cs/agenthub",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"description": "A web-based UI for AI Agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@anthropic-ai/claude-agent-sdk": "^0.2.
|
|
47
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.61",
|
|
48
48
|
"@codemirror/lang-css": "^6.3.1",
|
|
49
49
|
"@codemirror/lang-html": "^6.4.9",
|
|
50
50
|
"@codemirror/lang-javascript": "^6.2.4",
|
package/server/claude-sdk.js
CHANGED
|
@@ -263,6 +263,18 @@ function getAllSessions() {
|
|
|
263
263
|
return Array.from(activeSessions.keys());
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
+
// Periodic cleanup of stale sessions (every 5 minutes, remove sessions older than 2 hours)
|
|
267
|
+
const SESSION_MAX_AGE_MS = 2 * 60 * 60 * 1000;
|
|
268
|
+
setInterval(() => {
|
|
269
|
+
const now = Date.now();
|
|
270
|
+
for (const [sessionId, session] of activeSessions) {
|
|
271
|
+
if (now - session.startTime > SESSION_MAX_AGE_MS) {
|
|
272
|
+
console.log(`[WARN] Cleaning up stale active session: ${sessionId} (age: ${Math.round((now - session.startTime) / 60000)}min)`);
|
|
273
|
+
activeSessions.delete(sessionId);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}, 5 * 60 * 1000);
|
|
277
|
+
|
|
266
278
|
/**
|
|
267
279
|
* Transforms SDK messages to WebSocket format expected by frontend
|
|
268
280
|
* @param {Object} sdkMessage - SDK message object
|
|
@@ -662,32 +674,32 @@ async function queryClaudeSDK(command, options = {}, ws) {
|
|
|
662
674
|
cacheCreationTokens: hasPreciseCacheData ? undefined : cacheCreationTokens
|
|
663
675
|
});
|
|
664
676
|
|
|
665
|
-
//
|
|
666
|
-
usageDb.insertRecord({
|
|
667
|
-
user_uuid: userUuid,
|
|
668
|
-
session_id: capturedSessionId,
|
|
669
|
-
model: normalizedModel,
|
|
670
|
-
raw_model: modelKey,
|
|
671
|
-
input_tokens: inputTokens,
|
|
672
|
-
output_tokens: outputTokens,
|
|
673
|
-
cache_read_tokens: cacheReadTokens,
|
|
674
|
-
cache_creation_tokens: cacheCreationTokens,
|
|
675
|
-
cost_usd: cost,
|
|
676
|
-
source: 'sdk'
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
// Update daily summary
|
|
677
|
+
// Atomically insert usage record + update daily summary in a single transaction
|
|
680
678
|
const today = new Date().toISOString().split('T')[0];
|
|
681
|
-
usageDb.
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
679
|
+
usageDb.recordUsageTransaction(
|
|
680
|
+
{
|
|
681
|
+
user_uuid: userUuid,
|
|
682
|
+
session_id: capturedSessionId,
|
|
683
|
+
model: normalizedModel,
|
|
684
|
+
raw_model: modelKey,
|
|
685
|
+
input_tokens: inputTokens,
|
|
686
|
+
output_tokens: outputTokens,
|
|
687
|
+
cache_read_tokens: cacheReadTokens,
|
|
688
|
+
cache_creation_tokens: cacheCreationTokens,
|
|
689
|
+
cost_usd: cost,
|
|
690
|
+
source: 'sdk'
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
user_uuid: userUuid,
|
|
694
|
+
date: today,
|
|
695
|
+
model: normalizedModel,
|
|
696
|
+
total_input_tokens: inputTokens,
|
|
697
|
+
total_output_tokens: outputTokens,
|
|
698
|
+
total_cost_usd: cost,
|
|
699
|
+
session_count: 0, // Session count updated separately
|
|
700
|
+
request_count: 1
|
|
701
|
+
}
|
|
702
|
+
);
|
|
691
703
|
|
|
692
704
|
console.log(`Recorded usage for user ${userUuid}: ${normalizedModel}, cost: $${cost.toFixed(6)}`);
|
|
693
705
|
}
|
package/server/database/db.js
CHANGED
|
@@ -45,6 +45,11 @@ try {
|
|
|
45
45
|
// Create database connection
|
|
46
46
|
const db = new Database(DB_PATH);
|
|
47
47
|
|
|
48
|
+
// Optimize SQLite for concurrent access
|
|
49
|
+
db.pragma('journal_mode = WAL'); // Write-Ahead Logging for concurrent reads during writes
|
|
50
|
+
db.pragma('busy_timeout = 5000'); // Wait up to 5s when database is locked instead of failing immediately
|
|
51
|
+
db.pragma('synchronous = NORMAL'); // Balance between safety and performance with WAL
|
|
52
|
+
|
|
48
53
|
// Show app installation path prominently
|
|
49
54
|
const appInstallPath = path.join(__dirname, '../..');
|
|
50
55
|
console.log('');
|
|
@@ -675,6 +680,48 @@ const usageDb = {
|
|
|
675
680
|
}
|
|
676
681
|
},
|
|
677
682
|
|
|
683
|
+
// Atomic transaction: insert usage record + upsert daily summary
|
|
684
|
+
recordUsageTransaction: db.transaction((record, summary) => {
|
|
685
|
+
const insertStmt = db.prepare(`
|
|
686
|
+
INSERT INTO usage_records (user_uuid, session_id, model, raw_model, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, cost_usd, source, created_at)
|
|
687
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
688
|
+
`);
|
|
689
|
+
insertStmt.run(
|
|
690
|
+
record.user_uuid,
|
|
691
|
+
record.session_id || null,
|
|
692
|
+
record.model,
|
|
693
|
+
record.raw_model || null,
|
|
694
|
+
record.input_tokens || 0,
|
|
695
|
+
record.output_tokens || 0,
|
|
696
|
+
record.cache_read_tokens || 0,
|
|
697
|
+
record.cache_creation_tokens || 0,
|
|
698
|
+
record.cost_usd || 0,
|
|
699
|
+
record.source || 'sdk',
|
|
700
|
+
record.created_at || new Date().toISOString()
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
const upsertStmt = db.prepare(`
|
|
704
|
+
INSERT INTO usage_daily_summary (user_uuid, date, model, total_input_tokens, total_output_tokens, total_cost_usd, session_count, request_count)
|
|
705
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
706
|
+
ON CONFLICT(user_uuid, date, model) DO UPDATE SET
|
|
707
|
+
total_input_tokens = total_input_tokens + excluded.total_input_tokens,
|
|
708
|
+
total_output_tokens = total_output_tokens + excluded.total_output_tokens,
|
|
709
|
+
total_cost_usd = total_cost_usd + excluded.total_cost_usd,
|
|
710
|
+
session_count = session_count + excluded.session_count,
|
|
711
|
+
request_count = request_count + excluded.request_count
|
|
712
|
+
`);
|
|
713
|
+
upsertStmt.run(
|
|
714
|
+
summary.user_uuid,
|
|
715
|
+
summary.date,
|
|
716
|
+
summary.model,
|
|
717
|
+
summary.total_input_tokens || 0,
|
|
718
|
+
summary.total_output_tokens || 0,
|
|
719
|
+
summary.total_cost_usd || 0,
|
|
720
|
+
summary.session_count || 0,
|
|
721
|
+
summary.request_count || 0
|
|
722
|
+
);
|
|
723
|
+
}),
|
|
724
|
+
|
|
678
725
|
// Get all users usage summary
|
|
679
726
|
getAllUsersSummary: () => {
|
|
680
727
|
try {
|