@trops/dash-core 0.1.622 → 0.1.624
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/electron/index.js +314 -3
- package/dist/electron/index.js.map +1 -1
- package/dist/index.esm.js +686 -252
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +686 -252
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/electron/index.js
CHANGED
|
@@ -972,10 +972,15 @@ const BOTS_RUN$1 = "bots-run";
|
|
|
972
972
|
const BOTS_STOP$1 = "bots-stop";
|
|
973
973
|
const BOTS_APPROVE$1 = "bots-approve";
|
|
974
974
|
const BOTS_LIST_APPROVALS$1 = "bots-list-approvals";
|
|
975
|
+
const BOTS_GET_BUDGETS$1 = "bots-get-budgets";
|
|
976
|
+
const BOTS_SET_BUDGET$1 = "bots-set-budget";
|
|
977
|
+
const BOTS_GET_SPEND$1 = "bots-get-spend";
|
|
978
|
+
const BOTS_RESUME_BUDGET$1 = "bots-resume-budget";
|
|
975
979
|
|
|
976
980
|
// --- Main → Renderer (send) ---
|
|
977
981
|
const BOT_STREAM$2 = "bot-stream"; // { botId, event: BotEvent }
|
|
978
982
|
const BOT_APPROVAL_PENDING$2 = "bot-approval-pending"; // { id, request }
|
|
983
|
+
const BOT_BUDGET_ALERT$2 = "bot-budget-alert"; // { botId, cost, estimated, status }
|
|
979
984
|
|
|
980
985
|
var botEvents$1 = {
|
|
981
986
|
BOTS_LIST: BOTS_LIST$1,
|
|
@@ -986,8 +991,13 @@ var botEvents$1 = {
|
|
|
986
991
|
BOTS_STOP: BOTS_STOP$1,
|
|
987
992
|
BOTS_APPROVE: BOTS_APPROVE$1,
|
|
988
993
|
BOTS_LIST_APPROVALS: BOTS_LIST_APPROVALS$1,
|
|
994
|
+
BOTS_GET_BUDGETS: BOTS_GET_BUDGETS$1,
|
|
995
|
+
BOTS_SET_BUDGET: BOTS_SET_BUDGET$1,
|
|
996
|
+
BOTS_GET_SPEND: BOTS_GET_SPEND$1,
|
|
997
|
+
BOTS_RESUME_BUDGET: BOTS_RESUME_BUDGET$1,
|
|
989
998
|
BOT_STREAM: BOT_STREAM$2,
|
|
990
999
|
BOT_APPROVAL_PENDING: BOT_APPROVAL_PENDING$2,
|
|
1000
|
+
BOT_BUDGET_ALERT: BOT_BUDGET_ALERT$2,
|
|
991
1001
|
};
|
|
992
1002
|
|
|
993
1003
|
/**
|
|
@@ -40808,7 +40818,7 @@ function getProvider$2(providerId) {
|
|
|
40808
40818
|
return PROVIDERS[providerId] || PROVIDERS[DEFAULT_PROVIDER$1];
|
|
40809
40819
|
}
|
|
40810
40820
|
|
|
40811
|
-
function getCuratedModels(providerId) {
|
|
40821
|
+
function getCuratedModels$1(providerId) {
|
|
40812
40822
|
return getProvider$2(providerId).curatedModels.slice();
|
|
40813
40823
|
}
|
|
40814
40824
|
|
|
@@ -40860,7 +40870,7 @@ var modelProviders = {
|
|
|
40860
40870
|
PROVIDERS,
|
|
40861
40871
|
DEFAULT_PROVIDER: DEFAULT_PROVIDER$1,
|
|
40862
40872
|
getProvider: getProvider$2,
|
|
40863
|
-
getCuratedModels,
|
|
40873
|
+
getCuratedModels: getCuratedModels$1,
|
|
40864
40874
|
getDefaultModel: getDefaultModel$1,
|
|
40865
40875
|
migrateModelId: migrateModelId$3,
|
|
40866
40876
|
isKnownRetired,
|
|
@@ -73658,6 +73668,7 @@ const Store = require$$0$5;
|
|
|
73658
73668
|
*/
|
|
73659
73669
|
function createElectronHost$1() {
|
|
73660
73670
|
const store = new Store({ name: "dash-bots" });
|
|
73671
|
+
const budgetStore = new Store({ name: "dash-bot-budgets" });
|
|
73661
73672
|
const botsRoot = path$3.join(app$2.getPath("userData"), "bots");
|
|
73662
73673
|
|
|
73663
73674
|
return {
|
|
@@ -73669,6 +73680,12 @@ function createElectronHost$1() {
|
|
|
73669
73680
|
read: () => store.get("store", { bots: {}, runs: {} }),
|
|
73670
73681
|
write: (obj) => store.set("store", obj),
|
|
73671
73682
|
},
|
|
73683
|
+
// Separate blob for budgets + monthly spend (Slice 5).
|
|
73684
|
+
budgetPersistence: {
|
|
73685
|
+
read: () =>
|
|
73686
|
+
budgetStore.get("store", { budgets: {}, spend: {}, overrides: {} }),
|
|
73687
|
+
write: (obj) => budgetStore.set("store", obj),
|
|
73688
|
+
},
|
|
73672
73689
|
};
|
|
73673
73690
|
}
|
|
73674
73691
|
|
|
@@ -74929,6 +74946,9 @@ let BotRunner$1 = class BotRunner {
|
|
|
74929
74946
|
deps.makeRequestPermission || createRequestPermission;
|
|
74930
74947
|
this._gate = deps.gate; // forwarded to PermissionGate ctx (optional)
|
|
74931
74948
|
this._isPaused = deps.isPaused || (() => false);
|
|
74949
|
+
// Called after each run with the run's token usage so budgets (Slice 5)
|
|
74950
|
+
// can accrue cost. Optional — default no-op keeps the runner portable.
|
|
74951
|
+
this._onUsage = deps.onUsage || null;
|
|
74932
74952
|
/** @type {Map<string, AbortController>} */
|
|
74933
74953
|
this._active = new Map();
|
|
74934
74954
|
}
|
|
@@ -74979,9 +74999,11 @@ let BotRunner$1 = class BotRunner {
|
|
|
74979
74999
|
let status = "completed";
|
|
74980
75000
|
let usage = null;
|
|
74981
75001
|
let errorMessage = null;
|
|
75002
|
+
let runProfile = null;
|
|
74982
75003
|
|
|
74983
75004
|
try {
|
|
74984
75005
|
const profile = await this._resolveRunProfile(bot);
|
|
75006
|
+
runProfile = profile;
|
|
74985
75007
|
const engine = this._engines.getEngine(profile.engineId);
|
|
74986
75008
|
if (!engine) {
|
|
74987
75009
|
throw new Error(`no engine registered for "${profile.engineId}"`);
|
|
@@ -75068,6 +75090,19 @@ let BotRunner$1 = class BotRunner {
|
|
|
75068
75090
|
error: errorMessage,
|
|
75069
75091
|
};
|
|
75070
75092
|
this._store.appendRun(botId, runRecord);
|
|
75093
|
+
|
|
75094
|
+
// Feed usage to budgets (Slice 5). Optional hook; only when we captured
|
|
75095
|
+
// usage and know which provider/model produced it.
|
|
75096
|
+
if (usage && runProfile && this._onUsage) {
|
|
75097
|
+
this._onUsage({
|
|
75098
|
+
botId,
|
|
75099
|
+
workspaceId: bot.workspaceId,
|
|
75100
|
+
providerId: runProfile.providerId,
|
|
75101
|
+
model: runProfile.model,
|
|
75102
|
+
usage,
|
|
75103
|
+
});
|
|
75104
|
+
}
|
|
75105
|
+
|
|
75071
75106
|
return runRecord;
|
|
75072
75107
|
}
|
|
75073
75108
|
};
|
|
@@ -75180,6 +75215,211 @@ let BotScheduler$1 = class BotScheduler {
|
|
|
75180
75215
|
|
|
75181
75216
|
var BotScheduler_1 = BotScheduler$1;
|
|
75182
75217
|
|
|
75218
|
+
var BudgetController$2 = {exports: {}};
|
|
75219
|
+
|
|
75220
|
+
/**
|
|
75221
|
+
* BudgetController.js
|
|
75222
|
+
*
|
|
75223
|
+
* Per-run cost capture and monthly spend limits for bots (PRD US-019 / FR-017).
|
|
75224
|
+
* Records each run's token usage as a dollar cost (from the provider registry's
|
|
75225
|
+
* per-model pricing), accrues it to bot / workspace / global monthly buckets,
|
|
75226
|
+
* and reports status: ok, warn (≥80%), or exceeded (≥100%). A bot that has
|
|
75227
|
+
* exceeded any applicable budget is over-budget and should auto-pause; the user
|
|
75228
|
+
* may explicitly (and audibly) override to keep it running for the rest of the
|
|
75229
|
+
* month.
|
|
75230
|
+
*
|
|
75231
|
+
* Portable (NFR-006): persistence, pricing lookup, clock, and audit sink are
|
|
75232
|
+
* injected. No Electron.
|
|
75233
|
+
*
|
|
75234
|
+
* new BudgetController({ persistence, getPricing, clock?, audit? })
|
|
75235
|
+
* persistence: { read(): object, write(obj): void } // { budgets, spend, overrides }
|
|
75236
|
+
* getPricing: (providerId, model) => { input, output } | null // USD per 1M tokens
|
|
75237
|
+
* clock: () => number (epoch ms)
|
|
75238
|
+
* audit: (entry) => void
|
|
75239
|
+
*/
|
|
75240
|
+
|
|
75241
|
+
const WARN_AT = 0.8;
|
|
75242
|
+
|
|
75243
|
+
function monthKey(ms) {
|
|
75244
|
+
const d = new Date(ms);
|
|
75245
|
+
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
75246
|
+
}
|
|
75247
|
+
|
|
75248
|
+
function scopeKey(scope, id) {
|
|
75249
|
+
if (scope === "global") return "global";
|
|
75250
|
+
if (scope === "workspace") return `workspace:${id}`;
|
|
75251
|
+
if (scope === "bot") return `bot:${id}`;
|
|
75252
|
+
throw new Error(`BudgetController: unknown scope "${scope}"`);
|
|
75253
|
+
}
|
|
75254
|
+
|
|
75255
|
+
function stateFor(spent, budget) {
|
|
75256
|
+
if (!(budget > 0)) return "ok"; // no limit set
|
|
75257
|
+
const pct = spent / budget;
|
|
75258
|
+
if (pct >= 1) return "exceeded";
|
|
75259
|
+
if (pct >= WARN_AT) return "warn";
|
|
75260
|
+
return "ok";
|
|
75261
|
+
}
|
|
75262
|
+
|
|
75263
|
+
// Most-restrictive wins.
|
|
75264
|
+
const RANK = { ok: 0, warn: 1, exceeded: 2 };
|
|
75265
|
+
|
|
75266
|
+
let BudgetController$1 = class BudgetController {
|
|
75267
|
+
constructor({ persistence, getPricing, clock, audit } = {}) {
|
|
75268
|
+
if (!persistence || typeof persistence.read !== "function") {
|
|
75269
|
+
throw new Error(
|
|
75270
|
+
"BudgetController: persistence with read()/write() is required",
|
|
75271
|
+
);
|
|
75272
|
+
}
|
|
75273
|
+
this._persistence = persistence;
|
|
75274
|
+
this._getPricing = getPricing || (() => null);
|
|
75275
|
+
this._now = clock || (() => Date.now());
|
|
75276
|
+
this._audit = audit || (() => {});
|
|
75277
|
+
}
|
|
75278
|
+
|
|
75279
|
+
_load() {
|
|
75280
|
+
const d = this._persistence.read() || {};
|
|
75281
|
+
if (!d.budgets) d.budgets = {};
|
|
75282
|
+
if (!d.spend) d.spend = {};
|
|
75283
|
+
if (!d.overrides) d.overrides = {};
|
|
75284
|
+
return d;
|
|
75285
|
+
}
|
|
75286
|
+
|
|
75287
|
+
_save(d) {
|
|
75288
|
+
this._persistence.write(d);
|
|
75289
|
+
}
|
|
75290
|
+
|
|
75291
|
+
/** Set a monthly USD limit for a scope. @param scope "bot"|"workspace"|"global" */
|
|
75292
|
+
setBudget(scope, id, monthlyUsd) {
|
|
75293
|
+
const key = scopeKey(scope, id);
|
|
75294
|
+
const d = this._load();
|
|
75295
|
+
if (monthlyUsd == null) delete d.budgets[key];
|
|
75296
|
+
else d.budgets[key] = monthlyUsd;
|
|
75297
|
+
this._save(d);
|
|
75298
|
+
this._audit({ type: "budget-set", scope: key, monthlyUsd });
|
|
75299
|
+
return d.budgets[key];
|
|
75300
|
+
}
|
|
75301
|
+
|
|
75302
|
+
/** @returns {object} scopeKey → monthly USD limit */
|
|
75303
|
+
getBudgets() {
|
|
75304
|
+
return { ...this._load().budgets };
|
|
75305
|
+
}
|
|
75306
|
+
|
|
75307
|
+
/**
|
|
75308
|
+
* Record a run's usage. Accrues cost to the bot/workspace/global buckets for
|
|
75309
|
+
* the current month.
|
|
75310
|
+
* @returns {{ cost: number, estimated: boolean, status: object }}
|
|
75311
|
+
*/
|
|
75312
|
+
recordUsage({ botId, workspaceId, providerId, model, usage } = {}) {
|
|
75313
|
+
const inTok = (usage && usage.inputTokens) || 0;
|
|
75314
|
+
const outTok = (usage && usage.outputTokens) || 0;
|
|
75315
|
+
const pricing = this._getPricing(providerId, model);
|
|
75316
|
+
let cost = 0;
|
|
75317
|
+
let estimated = false;
|
|
75318
|
+
if (pricing) {
|
|
75319
|
+
cost = (inTok / 1e6) * pricing.input + (outTok / 1e6) * pricing.output;
|
|
75320
|
+
} else {
|
|
75321
|
+
// Unknown pricing — track tokens only and flag the figure as estimated.
|
|
75322
|
+
estimated = true;
|
|
75323
|
+
}
|
|
75324
|
+
|
|
75325
|
+
const d = this._load();
|
|
75326
|
+
const mk = monthKey(this._now());
|
|
75327
|
+
if (!d.spend[mk]) d.spend[mk] = {};
|
|
75328
|
+
|
|
75329
|
+
const keys = [scopeKey("bot", botId), scopeKey("global")];
|
|
75330
|
+
if (workspaceId) keys.splice(1, 0, scopeKey("workspace", workspaceId));
|
|
75331
|
+
for (const key of keys) {
|
|
75332
|
+
const bucket = d.spend[mk][key] || {
|
|
75333
|
+
usd: 0,
|
|
75334
|
+
inputTokens: 0,
|
|
75335
|
+
outputTokens: 0,
|
|
75336
|
+
};
|
|
75337
|
+
bucket.usd += cost;
|
|
75338
|
+
bucket.inputTokens += inTok;
|
|
75339
|
+
bucket.outputTokens += outTok;
|
|
75340
|
+
d.spend[mk][key] = bucket;
|
|
75341
|
+
}
|
|
75342
|
+
this._save(d);
|
|
75343
|
+
|
|
75344
|
+
this._audit({
|
|
75345
|
+
type: "usage",
|
|
75346
|
+
botId,
|
|
75347
|
+
workspaceId,
|
|
75348
|
+
cost,
|
|
75349
|
+
estimated,
|
|
75350
|
+
inTok,
|
|
75351
|
+
outTok,
|
|
75352
|
+
});
|
|
75353
|
+
return { cost, estimated, status: this.status(botId, workspaceId) };
|
|
75354
|
+
}
|
|
75355
|
+
|
|
75356
|
+
_spent(mk, key) {
|
|
75357
|
+
const d = this._load();
|
|
75358
|
+
return (d.spend[mk] && d.spend[mk][key] && d.spend[mk][key].usd) || 0;
|
|
75359
|
+
}
|
|
75360
|
+
|
|
75361
|
+
/**
|
|
75362
|
+
* Budget status for a bot across its applicable scopes.
|
|
75363
|
+
* @returns {{ scopes: object[], overall: "ok"|"warn"|"exceeded" }}
|
|
75364
|
+
*/
|
|
75365
|
+
status(botId, workspaceId) {
|
|
75366
|
+
const d = this._load();
|
|
75367
|
+
const mk = monthKey(this._now());
|
|
75368
|
+
const applicable = [scopeKey("bot", botId)];
|
|
75369
|
+
if (workspaceId) applicable.push(scopeKey("workspace", workspaceId));
|
|
75370
|
+
applicable.push(scopeKey("global"));
|
|
75371
|
+
|
|
75372
|
+
const scopes = [];
|
|
75373
|
+
let overall = "ok";
|
|
75374
|
+
for (const key of applicable) {
|
|
75375
|
+
const budget = d.budgets[key];
|
|
75376
|
+
if (budget == null) continue; // no limit for this scope
|
|
75377
|
+
const spent = this._spent(mk, key);
|
|
75378
|
+
const state = stateFor(spent, budget);
|
|
75379
|
+
scopes.push({ scope: key, spent, budget, pct: spent / budget, state });
|
|
75380
|
+
if (RANK[state] > RANK[overall]) overall = state;
|
|
75381
|
+
}
|
|
75382
|
+
return { scopes, overall };
|
|
75383
|
+
}
|
|
75384
|
+
|
|
75385
|
+
/**
|
|
75386
|
+
* Whether a bot should be paused for budget. True when any applicable budget
|
|
75387
|
+
* is exceeded AND the user hasn't overridden for this month.
|
|
75388
|
+
*/
|
|
75389
|
+
isOverBudget(botId, workspaceId) {
|
|
75390
|
+
const mk = monthKey(this._now());
|
|
75391
|
+
const d = this._load();
|
|
75392
|
+
if (d.overrides[mk] && d.overrides[mk][botId]) return false;
|
|
75393
|
+
return this.status(botId, workspaceId).overall === "exceeded";
|
|
75394
|
+
}
|
|
75395
|
+
|
|
75396
|
+
/**
|
|
75397
|
+
* Explicit, audited override to keep a budget-exceeded bot running for the
|
|
75398
|
+
* rest of the current month (US-019 AC6). Cleared automatically next month
|
|
75399
|
+
* (overrides are keyed by month).
|
|
75400
|
+
*/
|
|
75401
|
+
resumeOverBudget(botId) {
|
|
75402
|
+
const d = this._load();
|
|
75403
|
+
const mk = monthKey(this._now());
|
|
75404
|
+
if (!d.overrides[mk]) d.overrides[mk] = {};
|
|
75405
|
+
d.overrides[mk][botId] = true;
|
|
75406
|
+
this._save(d);
|
|
75407
|
+
this._audit({ type: "budget-override", botId, month: mk });
|
|
75408
|
+
return true;
|
|
75409
|
+
}
|
|
75410
|
+
|
|
75411
|
+
/** @returns {object} spend buckets for a month (default current) */
|
|
75412
|
+
getSpend(mk) {
|
|
75413
|
+
const d = this._load();
|
|
75414
|
+
return d.spend[mk || monthKey(this._now())] || {};
|
|
75415
|
+
}
|
|
75416
|
+
};
|
|
75417
|
+
|
|
75418
|
+
BudgetController$2.exports = BudgetController$1;
|
|
75419
|
+
BudgetController$2.exports.WARN_AT = WARN_AT;
|
|
75420
|
+
|
|
75421
|
+
var BudgetControllerExports = BudgetController$2.exports;
|
|
75422
|
+
|
|
75183
75423
|
/**
|
|
75184
75424
|
* mcpResult.js
|
|
75185
75425
|
*
|
|
@@ -75244,14 +75484,26 @@ const ApprovalRegistry = approvalsExports;
|
|
|
75244
75484
|
const engines = engines$1;
|
|
75245
75485
|
const BotRunner = BotRunner_1;
|
|
75246
75486
|
const BotScheduler = BotScheduler_1;
|
|
75487
|
+
const BudgetController = BudgetControllerExports;
|
|
75247
75488
|
const { normalizeMcpResult } = mcpResult;
|
|
75248
75489
|
const {
|
|
75249
75490
|
getProvider: getProvider$1,
|
|
75250
75491
|
getDefaultModel,
|
|
75492
|
+
getCuratedModels,
|
|
75251
75493
|
migrateModelId,
|
|
75252
75494
|
DEFAULT_PROVIDER,
|
|
75253
75495
|
} = modelProviders;
|
|
75254
|
-
const {
|
|
75496
|
+
const {
|
|
75497
|
+
BOT_STREAM: BOT_STREAM$1,
|
|
75498
|
+
BOT_APPROVAL_PENDING: BOT_APPROVAL_PENDING$1,
|
|
75499
|
+
BOT_BUDGET_ALERT: BOT_BUDGET_ALERT$1,
|
|
75500
|
+
} = botEvents$1;
|
|
75501
|
+
|
|
75502
|
+
/** Pricing lookup for BudgetController: curated-model pricing by provider. */
|
|
75503
|
+
function pricingFor(providerId, model) {
|
|
75504
|
+
const m = getCuratedModels(providerId).find((c) => c.value === model);
|
|
75505
|
+
return (m && m.pricing) || null;
|
|
75506
|
+
}
|
|
75255
75507
|
|
|
75256
75508
|
const botController$1 = {
|
|
75257
75509
|
_ready: false,
|
|
@@ -75299,6 +75551,12 @@ const botController$1 = {
|
|
|
75299
75551
|
},
|
|
75300
75552
|
};
|
|
75301
75553
|
|
|
75554
|
+
// Budgets: accrue per-run cost, auto-pause bots over their monthly cap.
|
|
75555
|
+
this._budgets = new BudgetController({
|
|
75556
|
+
persistence: host.budgetPersistence,
|
|
75557
|
+
getPricing: pricingFor,
|
|
75558
|
+
});
|
|
75559
|
+
|
|
75302
75560
|
this._runner = new BotRunner({
|
|
75303
75561
|
engines,
|
|
75304
75562
|
store: this._store,
|
|
@@ -75307,6 +75565,24 @@ const botController$1 = {
|
|
|
75307
75565
|
resolveTools: (bot) => this._resolveTools(bot),
|
|
75308
75566
|
callTool: (serverName, toolName, args, o) =>
|
|
75309
75567
|
this._callTool(serverName, toolName, args, o),
|
|
75568
|
+
// A bot over any applicable budget is paused at the permission gate.
|
|
75569
|
+
isPaused: (botId) =>
|
|
75570
|
+
this._budgets.isOverBudget(
|
|
75571
|
+
botId,
|
|
75572
|
+
(this._store.get(botId) || {}).workspaceId,
|
|
75573
|
+
),
|
|
75574
|
+
// After each run, accrue cost and alert on warn/exceeded.
|
|
75575
|
+
onUsage: (u) => {
|
|
75576
|
+
const r = this._budgets.recordUsage(u);
|
|
75577
|
+
if (r.status.overall !== "ok") {
|
|
75578
|
+
this._broadcast(BOT_BUDGET_ALERT$1, {
|
|
75579
|
+
botId: u.botId,
|
|
75580
|
+
cost: r.cost,
|
|
75581
|
+
estimated: r.estimated,
|
|
75582
|
+
status: r.status,
|
|
75583
|
+
});
|
|
75584
|
+
}
|
|
75585
|
+
},
|
|
75310
75586
|
});
|
|
75311
75587
|
|
|
75312
75588
|
this._scheduler = new BotScheduler({
|
|
@@ -75374,6 +75650,26 @@ const botController$1 = {
|
|
|
75374
75650
|
return this._approvals.list();
|
|
75375
75651
|
},
|
|
75376
75652
|
|
|
75653
|
+
// ---- budgets (US-019) ---------------------------------------------------
|
|
75654
|
+
|
|
75655
|
+
getBudgets() {
|
|
75656
|
+
return this._budgets.getBudgets();
|
|
75657
|
+
},
|
|
75658
|
+
|
|
75659
|
+
setBudget(scope, id, monthlyUsd) {
|
|
75660
|
+
return this._budgets.setBudget(scope, id, monthlyUsd);
|
|
75661
|
+
},
|
|
75662
|
+
|
|
75663
|
+
/** Current-month (or given month) spend buckets by scope. */
|
|
75664
|
+
getSpend(month) {
|
|
75665
|
+
return this._budgets.getSpend(month);
|
|
75666
|
+
},
|
|
75667
|
+
|
|
75668
|
+
/** Explicit, audited override to keep a budget-exceeded bot running. */
|
|
75669
|
+
resumeBudget(botId) {
|
|
75670
|
+
return this._budgets.resumeOverBudget(botId);
|
|
75671
|
+
},
|
|
75672
|
+
|
|
75377
75673
|
// ---- internals ----------------------------------------------------------
|
|
75378
75674
|
|
|
75379
75675
|
_run(botId, opts) {
|
|
@@ -79367,8 +79663,13 @@ const {
|
|
|
79367
79663
|
BOTS_STOP,
|
|
79368
79664
|
BOTS_APPROVE,
|
|
79369
79665
|
BOTS_LIST_APPROVALS,
|
|
79666
|
+
BOTS_GET_BUDGETS,
|
|
79667
|
+
BOTS_SET_BUDGET,
|
|
79668
|
+
BOTS_GET_SPEND,
|
|
79669
|
+
BOTS_RESUME_BUDGET,
|
|
79370
79670
|
BOT_STREAM,
|
|
79371
79671
|
BOT_APPROVAL_PENDING,
|
|
79672
|
+
BOT_BUDGET_ALERT,
|
|
79372
79673
|
} = botEvents$1;
|
|
79373
79674
|
|
|
79374
79675
|
let _nextListenerId = 0;
|
|
@@ -79397,10 +79698,20 @@ const botApi$1 = {
|
|
|
79397
79698
|
ipcRenderer$3.invoke(BOTS_APPROVE, { approvalId, decision }),
|
|
79398
79699
|
listApprovals: () => ipcRenderer$3.invoke(BOTS_LIST_APPROVALS),
|
|
79399
79700
|
|
|
79701
|
+
// --- Budgets (US-019) ---
|
|
79702
|
+
getBudgets: () => ipcRenderer$3.invoke(BOTS_GET_BUDGETS),
|
|
79703
|
+
/** scope: "bot"|"workspace"|"global" */
|
|
79704
|
+
setBudget: (scope, id, monthlyUsd) =>
|
|
79705
|
+
ipcRenderer$3.invoke(BOTS_SET_BUDGET, { scope, id, monthlyUsd }),
|
|
79706
|
+
getSpend: (month) => ipcRenderer$3.invoke(BOTS_GET_SPEND, { month }),
|
|
79707
|
+
resumeBudget: (botId) => ipcRenderer$3.invoke(BOTS_RESUME_BUDGET, { botId }),
|
|
79708
|
+
|
|
79400
79709
|
/** Subscribe to streamed run events: { botId, event }. */
|
|
79401
79710
|
onStream: (callback) => _addListener(BOT_STREAM, callback),
|
|
79402
79711
|
/** Subscribe to new pending approvals: { id, request }. */
|
|
79403
79712
|
onApprovalPending: (callback) => _addListener(BOT_APPROVAL_PENDING, callback),
|
|
79713
|
+
/** Subscribe to budget warn/exceeded alerts. */
|
|
79714
|
+
onBudgetAlert: (callback) => _addListener(BOT_BUDGET_ALERT, callback),
|
|
79404
79715
|
|
|
79405
79716
|
removeListener: (id) => {
|
|
79406
79717
|
const entry = _listenerMap.get(id);
|