brownian-code 2026.2.11 → 2026.2.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brownian-code",
3
- "version": "2026.2.11",
3
+ "version": "2026.2.13",
4
4
  "description": "Brownian Code - AI agent for crypto research",
5
5
  "type": "module",
6
6
  "main": "src/index.tsx",
@@ -1,7 +1,7 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Box, Text, useInput } from 'ink';
3
3
  import { colors } from '../theme.js';
4
- import { PROVIDERS as PROVIDER_DEFS } from '@/providers';
4
+ import { PROVIDERS as PROVIDER_DEFS } from '../providers.js';
5
5
 
6
6
  export interface Model {
7
7
  id: string; // API model identifier (e.g., "claude-opus-4-6")
package/src/index.tsx CHANGED
@@ -21,6 +21,7 @@ import React from 'react';
21
21
  import { render } from 'ink';
22
22
  import { config } from 'dotenv';
23
23
  import { CLI } from './cli.js';
24
+ import { checkForUpdate } from './utils/update-checker.js';
24
25
 
25
26
  // Handle --version / -v
26
27
  if (process.argv.includes('--version') || process.argv.includes('-v')) {
@@ -65,6 +66,10 @@ More info: https://brownian.xyz
65
66
  process.exit(0);
66
67
  }
67
68
 
69
+ // Check for updates (non-blocking, best-effort)
70
+ const updateMsg = checkForUpdate(projectRoot);
71
+ if (updateMsg) console.log(`\n ${updateMsg}\n`);
72
+
68
73
  // Load environment variables
69
74
  config({ quiet: true });
70
75
 
package/src/model/llm.ts CHANGED
@@ -8,10 +8,10 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models';
8
8
  import { StructuredToolInterface } from '@langchain/core/tools';
9
9
  import { Runnable } from '@langchain/core/runnables';
10
10
  import { z } from 'zod';
11
- import { DEFAULT_SYSTEM_PROMPT } from '@/agent/prompts';
12
- import type { TokenUsage } from '@/agent/types';
13
- import { logger } from '@/utils';
14
- import { resolveProvider, getProviderById } from '@/providers';
11
+ import { DEFAULT_SYSTEM_PROMPT } from '../agent/prompts.js';
12
+ import type { TokenUsage } from '../agent/types.js';
13
+ import { logger } from '../utils/index.js';
14
+ import { resolveProvider, getProviderById } from '../providers.js';
15
15
 
16
16
  export const DEFAULT_PROVIDER = 'anthropic';
17
17
  export const DEFAULT_MODEL = 'claude-sonnet-4-5';
@@ -2,7 +2,7 @@ import { DynamicStructuredTool } from '@langchain/core/tools';
2
2
  import { chromium, Browser, Page } from 'playwright';
3
3
  import { z } from 'zod';
4
4
  import { formatToolResult } from '../types.js';
5
- import { logger } from '@/utils';
5
+ import { logger } from '../../utils/index.js';
6
6
 
7
7
  let browser: Browser | null = null;
8
8
  let page: Page | null = null;
@@ -3,7 +3,7 @@ import { ExaSearchResults } from '@langchain/exa';
3
3
  import Exa from 'exa-js';
4
4
  import { z } from 'zod';
5
5
  import { formatToolResult, parseSearchResults } from '../types.js';
6
- import { logger } from '@/utils';
6
+ import { logger } from '../../utils/index.js';
7
7
 
8
8
  // Lazily initialized to avoid errors when API key is not set
9
9
  let exaTool: ExaSearchResults | null = null;
package/src/utils/env.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from 'fs';
2
2
  import { config } from 'dotenv';
3
- import { getProviderById } from '@/providers';
3
+ import { getProviderById } from '../providers.js';
4
4
 
5
5
  // Load .env on module import
6
6
  config({ quiet: true });
@@ -0,0 +1,110 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { execSync } from 'child_process';
4
+
5
+ const PACKAGE_NAME = 'brownian-code';
6
+ const CHECK_INTERVAL_MS = 4 * 60 * 60 * 1000; // 4 hours
7
+
8
+ interface UpdateCache {
9
+ lastCheck: number;
10
+ latestVersion: string | null;
11
+ }
12
+
13
+ function getCacheDir(): string {
14
+ const home = process.env.HOME || process.env.USERPROFILE || '/tmp';
15
+ const dir = join(home, '.brownian');
16
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
17
+ return dir;
18
+ }
19
+
20
+ function getCachePath(): string {
21
+ return join(getCacheDir(), 'update-check.json');
22
+ }
23
+
24
+ function readCache(): UpdateCache | null {
25
+ try {
26
+ return JSON.parse(readFileSync(getCachePath(), 'utf-8'));
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ function writeCache(cache: UpdateCache): void {
33
+ try {
34
+ writeFileSync(getCachePath(), JSON.stringify(cache));
35
+ } catch {
36
+ // Silently fail — update check is best-effort
37
+ }
38
+ }
39
+
40
+ function getCurrentVersion(projectRoot: string): string | null {
41
+ try {
42
+ const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf-8'));
43
+ return pkg.version ?? null;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ function fetchLatestVersion(): string | null {
50
+ try {
51
+ const result = execSync(`npm view ${PACKAGE_NAME} version`, {
52
+ timeout: 5000,
53
+ stdio: ['ignore', 'pipe', 'ignore'],
54
+ encoding: 'utf-8',
55
+ });
56
+ return result.trim() || null;
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ function isNewer(latest: string, current: string): boolean {
63
+ const lParts = latest.split('.').map(Number);
64
+ const cParts = current.split('.').map(Number);
65
+ for (let i = 0; i < Math.max(lParts.length, cParts.length); i++) {
66
+ const l = lParts[i] ?? 0;
67
+ const c = cParts[i] ?? 0;
68
+ if (l > c) return true;
69
+ if (l < c) return false;
70
+ }
71
+ return false;
72
+ }
73
+
74
+ /**
75
+ * Check for updates in the background. Returns an update message if a
76
+ * newer version is available, or null. Never throws.
77
+ */
78
+ export function checkForUpdate(projectRoot: string): string | null {
79
+ try {
80
+ const current = getCurrentVersion(projectRoot);
81
+ if (!current) return null;
82
+
83
+ const cache = readCache();
84
+ const now = Date.now();
85
+
86
+ // Use cached result if recent enough
87
+ if (cache && now - cache.lastCheck < CHECK_INTERVAL_MS) {
88
+ if (cache.latestVersion && isNewer(cache.latestVersion, current)) {
89
+ return formatMessage(current, cache.latestVersion);
90
+ }
91
+ return null;
92
+ }
93
+
94
+ // Fetch fresh version from registry
95
+ const latest = fetchLatestVersion();
96
+ writeCache({ lastCheck: now, latestVersion: latest });
97
+
98
+ if (latest && isNewer(latest, current)) {
99
+ return formatMessage(current, latest);
100
+ }
101
+
102
+ return null;
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
107
+
108
+ function formatMessage(current: string, latest: string): string {
109
+ return `Update available: v${current} → v${latest}. Run: bun add -g brownian-code@latest`;
110
+ }