@bike4mind/cli 0.2.1-epic-lattice.17016 → 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
 
@@ -3323,19 +3327,7 @@ import { z as z127 } from "zod";
3323
3327
  var createArtifactSchema = z127.object({
3324
3328
  id: z127.string().optional(),
3325
3329
  // Allow custom ID for AI-generated artifacts
3326
- type: z127.enum([
3327
- "mermaid",
3328
- "recharts",
3329
- "python",
3330
- "react",
3331
- "html",
3332
- "svg",
3333
- "code",
3334
- "quest",
3335
- "file",
3336
- "questmaster",
3337
- "lattice"
3338
- ]),
3330
+ type: z127.enum(["mermaid", "recharts", "python", "react", "html", "svg", "code", "quest", "file", "questmaster"]),
3339
3331
  title: z127.string().min(1).max(255),
3340
3332
  description: z127.string().max(1e3).optional(),
3341
3333
  content: z127.string().min(1),
@@ -7251,644 +7243,6 @@ var deleteFileTool = {
7251
7243
  })
7252
7244
  };
7253
7245
 
7254
- // ../../b4m-core/packages/services/dist/src/llm/tools/implementation/lattice/index.js
7255
- function createModelData(name, modelType, userId, description, sessionId) {
7256
- const now = /* @__PURE__ */ new Date();
7257
- return {
7258
- name,
7259
- description: description || "",
7260
- modelType,
7261
- userId,
7262
- sessionId,
7263
- data: { entities: [], relationships: [] },
7264
- rules: { rules: [], rulesets: [] },
7265
- views: { views: [] },
7266
- settings: {
7267
- currency: "USD",
7268
- fiscalYearStart: "01-01",
7269
- periodGrain: "quarter",
7270
- defaultDecimalPlaces: 2,
7271
- negativeFormat: "parentheses"
7272
- },
7273
- scenarios: [],
7274
- operations: [],
7275
- operationIndex: -1,
7276
- version: 1,
7277
- createdAt: now,
7278
- updatedAt: now
7279
- };
7280
- }
7281
- var latticeCreateModelTool = {
7282
- name: "lattice_create_model",
7283
- implementation: (context) => ({
7284
- toolFn: async (params) => {
7285
- const { name, description, modelType = "custom" } = params;
7286
- const modelData = createModelData(name, modelType, context.userId, description);
7287
- let model;
7288
- let persistenceStatus = "unknown";
7289
- const dbKeys = Object.keys(context.db || {});
7290
- const hasLatticeModels = !!context.db?.latticeModels;
7291
- if (context.db.latticeModels) {
7292
- try {
7293
- model = await context.db.latticeModels.create(modelData);
7294
- persistenceStatus = `PERSISTED to MongoDB (id: ${model.id})`;
7295
- context.logger.info(`[Lattice] Created model ${model.id} in database`);
7296
- } catch (error) {
7297
- persistenceStatus = `FAILED: ${error instanceof Error ? error.message : String(error)}`;
7298
- context.logger.error(`[Lattice] Failed to persist model to database:`, error);
7299
- model = {
7300
- ...modelData,
7301
- id: `lattice_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
7302
- };
7303
- }
7304
- } else {
7305
- persistenceStatus = `IN-MEMORY (no latticeModels adapter). DB keys: [${dbKeys.join(", ")}]`;
7306
- context.logger.warn("[Lattice] No database adapter available, creating in-memory model");
7307
- model = {
7308
- ...modelData,
7309
- id: `lattice_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
7310
- };
7311
- }
7312
- const artifactData = {
7313
- type: "lattice",
7314
- id: model.id,
7315
- title: name,
7316
- content: JSON.stringify(model),
7317
- metadata: {
7318
- modelType,
7319
- periodGrain: model.settings?.periodGrain || "quarter",
7320
- currency: model.settings?.currency || "USD",
7321
- entityCount: model.data?.entities?.length || 0,
7322
- ruleCount: model.rules?.rules?.length || 0,
7323
- // DEBUG: Include persistence info in metadata
7324
- _debug: {
7325
- persistenceStatus,
7326
- hasLatticeModels,
7327
- dbKeys
7328
- }
7329
- },
7330
- createdAt: model.createdAt?.toISOString?.() || (/* @__PURE__ */ new Date()).toISOString(),
7331
- updatedAt: model.updatedAt?.toISOString?.() || (/* @__PURE__ */ new Date()).toISOString()
7332
- };
7333
- return `I've created a new ${modelType} financial model called "${name}".
7334
-
7335
- <artifact identifier="${model.id}" type="application/vnd.b4m.lattice" title="${name}">
7336
- ${JSON.stringify(artifactData, null, 2)}
7337
- </artifact>
7338
-
7339
- You can now add line items, set values, and create formulas. Try saying:
7340
- - "Add Revenue and COGS line items"
7341
- - "Set Q1 Revenue to 100,000"
7342
- - "Gross Profit equals Revenue minus COGS"`;
7343
- },
7344
- toolSchema: {
7345
- name: "lattice_create_model",
7346
- description: `Create a new Lattice financial model. This is the first step when building a pro-forma or financial analysis.
7347
-
7348
- **When to use:** When the user wants to:
7349
- - Create a new financial model, spreadsheet, or pro-forma
7350
- - Start building an income statement, balance sheet, or cash flow
7351
- - Set up a SaaS metrics dashboard
7352
- - Begin any structured numerical analysis
7353
-
7354
- **Model types:**
7355
- - income_statement: P&L with revenue, expenses, profits
7356
- - balance_sheet: Assets, liabilities, equity
7357
- - cashflow: Operating, investing, financing cash flows
7358
- - saas_metrics: MRR, ARR, churn, CAC, LTV
7359
- - custom: Any other structured model
7360
-
7361
- After creating a model, add entities with \`lattice_add_entity\` and define formulas with \`lattice_create_rule\`.`,
7362
- parameters: {
7363
- type: "object",
7364
- properties: {
7365
- name: {
7366
- type: "string",
7367
- description: 'Name of the model (e.g., "2024 Budget", "Q1 Forecast")'
7368
- },
7369
- description: {
7370
- type: "string",
7371
- description: "Optional description of what this model represents"
7372
- },
7373
- modelType: {
7374
- type: "string",
7375
- enum: ["income_statement", "balance_sheet", "cashflow", "saas_metrics", "custom"],
7376
- description: "Type of financial model to create"
7377
- }
7378
- },
7379
- required: ["name"]
7380
- }
7381
- }
7382
- })
7383
- };
7384
- var latticeAddEntityTool = {
7385
- name: "lattice_add_entity",
7386
- implementation: (context) => ({
7387
- toolFn: async (params) => {
7388
- const { modelId, name, type, displayName, initialValues = [] } = params;
7389
- const entityId = name.toLowerCase().replace(/\s+/g, "_");
7390
- const entityData = {
7391
- id: entityId,
7392
- type,
7393
- name,
7394
- displayName: displayName || name,
7395
- attributes: initialValues.map((v) => ({
7396
- key: v.key,
7397
- value: v.value,
7398
- dataType: v.dataType || (typeof v.value === "number" ? "number" : "string"),
7399
- isComputed: false
7400
- })),
7401
- metadata: {},
7402
- createdAt: /* @__PURE__ */ new Date(),
7403
- updatedAt: /* @__PURE__ */ new Date()
7404
- };
7405
- if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
7406
- try {
7407
- const model = await context.db.latticeModels.findById(modelId);
7408
- if (model) {
7409
- const existingIndex = model.data.entities.findIndex((e) => e.id === entityId);
7410
- if (existingIndex >= 0) {
7411
- model.data.entities[existingIndex] = entityData;
7412
- } else {
7413
- model.data.entities.push(entityData);
7414
- }
7415
- await context.db.latticeModels.update({
7416
- id: modelId,
7417
- data: model.data,
7418
- updatedAt: /* @__PURE__ */ new Date()
7419
- });
7420
- context.logger.info(`[Lattice] Added entity ${entityId} to model ${modelId}`);
7421
- } else {
7422
- context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
7423
- }
7424
- } catch (error) {
7425
- context.logger.error(`[Lattice] Failed to persist entity to database:`, error);
7426
- }
7427
- }
7428
- return JSON.stringify({
7429
- success: true,
7430
- action: "ADD_ENTITY",
7431
- modelId,
7432
- entityId,
7433
- data: entityData,
7434
- 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."}`
7435
- });
7436
- },
7437
- toolSchema: {
7438
- name: "lattice_add_entity",
7439
- description: `Add a line item, account, period, or other entity to a Lattice model.
7440
-
7441
- **When to use:** When the user mentions:
7442
- - Adding a revenue line, expense category, or account
7443
- - Creating periods (Q1, Q2, Jan, Feb, etc.)
7444
- - Adding any measurable item to their model
7445
-
7446
- **Entity types:**
7447
- - line_item: Revenue streams, expense lines, KPIs
7448
- - account: Cash, AR, AP, inventory, etc.
7449
- - period: Q1 2024, Jan, FY2025, etc.
7450
- - category: Groups of line items (Operating Expenses)
7451
- - scenario: Base case, upside, downside
7452
- - custom: Any other structured element
7453
-
7454
- **Examples:**
7455
- - "Add Revenue" \u2192 line_item named "Revenue"
7456
- - "Create a Marketing Expenses category" \u2192 category named "Marketing Expenses"
7457
- - "Add quarters Q1 through Q4" \u2192 4 period entities`,
7458
- parameters: {
7459
- type: "object",
7460
- properties: {
7461
- modelId: {
7462
- type: "string",
7463
- description: "ID of the model to add the entity to"
7464
- },
7465
- name: {
7466
- type: "string",
7467
- description: "Internal name for the entity (used in formulas)"
7468
- },
7469
- type: {
7470
- type: "string",
7471
- enum: ["line_item", "account", "period", "category", "scenario", "custom"],
7472
- description: "Type of entity"
7473
- },
7474
- displayName: {
7475
- type: "string",
7476
- description: "Human-readable display name (defaults to name)"
7477
- },
7478
- initialValues: {
7479
- type: "array",
7480
- items: {
7481
- type: "object",
7482
- properties: {
7483
- key: { type: "string", description: 'Period or attribute key (e.g., "Q1_2024")' },
7484
- value: { type: "string", description: "The value (number or string)" },
7485
- dataType: {
7486
- type: "string",
7487
- enum: ["number", "currency", "percentage", "string"],
7488
- description: "Data type for the value"
7489
- }
7490
- },
7491
- required: ["key", "value"]
7492
- },
7493
- description: "Optional initial values for this entity"
7494
- }
7495
- },
7496
- required: ["modelId", "name", "type"]
7497
- }
7498
- }
7499
- })
7500
- };
7501
- var latticeSetValueTool = {
7502
- name: "lattice_set_value",
7503
- implementation: (context) => ({
7504
- toolFn: async (params) => {
7505
- const { modelId, entityName, attributeKey, value: rawValue } = params;
7506
- let value = rawValue;
7507
- const numValue = parseFloat(rawValue);
7508
- if (!isNaN(numValue)) {
7509
- value = numValue;
7510
- } else if (rawValue.toLowerCase() === "true") {
7511
- value = true;
7512
- } else if (rawValue.toLowerCase() === "false") {
7513
- value = false;
7514
- }
7515
- const entityId = entityName.toLowerCase().replace(/\s+/g, "_");
7516
- if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
7517
- try {
7518
- const model = await context.db.latticeModels.findById(modelId);
7519
- if (model) {
7520
- const entity = model.data.entities.find((e) => e.id === entityId || e.name === entityName);
7521
- if (entity) {
7522
- const attrIndex = entity.attributes.findIndex((a) => a.key === attributeKey);
7523
- const dataType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "string";
7524
- const attributeData = {
7525
- key: attributeKey,
7526
- value,
7527
- dataType,
7528
- isComputed: false
7529
- };
7530
- if (attrIndex >= 0) {
7531
- entity.attributes[attrIndex] = attributeData;
7532
- } else {
7533
- entity.attributes.push(attributeData);
7534
- }
7535
- entity.updatedAt = /* @__PURE__ */ new Date();
7536
- await context.db.latticeModels.update({
7537
- id: modelId,
7538
- data: model.data,
7539
- updatedAt: /* @__PURE__ */ new Date()
7540
- });
7541
- context.logger.info(`[Lattice] Set ${entityId}.${attributeKey} = ${value} in model ${modelId}`);
7542
- } else {
7543
- context.logger.warn(`[Lattice] Entity ${entityName} not found in model ${modelId}`);
7544
- }
7545
- } else {
7546
- context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
7547
- }
7548
- } catch (error) {
7549
- context.logger.error(`[Lattice] Failed to persist value to database:`, error);
7550
- }
7551
- }
7552
- return JSON.stringify({
7553
- success: true,
7554
- action: "SET_VALUE",
7555
- modelId,
7556
- data: {
7557
- entityName,
7558
- attributeKey,
7559
- value
7560
- },
7561
- message: `Set ${entityName}.${attributeKey} = ${value}`
7562
- });
7563
- },
7564
- toolSchema: {
7565
- name: "lattice_set_value",
7566
- description: `Set a specific value in a Lattice model.
7567
-
7568
- **When to use:** When the user provides a specific number:
7569
- - "Revenue in Q1 is 150,000"
7570
- - "Set marketing spend to 50k for January"
7571
- - "COGS is 40% of revenue" (use lattice_create_rule for formulas)
7572
-
7573
- **Important:** This tool is for setting raw input values. For calculated values (formulas), use \`lattice_create_rule\` instead.
7574
-
7575
- **Examples:**
7576
- - "Q1 revenue is $100,000" \u2192 entityName="Revenue", attributeKey="Q1", value=100000
7577
- - "Set headcount to 25" \u2192 entityName="Headcount", attributeKey="current", value=25`,
7578
- parameters: {
7579
- type: "object",
7580
- properties: {
7581
- modelId: {
7582
- type: "string",
7583
- description: "ID of the model"
7584
- },
7585
- entityName: {
7586
- type: "string",
7587
- description: "Name of the entity (line item, account, etc.)"
7588
- },
7589
- attributeKey: {
7590
- type: "string",
7591
- description: 'Period or attribute key (e.g., "Q1_2024", "current", "budget")'
7592
- },
7593
- value: {
7594
- type: "string",
7595
- description: "The value to set (will be parsed as number if numeric)"
7596
- }
7597
- },
7598
- required: ["modelId", "entityName", "attributeKey", "value"]
7599
- }
7600
- }
7601
- })
7602
- };
7603
- var latticeCreateRuleTool = {
7604
- name: "lattice_create_rule",
7605
- implementation: (context) => ({
7606
- toolFn: async (params) => {
7607
- const { modelId, name, description, formula } = params;
7608
- const parsedRule = parseFormula(formula);
7609
- const ruleId = `rule_${Date.now()}`;
7610
- const ruleData = {
7611
- id: ruleId,
7612
- name,
7613
- description: description || formula,
7614
- type: "formula",
7615
- definition: {
7616
- operation: parsedRule.operation,
7617
- inputs: parsedRule.inputs.map((ref) => ({
7618
- type: "entity",
7619
- ref
7620
- })),
7621
- output: {
7622
- targetEntityId: parsedRule.outputEntity.toLowerCase().replace(/\s+/g, "_"),
7623
- targetAttribute: "computed",
7624
- dataType: "number"
7625
- }
7626
- },
7627
- dependencies: parsedRule.inputs.map((i) => i.toLowerCase().replace(/\s+/g, "_")),
7628
- priority: 0,
7629
- enabled: true,
7630
- createdAt: /* @__PURE__ */ new Date(),
7631
- updatedAt: /* @__PURE__ */ new Date()
7632
- };
7633
- if (context.db.latticeModels && modelId && /^[a-f0-9]{24}$/.test(modelId)) {
7634
- try {
7635
- const model = await context.db.latticeModels.findById(modelId);
7636
- if (model) {
7637
- const existingIndex = model.rules.rules.findIndex((r) => r.id === ruleId || r.name === name);
7638
- if (existingIndex >= 0) {
7639
- model.rules.rules[existingIndex] = ruleData;
7640
- } else {
7641
- model.rules.rules.push(ruleData);
7642
- }
7643
- await context.db.latticeModels.update({
7644
- id: modelId,
7645
- rules: model.rules,
7646
- updatedAt: /* @__PURE__ */ new Date()
7647
- });
7648
- context.logger.info(`[Lattice] Created rule ${ruleId} in model ${modelId}`);
7649
- } else {
7650
- context.logger.warn(`[Lattice] Model ${modelId} not found in database`);
7651
- }
7652
- } catch (error) {
7653
- context.logger.error(`[Lattice] Failed to persist rule to database:`, error);
7654
- }
7655
- }
7656
- return JSON.stringify({
7657
- success: true,
7658
- action: "CREATE_RULE",
7659
- modelId,
7660
- ruleId,
7661
- data: {
7662
- name,
7663
- description,
7664
- formula,
7665
- parsed: parsedRule
7666
- },
7667
- message: `Created rule "${name}": ${formula}`
7668
- });
7669
- },
7670
- toolSchema: {
7671
- name: "lattice_create_rule",
7672
- description: `Create a formula or calculation rule in a Lattice model.
7673
-
7674
- **When to use:** When the user defines a calculation:
7675
- - "Gross profit equals revenue minus COGS"
7676
- - "Net margin is net income divided by revenue times 100"
7677
- - "Total expenses is the sum of all expense line items"
7678
-
7679
- **Formula syntax (natural language):**
7680
- - Arithmetic: "X equals Y plus Z", "A minus B", "C times D"
7681
- - Aggregation: "sum of", "average of", "total"
7682
- - Percentage: "X percent of Y", "as a percentage"
7683
- - Comparison: "if X is greater than Y then A else B"
7684
-
7685
- **Examples:**
7686
- - "Gross Profit = Revenue - COGS"
7687
- - "Gross Margin = Gross Profit / Revenue * 100"
7688
- - "Total OpEx = sum of all Operating Expenses"
7689
- - "YoY Growth = (Current Year - Prior Year) / Prior Year * 100"`,
7690
- parameters: {
7691
- type: "object",
7692
- properties: {
7693
- modelId: {
7694
- type: "string",
7695
- description: "ID of the model"
7696
- },
7697
- name: {
7698
- type: "string",
7699
- description: 'Name for this rule (e.g., "Gross Profit Calculation")'
7700
- },
7701
- description: {
7702
- type: "string",
7703
- description: "Optional description of what this rule calculates"
7704
- },
7705
- formula: {
7706
- type: "string",
7707
- description: "The formula in natural language or equation format"
7708
- }
7709
- },
7710
- required: ["modelId", "name", "formula"]
7711
- }
7712
- }
7713
- })
7714
- };
7715
- var latticeQueryTool = {
7716
- name: "lattice_query",
7717
- implementation: () => ({
7718
- toolFn: async (params) => {
7719
- const { modelId, query } = params;
7720
- const parsedQuery = parseQuery(query);
7721
- return JSON.stringify({
7722
- success: true,
7723
- action: "QUERY",
7724
- modelId,
7725
- data: {
7726
- query,
7727
- parsed: parsedQuery
7728
- },
7729
- message: `Querying model for: ${query}`
7730
- });
7731
- },
7732
- toolSchema: {
7733
- name: "lattice_query",
7734
- description: `Query a Lattice model for specific values or aggregations.
7735
-
7736
- **When to use:** When the user asks about values in the model:
7737
- - "What's Q1 gross margin?"
7738
- - "Show me total revenue for 2024"
7739
- - "What are all the expense categories?"
7740
- - "Compare Q1 vs Q2 performance"
7741
-
7742
- **Query types:**
7743
- - Single value: "What is [entity] for [period]?"
7744
- - Aggregation: "Total [entity] for [range]"
7745
- - Comparison: "Compare [entity] across [periods]"
7746
- - List: "Show all [entity type]"
7747
-
7748
- **Examples:**
7749
- - "What's Q2 revenue?" \u2192 Returns specific value
7750
- - "Total revenue for 2024" \u2192 Sums Q1-Q4
7751
- - "Compare gross margin Q1 vs Q2" \u2192 Side-by-side comparison`,
7752
- parameters: {
7753
- type: "object",
7754
- properties: {
7755
- modelId: {
7756
- type: "string",
7757
- description: "ID of the model to query"
7758
- },
7759
- query: {
7760
- type: "string",
7761
- description: "Natural language query about the model"
7762
- }
7763
- },
7764
- required: ["modelId", "query"]
7765
- }
7766
- }
7767
- })
7768
- };
7769
- var latticeExplainTool = {
7770
- name: "lattice_explain",
7771
- implementation: () => ({
7772
- toolFn: async (params) => {
7773
- const { modelId, entityName, attributeKey } = params;
7774
- return JSON.stringify({
7775
- success: true,
7776
- action: "EXPLAIN",
7777
- modelId,
7778
- data: {
7779
- entityName,
7780
- attributeKey
7781
- },
7782
- message: `Explaining calculation for ${entityName}.${attributeKey}`
7783
- });
7784
- },
7785
- toolSchema: {
7786
- name: "lattice_explain",
7787
- description: `Explain how a value in a Lattice model was calculated.
7788
-
7789
- **When to use:** When the user wants to understand a calculation:
7790
- - "How did you calculate gross profit?"
7791
- - "Explain the Q2 margin number"
7792
- - "Show me the formula for total expenses"
7793
- - "Why is this number so high?"
7794
-
7795
- This tool traces the calculation chain from the target value back through all its inputs and rules, providing a step-by-step explanation.`,
7796
- parameters: {
7797
- type: "object",
7798
- properties: {
7799
- modelId: {
7800
- type: "string",
7801
- description: "ID of the model"
7802
- },
7803
- entityName: {
7804
- type: "string",
7805
- description: "Name of the entity containing the value to explain"
7806
- },
7807
- attributeKey: {
7808
- type: "string",
7809
- description: "Period or attribute key of the value to explain"
7810
- }
7811
- },
7812
- required: ["modelId", "entityName", "attributeKey"]
7813
- }
7814
- }
7815
- })
7816
- };
7817
- function parseFormula(formula) {
7818
- const normalized = formula.toLowerCase().trim();
7819
- const equalsMatch = normalized.match(/^(\w+)\s*(?:=|equals?)\s*(.+)$/i);
7820
- if (equalsMatch) {
7821
- const [, output, expression] = equalsMatch;
7822
- if (expression.includes("+") || expression.includes("plus")) {
7823
- return {
7824
- operation: "ADD",
7825
- outputEntity: output,
7826
- inputs: expression.split(/[+]|plus/i).map((s) => s.trim())
7827
- };
7828
- }
7829
- if (expression.includes("-") || expression.includes("minus")) {
7830
- return {
7831
- operation: "SUBTRACT",
7832
- outputEntity: output,
7833
- inputs: expression.split(/[-]|minus/i).map((s) => s.trim())
7834
- };
7835
- }
7836
- if (expression.includes("*") || expression.includes("times") || expression.includes("x")) {
7837
- return {
7838
- operation: "MULTIPLY",
7839
- outputEntity: output,
7840
- inputs: expression.split(/[*x]|times/i).map((s) => s.trim())
7841
- };
7842
- }
7843
- if (expression.includes("/") || expression.includes("divided")) {
7844
- return {
7845
- operation: "DIVIDE",
7846
- outputEntity: output,
7847
- inputs: expression.split(/[/]|divided by/i).map((s) => s.trim())
7848
- };
7849
- }
7850
- if (expression.includes("sum")) {
7851
- return {
7852
- operation: "SUM",
7853
- outputEntity: output,
7854
- inputs: [expression.replace(/sum of/i, "").trim()]
7855
- };
7856
- }
7857
- }
7858
- return {
7859
- operation: "REFERENCE",
7860
- outputEntity: "unknown",
7861
- inputs: [formula]
7862
- };
7863
- }
7864
- function parseQuery(query) {
7865
- const normalized = query.toLowerCase();
7866
- let type = "single_value";
7867
- if (normalized.includes("total") || normalized.includes("sum")) {
7868
- type = "aggregation";
7869
- } else if (normalized.includes("compare") || normalized.includes("vs")) {
7870
- type = "comparison";
7871
- } else if (normalized.includes("list") || normalized.includes("show all")) {
7872
- type = "list";
7873
- }
7874
- const entities = [];
7875
- const periods = [];
7876
- const periodPatterns = [/q[1-4]/gi, /\d{4}/g, /jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec/gi];
7877
- for (const pattern of periodPatterns) {
7878
- const matches = query.match(pattern);
7879
- if (matches) {
7880
- periods.push(...matches);
7881
- }
7882
- }
7883
- const commonEntities = ["revenue", "expenses", "profit", "margin", "income", "cost", "cogs", "ebitda"];
7884
- for (const entity of commonEntities) {
7885
- if (normalized.includes(entity)) {
7886
- entities.push(entity);
7887
- }
7888
- }
7889
- return { type, entities, periods };
7890
- }
7891
-
7892
7246
  // ../../b4m-core/packages/services/dist/src/llm/tools/index.js
7893
7247
  var tools = {
7894
7248
  dice_roll: diceRollTool,
@@ -7916,14 +7270,7 @@ var tools = {
7916
7270
  create_file: createFileTool,
7917
7271
  glob_files: globFilesTool,
7918
7272
  grep_search: grepSearchTool,
7919
- delete_file: deleteFileTool,
7920
- // Lattice financial modeling tools
7921
- lattice_create_model: latticeCreateModelTool,
7922
- lattice_add_entity: latticeAddEntityTool,
7923
- lattice_set_value: latticeSetValueTool,
7924
- lattice_create_rule: latticeCreateRuleTool,
7925
- lattice_query: latticeQueryTool,
7926
- lattice_explain: latticeExplainTool
7273
+ delete_file: deleteFileTool
7927
7274
  };
7928
7275
  var generateTools = (userId, user, logger2, { db }, storage, imageGenerateStorage, statusUpdate, onStart, onFinish, llm, config, model) => {
7929
7276
  const context = {
@@ -8053,7 +7400,6 @@ var QuestStartBodySchema = z137.object({
8053
7400
  enableMementos: z137.boolean().optional(),
8054
7401
  enableArtifacts: z137.boolean().optional(),
8055
7402
  enableAgents: z137.boolean().optional(),
8056
- enableLattice: z137.boolean().optional(),
8057
7403
  promptMeta: PromptMetaZodSchema,
8058
7404
  tools: z137.array(z137.union([b4mLLMTools, z137.string()])).optional(),
8059
7405
  mcpServers: z137.array(z137.string()).optional(),
@@ -9414,10 +8760,13 @@ var ApiClient = class {
9414
8760
  }
9415
8761
  };
9416
8762
 
8763
+ // src/index.tsx
8764
+ import { isAxiosError as isAxiosError2 } from "axios";
8765
+
9417
8766
  // package.json
9418
8767
  var package_default = {
9419
8768
  name: "@bike4mind/cli",
9420
- version: "0.2.1-epic-lattice.17016+de930b247",
8769
+ version: "0.2.1-feat-cli-usage-command.16999+815cdcd08",
9421
8770
  type: "module",
9422
8771
  description: "Interactive CLI tool for Bike4Mind with ReAct agents",
9423
8772
  license: "UNLICENSED",
@@ -9514,10 +8863,10 @@ var package_default = {
9514
8863
  },
9515
8864
  devDependencies: {
9516
8865
  "@bike4mind/agents": "0.1.0",
9517
- "@bike4mind/common": "2.36.1-epic-lattice.17016+de930b247",
9518
- "@bike4mind/mcp": "1.19.4-epic-lattice.17016+de930b247",
9519
- "@bike4mind/services": "2.29.1-epic-lattice.17016+de930b247",
9520
- "@bike4mind/utils": "2.0.3-epic-lattice.17016+de930b247",
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",
9521
8870
  "@types/diff": "^5.0.9",
9522
8871
  "@types/jsonwebtoken": "^9.0.4",
9523
8872
  "@types/node": "^22.9.0",
@@ -9528,9 +8877,14 @@ var package_default = {
9528
8877
  tsx: "^4.21.0",
9529
8878
  typescript: "^5.9.3"
9530
8879
  },
9531
- gitHead: "de930b24759febad0c3d6d6de321c3ed7aa01bbc"
8880
+ gitHead: "815cdcd0897c92e3713ce3e04c930b70332cb318"
9532
8881
  };
9533
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
+
9534
8888
  // src/index.tsx
9535
8889
  process.removeAllListeners("warning");
9536
8890
  process.on("warning", (warning) => {
@@ -9541,6 +8895,7 @@ process.on("warning", (warning) => {
9541
8895
  });
9542
8896
  var exitTimestamp = null;
9543
8897
  var EXIT_TIMEOUT_MS = 2e3;
8898
+ var usageCache = null;
9544
8899
  function CliApp() {
9545
8900
  const { exit } = useApp();
9546
8901
  const [state, setState] = useState3({
@@ -9905,6 +9260,7 @@ Available commands:
9905
9260
  /login - Authenticate with your B4M account
9906
9261
  /logout - Clear authentication and sign out
9907
9262
  /whoami - Show current authenticated user
9263
+ /usage - Show credit usage and balance
9908
9264
  /save <name> - Save current session
9909
9265
  /sessions - List saved sessions
9910
9266
  /config - Show configuration
@@ -10075,6 +9431,103 @@ Tool Permissions:
10075
9431
  console.log(`Expires: ${new Date(authTokens.expiresAt).toLocaleString()}`);
10076
9432
  console.log("");
10077
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
+ }
10078
9531
  default:
10079
9532
  console.log(`Unknown command: /${command}`);
10080
9533
  console.log("Type /help for available commands");