balchemy 0.1.0

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 (67) hide show
  1. package/README.md +59 -0
  2. package/assets/bcrow.png +0 -0
  3. package/dist/agent-store.d.ts +40 -0
  4. package/dist/agent-store.d.ts.map +1 -0
  5. package/dist/agent-store.js +206 -0
  6. package/dist/agent-store.js.map +1 -0
  7. package/dist/config-loader.d.ts +8 -0
  8. package/dist/config-loader.d.ts.map +1 -0
  9. package/dist/config-loader.js +106 -0
  10. package/dist/config-loader.js.map +1 -0
  11. package/dist/docker-gen.d.ts +6 -0
  12. package/dist/docker-gen.d.ts.map +1 -0
  13. package/dist/docker-gen.js +40 -0
  14. package/dist/docker-gen.js.map +1 -0
  15. package/dist/index.d.ts +16 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +143 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/openai-oauth.d.ts +28 -0
  20. package/dist/openai-oauth.d.ts.map +1 -0
  21. package/dist/openai-oauth.js +215 -0
  22. package/dist/openai-oauth.js.map +1 -0
  23. package/dist/runner.d.ts +6 -0
  24. package/dist/runner.d.ts.map +1 -0
  25. package/dist/runner.js +63 -0
  26. package/dist/runner.js.map +1 -0
  27. package/dist/terminal-logo.d.ts +15 -0
  28. package/dist/terminal-logo.d.ts.map +1 -0
  29. package/dist/terminal-logo.js +121 -0
  30. package/dist/terminal-logo.js.map +1 -0
  31. package/dist/tui/AgentBridge.d.ts +35 -0
  32. package/dist/tui/AgentBridge.d.ts.map +1 -0
  33. package/dist/tui/AgentBridge.js +235 -0
  34. package/dist/tui/AgentBridge.js.map +1 -0
  35. package/dist/tui/App.d.ts +8 -0
  36. package/dist/tui/App.d.ts.map +1 -0
  37. package/dist/tui/App.js +118 -0
  38. package/dist/tui/App.js.map +1 -0
  39. package/dist/tui/ChatAgent.d.ts +41 -0
  40. package/dist/tui/ChatAgent.d.ts.map +1 -0
  41. package/dist/tui/ChatAgent.js +312 -0
  42. package/dist/tui/ChatAgent.js.map +1 -0
  43. package/dist/tui/ChatPanel.d.ts +10 -0
  44. package/dist/tui/ChatPanel.d.ts.map +1 -0
  45. package/dist/tui/ChatPanel.js +43 -0
  46. package/dist/tui/ChatPanel.js.map +1 -0
  47. package/dist/tui/StatusPanel.d.ts +8 -0
  48. package/dist/tui/StatusPanel.d.ts.map +1 -0
  49. package/dist/tui/StatusPanel.js +25 -0
  50. package/dist/tui/StatusPanel.js.map +1 -0
  51. package/dist/tui/start.d.ts +3 -0
  52. package/dist/tui/start.d.ts.map +1 -0
  53. package/dist/tui/start.js +14 -0
  54. package/dist/tui/start.js.map +1 -0
  55. package/dist/tui/types.d.ts +61 -0
  56. package/dist/tui/types.d.ts.map +1 -0
  57. package/dist/tui/types.js +3 -0
  58. package/dist/tui/types.js.map +1 -0
  59. package/dist/wizard.d.ts +16 -0
  60. package/dist/wizard.d.ts.map +1 -0
  61. package/dist/wizard.js +716 -0
  62. package/dist/wizard.js.map +1 -0
  63. package/package.json +57 -0
  64. package/templates/.env.example +19 -0
  65. package/templates/Dockerfile +20 -0
  66. package/templates/agent.config.yaml +71 -0
  67. package/templates/docker-compose.yml +27 -0
package/dist/index.js ADDED
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * create-balchemy-agent CLI entry point.
4
+ *
5
+ * On launch:
6
+ * - If ~/.balchemy/agent.json exists → offer to resume or start fresh
7
+ * - If no cached agent → run wizard
8
+ *
9
+ * Sub-commands:
10
+ * (no args) Resume cached agent or run wizard
11
+ * init / --init Force run wizard (ignore cache)
12
+ * start [config] Start from agent.config.yaml
13
+ * docker [outDir] Generate Docker files
14
+ */
15
+ import * as path from "path";
16
+ import * as readline from "readline";
17
+ import { loadAgent, clearAgent } from "./agent-store.js";
18
+ const [, , cmd, ...args] = process.argv;
19
+ const T = "\x1b[38;2;0;172;176m";
20
+ const G = "\x1b[38;2;186;115;6m";
21
+ const W = "\x1b[1;37m";
22
+ const D = "\x1b[38;5;245m";
23
+ const R = "\x1b[0m";
24
+ function ask(rl, question, defaultVal = "") {
25
+ return new Promise((resolve) => {
26
+ const hint = defaultVal ? ` ${D}[${defaultVal}]${R}` : "";
27
+ rl.question(` ${question}${hint}: `, (answer) => {
28
+ resolve(answer.trim() || defaultVal);
29
+ });
30
+ });
31
+ }
32
+ async function main() {
33
+ switch (cmd) {
34
+ case "--init":
35
+ case "init": {
36
+ // Force wizard — ignore cache
37
+ const { runWizard } = await import("./wizard.js");
38
+ await runWizard(process.cwd());
39
+ break;
40
+ }
41
+ case "start": {
42
+ const configPath = args[0] ?? path.join(process.cwd(), "agent.config.yaml");
43
+ const resolvedPath = path.resolve(configPath);
44
+ const dotenv = await import("dotenv");
45
+ const envPath = path.join(path.dirname(resolvedPath), ".env");
46
+ const fs = await import("fs");
47
+ if (fs.existsSync(envPath)) {
48
+ dotenv.config({ path: envPath });
49
+ }
50
+ else {
51
+ dotenv.config();
52
+ }
53
+ const { loadConfig } = await import("./config-loader.js");
54
+ const config = loadConfig(resolvedPath);
55
+ const publicId = config.mcpEndpoint.split("/").filter(Boolean).pop() ?? "unknown";
56
+ const { startTui } = await import("./tui/start.js");
57
+ await startTui({
58
+ mcpEndpoint: config.mcpEndpoint,
59
+ apiKey: config.apiKey,
60
+ llmProvider: config.llmProvider,
61
+ llmApiKey: config.llmApiKey,
62
+ llmModel: config.llmModel,
63
+ llmBaseUrl: config.llmBaseUrl,
64
+ maxDailyLlmCost: config.maxDailyLlmCost,
65
+ llmTimeoutMs: config.llmTimeoutMs,
66
+ publicId,
67
+ strategy: "custom",
68
+ shadowMode: false,
69
+ });
70
+ break;
71
+ }
72
+ case "docker": {
73
+ const outDir = args[0] ?? process.cwd();
74
+ const { generateDocker } = await import("./docker-gen.js");
75
+ await generateDocker(outDir);
76
+ process.stdout.write(`Docker files written to ${outDir}\n`);
77
+ break;
78
+ }
79
+ case undefined: {
80
+ // Default: check for cached agent
81
+ const cached = loadAgent();
82
+ if (cached) {
83
+ // Show cached agent info and ask what to do
84
+ const { renderLogo } = await import("./terminal-logo.js");
85
+ process.stdout.write(renderLogo(20));
86
+ process.stdout.write(`\n ${G}B${T}alchemy ${W}Agent${R}\n\n`);
87
+ process.stdout.write(` ${D}Cached agent found:${R}\n`);
88
+ process.stdout.write(` ${T}Agent:${R} ${cached.publicId}\n`);
89
+ process.stdout.write(` ${T}Endpoint:${R} ${cached.mcpEndpoint}\n`);
90
+ process.stdout.write(` ${T}Model:${R} ${cached.llmModel ?? "default"}\n`);
91
+ process.stdout.write(` ${T}Strategy:${R} ${cached.strategy}\n`);
92
+ process.stdout.write(` ${T}Mode:${R} ${cached.shadowMode ? "Shadow" : "LIVE"}\n`);
93
+ process.stdout.write(` ${D}Saved:${R} ${cached.createdAt}\n\n`);
94
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
95
+ const choice = await ask(rl, `${W}Resume this agent?${R} (y/n/new)`, "y");
96
+ rl.close();
97
+ if (choice === "y" || choice === "yes") {
98
+ // Resume — go straight to TUI
99
+ const { startTui } = await import("./tui/start.js");
100
+ await startTui({
101
+ mcpEndpoint: cached.mcpEndpoint,
102
+ apiKey: cached.apiKey,
103
+ llmProvider: cached.llmProvider,
104
+ llmApiKey: cached.llmApiKey,
105
+ llmModel: cached.llmModel,
106
+ llmBaseUrl: cached.llmBaseUrl,
107
+ maxDailyLlmCost: cached.maxDailyLlmCost,
108
+ publicId: cached.publicId,
109
+ strategy: cached.strategy,
110
+ shadowMode: cached.shadowMode,
111
+ });
112
+ }
113
+ else if (choice === "new" || choice === "n") {
114
+ // New agent — clear cache and run wizard
115
+ if (choice === "new")
116
+ clearAgent();
117
+ const { runWizard } = await import("./wizard.js");
118
+ await runWizard(process.cwd());
119
+ }
120
+ }
121
+ else {
122
+ // No cached agent — run wizard
123
+ const { runWizard } = await import("./wizard.js");
124
+ await runWizard(process.cwd());
125
+ }
126
+ break;
127
+ }
128
+ default: {
129
+ process.stdout.write(`${T}Balchemy Agent CLI${R}\n\n` +
130
+ "Usage:\n" +
131
+ " npx create-balchemy-agent Resume agent or setup wizard\n" +
132
+ " npx create-balchemy-agent init Force new setup wizard\n" +
133
+ " balchemy-agent start [config] Start from config file\n" +
134
+ " balchemy-agent docker [outDir] Generate Docker files\n\n");
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ main().catch((err) => {
140
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
141
+ process.exit(1);
142
+ });
143
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * OpenAI OAuth 2.0 + PKCE authentication for ChatGPT Plus/Pro subscriptions.
3
+ *
4
+ * Uses the same flow as the official Codex CLI:
5
+ * 1. Generate PKCE code_verifier + code_challenge
6
+ * 2. Start localhost callback server
7
+ * 3. Open browser → user logs in with ChatGPT account
8
+ * 4. Exchange auth code for access_token + refresh_token
9
+ * 5. Auto-refresh before expiry
10
+ *
11
+ * Reference: https://developers.openai.com/codex/auth
12
+ */
13
+ export interface OAuthTokens {
14
+ accessToken: string;
15
+ refreshToken: string;
16
+ expiresAt: number;
17
+ accountId?: string;
18
+ }
19
+ /**
20
+ * Run the full OAuth PKCE flow. Opens browser, waits for callback, returns tokens.
21
+ * Rejects after timeoutMs (default 120s).
22
+ */
23
+ export declare function loginWithOpenAI(timeoutMs?: number): Promise<OAuthTokens>;
24
+ /**
25
+ * Refresh an expired access token using the refresh token.
26
+ */
27
+ export declare function refreshAccessToken(refreshToken: string): Promise<OAuthTokens>;
28
+ //# sourceMappingURL=openai-oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-oauth.d.ts","sourceRoot":"","sources":["../src/openai-oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA8BD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,SAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAwFzE;AAkDD;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA6BnF"}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * OpenAI OAuth 2.0 + PKCE authentication for ChatGPT Plus/Pro subscriptions.
3
+ *
4
+ * Uses the same flow as the official Codex CLI:
5
+ * 1. Generate PKCE code_verifier + code_challenge
6
+ * 2. Start localhost callback server
7
+ * 3. Open browser → user logs in with ChatGPT account
8
+ * 4. Exchange auth code for access_token + refresh_token
9
+ * 5. Auto-refresh before expiry
10
+ *
11
+ * Reference: https://developers.openai.com/codex/auth
12
+ */
13
+ import * as http from "http";
14
+ import * as crypto from "crypto";
15
+ import { exec } from "child_process";
16
+ // ── Constants ─────────────────────────────────────────────────────────────────
17
+ const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
18
+ const AUTH_URL = "https://auth.openai.com/oauth/authorize";
19
+ const TOKEN_URL = "https://auth.openai.com/oauth/token";
20
+ const CALLBACK_PORT = 1455;
21
+ const REDIRECT_URI = `http://localhost:${CALLBACK_PORT}/auth/callback`;
22
+ const SCOPES = "openid profile email offline_access";
23
+ // ── PKCE Helpers ──────────────────────────────────────────────────────────────
24
+ function generateCodeVerifier() {
25
+ return crypto.randomBytes(32).toString("base64url");
26
+ }
27
+ function generateCodeChallenge(verifier) {
28
+ return crypto.createHash("sha256").update(verifier).digest("base64url");
29
+ }
30
+ function generateState() {
31
+ return crypto.randomBytes(16).toString("hex");
32
+ }
33
+ // ── Browser ───────────────────────────────────────────────────────────────────
34
+ function openBrowser(url) {
35
+ const cmd = process.platform === "darwin"
36
+ ? `open "${url}"`
37
+ : process.platform === "win32"
38
+ ? `start "${url}"`
39
+ : `xdg-open "${url}"`;
40
+ exec(cmd, () => { });
41
+ }
42
+ // ── OAuth Flow ────────────────────────────────────────────────────────────────
43
+ /**
44
+ * Run the full OAuth PKCE flow. Opens browser, waits for callback, returns tokens.
45
+ * Rejects after timeoutMs (default 120s).
46
+ */
47
+ export function loginWithOpenAI(timeoutMs = 120_000) {
48
+ return new Promise((resolve, reject) => {
49
+ const codeVerifier = generateCodeVerifier();
50
+ const codeChallenge = generateCodeChallenge(codeVerifier);
51
+ const state = generateState();
52
+ let server = null;
53
+ let timer = null;
54
+ const cleanup = () => {
55
+ if (timer)
56
+ clearTimeout(timer);
57
+ if (server)
58
+ server.close();
59
+ };
60
+ timer = setTimeout(() => {
61
+ cleanup();
62
+ reject(new Error("OAuth login timed out (120s). Try again."));
63
+ }, timeoutMs);
64
+ server = http.createServer(async (req, res) => {
65
+ const url = new URL(req.url ?? "/", `http://localhost:${CALLBACK_PORT}`);
66
+ if (url.pathname !== "/auth/callback") {
67
+ res.writeHead(404);
68
+ res.end("Not found");
69
+ return;
70
+ }
71
+ const code = url.searchParams.get("code");
72
+ const returnedState = url.searchParams.get("state");
73
+ const error = url.searchParams.get("error");
74
+ if (error) {
75
+ res.writeHead(200, { "Content-Type": "text/html" });
76
+ res.end(errorPage(error));
77
+ cleanup();
78
+ reject(new Error(`OAuth error: ${error}`));
79
+ return;
80
+ }
81
+ if (!code || returnedState !== state) {
82
+ res.writeHead(400, { "Content-Type": "text/html" });
83
+ res.end(errorPage("Invalid callback — state mismatch or missing code."));
84
+ cleanup();
85
+ reject(new Error("OAuth state mismatch"));
86
+ return;
87
+ }
88
+ // Exchange code for tokens
89
+ try {
90
+ const tokens = await exchangeCode(code, codeVerifier);
91
+ res.writeHead(200, { "Content-Type": "text/html" });
92
+ res.end(successPage());
93
+ cleanup();
94
+ resolve(tokens);
95
+ }
96
+ catch (err) {
97
+ res.writeHead(500, { "Content-Type": "text/html" });
98
+ res.end(errorPage(err instanceof Error ? err.message : "Token exchange failed"));
99
+ cleanup();
100
+ reject(err);
101
+ }
102
+ });
103
+ server.listen(CALLBACK_PORT, () => {
104
+ // Build auth URL
105
+ const params = new URLSearchParams({
106
+ client_id: CLIENT_ID,
107
+ redirect_uri: REDIRECT_URI,
108
+ response_type: "code",
109
+ scope: SCOPES,
110
+ state,
111
+ code_challenge: codeChallenge,
112
+ code_challenge_method: "S256",
113
+ });
114
+ const authUrl = `${AUTH_URL}?${params.toString()}`;
115
+ openBrowser(authUrl);
116
+ });
117
+ server.on("error", (err) => {
118
+ cleanup();
119
+ if (err.code === "EADDRINUSE") {
120
+ reject(new Error(`Port ${CALLBACK_PORT} is in use. Close other apps and try again.`));
121
+ }
122
+ else {
123
+ reject(err);
124
+ }
125
+ });
126
+ });
127
+ }
128
+ // ── Token Exchange ────────────────────────────────────────────────────────────
129
+ async function exchangeCode(code, codeVerifier) {
130
+ const body = new URLSearchParams({
131
+ grant_type: "authorization_code",
132
+ client_id: CLIENT_ID,
133
+ code,
134
+ redirect_uri: REDIRECT_URI,
135
+ code_verifier: codeVerifier,
136
+ });
137
+ const res = await fetch(TOKEN_URL, {
138
+ method: "POST",
139
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
140
+ body: body.toString(),
141
+ });
142
+ if (!res.ok) {
143
+ const text = await res.text();
144
+ throw new Error(`Token exchange failed: ${res.status} ${text}`);
145
+ }
146
+ const data = (await res.json());
147
+ // Extract accountId from access token (JWT payload)
148
+ let accountId;
149
+ try {
150
+ const payload = JSON.parse(Buffer.from(data.access_token.split(".")[1], "base64").toString());
151
+ accountId = payload.sub ?? payload.account_id;
152
+ }
153
+ catch {
154
+ // non-critical
155
+ }
156
+ return {
157
+ accessToken: data.access_token,
158
+ refreshToken: data.refresh_token,
159
+ expiresAt: Date.now() + data.expires_in * 1000,
160
+ accountId,
161
+ };
162
+ }
163
+ /**
164
+ * Refresh an expired access token using the refresh token.
165
+ */
166
+ export async function refreshAccessToken(refreshToken) {
167
+ const body = new URLSearchParams({
168
+ grant_type: "refresh_token",
169
+ client_id: CLIENT_ID,
170
+ refresh_token: refreshToken,
171
+ });
172
+ const res = await fetch(TOKEN_URL, {
173
+ method: "POST",
174
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
175
+ body: body.toString(),
176
+ });
177
+ if (!res.ok) {
178
+ const text = await res.text();
179
+ throw new Error(`Token refresh failed: ${res.status} ${text}`);
180
+ }
181
+ const data = (await res.json());
182
+ return {
183
+ accessToken: data.access_token,
184
+ refreshToken: data.refresh_token,
185
+ expiresAt: Date.now() + data.expires_in * 1000,
186
+ };
187
+ }
188
+ // ── HTML Pages ────────────────────────────────────────────────────────────────
189
+ /**
190
+ * Escape HTML special characters to prevent XSS when interpolating
191
+ * untrusted strings (e.g. OAuth error parameters) into HTML responses.
192
+ */
193
+ function escapeHtml(s) {
194
+ return s
195
+ .replace(/&/g, "&amp;")
196
+ .replace(/</g, "&lt;")
197
+ .replace(/>/g, "&gt;")
198
+ .replace(/"/g, "&quot;")
199
+ .replace(/'/g, "&#39;");
200
+ }
201
+ function successPage() {
202
+ return `<!DOCTYPE html><html><body style="font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;background:#0a0a0a;color:#00acb0">
203
+ <div style="text-align:center">
204
+ <h1>Logged in</h1>
205
+ <p style="color:#888">You can close this tab and return to the terminal.</p>
206
+ </div></body></html>`;
207
+ }
208
+ function errorPage(msg) {
209
+ return `<!DOCTYPE html><html><body style="font-family:system-ui;display:flex;justify-content:center;align-items:center;height:100vh;background:#0a0a0a;color:#e55">
210
+ <div style="text-align:center">
211
+ <h1>Login failed</h1>
212
+ <p style="color:#888">${escapeHtml(msg)}</p>
213
+ </div></body></html>`;
214
+ }
215
+ //# sourceMappingURL=openai-oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-oauth.js","sourceRoot":"","sources":["../src/openai-oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Starts the AgentLoop from a config file path.
3
+ * Handles process signals (SIGINT, SIGTERM) for graceful shutdown.
4
+ */
5
+ export declare function runAgent(configPath: string): Promise<void>;
6
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmBH,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoDhE"}
package/dist/runner.js ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Starts the AgentLoop from a config file path.
3
+ * Handles process signals (SIGINT, SIGTERM) for graceful shutdown.
4
+ */
5
+ import * as dotenv from 'dotenv';
6
+ import * as path from 'path';
7
+ import * as fs from 'fs';
8
+ import { AgentLoop } from '@balchemyai/agent-sdk';
9
+ import { loadConfig } from './config-loader.js';
10
+ function loadDotEnv(configPath) {
11
+ // Look for .env relative to the config file directory
12
+ const envPath = path.join(path.dirname(configPath), '.env');
13
+ if (fs.existsSync(envPath)) {
14
+ dotenv.config({ path: envPath });
15
+ }
16
+ else {
17
+ // Fall back to cwd
18
+ dotenv.config();
19
+ }
20
+ }
21
+ export async function runAgent(configPath) {
22
+ const resolvedPath = path.resolve(configPath);
23
+ // Load .env before parsing config (config may reference env vars)
24
+ loadDotEnv(resolvedPath);
25
+ process.stdout.write(`Starting Balchemy agent from: ${resolvedPath}\n`);
26
+ const config = loadConfig(resolvedPath);
27
+ const loop = new AgentLoop({
28
+ ...config,
29
+ onStatusChange: (status) => {
30
+ process.stdout.write(`[agent] status=${status.status} events=${status.eventsReceived} ` +
31
+ `decisions=${status.decisionsExecuted} trades=${status.tradesExecuted} ` +
32
+ `llmCost=$${status.llmCostToday.toFixed(4)}/${status.maxDailyLlmCost}\n`);
33
+ },
34
+ onDecision: (decision) => {
35
+ process.stdout.write(`[agent] decision action=${decision.action}` +
36
+ (decision.token ? ` token=${decision.token}` : '') +
37
+ (decision.amount ? ` amount=${decision.amount}` : '') +
38
+ (decision.confidence !== undefined ? ` confidence=${decision.confidence}` : '') +
39
+ '\n');
40
+ },
41
+ onError: (err) => {
42
+ process.stderr.write(`[agent] error: ${err.message}\n`);
43
+ },
44
+ });
45
+ // Graceful shutdown
46
+ let stopping = false;
47
+ const shutdown = async () => {
48
+ if (stopping)
49
+ return;
50
+ stopping = true;
51
+ process.stdout.write('\n[agent] Shutting down...\n');
52
+ await loop.stop();
53
+ process.stdout.write('[agent] Stopped.\n');
54
+ process.exit(0);
55
+ };
56
+ process.on('SIGINT', () => { void shutdown(); });
57
+ process.on('SIGTERM', () => { void shutdown(); });
58
+ await loop.start();
59
+ process.stdout.write('[agent] Running. Press Ctrl+C to stop.\n');
60
+ // Keep process alive
61
+ await new Promise(() => { });
62
+ }
63
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Terminal image rendering for BCrow logo.
3
+ *
4
+ * Terminal emulators do not share one universal "render this SVG" API, so we
5
+ * ship a bundled PNG and use native image protocols when the terminal supports
6
+ * them. Everyone else gets the ANSI art fallback.
7
+ */
8
+ /**
9
+ * Render the BCrow logo. Uses native terminal image protocol when available,
10
+ * falls back to ANSI block art.
11
+ *
12
+ * @param widthCols — terminal columns to use for the image (default 20)
13
+ */
14
+ export declare function renderLogo(widthCols?: number): string;
15
+ //# sourceMappingURL=terminal-logo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-logo.d.ts","sourceRoot":"","sources":["../src/terminal-logo.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+FH;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,SAAS,SAAK,GAAG,MAAM,CAsBjD"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Terminal image rendering for BCrow logo.
3
+ *
4
+ * Terminal emulators do not share one universal "render this SVG" API, so we
5
+ * ship a bundled PNG and use native image protocols when the terminal supports
6
+ * them. Everyone else gets the ANSI art fallback.
7
+ */
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ import { fileURLToPath } from "url";
11
+ const __filename_esm = fileURLToPath(import.meta.url);
12
+ const __dirname_esm = path.dirname(__filename_esm);
13
+ // ── Brand colors ──────────────────────────────────────────────────────────────
14
+ const T = "\x1b[38;2;0;172;176m"; // teal
15
+ const DT = "\x1b[38;2;0;120;124m"; // dark teal
16
+ const G = "\x1b[38;2;186;115;6m"; // gold
17
+ const R = "\x1b[0m"; // reset
18
+ // ── ANSI fallback logo ────────────────────────────────────────────────────────
19
+ // Per-letter gradient colors matching the Balchemy brand logo
20
+ // Generated via figlet -f small, then colorized per-letter
21
+ const C1 = "\x1b[38;2;0;172;176m"; // B — teal
22
+ const C2 = "\x1b[38;2;0;152;160m"; // A — teal-blue
23
+ const C3 = "\x1b[38;2;180;100;140m"; // L — rose
24
+ const C4 = "\x1b[38;2;186;155;6m"; // C — gold
25
+ const C5 = "\x1b[38;2;0;160;170m"; // H — teal
26
+ const C6 = "\x1b[38;2;80;130;200m"; // E — blue
27
+ const C7 = "\x1b[38;2;170;90;150m"; // M — magenta
28
+ const C8 = "\x1b[38;2;90;140;190m"; // Y — slate blue
29
+ const D = "\x1b[38;5;245m"; // dim gray
30
+ const ANSI_LOGO = [
31
+ ``,
32
+ ` ${C1} ___ ${R}${C2} _ ${R}${C3} _ ${R}${C4} ___ ${R}${C5} _ _ ${R}${C6} ___ ${R}${C7} __ __ ${R}${C8}__ __${R}`,
33
+ ` ${C1}| _ )${R}${C2} /_\\ ${R}${C3}| | ${R}${C4} / __|${R}${C5}| || |${R}${C6}| __|${R}${C7}| \\/ |${R}${C8}\\ \\ / /${R}`,
34
+ ` ${C1}| _ \\${R}${C2} / _ \\ ${R}${C3}| |__ ${R}${C4}| (__ ${R}${C5}| __ |${R}${C6}| _| ${R}${C7}| |\\/| |${R}${C8} \\ V / ${R}`,
35
+ ` ${C1}|___/${R}${C2}/_/ \\_\\${R}${C3}|____|${R}${C4} \\___|${R}${C5}|_||_|${R}${C6}|___|${R}${C7}|_| |_|${R}${C8} |_| ${R}`,
36
+ ` ${D}${"─".repeat(52)}${R}`,
37
+ ``,
38
+ ].join("\n");
39
+ function detectProtocol() {
40
+ const term = process.env.TERM_PROGRAM ?? "";
41
+ const termInfo = process.env.TERM ?? "";
42
+ // iTerm2 inline image protocol
43
+ if (term === "iTerm.app" ||
44
+ term === "WezTerm" ||
45
+ process.env.WEZTERM_EXECUTABLE) {
46
+ return "iterm2";
47
+ }
48
+ // Kitty graphics protocol
49
+ if (process.env.KITTY_WINDOW_ID || termInfo.includes("kitty")) {
50
+ return "kitty";
51
+ }
52
+ return "none";
53
+ }
54
+ // ── iTerm2 inline image rendering ─────────────────────────────────────────────
55
+ function renderIterm2(image, widthCols) {
56
+ return `\x1b]1337;File=size=${image.byteSize};inline=1;width=${widthCols};preserveAspectRatio=1:${image.base64}\x07`;
57
+ }
58
+ // ── Kitty graphics protocol rendering ─────────────────────────────────────────
59
+ function renderKitty(image, widthCols) {
60
+ const chunks = [];
61
+ const chunkSize = 4096;
62
+ for (let i = 0; i < image.base64.length; i += chunkSize) {
63
+ const chunk = image.base64.slice(i, i + chunkSize);
64
+ const isLast = i + chunkSize >= image.base64.length;
65
+ if (i === 0) {
66
+ chunks.push(`\x1b_Gq=2,f=100,a=T,c=${widthCols},m=${isLast ? 0 : 1};${chunk}\x1b\\`);
67
+ }
68
+ else {
69
+ chunks.push(`\x1b_Gm=${isLast ? 0 : 1};${chunk}\x1b\\`);
70
+ }
71
+ }
72
+ return chunks.join("");
73
+ }
74
+ // ── Public API ────────────────────────────────────────────────────────────────
75
+ /**
76
+ * Render the BCrow logo. Uses native terminal image protocol when available,
77
+ * falls back to ANSI block art.
78
+ *
79
+ * @param widthCols — terminal columns to use for the image (default 20)
80
+ */
81
+ export function renderLogo(widthCols = 20) {
82
+ const protocol = detectProtocol();
83
+ if (protocol === "none") {
84
+ return ANSI_LOGO;
85
+ }
86
+ // Try to load the bundled image
87
+ const image = loadBundledImage();
88
+ if (!image) {
89
+ return ANSI_LOGO;
90
+ }
91
+ if (protocol === "iterm2") {
92
+ return "\n" + renderIterm2(image, widthCols) + "\n";
93
+ }
94
+ if (protocol === "kitty") {
95
+ return "\n" + renderKitty(image, widthCols) + "\n";
96
+ }
97
+ return ANSI_LOGO;
98
+ }
99
+ function loadBundledImage() {
100
+ const candidates = [
101
+ path.join(__dirname_esm, "..", "assets", "bcrow.png"),
102
+ path.join(__dirname_esm, "assets", "bcrow.png"),
103
+ path.join(process.cwd(), "create-balchemy-agent", "assets", "bcrow.png"),
104
+ ];
105
+ for (const candidate of candidates) {
106
+ try {
107
+ if (fs.existsSync(candidate)) {
108
+ const buf = fs.readFileSync(candidate);
109
+ return {
110
+ base64: buf.toString("base64"),
111
+ byteSize: buf.byteLength,
112
+ };
113
+ }
114
+ }
115
+ catch {
116
+ // continue
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+ //# sourceMappingURL=terminal-logo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-logo.js","sourceRoot":"","sources":["../src/terminal-logo.ts"],"names":[],"mappings":"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"}