balchemy 0.2.1 → 0.2.3

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/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  </h1>
4
4
 
5
5
  <p align="center">
6
- <strong>Autonomous AI Trading Agent Platform</strong><br/>
7
- Deploy a dual-LLM trading agent on Solana and EVM chains in 5 minutes.
6
+ <strong>Balchemy CLI</strong><br/>
7
+ Initialize a local trading agent, connect model credentials, set risk rules, and generate deployment files.
8
8
  </p>
9
9
 
10
10
  <p align="center">
@@ -17,20 +17,20 @@
17
17
 
18
18
  ---
19
19
 
20
- ## What Is Balchemy?
20
+ ## What Balchemy Does
21
21
 
22
- Balchemy is an autonomous AI trading platform that connects your LLM to on-chain markets through the Model Context Protocol (MCP). You provide the strategy in natural language; your LLM decides when and what to trade; Balchemy handles wallets, execution, risk checks, and 100+ trading tools.
22
+ Balchemy connects an external LLM to on-chain markets through MCP. The outer LLM
23
+ chooses tools and instructions. Balchemy applies scopes, behavior rules, risk
24
+ checks, execution, and records.
23
25
 
24
- **Architecture Dual-LLM System:**
25
- - **External LLM** (your choice: Claude, GPT, Gemini, Grok, OpenRouter) — the brain that makes all decisions
26
- - **Inner LLM** (GPT-5.4-mini, server-side) — infrastructure servant that fetches data, formats responses, and serves the external LLM
26
+ The inner LLM is infrastructure support. It fetches data and formats responses.
27
+ It does not make trading decisions.
27
28
 
28
- Your LLM calls Balchemy tools via MCP — it picks the right tool from natural language descriptions, not hardcoded tool names.
29
-
30
- ```
31
- You (strategy) → External LLM (decisions) → Balchemy MCP (execution) → Solana / Base / Ethereum
32
-
33
- Inner LLM (data fetching, formatting)
29
+ ```text
30
+ Strategy -> external LLM -> Balchemy MCP -> policy -> execution -> record
31
+ |
32
+ v
33
+ inner LLM support
34
34
  ```
35
35
 
36
36
  ## Quick Start
@@ -39,160 +39,186 @@ You (strategy) → External LLM (decisions) → Balchemy MCP (execution) → Sol
39
39
  npx balchemy
40
40
  ```
41
41
 
42
- The interactive wizard walks you through:
43
- 1. **Pick your LLM** — Anthropic, OpenAI, Gemini, Grok, or OpenRouter
44
- 2. **Set up wallets** — auto-provisioned Solana and EVM wallets
45
- 3. **Define your strategy** natural language rules ("max 5% position size, only trade on Solana")
46
- 4. **Start the 24/7 agent loop** — your LLM monitors markets and trades autonomously
42
+ The wizard handles five local steps:
43
+
44
+ 1. Choose an LLM provider.
45
+ 2. Add the provider API key.
46
+ 3. Set wallet and chain preferences.
47
+ 4. Define behavior rules in plain language.
48
+ 5. Start or export the agent runtime.
49
+
50
+ ## Commands
47
51
 
48
52
  ```bash
49
53
  npx balchemy # Setup wizard or resume cached agent
50
- npx balchemy init # Force new setup wizard
51
- npx balchemy start # Start from agent.config.yaml
52
- npx balchemy list # List saved agents
53
- npx balchemy docker # Generate Docker files for deployment
54
- npx balchemy --help # Show full usage info
55
- npx balchemy --version # Show version
56
- npx balchemy --no-color # Disable colored output (also: NO_COLOR=1)
54
+ npx balchemy init # New setup wizard
55
+ npx balchemy start # Start from agent.config.yaml
56
+ npx balchemy list # List saved agents
57
+ npx balchemy docker # Generate Docker files
58
+ npx balchemy --help # Usage
59
+ npx balchemy --version # Version
60
+ npx balchemy --no-color # Disable color output
57
61
  ```
58
62
 
59
- ## What You Can Do
63
+ `NO_COLOR=1` also disables colored output.
60
64
 
61
- ### Trade
62
- - **Buy/Sell tokens** — `buy 0.1 SOL worth of BONK on Solana`
63
- - **Limit orders** — set price targets, get filled automatically
64
- - **DCA (Dollar-Cost Averaging)** — schedule recurring buys
65
- - **Trailing stops** — lock profits as price moves
65
+ ## Files Written
66
66
 
67
- ### Research
68
- - **Token analysis** — market cap, volume, holder distribution, risk scores
69
- - **Smart money tracking** — follow profitable wallets
70
- - **Bundle detection** — spot insider token launches
71
- - **Price queries** — real-time Solana and EVM token prices
72
-
73
- ### Manage
74
- - **Portfolio view** — see balances, P&L, positions across chains
75
- - **Behavior rules** — define trading constraints in natural language
76
- - **Subscriptions** — set up alerts for market events
77
- - **24/7 autonomous loop** — your LLM monitors and acts even when you're away
78
-
79
- ### Risk Management (Built-In)
80
- Every trade passes through multi-layer safety checks before execution:
81
- - **RugCheck integration** — community risk scores
82
- - **Honeypot detection** — simulated sell before buying
83
- - **Contract verification** — liquidity locks, ownership renouncing
84
- - **Behavior rules enforcement** — your constraints are always respected
85
-
86
- ## 100+ MCP Tools
87
-
88
- Your LLM accesses Balchemy through these primary agent-facing tools:
89
-
90
- | Tool | What It Does |
91
- |------|-------------|
92
- | `trade_command` | Execute buy/sell/swap on Solana or EVM |
93
- | `ask_bot` | Natural language market queries (inner LLM-powered) |
94
- | `agent_research` | Deep token research with technical analysis |
95
- | `agent_portfolio` | Portfolio, positions, P&L overview |
96
- | `configure_behavior_rules` | Set trading constraints in natural language |
97
- | `get_behavior_rules` | Read current active rules |
98
- | `create_subscription` | Set up market event alerts |
99
- | `setup_agent` | Wallet provisioning and onboarding |
100
-
101
- Plus 90+ internal tools for research, risk scoring, and execution — your LLM picks the right one from descriptions.
102
-
103
- ## LLM Providers
104
-
105
- | Provider | Environment Variable | Notes |
106
- |-----------|---------------------|-------|
107
- | Anthropic | `ANTHROPIC_API_KEY` | Haiku 4.5, Sonnet 4.6, Opus 4.6+ |
108
- | OpenAI | `OPENAI_API_KEY` | GPT-4o, GPT-5.4, etc. |
109
- | Google Gemini | `GEMINI_API_KEY` | Gemini 3.1 Pro, Flash |
110
- | Google Vertex AI | `GOOGLE_APPLICATION_CREDENTIALS` | Gemini 3.1 Pro via Vertex AI |
111
- | xAI Grok | `GROK_API_KEY` | Grok-4-1-fast for research |
112
- | OpenRouter | `OPENROUTER_API_KEY` | Access to 100+ models |
113
-
114
- ## Configuration
115
-
116
- After setup, `agent.config.yaml` and `.env` are generated in the current directory:
67
+ The CLI writes local runtime files in the current directory.
117
68
 
118
69
  ```yaml
119
70
  # agent.config.yaml
120
71
  llm:
121
72
  provider: anthropic
122
- model: claude-sonnet-4-20250514
73
+ model: selected-in-wizard
123
74
 
124
75
  agent:
125
76
  name: my-trading-agent
126
- strategy: Focus on Solana memecoins with high volume and positive sentiment
77
+ strategy: Focus on liquid markets. Keep position size below 5%.
127
78
 
128
79
  wallets:
129
80
  solana:
130
81
  chain: solana
131
82
  evm:
132
- chainId: 8453 # Base
83
+ chainId: 8453
133
84
  ```
134
85
 
135
- Edit these files to change settings without re-running the wizard.
86
+ Secrets belong in `.env`. Do not commit that file.
87
+
88
+ ## What You Can Do
89
+
90
+ ### Trade
136
91
 
137
- ## 24/7 Autonomous Mode
92
+ - Buy and sell on Solana and EVM chains.
93
+ - Create limit orders.
94
+ - Schedule DCA rules.
95
+ - Use trailing stops.
138
96
 
139
- The SDK includes an `AgentLoop` that keeps your LLM connected to Balchemy permanently:
97
+ ### Research
98
+
99
+ - Query token price, liquidity, volume, and holder distribution.
100
+ - Check risk signals before a trade.
101
+ - Track wallets and market events.
102
+ - Compare positions across chains.
103
+
104
+ ### Manage
105
+
106
+ - View portfolio, balances, positions, and PnL.
107
+ - Define behavior rules in natural language.
108
+ - Subscribe to market events.
109
+ - Rotate scoped MCP keys in Hub.
110
+
111
+ ## Risk Model
112
+
113
+ Every trade path is expected to pass risk checks before execution:
114
+
115
+ - RugCheck signals.
116
+ - Honeypot simulation.
117
+ - Contract and liquidity checks.
118
+ - Behavior rule enforcement.
119
+ - Scoped MCP permissions.
120
+
121
+ The wallet remains the authority. Keep private keys and seed phrases out of
122
+ source, logs, screenshots, and prompts.
123
+
124
+ ## MCP Tools
125
+
126
+ The CLI connects agents to the curated agent-facing MCP surface.
127
+
128
+ | Tool | Purpose |
129
+ | --- | --- |
130
+ | `trade_command` | Buy, sell, or swap through the approved execution path |
131
+ | `ask_bot` | Natural language market query support |
132
+ | `agent_research` | Token and market research |
133
+ | `agent_portfolio` | Portfolio, positions, and PnL |
134
+ | `configure_behavior_rules` | Trading constraints in natural language |
135
+ | `get_behavior_rules` | Current active rules |
136
+ | `create_subscription` | Market event subscription |
137
+ | `setup_agent` | Onboarding and runtime setup |
138
+
139
+ Granular internal tools stay hidden unless the platform enables them for a bot.
140
+
141
+ ## LLM Providers
142
+
143
+ | Provider | Environment variable |
144
+ | --- | --- |
145
+ | Anthropic | `ANTHROPIC_API_KEY` |
146
+ | OpenAI | `OPENAI_API_KEY` |
147
+ | Google Gemini | `GEMINI_API_KEY` |
148
+ | Google Vertex AI | `GOOGLE_APPLICATION_CREDENTIALS` |
149
+ | xAI Grok | `GROK_API_KEY` |
150
+ | OpenRouter | `OPENROUTER_API_KEY` |
151
+
152
+ Pick the model in the wizard or edit `agent.config.yaml`.
153
+
154
+ ## Runtime Mode
155
+
156
+ The SDK includes an `AgentLoop` for long-running agents.
140
157
 
141
158
  ```ts
142
- import { BalchemyAgentSdk, AgentLoop } from "@balchemyai/agent-sdk";
159
+ import { AgentLoop, BalchemyAgentSdk } from "@balchemyai/agent-sdk";
160
+
161
+ const sdk = new BalchemyAgentSdk({
162
+ apiBaseUrl: "https://api.balchemy.ai/api",
163
+ });
143
164
 
144
- const sdk = new BalchemyAgentSdk({ apiBaseUrl: "https://api.balchemy.ai/api" });
145
- const loop = new AgentLoop(sdk, { apiKey: "your-mcp-api-key" });
165
+ const loop = new AgentLoop(sdk, {
166
+ apiKey: "your-mcp-api-key",
167
+ });
146
168
 
147
- // Starts SSE event stream + polling cycle
148
- // Your LLM receives market events and decides whether to act
149
169
  await loop.start();
150
170
  ```
151
171
 
152
- Events flow: Market event → SSE push → Your LLM evaluates → `trade_command` if relevant → Execution → Confirmation push
172
+ Event flow:
173
+
174
+ ```text
175
+ Market event -> SSE -> external LLM evaluates -> MCP tool call -> execution -> confirmation
176
+ ```
177
+
178
+ ## Hub
153
179
 
154
- ## Hub Integration
180
+ Agents registered through the CLI appear in [Balchemy Hub](https://balchemy.ai/hub).
155
181
 
156
- When you bind your EVM wallet during setup, your agent appears in your [Balchemy Hub](https://balchemy.ai/hub) dashboard:
157
- - **Monitor** — live portfolio, trade history, event log
158
- - **API Keys** — create, rotate, and revoke MCP keys with scoped permissions
159
- - **Scope Management** `read` for data-only, `trade` for full execution
160
- - **Wallet Management** — link/unlink Solana and EVM wallets
182
+ - Monitor portfolio, trade history, and event logs.
183
+ - Create, rotate, and revoke MCP keys.
184
+ - Keep `read` and `trade` scopes separate.
185
+ - Link Solana and EVM wallets.
161
186
 
162
187
  ## Security
163
188
 
164
- - **Encrypted credentials** AES-256-GCM at rest with PBKDF2 key derivation
165
- - **Scoped API keys** `read` and `trade` permissions, instant revocation
166
- - **No seed phrases stored** — all keys encrypted, never logged
167
- - **Behavior rules** hard constraints that even your LLM cannot override
168
- - **Pre-trade safety checks** RugCheck, honeypot detection, contract verification
189
+ - Credentials are encrypted at rest.
190
+ - MCP keys are scoped and revocable.
191
+ - Behavior rules constrain execution.
192
+ - Pre-trade checks run before approved execution.
193
+ - Seed phrases should never be stored in source or logs.
169
194
 
170
- ## TUI Keyboard Shortcuts
195
+ ## TUI Shortcuts
171
196
 
172
197
  | Shortcut | Action |
173
- |----------|--------|
174
- | `^S` | Open settings panel |
198
+ | --- | --- |
199
+ | `^S` | Open settings |
175
200
  | `^L` | Clear chat history |
176
- | `^N` | New agent (return to launcher) |
201
+ | `^N` | New agent |
177
202
  | `^Q` | Quit |
178
203
  | `PgUp/PgDn` | Scroll message history |
179
- | `Esc` | Go back (settings/wizard) |
204
+ | `Esc` | Go back |
180
205
 
181
206
  ## Requirements
182
207
 
183
208
  - Node.js 18+
184
- - An LLM API key from any supported provider
209
+ - One supported LLM provider key
185
210
 
186
211
  ## Links
187
212
 
188
- - **Platform:** [balchemy.ai](https://balchemy.ai)
189
- - **Documentation:** [balchemy.ai/hub/docs](https://balchemy.ai/hub/docs)
190
- - **Agent Explorer:** [balchemy.ai/explorer](https://balchemy.ai/explorer)
191
- - **GitHub:** [github.com/balchemy/balchemy-agent](https://github.com/balchemy/balchemy-agent)
192
- - **npm:** [balchemy](https://www.npmjs.com/package/balchemy) · [@balchemyai/agent-sdk](https://www.npmjs.com/package/@balchemyai/agent-sdk)
193
- - **X:** [@balchemyai](https://x.com/balchemyai)
194
- - **Contact:** [burak@balchemy.ai](mailto:burak@balchemy.ai)
213
+ - [Platform](https://balchemy.ai)
214
+ - [Documentation](https://balchemy.ai/hub/docs)
215
+ - [Agent Explorer](https://balchemy.ai/explorer)
216
+ - [GitHub](https://github.com/balchemy/balchemy-agent)
217
+ - [npm: balchemy](https://www.npmjs.com/package/balchemy)
218
+ - [npm: @balchemyai/agent-sdk](https://www.npmjs.com/package/@balchemyai/agent-sdk)
219
+ - [X: @balchemyai](https://x.com/balchemyai)
220
+ - [Contact](mailto:burak@balchemy.ai)
195
221
 
196
222
  ## License
197
223
 
198
- MIT
224
+ MIT
@@ -0,0 +1,23 @@
1
+ export interface CliFlags {
2
+ help: boolean;
3
+ version: boolean;
4
+ noColor: boolean;
5
+ json: boolean;
6
+ quiet: boolean;
7
+ verbose: boolean;
8
+ debug: boolean;
9
+ ci: boolean;
10
+ dryRun: boolean;
11
+ yes: boolean;
12
+ force: boolean;
13
+ }
14
+ export interface ParsedCliArgs {
15
+ flags: CliFlags;
16
+ commandPath: string[];
17
+ args: string[];
18
+ unknownFlags: string[];
19
+ }
20
+ export declare function parseCliArgs(rawArgs: string[]): ParsedCliArgs;
21
+ export declare function commandKey(commandPath: string[]): string;
22
+ export declare function isNonInteractive(flags: CliFlags): boolean;
23
+ //# sourceMappingURL=cli-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-options.d.ts","sourceRoot":"","sources":["../src/cli-options.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,OAAO,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,QAAQ,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAqED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,aAAa,CA2B7D;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,CAExD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,87 @@
1
+ const GLOBAL_FLAG_ALIASES = {
2
+ "--help": "help",
3
+ "-h": "help",
4
+ "--version": "version",
5
+ "-v": "version",
6
+ "--no-color": "noColor",
7
+ "--json": "json",
8
+ "--quiet": "quiet",
9
+ "-q": "quiet",
10
+ "--verbose": "verbose",
11
+ "--debug": "debug",
12
+ "--ci": "ci",
13
+ "--dry-run": "dryRun",
14
+ "--yes": "yes",
15
+ "-y": "yes",
16
+ "--force": "force",
17
+ };
18
+ function defaultFlags() {
19
+ return {
20
+ help: false,
21
+ version: false,
22
+ noColor: false,
23
+ json: false,
24
+ quiet: false,
25
+ verbose: false,
26
+ debug: false,
27
+ ci: false,
28
+ dryRun: false,
29
+ yes: false,
30
+ force: false,
31
+ };
32
+ }
33
+ function splitCommandAndArgs(positional) {
34
+ const first = positional[0];
35
+ const second = positional[1];
36
+ if (!first)
37
+ return { commandPath: [], args: [] };
38
+ if (first === "agent" && ["list", "current", "use"].includes(second ?? "")) {
39
+ return { commandPath: [first, second], args: positional.slice(2) };
40
+ }
41
+ if (first === "auth" && ["status", "login", "logout"].includes(second ?? "")) {
42
+ return { commandPath: [first, second], args: positional.slice(2) };
43
+ }
44
+ if (first === "context" && ["current", "status"].includes(second ?? "")) {
45
+ return { commandPath: ["agent", "current"], args: positional.slice(2) };
46
+ }
47
+ if (first === "tui") {
48
+ return { commandPath: ["start"], args: positional.slice(1) };
49
+ }
50
+ if (first === "config" && ["validate", "list"].includes(second ?? "")) {
51
+ return { commandPath: [first, second], args: positional.slice(2) };
52
+ }
53
+ if (first === "docker" && second === "generate") {
54
+ return { commandPath: [first], args: positional.slice(2) };
55
+ }
56
+ return { commandPath: [first], args: positional.slice(1) };
57
+ }
58
+ export function parseCliArgs(rawArgs) {
59
+ const flags = defaultFlags();
60
+ const positional = [];
61
+ const unknownFlags = [];
62
+ for (const arg of rawArgs) {
63
+ if (arg === "--init") {
64
+ positional.push("init");
65
+ continue;
66
+ }
67
+ const flagName = GLOBAL_FLAG_ALIASES[arg];
68
+ if (flagName) {
69
+ flags[flagName] = true;
70
+ continue;
71
+ }
72
+ if (arg.startsWith("--")) {
73
+ unknownFlags.push(arg);
74
+ continue;
75
+ }
76
+ positional.push(arg);
77
+ }
78
+ const { commandPath, args } = splitCommandAndArgs(positional);
79
+ return { flags, commandPath, args, unknownFlags };
80
+ }
81
+ export function commandKey(commandPath) {
82
+ return commandPath.join(" ");
83
+ }
84
+ export function isNonInteractive(flags) {
85
+ return flags.ci || !process.stdin.isTTY || !process.stdout.isTTY;
86
+ }
87
+ //# sourceMappingURL=cli-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-options.js","sourceRoot":"","sources":["../src/cli-options.ts"],"names":[],"mappings":"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"}
@@ -2,5 +2,24 @@
2
2
  * Generates Dockerfile + docker-compose.yml for the agent.
3
3
  * Copies static template files and does not mutate them.
4
4
  */
5
- export declare function generateDocker(outDir: string): Promise<void>;
5
+ export type DockerFileAction = "create" | "overwrite" | "skip";
6
+ export interface DockerFilePlan {
7
+ filename: string;
8
+ path: string;
9
+ action: DockerFileAction;
10
+ exists: boolean;
11
+ wouldOverwrite: boolean;
12
+ containsSecret: boolean;
13
+ }
14
+ export interface DockerGenerationPlan {
15
+ outDir: string;
16
+ files: DockerFilePlan[];
17
+ hasOverwrites: boolean;
18
+ }
19
+ export interface DockerGenerationOptions {
20
+ dryRun?: boolean;
21
+ force?: boolean;
22
+ }
23
+ export declare function buildDockerPlan(outDir: string): DockerGenerationPlan;
24
+ export declare function generateDocker(outDir: string, options?: DockerGenerationOptions): Promise<DockerGenerationPlan>;
6
25
  //# sourceMappingURL=docker-gen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"docker-gen.d.ts","sourceRoot":"","sources":["../src/docker-gen.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoBH,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBlE"}
1
+ {"version":3,"file":"docker-gen.d.ts","sourceRoot":"","sources":["../src/docker-gen.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAgCD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,CAqCpE;AAMD,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,oBAAoB,CAAC,CA4B/B"}
@@ -2,39 +2,101 @@
2
2
  * Generates Dockerfile + docker-compose.yml for the agent.
3
3
  * Copies static template files and does not mutate them.
4
4
  */
5
- import * as fs from 'fs';
6
- import * as path from 'path';
7
- import { fileURLToPath } from 'url';
5
+ import * as fs from "node:fs";
6
+ import * as path from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { TerminalError } from "./errors.js";
8
9
  const __filename_esm = fileURLToPath(import.meta.url);
9
10
  const __dirname_esm = path.dirname(__filename_esm);
10
- const TEMPLATES_DIR = path.join(__dirname_esm, '..', 'templates');
11
- function copyTemplate(filename, outDir) {
12
- const src = path.join(TEMPLATES_DIR, filename);
13
- const dest = path.join(outDir, filename);
11
+ const TEMPLATES_DIR = path.join(__dirname_esm, "..", "templates");
12
+ function templatePath(filename) {
13
+ return path.join(TEMPLATES_DIR, filename);
14
+ }
15
+ function assertTemplateExists(filename) {
16
+ const src = templatePath(filename);
14
17
  if (!fs.existsSync(src)) {
15
- throw new Error(`Template file not found: ${src}`);
18
+ throw new TerminalError({
19
+ code: "CONFIG_FILE_MISSING",
20
+ title: "Docker template missing",
21
+ cause: `Template file not found: ${src}`,
22
+ fix: "Reinstall the balchemy CLI package or run from a complete build artifact.",
23
+ exitCode: 2,
24
+ });
16
25
  }
17
- fs.copyFileSync(src, dest);
18
- process.stdout.write(` wrote ${dest}\n`);
19
26
  }
20
- export async function generateDocker(outDir) {
27
+ function plannedFile(outDir, filename, overwriteAllowed) {
28
+ const dest = path.join(outDir, filename);
29
+ const exists = fs.existsSync(dest);
30
+ return {
31
+ filename,
32
+ path: dest,
33
+ action: exists ? (overwriteAllowed ? "overwrite" : "skip") : "create",
34
+ exists,
35
+ wouldOverwrite: exists && overwriteAllowed,
36
+ containsSecret: false,
37
+ };
38
+ }
39
+ export function buildDockerPlan(outDir) {
21
40
  if (!fs.existsSync(outDir)) {
22
- throw new Error(`Output directory does not exist: ${outDir}`);
41
+ throw new TerminalError({
42
+ code: "CONFIG_FILE_MISSING",
43
+ title: "Output directory does not exist",
44
+ cause: `Output directory does not exist: ${outDir}`,
45
+ fix: "Create the directory first or pass an existing output directory.",
46
+ commandSuggestion: "mkdir -p ./deploy && balchemy docker ./deploy --dry-run",
47
+ exitCode: 2,
48
+ });
49
+ }
50
+ if (!fs.statSync(outDir).isDirectory()) {
51
+ throw new TerminalError({
52
+ code: "CONFIG_FILE_MISSING",
53
+ title: "Output path is not a directory",
54
+ cause: `Output path is not a directory: ${outDir}`,
55
+ fix: "Pass a directory path for Docker file generation.",
56
+ exitCode: 2,
57
+ });
58
+ }
59
+ for (const filename of ["Dockerfile", "docker-compose.yml", ".env.example"]) {
60
+ assertTemplateExists(filename);
61
+ }
62
+ const files = [
63
+ plannedFile(outDir, "Dockerfile", true),
64
+ plannedFile(outDir, "docker-compose.yml", true),
65
+ plannedFile(outDir, ".env.example", false),
66
+ ];
67
+ return {
68
+ outDir,
69
+ files,
70
+ hasOverwrites: files.some((file) => file.wouldOverwrite),
71
+ };
72
+ }
73
+ function copyTemplate(filename, dest) {
74
+ fs.copyFileSync(templatePath(filename), dest);
75
+ }
76
+ export async function generateDocker(outDir, options = {}) {
77
+ const plan = buildDockerPlan(outDir);
78
+ if (options.dryRun) {
79
+ return plan;
23
80
  }
24
- process.stdout.write(`Generating Docker files in ${outDir}...\n`);
25
- copyTemplate('Dockerfile', outDir);
26
- copyTemplate('docker-compose.yml', outDir);
27
- // Write .env.example only if not already present (don't overwrite real .env)
28
- const envExampleDest = path.join(outDir, '.env.example');
29
- if (!fs.existsSync(envExampleDest)) {
30
- copyTemplate('.env.example', envExampleDest);
81
+ if (plan.hasOverwrites && !options.force) {
82
+ const targets = plan.files
83
+ .filter((file) => file.wouldOverwrite)
84
+ .map((file) => file.path)
85
+ .join(", ");
86
+ throw new TerminalError({
87
+ code: "FILE_OVERWRITE_CONFIRMATION_REQUIRED",
88
+ title: "Overwrite confirmation required",
89
+ cause: `Existing files would be overwritten: ${targets}`,
90
+ fix: "Review the preview and type overwrite, or rerun with --dry-run to inspect without writing.",
91
+ commandSuggestion: `balchemy docker ${outDir} --dry-run`,
92
+ exitCode: 4,
93
+ });
31
94
  }
32
- else {
33
- process.stdout.write(` skipped .env.example (already exists)\n`);
95
+ for (const file of plan.files) {
96
+ if (file.action === "skip")
97
+ continue;
98
+ copyTemplate(file.filename, file.path);
34
99
  }
35
- process.stdout.write(`\nNext steps:\n` +
36
- ` 1. Copy .env.example to .env and fill in your credentials\n` +
37
- ` 2. Place your agent.config.yaml in the same directory\n` +
38
- ` 3. Run: docker compose up -d\n`);
100
+ return plan;
39
101
  }
40
102
  //# sourceMappingURL=docker-gen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"docker-gen.js","sourceRoot":"","sources":["../src/docker-gen.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;AACnD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAElE,SAAS,YAAY,CAAC,QAAgB,EAAE,MAAc;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAc;IACjD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,oCAAoC,MAAM,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,MAAM,OAAO,CAAC,CAAC;IAElE,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACnC,YAAY,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;IAE3C,6EAA6E;IAC7E,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACnC,YAAY,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iBAAiB;QACjB,+DAA+D;QAC/D,2DAA2D;QAC3D,kCAAkC,CACnC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"docker-gen.js","sourceRoot":"","sources":["../src/docker-gen.ts"],"names":[],"mappings":"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"}