@adport/provider-tiktok 0.3.0 → 0.4.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.
package/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ declare class TikTokClient {
20
20
  private readonly fetchImpl;
21
21
  readonly baseUrl: string;
22
22
  constructor(credentials: TikTokCredentials, fetchImpl?: typeof fetch);
23
- get<T>(path: string, params?: Record<string, string | number | unknown[] | object | undefined>): Promise<T>;
23
+ get<T>(path: string, params?: Record<string, unknown>): Promise<T>;
24
24
  post<T>(path: string, body: Record<string, unknown>): Promise<T>;
25
25
  private send;
26
26
  }
@@ -58,10 +58,18 @@ declare class TikTokAdsProvider implements AdProvider {
58
58
  end_date: string;
59
59
  page_size?: number;
60
60
  }): Promise<Array<Record<string, unknown>>>;
61
+ apiRead(input: {
62
+ account_id: string;
63
+ path: string;
64
+ params?: Record<string, unknown>;
65
+ }): Promise<unknown>;
61
66
  listCampaigns(accountId: string, campaignIds?: string[]): Promise<TikTokCampaign[]>;
62
67
  previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview>;
63
68
  applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult>;
64
69
  private plan;
70
+ private planApiCreate;
71
+ private planApiUpdate;
72
+ private planApiDelete;
65
73
  private planCreateCampaign;
66
74
  private planSetStatus;
67
75
  private planSetBudget;
package/dist/index.js CHANGED
@@ -194,6 +194,10 @@ var TikTokAdsProvider = class {
194
194
  });
195
195
  return data.list ?? [];
196
196
  }
197
+ async apiRead(input) {
198
+ const path = validateTikTokPath(input.path);
199
+ return this.client.get(path, { ...input.params, advertiser_id: input.account_id });
200
+ }
197
201
  async listCampaigns(accountId, campaignIds) {
198
202
  const data = await this.client.get("campaign/get", {
199
203
  advertiser_id: accountId,
@@ -227,10 +231,61 @@ var TikTokAdsProvider = class {
227
231
  return this.planSetStatus(op.accountId, payload);
228
232
  case "tiktok_set_budget":
229
233
  return this.planSetBudget(op.accountId, payload);
234
+ case "tiktok_api_create":
235
+ return this.planApiCreate(op.accountId, payload, guard);
236
+ case "tiktok_api_update":
237
+ return this.planApiUpdate(op.accountId, payload);
238
+ case "tiktok_api_delete":
239
+ return this.planApiDelete(op.accountId, payload);
230
240
  default:
231
241
  throw new AdportError2("PROVIDER_ERROR", `tiktok: unsupported write tool ${op.tool}`);
232
242
  }
233
243
  }
244
+ async planApiCreate(accountId, payload, guard) {
245
+ const path = validateTikTokMutationPath(payload.path, "create");
246
+ const body = structuredClone(payload.body);
247
+ body.advertiser_id = accountId;
248
+ const coercions = [];
249
+ if (guard.forcePausedCreation) {
250
+ coerceTikTokCreatedStatuses(body, coercions);
251
+ if (path === "campaign/create" && body.operation_status !== "DISABLE") {
252
+ body.operation_status = "DISABLE";
253
+ coercions.push("operation_status set to DISABLE by policy (paused_creation)");
254
+ }
255
+ }
256
+ return {
257
+ summary: `Create TikTok resource via /${path}`,
258
+ changes: [`+ POST /${path} ${JSON.stringify(body)}`],
259
+ coercions,
260
+ budgetDeltas: collectTikTokBudgets(body),
261
+ execute: async () => extractTikTokIds(await this.client.post(path, body))
262
+ };
263
+ }
264
+ async planApiUpdate(accountId, payload) {
265
+ const path = validateTikTokMutationPath(payload.path, "update");
266
+ if (containsTikTokBudget(payload.body)) {
267
+ throw new AdportError2("INVALID_INPUT", "tiktok: budget updates require a typed budget tool for policy checks");
268
+ }
269
+ const body = { ...payload.body, advertiser_id: accountId };
270
+ return {
271
+ summary: `Update TikTok resource via /${path}`,
272
+ changes: [`~ POST /${path} ${JSON.stringify(body)}`],
273
+ coercions: [],
274
+ budgetDeltas: [],
275
+ execute: async () => extractTikTokIds(await this.client.post(path, body))
276
+ };
277
+ }
278
+ async planApiDelete(accountId, payload) {
279
+ const path = validateTikTokMutationPath(payload.path, "delete");
280
+ const body = { ...payload.body, advertiser_id: accountId };
281
+ return {
282
+ summary: `Permanently delete TikTok resource via /${path}`,
283
+ changes: [`- POST /${path} ${JSON.stringify(body)}`],
284
+ coercions: [],
285
+ budgetDeltas: [],
286
+ execute: async () => extractTikTokIds(await this.client.post(path, body))
287
+ };
288
+ }
234
289
  async planCreateCampaign(accountId, payload, guard) {
235
290
  const coercions = [];
236
291
  let operationStatus = payload.operation_status ?? "ENABLE";
@@ -332,6 +387,74 @@ var TikTokAdsProvider = class {
332
387
  function round2(n) {
333
388
  return Math.round(n * 100) / 100;
334
389
  }
390
+ function validateTikTokPath(path) {
391
+ const normalized = path.replace(/^\/+|\/+$/g, "").trim();
392
+ if (!/^[a-z0-9_/-]{3,160}$/.test(normalized) || normalized.includes("..") || normalized.includes("//")) {
393
+ throw new AdportError2("INVALID_INPUT", `tiktok: invalid Business API path "${path}"`);
394
+ }
395
+ return normalized;
396
+ }
397
+ function validateTikTokMutationPath(path, kind) {
398
+ const normalized = validateTikTokPath(path);
399
+ if (!normalized.endsWith(`/${kind}`)) {
400
+ throw new AdportError2("INVALID_INPUT", `tiktok: ${kind} tool requires an endpoint ending in /${kind}`);
401
+ }
402
+ return normalized;
403
+ }
404
+ function isRecord(value) {
405
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
406
+ }
407
+ function coerceTikTokCreatedStatuses(value, coercions, path = "body") {
408
+ if (Array.isArray(value)) return value.forEach((item, index) => coerceTikTokCreatedStatuses(item, coercions, `${path}[${index}]`));
409
+ if (!isRecord(value)) return;
410
+ for (const [key, child] of Object.entries(value)) {
411
+ if (key === "operation_status" && child === "ENABLE") {
412
+ value[key] = "DISABLE";
413
+ coercions.push(`${path}.${key} coerced to DISABLE by policy (paused_creation)`);
414
+ } else {
415
+ coerceTikTokCreatedStatuses(child, coercions, `${path}.${key}`);
416
+ }
417
+ }
418
+ }
419
+ function containsTikTokBudget(value) {
420
+ if (Array.isArray(value)) return value.some(containsTikTokBudget);
421
+ if (!isRecord(value)) return false;
422
+ return Object.entries(value).some(([key, child]) => /budget/i.test(key) || containsTikTokBudget(child));
423
+ }
424
+ function collectTikTokBudgets(value, path = "body") {
425
+ const deltas = [];
426
+ if (Array.isArray(value)) {
427
+ value.forEach((item, index) => deltas.push(...collectTikTokBudgets(item, `${path}[${index}]`)));
428
+ return deltas;
429
+ }
430
+ if (!isRecord(value)) return deltas;
431
+ for (const [key, child] of Object.entries(value)) {
432
+ const childPath = `${path}.${key}`;
433
+ if (/budget/i.test(key)) {
434
+ const amount = Number(child);
435
+ if (Number.isFinite(amount) && amount > 0) {
436
+ deltas.push({ target: childPath, toMicros: Math.round(amount * UNITS_TO_MICROS) });
437
+ }
438
+ }
439
+ deltas.push(...collectTikTokBudgets(child, childPath));
440
+ }
441
+ return deltas;
442
+ }
443
+ function extractTikTokIds(value) {
444
+ const ids = [];
445
+ const visit = (item) => {
446
+ if (Array.isArray(item)) return item.forEach(visit);
447
+ if (!isRecord(item)) return;
448
+ for (const [key, child] of Object.entries(item)) {
449
+ if (/(?:^|_)ids?$/.test(key)) {
450
+ if (Array.isArray(child)) child.forEach((id) => ids.push(String(id)));
451
+ else if (typeof child === "string" || typeof child === "number") ids.push(String(child));
452
+ } else visit(child);
453
+ }
454
+ };
455
+ visit(value);
456
+ return [...new Set(ids)];
457
+ }
335
458
 
336
459
  // src/tools.ts
337
460
  import { defineTool, guardedWriteTool } from "@adport/core";
@@ -349,6 +472,20 @@ var OBJECTIVES = [
349
472
  var operationStatusSchema = z.enum(["ENABLE", "DISABLE"]);
350
473
  function tiktokTools(provider) {
351
474
  return [
475
+ defineTool({
476
+ name: "tiktok_api_read",
477
+ namespace: "tiktok",
478
+ description: "Call any documented TikTok Business API v1.3 GET endpoint with advertiser scoping and API-shaped parameters.",
479
+ input: z.object({
480
+ account_id: z.string(),
481
+ path: z.string().min(3).describe("Relative endpoint, e.g. adgroup/get, ad/get, creative/report/get"),
482
+ params: z.record(z.string(), z.unknown()).optional()
483
+ }),
484
+ annotations: { readOnly: true },
485
+ async handler(input) {
486
+ return provider.apiRead(input);
487
+ }
488
+ }),
352
489
  defineTool({
353
490
  name: "tiktok_campaigns",
354
491
  namespace: "tiktok",
@@ -417,6 +554,31 @@ function tiktokTools(provider) {
417
554
  campaign_id: z.string(),
418
555
  budget: z.number().positive()
419
556
  })
557
+ }),
558
+ guardedWriteTool({
559
+ name: "tiktok_api_create",
560
+ namespace: "tiktok",
561
+ description: "Create through any documented TikTok Business API v1.3 /create endpoint. The advertiser is forced to account_id, campaign creation is paused, and budget fields are policy-checked.",
562
+ provider: "tiktok",
563
+ kind: "create",
564
+ payload: z.object({ path: z.string().min(3), body: z.record(z.string(), z.unknown()) })
565
+ }),
566
+ guardedWriteTool({
567
+ name: "tiktok_api_update",
568
+ namespace: "tiktok",
569
+ description: "Update non-budget fields through any documented TikTok Business API v1.3 /update endpoint.",
570
+ provider: "tiktok",
571
+ kind: "update",
572
+ payload: z.object({ path: z.string().min(3), body: z.record(z.string(), z.unknown()) })
573
+ }),
574
+ guardedWriteTool({
575
+ name: "tiktok_api_delete",
576
+ namespace: "tiktok",
577
+ description: "Permanently delete through any documented TikTok Business API v1.3 /delete endpoint.",
578
+ provider: "tiktok",
579
+ kind: "remove",
580
+ destructive: true,
581
+ payload: z.object({ path: z.string().min(3), body: z.record(z.string(), z.unknown()) })
420
582
  })
421
583
  ];
422
584
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/provider.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\n\nexport interface TikTokCredentials {\n /** Long-term access token (does not expire; revoked by the advertiser). */\n accessToken: string;\n appId?: string;\n secret?: string;\n /** Use the sandbox environment (sandbox-ads.tiktok.com). */\n sandbox?: boolean;\n}\n\nexport const TIKTOK_API_VERSION = 'v1.3';\nconst PROD_BASE = 'https://business-api.tiktok.com/open_api';\nconst SANDBOX_BASE = 'https://sandbox-ads.tiktok.com/open_api';\n\n/** Business-error codes worth a targeted hint. */\nconst CODE_HINTS: Record<number, string> = {\n 40105: 'The access token is invalid — re-run `adport connect tiktok`.',\n 40102: 'The access token expired or was revoked — re-run `adport connect tiktok`.',\n 40100: 'Rate limited (app level) — back off and retry.',\n 40133: 'Rate limited (advertiser level) — back off and retry.',\n};\n\n/**\n * TikTok Business API client. Peculiarities handled here:\n * - every endpoint needs a TRAILING SLASH (404 otherwise)\n * - business errors arrive as HTTP 200 with a non-zero envelope `code`\n * - array query params are JSON-encoded strings\n */\nexport class TikTokClient {\n readonly baseUrl: string;\n\n constructor(\n private readonly credentials: TikTokCredentials,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {\n this.baseUrl = `${credentials.sandbox ? SANDBOX_BASE : PROD_BASE}/${TIKTOK_API_VERSION}`;\n }\n\n async get<T>(path: string, params: Record<string, string | number | unknown[] | object | undefined> = {}): Promise<T> {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n search.set(key, typeof value === 'object' ? JSON.stringify(value) : String(value));\n }\n const query = search.size > 0 ? `?${search}` : '';\n return this.send<T>(`${path}${query}`, { method: 'GET' });\n }\n\n async post<T>(path: string, body: Record<string, unknown>): Promise<T> {\n return this.send<T>(path, { method: 'POST', body: JSON.stringify(body) });\n }\n\n private async send<T>(pathWithQuery: string, init: { method: string; body?: string }): Promise<T> {\n // Trailing slash is mandatory before the query string.\n const [path, query] = pathWithQuery.split('?');\n const url = `${this.baseUrl}/${path!.replace(/^\\/|\\/$/g, '')}/${query ? `?${query}` : ''}`;\n const response = await this.fetchImpl(url, {\n method: init.method,\n headers: {\n 'Access-Token': this.credentials.accessToken,\n ...(init.body ? { 'content-type': 'application/json' } : {}),\n },\n body: init.body,\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', `TikTok API HTTP ${response.status} for ${path} (check method and trailing slash)`, raw);\n }\n const envelope = JSON.parse(raw) as { code: number; message: string; request_id?: string; data?: T };\n if (envelope.code !== 0) {\n const hint = CODE_HINTS[envelope.code];\n throw new AdportError(\n 'PROVIDER_ERROR',\n `TikTok API error ${envelope.code}: ${envelope.message}${hint ? `\\n ${hint}` : ''}` +\n (envelope.request_id ? ` [request_id: ${envelope.request_id}]` : ''),\n envelope,\n );\n }\n return (envelope.data ?? {}) as T;\n }\n}\n","import {\n AdportError,\n resolveDateRange,\n type Account,\n type AdProvider,\n type MetricName,\n type NormalizedQuery,\n type ProviderCapabilities,\n type Report,\n type ReportRow,\n type StandardActions,\n type WriteGuard,\n type WriteOperation,\n type WritePreview,\n type WriteResult,\n} from '@adport/core';\nimport { TikTokClient } from './client.js';\n\n/** TikTok budgets are floats in whole account-currency units. */\nexport const UNITS_TO_MICROS = 1_000_000;\n\nconst DATA_LEVEL = {\n account: 'AUCTION_ADVERTISER',\n campaign: 'AUCTION_CAMPAIGN',\n ad_group: 'AUCTION_ADGROUP',\n ad: 'AUCTION_AD',\n} as const;\n\nconst ID_DIMENSION = {\n account: 'advertiser_id',\n campaign: 'campaign_id',\n ad_group: 'adgroup_id',\n ad: 'ad_id',\n} as const;\n\ninterface TikTokCampaign {\n campaign_id: string;\n campaign_name: string;\n operation_status: 'ENABLE' | 'DISABLE';\n secondary_status?: string;\n budget: number;\n budget_mode: string;\n objective_type: string;\n}\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: () => Promise<string[]>;\n}\n\nexport class TikTokAdsProvider implements AdProvider {\n readonly id = 'tiktok';\n\n constructor(\n private readonly client: TikTokClient,\n private readonly app: { appId: string; secret: string },\n ) {}\n\n capabilities(): ProviderCapabilities {\n // No server-side dry run in the Marketing API — previews are client-side diffs.\n return { serverDryRun: false };\n }\n\n standardActions(): StandardActions {\n return {\n pauseCampaign: (accountId, campaignId) => ({\n tool: 'tiktok_set_campaign_status',\n input: { account_id: accountId, campaign_ids: [campaignId], operation_status: 'DISABLE' },\n }),\n };\n }\n\n async listAccounts(): Promise<Account[]> {\n const authorized = await this.client.get<{ list?: Array<{ advertiser_id: string; advertiser_name: string }> }>(\n 'oauth2/advertiser/get',\n { app_id: this.appId(), secret: this.secret() },\n );\n const ids = (authorized.list ?? []).map((a) => a.advertiser_id);\n if (ids.length === 0) return [];\n const info = await this.client.get<{\n list?: Array<{ advertiser_id: string; name?: string; currency?: string; status?: string; timezone?: string }>;\n }>('advertiser/info', { advertiser_ids: ids.slice(0, 100), fields: ['name', 'currency', 'status', 'timezone'] });\n const byId = new Map((info.list ?? []).map((i) => [i.advertiser_id, i]));\n return ids.map((id) => {\n const details = byId.get(id);\n return {\n provider: this.id,\n id,\n name: details?.name ?? (authorized.list ?? []).find((a) => a.advertiser_id === id)?.advertiser_name ?? id,\n currency: details?.currency,\n status: details?.status?.replace('STATUS_', ''),\n };\n });\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n const range = resolveDateRange(query.dateRange);\n const accountIds = query.accountIds ?? (await this.listAccounts()).map((a) => a.id);\n const level = DATA_LEVEL[query.level];\n const idDim = ID_DIMENSION[query.level];\n const nameMetric = { campaign: 'campaign_name', ad_group: 'adgroup_name', ad: 'ad_name' }[\n query.level as string\n ];\n\n const rows: ReportRow[] = [];\n for (const accountId of accountIds) {\n const data = await this.client.get<{\n list?: Array<{ dimensions: Record<string, string>; metrics: Record<string, string> }>;\n page_info?: { total_number?: number };\n }>('report/integrated/get', {\n advertiser_id: accountId,\n report_type: 'BASIC',\n data_level: level,\n dimensions: [idDim],\n metrics: [\n ...(nameMetric ? [nameMetric] : []),\n 'spend',\n 'impressions',\n 'clicks',\n 'conversion',\n 'complete_payment_roas',\n 'total_complete_payment_rate',\n ],\n start_date: range.start,\n end_date: range.end,\n page: 1,\n page_size: Math.min(query.limit ?? 200, 1000),\n });\n for (const row of data.list ?? []) {\n rows.push(this.toReportRow(row, accountId, query, idDim, nameMetric));\n }\n }\n return { rows };\n }\n\n private toReportRow(\n row: { dimensions: Record<string, string>; metrics: Record<string, string> },\n accountId: string,\n query: NormalizedQuery,\n idDim: string,\n nameMetric?: string,\n ): ReportRow {\n // All TikTok metric values are strings (\"14.68\"); spend is whole currency units.\n const num = (key: string) => Number(row.metrics[key] ?? 0);\n const spend = num('spend');\n const impressions = num('impressions');\n const clicks = num('clicks');\n const conversions = num('conversion');\n // total_complete_payment_rate is (despite the name) total purchase VALUE.\n const conversionValue = num('total_complete_payment_rate');\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: round2(conversionValue),\n ctr: impressions > 0 ? round2((clicks / impressions) * 100) : 0,\n cpc: clicks > 0 ? round2(spend / clicks) : 0,\n cpm: impressions > 0 ? round2((spend / impressions) * 1000) : 0,\n cpa: conversions > 0 ? round2(spend / conversions) : 0,\n roas: spend > 0 ? round2(conversionValue / spend) : 0,\n };\n const entityId = row.dimensions[idDim] ?? '';\n return {\n provider: this.id,\n accountId,\n entity: {\n level: query.level,\n id: entityId,\n name: (nameMetric ? row.metrics[nameMetric] : undefined) ?? entityId,\n },\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n };\n }\n\n async rawReport(input: {\n account_id: string;\n data_level: string;\n dimensions: string[];\n metrics: string[];\n start_date: string;\n end_date: string;\n page_size?: number;\n }): Promise<Array<Record<string, unknown>>> {\n const data = await this.client.get<{ list?: Array<Record<string, unknown>> }>('report/integrated/get', {\n advertiser_id: input.account_id,\n report_type: 'BASIC',\n data_level: input.data_level,\n dimensions: input.dimensions,\n metrics: input.metrics,\n start_date: input.start_date,\n end_date: input.end_date,\n page: 1,\n page_size: Math.min(input.page_size ?? 200, 1000),\n });\n return data.list ?? [];\n }\n\n async listCampaigns(accountId: string, campaignIds?: string[]): Promise<TikTokCampaign[]> {\n const data = await this.client.get<{ list?: TikTokCampaign[] }>('campaign/get', {\n advertiser_id: accountId,\n ...(campaignIds ? { filtering: { campaign_ids: campaignIds } } : {}),\n page: 1,\n page_size: campaignIds ? campaignIds.length : 100,\n });\n return data.list ?? [];\n }\n\n async previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview> {\n const plan = await this.plan(op, guard);\n // No validate_only equivalent — the preview is a client-side diff from live lookups.\n return {\n summary: plan.summary,\n changes: plan.changes,\n coercions: plan.coercions,\n budgetDeltas: plan.budgetDeltas,\n serverValidated: false,\n };\n }\n\n async applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult> {\n const plan = await this.plan(op, guard);\n const resourceIds = await plan.execute();\n return { applied: true, resourceIds };\n }\n\n private async plan(op: WriteOperation, guard: WriteGuard): Promise<WritePlan> {\n const payload = op.payload as never;\n switch (op.tool) {\n case 'tiktok_create_campaign':\n return this.planCreateCampaign(op.accountId, payload, guard);\n case 'tiktok_set_campaign_status':\n return this.planSetStatus(op.accountId, payload);\n case 'tiktok_set_budget':\n return this.planSetBudget(op.accountId, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `tiktok: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planCreateCampaign(\n accountId: string,\n payload: { campaign_name: string; objective_type: string; budget_mode: string; budget?: number; operation_status?: string },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n // TikTok creates campaigns ENABLED by default; DISABLE is its \"paused\".\n let operationStatus = payload.operation_status ?? 'ENABLE';\n if (guard.forcePausedCreation && operationStatus === 'ENABLE') {\n operationStatus = 'DISABLE';\n coercions.push('operation_status coerced to DISABLE (paused) by policy (paused_creation)');\n }\n if (payload.budget_mode !== 'BUDGET_MODE_INFINITE' && !payload.budget) {\n throw new AdportError('INVALID_INPUT', `tiktok: budget is required for ${payload.budget_mode}`);\n }\n const budgetDeltas: WritePreview['budgetDeltas'] = [];\n if (payload.budget) {\n budgetDeltas.push({\n target: `new campaign \"${payload.campaign_name}\" ${payload.budget_mode === 'BUDGET_MODE_DAY' ? 'daily' : 'total'} budget`,\n toMicros: payload.budget * UNITS_TO_MICROS,\n });\n }\n return {\n summary: `Create TikTok campaign \"${payload.campaign_name}\" (${payload.objective_type}, ${operationStatus === 'DISABLE' ? 'paused' : 'active'})`,\n changes: [\n `+ campaign \"${payload.campaign_name}\" objective=${payload.objective_type} ${payload.budget_mode}` +\n (payload.budget ? ` budget=${payload.budget}` : '') +\n ` operation_status=${operationStatus}`,\n ],\n coercions,\n budgetDeltas,\n execute: async () => {\n const data = await this.client.post<{ campaign_id?: string }>('campaign/create', {\n advertiser_id: accountId,\n campaign_name: payload.campaign_name,\n objective_type: payload.objective_type,\n budget_mode: payload.budget_mode,\n ...(payload.budget ? { budget: payload.budget } : {}),\n operation_status: operationStatus,\n });\n return data.campaign_id ? [data.campaign_id] : [];\n },\n };\n }\n\n private async planSetStatus(\n accountId: string,\n payload: { campaign_ids: string[]; operation_status: 'ENABLE' | 'DISABLE' },\n ): Promise<WritePlan> {\n const campaigns = await this.listCampaigns(accountId, payload.campaign_ids);\n const changes = payload.campaign_ids.map((id) => {\n const campaign = campaigns.find((c) => c.campaign_id === id);\n return `~ campaign ${id} (\"${campaign?.campaign_name ?? '?'}\") operation_status ${campaign?.operation_status ?? '?'} → ${payload.operation_status}`;\n });\n return {\n summary: `Set ${payload.campaign_ids.length} TikTok campaign(s) → ${payload.operation_status === 'DISABLE' ? 'paused' : 'active'}`,\n changes,\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n await this.client.post('campaign/status/update', {\n advertiser_id: accountId,\n campaign_ids: payload.campaign_ids,\n operation_status: payload.operation_status,\n });\n return payload.campaign_ids;\n },\n };\n }\n\n private async planSetBudget(\n accountId: string,\n payload: { campaign_id: string; budget: number },\n ): Promise<WritePlan> {\n const campaigns = await this.listCampaigns(accountId, [payload.campaign_id]);\n const campaign = campaigns[0];\n if (!campaign) {\n throw new AdportError('PROVIDER_ERROR', `tiktok: campaign ${payload.campaign_id} not found in advertiser ${accountId}`);\n }\n if (campaign.budget_mode === 'BUDGET_MODE_INFINITE') {\n throw new AdportError('PROVIDER_ERROR', `tiktok: campaign \"${campaign.campaign_name}\" has no budget (BUDGET_MODE_INFINITE)`);\n }\n return {\n summary: `Change TikTok campaign \"${campaign.campaign_name}\" budget ${campaign.budget} → ${payload.budget} (${campaign.budget_mode})`,\n changes: [\n `~ campaign ${payload.campaign_id} budget ${campaign.budget} → ${payload.budget}`,\n '! TikTok requires the new budget to be ≥105% of already-spent amount; the API rejects violations',\n ],\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${campaign.campaign_name}\" ${campaign.budget_mode === 'BUDGET_MODE_DAY' ? 'daily' : 'total'} budget`,\n fromMicros: campaign.budget * UNITS_TO_MICROS,\n toMicros: payload.budget * UNITS_TO_MICROS,\n },\n ],\n execute: async () => {\n await this.client.post('campaign/update', {\n advertiser_id: accountId,\n campaign_id: payload.campaign_id,\n budget: payload.budget,\n });\n return [payload.campaign_id];\n },\n };\n }\n\n private appId(): string {\n return this.app.appId;\n }\n\n private secret(): string {\n return this.app.secret;\n }\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { TikTokAdsProvider } from './provider.js';\n\nconst OBJECTIVES = [\n 'REACH',\n 'TRAFFIC',\n 'VIDEO_VIEWS',\n 'LEAD_GENERATION',\n 'ENGAGEMENT',\n 'APP_PROMOTION',\n 'WEB_CONVERSIONS',\n 'PRODUCT_SALES',\n] as const;\n\nconst operationStatusSchema = z.enum(['ENABLE', 'DISABLE']);\n\nexport function tiktokTools(provider: TikTokAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'tiktok_campaigns',\n namespace: 'tiktok',\n description: 'List TikTok campaigns (id, name, operation_status ENABLE/DISABLE, budget, budget_mode, objective).',\n input: z.object({\n account_id: z.string().describe('Advertiser id'),\n campaign_ids: z.array(z.string()).optional(),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const campaigns = await provider.listCampaigns(input.account_id, input.campaign_ids);\n return { campaigns };\n },\n }),\n defineTool({\n name: 'tiktok_report',\n namespace: 'tiktok',\n description:\n 'Raw TikTok synchronous reporting (report/integrated). Metric values come back as strings; ' +\n 'note total_complete_payment_rate is total purchase VALUE (misleading name), complete_payment_roas is purchase ROAS.',\n input: z.object({\n account_id: z.string(),\n data_level: z.enum(['AUCTION_ADVERTISER', 'AUCTION_CAMPAIGN', 'AUCTION_ADGROUP', 'AUCTION_AD']).default('AUCTION_CAMPAIGN'),\n dimensions: z.array(z.string()).min(1).default(['campaign_id']),\n metrics: z.array(z.string()).min(1).default(['spend', 'impressions', 'clicks', 'conversion']),\n start_date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n end_date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n page_size: z.number().int().positive().max(1000).default(200),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const rows = await provider.rawReport(input);\n return { rows, row_count: rows.length };\n },\n }),\n guardedWriteTool({\n name: 'tiktok_create_campaign',\n namespace: 'tiktok',\n description:\n 'Create a TikTok campaign. Budget is a float in whole account-currency units (min ~20 USD-equivalent for ' +\n 'DAY/TOTAL modes). TikTok has no server-side dry run — the preview is a client-side diff.',\n provider: 'tiktok',\n kind: 'create',\n payload: z.object({\n campaign_name: z.string().min(1).max(512),\n objective_type: z.enum(OBJECTIVES).default('TRAFFIC'),\n budget_mode: z.enum(['BUDGET_MODE_DAY', 'BUDGET_MODE_TOTAL', 'BUDGET_MODE_INFINITE']).default('BUDGET_MODE_DAY'),\n budget: z.number().positive().optional().describe('Whole currency units (float), required unless BUDGET_MODE_INFINITE'),\n operation_status: operationStatusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'tiktok_set_campaign_status',\n namespace: 'tiktok',\n description: 'Enable or pause (DISABLE) up to 20 TikTok campaigns.',\n provider: 'tiktok',\n kind: 'update',\n payload: z.object({\n campaign_ids: z.array(z.string()).min(1).max(20),\n operation_status: operationStatusSchema,\n }),\n }),\n guardedWriteTool({\n name: 'tiktok_set_budget',\n namespace: 'tiktok',\n description:\n \"Change a TikTok campaign's budget (whole currency units). TikTok rejects new budgets below 105% of current spend.\",\n provider: 'tiktok',\n kind: 'update',\n payload: z.object({\n campaign_id: z.string(),\n budget: z.number().positive(),\n }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { TikTokClient, type TikTokCredentials } from './client.js';\nimport { TikTokAdsProvider } from './provider.js';\nimport { tiktokTools } from './tools.js';\n\nexport { TikTokClient, TIKTOK_API_VERSION, type TikTokCredentials } from './client.js';\nexport { TikTokAdsProvider, UNITS_TO_MICROS } from './provider.js';\nexport { tiktokTools } from './tools.js';\n\nexport async function resolveTikTokCredentials(\n store: CredentialStore,\n): Promise<(TikTokCredentials & { appId: string; secret: string }) | undefined> {\n const record = await store.get('tiktok');\n if (record?.data.access_token && record.data.app_id && record.data.secret) {\n return {\n accessToken: record.data.access_token,\n appId: record.data.app_id,\n secret: record.data.secret,\n sandbox: record.data.sandbox === 'true',\n };\n }\n const env = process.env;\n if (env.TIKTOK_ACCESS_TOKEN && env.TIKTOK_APP_ID && env.TIKTOK_APP_SECRET) {\n return {\n accessToken: env.TIKTOK_ACCESS_TOKEN,\n appId: env.TIKTOK_APP_ID,\n secret: env.TIKTOK_APP_SECRET,\n sandbox: env.TIKTOK_SANDBOX === 'true',\n };\n }\n return undefined;\n}\n\nexport async function createTikTokModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveTikTokCredentials(store);\n if (!credentials) return undefined;\n const provider = new TikTokAdsProvider(new TikTokClient(credentials), {\n appId: credentials.appId,\n secret: credentials.secret,\n });\n return { provider, tools: tiktokTools(provider) };\n}\n"],"mappings":"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","names":["AdportError"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/provider.ts","../src/tools.ts","../src/index.ts"],"sourcesContent":["import { AdportError } from '@adport/core';\n\nexport interface TikTokCredentials {\n /** Long-term access token (does not expire; revoked by the advertiser). */\n accessToken: string;\n appId?: string;\n secret?: string;\n /** Use the sandbox environment (sandbox-ads.tiktok.com). */\n sandbox?: boolean;\n}\n\nexport const TIKTOK_API_VERSION = 'v1.3';\nconst PROD_BASE = 'https://business-api.tiktok.com/open_api';\nconst SANDBOX_BASE = 'https://sandbox-ads.tiktok.com/open_api';\n\n/** Business-error codes worth a targeted hint. */\nconst CODE_HINTS: Record<number, string> = {\n 40105: 'The access token is invalid — re-run `adport connect tiktok`.',\n 40102: 'The access token expired or was revoked — re-run `adport connect tiktok`.',\n 40100: 'Rate limited (app level) — back off and retry.',\n 40133: 'Rate limited (advertiser level) — back off and retry.',\n};\n\n/**\n * TikTok Business API client. Peculiarities handled here:\n * - every endpoint needs a TRAILING SLASH (404 otherwise)\n * - business errors arrive as HTTP 200 with a non-zero envelope `code`\n * - array query params are JSON-encoded strings\n */\nexport class TikTokClient {\n readonly baseUrl: string;\n\n constructor(\n private readonly credentials: TikTokCredentials,\n private readonly fetchImpl: typeof fetch = fetch,\n ) {\n this.baseUrl = `${credentials.sandbox ? SANDBOX_BASE : PROD_BASE}/${TIKTOK_API_VERSION}`;\n }\n\n async get<T>(path: string, params: Record<string, unknown> = {}): Promise<T> {\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined) continue;\n search.set(key, typeof value === 'object' ? JSON.stringify(value) : String(value));\n }\n const query = search.size > 0 ? `?${search}` : '';\n return this.send<T>(`${path}${query}`, { method: 'GET' });\n }\n\n async post<T>(path: string, body: Record<string, unknown>): Promise<T> {\n return this.send<T>(path, { method: 'POST', body: JSON.stringify(body) });\n }\n\n private async send<T>(pathWithQuery: string, init: { method: string; body?: string }): Promise<T> {\n // Trailing slash is mandatory before the query string.\n const [path, query] = pathWithQuery.split('?');\n const url = `${this.baseUrl}/${path!.replace(/^\\/|\\/$/g, '')}/${query ? `?${query}` : ''}`;\n const response = await this.fetchImpl(url, {\n method: init.method,\n headers: {\n 'Access-Token': this.credentials.accessToken,\n ...(init.body ? { 'content-type': 'application/json' } : {}),\n },\n body: init.body,\n });\n const raw = await response.text();\n if (!response.ok) {\n throw new AdportError('PROVIDER_ERROR', `TikTok API HTTP ${response.status} for ${path} (check method and trailing slash)`, raw);\n }\n const envelope = JSON.parse(raw) as { code: number; message: string; request_id?: string; data?: T };\n if (envelope.code !== 0) {\n const hint = CODE_HINTS[envelope.code];\n throw new AdportError(\n 'PROVIDER_ERROR',\n `TikTok API error ${envelope.code}: ${envelope.message}${hint ? `\\n ${hint}` : ''}` +\n (envelope.request_id ? ` [request_id: ${envelope.request_id}]` : ''),\n envelope,\n );\n }\n return (envelope.data ?? {}) as T;\n }\n}\n","import {\n AdportError,\n resolveDateRange,\n type Account,\n type AdProvider,\n type MetricName,\n type NormalizedQuery,\n type ProviderCapabilities,\n type Report,\n type ReportRow,\n type StandardActions,\n type WriteGuard,\n type WriteOperation,\n type WritePreview,\n type WriteResult,\n} from '@adport/core';\nimport { TikTokClient } from './client.js';\n\n/** TikTok budgets are floats in whole account-currency units. */\nexport const UNITS_TO_MICROS = 1_000_000;\n\nconst DATA_LEVEL = {\n account: 'AUCTION_ADVERTISER',\n campaign: 'AUCTION_CAMPAIGN',\n ad_group: 'AUCTION_ADGROUP',\n ad: 'AUCTION_AD',\n} as const;\n\nconst ID_DIMENSION = {\n account: 'advertiser_id',\n campaign: 'campaign_id',\n ad_group: 'adgroup_id',\n ad: 'ad_id',\n} as const;\n\ninterface TikTokCampaign {\n campaign_id: string;\n campaign_name: string;\n operation_status: 'ENABLE' | 'DISABLE';\n secondary_status?: string;\n budget: number;\n budget_mode: string;\n objective_type: string;\n}\n\ninterface WritePlan {\n summary: string;\n changes: string[];\n coercions: string[];\n budgetDeltas: WritePreview['budgetDeltas'];\n execute: () => Promise<string[]>;\n}\n\nexport class TikTokAdsProvider implements AdProvider {\n readonly id = 'tiktok';\n\n constructor(\n private readonly client: TikTokClient,\n private readonly app: { appId: string; secret: string },\n ) {}\n\n capabilities(): ProviderCapabilities {\n // No server-side dry run in the Marketing API — previews are client-side diffs.\n return { serverDryRun: false };\n }\n\n standardActions(): StandardActions {\n return {\n pauseCampaign: (accountId, campaignId) => ({\n tool: 'tiktok_set_campaign_status',\n input: { account_id: accountId, campaign_ids: [campaignId], operation_status: 'DISABLE' },\n }),\n };\n }\n\n async listAccounts(): Promise<Account[]> {\n const authorized = await this.client.get<{ list?: Array<{ advertiser_id: string; advertiser_name: string }> }>(\n 'oauth2/advertiser/get',\n { app_id: this.appId(), secret: this.secret() },\n );\n const ids = (authorized.list ?? []).map((a) => a.advertiser_id);\n if (ids.length === 0) return [];\n const info = await this.client.get<{\n list?: Array<{ advertiser_id: string; name?: string; currency?: string; status?: string; timezone?: string }>;\n }>('advertiser/info', { advertiser_ids: ids.slice(0, 100), fields: ['name', 'currency', 'status', 'timezone'] });\n const byId = new Map((info.list ?? []).map((i) => [i.advertiser_id, i]));\n return ids.map((id) => {\n const details = byId.get(id);\n return {\n provider: this.id,\n id,\n name: details?.name ?? (authorized.list ?? []).find((a) => a.advertiser_id === id)?.advertiser_name ?? id,\n currency: details?.currency,\n status: details?.status?.replace('STATUS_', ''),\n };\n });\n }\n\n async report(query: NormalizedQuery): Promise<Report> {\n const range = resolveDateRange(query.dateRange);\n const accountIds = query.accountIds ?? (await this.listAccounts()).map((a) => a.id);\n const level = DATA_LEVEL[query.level];\n const idDim = ID_DIMENSION[query.level];\n const nameMetric = { campaign: 'campaign_name', ad_group: 'adgroup_name', ad: 'ad_name' }[\n query.level as string\n ];\n\n const rows: ReportRow[] = [];\n for (const accountId of accountIds) {\n const data = await this.client.get<{\n list?: Array<{ dimensions: Record<string, string>; metrics: Record<string, string> }>;\n page_info?: { total_number?: number };\n }>('report/integrated/get', {\n advertiser_id: accountId,\n report_type: 'BASIC',\n data_level: level,\n dimensions: [idDim],\n metrics: [\n ...(nameMetric ? [nameMetric] : []),\n 'spend',\n 'impressions',\n 'clicks',\n 'conversion',\n 'complete_payment_roas',\n 'total_complete_payment_rate',\n ],\n start_date: range.start,\n end_date: range.end,\n page: 1,\n page_size: Math.min(query.limit ?? 200, 1000),\n });\n for (const row of data.list ?? []) {\n rows.push(this.toReportRow(row, accountId, query, idDim, nameMetric));\n }\n }\n return { rows };\n }\n\n private toReportRow(\n row: { dimensions: Record<string, string>; metrics: Record<string, string> },\n accountId: string,\n query: NormalizedQuery,\n idDim: string,\n nameMetric?: string,\n ): ReportRow {\n // All TikTok metric values are strings (\"14.68\"); spend is whole currency units.\n const num = (key: string) => Number(row.metrics[key] ?? 0);\n const spend = num('spend');\n const impressions = num('impressions');\n const clicks = num('clicks');\n const conversions = num('conversion');\n // total_complete_payment_rate is (despite the name) total purchase VALUE.\n const conversionValue = num('total_complete_payment_rate');\n const all: Record<MetricName, number> = {\n spend: round2(spend),\n impressions,\n clicks,\n conversions,\n conversion_value: round2(conversionValue),\n ctr: impressions > 0 ? round2((clicks / impressions) * 100) : 0,\n cpc: clicks > 0 ? round2(spend / clicks) : 0,\n cpm: impressions > 0 ? round2((spend / impressions) * 1000) : 0,\n cpa: conversions > 0 ? round2(spend / conversions) : 0,\n roas: spend > 0 ? round2(conversionValue / spend) : 0,\n };\n const entityId = row.dimensions[idDim] ?? '';\n return {\n provider: this.id,\n accountId,\n entity: {\n level: query.level,\n id: entityId,\n name: (nameMetric ? row.metrics[nameMetric] : undefined) ?? entityId,\n },\n metrics: Object.fromEntries(query.metrics.map((m) => [m, all[m]])),\n };\n }\n\n async rawReport(input: {\n account_id: string;\n data_level: string;\n dimensions: string[];\n metrics: string[];\n start_date: string;\n end_date: string;\n page_size?: number;\n }): Promise<Array<Record<string, unknown>>> {\n const data = await this.client.get<{ list?: Array<Record<string, unknown>> }>('report/integrated/get', {\n advertiser_id: input.account_id,\n report_type: 'BASIC',\n data_level: input.data_level,\n dimensions: input.dimensions,\n metrics: input.metrics,\n start_date: input.start_date,\n end_date: input.end_date,\n page: 1,\n page_size: Math.min(input.page_size ?? 200, 1000),\n });\n return data.list ?? [];\n }\n\n async apiRead(input: {\n account_id: string;\n path: string;\n params?: Record<string, unknown>;\n }): Promise<unknown> {\n const path = validateTikTokPath(input.path);\n return this.client.get(path, { ...input.params, advertiser_id: input.account_id });\n }\n\n async listCampaigns(accountId: string, campaignIds?: string[]): Promise<TikTokCampaign[]> {\n const data = await this.client.get<{ list?: TikTokCampaign[] }>('campaign/get', {\n advertiser_id: accountId,\n ...(campaignIds ? { filtering: { campaign_ids: campaignIds } } : {}),\n page: 1,\n page_size: campaignIds ? campaignIds.length : 100,\n });\n return data.list ?? [];\n }\n\n async previewWrite(op: WriteOperation, guard: WriteGuard): Promise<WritePreview> {\n const plan = await this.plan(op, guard);\n // No validate_only equivalent — the preview is a client-side diff from live lookups.\n return {\n summary: plan.summary,\n changes: plan.changes,\n coercions: plan.coercions,\n budgetDeltas: plan.budgetDeltas,\n serverValidated: false,\n };\n }\n\n async applyWrite(op: WriteOperation, guard: WriteGuard): Promise<WriteResult> {\n const plan = await this.plan(op, guard);\n const resourceIds = await plan.execute();\n return { applied: true, resourceIds };\n }\n\n private async plan(op: WriteOperation, guard: WriteGuard): Promise<WritePlan> {\n const payload = op.payload as never;\n switch (op.tool) {\n case 'tiktok_create_campaign':\n return this.planCreateCampaign(op.accountId, payload, guard);\n case 'tiktok_set_campaign_status':\n return this.planSetStatus(op.accountId, payload);\n case 'tiktok_set_budget':\n return this.planSetBudget(op.accountId, payload);\n case 'tiktok_api_create':\n return this.planApiCreate(op.accountId, payload, guard);\n case 'tiktok_api_update':\n return this.planApiUpdate(op.accountId, payload);\n case 'tiktok_api_delete':\n return this.planApiDelete(op.accountId, payload);\n default:\n throw new AdportError('PROVIDER_ERROR', `tiktok: unsupported write tool ${op.tool}`);\n }\n }\n\n private async planApiCreate(\n accountId: string,\n payload: { path: string; body: Record<string, unknown> },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const path = validateTikTokMutationPath(payload.path, 'create');\n const body = structuredClone(payload.body);\n body.advertiser_id = accountId;\n const coercions: string[] = [];\n if (guard.forcePausedCreation) {\n coerceTikTokCreatedStatuses(body, coercions);\n if (path === 'campaign/create' && body.operation_status !== 'DISABLE') {\n body.operation_status = 'DISABLE';\n coercions.push('operation_status set to DISABLE by policy (paused_creation)');\n }\n }\n return {\n summary: `Create TikTok resource via /${path}`,\n changes: [`+ POST /${path} ${JSON.stringify(body)}`],\n coercions,\n budgetDeltas: collectTikTokBudgets(body),\n execute: async () => extractTikTokIds(await this.client.post(path, body)),\n };\n }\n\n private async planApiUpdate(\n accountId: string,\n payload: { path: string; body: Record<string, unknown> },\n ): Promise<WritePlan> {\n const path = validateTikTokMutationPath(payload.path, 'update');\n if (containsTikTokBudget(payload.body)) {\n throw new AdportError('INVALID_INPUT', 'tiktok: budget updates require a typed budget tool for policy checks');\n }\n const body = { ...payload.body, advertiser_id: accountId };\n return {\n summary: `Update TikTok resource via /${path}`,\n changes: [`~ POST /${path} ${JSON.stringify(body)}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => extractTikTokIds(await this.client.post(path, body)),\n };\n }\n\n private async planApiDelete(\n accountId: string,\n payload: { path: string; body: Record<string, unknown> },\n ): Promise<WritePlan> {\n const path = validateTikTokMutationPath(payload.path, 'delete');\n const body = { ...payload.body, advertiser_id: accountId };\n return {\n summary: `Permanently delete TikTok resource via /${path}`,\n changes: [`- POST /${path} ${JSON.stringify(body)}`],\n coercions: [],\n budgetDeltas: [],\n execute: async () => extractTikTokIds(await this.client.post(path, body)),\n };\n }\n\n private async planCreateCampaign(\n accountId: string,\n payload: { campaign_name: string; objective_type: string; budget_mode: string; budget?: number; operation_status?: string },\n guard: WriteGuard,\n ): Promise<WritePlan> {\n const coercions: string[] = [];\n // TikTok creates campaigns ENABLED by default; DISABLE is its \"paused\".\n let operationStatus = payload.operation_status ?? 'ENABLE';\n if (guard.forcePausedCreation && operationStatus === 'ENABLE') {\n operationStatus = 'DISABLE';\n coercions.push('operation_status coerced to DISABLE (paused) by policy (paused_creation)');\n }\n if (payload.budget_mode !== 'BUDGET_MODE_INFINITE' && !payload.budget) {\n throw new AdportError('INVALID_INPUT', `tiktok: budget is required for ${payload.budget_mode}`);\n }\n const budgetDeltas: WritePreview['budgetDeltas'] = [];\n if (payload.budget) {\n budgetDeltas.push({\n target: `new campaign \"${payload.campaign_name}\" ${payload.budget_mode === 'BUDGET_MODE_DAY' ? 'daily' : 'total'} budget`,\n toMicros: payload.budget * UNITS_TO_MICROS,\n });\n }\n return {\n summary: `Create TikTok campaign \"${payload.campaign_name}\" (${payload.objective_type}, ${operationStatus === 'DISABLE' ? 'paused' : 'active'})`,\n changes: [\n `+ campaign \"${payload.campaign_name}\" objective=${payload.objective_type} ${payload.budget_mode}` +\n (payload.budget ? ` budget=${payload.budget}` : '') +\n ` operation_status=${operationStatus}`,\n ],\n coercions,\n budgetDeltas,\n execute: async () => {\n const data = await this.client.post<{ campaign_id?: string }>('campaign/create', {\n advertiser_id: accountId,\n campaign_name: payload.campaign_name,\n objective_type: payload.objective_type,\n budget_mode: payload.budget_mode,\n ...(payload.budget ? { budget: payload.budget } : {}),\n operation_status: operationStatus,\n });\n return data.campaign_id ? [data.campaign_id] : [];\n },\n };\n }\n\n private async planSetStatus(\n accountId: string,\n payload: { campaign_ids: string[]; operation_status: 'ENABLE' | 'DISABLE' },\n ): Promise<WritePlan> {\n const campaigns = await this.listCampaigns(accountId, payload.campaign_ids);\n const changes = payload.campaign_ids.map((id) => {\n const campaign = campaigns.find((c) => c.campaign_id === id);\n return `~ campaign ${id} (\"${campaign?.campaign_name ?? '?'}\") operation_status ${campaign?.operation_status ?? '?'} → ${payload.operation_status}`;\n });\n return {\n summary: `Set ${payload.campaign_ids.length} TikTok campaign(s) → ${payload.operation_status === 'DISABLE' ? 'paused' : 'active'}`,\n changes,\n coercions: [],\n budgetDeltas: [],\n execute: async () => {\n await this.client.post('campaign/status/update', {\n advertiser_id: accountId,\n campaign_ids: payload.campaign_ids,\n operation_status: payload.operation_status,\n });\n return payload.campaign_ids;\n },\n };\n }\n\n private async planSetBudget(\n accountId: string,\n payload: { campaign_id: string; budget: number },\n ): Promise<WritePlan> {\n const campaigns = await this.listCampaigns(accountId, [payload.campaign_id]);\n const campaign = campaigns[0];\n if (!campaign) {\n throw new AdportError('PROVIDER_ERROR', `tiktok: campaign ${payload.campaign_id} not found in advertiser ${accountId}`);\n }\n if (campaign.budget_mode === 'BUDGET_MODE_INFINITE') {\n throw new AdportError('PROVIDER_ERROR', `tiktok: campaign \"${campaign.campaign_name}\" has no budget (BUDGET_MODE_INFINITE)`);\n }\n return {\n summary: `Change TikTok campaign \"${campaign.campaign_name}\" budget ${campaign.budget} → ${payload.budget} (${campaign.budget_mode})`,\n changes: [\n `~ campaign ${payload.campaign_id} budget ${campaign.budget} → ${payload.budget}`,\n '! TikTok requires the new budget to be ≥105% of already-spent amount; the API rejects violations',\n ],\n coercions: [],\n budgetDeltas: [\n {\n target: `campaign \"${campaign.campaign_name}\" ${campaign.budget_mode === 'BUDGET_MODE_DAY' ? 'daily' : 'total'} budget`,\n fromMicros: campaign.budget * UNITS_TO_MICROS,\n toMicros: payload.budget * UNITS_TO_MICROS,\n },\n ],\n execute: async () => {\n await this.client.post('campaign/update', {\n advertiser_id: accountId,\n campaign_id: payload.campaign_id,\n budget: payload.budget,\n });\n return [payload.campaign_id];\n },\n };\n }\n\n private appId(): string {\n return this.app.appId;\n }\n\n private secret(): string {\n return this.app.secret;\n }\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\nfunction validateTikTokPath(path: string): string {\n const normalized = path.replace(/^\\/+|\\/+$/g, '').trim();\n if (!/^[a-z0-9_/-]{3,160}$/.test(normalized) || normalized.includes('..') || normalized.includes('//')) {\n throw new AdportError('INVALID_INPUT', `tiktok: invalid Business API path \"${path}\"`);\n }\n return normalized;\n}\n\nfunction validateTikTokMutationPath(path: string, kind: 'create' | 'update' | 'delete'): string {\n const normalized = validateTikTokPath(path);\n if (!normalized.endsWith(`/${kind}`)) {\n throw new AdportError('INVALID_INPUT', `tiktok: ${kind} tool requires an endpoint ending in /${kind}`);\n }\n return normalized;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction coerceTikTokCreatedStatuses(value: unknown, coercions: string[], path = 'body'): void {\n if (Array.isArray(value)) return value.forEach((item, index) => coerceTikTokCreatedStatuses(item, coercions, `${path}[${index}]`));\n if (!isRecord(value)) return;\n for (const [key, child] of Object.entries(value)) {\n if (key === 'operation_status' && child === 'ENABLE') {\n value[key] = 'DISABLE';\n coercions.push(`${path}.${key} coerced to DISABLE by policy (paused_creation)`);\n } else {\n coerceTikTokCreatedStatuses(child, coercions, `${path}.${key}`);\n }\n }\n}\n\nfunction containsTikTokBudget(value: unknown): boolean {\n if (Array.isArray(value)) return value.some(containsTikTokBudget);\n if (!isRecord(value)) return false;\n return Object.entries(value).some(([key, child]) => /budget/i.test(key) || containsTikTokBudget(child));\n}\n\nfunction collectTikTokBudgets(value: unknown, path = 'body'): WritePreview['budgetDeltas'] {\n const deltas: WritePreview['budgetDeltas'] = [];\n if (Array.isArray(value)) {\n value.forEach((item, index) => deltas.push(...collectTikTokBudgets(item, `${path}[${index}]`)));\n return deltas;\n }\n if (!isRecord(value)) return deltas;\n for (const [key, child] of Object.entries(value)) {\n const childPath = `${path}.${key}`;\n if (/budget/i.test(key)) {\n const amount = Number(child);\n if (Number.isFinite(amount) && amount > 0) {\n deltas.push({ target: childPath, toMicros: Math.round(amount * UNITS_TO_MICROS) });\n }\n }\n deltas.push(...collectTikTokBudgets(child, childPath));\n }\n return deltas;\n}\n\nfunction extractTikTokIds(value: unknown): string[] {\n const ids: string[] = [];\n const visit = (item: unknown): void => {\n if (Array.isArray(item)) return item.forEach(visit);\n if (!isRecord(item)) return;\n for (const [key, child] of Object.entries(item)) {\n if (/(?:^|_)ids?$/.test(key)) {\n if (Array.isArray(child)) child.forEach((id) => ids.push(String(id)));\n else if (typeof child === 'string' || typeof child === 'number') ids.push(String(child));\n } else visit(child);\n }\n };\n visit(value);\n return [...new Set(ids)];\n}\n","import { defineTool, guardedWriteTool, type AnyToolDefinition } from '@adport/core';\nimport { z } from 'zod';\nimport type { TikTokAdsProvider } from './provider.js';\n\nconst OBJECTIVES = [\n 'REACH',\n 'TRAFFIC',\n 'VIDEO_VIEWS',\n 'LEAD_GENERATION',\n 'ENGAGEMENT',\n 'APP_PROMOTION',\n 'WEB_CONVERSIONS',\n 'PRODUCT_SALES',\n] as const;\n\nconst operationStatusSchema = z.enum(['ENABLE', 'DISABLE']);\n\nexport function tiktokTools(provider: TikTokAdsProvider): AnyToolDefinition[] {\n return [\n defineTool({\n name: 'tiktok_api_read',\n namespace: 'tiktok',\n description: 'Call any documented TikTok Business API v1.3 GET endpoint with advertiser scoping and API-shaped parameters.',\n input: z.object({\n account_id: z.string(),\n path: z.string().min(3).describe('Relative endpoint, e.g. adgroup/get, ad/get, creative/report/get'),\n params: z.record(z.string(), z.unknown()).optional(),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n return provider.apiRead(input);\n },\n }),\n defineTool({\n name: 'tiktok_campaigns',\n namespace: 'tiktok',\n description: 'List TikTok campaigns (id, name, operation_status ENABLE/DISABLE, budget, budget_mode, objective).',\n input: z.object({\n account_id: z.string().describe('Advertiser id'),\n campaign_ids: z.array(z.string()).optional(),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const campaigns = await provider.listCampaigns(input.account_id, input.campaign_ids);\n return { campaigns };\n },\n }),\n defineTool({\n name: 'tiktok_report',\n namespace: 'tiktok',\n description:\n 'Raw TikTok synchronous reporting (report/integrated). Metric values come back as strings; ' +\n 'note total_complete_payment_rate is total purchase VALUE (misleading name), complete_payment_roas is purchase ROAS.',\n input: z.object({\n account_id: z.string(),\n data_level: z.enum(['AUCTION_ADVERTISER', 'AUCTION_CAMPAIGN', 'AUCTION_ADGROUP', 'AUCTION_AD']).default('AUCTION_CAMPAIGN'),\n dimensions: z.array(z.string()).min(1).default(['campaign_id']),\n metrics: z.array(z.string()).min(1).default(['spend', 'impressions', 'clicks', 'conversion']),\n start_date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n end_date: z.string().regex(/^\\d{4}-\\d{2}-\\d{2}$/),\n page_size: z.number().int().positive().max(1000).default(200),\n }),\n annotations: { readOnly: true },\n async handler(input) {\n const rows = await provider.rawReport(input);\n return { rows, row_count: rows.length };\n },\n }),\n guardedWriteTool({\n name: 'tiktok_create_campaign',\n namespace: 'tiktok',\n description:\n 'Create a TikTok campaign. Budget is a float in whole account-currency units (min ~20 USD-equivalent for ' +\n 'DAY/TOTAL modes). TikTok has no server-side dry run — the preview is a client-side diff.',\n provider: 'tiktok',\n kind: 'create',\n payload: z.object({\n campaign_name: z.string().min(1).max(512),\n objective_type: z.enum(OBJECTIVES).default('TRAFFIC'),\n budget_mode: z.enum(['BUDGET_MODE_DAY', 'BUDGET_MODE_TOTAL', 'BUDGET_MODE_INFINITE']).default('BUDGET_MODE_DAY'),\n budget: z.number().positive().optional().describe('Whole currency units (float), required unless BUDGET_MODE_INFINITE'),\n operation_status: operationStatusSchema.optional(),\n }),\n }),\n guardedWriteTool({\n name: 'tiktok_set_campaign_status',\n namespace: 'tiktok',\n description: 'Enable or pause (DISABLE) up to 20 TikTok campaigns.',\n provider: 'tiktok',\n kind: 'update',\n payload: z.object({\n campaign_ids: z.array(z.string()).min(1).max(20),\n operation_status: operationStatusSchema,\n }),\n }),\n guardedWriteTool({\n name: 'tiktok_set_budget',\n namespace: 'tiktok',\n description:\n \"Change a TikTok campaign's budget (whole currency units). TikTok rejects new budgets below 105% of current spend.\",\n provider: 'tiktok',\n kind: 'update',\n payload: z.object({\n campaign_id: z.string(),\n budget: z.number().positive(),\n }),\n }),\n guardedWriteTool({\n name: 'tiktok_api_create',\n namespace: 'tiktok',\n description:\n 'Create through any documented TikTok Business API v1.3 /create endpoint. The advertiser is forced to account_id, campaign creation is paused, and budget fields are policy-checked.',\n provider: 'tiktok',\n kind: 'create',\n payload: z.object({ path: z.string().min(3), body: z.record(z.string(), z.unknown()) }),\n }),\n guardedWriteTool({\n name: 'tiktok_api_update',\n namespace: 'tiktok',\n description: 'Update non-budget fields through any documented TikTok Business API v1.3 /update endpoint.',\n provider: 'tiktok',\n kind: 'update',\n payload: z.object({ path: z.string().min(3), body: z.record(z.string(), z.unknown()) }),\n }),\n guardedWriteTool({\n name: 'tiktok_api_delete',\n namespace: 'tiktok',\n description: 'Permanently delete through any documented TikTok Business API v1.3 /delete endpoint.',\n provider: 'tiktok',\n kind: 'remove',\n destructive: true,\n payload: z.object({ path: z.string().min(3), body: z.record(z.string(), z.unknown()) }),\n }),\n ];\n}\n","import type { CredentialStore, ProviderModule } from '@adport/core';\nimport { TikTokClient, type TikTokCredentials } from './client.js';\nimport { TikTokAdsProvider } from './provider.js';\nimport { tiktokTools } from './tools.js';\n\nexport { TikTokClient, TIKTOK_API_VERSION, type TikTokCredentials } from './client.js';\nexport { TikTokAdsProvider, UNITS_TO_MICROS } from './provider.js';\nexport { tiktokTools } from './tools.js';\n\nexport async function resolveTikTokCredentials(\n store: CredentialStore,\n): Promise<(TikTokCredentials & { appId: string; secret: string }) | undefined> {\n const record = await store.get('tiktok');\n if (record?.data.access_token && record.data.app_id && record.data.secret) {\n return {\n accessToken: record.data.access_token,\n appId: record.data.app_id,\n secret: record.data.secret,\n sandbox: record.data.sandbox === 'true',\n };\n }\n const env = process.env;\n if (env.TIKTOK_ACCESS_TOKEN && env.TIKTOK_APP_ID && env.TIKTOK_APP_SECRET) {\n return {\n accessToken: env.TIKTOK_ACCESS_TOKEN,\n appId: env.TIKTOK_APP_ID,\n secret: env.TIKTOK_APP_SECRET,\n sandbox: env.TIKTOK_SANDBOX === 'true',\n };\n }\n return undefined;\n}\n\nexport async function createTikTokModule(store: CredentialStore): Promise<ProviderModule | undefined> {\n const credentials = await resolveTikTokCredentials(store);\n if (!credentials) return undefined;\n const provider = new TikTokAdsProvider(new TikTokClient(credentials), {\n appId: credentials.appId,\n secret: credentials.secret,\n });\n return { provider, tools: tiktokTools(provider) };\n}\n"],"mappings":"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","names":["AdportError"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adport/provider-tiktok",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "TikTok Ads provider for adport — TikTok Business/Marketing API v1.3 with sandbox support",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "zod": "^4.4.3",
20
- "@adport/core": "0.3.0"
20
+ "@adport/core": "0.4.0"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^26.1.2",