claudedesk 3.7.1 → 3.8.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/terminal-routes.d.ts.map +1 -1
- package/dist/api/terminal-routes.js +316 -5
- package/dist/api/terminal-routes.js.map +1 -1
- package/dist/api/workspace-routes.d.ts.map +1 -1
- package/dist/api/workspace-routes.js +222 -0
- package/dist/api/workspace-routes.js.map +1 -1
- package/dist/client/assets/index-Ca-cAQep.js +7846 -0
- package/dist/client/assets/index-WKt2pA41.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/config/workspaces.d.ts +77 -0
- package/dist/config/workspaces.d.ts.map +1 -1
- package/dist/config/workspaces.js +97 -0
- package/dist/config/workspaces.js.map +1 -1
- package/dist/core/allocator-manager.d.ts +98 -0
- package/dist/core/allocator-manager.d.ts.map +1 -0
- package/dist/core/allocator-manager.js +401 -0
- package/dist/core/allocator-manager.js.map +1 -0
- package/dist/core/github-integration.d.ts +30 -1
- package/dist/core/github-integration.d.ts.map +1 -1
- package/dist/core/github-integration.js +153 -10
- package/dist/core/github-integration.js.map +1 -1
- package/dist/core/github-oauth.d.ts.map +1 -1
- package/dist/core/github-oauth.js +1 -0
- package/dist/core/github-oauth.js.map +1 -1
- package/dist/core/idea-manager.d.ts.map +1 -1
- package/dist/core/idea-manager.js +6 -2
- package/dist/core/idea-manager.js.map +1 -1
- package/dist/core/terminal-session.d.ts +1 -0
- package/dist/core/terminal-session.d.ts.map +1 -1
- package/dist/core/terminal-session.js +48 -2
- package/dist/core/terminal-session.js.map +1 -1
- package/dist/core/token-encryption.d.ts +35 -0
- package/dist/core/token-encryption.d.ts.map +1 -0
- package/dist/core/token-encryption.js +162 -0
- package/dist/core/token-encryption.js.map +1 -0
- package/package.json +1 -1
- package/dist/client/assets/index-C1EMDno6.js +0 -7846
- package/dist/client/assets/index-DyFFEzMu.css +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-routes.d.ts","sourceRoot":"","sources":["../../src/api/terminal-routes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"terminal-routes.d.ts","sourceRoot":"","sources":["../../src/api/terminal-routes.ts"],"names":[],"mappings":"AAoRA,eAAO,MAAM,cAAc,4CAAW,CAAC"}
|
|
@@ -15,6 +15,7 @@ import { gitlabIntegration } from '../core/gitlab-integration.js';
|
|
|
15
15
|
import { usageManager } from '../core/usage-manager.js';
|
|
16
16
|
import { settingsManager } from '../config/settings.js';
|
|
17
17
|
import { queryClaudeQuota, clearQuotaCache } from '../core/claude-usage-query.js';
|
|
18
|
+
import { allocatorManager } from '../core/allocator-manager.js';
|
|
18
19
|
import { contextManager } from '../core/context-manager.js';
|
|
19
20
|
/**
|
|
20
21
|
* Try to refresh a GitLab token using the refresh token.
|
|
@@ -356,6 +357,16 @@ terminalRouter.post('/sessions', (req, res) => {
|
|
|
356
357
|
return;
|
|
357
358
|
}
|
|
358
359
|
}
|
|
360
|
+
// Budget: block new sessions if degradation is active
|
|
361
|
+
const budgetResult = allocatorManager.checkBudgetLimits();
|
|
362
|
+
if (budgetResult.activeDegradations.some((d) => d.type === 'block-new-sessions')) {
|
|
363
|
+
res.status(429).json({
|
|
364
|
+
success: false,
|
|
365
|
+
error: 'New sessions blocked due to high API usage. Wait for quota to reset or adjust budget settings.',
|
|
366
|
+
budgetBlocked: true,
|
|
367
|
+
});
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
359
370
|
const session = terminalSessionManager.createSession(ids, worktreeOptions);
|
|
360
371
|
// Set handoff summary if provided (e.g., from idea promotion)
|
|
361
372
|
if (handoffSummary && typeof handoffSummary === 'string') {
|
|
@@ -903,6 +914,24 @@ terminalRouter.patch('/sessions/:id/mode', (req, res) => {
|
|
|
903
914
|
res.status(400).json({ success: false, error: errorMsg });
|
|
904
915
|
}
|
|
905
916
|
});
|
|
917
|
+
// Set session model override (budget allocator / manual)
|
|
918
|
+
terminalRouter.patch('/sessions/:id/model', (req, res) => {
|
|
919
|
+
try {
|
|
920
|
+
const { id } = req.params;
|
|
921
|
+
const { model } = req.body; // string or null to clear
|
|
922
|
+
const session = terminalSessionManager.getSession(id);
|
|
923
|
+
if (!session) {
|
|
924
|
+
res.status(404).json({ success: false, error: 'Session not found' });
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
session.modelOverride = model || undefined;
|
|
928
|
+
res.json({ success: true, data: { model: session.modelOverride } });
|
|
929
|
+
}
|
|
930
|
+
catch (error) {
|
|
931
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
932
|
+
res.status(400).json({ success: false, error: errorMsg });
|
|
933
|
+
}
|
|
934
|
+
});
|
|
906
935
|
// Export session as markdown or JSON
|
|
907
936
|
terminalRouter.get('/sessions/:id/export', (req, res) => {
|
|
908
937
|
try {
|
|
@@ -2463,9 +2492,21 @@ terminalRouter.post('/sessions/:id/generate-pr-content', async (req, res) => {
|
|
|
2463
2492
|
commitMessages = '';
|
|
2464
2493
|
}
|
|
2465
2494
|
}
|
|
2466
|
-
// Get file changes summary (diff stat)
|
|
2495
|
+
// Get file changes summary (diff stat) - include both committed and uncommitted changes
|
|
2467
2496
|
let fileChanges = '';
|
|
2468
2497
|
let diffSample = '';
|
|
2498
|
+
// First check for uncommitted changes (staged + unstaged)
|
|
2499
|
+
let uncommittedChanges = '';
|
|
2500
|
+
try {
|
|
2501
|
+
uncommittedChanges = execSync('git diff HEAD --stat', {
|
|
2502
|
+
cwd: workingDir,
|
|
2503
|
+
encoding: 'utf-8',
|
|
2504
|
+
timeout: 10000,
|
|
2505
|
+
}).trim();
|
|
2506
|
+
}
|
|
2507
|
+
catch {
|
|
2508
|
+
// Ignore errors
|
|
2509
|
+
}
|
|
2469
2510
|
try {
|
|
2470
2511
|
const mergeBase = execSync(`git merge-base ${diffRef} HEAD`, {
|
|
2471
2512
|
cwd: workingDir,
|
|
@@ -2485,9 +2526,20 @@ terminalRouter.post('/sessions/:id/generate-pr-content', async (req, res) => {
|
|
|
2485
2526
|
}).trim();
|
|
2486
2527
|
// Limit to 3000 chars to avoid token limits
|
|
2487
2528
|
diffSample = rawDiff.length > 3000 ? rawDiff.substring(0, 3000) + '\n...(truncated)' : rawDiff;
|
|
2529
|
+
// If we have uncommitted changes, append them
|
|
2530
|
+
if (uncommittedChanges) {
|
|
2531
|
+
fileChanges = fileChanges + (fileChanges ? '\n\n=== Uncommitted changes ===\n' : '') + uncommittedChanges;
|
|
2532
|
+
diffSample = diffSample + '\n\n=== Uncommitted changes ===\n(see file stats above)';
|
|
2533
|
+
}
|
|
2488
2534
|
}
|
|
2489
2535
|
catch {
|
|
2490
|
-
|
|
2536
|
+
// If committed diff failed but we have uncommitted changes, use those
|
|
2537
|
+
if (uncommittedChanges) {
|
|
2538
|
+
fileChanges = uncommittedChanges;
|
|
2539
|
+
}
|
|
2540
|
+
else {
|
|
2541
|
+
fileChanges = '';
|
|
2542
|
+
}
|
|
2491
2543
|
}
|
|
2492
2544
|
// If scoped to files but got no results, fall back to unscoped
|
|
2493
2545
|
if (hasFileScope && !commitMessages && !fileChanges) {
|
|
@@ -3132,6 +3184,7 @@ terminalRouter.get('/sessions/:id/ship-summary', (req, res) => {
|
|
|
3132
3184
|
console.log(`[ship-summary] Checking for existing PR/MR. Branch: ${currentBranch}, baseBranch: ${baseBranch}, platform: ${creds.platform}, hasToken: ${!!creds.token}, workspaceId: ${workspace?.id}`);
|
|
3133
3185
|
if (creds.platform === 'github') {
|
|
3134
3186
|
// Try gh CLI first
|
|
3187
|
+
let ghWorked = false;
|
|
3135
3188
|
try {
|
|
3136
3189
|
const prJson = execSync(`gh pr view --json url,number,title,state`, {
|
|
3137
3190
|
cwd: workingDir,
|
|
@@ -3147,10 +3200,130 @@ terminalRouter.get('/sessions/:id/ship-summary', (req, res) => {
|
|
|
3147
3200
|
title: pr.title,
|
|
3148
3201
|
state: pr.state?.toLowerCase() || 'open',
|
|
3149
3202
|
};
|
|
3203
|
+
ghWorked = true;
|
|
3204
|
+
console.log(`[ship-summary] Found existing PR via gh CLI:`, existingPR);
|
|
3150
3205
|
}
|
|
3151
3206
|
}
|
|
3152
|
-
catch {
|
|
3153
|
-
//
|
|
3207
|
+
catch (ghErr) {
|
|
3208
|
+
// gh not available or no PR - try API fallback
|
|
3209
|
+
const err = ghErr;
|
|
3210
|
+
console.log(`[ship-summary] gh pr view failed:`, err.message || err.stderr || 'unknown error');
|
|
3211
|
+
}
|
|
3212
|
+
// If gh CLI didn't work, try GitHub API with OAuth token
|
|
3213
|
+
if (!ghWorked && creds.token) {
|
|
3214
|
+
console.log(`[ship-summary] Trying GitHub API fallback with token`);
|
|
3215
|
+
// Helper function to make the API call with a given token
|
|
3216
|
+
const checkPRWithToken = (token) => {
|
|
3217
|
+
try {
|
|
3218
|
+
// Get remote URL to determine repo owner/name
|
|
3219
|
+
const remoteUrl = execSync('git remote get-url origin', {
|
|
3220
|
+
cwd: workingDir,
|
|
3221
|
+
encoding: 'utf-8',
|
|
3222
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
3223
|
+
}).trim();
|
|
3224
|
+
// Parse GitHub repo from remote URL
|
|
3225
|
+
const match = remoteUrl.match(/github\.com[:/](.+?)\/(.+?)(?:\.git)?$/);
|
|
3226
|
+
if (!match) {
|
|
3227
|
+
return { found: false, error: 'Could not parse GitHub remote URL' };
|
|
3228
|
+
}
|
|
3229
|
+
const owner = match[1];
|
|
3230
|
+
const repo = match[2].replace(/\.git$/, '');
|
|
3231
|
+
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/pulls?head=${owner}:${encodeURIComponent(currentBranch)}&state=all`;
|
|
3232
|
+
console.log(`[ship-summary] GitHub API URL: ${apiUrl}`);
|
|
3233
|
+
const tempScriptPath = join(tmpdir(), `github-pr-check-${randomUUID()}.mjs`);
|
|
3234
|
+
const tempResultPath = join(tmpdir(), `github-pr-result-${randomUUID()}.json`);
|
|
3235
|
+
const scriptContent = `
|
|
3236
|
+
import { writeFileSync } from 'fs';
|
|
3237
|
+
try {
|
|
3238
|
+
const response = await fetch(${JSON.stringify(apiUrl)}, {
|
|
3239
|
+
headers: {
|
|
3240
|
+
'Accept': 'application/vnd.github+json',
|
|
3241
|
+
'Authorization': 'Bearer ' + ${JSON.stringify(token)},
|
|
3242
|
+
'X-GitHub-Api-Version': '2022-11-28',
|
|
3243
|
+
'User-Agent': 'ClaudeDesk',
|
|
3244
|
+
},
|
|
3245
|
+
});
|
|
3246
|
+
const text = await response.text();
|
|
3247
|
+
const status = response.status;
|
|
3248
|
+
|
|
3249
|
+
if (!response.ok) {
|
|
3250
|
+
writeFileSync(${JSON.stringify(tempResultPath)}, JSON.stringify({
|
|
3251
|
+
found: false,
|
|
3252
|
+
status,
|
|
3253
|
+
error: text,
|
|
3254
|
+
}));
|
|
3255
|
+
} else {
|
|
3256
|
+
const data = JSON.parse(text);
|
|
3257
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
3258
|
+
// Get the first PR (most recent)
|
|
3259
|
+
const pr = data[0];
|
|
3260
|
+
writeFileSync(${JSON.stringify(tempResultPath)}, JSON.stringify({
|
|
3261
|
+
found: true,
|
|
3262
|
+
url: pr.html_url,
|
|
3263
|
+
number: pr.number,
|
|
3264
|
+
title: pr.title,
|
|
3265
|
+
state: pr.state,
|
|
3266
|
+
}));
|
|
3267
|
+
} else {
|
|
3268
|
+
writeFileSync(${JSON.stringify(tempResultPath)}, JSON.stringify({
|
|
3269
|
+
found: false,
|
|
3270
|
+
error: 'No PRs found for this branch',
|
|
3271
|
+
}));
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
} catch (err) {
|
|
3275
|
+
writeFileSync(${JSON.stringify(tempResultPath)}, JSON.stringify({
|
|
3276
|
+
found: false,
|
|
3277
|
+
error: err.message,
|
|
3278
|
+
}));
|
|
3279
|
+
}
|
|
3280
|
+
`;
|
|
3281
|
+
writeFileSync(tempScriptPath, scriptContent);
|
|
3282
|
+
try {
|
|
3283
|
+
execSync(`node "${tempScriptPath}"`, {
|
|
3284
|
+
cwd: workingDir,
|
|
3285
|
+
encoding: 'utf-8',
|
|
3286
|
+
timeout: 15000,
|
|
3287
|
+
});
|
|
3288
|
+
const resultJson = readFileSync(tempResultPath, 'utf-8');
|
|
3289
|
+
console.log(`[ship-summary] GitHub API result:`, resultJson);
|
|
3290
|
+
return JSON.parse(resultJson);
|
|
3291
|
+
}
|
|
3292
|
+
finally {
|
|
3293
|
+
try {
|
|
3294
|
+
unlinkSync(tempScriptPath);
|
|
3295
|
+
}
|
|
3296
|
+
catch { }
|
|
3297
|
+
try {
|
|
3298
|
+
unlinkSync(tempResultPath);
|
|
3299
|
+
}
|
|
3300
|
+
catch { }
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
catch (e) {
|
|
3304
|
+
console.log(`[ship-summary] GitHub API PR check error:`, e);
|
|
3305
|
+
return { found: false, error: String(e) };
|
|
3306
|
+
}
|
|
3307
|
+
};
|
|
3308
|
+
// First attempt with current token
|
|
3309
|
+
let result = checkPRWithToken(creds.token);
|
|
3310
|
+
// If we got a 401, try refreshing the token (for GitHub this would use OAuth refresh)
|
|
3311
|
+
if (result.status === 401 && workspace) {
|
|
3312
|
+
console.log(`[ship-summary] Got 401, token may be expired for workspace ${workspace.id}`);
|
|
3313
|
+
// GitHub OAuth tokens don't expire by default, but if refresh logic is implemented, it would go here
|
|
3314
|
+
}
|
|
3315
|
+
if (result.found) {
|
|
3316
|
+
existingPR = {
|
|
3317
|
+
url: result.url,
|
|
3318
|
+
number: result.number,
|
|
3319
|
+
title: result.title,
|
|
3320
|
+
state: result.state?.toLowerCase() || 'open',
|
|
3321
|
+
};
|
|
3322
|
+
console.log(`[ship-summary] Found existing PR via GitHub API:`, existingPR);
|
|
3323
|
+
}
|
|
3324
|
+
else {
|
|
3325
|
+
console.log(`[ship-summary] No PR found via GitHub API, error:`, result.error);
|
|
3326
|
+
}
|
|
3154
3327
|
}
|
|
3155
3328
|
}
|
|
3156
3329
|
else if (creds.platform === 'gitlab') {
|
|
@@ -3332,6 +3505,36 @@ try {
|
|
|
3332
3505
|
unpushedCommits = 0;
|
|
3333
3506
|
}
|
|
3334
3507
|
}
|
|
3508
|
+
// Check if branch is ahead of base branch (even if pushed)
|
|
3509
|
+
// This allows creating a PR for already-pushed branches
|
|
3510
|
+
let commitsAheadOfBase = 0;
|
|
3511
|
+
if (currentBranch && currentBranch !== baseBranch) {
|
|
3512
|
+
try {
|
|
3513
|
+
// Try origin/baseBranch first (most accurate)
|
|
3514
|
+
const count = execSync(`git rev-list --count origin/${baseBranch}..HEAD`, {
|
|
3515
|
+
cwd: workingDir,
|
|
3516
|
+
encoding: 'utf-8',
|
|
3517
|
+
timeout: 5000,
|
|
3518
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
3519
|
+
}).trim();
|
|
3520
|
+
commitsAheadOfBase = parseInt(count, 10) || 0;
|
|
3521
|
+
}
|
|
3522
|
+
catch {
|
|
3523
|
+
// Fall back to local base branch
|
|
3524
|
+
try {
|
|
3525
|
+
const count = execSync(`git rev-list --count ${baseBranch}..HEAD`, {
|
|
3526
|
+
cwd: workingDir,
|
|
3527
|
+
encoding: 'utf-8',
|
|
3528
|
+
timeout: 5000,
|
|
3529
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
3530
|
+
}).trim();
|
|
3531
|
+
commitsAheadOfBase = parseInt(count, 10) || 0;
|
|
3532
|
+
}
|
|
3533
|
+
catch {
|
|
3534
|
+
commitsAheadOfBase = 0;
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3335
3538
|
// Get staged and unstaged files
|
|
3336
3539
|
let hasStagedChanges = false;
|
|
3337
3540
|
let hasUnstagedChanges = false;
|
|
@@ -3597,7 +3800,7 @@ try {
|
|
|
3597
3800
|
}
|
|
3598
3801
|
}
|
|
3599
3802
|
const hasUncommittedChanges = hasStagedChanges || hasUnstagedChanges;
|
|
3600
|
-
const hasChangesToShip = files.length > 0 || unpushedCommits > 0;
|
|
3803
|
+
const hasChangesToShip = files.length > 0 || unpushedCommits > 0 || commitsAheadOfBase > 0;
|
|
3601
3804
|
res.json({
|
|
3602
3805
|
success: true,
|
|
3603
3806
|
data: {
|
|
@@ -3609,6 +3812,7 @@ try {
|
|
|
3609
3812
|
hasUncommittedChanges,
|
|
3610
3813
|
hasChangesToShip,
|
|
3611
3814
|
unpushedCommits,
|
|
3815
|
+
commitsAheadOfBase,
|
|
3612
3816
|
hasStagedChanges,
|
|
3613
3817
|
hasUnstagedChanges,
|
|
3614
3818
|
existingPR,
|
|
@@ -3894,6 +4098,113 @@ terminalRouter.post('/usage/quota/refresh', async (_req, res) => {
|
|
|
3894
4098
|
}
|
|
3895
4099
|
});
|
|
3896
4100
|
// ============================================================================
|
|
4101
|
+
// Budget Allocator Endpoints
|
|
4102
|
+
// ============================================================================
|
|
4103
|
+
// Get allocator config
|
|
4104
|
+
terminalRouter.get('/usage/budget-config', (_req, res) => {
|
|
4105
|
+
try {
|
|
4106
|
+
const config = allocatorManager.getConfig();
|
|
4107
|
+
res.json({ success: true, data: config });
|
|
4108
|
+
}
|
|
4109
|
+
catch (error) {
|
|
4110
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4111
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4112
|
+
}
|
|
4113
|
+
});
|
|
4114
|
+
// Update allocator config
|
|
4115
|
+
terminalRouter.put('/usage/budget-config', (req, res) => {
|
|
4116
|
+
try {
|
|
4117
|
+
const config = allocatorManager.updateConfig(req.body);
|
|
4118
|
+
res.json({ success: true, data: config });
|
|
4119
|
+
}
|
|
4120
|
+
catch (error) {
|
|
4121
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4122
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4123
|
+
}
|
|
4124
|
+
});
|
|
4125
|
+
// Reset allocator config to defaults
|
|
4126
|
+
terminalRouter.post('/usage/budget-config/reset', (_req, res) => {
|
|
4127
|
+
try {
|
|
4128
|
+
const config = allocatorManager.resetConfig();
|
|
4129
|
+
res.json({ success: true, data: config });
|
|
4130
|
+
}
|
|
4131
|
+
catch (error) {
|
|
4132
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4133
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4134
|
+
}
|
|
4135
|
+
});
|
|
4136
|
+
// Get burn rate + projection
|
|
4137
|
+
terminalRouter.get('/usage/burn-rate', (_req, res) => {
|
|
4138
|
+
try {
|
|
4139
|
+
const burnRate = allocatorManager.getBurnRate();
|
|
4140
|
+
res.json({ success: true, data: burnRate });
|
|
4141
|
+
}
|
|
4142
|
+
catch (error) {
|
|
4143
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4144
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4145
|
+
}
|
|
4146
|
+
});
|
|
4147
|
+
// Get utilization history for chart
|
|
4148
|
+
terminalRouter.get('/usage/history', (_req, res) => {
|
|
4149
|
+
try {
|
|
4150
|
+
const history = allocatorManager.getUtilizationHistory();
|
|
4151
|
+
res.json({ success: true, data: history });
|
|
4152
|
+
}
|
|
4153
|
+
catch (error) {
|
|
4154
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4155
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4156
|
+
}
|
|
4157
|
+
});
|
|
4158
|
+
// Estimate cost for next message
|
|
4159
|
+
terminalRouter.post('/usage/estimate', (req, res) => {
|
|
4160
|
+
try {
|
|
4161
|
+
const { sessionId } = req.body || {};
|
|
4162
|
+
const estimate = allocatorManager.estimateMessageCost(sessionId);
|
|
4163
|
+
res.json({ success: true, data: estimate });
|
|
4164
|
+
}
|
|
4165
|
+
catch (error) {
|
|
4166
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4167
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4168
|
+
}
|
|
4169
|
+
});
|
|
4170
|
+
// Check budget limits before sending
|
|
4171
|
+
terminalRouter.post('/usage/check-budget', (req, res) => {
|
|
4172
|
+
try {
|
|
4173
|
+
const { fiveHour, sevenDay } = req.body || {};
|
|
4174
|
+
const result = allocatorManager.checkBudgetLimits(fiveHour !== undefined && sevenDay !== undefined
|
|
4175
|
+
? { fiveHour, sevenDay }
|
|
4176
|
+
: undefined);
|
|
4177
|
+
res.json({ success: true, data: result });
|
|
4178
|
+
}
|
|
4179
|
+
catch (error) {
|
|
4180
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4181
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4182
|
+
}
|
|
4183
|
+
});
|
|
4184
|
+
// Estimate queue batch cost
|
|
4185
|
+
terminalRouter.post('/usage/estimate-queue', (req, res) => {
|
|
4186
|
+
try {
|
|
4187
|
+
const { messageCount } = req.body || {};
|
|
4188
|
+
const estimate = allocatorManager.estimateQueueCost(messageCount || 1);
|
|
4189
|
+
res.json({ success: true, data: estimate });
|
|
4190
|
+
}
|
|
4191
|
+
catch (error) {
|
|
4192
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4193
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4194
|
+
}
|
|
4195
|
+
});
|
|
4196
|
+
// Record a utilization sample (called by frontend or cron)
|
|
4197
|
+
terminalRouter.post('/usage/sample', async (_req, res) => {
|
|
4198
|
+
try {
|
|
4199
|
+
await allocatorManager.recordUtilizationSample();
|
|
4200
|
+
res.json({ success: true });
|
|
4201
|
+
}
|
|
4202
|
+
catch (error) {
|
|
4203
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4204
|
+
res.status(500).json({ success: false, error: errorMsg });
|
|
4205
|
+
}
|
|
4206
|
+
});
|
|
4207
|
+
// ============================================================================
|
|
3897
4208
|
// Context Management Endpoints
|
|
3898
4209
|
// ============================================================================
|
|
3899
4210
|
// Get context state for a session
|