@bike4mind/cli 0.2.1-epic-lattice.17015 → 0.2.1-feat-cli-usage-command.16999

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/index.js CHANGED
@@ -4,10 +4,9 @@ import {
4
4
  getEffectiveApiKey,
5
5
  getOpenWeatherKey,
6
6
  getSerperKey
7
- } from "./chunk-GSXZOOWP.js";
8
- import "./chunk-NHP3UPYU.js";
9
- import "./chunk-FMXGH4KC.js";
10
- import "./chunk-HHHI5KKC.js";
7
+ } from "./chunk-QXGU6KYI.js";
8
+ import "./chunk-LZMRYJ3B.js";
9
+ import "./chunk-F3ETXO5N.js";
11
10
  import {
12
11
  BFLImageService,
13
12
  BaseStorage,
@@ -16,7 +15,7 @@ import {
16
15
  OpenAIBackend,
17
16
  OpenAIImageService,
18
17
  XAIImageService
19
- } from "./chunk-7JFN4F7V.js";
18
+ } from "./chunk-U3GPYBTD.js";
20
19
  import {
21
20
  Logger
22
21
  } from "./chunk-AMDXHL6S.js";
@@ -30,6 +29,7 @@ import {
30
29
  AuthEvents,
31
30
  BFL_IMAGE_MODELS,
32
31
  BFL_SAFETY_TOLERANCE,
32
+ CREDIT_DEDUCT_TRANSACTION_TYPES,
33
33
  ChatCompletionCreateInputSchema,
34
34
  ChatModels,
35
35
  CollectionType,
@@ -73,7 +73,7 @@ import {
73
73
  XAI_IMAGE_MODELS,
74
74
  b4mLLMTools,
75
75
  getMcpProviderMetadata
76
- } from "./chunk-ZMZK7YVU.js";
76
+ } from "./chunk-WR6YZRQV.js";
77
77
  import {
78
78
  __require
79
79
  } from "./chunk-PDX44BCA.js";
@@ -178,6 +178,10 @@ var COMMANDS = [
178
178
  {
179
179
  name: "trusted",
180
180
  description: "List all trusted tools"
181
+ },
182
+ {
183
+ name: "usage",
184
+ description: "Show credit usage and balance"
181
185
  }
182
186
  ];
183
187
 
@@ -7239,632 +7243,6 @@ var deleteFileTool = {
7239
7243
  })
7240
7244
  };
7241
7245
 
7242
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/lattice/index.js
7243
- function createModelData(name, modelType, userId, description, sessionId) {
7244
- const now = /* @__PURE__ */ new Date();
7245
- return {
7246
- name,
7247
- description: description || "",
7248
- modelType,
7249
- userId,
7250
- sessionId,
7251
- data: { entities: [], relationships: [] },
7252
- rules: { rules: [], rulesets: [] },
7253
- views: { views: [] },
7254
- settings: {
7255
- currency: "USD",
7256
- fiscalYearStart: "01-01",
7257
- periodGrain: "quarter",
7258
- defaultDecimalPlaces: 2,
7259
- negativeFormat: "parentheses"
7260
- },
7261
- scenarios: [],
7262
- operations: [],
7263
- operationIndex: -1,
7264
- version: 1,
7265
- createdAt: now,
7266
- updatedAt: now
7267
- };
7268
- }
7269
- var latticeCreateModelTool = {
7270
- name: "lattice_create_model",
7271
- implementation: (context) => ({
7272
- toolFn: async (params) => {
7273
- const { name, description, modelType = "custom" } = params;
7274
- const modelData = createModelData(name, modelType, context.userId, description);
7275
- let model;
7276
- if (context.db.latticeModels) {
7277
- try {
7278
- model = await context.db.latticeModels.create(modelData);
7279
- context.logger.info(`[Lattice] Created model ${model.id} in database`);
7280
- } catch (error) {
7281
- context.logger.error(`[Lattice] Failed to persist model to database:`, error);
7282
- model = {
7283
- ...modelData,
7284
- id: `lattice_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
7285
- };
7286
- }
7287
- } else {
7288
- context.logger.warn("[Lattice] No database adapter available, creating in-memory model");
7289
- model = {
7290
- ...modelData,
7291
- id: `lattice_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
7292
- };
7293
- }
7294
- const artifactData = {
7295
- type: "lattice",
7296
- id: model.id,
7297
- title: name,
7298
- content: JSON.stringify(model),
7299
- metadata: {
7300
- modelType,
7301
- periodGrain: model.settings?.periodGrain || "quarter",
7302
- currency: model.settings?.currency || "USD",
7303
- entityCount: model.data?.entities?.length || 0,
7304
- ruleCount: model.rules?.rules?.length || 0
7305
- },
7306
- createdAt: model.createdAt?.toISOString?.() || (/* @__PURE__ */ new Date()).toISOString(),
7307
- updatedAt: model.updatedAt?.toISOString?.() || (/* @__PURE__ */ new Date()).toISOString()
7308
- };
7309
- return `I've created a new ${modelType} financial model called "${name}".
7310
-
7311
- <artifact identifier="${model.id}" type="application/vnd.b4m.lattice" title="${name}">
7312
- ${JSON.stringify(artifactData, null, 2)}
7313
- </artifact>
7314
-
7315
- You can now add line items, set values, and create formulas. Try saying:
7316
- - "Add Revenue and COGS line items"
7317
- - "Set Q1 Revenue to 100,000"
7318
- - "Gross Profit equals Revenue minus COGS"`;
7319
- },
7320
- toolSchema: {
7321
- name: "lattice_create_model",
7322
- description: `Create a new Lattice financial model. This is the first step when building a pro-forma or financial analysis.
7323
-
7324
- **When to use:** When the user wants to:
7325
- - Create a new financial model, spreadsheet, or pro-forma
7326
- - Start building an income statement, balance sheet, or cash flow
7327
- - Set up a SaaS metrics dashboard
7328
- - Begin any structured numerical analysis
7329
-
7330
- **Model types:**
7331
- - income_statement: P&L with revenue, expenses, profits
7332
- - balance_sheet: Assets, liabilities, equity
7333
- - cashflow: Operating, investing, financing cash flows
7334
- - saas_metrics: MRR, ARR, churn, CAC, LTV
7335
- - custom: Any other structured model
7336
-
7337
- After creating a model, add entities with \`lattice_add_entity\` and define formulas with \`lattice_create_rule\`.`,
7338
- parameters: {
7339
- type: "object",
7340
- properties: {
7341
- name: {
7342
- type: "string",
7343
- description: 'Name of the model (e.g., "2024 Budget", "Q1 Forecast")'
7344
- },
7345
- description: {
7346
- type: "string",
7347
- description: "Optional description of what this model represents"
7348
- },
7349
- modelType: {
7350
- type: "string",
7351
- enum: ["income_statement", "balance_sheet", "cashflow", "saas_metrics", "custom"],
7352
- description: "Type of financial model to create"
7353
- }
7354
- },
7355
- required: ["name"]
7356
- }
7357
- }
7358
- })
7359
- };
7360
- var latticeAddEntityTool = {
7361
- name: "lattice_add_entity",
7362
- implementation: (context) => ({
7363
- toolFn: async (params) => {
7364
- const { modelId, name, type, displayName, initialValues = [] } = params;
7365
- const entityId = name.toLowerCase().replace(/\s+/g, "_");
7366
- const entityData = {
7367
- id: entityId,
7368
- type,
7369
- name,
7370
- displayName: displayName || name,
7371
- attributes: initialValues.map((v) => ({
7372
- key: v.key,
7373
- value: v.value,
7374
- dataType: v.dataType || (typeof v.value === "number" ? "number" : "string"),
7375
- isComputed: false
7376
- })),
7377
- metadata: {},
7378
- createdAt: /* @__PURE__ */ new Date(),
7379
- updatedAt: /* @__PURE__ */ new Date()
7380
- };
7381
- if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
7382
- try {
7383
- const model = await context.db.latticeModels.findById(modelId);
7384
- if (model) {
7385
- const existingIndex = model.data.entities.findIndex((e) => e.id === entityId);
7386
- if (existingIndex >= 0) {
7387
- model.data.entities[existingIndex] = entityData;
7388
- } else {
7389
- model.data.entities.push(entityData);
7390
- }
7391
- await context.db.latticeModels.update({
7392
- id: modelId,
7393
- data: model.data,
7394
- updatedAt: /* @__PURE__ */ new Date()
7395
- });
7396
- context.logger.info(`[Lattice] Added entity ${entityId} to model ${modelId}`);
7397
- } else {
7398
- context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
7399
- }
7400
- } catch (error) {
7401
- context.logger.error(`[Lattice] Failed to persist entity to database:`, error);
7402
- }
7403
- }
7404
- return JSON.stringify({
7405
- success: true,
7406
- action: "ADD_ENTITY",
7407
- modelId,
7408
- entityId,
7409
- data: entityData,
7410
- message: `Added ${type} "${displayName || name}" to model. ${initialValues.length > 0 ? `Set initial values: ${initialValues.map((v) => `${v.key}=${v.value}`).join(", ")}` : "No initial values set."}`
7411
- });
7412
- },
7413
- toolSchema: {
7414
- name: "lattice_add_entity",
7415
- description: `Add a line item, account, period, or other entity to a Lattice model.
7416
-
7417
- **When to use:** When the user mentions:
7418
- - Adding a revenue line, expense category, or account
7419
- - Creating periods (Q1, Q2, Jan, Feb, etc.)
7420
- - Adding any measurable item to their model
7421
-
7422
- **Entity types:**
7423
- - line_item: Revenue streams, expense lines, KPIs
7424
- - account: Cash, AR, AP, inventory, etc.
7425
- - period: Q1 2024, Jan, FY2025, etc.
7426
- - category: Groups of line items (Operating Expenses)
7427
- - scenario: Base case, upside, downside
7428
- - custom: Any other structured element
7429
-
7430
- **Examples:**
7431
- - "Add Revenue" \u2192 line_item named "Revenue"
7432
- - "Create a Marketing Expenses category" \u2192 category named "Marketing Expenses"
7433
- - "Add quarters Q1 through Q4" \u2192 4 period entities`,
7434
- parameters: {
7435
- type: "object",
7436
- properties: {
7437
- modelId: {
7438
- type: "string",
7439
- description: "ID of the model to add the entity to"
7440
- },
7441
- name: {
7442
- type: "string",
7443
- description: "Internal name for the entity (used in formulas)"
7444
- },
7445
- type: {
7446
- type: "string",
7447
- enum: ["line_item", "account", "period", "category", "scenario", "custom"],
7448
- description: "Type of entity"
7449
- },
7450
- displayName: {
7451
- type: "string",
7452
- description: "Human-readable display name (defaults to name)"
7453
- },
7454
- initialValues: {
7455
- type: "array",
7456
- items: {
7457
- type: "object",
7458
- properties: {
7459
- key: { type: "string", description: 'Period or attribute key (e.g., "Q1_2024")' },
7460
- value: { type: "string", description: "The value (number or string)" },
7461
- dataType: {
7462
- type: "string",
7463
- enum: ["number", "currency", "percentage", "string"],
7464
- description: "Data type for the value"
7465
- }
7466
- },
7467
- required: ["key", "value"]
7468
- },
7469
- description: "Optional initial values for this entity"
7470
- }
7471
- },
7472
- required: ["modelId", "name", "type"]
7473
- }
7474
- }
7475
- })
7476
- };
7477
- var latticeSetValueTool = {
7478
- name: "lattice_set_value",
7479
- implementation: (context) => ({
7480
- toolFn: async (params) => {
7481
- const { modelId, entityName, attributeKey, value: rawValue } = params;
7482
- let value = rawValue;
7483
- const numValue = parseFloat(rawValue);
7484
- if (!isNaN(numValue)) {
7485
- value = numValue;
7486
- } else if (rawValue.toLowerCase() === "true") {
7487
- value = true;
7488
- } else if (rawValue.toLowerCase() === "false") {
7489
- value = false;
7490
- }
7491
- const entityId = entityName.toLowerCase().replace(/\s+/g, "_");
7492
- if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
7493
- try {
7494
- const model = await context.db.latticeModels.findById(modelId);
7495
- if (model) {
7496
- const entity = model.data.entities.find((e) => e.id === entityId || e.name === entityName);
7497
- if (entity) {
7498
- const attrIndex = entity.attributes.findIndex((a) => a.key === attributeKey);
7499
- const dataType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "string";
7500
- const attributeData = {
7501
- key: attributeKey,
7502
- value,
7503
- dataType,
7504
- isComputed: false
7505
- };
7506
- if (attrIndex >= 0) {
7507
- entity.attributes[attrIndex] = attributeData;
7508
- } else {
7509
- entity.attributes.push(attributeData);
7510
- }
7511
- entity.updatedAt = /* @__PURE__ */ new Date();
7512
- await context.db.latticeModels.update({
7513
- id: modelId,
7514
- data: model.data,
7515
- updatedAt: /* @__PURE__ */ new Date()
7516
- });
7517
- context.logger.info(`[Lattice] Set ${entityId}.${attributeKey} = ${value} in model ${modelId}`);
7518
- } else {
7519
- context.logger.warn(`[Lattice] Entity ${entityName} not found in model ${modelId}`);
7520
- }
7521
- } else {
7522
- context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
7523
- }
7524
- } catch (error) {
7525
- context.logger.error(`[Lattice] Failed to persist value to database:`, error);
7526
- }
7527
- }
7528
- return JSON.stringify({
7529
- success: true,
7530
- action: "SET_VALUE",
7531
- modelId,
7532
- data: {
7533
- entityName,
7534
- attributeKey,
7535
- value
7536
- },
7537
- message: `Set ${entityName}.${attributeKey} = ${value}`
7538
- });
7539
- },
7540
- toolSchema: {
7541
- name: "lattice_set_value",
7542
- description: `Set a specific value in a Lattice model.
7543
-
7544
- **When to use:** When the user provides a specific number:
7545
- - "Revenue in Q1 is 150,000"
7546
- - "Set marketing spend to 50k for January"
7547
- - "COGS is 40% of revenue" (use lattice_create_rule for formulas)
7548
-
7549
- **Important:** This tool is for setting raw input values. For calculated values (formulas), use \`lattice_create_rule\` instead.
7550
-
7551
- **Examples:**
7552
- - "Q1 revenue is $100,000" \u2192 entityName="Revenue", attributeKey="Q1", value=100000
7553
- - "Set headcount to 25" \u2192 entityName="Headcount", attributeKey="current", value=25`,
7554
- parameters: {
7555
- type: "object",
7556
- properties: {
7557
- modelId: {
7558
- type: "string",
7559
- description: "ID of the model"
7560
- },
7561
- entityName: {
7562
- type: "string",
7563
- description: "Name of the entity (line item, account, etc.)"
7564
- },
7565
- attributeKey: {
7566
- type: "string",
7567
- description: 'Period or attribute key (e.g., "Q1_2024", "current", "budget")'
7568
- },
7569
- value: {
7570
- type: "string",
7571
- description: "The value to set (will be parsed as number if numeric)"
7572
- }
7573
- },
7574
- required: ["modelId", "entityName", "attributeKey", "value"]
7575
- }
7576
- }
7577
- })
7578
- };
7579
- var latticeCreateRuleTool = {
7580
- name: "lattice_create_rule",
7581
- implementation: (context) => ({
7582
- toolFn: async (params) => {
7583
- const { modelId, name, description, formula } = params;
7584
- const parsedRule = parseFormula(formula);
7585
- const ruleId = `rule_${Date.now()}`;
7586
- const ruleData = {
7587
- id: ruleId,
7588
- name,
7589
- description: description || formula,
7590
- type: "formula",
7591
- definition: {
7592
- operation: parsedRule.operation,
7593
- inputs: parsedRule.inputs.map((ref) => ({
7594
- type: "entity",
7595
- ref
7596
- })),
7597
- output: {
7598
- targetEntityId: parsedRule.outputEntity.toLowerCase().replace(/\s+/g, "_"),
7599
- targetAttribute: "computed",
7600
- dataType: "number"
7601
- }
7602
- },
7603
- dependencies: parsedRule.inputs.map((i) => i.toLowerCase().replace(/\s+/g, "_")),
7604
- priority: 0,
7605
- enabled: true,
7606
- createdAt: /* @__PURE__ */ new Date(),
7607
- updatedAt: /* @__PURE__ */ new Date()
7608
- };
7609
- if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
7610
- try {
7611
- const model = await context.db.latticeModels.findById(modelId);
7612
- if (model) {
7613
- const existingIndex = model.rules.rules.findIndex((r) => r.id === ruleId || r.name === name);
7614
- if (existingIndex >= 0) {
7615
- model.rules.rules[existingIndex] = ruleData;
7616
- } else {
7617
- model.rules.rules.push(ruleData);
7618
- }
7619
- await context.db.latticeModels.update({
7620
- id: modelId,
7621
- rules: model.rules,
7622
- updatedAt: /* @__PURE__ */ new Date()
7623
- });
7624
- context.logger.info(`[Lattice] Created rule ${ruleId} in model ${modelId}`);
7625
- } else {
7626
- context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
7627
- }
7628
- } catch (error) {
7629
- context.logger.error(`[Lattice] Failed to persist rule to database:`, error);
7630
- }
7631
- }
7632
- return JSON.stringify({
7633
- success: true,
7634
- action: "CREATE_RULE",
7635
- modelId,
7636
- ruleId,
7637
- data: {
7638
- name,
7639
- description,
7640
- formula,
7641
- parsed: parsedRule
7642
- },
7643
- message: `Created rule "${name}": ${formula}`
7644
- });
7645
- },
7646
- toolSchema: {
7647
- name: "lattice_create_rule",
7648
- description: `Create a formula or calculation rule in a Lattice model.
7649
-
7650
- **When to use:** When the user defines a calculation:
7651
- - "Gross profit equals revenue minus COGS"
7652
- - "Net margin is net income divided by revenue times 100"
7653
- - "Total expenses is the sum of all expense line items"
7654
-
7655
- **Formula syntax (natural language):**
7656
- - Arithmetic: "X equals Y plus Z", "A minus B", "C times D"
7657
- - Aggregation: "sum of", "average of", "total"
7658
- - Percentage: "X percent of Y", "as a percentage"
7659
- - Comparison: "if X is greater than Y then A else B"
7660
-
7661
- **Examples:**
7662
- - "Gross Profit = Revenue - COGS"
7663
- - "Gross Margin = Gross Profit / Revenue * 100"
7664
- - "Total OpEx = sum of all Operating Expenses"
7665
- - "YoY Growth = (Current Year - Prior Year) / Prior Year * 100"`,
7666
- parameters: {
7667
- type: "object",
7668
- properties: {
7669
- modelId: {
7670
- type: "string",
7671
- description: "ID of the model"
7672
- },
7673
- name: {
7674
- type: "string",
7675
- description: 'Name for this rule (e.g., "Gross Profit Calculation")'
7676
- },
7677
- description: {
7678
- type: "string",
7679
- description: "Optional description of what this rule calculates"
7680
- },
7681
- formula: {
7682
- type: "string",
7683
- description: "The formula in natural language or equation format"
7684
- }
7685
- },
7686
- required: ["modelId", "name", "formula"]
7687
- }
7688
- }
7689
- })
7690
- };
7691
- var latticeQueryTool = {
7692
- name: "lattice_query",
7693
- implementation: () => ({
7694
- toolFn: async (params) => {
7695
- const { modelId, query } = params;
7696
- const parsedQuery = parseQuery(query);
7697
- return JSON.stringify({
7698
- success: true,
7699
- action: "QUERY",
7700
- modelId,
7701
- data: {
7702
- query,
7703
- parsed: parsedQuery
7704
- },
7705
- message: `Querying model for: ${query}`
7706
- });
7707
- },
7708
- toolSchema: {
7709
- name: "lattice_query",
7710
- description: `Query a Lattice model for specific values or aggregations.
7711
-
7712
- **When to use:** When the user asks about values in the model:
7713
- - "What's Q1 gross margin?"
7714
- - "Show me total revenue for 2024"
7715
- - "What are all the expense categories?"
7716
- - "Compare Q1 vs Q2 performance"
7717
-
7718
- **Query types:**
7719
- - Single value: "What is [entity] for [period]?"
7720
- - Aggregation: "Total [entity] for [range]"
7721
- - Comparison: "Compare [entity] across [periods]"
7722
- - List: "Show all [entity type]"
7723
-
7724
- **Examples:**
7725
- - "What's Q2 revenue?" \u2192 Returns specific value
7726
- - "Total revenue for 2024" \u2192 Sums Q1-Q4
7727
- - "Compare gross margin Q1 vs Q2" \u2192 Side-by-side comparison`,
7728
- parameters: {
7729
- type: "object",
7730
- properties: {
7731
- modelId: {
7732
- type: "string",
7733
- description: "ID of the model to query"
7734
- },
7735
- query: {
7736
- type: "string",
7737
- description: "Natural language query about the model"
7738
- }
7739
- },
7740
- required: ["modelId", "query"]
7741
- }
7742
- }
7743
- })
7744
- };
7745
- var latticeExplainTool = {
7746
- name: "lattice_explain",
7747
- implementation: () => ({
7748
- toolFn: async (params) => {
7749
- const { modelId, entityName, attributeKey } = params;
7750
- return JSON.stringify({
7751
- success: true,
7752
- action: "EXPLAIN",
7753
- modelId,
7754
- data: {
7755
- entityName,
7756
- attributeKey
7757
- },
7758
- message: `Explaining calculation for ${entityName}.${attributeKey}`
7759
- });
7760
- },
7761
- toolSchema: {
7762
- name: "lattice_explain",
7763
- description: `Explain how a value in a Lattice model was calculated.
7764
-
7765
- **When to use:** When the user wants to understand a calculation:
7766
- - "How did you calculate gross profit?"
7767
- - "Explain the Q2 margin number"
7768
- - "Show me the formula for total expenses"
7769
- - "Why is this number so high?"
7770
-
7771
- This tool traces the calculation chain from the target value back through all its inputs and rules, providing a step-by-step explanation.`,
7772
- parameters: {
7773
- type: "object",
7774
- properties: {
7775
- modelId: {
7776
- type: "string",
7777
- description: "ID of the model"
7778
- },
7779
- entityName: {
7780
- type: "string",
7781
- description: "Name of the entity containing the value to explain"
7782
- },
7783
- attributeKey: {
7784
- type: "string",
7785
- description: "Period or attribute key of the value to explain"
7786
- }
7787
- },
7788
- required: ["modelId", "entityName", "attributeKey"]
7789
- }
7790
- }
7791
- })
7792
- };
7793
- function parseFormula(formula) {
7794
- const normalized = formula.toLowerCase().trim();
7795
- const equalsMatch = normalized.match(/^(\w+)\s*(?:=|equals?)\s*(.+)$/i);
7796
- if (equalsMatch) {
7797
- const [, output, expression] = equalsMatch;
7798
- if (expression.includes("+") || expression.includes("plus")) {
7799
- return {
7800
- operation: "ADD",
7801
- outputEntity: output,
7802
- inputs: expression.split(/[+]|plus/i).map((s) => s.trim())
7803
- };
7804
- }
7805
- if (expression.includes("-") || expression.includes("minus")) {
7806
- return {
7807
- operation: "SUBTRACT",
7808
- outputEntity: output,
7809
- inputs: expression.split(/[-]|minus/i).map((s) => s.trim())
7810
- };
7811
- }
7812
- if (expression.includes("*") || expression.includes("times") || expression.includes("x")) {
7813
- return {
7814
- operation: "MULTIPLY",
7815
- outputEntity: output,
7816
- inputs: expression.split(/[*x]|times/i).map((s) => s.trim())
7817
- };
7818
- }
7819
- if (expression.includes("/") || expression.includes("divided")) {
7820
- return {
7821
- operation: "DIVIDE",
7822
- outputEntity: output,
7823
- inputs: expression.split(/[/]|divided by/i).map((s) => s.trim())
7824
- };
7825
- }
7826
- if (expression.includes("sum")) {
7827
- return {
7828
- operation: "SUM",
7829
- outputEntity: output,
7830
- inputs: [expression.replace(/sum of/i, "").trim()]
7831
- };
7832
- }
7833
- }
7834
- return {
7835
- operation: "REFERENCE",
7836
- outputEntity: "unknown",
7837
- inputs: [formula]
7838
- };
7839
- }
7840
- function parseQuery(query) {
7841
- const normalized = query.toLowerCase();
7842
- let type = "single_value";
7843
- if (normalized.includes("total") || normalized.includes("sum")) {
7844
- type = "aggregation";
7845
- } else if (normalized.includes("compare") || normalized.includes("vs")) {
7846
- type = "comparison";
7847
- } else if (normalized.includes("list") || normalized.includes("show all")) {
7848
- type = "list";
7849
- }
7850
- const entities = [];
7851
- const periods = [];
7852
- const periodPatterns = [/q[1-4]/gi, /\d{4}/g, /jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/gi];
7853
- for (const pattern of periodPatterns) {
7854
- const matches = query.match(pattern);
7855
- if (matches) {
7856
- periods.push(...matches);
7857
- }
7858
- }
7859
- const commonEntities = ["revenue", "expenses", "profit", "margin", "income", "cost", "cogs", "ebitda"];
7860
- for (const entity of commonEntities) {
7861
- if (normalized.includes(entity)) {
7862
- entities.push(entity);
7863
- }
7864
- }
7865
- return { type, entities, periods };
7866
- }
7867
-
7868
7246
  // ../../b4m-core/packages/services/dist/src/llm/tools/index.js
7869
7247
  var tools = {
7870
7248
  dice_roll: diceRollTool,
@@ -7892,14 +7270,7 @@ var tools = {
7892
7270
  create_file: createFileTool,
7893
7271
  glob_files: globFilesTool,
7894
7272
  grep_search: grepSearchTool,
7895
- delete_file: deleteFileTool,
7896
- // Lattice financial modeling tools
7897
- lattice_create_model: latticeCreateModelTool,
7898
- lattice_add_entity: latticeAddEntityTool,
7899
- lattice_set_value: latticeSetValueTool,
7900
- lattice_create_rule: latticeCreateRuleTool,
7901
- lattice_query: latticeQueryTool,
7902
- lattice_explain: latticeExplainTool
7273
+ delete_file: deleteFileTool
7903
7274
  };
7904
7275
  var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
7905
7276
  const context = {
@@ -8029,7 +7400,6 @@ var QuestStartBodySchema = z137.object({
8029
7400
  enableMementos: z137.boolean().optional(),
8030
7401
  enableArtifacts: z137.boolean().optional(),
8031
7402
  enableAgents: z137.boolean().optional(),
8032
- enableLattice: z137.boolean().optional(),
8033
7403
  promptMeta: PromptMetaZodSchema,
8034
7404
  tools: z137.array(z137.union([b4mLLMTools, z137.string()])).optional(),
8035
7405
  mcpServers: z137.array(z137.string()).optional(),
@@ -9390,10 +8760,13 @@ var ApiClient = class {
9390
8760
  }
9391
8761
  };
9392
8762
 
8763
+ // src/index.tsx
8764
+ import { isAxiosError as isAxiosError2 } from "axios";
8765
+
9393
8766
  // package.json
9394
8767
  var package_default = {
9395
8768
  name: "@bike4mind/cli",
9396
- version: "0.2.1-epic-lattice.17015+ef53d52c1",
8769
+ version: "0.2.1-feat-cli-usage-command.16999+815cdcd08",
9397
8770
  type: "module",
9398
8771
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
9399
8772
  license: "UNLICENSED",
@@ -9490,10 +8863,10 @@ var package_default = {
9490
8863
  },
9491
8864
  devDependencies: {
9492
8865
  "@bike4mind/agents": "0.1.0",
9493
- "@bike4mind/common": "2.36.1-epic-lattice.17015+ef53d52c1",
9494
- "@bike4mind/mcp": "1.19.4-epic-lattice.17015+ef53d52c1",
9495
- "@bike4mind/services": "2.29.1-epic-lattice.17015+ef53d52c1",
9496
- "@bike4mind/utils": "2.0.3-epic-lattice.17015+ef53d52c1",
8866
+ "@bike4mind/common": "2.36.1-feat-cli-usage-command.16999+815cdcd08",
8867
+ "@bike4mind/mcp": "1.19.4-feat-cli-usage-command.16999+815cdcd08",
8868
+ "@bike4mind/services": "2.29.1-feat-cli-usage-command.16999+815cdcd08",
8869
+ "@bike4mind/utils": "2.0.3-feat-cli-usage-command.16999+815cdcd08",
9497
8870
  "@types/diff": "^5.0.9",
9498
8871
  "@types/jsonwebtoken": "^9.0.4",
9499
8872
  "@types/node": "^22.9.0",
@@ -9504,9 +8877,14 @@ var package_default = {
9504
8877
  tsx: "^4.21.0",
9505
8878
  typescript: "^5.9.3"
9506
8879
  },
9507
- gitHead: "ef53d52c1d52fff50c780e7ae56cc814e38c030f"
8880
+ gitHead: "815cdcd0897c92e3713ce3e04c930b70332cb318"
9508
8881
  };
9509
8882
 
8883
+ // src/config/constants.ts
8884
+ var USAGE_DAYS = 30;
8885
+ var MODEL_NAME_COLUMN_WIDTH = 18;
8886
+ var USAGE_CACHE_TTL = 5 * 60 * 1e3;
8887
+
9510
8888
  // src/index.tsx
9511
8889
  process.removeAllListeners("warning");
9512
8890
  process.on("warning", (warning) => {
@@ -9517,6 +8895,7 @@ process.on("warning", (warning) => {
9517
8895
  });
9518
8896
  var exitTimestamp = null;
9519
8897
  var EXIT_TIMEOUT_MS = 2e3;
8898
+ var usageCache = null;
9520
8899
  function CliApp() {
9521
8900
  const { exit } = useApp();
9522
8901
  const [state, setState] = useState3({
@@ -9881,6 +9260,7 @@ Available commands:
9881
9260
  /login - Authenticate with your B4M account
9882
9261
  /logout - Clear authentication and sign out
9883
9262
  /whoami - Show current authenticated user
9263
+ /usage - Show credit usage and balance
9884
9264
  /save <name> - Save current session
9885
9265
  /sessions - List saved sessions
9886
9266
  /config - Show configuration
@@ -10051,6 +9431,103 @@ Tool Permissions:
10051
9431
  console.log(`Expires: ${new Date(authTokens.expiresAt).toLocaleString()}`);
10052
9432
  console.log("");
10053
9433
  break;
9434
+ case "usage": {
9435
+ const usageAuthTokens = await state.configStore.getAuthTokens();
9436
+ if (!usageAuthTokens || new Date(usageAuthTokens.expiresAt) <= /* @__PURE__ */ new Date()) {
9437
+ console.log("\nNot authenticated. Run /login to authenticate.\n");
9438
+ return;
9439
+ }
9440
+ console.log("\nFetching usage data...");
9441
+ const now = Date.now();
9442
+ let currentCredits;
9443
+ let transactions;
9444
+ if (usageCache && now - usageCache.timestamp < USAGE_CACHE_TTL) {
9445
+ currentCredits = usageCache.data.currentCredits;
9446
+ transactions = usageCache.data.transactions;
9447
+ console.log("(using cached data)\n");
9448
+ } else {
9449
+ try {
9450
+ const config = await state.configStore.get();
9451
+ const apiBaseURL = getApiUrl(config.apiConfig);
9452
+ const apiClient = new ApiClient(apiBaseURL, state.configStore);
9453
+ const [identifyResponse, transactionsResponse] = await Promise.all([
9454
+ apiClient.get("/api/identify"),
9455
+ apiClient.get(`/api/credits/transactions?days=${USAGE_DAYS}`)
9456
+ ]);
9457
+ currentCredits = identifyResponse.user.currentCredits;
9458
+ transactions = transactionsResponse || [];
9459
+ usageCache = {
9460
+ data: { currentCredits, transactions },
9461
+ timestamp: now
9462
+ };
9463
+ } catch (error) {
9464
+ if (isAxiosError2(error)) {
9465
+ if (error.response?.status === 401) {
9466
+ console.log("\n\u274C Authentication failed. Run /login to re-authenticate.\n");
9467
+ } else if (error.response?.status === 403) {
9468
+ console.log("\n\u274C Access denied. You may not have permission to view usage data.\n");
9469
+ } else if (error.response?.status === 404) {
9470
+ console.log("\n\u274C Usage endpoint not available on this server.\n");
9471
+ } else if (error.code === "ECONNREFUSED" || error.code === "ENOTFOUND") {
9472
+ console.log("\n\u274C Cannot connect to server. Check your API configuration with /api-info\n");
9473
+ } else {
9474
+ console.log(`
9475
+ \u274C Error fetching usage data: ${error.message}
9476
+ `);
9477
+ }
9478
+ } else if (error instanceof Error) {
9479
+ console.log(`
9480
+ \u274C Error: ${error.message}
9481
+ `);
9482
+ } else {
9483
+ console.log("\n\u274C Unexpected error fetching usage data. Please try again.\n");
9484
+ }
9485
+ break;
9486
+ }
9487
+ }
9488
+ const deductTransactions = transactions.filter(
9489
+ (t) => CREDIT_DEDUCT_TRANSACTION_TYPES.includes(t.type)
9490
+ );
9491
+ const totalCreditsUsed = deductTransactions.reduce((sum2, t) => sum2 + Math.abs(t.credits), 0);
9492
+ const dailyBurnRate = Math.round(totalCreditsUsed / USAGE_DAYS);
9493
+ let daysRemainingStr;
9494
+ if (dailyBurnRate === 0) {
9495
+ daysRemainingStr = "N/A (no usage)";
9496
+ } else {
9497
+ const daysRemaining = Math.floor(currentCredits / dailyBurnRate);
9498
+ daysRemainingStr = `~${daysRemaining.toLocaleString()} days`;
9499
+ }
9500
+ const usageByModel = {};
9501
+ for (const t of deductTransactions) {
9502
+ const modelName = t.metadata?.modelName || ("model" in t ? t.model : null) || "Unknown";
9503
+ usageByModel[modelName] = (usageByModel[modelName] || 0) + Math.abs(t.credits);
9504
+ }
9505
+ const sortedModels = Object.entries(usageByModel).sort((a, b) => b[1] - a[1]);
9506
+ console.log("\n Credit Usage Summary");
9507
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n");
9508
+ console.log(`Credits Balance: ${currentCredits.toLocaleString()}`);
9509
+ console.log(`Daily Burn Rate: ${dailyBurnRate.toLocaleString()} credits/day`);
9510
+ console.log(`Days Remaining: ${daysRemainingStr}`);
9511
+ if (currentCredits === 0) {
9512
+ console.log("\n\u26A0\uFE0F You have no credits remaining.");
9513
+ console.log("\u{1F4A1} Visit bike4mind.io to purchase more credits.\n");
9514
+ break;
9515
+ }
9516
+ if (sortedModels.length > 0) {
9517
+ console.log(`
9518
+ Usage by Model (Last ${USAGE_DAYS} days):`);
9519
+ for (const [model, credits] of sortedModels) {
9520
+ const percentage = totalCreditsUsed > 0 ? Math.round(credits / totalCreditsUsed * 100) : 0;
9521
+ const paddedModel = model.padEnd(MODEL_NAME_COLUMN_WIDTH);
9522
+ console.log(` ${paddedModel}${credits.toLocaleString()} credits (${percentage}%)`);
9523
+ }
9524
+ } else {
9525
+ console.log(`
9526
+ No usage data available for the last ${USAGE_DAYS} days.`);
9527
+ }
9528
+ console.log("");
9529
+ break;
9530
+ }
10054
9531
  default:
10055
9532
  console.log(`Unknown command: /${command}`);
10056
9533
  console.log("Type /help for available commands");