openads-ai 0.2.1 β†’ 0.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.
package/README.md CHANGED
@@ -11,10 +11,11 @@
11
11
  AI Command Center for Marketers
12
12
  ```
13
13
 
14
- > **Talk to your ad campaigns in plain English.** Connect your Google Ads and Meta accounts, pick your favorite AI model, and let OpenAds handle the analysis while you focus on strategy.
14
+ > **Talk to your ad campaigns in plain English.** Connect your Google Ads, Google Analytics (GA4), and Meta accounts, pick your favorite AI model, and let OpenAds handle the analysis while you focus on strategy.
15
15
 
16
16
  <p align="center">
17
17
  <img src="https://img.shields.io/badge/Google%20Ads-MCP-4285F4?style=flat-square&logo=google-ads" />
18
+ <img src="https://img.shields.io/badge/Google%20Analytics%204-MCP-E37400?style=flat-square&logo=google-analytics" />
18
19
  <img src="https://img.shields.io/badge/Meta%20Ads-MCP-1877F2?style=flat-square&logo=meta" />
19
20
  <img src="https://img.shields.io/badge/License-MIT-green?style=flat-square" />
20
21
  </p>
@@ -32,7 +33,7 @@ OpenAds is an **open-source CLI tool** that turns any AI model into a marketing
32
33
  | Feature | What it means for you |
33
34
  |---|---|
34
35
  | 🧠 **Pre-built marketing skills** | The AI already knows Google Ads best practices, Meta creative formats, CRO frameworks, and copywriting rules. You just ask. |
35
- | πŸ”Œ **Direct platform access** | Connect your Google Ads and Meta accounts. The AI reads your live data β€” no more copy-pasting reports. |
36
+ | πŸ”Œ **Direct platform access** | Connect your Google Ads, Google Analytics (GA4), and Meta accounts. The AI reads your live data β€” no more copy-pasting reports. |
36
37
  | πŸ€– **Bring your own model** | Use Google Gemini, OpenAI, Claude, or a local model running on your machine. Your choice. |
37
38
  | πŸ›‘οΈ **Nothing goes live without you** | The AI can read freely, but every write operation (campaign change, budget edit) requires your explicit approval. |
38
39
  | ⚑ **Autonomous loops** | Let the AI research competitors, test ad variants, and generate hypotheses overnight. Review in the morning. |
@@ -51,33 +52,56 @@ Here is a look at OpenAds in action:
51
52
 
52
53
  ## ⚑ Quick Start
53
54
 
54
- ### 1. Install
55
+ ### πŸ‘Ά New to the terminal? Start here!
55
56
 
57
+ OpenAds is a local desktop application that runs in your computer's **Terminal** (a text-based window where you can run commands). Follow these simple steps to get started:
58
+
59
+ #### Step 1: Install Node.js (Required)
60
+ OpenAds runs on your computer using Node.js. If you don't have it yet, installing it is just like any normal application:
61
+ 1. Go to [nodejs.org](https://nodejs.org/) and click the **LTS (Recommended)** button to download it.
62
+ 2. Open the downloaded file and run the installer (just click "Next" until it finishes).
63
+
64
+ #### Step 2: Open your Terminal
65
+ * **Mac:** Press `Cmd + Space` (Spotlight search), type **Terminal**, and press `Enter`.
66
+ * **Windows:** Press the `Windows Key` on your keyboard, type **cmd** (Command Prompt), and press `Enter`.
67
+
68
+ #### Step 3: Install OpenAds
69
+ Copy the command below, paste it into your Terminal window, and press **Enter**:
56
70
  ```bash
57
71
  npm install -g openads-ai
58
72
  ```
73
+ > πŸ’‘ **Permissions Error?** If your Terminal shows a red error about "EACCES" or permissions, copy and paste this command instead:
74
+ > `sudo npm install -g openads-ai` (Mac will ask you to type your computer password and press Enter).
59
75
 
60
- > **Tip:** If you see a permissions error, prefix with `sudo` or [configure npm for global installs](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally).
61
-
62
- ### 2. Set up (one time)
63
-
76
+ #### Step 4: Run the Setup Wizard
77
+ Paste this command into your Terminal and press **Enter**:
64
78
  ```bash
65
79
  openads setup
66
80
  ```
81
+ This launches a beautiful, step-by-step interactive setup wizard where you can:
82
+ * **Select your favorite AI model** (Google Gemini, OpenAI, Claude, or a free local model)
83
+ * **Link your ad accounts** (Google Ads, GA4, and/or Meta Ads)
84
+ * **Describe your business** (so the AI writes copy tailored exactly to your brand)
85
+
86
+ #### Step 5: Start using OpenAds!
87
+ Whenever you want to audit your campaigns or write copy, just open your Terminal, type:
88
+ ```bash
89
+ openads
90
+ ```
91
+ and press **Enter** to open your dashboard.
92
+
93
+ ---
67
94
 
68
- The setup wizard walks you through three things:
69
- - **Pick your AI model** β€” choose from Google Gemini, OpenAI, Claude, a local model, or any OpenAI-compatible provider
70
- - **Connect your ad accounts** β€” Google Ads and/or Meta Ads (both optional)
71
- - **Describe your business** β€” so the AI can tailor copy and strategy to your product
95
+ ### πŸ’» Already know how to use the terminal? Quick commands
72
96
 
73
- ### 3. Launch
97
+ If you're already familiar with node packages, just run:
74
98
 
75
99
  ```bash
100
+ npm install -g openads-ai
101
+ openads setup
76
102
  openads
77
103
  ```
78
104
 
79
- That's it. You'll see a menu with quick actions. Pick one, or just type your question in plain English.
80
-
81
105
  ---
82
106
 
83
107
  ## πŸ’‘ What can I do with it?
@@ -187,7 +211,7 @@ This verifies your config file, API keys, platform connections (live token check
187
211
 
188
212
  ## πŸ—ΊοΈ Roadmap
189
213
 
190
- - [x] Google Ads integration via MCP
214
+ - [x] Google Ads & GA4 integration via MCP
191
215
  - [x] Meta Ads integration via MCP
192
216
  - [x] Interactive setup wizard with live token verification
193
217
  - [x] 12 pre-built skills: Ads, CRO, Copywriting, Analytics, Email, Video, Research, Strategy
package/dist/cli.js CHANGED
@@ -12,6 +12,7 @@ import { runSetup } from './setup.js';
12
12
  import { runDoctor } from './doctor.js';
13
13
  import { runScheduleManager, runScheduledTask, openReportInBrowser, listReports } from './schedule.js';
14
14
  import enquirer from 'enquirer';
15
+ import { hasGlobalRtk } from './token-optimizer.js';
15
16
  const __filename = fileURLToPath(import.meta.url);
16
17
  const __dirname = path.dirname(__filename);
17
18
  const pkgDir = path.resolve(__dirname, '..');
@@ -81,25 +82,21 @@ _You can also edit this file manually at: ${contextPath}_
81
82
  // Makes the agent behave as "OpenAds" instead of generic Pi.
82
83
  function buildSystemPrompt(config) {
83
84
  const contextPath = path.join(CONFIG_DIR, 'context', 'my-business.md');
85
+ const isLaunchMode = config.mode === 'launch';
84
86
  const parts = [
85
87
  'You are OpenAds, an AI marketing assistant built for digital marketers.',
86
88
  'You specialize in Google Ads, Meta Ads, copywriting, analytics, CRO, and go-to-market strategy.',
87
89
  'Always speak in plain marketing language. Never use developer jargon.',
88
90
  'Address the user as a marketing professional.',
89
91
  'When writing ad copy or recommendations, always reference the user\'s product context first.',
90
- 'For any write operation (creating campaigns, changing budgets), always preview the change and ask for explicit confirmation before executing.',
91
- '',
92
- '## Memory',
93
- '',
94
- `Your business context file is at: ${contextPath}`,
95
- 'This file contains everything you have learned about the user\'s business across sessions.',
96
- 'At the START of every conversation, read this file to recall past context.',
97
- 'At the END of a conversation (or when you learn something significant), APPEND new insights to the "## Learnings" section of that file.',
98
- 'Things worth remembering: product details, audience segments, campaign performance benchmarks, winning ad angles, competitor insights, budget constraints, seasonal patterns, and any preferences the user expresses.',
99
- 'Format each learning as a bullet point with a date, e.g.: "- (2026-05-24) Best-performing Meta creative uses customer testimonial videos."',
100
- 'Never overwrite existing learnings β€” only append new ones.',
101
- 'If the learnings section grows beyond 50 items, summarize the oldest 25 into a "## Summary" section at the top and remove the individual bullets.',
102
92
  ];
93
+ if (isLaunchMode) {
94
+ parts.push('YOU ARE OPERATING IN LAUNCH MODE (READ-WRITE).', 'You are authorized to execute active write modifications on ad accounts (e.g. pausing campaigns, scaling bids, altering daily budgets, creating ads).', 'CRITICAL SAFETY RULE: For any write operation, you MUST generate a clear visual preview card outlining the exact changes and ask the user for explicit confirmation (Y/N) before executing. NEVER make active changes without their explicit confirmation.');
95
+ }
96
+ else {
97
+ parts.push('YOU ARE OPERATING IN AUDIT MODE (SAFE / READ-ONLY).', 'You are authorized to read campaigns, analyze performance data, find budget waste, and recommend copy or landing page changes.', 'CRITICAL SAFETY RULE: You are NOT authorized to make any active modifications to campaigns, budgets, or ad creative settings under any circumstances.', 'If the user asks you to pause a campaign, change a budget, or execute a write operation, explain politely that OpenAds is currently in Audit Mode (Safe/Read-only). Outline the exact steps you would take, and tell them to toggle to Launch Mode in Settings (`openads setup`) to execute them.');
98
+ }
99
+ parts.push('', '## Memory', '', `Your business context file is at: ${contextPath}`, 'This file contains everything you have learned about the user\'s business across sessions.', 'At the START of every conversation, read this file to recall past context.', 'At the END of a conversation (or when you learn something significant), APPEND new insights to the "## Learnings" section of that file.', 'Things worth remembering: product details, audience segments, campaign performance benchmarks, winning ad angles, competitor insights, budget constraints, seasonal patterns, and any preferences the user expresses.', 'Format each learning as a bullet point with a date, e.g.: "- (2026-05-24) Best-performing Meta creative uses customer testimonial videos."', 'Never overwrite existing learnings β€” only append new ones.', 'If the learnings section grows beyond 50 items, summarize the oldest 25 into a "## Summary" section at the top and remove the individual bullets.');
103
100
  if (config?.productContext) {
104
101
  parts.push(`\nThe user's business: ${config.productContext}`);
105
102
  }
@@ -252,13 +249,15 @@ async function main() {
252
249
  const modelName = chalk.cyan.bold(cleanProvider);
253
250
  const googleStatus = config.connectGoogle ? chalk.green('● Connected') : chalk.gray('β—‹ Not connected');
254
251
  const metaStatus = config.metaToken ? chalk.green('● Connected') : chalk.gray('β—‹ Not connected');
252
+ const modeName = config.mode === 'launch' ? chalk.red.bold('Launch Mode (Read-Write)') : chalk.green.bold('Audit Mode (Safe / Read-only)');
255
253
  // Build compact status panel
256
254
  const statusLines = [
257
255
  ` ${chalk.bold.white('Model')} ${modelName}`,
256
+ ` ${chalk.bold.white('Mode')} ${modeName}`,
258
257
  ` ${chalk.bold.white('Google Ads')} ${googleStatus}`,
259
258
  ` ${chalk.bold.white('Meta Ads')} ${metaStatus}`,
260
259
  '',
261
- ` ${chalk.gray('v0.1.0')} ${chalk.gray('Β·')} ${chalk.gray('AI Command Center for Marketers')}`,
260
+ ` ${chalk.gray('v0.2.1')} ${chalk.gray('Β·')} ${chalk.gray('AI Command Center for Marketers')}`,
262
261
  ].join('\n');
263
262
  console.log(boxen(statusLines, {
264
263
  padding: { top: 1, bottom: 1, left: 2, right: 2 },
@@ -374,12 +373,21 @@ async function main() {
374
373
  baseDelayMs: 15000,
375
374
  provider: { maxRetryDelayMs: 120000 }
376
375
  };
376
+ // Setup MCP Servers inside settings.json
377
+ settings.mcpServers = settings.mcpServers || {};
378
+ const useRtk = hasGlobalRtk();
379
+ // Google Ads integration
380
+ if (config.connectGoogle) {
381
+ settings.mcpServers['google-ads'] = {
382
+ command: useRtk ? 'rtk' : 'uvx',
383
+ args: useRtk ? ['uvx', 'adloop'] : ['adloop']
384
+ };
385
+ }
377
386
  // Inject Meta MCP server if token is present
378
387
  if (config.metaToken) {
379
- settings.mcpServers = settings.mcpServers || {};
380
388
  settings.mcpServers['meta-ads'] = {
381
- command: 'npx',
382
- args: ['-y', '@meta/mcp-server'],
389
+ command: useRtk ? 'rtk' : 'npx',
390
+ args: useRtk ? ['npx', '-y', '@meta/mcp-server'] : ['-y', '@meta/mcp-server'],
383
391
  env: { META_ACCESS_TOKEN: config.metaToken }
384
392
  };
385
393
  }
package/dist/schedule.js CHANGED
@@ -7,6 +7,7 @@ import { spawnSync } from 'child_process';
7
7
  import gradient from 'gradient-string';
8
8
  import open from 'open';
9
9
  import { compileHtmlReport } from './report-template.js';
10
+ import { optimizeTokenContext } from './token-optimizer.js';
10
11
  const openadsGradient = gradient(['#00d2ff', '#3a7bd5', '#00d2ff']);
11
12
  const CONFIG_DIR = path.join(os.homedir(), '.openads');
12
13
  const SCHEDULES_DIR = path.join(CONFIG_DIR, 'schedules');
@@ -261,11 +262,15 @@ export async function runScheduledTask(name) {
261
262
  stdio: ['ignore', 'pipe', 'pipe'],
262
263
  });
263
264
  if (result.stdout) {
264
- console.log(result.stdout);
265
- fullMarkdown += result.stdout;
265
+ const cleanOutput = optimizeTokenContext(result.stdout);
266
+ console.log(cleanOutput);
267
+ fullMarkdown += cleanOutput;
266
268
  }
267
269
  if (result.stderr) {
268
- console.error(result.stderr);
270
+ const cleanError = optimizeTokenContext(result.stderr);
271
+ if (cleanError.trim()) {
272
+ console.error(cleanError);
273
+ }
269
274
  }
270
275
  // Compile and save the HTML report
271
276
  try {
package/dist/setup.js CHANGED
@@ -125,6 +125,7 @@ export async function runSetup() {
125
125
  let customModel = '';
126
126
  let localModelName = '';
127
127
  let localBaseUrl = '';
128
+ let mode = 'audit';
128
129
  if (provider === 'local') {
129
130
  console.log(chalk.yellow('\n--- Local AI Setup ---'));
130
131
  console.log('You can run models 100% offline using tools like Ollama or LM Studio.');
@@ -209,8 +210,26 @@ export async function runSetup() {
209
210
  console.log(chalk.green(`\nβœ“ Local AI configured β€” ready to connect to ${localBaseUrl}.\n`));
210
211
  }
211
212
  console.log(chalk.gray('─────────────────────────────────────────\n'));
212
- // Step 2: Google Ads
213
- console.log(chalk.cyan('Step 2/4: Connect Google Ads (optional)\n'));
213
+ // Step 2: Choose operational mode
214
+ console.log(chalk.cyan('Step 2/5: Choose operational mode\n'));
215
+ console.log('OpenAds has two operational modes:');
216
+ console.log(` - ${chalk.green.bold('Audit Mode (Safe / Read-only)')}: AI can analyze performance, find budget waste, and recommend strategies. Zero risk.`);
217
+ console.log(` - ${chalk.red.bold('Launch Mode (Read-Write)')}: AI is authorized to optimize bids, modify budgets, and launch ads (always requires confirmation).\n`);
218
+ const modeAnswers = await enquirer.prompt({
219
+ type: 'select',
220
+ name: 'selectedMode',
221
+ message: 'Choose your default operational mode:',
222
+ choices: [
223
+ { name: 'audit', message: 'Audit Mode (Safe / Read-only β€” Recommended)' },
224
+ { name: 'launch', message: 'Launch Mode (Read-Write β€” Active campaign changes)' }
225
+ ],
226
+ initial: existingConfig.mode === 'launch' ? 1 : 0
227
+ });
228
+ mode = modeAnswers.selectedMode;
229
+ console.log(chalk.green(`\nβœ“ Operational mode configured: ${mode === 'launch' ? 'Launch Mode (Read-Write)' : 'Audit Mode (Safe / Read-only)'}.\n`));
230
+ console.log(chalk.gray('─────────────────────────────────────────\n'));
231
+ // Step 3: Google Ads
232
+ console.log(chalk.cyan('Step 3/5: Connect Google Ads (optional)\n'));
214
233
  console.log('OpenAds can read and analyze your Google Ads campaigns, keywords, and performance.\n');
215
234
  const { connectGoogle } = await enquirer.prompt({
216
235
  type: 'confirm',
@@ -248,8 +267,8 @@ export async function runSetup() {
248
267
  console.log(chalk.green('βœ“ Google Ads module enabled.\n'));
249
268
  }
250
269
  console.log(chalk.gray('─────────────────────────────────────────\n'));
251
- // Step 3: Meta Ads
252
- console.log(chalk.cyan('Step 3/4: Connect Meta Ads (optional)\n'));
270
+ // Step 4: Meta Ads
271
+ console.log(chalk.cyan('Step 4/5: Connect Meta Ads (optional)\n'));
253
272
  console.log('OpenAds can read your Meta campaigns, creatives, and audience performance.\n');
254
273
  let metaToken = '';
255
274
  const { connectMeta } = await enquirer.prompt({
@@ -355,8 +374,8 @@ export async function runSetup() {
355
374
  }
356
375
  }
357
376
  console.log(chalk.gray('─────────────────────────────────────────\n'));
358
- // Step 4: Business Context
359
- console.log(chalk.cyan('Step 4/4: Tell me about your business\n'));
377
+ // Step 5: Business Context
378
+ console.log(chalk.cyan('Step 5/5: Tell me about your business\n'));
360
379
  const { productContext } = await enquirer.prompt({
361
380
  type: 'input',
362
381
  name: 'productContext',
@@ -379,6 +398,7 @@ export async function runSetup() {
379
398
  provider: finalModel,
380
399
  apiKey,
381
400
  localBaseUrl,
401
+ mode,
382
402
  connectGoogle,
383
403
  metaToken,
384
404
  productContext
@@ -0,0 +1,66 @@
1
+ import { spawnSync } from 'child_process';
2
+ /**
3
+ * Checks if the Rust Token Killer (rtk) CLI binary is installed globally on the user's machine.
4
+ */
5
+ export function hasGlobalRtk() {
6
+ try {
7
+ const result = spawnSync('rtk', ['--version'], { stdio: 'ignore' });
8
+ return result.status === 0;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
13
+ }
14
+ /**
15
+ * TS-native token compression utility. Filters out progress bars, experimental warnings,
16
+ * download lines, and redundant log noise from CLI processes to optimize AI context windows.
17
+ */
18
+ export function optimizeTokenContext(text) {
19
+ if (!text)
20
+ return '';
21
+ // 1. Strip ANSI escape sequences (colors, text formatting, control characters)
22
+ const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
23
+ let cleanText = text.replace(ansiRegex, '');
24
+ // 2. Process line-by-line to strip installation/CLI environment noise
25
+ const lines = cleanText.split('\n');
26
+ const filteredLines = lines.filter(line => {
27
+ const l = line.trim();
28
+ if (!l)
29
+ return true; // Keep spacing empty lines
30
+ // Ignore Node, NPM, NPX, UV/UVX experimental warnings & logs
31
+ if (l.startsWith('ExperimentalWarning:'))
32
+ return false;
33
+ if (l.startsWith('npm notice'))
34
+ return false;
35
+ if (l.startsWith('npm warn'))
36
+ return false;
37
+ if (l.startsWith('npm ERR!'))
38
+ return false;
39
+ if (l.includes('npx: installed'))
40
+ return false;
41
+ if (l.includes('npm install'))
42
+ return false;
43
+ if (l.includes('audited'))
44
+ return false;
45
+ if (l.includes('found 0 vulnerabilities'))
46
+ return false;
47
+ // Ignore package/tool downloader progress indicators
48
+ if (l.includes('Retrieving'))
49
+ return false;
50
+ if (l.includes('Downloading'))
51
+ return false;
52
+ if (l.startsWith('Resolving'))
53
+ return false;
54
+ if (l.startsWith('Installed'))
55
+ return false;
56
+ if (l.includes('Warning:'))
57
+ return false;
58
+ // Ignore progress bar lines
59
+ if (l.includes('[====='))
60
+ return false;
61
+ if (l.includes('========>'))
62
+ return false;
63
+ return true;
64
+ });
65
+ return filteredLines.join('\n');
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openads-ai",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Open-source AI command center for digital marketers. Audit campaigns, write ad copy, and build strategies β€” from your terminal.",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {