adscriptly 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 (46) hide show
  1. package/README.md +100 -0
  2. package/dist/cli-client/charts.d.ts +7 -0
  3. package/dist/cli-client/charts.d.ts.map +1 -0
  4. package/dist/cli-client/charts.js +417 -0
  5. package/dist/cli-client/charts.js.map +1 -0
  6. package/dist/cli-client/commands/chat.d.ts +3 -0
  7. package/dist/cli-client/commands/chat.d.ts.map +1 -0
  8. package/dist/cli-client/commands/chat.js +523 -0
  9. package/dist/cli-client/commands/chat.js.map +1 -0
  10. package/dist/cli-client/commands/config.d.ts +3 -0
  11. package/dist/cli-client/commands/config.d.ts.map +1 -0
  12. package/dist/cli-client/commands/config.js +23 -0
  13. package/dist/cli-client/commands/config.js.map +1 -0
  14. package/dist/cli-client/commands/login.d.ts +3 -0
  15. package/dist/cli-client/commands/login.d.ts.map +1 -0
  16. package/dist/cli-client/commands/login.js +115 -0
  17. package/dist/cli-client/commands/login.js.map +1 -0
  18. package/dist/cli-client/commands/logout.d.ts +3 -0
  19. package/dist/cli-client/commands/logout.d.ts.map +1 -0
  20. package/dist/cli-client/commands/logout.js +24 -0
  21. package/dist/cli-client/commands/logout.js.map +1 -0
  22. package/dist/cli-client/display.d.ts +26 -0
  23. package/dist/cli-client/display.d.ts.map +1 -0
  24. package/dist/cli-client/display.js +151 -0
  25. package/dist/cli-client/display.js.map +1 -0
  26. package/dist/cli-client/index.d.ts +3 -0
  27. package/dist/cli-client/index.d.ts.map +1 -0
  28. package/dist/cli-client/index.js +17 -0
  29. package/dist/cli-client/index.js.map +1 -0
  30. package/dist/cli-client/report-prompt.d.ts +5 -0
  31. package/dist/cli-client/report-prompt.d.ts.map +1 -0
  32. package/dist/cli-client/report-prompt.js +200 -0
  33. package/dist/cli-client/report-prompt.js.map +1 -0
  34. package/dist/cli-client/sse.d.ts +6 -0
  35. package/dist/cli-client/sse.d.ts.map +1 -0
  36. package/dist/cli-client/sse.js +39 -0
  37. package/dist/cli-client/sse.js.map +1 -0
  38. package/dist/mcp-server/index.d.ts +3 -0
  39. package/dist/mcp-server/index.d.ts.map +1 -0
  40. package/dist/mcp-server/index.js +266 -0
  41. package/dist/mcp-server/index.js.map +1 -0
  42. package/dist/shared/config.d.ts +21 -0
  43. package/dist/shared/config.d.ts.map +1 -0
  44. package/dist/shared/config.js +41 -0
  45. package/dist/shared/config.js.map +1 -0
  46. package/package.json +59 -0
@@ -0,0 +1,115 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { readConfig, writeConfig, getApiBaseUrl } from "../../shared/config.js";
4
+ import { createServer } from "node:http";
5
+ import { randomBytes } from "node:crypto";
6
+ import { exec } from "node:child_process";
7
+ import { platform } from "node:os";
8
+ const LOGIN_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
9
+ export const loginCommand = new Command("login")
10
+ .description("Authenticate with Adscriptly via browser")
11
+ .action(async () => {
12
+ const config = readConfig();
13
+ if (config.token) {
14
+ const apiBaseUrl = getApiBaseUrl(config);
15
+ console.log(chalk.green("Already authenticated."));
16
+ console.log(chalk.dim(" API: ") + apiBaseUrl);
17
+ if (config.email) {
18
+ console.log(chalk.dim(" Email: ") + config.email);
19
+ }
20
+ console.log(chalk.dim("\nRun ") + chalk.cyan('"adscriptly logout"') + chalk.dim(" to sign out."));
21
+ return;
22
+ }
23
+ const apiBaseUrl = getApiBaseUrl(config);
24
+ const state = randomBytes(16).toString("hex");
25
+ try {
26
+ const token = await startAuthFlow(apiBaseUrl, state);
27
+ // Store token in config, persisting the API URL used during auth
28
+ // so subsequent commands in fresh terminals hit the same server
29
+ const DEFAULT_API_URL = "https://app.adscriptly.com";
30
+ const configToWrite = { ...config, token };
31
+ if (apiBaseUrl !== DEFAULT_API_URL) {
32
+ configToWrite.apiUrl = apiBaseUrl;
33
+ }
34
+ writeConfig(configToWrite);
35
+ console.log(chalk.green("\nAuthenticated successfully!"));
36
+ console.log(chalk.dim("Token stored in ~/.adscriptly/config.json"));
37
+ console.log(chalk.dim("\nRun ") + chalk.cyan('"adscriptly chat"') + chalk.dim(" to start."));
38
+ }
39
+ catch (err) {
40
+ const message = err instanceof Error ? err.message : String(err);
41
+ console.error(chalk.red(`\nLogin failed: ${message}`));
42
+ process.exit(1);
43
+ }
44
+ });
45
+ /**
46
+ * Start a temporary localhost HTTP server, open browser to auth page,
47
+ * wait for callback with token, validate state, return token.
48
+ */
49
+ function startAuthFlow(apiBaseUrl, state) {
50
+ return new Promise((resolve, reject) => {
51
+ const server = createServer((req, res) => {
52
+ const url = new URL(req.url || "/", "http://localhost");
53
+ if (url.pathname !== "/callback") {
54
+ res.writeHead(404, { "Content-Type": "text/plain" });
55
+ res.end("Not found");
56
+ return;
57
+ }
58
+ const token = url.searchParams.get("token");
59
+ const receivedState = url.searchParams.get("state");
60
+ // Send success page to browser
61
+ res.writeHead(200, { "Content-Type": "text/html" });
62
+ res.end([
63
+ "<html><body style=\"background:#000;color:#fff;font-family:system-ui;",
64
+ "display:flex;align-items:center;justify-content:center;height:100vh;margin:0\">",
65
+ "<div style=\"text-align:center\">",
66
+ "<h1 style=\"color:#60a5fa\">&#10003; Authenticated</h1>",
67
+ "<p style=\"color:#9ca3af\">You can close this tab and return to the terminal.</p>",
68
+ "</div></body></html>",
69
+ ].join(""));
70
+ // Close server, then resolve/reject
71
+ server.close();
72
+ clearTimeout(timeout);
73
+ if (!token || !receivedState) {
74
+ reject(new Error("Missing token or state in callback"));
75
+ return;
76
+ }
77
+ if (receivedState !== state) {
78
+ reject(new Error("State parameter mismatch. Login may have been tampered with."));
79
+ return;
80
+ }
81
+ resolve(token);
82
+ });
83
+ // Timeout after 5 minutes
84
+ const timeout = setTimeout(() => {
85
+ server.close();
86
+ reject(new Error("Login timed out after 5 minutes. Please try again."));
87
+ }, LOGIN_TIMEOUT_MS);
88
+ server.listen(0, "127.0.0.1", () => {
89
+ const addr = server.address();
90
+ const port = typeof addr === "object" && addr ? addr.port : 0;
91
+ const redirectUri = `http://127.0.0.1:${port}/callback`;
92
+ const authUrl = `${apiBaseUrl}/cli/auth?state=${encodeURIComponent(state)}&redirect_uri=${encodeURIComponent(redirectUri)}`;
93
+ console.log(chalk.cyan("Opening browser for authentication..."));
94
+ console.log(chalk.dim("\nIf the browser doesn't open, visit:\n ") + authUrl + "\n");
95
+ console.log(chalk.dim("Waiting for authorization..."));
96
+ openBrowser(authUrl);
97
+ });
98
+ server.on("error", (err) => {
99
+ clearTimeout(timeout);
100
+ reject(new Error(`Failed to start auth server: ${err.message}`));
101
+ });
102
+ });
103
+ }
104
+ function openBrowser(url) {
105
+ const os = platform();
106
+ const cmd = os === "darwin"
107
+ ? `open "${url}"`
108
+ : os === "win32"
109
+ ? `start "" "${url}"`
110
+ : `xdg-open "${url}"`;
111
+ exec(cmd, () => {
112
+ // Silently ignore errors — URL is printed for manual copy
113
+ });
114
+ }
115
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/cli-client/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare const logoutCommand: Command;
3
+ //# sourceMappingURL=logout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.d.ts","sourceRoot":"","sources":["../../../src/cli-client/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,aAAa,SAgBtB,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { readConfig } from "../../shared/config.js";
4
+ import { existsSync, unlinkSync } from "node:fs";
5
+ import { join } from "node:path";
6
+ import { homedir } from "node:os";
7
+ const CONFIG_FILE = join(homedir(), ".adscriptly", "config.json");
8
+ export const logoutCommand = new Command("logout")
9
+ .description("Remove stored authentication token")
10
+ .action(async () => {
11
+ const config = readConfig();
12
+ if (!config.token) {
13
+ console.log(chalk.dim("Not currently authenticated."));
14
+ return;
15
+ }
16
+ if (existsSync(CONFIG_FILE)) {
17
+ unlinkSync(CONFIG_FILE);
18
+ console.log(chalk.green("Logged out.") + chalk.dim(" Token removed from ~/.adscriptly/config.json"));
19
+ }
20
+ else {
21
+ console.log(chalk.dim("No config file found."));
22
+ }
23
+ });
24
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../../src/cli-client/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;AAElE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC;KAC/C,WAAW,CAAC,oCAAoC,CAAC;KACjD,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC,CAAC;QACvD,OAAO;IACT,CAAC;IAED,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,UAAU,CAAC,WAAW,CAAC,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC,CAAC;IACvG,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;IAClD,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ export interface Spinner {
2
+ update(text: string): void;
3
+ stop(finalText?: string): void;
4
+ }
5
+ export declare function startSpinner(text: string): Spinner;
6
+ export declare function toolCallLabel(toolName: string): string;
7
+ export type StepStatus = "active" | "complete" | "error";
8
+ export interface ToolStep {
9
+ toolName: string;
10
+ status: StepStatus;
11
+ startTime: number;
12
+ endTime?: number;
13
+ error?: string;
14
+ }
15
+ export declare function renderStep(step: ToolStep): string;
16
+ export declare class StepList {
17
+ private steps;
18
+ private spinner;
19
+ startStep(toolName: string): void;
20
+ completeStep(toolName: string): void;
21
+ errorStep(toolName: string, error?: string): void;
22
+ clear(): void;
23
+ private findActive;
24
+ private stopSpinner;
25
+ }
26
+ //# sourceMappingURL=display.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../../src/cli-client/display.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAsBlD;AAoED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAID,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAkBjD;AAID,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,OAAO,CAAwB;IAEvC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAcjC,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAWpC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAYjD,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,WAAW;CAMpB"}
@@ -0,0 +1,151 @@
1
+ import chalk from "chalk";
2
+ // --- Braille spinner ---
3
+ const BRAILLE_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
4
+ export function startSpinner(text) {
5
+ let frameIndex = 0;
6
+ const stream = process.stderr;
7
+ const interval = setInterval(() => {
8
+ const frame = chalk.cyan(BRAILLE_FRAMES[frameIndex % BRAILLE_FRAMES.length]);
9
+ stream.write(`\r${frame} ${text}`);
10
+ frameIndex++;
11
+ }, 80);
12
+ return {
13
+ update(newText) {
14
+ text = newText;
15
+ },
16
+ stop(finalText) {
17
+ clearInterval(interval);
18
+ stream.write("\r" + " ".repeat(text.length + 4) + "\r");
19
+ if (finalText) {
20
+ stream.write(finalText + "\n");
21
+ }
22
+ },
23
+ };
24
+ }
25
+ // --- Tool call labels ---
26
+ const TOOL_LABELS = {
27
+ // Core Google Ads
28
+ list_accounts: "Listing Google Ads accounts",
29
+ execute_gaql_query: "Running GAQL query",
30
+ run_gaql: "Running GAQL query",
31
+ list_gaql_resources: "Loading GAQL resource reference",
32
+ get_account_currency: "Checking account currency",
33
+ // Performance & assets
34
+ get_campaign_performance: "Fetching campaign performance",
35
+ get_ad_performance: "Fetching ad performance",
36
+ get_ad_creatives: "Fetching ad creatives",
37
+ get_image_assets: "Fetching image assets",
38
+ get_image_asset_urls: "Fetching image asset URLs",
39
+ get_asset_usage: "Checking asset usage",
40
+ analyze_image_assets: "Analyzing image assets",
41
+ // Mutations
42
+ execute_mutate: "Executing Google Ads mutation",
43
+ // Tier 1 reports
44
+ get_search_terms_report: "Pulling search terms report",
45
+ get_keyword_report: "Pulling keyword report",
46
+ get_device_report: "Pulling device report",
47
+ get_location_report: "Pulling location report",
48
+ get_schedule_report: "Pulling schedule report",
49
+ get_age_report: "Pulling age demographics report",
50
+ get_gender_report: "Pulling gender demographics report",
51
+ get_income_report: "Pulling income demographics report",
52
+ get_asset_performance_report: "Pulling asset performance report",
53
+ get_auction_insights: "Pulling auction insights",
54
+ // Tier 2 reports
55
+ get_ad_group_report: "Pulling ad group report",
56
+ get_user_location_report: "Checking user location targeting",
57
+ get_conversion_action_report: "Pulling conversion action report",
58
+ get_landing_page_report: "Pulling landing page report",
59
+ get_call_details_report: "Pulling call details report",
60
+ get_change_history: "Pulling change history",
61
+ // Adscriptly read
62
+ get_dashboard_analytics: "Loading dashboard analytics",
63
+ get_keyword_performance: "Loading keyword performance",
64
+ get_keyword_kpis: "Loading keyword KPIs",
65
+ get_conversion_stats: "Loading conversion stats",
66
+ get_conversion_analytics: "Loading conversion analytics",
67
+ get_pending_negative_keywords: "Loading pending negative keywords",
68
+ get_adscriptly_search_terms: "Loading search terms",
69
+ get_negative_keyword_agent_status: "Checking keyword agent status",
70
+ get_pending_positive_keywords: "Loading pending positive keywords",
71
+ get_sync_status: "Checking sync status",
72
+ // Keyword research
73
+ get_keyword_ideas: "Generating keyword ideas",
74
+ get_keyword_metrics: "Fetching keyword metrics",
75
+ // Adscriptly write
76
+ approve_negative_keywords: "Approving negative keywords",
77
+ dismiss_negative_keywords: "Dismissing negative keywords",
78
+ apply_negative_keywords: "Applying negative keywords",
79
+ approve_positive_keywords: "Approving positive keywords",
80
+ trigger_sync: "Triggering data sync",
81
+ };
82
+ export function toolCallLabel(toolName) {
83
+ return TOOL_LABELS[toolName] || `Running ${toolName}`;
84
+ }
85
+ export function renderStep(step) {
86
+ const label = toolCallLabel(step.toolName);
87
+ switch (step.status) {
88
+ case "active": {
89
+ return `${chalk.cyan("⠋")} ${label}`;
90
+ }
91
+ case "complete": {
92
+ const duration = step.endTime
93
+ ? ((step.endTime - step.startTime) / 1000).toFixed(1)
94
+ : "0.0";
95
+ return `${chalk.green("✓")} ${label} ${chalk.dim(`(${duration}s)`)}`;
96
+ }
97
+ case "error": {
98
+ const msg = step.error ? ` — ${step.error}` : "";
99
+ return `${chalk.red("✗")} ${label}${chalk.red(msg)}`;
100
+ }
101
+ }
102
+ }
103
+ // --- Step list manager ---
104
+ export class StepList {
105
+ steps = [];
106
+ spinner = null;
107
+ startStep(toolName) {
108
+ // Complete any active spinner
109
+ this.stopSpinner();
110
+ const step = {
111
+ toolName,
112
+ status: "active",
113
+ startTime: Date.now(),
114
+ };
115
+ this.steps.push(step);
116
+ this.spinner = startSpinner(toolCallLabel(toolName));
117
+ }
118
+ completeStep(toolName) {
119
+ this.stopSpinner();
120
+ const step = this.findActive(toolName);
121
+ if (step) {
122
+ step.status = "complete";
123
+ step.endTime = Date.now();
124
+ process.stderr.write(renderStep(step) + "\n");
125
+ }
126
+ }
127
+ errorStep(toolName, error) {
128
+ this.stopSpinner();
129
+ const step = this.findActive(toolName);
130
+ if (step) {
131
+ step.status = "error";
132
+ step.endTime = Date.now();
133
+ step.error = error;
134
+ process.stderr.write(renderStep(step) + "\n");
135
+ }
136
+ }
137
+ clear() {
138
+ this.stopSpinner();
139
+ this.steps = [];
140
+ }
141
+ findActive(toolName) {
142
+ return this.steps.find((s) => s.toolName === toolName && s.status === "active");
143
+ }
144
+ stopSpinner() {
145
+ if (this.spinner) {
146
+ this.spinner.stop();
147
+ this.spinner = null;
148
+ }
149
+ }
150
+ }
151
+ //# sourceMappingURL=display.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"display.js","sourceRoot":"","sources":["../../src/cli-client/display.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli-client/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { loginCommand } from "./commands/login.js";
4
+ import { logoutCommand } from "./commands/logout.js";
5
+ import { chatCommand } from "./commands/chat.js";
6
+ import { configCommand } from "./commands/config.js";
7
+ const program = new Command();
8
+ program
9
+ .name("adscriptly")
10
+ .description("AI-powered CLI for managing Google Ads from the terminal")
11
+ .version("0.1.0");
12
+ program.addCommand(loginCommand);
13
+ program.addCommand(logoutCommand);
14
+ program.addCommand(chatCommand, { isDefault: true });
15
+ program.addCommand(configCommand);
16
+ program.parse();
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli-client/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,0DAA0D,CAAC;KACvE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACrD,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ type Period = "weekly" | "monthly" | "quarterly";
2
+ type Format = "md" | "pdf" | "docx";
3
+ export declare function buildReportPrompt(period: Period, format: Format, accountName?: string | null): string;
4
+ export {};
5
+ //# sourceMappingURL=report-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-prompt.d.ts","sourceRoot":"","sources":["../../src/cli-client/report-prompt.ts"],"names":[],"mappings":"AAAA,KAAK,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAAC;AACjD,KAAK,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC;AAwKpC,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAC1B,MAAM,CAyCR"}
@@ -0,0 +1,200 @@
1
+ const DATE_RANGES = {
2
+ weekly: { current: "LAST_7_DAYS", previous: "the 7 days before that", label: "Week" },
3
+ monthly: { current: "LAST_30_DAYS", previous: "the 30 days before that", label: "Month" },
4
+ quarterly: { current: "last 90 days", previous: "the 90 days before that", label: "Quarter" },
5
+ };
6
+ function getMarkdownTemplate(period) {
7
+ if (period === "weekly") {
8
+ return `
9
+ Output the report as markdown directly. Use this structure:
10
+
11
+ ## Weekly Report — [Campaign Name]
12
+ **[Date Range]**
13
+
14
+ ### Performance
15
+ | Metric | This Week | Last Week | Change |
16
+ |--------|-----------|-----------|--------|
17
+ | Conversions | [value] | [value] | [+/-X% ▲/▼] |
18
+ | CPA | [value] | [value] | [+/-X% ▲/▼] |
19
+ | Spend | [value] | [value] | [+/-X% ▲/▼] |
20
+ | CTR | [value] | [value] | [+/-X% ▲/▼] |
21
+
22
+ ### Conversions
23
+ | Type | Count |
24
+ |------|-------|
25
+ | [type] | [count] |
26
+
27
+ ### Changes Made
28
+ - [list optimizations from change history]
29
+
30
+ ### Next Week
31
+ - [2-3 actionable recommendations]`;
32
+ }
33
+ if (period === "monthly") {
34
+ return `
35
+ Output the report as markdown directly. Use this structure:
36
+
37
+ ## Monthly Report — [Campaign Name]
38
+ **[Month Year]**
39
+
40
+ ### Executive Summary
41
+ [2-3 sentence overview: conversions, CPA trend, key wins, impression share opportunity]
42
+
43
+ ### Performance
44
+ | Metric | This Month | Last Month | Change |
45
+ |--------|------------|------------|--------|
46
+ | Conversions | [value] | [value] | [+/-X% ▲/▼] |
47
+ | CPA | [value] | [value] | [+/-X% ▲/▼] |
48
+ | Spend | [value] | [value] | [+/-X% ▲/▼] |
49
+ | Conv Rate | [value] | [value] | [+/-X% ▲/▼] |
50
+ | CTR | [value] | [value] | [+/-X% ▲/▼] |
51
+
52
+ ### Signed Cases (if Adscriptly offline data available)
53
+ | Name | Date | Status |
54
+ |------|------|--------|
55
+ | [name] | [date] | [status] |
56
+
57
+ **Signed:** [n] | **Pending:** [n] | **Close Rate:** [n%]
58
+
59
+ (If no Adscriptly data, show conversion breakdown by type instead)
60
+
61
+ ### Competitive Position
62
+ | Metric | Current |
63
+ |--------|---------|
64
+ | Impression Share | [value] |
65
+ | Lost to Rank | [value] |
66
+ | Lost to Budget | [value] |
67
+ | Est. Ceiling | [calculated: current spend / current IS * 0.85] |
68
+
69
+ ### Optimizations Completed
70
+ | Category | Changes | Impact |
71
+ |----------|---------|--------|
72
+ | [category] | [description] | [impact] |
73
+
74
+ ### Recommendations
75
+ | Priority | Action | Expected Impact |
76
+ |----------|--------|-----------------|
77
+ | 1 | [action] | [impact] |
78
+ | 2 | [action] | [impact] |
79
+ | 3 | [action] | [impact] |`;
80
+ }
81
+ // quarterly
82
+ return `
83
+ Output the report as markdown directly. Use this structure:
84
+
85
+ ## Q[N] [Year] Report — [Campaign Name]
86
+
87
+ ### Executive Summary
88
+ [3-4 sentence overview: business impact, ROI, trajectory, scaling opportunity]
89
+
90
+ ### Business Impact
91
+ | Metric | Q[N] [Year] |
92
+ |--------|-------------|
93
+ | Signed Cases | [value] |
94
+ | Total Spend | [value] |
95
+ | Cost per Signed Case | [value] |
96
+ | Est. Revenue Generated | [value] |
97
+ | ROI | [value] |
98
+
99
+ (If no Adscriptly signed case data, show total conversions and CPA instead)
100
+
101
+ ### Performance Trend
102
+ | Month | Conversions | CPA | Spend |
103
+ |-------|-------------|-----|-------|
104
+ | [month 1] | [value] | [value] | [value] |
105
+ | [month 2] | [value] | [value] | [value] |
106
+ | [month 3] | [value] | [value] | [value] |
107
+
108
+ ### Competitive Position
109
+ | Metric | Current |
110
+ |--------|---------|
111
+ | Impression Share | [value] |
112
+ | Lost to Rank | [value] |
113
+ | Lost to Budget | [value] |
114
+
115
+ ### Optimizations Completed
116
+ | Category | Changes | Impact |
117
+ |----------|---------|--------|
118
+ | [category] | [description] | [impact] |
119
+
120
+ ### Scaling Roadmap
121
+ | Phase | Action | Investment | Projected Return |
122
+ |-------|--------|------------|------------------|
123
+ | Now | [action] | [amount] | [projection] |
124
+ | Next Quarter | [action] | [amount] | [projection] |
125
+ | Future | [action] | [amount] | [projection] |
126
+
127
+ ### Recommendations for Next Quarter
128
+ 1. [recommendation]
129
+ 2. [recommendation]
130
+ 3. [recommendation]`;
131
+ }
132
+ function getFormatInstructions(period, format) {
133
+ if (format === "md") {
134
+ return getMarkdownTemplate(period);
135
+ }
136
+ const toolCallInstructions = `After gathering and analyzing the data, call the generate_report_file tool with:
137
+ - format: "${format}"
138
+ - title: The report title (e.g. "${DATE_RANGES[period].label} Report — [Campaign Name]")
139
+ - sections: An array of section objects. Each section has:
140
+ - heading (string): The section title
141
+ - body (string, optional): Paragraph text
142
+ - table (optional): { headers: string[], rows: string[][] }
143
+
144
+ Structure your sections to match the report template — one section per major area (Executive Summary, Performance, Conversions, Competitive Position, Optimizations, Recommendations, etc.).
145
+
146
+ IMPORTANT: All table cell values must be strings. Convert numbers to strings (e.g. "47" not 47, "$1,312" not 1312).
147
+
148
+ The tool returns a download URL. Show the URL to the user so they can download the file.`;
149
+ if (format === "pdf") {
150
+ return `${toolCallInstructions}
151
+
152
+ Format as a clean professional document with an executive summary section followed by detailed data sections with tables.`;
153
+ }
154
+ // docx
155
+ return `${toolCallInstructions}
156
+
157
+ This is a full narrative document — use detailed body text in each section. More verbose than a presentation, suitable for clients who want to read, edit, or annotate.`;
158
+ }
159
+ export function buildReportPrompt(period, format, accountName) {
160
+ const range = DATE_RANGES[period];
161
+ return `Generate a ${period} performance report${accountName ? ` for ${accountName}` : ""}.
162
+
163
+ ## Instructions
164
+
165
+ ### Step 1: Gather Data
166
+ Call these tools to collect data:
167
+ - get_campaign_performance for ${range.current} — get spend, conversions, CPA, CTR, impression share
168
+ - get_campaign_performance for ${range.previous} — same metrics for comparison period
169
+ - get_conversion_action_report — conversion types breakdown
170
+ - get_conversion_analytics — Adscriptly offline conversion data (signed cases if available)
171
+ - get_change_history — what optimizations were made this period
172
+ ${period !== "weekly" ? `- get_auction_insights — competitive position data` : ""}
173
+
174
+ ### Step 2: Calculate Comparisons
175
+ For each metric, calculate:
176
+ - Absolute values for both periods
177
+ - % change = ((current - previous) / previous) * 100
178
+ - Use ▲ for improvements (more conversions, lower CPA, higher CTR)
179
+ - Use ▼ for declines
180
+
181
+ ### Step 3: Detect Conversion Source
182
+ - If Adscriptly offline conversions exist → show "Signed Cases" table with names, dates, status
183
+ - If only Google Ads conversions → show conversion counts by type
184
+ - If no conversions → flag "Conversion Tracking Gap" and generate traffic-only report
185
+
186
+ ### Step 4: Generate Report
187
+ ${getFormatInstructions(period, format)}
188
+
189
+ ### Error Handling
190
+ - If no account is selected, ask which account to report on
191
+ - If insufficient data for the period, say how much data is available and offer a partial report
192
+ - If a tool call fails, continue with available data and note what's missing
193
+
194
+ ### Important
195
+ - Use actual numbers from tool results, never make up data
196
+ - Format currency based on account currency (use get_account_currency if unsure)
197
+ - Keep the tone professional and client-ready
198
+ - Every metric comparison must show the actual values AND the % change`;
199
+ }
200
+ //# sourceMappingURL=report-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-prompt.js","sourceRoot":"","sources":["../../src/cli-client/report-prompt.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,GAAyE;IACxF,MAAM,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,wBAAwB,EAAE,KAAK,EAAE,MAAM,EAAE;IACrF,OAAO,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,yBAAyB,EAAE,KAAK,EAAE,OAAO,EAAE;IACzF,SAAS,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,yBAAyB,EAAE,KAAK,EAAE,SAAS,EAAE;CAC9F,CAAC;AAEF,SAAS,mBAAmB,CAAC,MAAc;IACzC,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,OAAO;;;;;;;;;;;;;;;;;;;;;;;mCAuBwB,CAAC;IAClC,CAAC;IAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BA6CiB,CAAC;IAC3B,CAAC;IAED,YAAY;IACZ,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAgDW,CAAC;AACrB,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAc,EAAE,MAAc;IAC3D,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,oBAAoB,GAAG;aAClB,MAAM;mCACgB,WAAW,CAAC,MAAM,CAAC,CAAC,KAAK;;;;;;;;;;yFAU6B,CAAC;IAExF,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACrB,OAAO,GAAG,oBAAoB;;0HAEwF,CAAC;IACzH,CAAC;IAED,OAAO;IACP,OAAO,GAAG,oBAAoB;;wKAEwI,CAAC;AACzK,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAAc,EACd,MAAc,EACd,WAA2B;IAE3B,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAElC,OAAO,cAAc,MAAM,sBAAsB,WAAW,CAAC,CAAC,CAAC,QAAQ,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE;;;;;;iCAM1D,KAAK,CAAC,OAAO;iCACb,KAAK,CAAC,QAAQ;;;;EAI7C,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,oDAAoD,CAAC,CAAC,CAAC,EAAE;;;;;;;;;;;;;;;EAe/E,qBAAqB,CAAC,MAAM,EAAE,MAAM,CAAC;;;;;;;;;;;uEAWgC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface SSEEvent {
2
+ event: string;
3
+ data: string;
4
+ }
5
+ export declare function parseSSE(response: Response): AsyncGenerator<SSEEvent>;
6
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/cli-client/sse.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAuB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAwC5E"}
@@ -0,0 +1,39 @@
1
+ export async function* parseSSE(response) {
2
+ const body = response.body;
3
+ if (!body)
4
+ return;
5
+ const reader = body.getReader();
6
+ const decoder = new TextDecoder();
7
+ let buffer = "";
8
+ try {
9
+ for (;;) {
10
+ const { done, value } = await reader.read();
11
+ if (done)
12
+ break;
13
+ buffer += decoder.decode(value, { stream: true });
14
+ const parts = buffer.split("\n\n");
15
+ buffer = parts.pop();
16
+ for (const part of parts) {
17
+ if (!part.trim())
18
+ continue;
19
+ let event = "";
20
+ let data = "";
21
+ for (const line of part.split("\n")) {
22
+ if (line.startsWith("event: ")) {
23
+ event = line.slice(7);
24
+ }
25
+ else if (line.startsWith("data: ")) {
26
+ data = line.slice(6);
27
+ }
28
+ }
29
+ if (event && data) {
30
+ yield { event, data };
31
+ }
32
+ }
33
+ }
34
+ }
35
+ finally {
36
+ reader.releaseLock();
37
+ }
38
+ }
39
+ //# sourceMappingURL=sse.js.map