@blockrun/franklin 3.2.0 → 3.2.2

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.
Files changed (2) hide show
  1. package/dist/banner.js +137 -10
  2. package/package.json +1 -1
package/dist/banner.js CHANGED
@@ -1,5 +1,45 @@
1
1
  import chalk from 'chalk';
2
- // "FRANKLIN" the AI agent with a wallet
2
+ // ─── Ben Franklin portrait ─────────────────────────────────────────────────
3
+ //
4
+ // Generated once, at build time, from the Joseph Duplessis 1785 oil painting
5
+ // of Benjamin Franklin (same source as the portrait on the US $100 bill).
6
+ // Public domain image from Wikimedia Commons:
7
+ // https://commons.wikimedia.org/wiki/File:BenFranklinDuplessis.jpg
8
+ //
9
+ // Pipeline:
10
+ // 1. Crop the 2403×2971 original to a 1400×1400 square centred on the face
11
+ // (sips --cropToHeightWidth 1400 1400 --cropOffset 400 500)
12
+ // 2. Convert via chafa:
13
+ // chafa --size=30x14 --symbols=block --colors=256 ben-face.jpg
14
+ // 3. Strip cursor visibility control codes (\x1b[?25l / \x1b[?25h)
15
+ // 4. Paste here as hex-escaped string array (readable + diff-friendly)
16
+ //
17
+ // Visible dimensions: ~28 characters wide × 14 rows tall.
18
+ //
19
+ // Rendered best in a 256-color or truecolor terminal. Degrades gracefully
20
+ // on ancient terminals — but those are long gone and we don't support them.
21
+ const BEN_PORTRAIT_ROWS = [
22
+ '\x1b[0m\x1b[38;5;16;48;5;16m \x1b[38;5;232m▁\x1b[38;5;235;48;5;232m▂\x1b[38;5;58;48;5;233m▄\x1b[38;5;95;48;5;234m▆\x1b[38;5;137;48;5;58m▄\x1b[38;5;173m▅\x1b[48;5;94m▅\x1b[48;5;58m▆▅\x1b[48;5;237m▄\x1b[38;5;137;48;5;234m▃\x1b[38;5;235;48;5;233m▂ \x1b[38;5;233;48;5;232m▂▅\x1b[48;5;233m \x1b[0m',
23
+ '\x1b[38;5;16;48;5;16m \x1b[38;5;235;48;5;232m▗\x1b[38;5;233;48;5;236m▘\x1b[38;5;8;48;5;239m▌\x1b[38;5;95;48;5;137m▋\x1b[38;5;137;48;5;179m▘ \x1b[38;5;179;48;5;173m▃\x1b[48;5;179m \x1b[48;5;173m▊\x1b[38;5;58;48;5;137m▝\x1b[38;5;94;48;5;235m▖\x1b[38;5;234;48;5;233m▅▄▂ ▂▗▄▃\x1b[0m',
24
+ '\x1b[38;5;16;48;5;16m \x1b[38;5;235;48;5;232m▗\x1b[38;5;236;48;5;237m▍ \x1b[38;5;58;48;5;94m▋\x1b[38;5;95;48;5;173m▌\x1b[48;5;179m \x1b[38;5;179;48;5;215m▍\x1b[48;5;221m▔\x1b[38;5;222;48;5;180m▍\x1b[48;5;179m \x1b[38;5;173m▕\x1b[38;5;179;48;5;173m▅\x1b[38;5;137m▕\x1b[38;5;95;48;5;58m▍\x1b[38;5;58;48;5;235m▖\x1b[38;5;235;48;5;234m▖▃▄ \x1b[0m',
25
+ '\x1b[38;5;16;48;5;16m \x1b[38;5;233m▗\x1b[48;5;235m▏\x1b[38;5;237;48;5;238m▊\x1b[38;5;238;48;5;236m▌\x1b[38;5;236;48;5;58m▖\x1b[38;5;95;48;5;179m▌ \x1b[38;5;137m▗\x1b[38;5;94m▄\x1b[38;5;58m▄\x1b[38;5;94m▄\x1b[38;5;137m▖\x1b[38;5;173m▗\x1b[38;5;131m▗\x1b[38;5;58;48;5;137m▃\x1b[38;5;131;48;5;58m▘\x1b[38;5;234m▕\x1b[48;5;236m▖\x1b[38;5;236;48;5;235m▃ \x1b[38;5;234m▝\x1b[38;5;235;48;5;234m▃\x1b[0m',
26
+ '\x1b[38;5;16;48;5;16m \x1b[38;5;235;48;5;232m▂\x1b[38;5;236;48;5;234m▄\x1b[38;5;237;48;5;236m▗\x1b[38;5;8;48;5;239m▖\x1b[38;5;240;48;5;8m▎\x1b[38;5;94;48;5;236m▕\x1b[38;5;137;48;5;179m▍ \x1b[38;5;94;48;5;137m▝\x1b[38;5;173;48;5;94m▂\x1b[38;5;137;48;5;58m▂\x1b[48;5;94m▃\x1b[48;5;179m▘\x1b[38;5;173m▝\x1b[38;5;137;48;5;235m▍\x1b[38;5;94;48;5;236m▝\x1b[38;5;235;48;5;94m▖\x1b[38;5;52;48;5;58m▖\x1b[38;5;235;48;5;233m▝\x1b[48;5;236m▁\x1b[48;5;235m \x1b[0m',
27
+ '\x1b[38;5;232;48;5;16m▗\x1b[38;5;233;48;5;236m▌\x1b[38;5;95;48;5;239m▅\x1b[48;5;240m▃\x1b[38;5;94;48;5;238m▖\x1b[38;5;240;48;5;8m▝\x1b[38;5;95;48;5;236m▘\x1b[38;5;236;48;5;95m▘\x1b[38;5;173;48;5;179m▏ \x1b[38;5;215m▄ \x1b[38;5;179;48;5;137m▅\x1b[38;5;137;48;5;179m▘\x1b[38;5;216m▘\x1b[38;5;179;48;5;216m▃\x1b[48;5;94m▌\x1b[38;5;94;48;5;131m▘\x1b[38;5;95;48;5;94m▋\x1b[38;5;94;48;5;52m▃\x1b[38;5;52;48;5;233m▎\x1b[38;5;233;48;5;235m▅\x1b[38;5;234m▂ \x1b[0m',
28
+ '\x1b[38;5;233;48;5;232m▕\x1b[38;5;234;48;5;236m▘\x1b[38;5;8;48;5;95m▌ \x1b[38;5;236m▃\x1b[38;5;58;48;5;234m▘\x1b[38;5;94m▝\x1b[48;5;137m▎\x1b[38;5;179;48;5;173m▍\x1b[38;5;173;48;5;179m▌▆▖▃▞\x1b[38;5;94;48;5;173m▗\x1b[48;5;179m▄\x1b[38;5;179;48;5;58m▘\x1b[38;5;94;48;5;52m▝\x1b[38;5;130;48;5;131m▃\x1b[38;5;94;48;5;58m▍\x1b[38;5;52;48;5;232m▎\x1b[38;5;232;48;5;233m▌\x1b[38;5;233;48;5;234m▏\x1b[38;5;234;48;5;235m▎\x1b[38;5;236m▌▅▄ \x1b[0m',
29
+ '\x1b[38;5;232;48;5;235m▋\x1b[38;5;58;48;5;236m▝\x1b[48;5;58m \x1b[38;5;239;48;5;94m▅\x1b[38;5;237;48;5;235m▂\x1b[38;5;235;48;5;233m▂\x1b[38;5;234;48;5;94m▄\x1b[38;5;94;48;5;137m▖\x1b[48;5;173m \x1b[38;5;173;48;5;179m▃ \x1b[38;5;137m▂▃▂\x1b[38;5;131;48;5;137m▃\x1b[38;5;58;48;5;131m▝\x1b[38;5;94;48;5;52m▅\x1b[48;5;94m \x1b[48;5;58m▍\x1b[38;5;235;48;5;232m▎\x1b[38;5;232;48;5;233m▋\x1b[38;5;233;48;5;234m▍\x1b[38;5;235;48;5;236m▏ \x1b[38;5;236;48;5;235m▎ \x1b[0m',
30
+ '\x1b[38;5;234;48;5;235m▏\x1b[38;5;236;48;5;237m▋\x1b[38;5;237;48;5;8m▃\x1b[38;5;235;48;5;238m▗\x1b[38;5;237m▖\x1b[38;5;58;48;5;234m▌\x1b[38;5;234;48;5;233m▎\x1b[38;5;236;48;5;137m▎\x1b[38;5;137;48;5;173m▄ \x1b[38;5;173;48;5;179m▄▃ \x1b[38;5;179;48;5;215m▅\x1b[38;5;173;48;5;179m▄\x1b[38;5;179;48;5;137m▘\x1b[38;5;137;48;5;131m▌\x1b[48;5;94m \x1b[38;5;58m▗\x1b[38;5;233;48;5;58m▗\x1b[48;5;233m \x1b[38;5;234;48;5;236m▘ \x1b[38;5;236;48;5;235m▃▞\x1b[38;5;235;48;5;236m▄\x1b[48;5;235m \x1b[0m',
31
+ '\x1b[38;5;234;48;5;235m▏▆\x1b[38;5;235;48;5;237m▌\x1b[38;5;236m▝\x1b[38;5;237;48;5;234m▍\x1b[38;5;234;48;5;233m▖\x1b[38;5;240;48;5;234m▗\x1b[38;5;101;48;5;186m▌\x1b[38;5;137m▝\x1b[48;5;137m \x1b[48;5;173m▆▄▃\x1b[38;5;131m▂\x1b[38;5;130;48;5;137m▂\x1b[38;5;58;48;5;94m▃\x1b[48;5;58m \x1b[38;5;234;48;5;233m▏\x1b[38;5;235;48;5;234m▅\x1b[48;5;236m▌ ▝ \x1b[48;5;235m \x1b[0m',
32
+ '\x1b[38;5;234;48;5;233m▕\x1b[38;5;239;48;5;235m▂\x1b[38;5;95m▃\x1b[48;5;237m▄\x1b[48;5;236m▄\x1b[48;5;235m▄\x1b[38;5;236;48;5;240m▘\x1b[38;5;101;48;5;95m▕\x1b[48;5;186m▖\x1b[38;5;179;48;5;229m▝\x1b[38;5;223;48;5;137m▃\x1b[38;5;137;48;5;131m▁\x1b[38;5;95m▅\x1b[38;5;94m▂\x1b[48;5;94m \x1b[38;5;58m▗\x1b[38;5;94;48;5;58m▔\x1b[38;5;236m▁ \x1b[48;5;235m▆\x1b[38;5;235;48;5;236m▍\x1b[38;5;236;48;5;235m▆\x1b[48;5;236m \x1b[38;5;235m▅\x1b[48;5;235m \x1b[0m',
33
+ '\x1b[38;5;237;48;5;95m▔ \x1b[38;5;137;48;5;101m▝\x1b[48;5;187m▅\x1b[38;5;180;48;5;229m▂\x1b[38;5;143;48;5;222m▔\x1b[38;5;186;48;5;58m▅\x1b[38;5;179m▂\x1b[38;5;95m▁\x1b[38;5;235m▂\x1b[38;5;236m▄\x1b[48;5;233m▌\x1b[38;5;235m▔\x1b[38;5;233;48;5;236m▅\x1b[38;5;234m▃\x1b[38;5;235m▁ ▔\x1b[48;5;235m \x1b[0m',
34
+ '\x1b[38;5;101;48;5;137m▔\x1b[38;5;95;48;5;101m▄▔\x1b[38;5;101;48;5;95m▄ ▗ \x1b[38;5;240m▖\x1b[38;5;95;48;5;101m▘\x1b[38;5;137m▔\x1b[48;5;222m▅\x1b[48;5;186m▃\x1b[48;5;179m▂\x1b[38;5;101;48;5;95m▌\x1b[48;5;58m \x1b[38;5;238;48;5;236m▁\x1b[38;5;180;48;5;234m▃\x1b[48;5;235m▄\x1b[38;5;179;48;5;234m▃\x1b[38;5;95m▁\x1b[38;5;234;48;5;235m▊\x1b[48;5;236m▆\x1b[38;5;235m▃\x1b[38;5;234m▂\x1b[38;5;235m▁ \x1b[38;5;236;48;5;235m▎\x1b[0m',
35
+ '\x1b[38;5;137;48;5;137m \x1b[48;5;95m▄ \x1b[38;5;95;48;5;101m▖\x1b[48;5;137m▝\x1b[48;5;95m \x1b[38;5;101m▅\x1b[48;5;239m▋\x1b[48;5;95m \x1b[38;5;95;48;5;137m▋\x1b[38;5;101;48;5;95m▍\x1b[38;5;95;48;5;101m▖\x1b[38;5;101;48;5;95m▆\x1b[38;5;239m▗\x1b[38;5;101m▄ \x1b[38;5;95;48;5;137m▅\x1b[38;5;137;48;5;180m▅\x1b[38;5;180;48;5;186m▃\x1b[48;5;143m▆\x1b[38;5;95m▔\x1b[38;5;143;48;5;235m▖\x1b[48;5;234m \x1b[38;5;235m▆\x1b[38;5;234;48;5;235m▝\x1b[38;5;235;48;5;234m▞\x1b[38;5;234;48;5;235m▄ \x1b[0m',
36
+ ];
37
+ // ─── FRANKLIN text banner (gold → emerald gradient) ────────────────────────
38
+ //
39
+ // Kept from v3.1.0. The text is laid out as 6 block-letter rows. Each row
40
+ // is tinted with a color interpolated between GOLD_START and EMERALD_END,
41
+ // giving the smooth vertical gradient that's been Franklin's banner since
42
+ // v3.1.0.
3
43
  const FRANKLIN_ART = [
4
44
  ' ███████╗██████╗ █████╗ ███╗ ██╗██╗ ██╗██╗ ██╗███╗ ██╗',
5
45
  ' ██╔════╝██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝██║ ██║████╗ ██║',
@@ -8,10 +48,6 @@ const FRANKLIN_ART = [
8
48
  ' ██║ ██║ ██║██║ ██║██║ ╚████║██║ ██╗███████╗██║██║ ╚████║',
9
49
  ' ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═══╝',
10
50
  ];
11
- // Franklin brand gradient: gold → emerald
12
- // Gold (#FFD700) is the $100 bill / Benjamins color — marketing product energy
13
- // Emerald (#10B981) is the trading / money-moving color
14
- // The gradient between them tells the Franklin story in one glance
15
51
  const GOLD_START = '#FFD700';
16
52
  const EMERALD_END = '#10B981';
17
53
  function hexToRgb(hex) {
@@ -31,15 +67,106 @@ function interpolateHex(start, end, t) {
31
67
  const [r2, g2, b2] = hexToRgb(end);
32
68
  return rgbToHex(r1 + (r2 - r1) * t, g1 + (g2 - g1) * t, b1 + (b2 - b1) * t);
33
69
  }
70
+ // ─── Banner layout ─────────────────────────────────────────────────────────
71
+ // Minimum terminal width to show the side-by-side portrait + text layout.
72
+ // The portrait is ~28 chars, the FRANKLIN text is ~65 chars, plus a 3-char
73
+ // gap = 96 chars. We add a small margin so 100 cols is the threshold.
74
+ const MIN_WIDTH_FOR_PORTRAIT = 100;
75
+ /**
76
+ * Pad a line to an exact visual width, ignoring ANSI escape codes when
77
+ * measuring. Used to align the portrait's right edge before the text block.
78
+ */
79
+ function padVisible(s, targetWidth) {
80
+ // Strip ANSI color codes to measure visible length
81
+ // eslint-disable-next-line no-control-regex
82
+ const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
83
+ // Unicode block characters are width 1 (they're half-blocks, not double-width)
84
+ const current = [...visible].length;
85
+ if (current >= targetWidth)
86
+ return s;
87
+ // Append a reset + padding so background colors don't bleed into the gap
88
+ return s + '\x1b[0m' + ' '.repeat(targetWidth - current);
89
+ }
34
90
  export function printBanner(version) {
35
- // Smooth 6-row gradient: each line gets its own interpolated color
36
- const steps = FRANKLIN_ART.length;
37
- for (let i = 0; i < steps; i++) {
38
- const t = i / (steps - 1);
91
+ const termWidth = process.stdout.columns ?? 80;
92
+ const useSideBySide = termWidth >= MIN_WIDTH_FOR_PORTRAIT;
93
+ if (useSideBySide) {
94
+ printSideBySide(version);
95
+ }
96
+ else {
97
+ printTextOnly(version);
98
+ }
99
+ }
100
+ /**
101
+ * Full layout: Ben Franklin portrait on the left, FRANKLIN text block on the
102
+ * right. Portrait is 14 rows × ~28 chars, text is 6 rows — text is vertically
103
+ * centred inside the portrait with 4 rows of padding above and 4 below,
104
+ * tagline sitting right under the FRANKLIN block.
105
+ *
106
+ * [portrait row 1] (empty)
107
+ * [portrait row 2] (empty)
108
+ * [portrait row 3] (empty)
109
+ * [portrait row 4] (empty)
110
+ * [portrait row 5] ███████╗██████╗ █████╗ ...
111
+ * [portrait row 6] ██╔════╝██╔══██╗██╔══██╗...
112
+ * [portrait row 7] █████╗ ██████╔╝███████║...
113
+ * [portrait row 8] ██╔══╝ ██╔══██╗██╔══██║...
114
+ * [portrait row 9] ██║ ██║ ██║██║ ██║...
115
+ * [portrait row 10] ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝...
116
+ * [portrait row 11] blockrun.ai · The AI agent with a wallet · vX
117
+ * [portrait row 12] (empty)
118
+ * [portrait row 13] (empty)
119
+ * [portrait row 14] (empty)
120
+ */
121
+ function printSideBySide(version) {
122
+ const TEXT_TOP_OFFSET = 4; // rows of portrait above the text
123
+ const PORTRAIT_WIDTH = 29; // columns (char width) of the portrait + 1 pad
124
+ const GAP = ' '; // gap between portrait and text
125
+ const portraitRows = BEN_PORTRAIT_ROWS;
126
+ const textRows = FRANKLIN_ART.length;
127
+ const totalRows = Math.max(portraitRows.length, TEXT_TOP_OFFSET + textRows + 2);
128
+ for (let i = 0; i < totalRows; i++) {
129
+ const portraitLine = i < portraitRows.length
130
+ ? padVisible(portraitRows[i], PORTRAIT_WIDTH)
131
+ : ' '.repeat(PORTRAIT_WIDTH);
132
+ // Text column content
133
+ let textCol = '';
134
+ const textIdx = i - TEXT_TOP_OFFSET;
135
+ if (textIdx >= 0 && textIdx < textRows) {
136
+ // FRANKLIN block letters with gradient colour
137
+ const t = textRows === 1 ? 0 : textIdx / (textRows - 1);
138
+ const color = interpolateHex(GOLD_START, EMERALD_END, t);
139
+ textCol = chalk.hex(color)(FRANKLIN_ART[textIdx]);
140
+ }
141
+ else if (textIdx === textRows) {
142
+ // Tagline row sits right under the FRANKLIN block.
143
+ // The big block-letter "FRANKLIN" above already says the product
144
+ // name — the tagline uses that real estate for the parent brand URL
145
+ // (blockrun.ai, which is a real live domain — unlike franklin.run
146
+ // which we own but haven't deployed yet, see v3.1.0 changelog).
147
+ textCol =
148
+ chalk.bold.hex(GOLD_START)(' blockrun.ai') +
149
+ chalk.dim(' · The AI agent with a wallet · v' + version);
150
+ }
151
+ // Write with a reset at the very start to prevent stray bg from the
152
+ // previous line bleeding into the current row's portrait column.
153
+ process.stdout.write('\x1b[0m' + portraitLine + GAP + textCol + '\x1b[0m\n');
154
+ }
155
+ // Trailing blank line for breathing room
156
+ process.stdout.write('\n');
157
+ }
158
+ /**
159
+ * Compact layout for narrow terminals: just the FRANKLIN text block with
160
+ * its gradient, no portrait. Matches the v3.1.0 banner exactly.
161
+ */
162
+ function printTextOnly(version) {
163
+ const textRows = FRANKLIN_ART.length;
164
+ for (let i = 0; i < textRows; i++) {
165
+ const t = textRows === 1 ? 0 : i / (textRows - 1);
39
166
  const color = interpolateHex(GOLD_START, EMERALD_END, t);
40
167
  console.log(chalk.hex(color)(FRANKLIN_ART[i]));
41
168
  }
42
- console.log(chalk.bold.hex(GOLD_START)(' Franklin') +
169
+ console.log(chalk.bold.hex(GOLD_START)(' blockrun.ai') +
43
170
  chalk.dim(' · The AI agent with a wallet · v' + version) +
44
171
  '\n');
45
172
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/franklin",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "Franklin — The AI agent with a wallet. Spends USDC autonomously to get real work done. Pay per action, no subscriptions.",
5
5
  "type": "module",
6
6
  "exports": {