apptvty 0.1.5 → 0.2.1

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/dist/cli.js CHANGED
@@ -22,7 +22,8 @@ async function register(options) {
22
22
  body: JSON.stringify({
23
23
  domain: options.domain,
24
24
  framework: options.framework ?? "other",
25
- agent_id: options.agentId
25
+ agent_id: options.agentId,
26
+ ...options.email && { email: options.email }
26
27
  }),
27
28
  signal: AbortSignal.timeout(2e4)
28
29
  });
@@ -39,15 +40,18 @@ async function register(options) {
39
40
  apiKey: data.api_key,
40
41
  companyId: data.company_id,
41
42
  walletAddress: data.wallet_address ?? null,
43
+ platformDepositAddress: data.platform_deposit_address ?? null,
42
44
  dashboardUrl: data.dashboard_url,
43
45
  claimTokenExpiresAt: data.claim_token_expires_at,
44
46
  trialEndsAt: data.trial_ends_at,
47
+ email: data.email ?? null,
45
48
  setup: {
46
49
  envVars: data.setup?.env_vars ?? {
47
50
  APPTVTY_SITE_ID: data.site_id,
48
51
  APPTVTY_API_KEY: data.api_key
49
52
  },
50
- files: data.setup?.files
53
+ files: data.setup?.files,
54
+ x402: data.setup?.x402
51
55
  }
52
56
  };
53
57
  }
@@ -85,7 +89,8 @@ async function migrate(options) {
85
89
  var args = process.argv.slice(2);
86
90
  var rawCmd = args[0];
87
91
  var isMigrate = rawCmd === "migrate";
88
- var cmdArgs = isMigrate ? args.slice(1) : args;
92
+ var isCreateSuperuser = rawCmd === "create-superuser";
93
+ var cmdArgs = isMigrate || isCreateSuperuser ? args.slice(1) : args;
89
94
  function getFlag(name, from = cmdArgs) {
90
95
  const idx = from.indexOf(`--${name}`);
91
96
  return idx !== -1 ? from[idx + 1] : void 0;
@@ -94,6 +99,7 @@ var isNonInteractive = cmdArgs.includes("--non-interactive") || cmdArgs.includes
94
99
  var flagDomain = getFlag("domain");
95
100
  var flagFramework = getFlag("framework");
96
101
  var flagApiUrl = getFlag("api-url");
102
+ var flagEmail = getFlag("email");
97
103
  function detectFramework() {
98
104
  try {
99
105
  const pkg = JSON.parse((0, import_fs.readFileSync)((0, import_path.join)(process.cwd(), "package.json"), "utf-8"));
@@ -186,7 +192,11 @@ function loadEnvVars() {
186
192
  const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.+?)\s*$/);
187
193
  if (m) out[m[1]] = m[2].replace(/^["']|["']$/g, "").trim();
188
194
  }
189
- return { siteId: out.APPTVTY_SITE_ID, apiKey: out.APPTVTY_API_KEY };
195
+ return {
196
+ siteId: out.APPTVTY_SITE_ID,
197
+ apiKey: out.APPTVTY_API_KEY,
198
+ claimToken: out.APPTVTY_CLAIM_TOKEN
199
+ };
190
200
  }
191
201
  async function runInit() {
192
202
  const framework = flagFramework ?? detectFramework();
@@ -201,6 +211,10 @@ async function runInit() {
201
211
  if (!domain && !isNonInteractive) {
202
212
  domain = await prompt(" Enter your site domain (e.g. mysite.com): ");
203
213
  }
214
+ let email = flagEmail ?? "";
215
+ if (!email && !isNonInteractive) {
216
+ email = await prompt(" Enter your email to claim this account (optional, press Enter to skip): ");
217
+ }
204
218
  if (!domain) {
205
219
  if (isNonInteractive) {
206
220
  process.stdout.write(JSON.stringify({ error: "domain is required (use --domain)" }) + "\n");
@@ -218,7 +232,8 @@ async function runInit() {
218
232
  domain,
219
233
  framework,
220
234
  agentId: "apptvty-cli",
221
- apiUrl: flagApiUrl
235
+ apiUrl: flagApiUrl,
236
+ ...email && { email }
222
237
  });
223
238
  } catch (err) {
224
239
  const msg = err instanceof RegistrationError ? err.message : String(err);
@@ -253,9 +268,12 @@ async function runInit() {
253
268
  company_id: result.companyId,
254
269
  wallet_address: result.walletAddress,
255
270
  dashboard_url: result.dashboardUrl,
271
+ claim_token_expires_at: result.claimTokenExpiresAt,
256
272
  trial_ends_at: result.trialEndsAt,
273
+ email: result.email ?? null,
257
274
  env_file: envFile,
258
- env_vars: result.setup.envVars
275
+ env_vars: result.setup.envVars,
276
+ x402: result.setup.x402
259
277
  }) + "\n"
260
278
  );
261
279
  return;
@@ -264,6 +282,9 @@ async function runInit() {
264
282
  if (result.walletAddress) {
265
283
  console.log(` \u2713 Wallet created: ${result.walletAddress}`);
266
284
  }
285
+ if (email) {
286
+ console.log(` \u2713 Email linked: ${email}`);
287
+ }
267
288
  console.log(` \u2713 Credentials written: ${envFile}`);
268
289
  if (scaffolded.length > 0) {
269
290
  for (const f of scaffolded) {
@@ -274,21 +295,83 @@ async function runInit() {
274
295
  console.log("\n middleware.ts");
275
296
  console.log(" import { withApptvty } from 'apptvty/nextjs';");
276
297
  console.log(" export default withApptvty({ apiKey: process.env.APPTVTY_API_KEY!, siteId: process.env.APPTVTY_SITE_ID! });");
298
+ console.log(" export default withApptvty({ apiKey: process.env.APPTVTY_API_KEY!, siteId: process.env.APPTVTY_SITE_ID! });");
277
299
  console.log(" export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };");
278
- console.log("\n app/query/route.ts");
300
+ console.log("\n app/query/route.ts (Agent Interface)");
279
301
  console.log(" import { createNextjsQueryHandler } from 'apptvty/nextjs';");
280
302
  console.log(" export const GET = createNextjsQueryHandler({ apiKey: process.env.APPTVTY_API_KEY!, siteId: process.env.APPTVTY_SITE_ID! });");
303
+ console.log("\n app/api/apptvty/logs/route.ts (Maintainer Dashboard)");
304
+ console.log(" import { createNextjsDashboardHandler } from 'apptvty/nextjs';");
305
+ console.log(" export const GET = createNextjsDashboardHandler({ apiKey: process.env.APPTVTY_API_KEY!, siteId: process.env.APPTVTY_SITE_ID! });");
281
306
  }
282
307
  console.log("\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
283
308
  console.log(" Free trial active \u2014 24 hours from now.");
284
- console.log(` Trial expires: ${result.trialEndsAt}`);
309
+ if (result.setup.x402) {
310
+ console.log(` \u2713 Agent-native payments (X402): ${result.setup.x402.chain}/${result.setup.x402.currency}`);
311
+ if (result.platformDepositAddress) {
312
+ console.log(` \u2713 Platform Deposit Address: ${result.platformDepositAddress}`);
313
+ console.log(" (Send USDC here to fund ad campaigns autonomously)");
314
+ }
315
+ }
316
+ console.log(` \u2713 Trial expires: ${result.trialEndsAt}`);
317
+ console.log("");
318
+ if (email) {
319
+ console.log(` A claim link has been sent to: ${email}`);
320
+ console.log(" Or open the link below to access your dashboard:");
321
+ } else {
322
+ console.log(" No email provided \u2014 click the link below to claim your account.");
323
+ console.log(" (This link expires in 30 days. Save it now.)");
324
+ }
285
325
  console.log("");
286
- console.log(" Open the dashboard to log in before the trial ends:");
287
326
  console.log(` ${result.dashboardUrl}`);
288
327
  console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
289
328
  console.log(" After the trial expires, this site's API key will stop working.");
329
+ console.log(" Advertisers: Deposit USDC to the address above, then fund via:");
330
+ console.log(" GET /v1/wallet/sync?tx_hash=<your_transaction_hash>");
290
331
  console.log(" Log in once to keep your credentials and continue receiving analytics.\n");
291
332
  }
333
+ async function runCreateSuperuser() {
334
+ console.log("\n \u{1F680} Apptvty \u2014 Create Superuser\n");
335
+ const { claimToken } = loadEnvVars();
336
+ if (!claimToken) {
337
+ console.error("\n \u2716 Error: APPTVTY_CLAIM_TOKEN not found in .env.local or .env");
338
+ console.log(" Please run `npx apptvty init` first to initialize your site.\n");
339
+ process.exit(1);
340
+ }
341
+ const email = await prompt(" Email: ");
342
+ const password = await prompt(" Password: ");
343
+ const name = await prompt(" Full Name (optional): ");
344
+ const apiUrl = flagApiUrl || "https://api.apptvty.com";
345
+ try {
346
+ const response = await fetch(`${apiUrl}/v1/auth/claim`, {
347
+ method: "POST",
348
+ headers: {
349
+ "Content-Type": "application/json"
350
+ },
351
+ body: JSON.stringify({
352
+ claim_token: claimToken,
353
+ email,
354
+ password,
355
+ name
356
+ })
357
+ });
358
+ const result = await response.json();
359
+ if (!response.ok) {
360
+ console.error(`
361
+ \u2716 Error: ${result.message || result.error?.message || response.statusText}`);
362
+ process.exit(1);
363
+ }
364
+ console.log("\n \u2713 Superuser created successfully!");
365
+ console.log(` \u2713 Email: ${result.user.email}`);
366
+ console.log(` \u2713 Company: ${result.company.name}
367
+ `);
368
+ console.log(" You can now log in at https://dashboard.apptvty.com\n");
369
+ } catch (err) {
370
+ console.error(`
371
+ \u2716 Network error: ${err.message}`);
372
+ process.exit(1);
373
+ }
374
+ }
292
375
  async function runMigrate() {
293
376
  const { siteId, apiKey } = loadEnvVars();
294
377
  if (!siteId || !apiKey) {
@@ -332,6 +415,8 @@ async function runMigrate() {
332
415
  async function main() {
333
416
  if (isMigrate) {
334
417
  await runMigrate();
418
+ } else if (isCreateSuperuser) {
419
+ await runCreateSuperuser();
335
420
  } else {
336
421
  await runInit();
337
422
  }
package/dist/index.d.mts CHANGED
@@ -1,25 +1,47 @@
1
- import { A as ApptvtyConfig, R as RequestLogEntry, Q as QueryRequest, B as BackendQueryResponse, P as PageAdsResponse, I as ImpressionLog, S as SiteOverviewStats, D as DailyStat, C as CrawlerBreakdown, a as RecentActivityItem, b as RecentQueryItem, c as SiteWalletInfo, d as CrawlerInfo, e as AgentQueryResponse, f as QueryEndpointDiscovery, g as AgentErrorResponse } from './types-C1oUTCsT.mjs';
2
- export { h as PageAd, i as QuerySource, j as SponsoredAd } from './types-C1oUTCsT.mjs';
1
+ import { C as CrawlerInfo, A as ApptvtyConfig, R as RequestLogEntry, Q as QueryRequest, B as BackendQueryResponse, P as PageAdsResponse, I as ImpressionLog, S as SiteOverviewStats, D as DailyStat, a as RecentActivityItem, b as RecentQueryItem, c as CrawlerBreakdown, d as SiteWalletInfo, e as CreateCampaignParams, f as CampaignRecord, U as UpdateCampaignParams, g as InsufficientBalanceError, h as AgentQueryResponse, i as QueryEndpointDiscovery, j as AgentErrorResponse } from './types-Zt2qHOrW.mjs';
2
+ export { k as CampaignStatus, l as PageAd, m as QuerySource, n as SponsoredAd } from './types-Zt2qHOrW.mjs';
3
3
  export { createNextjsQueryHandler, withApptvty } from './middleware/nextjs.mjs';
4
4
  export { createExpressMiddleware, createExpressQueryHandler } from './middleware/express.mjs';
5
5
  import 'next/server';
6
6
  import 'node:http';
7
7
 
8
8
  /**
9
- * HTTP client for the Apptvty API.
9
+ * Lightweight AI crawler detection.
10
10
  *
11
- * Handles:
12
- * - Batch log ingestion (/v1/logs/batch)
13
- * - Query processing (/v1/query) returns answer + optional ad
14
- * - Impression logging (/v1/impressions) — triggers billing for ads
11
+ * This is the single source of truth for crawler classification in the SDK.
12
+ * The backend (Python analytics API and TypeScript handlers) should eventually
13
+ * consume this same list rather than maintaining separate copies.
15
14
  */
16
15
 
16
+ declare function detectCrawler(userAgent: string): CrawlerInfo;
17
+ /** Returns the list of all known crawlers for reference (e.g. for agents.txt generation) */
18
+ declare function getKnownCrawlerNames(): string[];
19
+ interface ScraperServiceInfo {
20
+ isScraperService: boolean;
21
+ /** Identifier of the matched service, or null if not a scraper service. */
22
+ name: string | null;
23
+ }
24
+ /**
25
+ * Detect whether the request comes from a known content-extraction scraper service
26
+ * (Jina, FireCrawl, Cloudflare Browser Rendering, etc.) rather than a direct AI crawler.
27
+ *
28
+ * Use this alongside detectCrawler() — scraper services are a distinct category:
29
+ * they proxy on behalf of an AI consumer but apply their own readability transform.
30
+ */
31
+ declare function detectScraperService(userAgent: string): ScraperServiceInfo;
32
+
17
33
  declare class ApptvtyClient {
18
34
  private readonly baseUrl;
19
35
  private readonly apiKey;
20
36
  private readonly siteId;
21
37
  private readonly debug;
38
+ private x402Token?;
22
39
  constructor(config: ApptvtyConfig);
40
+ /**
41
+ * Set the X402 (LSAT) token for subsequent requests.
42
+ * This is called after the agent has successfully paid an X402 challenge.
43
+ */
44
+ setX402Token(macaroon: string, preimage: string): void;
23
45
  /**
24
46
  * Send a batch of request log entries to the Apptvty ingestion API.
25
47
  * Called by the logger's auto-flush — not called directly by user code.
@@ -61,6 +83,10 @@ declare class ApptvtyClient {
61
83
  days: number;
62
84
  stats: DailyStat[];
63
85
  }>;
86
+ /** Get recent activity logs (last 48h). */
87
+ getRecentActivity(siteId: string, limit?: number, offset?: number): Promise<RecentActivityItem[]>;
88
+ /** Get recent agent queries. */
89
+ getRecentQueries(siteId: string, limit?: number, offset?: number): Promise<RecentQueryItem[]>;
64
90
  /** Get crawler breakdown by type (default 30 days). */
65
91
  getSiteCrawlers(days?: number): Promise<{
66
92
  days: number;
@@ -76,7 +102,87 @@ declare class ApptvtyClient {
76
102
  }>;
77
103
  /** Get wallet balance and earnings. */
78
104
  getSiteWallet(): Promise<SiteWalletInfo>;
105
+ /**
106
+ * Create an ad campaign. The campaign goes live immediately once the wallet
107
+ * has sufficient balance.
108
+ *
109
+ * If the wallet balance is too low, throws `ApptvtyInsufficientBalanceError`
110
+ * which includes deposit instructions so the agent can fund the wallet and retry.
111
+ *
112
+ * @example
113
+ * // Site-based: ad copy derived from your site's crawled content
114
+ * const campaign = await client.createCampaign({
115
+ * name: 'My Kubernetes Blog',
116
+ * advertiser_site_id: 'site_abc123',
117
+ * keywords: ['kubernetes', 'devops', 'containers'],
118
+ * categories: ['technology'],
119
+ * bid_per_view_usdc: 0.001,
120
+ * daily_budget_usdc: 1.0,
121
+ * total_budget_usdc: 20.0,
122
+ * });
123
+ *
124
+ * // Static: manually written ad copy
125
+ * const campaign = await client.createCampaign({
126
+ * name: 'My Blog — Static',
127
+ * ad_text: 'Deep dives on Kubernetes, written by practitioners.',
128
+ * landing_url: 'https://myblog.com',
129
+ * keywords: ['kubernetes', 'devops'],
130
+ * categories: ['technology'],
131
+ * bid_per_view_usdc: 0.001,
132
+ * daily_budget_usdc: 1.0,
133
+ * total_budget_usdc: 10.0,
134
+ * });
135
+ */
136
+ createCampaign(params: CreateCampaignParams): Promise<CampaignRecord>;
137
+ /**
138
+ * List all campaigns for this account.
139
+ * Also returns the schema (valid categories, minimum bid) so the agent
140
+ * knows valid field values without guessing.
141
+ */
142
+ listCampaigns(): Promise<{
143
+ campaigns: CampaignRecord[];
144
+ total: number;
145
+ schema: {
146
+ valid_categories: string[];
147
+ valid_statuses: string[];
148
+ bid_per_view_usdc_minimum: number;
149
+ };
150
+ }>;
151
+ /**
152
+ * Get a single campaign by ID, including current spend and impression count.
153
+ * `budget_remaining_usdc` tells the agent how much budget is left before
154
+ * the campaign auto-pauses.
155
+ */
156
+ getCampaign(campaignId: string): Promise<CampaignRecord>;
157
+ /**
158
+ * Partially update a campaign. Only fields present in `params` are changed.
159
+ *
160
+ * @example
161
+ * // Pause a campaign
162
+ * await client.updateCampaign(id, { status: 'paused' });
163
+ *
164
+ * // Increase bid and daily budget
165
+ * await client.updateCampaign(id, { bid_per_view_usdc: 0.002, daily_budget_usdc: 5.0 });
166
+ */
167
+ updateCampaign(campaignId: string, params: UpdateCampaignParams): Promise<CampaignRecord>;
168
+ /**
169
+ * Pause a campaign immediately. Equivalent to updateCampaign(id, { status: 'paused' }).
170
+ * Campaigns are never deleted — billing history is retained.
171
+ */
172
+ pauseCampaign(campaignId: string): Promise<void>;
173
+ /**
174
+ * Resume a paused campaign.
175
+ */
176
+ resumeCampaign(campaignId: string): Promise<void>;
79
177
  private get;
178
+ private patch;
179
+ private delete;
180
+ /**
181
+ * Settle an X402 challenge from the site's own USDC wallet balance.
182
+ * Called automatically when `post()` receives a 402 with an X402 header.
183
+ * Returns the preimage on success, or throws if the wallet balance is too low.
184
+ */
185
+ private payX402Challenge;
80
186
  private post;
81
187
  private log;
82
188
  private warn;
@@ -87,6 +193,30 @@ declare class ApptvtyApiError extends Error {
87
193
  readonly body: string;
88
194
  constructor(statusCode: number, path: string, body: string);
89
195
  }
196
+ /**
197
+ * Thrown by createCampaign() when the wallet balance is too low.
198
+ *
199
+ * The `details` property contains everything needed to fund the wallet
200
+ * autonomously and retry — the agent can inspect `details.deposit.address`
201
+ * and send USDC on Base without human intervention.
202
+ *
203
+ * @example
204
+ * import { ApptvtyInsufficientBalanceError } from 'apptvty';
205
+ *
206
+ * try {
207
+ * await client.createCampaign(params);
208
+ * } catch (err) {
209
+ * if (err instanceof ApptvtyInsufficientBalanceError) {
210
+ * const { shortfall_usdc, deposit } = err.details;
211
+ * // Agent can now: send shortfall_usdc USDC to deposit.address on deposit.chain
212
+ * // Then retry createCampaign
213
+ * }
214
+ * }
215
+ */
216
+ declare class ApptvtyInsufficientBalanceError extends Error {
217
+ readonly details: InsufficientBalanceError | undefined;
218
+ constructor(details: InsufficientBalanceError | undefined);
219
+ }
90
220
 
91
221
  /**
92
222
  * Batched request logger.
@@ -122,18 +252,6 @@ declare class RequestLogger {
122
252
  private log;
123
253
  }
124
254
 
125
- /**
126
- * Lightweight AI crawler detection.
127
- *
128
- * This is the single source of truth for crawler classification in the SDK.
129
- * The backend (Python analytics API and TypeScript handlers) should eventually
130
- * consume this same list rather than maintaining separate copies.
131
- */
132
-
133
- declare function detectCrawler(userAgent: string): CrawlerInfo;
134
- /** Returns the list of all known crawlers for reference (e.g. for agents.txt generation) */
135
- declare function getKnownCrawlerNames(): string[];
136
-
137
255
  /**
138
256
  * Framework-agnostic query endpoint handler.
139
257
  *
@@ -167,4 +285,4 @@ interface QueryHandlerResponse {
167
285
  }
168
286
  declare function createQueryHandler(client: ApptvtyClient, config: ApptvtyConfig): (req: QueryHandlerRequest) => Promise<QueryHandlerResponse>;
169
287
 
170
- export { AgentErrorResponse, AgentQueryResponse, ApptvtyApiError, ApptvtyClient, ApptvtyConfig, CrawlerBreakdown, CrawlerInfo, DailyStat, ImpressionLog, PageAdsResponse, QueryEndpointDiscovery, RecentActivityItem, RecentQueryItem, RequestLogEntry, RequestLogger, SiteOverviewStats, SiteWalletInfo, createQueryHandler, detectCrawler, getKnownCrawlerNames };
288
+ export { AgentErrorResponse, AgentQueryResponse, ApptvtyApiError, ApptvtyClient, ApptvtyConfig, ApptvtyInsufficientBalanceError, CampaignRecord, CrawlerBreakdown, CrawlerInfo, CreateCampaignParams, DailyStat, ImpressionLog, InsufficientBalanceError, PageAdsResponse, QueryEndpointDiscovery, RecentActivityItem, RecentQueryItem, RequestLogEntry, RequestLogger, type ScraperServiceInfo, SiteOverviewStats, SiteWalletInfo, UpdateCampaignParams, createQueryHandler, detectCrawler, detectScraperService, getKnownCrawlerNames };
package/dist/index.d.ts CHANGED
@@ -1,25 +1,47 @@
1
- import { A as ApptvtyConfig, R as RequestLogEntry, Q as QueryRequest, B as BackendQueryResponse, P as PageAdsResponse, I as ImpressionLog, S as SiteOverviewStats, D as DailyStat, C as CrawlerBreakdown, a as RecentActivityItem, b as RecentQueryItem, c as SiteWalletInfo, d as CrawlerInfo, e as AgentQueryResponse, f as QueryEndpointDiscovery, g as AgentErrorResponse } from './types-C1oUTCsT.js';
2
- export { h as PageAd, i as QuerySource, j as SponsoredAd } from './types-C1oUTCsT.js';
1
+ import { C as CrawlerInfo, A as ApptvtyConfig, R as RequestLogEntry, Q as QueryRequest, B as BackendQueryResponse, P as PageAdsResponse, I as ImpressionLog, S as SiteOverviewStats, D as DailyStat, a as RecentActivityItem, b as RecentQueryItem, c as CrawlerBreakdown, d as SiteWalletInfo, e as CreateCampaignParams, f as CampaignRecord, U as UpdateCampaignParams, g as InsufficientBalanceError, h as AgentQueryResponse, i as QueryEndpointDiscovery, j as AgentErrorResponse } from './types-Zt2qHOrW.js';
2
+ export { k as CampaignStatus, l as PageAd, m as QuerySource, n as SponsoredAd } from './types-Zt2qHOrW.js';
3
3
  export { createNextjsQueryHandler, withApptvty } from './middleware/nextjs.js';
4
4
  export { createExpressMiddleware, createExpressQueryHandler } from './middleware/express.js';
5
5
  import 'next/server';
6
6
  import 'node:http';
7
7
 
8
8
  /**
9
- * HTTP client for the Apptvty API.
9
+ * Lightweight AI crawler detection.
10
10
  *
11
- * Handles:
12
- * - Batch log ingestion (/v1/logs/batch)
13
- * - Query processing (/v1/query) returns answer + optional ad
14
- * - Impression logging (/v1/impressions) — triggers billing for ads
11
+ * This is the single source of truth for crawler classification in the SDK.
12
+ * The backend (Python analytics API and TypeScript handlers) should eventually
13
+ * consume this same list rather than maintaining separate copies.
15
14
  */
16
15
 
16
+ declare function detectCrawler(userAgent: string): CrawlerInfo;
17
+ /** Returns the list of all known crawlers for reference (e.g. for agents.txt generation) */
18
+ declare function getKnownCrawlerNames(): string[];
19
+ interface ScraperServiceInfo {
20
+ isScraperService: boolean;
21
+ /** Identifier of the matched service, or null if not a scraper service. */
22
+ name: string | null;
23
+ }
24
+ /**
25
+ * Detect whether the request comes from a known content-extraction scraper service
26
+ * (Jina, FireCrawl, Cloudflare Browser Rendering, etc.) rather than a direct AI crawler.
27
+ *
28
+ * Use this alongside detectCrawler() — scraper services are a distinct category:
29
+ * they proxy on behalf of an AI consumer but apply their own readability transform.
30
+ */
31
+ declare function detectScraperService(userAgent: string): ScraperServiceInfo;
32
+
17
33
  declare class ApptvtyClient {
18
34
  private readonly baseUrl;
19
35
  private readonly apiKey;
20
36
  private readonly siteId;
21
37
  private readonly debug;
38
+ private x402Token?;
22
39
  constructor(config: ApptvtyConfig);
40
+ /**
41
+ * Set the X402 (LSAT) token for subsequent requests.
42
+ * This is called after the agent has successfully paid an X402 challenge.
43
+ */
44
+ setX402Token(macaroon: string, preimage: string): void;
23
45
  /**
24
46
  * Send a batch of request log entries to the Apptvty ingestion API.
25
47
  * Called by the logger's auto-flush — not called directly by user code.
@@ -61,6 +83,10 @@ declare class ApptvtyClient {
61
83
  days: number;
62
84
  stats: DailyStat[];
63
85
  }>;
86
+ /** Get recent activity logs (last 48h). */
87
+ getRecentActivity(siteId: string, limit?: number, offset?: number): Promise<RecentActivityItem[]>;
88
+ /** Get recent agent queries. */
89
+ getRecentQueries(siteId: string, limit?: number, offset?: number): Promise<RecentQueryItem[]>;
64
90
  /** Get crawler breakdown by type (default 30 days). */
65
91
  getSiteCrawlers(days?: number): Promise<{
66
92
  days: number;
@@ -76,7 +102,87 @@ declare class ApptvtyClient {
76
102
  }>;
77
103
  /** Get wallet balance and earnings. */
78
104
  getSiteWallet(): Promise<SiteWalletInfo>;
105
+ /**
106
+ * Create an ad campaign. The campaign goes live immediately once the wallet
107
+ * has sufficient balance.
108
+ *
109
+ * If the wallet balance is too low, throws `ApptvtyInsufficientBalanceError`
110
+ * which includes deposit instructions so the agent can fund the wallet and retry.
111
+ *
112
+ * @example
113
+ * // Site-based: ad copy derived from your site's crawled content
114
+ * const campaign = await client.createCampaign({
115
+ * name: 'My Kubernetes Blog',
116
+ * advertiser_site_id: 'site_abc123',
117
+ * keywords: ['kubernetes', 'devops', 'containers'],
118
+ * categories: ['technology'],
119
+ * bid_per_view_usdc: 0.001,
120
+ * daily_budget_usdc: 1.0,
121
+ * total_budget_usdc: 20.0,
122
+ * });
123
+ *
124
+ * // Static: manually written ad copy
125
+ * const campaign = await client.createCampaign({
126
+ * name: 'My Blog — Static',
127
+ * ad_text: 'Deep dives on Kubernetes, written by practitioners.',
128
+ * landing_url: 'https://myblog.com',
129
+ * keywords: ['kubernetes', 'devops'],
130
+ * categories: ['technology'],
131
+ * bid_per_view_usdc: 0.001,
132
+ * daily_budget_usdc: 1.0,
133
+ * total_budget_usdc: 10.0,
134
+ * });
135
+ */
136
+ createCampaign(params: CreateCampaignParams): Promise<CampaignRecord>;
137
+ /**
138
+ * List all campaigns for this account.
139
+ * Also returns the schema (valid categories, minimum bid) so the agent
140
+ * knows valid field values without guessing.
141
+ */
142
+ listCampaigns(): Promise<{
143
+ campaigns: CampaignRecord[];
144
+ total: number;
145
+ schema: {
146
+ valid_categories: string[];
147
+ valid_statuses: string[];
148
+ bid_per_view_usdc_minimum: number;
149
+ };
150
+ }>;
151
+ /**
152
+ * Get a single campaign by ID, including current spend and impression count.
153
+ * `budget_remaining_usdc` tells the agent how much budget is left before
154
+ * the campaign auto-pauses.
155
+ */
156
+ getCampaign(campaignId: string): Promise<CampaignRecord>;
157
+ /**
158
+ * Partially update a campaign. Only fields present in `params` are changed.
159
+ *
160
+ * @example
161
+ * // Pause a campaign
162
+ * await client.updateCampaign(id, { status: 'paused' });
163
+ *
164
+ * // Increase bid and daily budget
165
+ * await client.updateCampaign(id, { bid_per_view_usdc: 0.002, daily_budget_usdc: 5.0 });
166
+ */
167
+ updateCampaign(campaignId: string, params: UpdateCampaignParams): Promise<CampaignRecord>;
168
+ /**
169
+ * Pause a campaign immediately. Equivalent to updateCampaign(id, { status: 'paused' }).
170
+ * Campaigns are never deleted — billing history is retained.
171
+ */
172
+ pauseCampaign(campaignId: string): Promise<void>;
173
+ /**
174
+ * Resume a paused campaign.
175
+ */
176
+ resumeCampaign(campaignId: string): Promise<void>;
79
177
  private get;
178
+ private patch;
179
+ private delete;
180
+ /**
181
+ * Settle an X402 challenge from the site's own USDC wallet balance.
182
+ * Called automatically when `post()` receives a 402 with an X402 header.
183
+ * Returns the preimage on success, or throws if the wallet balance is too low.
184
+ */
185
+ private payX402Challenge;
80
186
  private post;
81
187
  private log;
82
188
  private warn;
@@ -87,6 +193,30 @@ declare class ApptvtyApiError extends Error {
87
193
  readonly body: string;
88
194
  constructor(statusCode: number, path: string, body: string);
89
195
  }
196
+ /**
197
+ * Thrown by createCampaign() when the wallet balance is too low.
198
+ *
199
+ * The `details` property contains everything needed to fund the wallet
200
+ * autonomously and retry — the agent can inspect `details.deposit.address`
201
+ * and send USDC on Base without human intervention.
202
+ *
203
+ * @example
204
+ * import { ApptvtyInsufficientBalanceError } from 'apptvty';
205
+ *
206
+ * try {
207
+ * await client.createCampaign(params);
208
+ * } catch (err) {
209
+ * if (err instanceof ApptvtyInsufficientBalanceError) {
210
+ * const { shortfall_usdc, deposit } = err.details;
211
+ * // Agent can now: send shortfall_usdc USDC to deposit.address on deposit.chain
212
+ * // Then retry createCampaign
213
+ * }
214
+ * }
215
+ */
216
+ declare class ApptvtyInsufficientBalanceError extends Error {
217
+ readonly details: InsufficientBalanceError | undefined;
218
+ constructor(details: InsufficientBalanceError | undefined);
219
+ }
90
220
 
91
221
  /**
92
222
  * Batched request logger.
@@ -122,18 +252,6 @@ declare class RequestLogger {
122
252
  private log;
123
253
  }
124
254
 
125
- /**
126
- * Lightweight AI crawler detection.
127
- *
128
- * This is the single source of truth for crawler classification in the SDK.
129
- * The backend (Python analytics API and TypeScript handlers) should eventually
130
- * consume this same list rather than maintaining separate copies.
131
- */
132
-
133
- declare function detectCrawler(userAgent: string): CrawlerInfo;
134
- /** Returns the list of all known crawlers for reference (e.g. for agents.txt generation) */
135
- declare function getKnownCrawlerNames(): string[];
136
-
137
255
  /**
138
256
  * Framework-agnostic query endpoint handler.
139
257
  *
@@ -167,4 +285,4 @@ interface QueryHandlerResponse {
167
285
  }
168
286
  declare function createQueryHandler(client: ApptvtyClient, config: ApptvtyConfig): (req: QueryHandlerRequest) => Promise<QueryHandlerResponse>;
169
287
 
170
- export { AgentErrorResponse, AgentQueryResponse, ApptvtyApiError, ApptvtyClient, ApptvtyConfig, CrawlerBreakdown, CrawlerInfo, DailyStat, ImpressionLog, PageAdsResponse, QueryEndpointDiscovery, RecentActivityItem, RecentQueryItem, RequestLogEntry, RequestLogger, SiteOverviewStats, SiteWalletInfo, createQueryHandler, detectCrawler, getKnownCrawlerNames };
288
+ export { AgentErrorResponse, AgentQueryResponse, ApptvtyApiError, ApptvtyClient, ApptvtyConfig, ApptvtyInsufficientBalanceError, CampaignRecord, CrawlerBreakdown, CrawlerInfo, CreateCampaignParams, DailyStat, ImpressionLog, InsufficientBalanceError, PageAdsResponse, QueryEndpointDiscovery, RecentActivityItem, RecentQueryItem, RequestLogEntry, RequestLogger, type ScraperServiceInfo, SiteOverviewStats, SiteWalletInfo, UpdateCampaignParams, createQueryHandler, detectCrawler, detectScraperService, getKnownCrawlerNames };