@affonso/cli 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +194 -0
  2. package/dist/index.js +1439 -0
  3. package/package.json +2 -2
package/dist/index.js ADDED
@@ -0,0 +1,1439 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var import_commander = require("commander");
28
+
29
+ // src/lib/client.ts
30
+ var import_sdk = require("@affonso/sdk");
31
+
32
+ // src/auth/storage.ts
33
+ var import_node_fs = __toESM(require("fs"));
34
+ var import_node_path = __toESM(require("path"));
35
+ var import_node_os = __toESM(require("os"));
36
+ var CONFIG_DIR = import_node_path.default.join(import_node_os.default.homedir(), ".config", "affonso");
37
+ var AUTH_FILE = import_node_path.default.join(CONFIG_DIR, "auth.json");
38
+ var CONFIG_FILE = import_node_path.default.join(CONFIG_DIR, "config.json");
39
+ function ensureDir() {
40
+ import_node_fs.default.mkdirSync(CONFIG_DIR, { recursive: true });
41
+ }
42
+ function loadAuth() {
43
+ try {
44
+ if (!import_node_fs.default.existsSync(AUTH_FILE)) return null;
45
+ const raw = import_node_fs.default.readFileSync(AUTH_FILE, "utf-8");
46
+ return JSON.parse(raw);
47
+ } catch (err) {
48
+ if (err instanceof SyntaxError) {
49
+ console.error(`Warning: ${AUTH_FILE} contains invalid JSON and was ignored.`);
50
+ }
51
+ return null;
52
+ }
53
+ }
54
+ function saveAuth(auth) {
55
+ ensureDir();
56
+ import_node_fs.default.writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), {
57
+ mode: 384
58
+ });
59
+ }
60
+ function clearAuth() {
61
+ try {
62
+ import_node_fs.default.unlinkSync(AUTH_FILE);
63
+ } catch (err) {
64
+ if (err.code !== "ENOENT") throw err;
65
+ }
66
+ }
67
+ function loadConfig() {
68
+ try {
69
+ if (!import_node_fs.default.existsSync(CONFIG_FILE)) return {};
70
+ const raw = import_node_fs.default.readFileSync(CONFIG_FILE, "utf-8");
71
+ return JSON.parse(raw);
72
+ } catch (err) {
73
+ if (err instanceof SyntaxError) {
74
+ console.error(`Warning: ${CONFIG_FILE} contains invalid JSON and was ignored.`);
75
+ }
76
+ return {};
77
+ }
78
+ }
79
+ function saveConfig(config) {
80
+ ensureDir();
81
+ const existing = loadConfig();
82
+ const merged = { ...existing, ...config };
83
+ import_node_fs.default.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), {
84
+ mode: 384
85
+ });
86
+ }
87
+
88
+ // src/auth/resolve.ts
89
+ function resolveAuth(flagApiKey) {
90
+ if (flagApiKey) {
91
+ return { apiKey: flagApiKey, source: "flag" };
92
+ }
93
+ const envKey = process.env.AFFONSO_API_KEY;
94
+ if (envKey) {
95
+ return { apiKey: envKey, source: "env" };
96
+ }
97
+ const config = loadConfig();
98
+ if (config.api_key) {
99
+ return { apiKey: config.api_key, source: "config" };
100
+ }
101
+ const auth = loadAuth();
102
+ if (auth?.access_token) {
103
+ if (auth.expires_at && Date.now() > auth.expires_at) {
104
+ return null;
105
+ }
106
+ return { apiKey: auth.access_token, source: "oauth" };
107
+ }
108
+ return null;
109
+ }
110
+ function resolveBaseUrl(flagBaseUrl) {
111
+ if (flagBaseUrl) return flagBaseUrl;
112
+ const config = loadConfig();
113
+ if (config.base_url) return config.base_url;
114
+ return "https://api.affonso.io/v1";
115
+ }
116
+
117
+ // src/auth/oauth.ts
118
+ var import_node_crypto = __toESM(require("crypto"));
119
+ var import_node_http = __toESM(require("http"));
120
+ var CLIENT_ID = "affonso-cli";
121
+ var SCOPES = "read write";
122
+ var LOGIN_TIMEOUT_MS = 5 * 60 * 1e3;
123
+ function escapeHtml(s) {
124
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
125
+ }
126
+ function base64url(buffer) {
127
+ return buffer.toString("base64url");
128
+ }
129
+ function generatePKCE() {
130
+ const verifier = base64url(import_node_crypto.default.randomBytes(32));
131
+ const challenge = base64url(import_node_crypto.default.createHash("sha256").update(verifier).digest());
132
+ return { verifier, challenge };
133
+ }
134
+ function getIssuerUrl(baseUrl) {
135
+ return baseUrl.replace(/\/v1\/?$/, "");
136
+ }
137
+ async function login(baseUrl) {
138
+ const issuer = getIssuerUrl(baseUrl);
139
+ const { verifier, challenge } = generatePKCE();
140
+ return new Promise((resolve, reject) => {
141
+ const server = import_node_http.default.createServer(async (req, res) => {
142
+ const url = new URL(req.url ?? "/", `http://localhost`);
143
+ if (url.pathname !== "/callback") {
144
+ res.writeHead(404);
145
+ res.end("Not found");
146
+ return;
147
+ }
148
+ const code = url.searchParams.get("code");
149
+ const error = url.searchParams.get("error");
150
+ if (error) {
151
+ const desc = url.searchParams.get("error_description") ?? error;
152
+ res.writeHead(200, { "Content-Type": "text/html" });
153
+ res.end(errorPage(desc));
154
+ server.close();
155
+ reject(new Error(`OAuth error: ${desc}`));
156
+ return;
157
+ }
158
+ if (!code) {
159
+ res.writeHead(400, { "Content-Type": "text/html" });
160
+ res.end(errorPage("No authorization code received"));
161
+ server.close();
162
+ reject(new Error("No authorization code received"));
163
+ return;
164
+ }
165
+ try {
166
+ const tokenRes = await fetch(`${issuer}/oauth/token`, {
167
+ method: "POST",
168
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
169
+ body: new URLSearchParams({
170
+ grant_type: "authorization_code",
171
+ code,
172
+ redirect_uri: `http://localhost:${port}/callback`,
173
+ client_id: CLIENT_ID,
174
+ code_verifier: verifier
175
+ })
176
+ });
177
+ if (!tokenRes.ok) {
178
+ const err = await tokenRes.text();
179
+ throw new Error(`Token exchange failed: ${err}`);
180
+ }
181
+ const tokens = await tokenRes.json();
182
+ saveAuth({
183
+ access_token: tokens.access_token,
184
+ refresh_token: tokens.refresh_token,
185
+ expires_at: Date.now() + tokens.expires_in * 1e3,
186
+ team_id: tokens.team_id,
187
+ team_name: tokens.team_name
188
+ });
189
+ res.writeHead(200, { "Content-Type": "text/html" });
190
+ res.end(successPage());
191
+ server.close();
192
+ resolve();
193
+ } catch (err) {
194
+ res.writeHead(200, { "Content-Type": "text/html" });
195
+ res.end(errorPage(err instanceof Error ? err.message : "Unknown error"));
196
+ server.close();
197
+ reject(err);
198
+ }
199
+ });
200
+ let port;
201
+ server.listen(0, "127.0.0.1", async () => {
202
+ const addr = server.address();
203
+ if (!addr || typeof addr === "string") {
204
+ reject(new Error("Failed to start local server"));
205
+ return;
206
+ }
207
+ port = addr.port;
208
+ const authUrl = new URL(`${issuer}/oauth/authorize`);
209
+ authUrl.searchParams.set("response_type", "code");
210
+ authUrl.searchParams.set("client_id", CLIENT_ID);
211
+ authUrl.searchParams.set("redirect_uri", `http://localhost:${port}/callback`);
212
+ authUrl.searchParams.set("code_challenge", challenge);
213
+ authUrl.searchParams.set("code_challenge_method", "S256");
214
+ authUrl.searchParams.set("scope", SCOPES);
215
+ console.log("Opening browser for authentication...");
216
+ console.log(`If the browser doesn't open, visit:
217
+ ${authUrl.toString()}
218
+ `);
219
+ try {
220
+ const open = (await import("open")).default;
221
+ await open(authUrl.toString());
222
+ } catch {
223
+ }
224
+ });
225
+ setTimeout(() => {
226
+ server.close();
227
+ reject(new Error("Login timed out after 5 minutes"));
228
+ }, LOGIN_TIMEOUT_MS);
229
+ });
230
+ }
231
+ async function refreshToken(baseUrl, refreshTokenValue) {
232
+ const issuer = getIssuerUrl(baseUrl);
233
+ try {
234
+ const res = await fetch(`${issuer}/oauth/token`, {
235
+ method: "POST",
236
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
237
+ body: new URLSearchParams({
238
+ grant_type: "refresh_token",
239
+ refresh_token: refreshTokenValue,
240
+ client_id: CLIENT_ID
241
+ })
242
+ });
243
+ if (!res.ok) return false;
244
+ const tokens = await res.json();
245
+ saveAuth({
246
+ access_token: tokens.access_token,
247
+ refresh_token: tokens.refresh_token,
248
+ expires_at: Date.now() + tokens.expires_in * 1e3,
249
+ team_id: tokens.team_id,
250
+ team_name: tokens.team_name
251
+ });
252
+ return true;
253
+ } catch {
254
+ return false;
255
+ }
256
+ }
257
+ function successPage() {
258
+ return `<!DOCTYPE html><html><body style="font-family:system-ui;text-align:center;padding:60px">
259
+ <h1>&#10003; Logged in to Affonso</h1>
260
+ <p>You can close this window and return to the terminal.</p>
261
+ </body></html>`;
262
+ }
263
+ function errorPage(message) {
264
+ return `<!DOCTYPE html><html><body style="font-family:system-ui;text-align:center;padding:60px">
265
+ <h1>Authentication Error</h1>
266
+ <p>${escapeHtml(message)}</p>
267
+ </body></html>`;
268
+ }
269
+
270
+ // src/lib/client.ts
271
+ async function getClient(opts2) {
272
+ const baseUrl = resolveBaseUrl(opts2.baseUrl);
273
+ let auth = resolveAuth(opts2.apiKey);
274
+ if (!auth) {
275
+ const stored = loadAuth();
276
+ if (stored?.refresh_token) {
277
+ const refreshed = await refreshToken(baseUrl, stored.refresh_token);
278
+ if (refreshed) {
279
+ auth = resolveAuth(opts2.apiKey);
280
+ } else {
281
+ console.error("Error: Session expired and token refresh failed. Run `affonso login` to re-authenticate.");
282
+ process.exit(1);
283
+ }
284
+ }
285
+ }
286
+ if (!auth) {
287
+ console.error("Error: Authentication required. Run `affonso login` or set AFFONSO_API_KEY.");
288
+ process.exit(1);
289
+ }
290
+ return new import_sdk.Affonso(auth.apiKey, { baseUrl });
291
+ }
292
+
293
+ // src/lib/errors.ts
294
+ var import_sdk2 = require("@affonso/sdk");
295
+ function handleError(err, json) {
296
+ if (err instanceof import_sdk2.AffonsoError) {
297
+ if (json) {
298
+ console.error(
299
+ JSON.stringify(
300
+ {
301
+ success: false,
302
+ error: {
303
+ code: err.code ?? "UNKNOWN",
304
+ message: err.message,
305
+ field: err.field,
306
+ details: err.details
307
+ }
308
+ },
309
+ null,
310
+ 2
311
+ )
312
+ );
313
+ } else {
314
+ const parts = [`Error: ${err.message}`];
315
+ if (err.code) parts[0] += ` (${err.code})`;
316
+ if (err.details && Array.isArray(err.details)) {
317
+ for (const detail of err.details) {
318
+ if (typeof detail === "object" && detail !== null) {
319
+ const d = detail;
320
+ if (d.field && d.message) {
321
+ parts.push(` \u2022 ${d.field}: ${d.message}`);
322
+ } else if (d.message) {
323
+ parts.push(` \u2022 ${d.message}`);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ console.error(parts.join("\n"));
329
+ }
330
+ if (err.status === 401) {
331
+ console.error("\nRun `affonso login` or set AFFONSO_API_KEY.");
332
+ }
333
+ process.exit(1);
334
+ }
335
+ if (err instanceof Error) {
336
+ if (json) {
337
+ console.error(JSON.stringify({ success: false, error: { code: "CLI_ERROR", message: err.message } }, null, 2));
338
+ } else {
339
+ console.error(`Error: ${err.message}`);
340
+ }
341
+ process.exit(1);
342
+ }
343
+ console.error("An unknown error occurred");
344
+ process.exit(1);
345
+ }
346
+
347
+ // src/lib/opts.ts
348
+ function opts(cmd) {
349
+ let current = cmd;
350
+ let merged = {};
351
+ while (current) {
352
+ merged = { ...current.opts(), ...merged };
353
+ current = current.parent;
354
+ }
355
+ return merged;
356
+ }
357
+
358
+ // src/output/json.ts
359
+ function formatJson(data) {
360
+ return JSON.stringify(data, null, 2);
361
+ }
362
+
363
+ // src/output/table.ts
364
+ var NO_COLOR = process.env.NO_COLOR !== void 0 || process.argv.includes("--no-color");
365
+ var dim = (s) => NO_COLOR ? s : `\x1B[2m${s}\x1B[0m`;
366
+ var bold = (s) => NO_COLOR ? s : `\x1B[1m${s}\x1B[0m`;
367
+ function formatTable(data, columns) {
368
+ if (data.length === 0) return "No results found.";
369
+ const cols = columns ?? Object.keys(data[0]);
370
+ const widths = {};
371
+ for (const col of cols) {
372
+ const header2 = col.toUpperCase().replace(/_/g, " ");
373
+ widths[col] = header2.length;
374
+ for (const row of data) {
375
+ const val = formatValue(row[col]);
376
+ widths[col] = Math.max(widths[col], val.length);
377
+ }
378
+ widths[col] = Math.min(widths[col], 40);
379
+ }
380
+ const header = cols.map((col) => {
381
+ const label = col.toUpperCase().replace(/_/g, " ");
382
+ return bold(label.padEnd(widths[col]));
383
+ }).join(" ");
384
+ const rows = data.map(
385
+ (row) => cols.map((col) => {
386
+ const val = formatValue(row[col]);
387
+ const truncated = val.length > 40 ? `${val.slice(0, 37)}...` : val;
388
+ return truncated.padEnd(widths[col]);
389
+ }).join(" ")
390
+ );
391
+ return [header, dim("\u2500".repeat(header.replace(/\x1b\[[0-9;]*m/g, "").length)), ...rows].join(
392
+ "\n"
393
+ );
394
+ }
395
+ function formatValue(val) {
396
+ if (val === null || val === void 0) return "\u2014";
397
+ if (val instanceof Date) return val.toISOString().split("T")[0];
398
+ if (typeof val === "string") {
399
+ if (/^\d{4}-\d{2}-\d{2}T/.test(val)) return val.split("T")[0];
400
+ return val;
401
+ }
402
+ if (typeof val === "boolean") return val ? "yes" : "no";
403
+ if (typeof val === "object") return JSON.stringify(val);
404
+ return String(val);
405
+ }
406
+ function formatSingle(data) {
407
+ const entries = Object.entries(data);
408
+ const maxKey = Math.max(...entries.map(([k]) => k.length));
409
+ return entries.map(([key, val]) => {
410
+ const label = bold(key.padEnd(maxKey));
411
+ return `${label} ${formatValue(val)}`;
412
+ }).join("\n");
413
+ }
414
+
415
+ // src/output/format.ts
416
+ function output(result, opts2, columns) {
417
+ if (opts2.json) {
418
+ console.log(formatJson(result));
419
+ return;
420
+ }
421
+ if (isPaginatedResponse(result)) {
422
+ console.log(formatTable(result.data, columns));
423
+ if (result.pagination) {
424
+ const p = result.pagination;
425
+ console.log(
426
+ `
427
+ Page ${p.page}/${p.total_pages} (${p.total} total)`
428
+ );
429
+ }
430
+ return;
431
+ }
432
+ if (Array.isArray(result)) {
433
+ console.log(formatTable(result, columns));
434
+ return;
435
+ }
436
+ if (result && typeof result === "object") {
437
+ console.log(formatSingle(result));
438
+ return;
439
+ }
440
+ console.log(String(result));
441
+ }
442
+ function isPaginatedResponse(val) {
443
+ return val !== null && typeof val === "object" && "data" in val && Array.isArray(val.data);
444
+ }
445
+ function outputSuccess(message, opts2) {
446
+ if (opts2.json) {
447
+ console.log(formatJson({ success: true, message }));
448
+ } else {
449
+ console.log(message);
450
+ }
451
+ }
452
+
453
+ // src/commands/affiliates.ts
454
+ function registerAffiliateCommands(program2) {
455
+ const affiliates = program2.command("affiliates").description("Manage affiliates");
456
+ affiliates.command("list").description("List affiliates").option("--limit <n>", "Items per page", "50").option("--page <n>", "Page number", "1").option("--status <status>", "Filter by status (pending, approved, rejected)").option("--search <query>", "Search by name or email").option("--group-id <id>", "Filter by group ID").option("--program-id <id>", "Filter by program ID").option("--expand <fields>", "Expand fields (comma-separated)").option("--sort <field:dir>", "Sort (e.g. createdAt:desc)").option("--date-from <date>", "Filter from date").option("--date-to <date>", "Filter to date").action(async function() {
457
+ const o = opts(this);
458
+ try {
459
+ const client = await getClient(o);
460
+ const result = await client.affiliates.list({
461
+ limit: Number(o.limit),
462
+ page: Number(o.page),
463
+ partnership_status: o.status,
464
+ search: o.search,
465
+ group_id: o.groupId,
466
+ program_id: o.programId,
467
+ expand: o.expand,
468
+ sort: o.sort,
469
+ dateFrom: o.dateFrom,
470
+ dateTo: o.dateTo
471
+ });
472
+ output(result, o, ["id", "name", "email", "partnership_status", "tracking_id", "created_at"]);
473
+ } catch (err) {
474
+ handleError(err, o.json);
475
+ }
476
+ });
477
+ affiliates.command("get <id>").description("Get an affiliate by ID").option("--expand <fields>", "Expand fields (comma-separated)").action(async function(id) {
478
+ const o = opts(this);
479
+ try {
480
+ const client = await getClient(o);
481
+ const result = await client.affiliates.retrieve(id, {
482
+ expand: o.expand
483
+ });
484
+ output(result, o);
485
+ } catch (err) {
486
+ handleError(err, o.json);
487
+ }
488
+ });
489
+ affiliates.command("create").description("Create an affiliate").requiredOption("--name <name>", "Affiliate name").requiredOption("--email <email>", "Affiliate email").requiredOption("--program-id <id>", "Program ID").option("--tracking-id <id>", "Custom tracking ID").option("--group-id <id>", "Group ID").option("--company-name <name>", "Company name").option("--country-code <code>", "Country code").option("--external-user-id <id>", "External user ID").action(async function() {
490
+ const o = opts(this);
491
+ try {
492
+ const client = await getClient(o);
493
+ const result = await client.affiliates.create({
494
+ name: o.name,
495
+ email: o.email,
496
+ program_id: o.programId,
497
+ tracking_id: o.trackingId,
498
+ group_id: o.groupId,
499
+ company_name: o.companyName,
500
+ country_code: o.countryCode,
501
+ external_user_id: o.externalUserId
502
+ });
503
+ output(result, o);
504
+ } catch (err) {
505
+ handleError(err, o.json);
506
+ }
507
+ });
508
+ affiliates.command("update <id>").description("Update an affiliate").option("--name <name>", "Affiliate name").option("--email <email>", "Affiliate email").option("--status <status>", "Status (pending, approved, rejected)").option("--group-id <id>", "Group ID").option("--company-name <name>", "Company name").option("--country-code <code>", "Country code").option("--external-user-id <id>", "External user ID").option("--onboarding-completed", "Mark onboarding as completed").action(async function(id) {
509
+ const o = opts(this);
510
+ try {
511
+ const client = await getClient(o);
512
+ const params = {};
513
+ if (o.name !== void 0) params.name = o.name;
514
+ if (o.email !== void 0) params.email = o.email;
515
+ if (o.status !== void 0) params.status = o.status;
516
+ if (o.groupId !== void 0) params.group_id = o.groupId;
517
+ if (o.companyName !== void 0) params.company_name = o.companyName;
518
+ if (o.countryCode !== void 0) params.country_code = o.countryCode;
519
+ if (o.externalUserId !== void 0) params.external_user_id = o.externalUserId;
520
+ if (o.onboardingCompleted) params.onboarding_completed = true;
521
+ const result = await client.affiliates.update(id, params);
522
+ output(result, o);
523
+ } catch (err) {
524
+ handleError(err, o.json);
525
+ }
526
+ });
527
+ affiliates.command("delete <id>").description("Delete an affiliate").action(async function(id) {
528
+ const o = opts(this);
529
+ try {
530
+ const client = await getClient(o);
531
+ const result = await client.affiliates.del(id);
532
+ outputSuccess(result.message ?? "Affiliate deleted.", o);
533
+ } catch (err) {
534
+ handleError(err, o.json);
535
+ }
536
+ });
537
+ }
538
+
539
+ // src/commands/referrals.ts
540
+ function registerReferralCommands(program2) {
541
+ const referrals = program2.command("referrals").description("Manage referrals");
542
+ referrals.command("list").description("List referrals").option("--limit <n>", "Items per page", "50").option("--starting-after <id>", "Cursor: fetch items after this ID").option("--ending-before <id>", "Cursor: fetch items before this ID").option("--affiliate-id <id>", "Filter by affiliate ID").option("--status <status>", "Filter by status").option("--order <dir>", "Order (asc, desc)").option("--expand <fields>", "Expand fields").option("--created-gte <date>", "Created after date").option("--created-lte <date>", "Created before date").action(async function() {
543
+ const o = opts(this);
544
+ try {
545
+ const client = await getClient(o);
546
+ const result = await client.referrals.list({
547
+ limit: Number(o.limit),
548
+ starting_after: o.startingAfter,
549
+ ending_before: o.endingBefore,
550
+ affiliate_id: o.affiliateId,
551
+ status: o.status,
552
+ order: o.order,
553
+ expand: o.expand,
554
+ created_gte: o.createdGte,
555
+ created_lte: o.createdLte
556
+ });
557
+ output(result, o, ["id", "affiliate_id", "email", "status", "created_at"]);
558
+ } catch (err) {
559
+ handleError(err, o.json);
560
+ }
561
+ });
562
+ referrals.command("get <id>").description("Get a referral by ID").option("--expand <fields>", "Expand fields").option("--include <fields>", "Include fields").action(async function(id) {
563
+ const o = opts(this);
564
+ try {
565
+ const client = await getClient(o);
566
+ const result = await client.referrals.retrieve(id, {
567
+ expand: o.expand,
568
+ include: o.include
569
+ });
570
+ output(result, o);
571
+ } catch (err) {
572
+ handleError(err, o.json);
573
+ }
574
+ });
575
+ referrals.command("create").description("Create a referral").requiredOption("--email <email>", "Referral email").requiredOption("--affiliate-id <id>", "Affiliate ID").option("--subscription-id <id>", "Subscription ID").option("--customer-id <id>", "Customer ID").option("--click-id <id>", "Click ID").option("--status <status>", "Initial status").option("--name <name>", "Referral name").action(async function() {
576
+ const o = opts(this);
577
+ try {
578
+ const client = await getClient(o);
579
+ const result = await client.referrals.create({
580
+ email: o.email,
581
+ affiliate_id: o.affiliateId,
582
+ subscription_id: o.subscriptionId,
583
+ customer_id: o.customerId,
584
+ click_id: o.clickId,
585
+ status: o.status,
586
+ name: o.name
587
+ });
588
+ output(result, o);
589
+ } catch (err) {
590
+ handleError(err, o.json);
591
+ }
592
+ });
593
+ referrals.command("update <id>").description("Update a referral").option("--email <email>", "Referral email").option("--status <status>", "Status").option("--subscription-id <id>", "Subscription ID").option("--customer-id <id>", "Customer ID").option("--name <name>", "Referral name").action(async function(id) {
594
+ const o = opts(this);
595
+ try {
596
+ const client = await getClient(o);
597
+ const params = {};
598
+ if (o.email !== void 0) params.email = o.email;
599
+ if (o.status !== void 0) params.status = o.status;
600
+ if (o.subscriptionId !== void 0) params.subscription_id = o.subscriptionId;
601
+ if (o.customerId !== void 0) params.customer_id = o.customerId;
602
+ if (o.name !== void 0) params.name = o.name;
603
+ const result = await client.referrals.update(id, params);
604
+ output(result, o);
605
+ } catch (err) {
606
+ handleError(err, o.json);
607
+ }
608
+ });
609
+ referrals.command("delete <id>").description("Delete a referral").action(async function(id) {
610
+ const o = opts(this);
611
+ try {
612
+ const client = await getClient(o);
613
+ const result = await client.referrals.del(id);
614
+ outputSuccess(result.message ?? "Referral deleted.", o);
615
+ } catch (err) {
616
+ handleError(err, o.json);
617
+ }
618
+ });
619
+ }
620
+
621
+ // src/commands/clicks.ts
622
+ function registerClickCommands(program2) {
623
+ const clicks = program2.command("clicks").description("Track click events");
624
+ clicks.command("create").description("Record a click event").requiredOption("--program-id <id>", "Program ID").requiredOption("--tracking-id <id>", "Tracking ID").option("--referrer <url>", "Referrer URL").option("--utm-source <val>", "UTM source").option("--utm-medium <val>", "UTM medium").option("--utm-campaign <val>", "UTM campaign").option("--utm-term <val>", "UTM term").option("--utm-content <val>", "UTM content").option("--sub1 <val>", "Sub-tracking parameter 1").option("--sub2 <val>", "Sub-tracking parameter 2").option("--sub3 <val>", "Sub-tracking parameter 3").option("--sub4 <val>", "Sub-tracking parameter 4").option("--sub5 <val>", "Sub-tracking parameter 5").option("--ip <ip>", "IP address").option("--user-agent <ua>", "User agent string").action(async function() {
625
+ const o = opts(this);
626
+ try {
627
+ const client = await getClient(o);
628
+ const result = await client.clicks.create({
629
+ programId: o.programId,
630
+ trackingId: o.trackingId,
631
+ referrer: o.referrer,
632
+ utmSource: o.utmSource,
633
+ utmMedium: o.utmMedium,
634
+ utmCampaign: o.utmCampaign,
635
+ utmTerm: o.utmTerm,
636
+ utmContent: o.utmContent,
637
+ sub1: o.sub1,
638
+ sub2: o.sub2,
639
+ sub3: o.sub3,
640
+ sub4: o.sub4,
641
+ sub5: o.sub5,
642
+ ip: o.ip,
643
+ userAgent: o.userAgent
644
+ });
645
+ output(result, o);
646
+ } catch (err) {
647
+ handleError(err, o.json);
648
+ }
649
+ });
650
+ }
651
+
652
+ // src/commands/commissions.ts
653
+ function registerCommissionCommands(program2) {
654
+ const commissions = program2.command("commissions").description("Manage commissions");
655
+ commissions.command("list").description("List commissions").option("--limit <n>", "Items per page", "50").option("--page <n>", "Page number", "1").option("--status <status>", "Filter by commission status").option("--sales-status <status>", "Filter by sales status").option("--referral-id <id>", "Filter by referral ID").option("--affiliate-id <id>", "Filter by affiliate ID").option("--expand <fields>", "Expand fields").option("--sort <field:dir>", "Sort").option("--date-from <date>", "Filter from date").option("--date-to <date>", "Filter to date").action(async function() {
656
+ const o = opts(this);
657
+ try {
658
+ const client = await getClient(o);
659
+ const result = await client.commissions.list({
660
+ limit: Number(o.limit),
661
+ page: Number(o.page),
662
+ status: o.status,
663
+ sales_status: o.salesStatus,
664
+ referral_id: o.referralId,
665
+ affiliate_id: o.affiliateId,
666
+ expand: o.expand,
667
+ sort: o.sort,
668
+ dateFrom: o.dateFrom,
669
+ dateTo: o.dateTo
670
+ });
671
+ output(result, o, [
672
+ "id",
673
+ "affiliate_id",
674
+ "sale_amount",
675
+ "sale_amount_currency",
676
+ "commission_amount",
677
+ "status",
678
+ "created_at"
679
+ ]);
680
+ } catch (err) {
681
+ handleError(err, o.json);
682
+ }
683
+ });
684
+ commissions.command("get <id>").description("Get a commission by ID").option("--expand <fields>", "Expand fields").action(async function(id) {
685
+ const o = opts(this);
686
+ try {
687
+ const client = await getClient(o);
688
+ const result = await client.commissions.retrieve(id, {
689
+ expand: o.expand
690
+ });
691
+ output(result, o);
692
+ } catch (err) {
693
+ handleError(err, o.json);
694
+ }
695
+ });
696
+ commissions.command("create").description("Create a commission").requiredOption("--referral-id <id>", "Referral ID").requiredOption("--sale-amount <n>", "Sale amount").requiredOption("--sale-amount-currency <code>", "Sale currency (e.g. USD)").requiredOption("--commission-amount <n>", "Commission amount").requiredOption("--commission-currency <code>", "Commission currency").option("--is-subscription", "Mark as subscription commission").option("--status <status>", "Commission status").option("--sales-status <status>", "Sales status").option("--payment-intent-id <id>", "Payment intent ID").option("--hold-period-days <n>", "Hold period in days").action(async function() {
697
+ const o = opts(this);
698
+ try {
699
+ const client = await getClient(o);
700
+ const result = await client.commissions.create({
701
+ referral_id: o.referralId,
702
+ sale_amount: Number(o.saleAmount),
703
+ sale_amount_currency: o.saleAmountCurrency,
704
+ commission_amount: Number(o.commissionAmount),
705
+ commission_currency: o.commissionCurrency,
706
+ is_subscription: o.isSubscription || void 0,
707
+ status: o.status,
708
+ sales_status: o.salesStatus,
709
+ payment_intent_id: o.paymentIntentId,
710
+ hold_period_days: o.holdPeriodDays ? Number(o.holdPeriodDays) : void 0
711
+ });
712
+ output(result, o);
713
+ } catch (err) {
714
+ handleError(err, o.json);
715
+ }
716
+ });
717
+ commissions.command("update <id>").description("Update a commission").option("--status <status>", "Commission status").option("--sales-status <status>", "Sales status").option("--hold-period-days <n>", "Hold period in days").option("--sale-amount <n>", "Sale amount").option("--sale-amount-currency <code>", "Sale currency").option("--commission-amount <n>", "Commission amount").option("--commission-currency <code>", "Commission currency").action(async function(id) {
718
+ const o = opts(this);
719
+ try {
720
+ const client = await getClient(o);
721
+ const params = {};
722
+ if (o.status !== void 0) params.status = o.status;
723
+ if (o.salesStatus !== void 0) params.sales_status = o.salesStatus;
724
+ if (o.holdPeriodDays !== void 0) params.hold_period_days = Number(o.holdPeriodDays);
725
+ if (o.saleAmount !== void 0) params.sale_amount = Number(o.saleAmount);
726
+ if (o.saleAmountCurrency !== void 0) params.sale_amount_currency = o.saleAmountCurrency;
727
+ if (o.commissionAmount !== void 0) params.commission_amount = Number(o.commissionAmount);
728
+ if (o.commissionCurrency !== void 0) params.commission_currency = o.commissionCurrency;
729
+ const result = await client.commissions.update(id, params);
730
+ output(result, o);
731
+ } catch (err) {
732
+ handleError(err, o.json);
733
+ }
734
+ });
735
+ commissions.command("delete <id>").description("Delete a commission").action(async function(id) {
736
+ const o = opts(this);
737
+ try {
738
+ const client = await getClient(o);
739
+ const result = await client.commissions.del(id);
740
+ outputSuccess(result.message ?? "Commission deleted.", o);
741
+ } catch (err) {
742
+ handleError(err, o.json);
743
+ }
744
+ });
745
+ }
746
+
747
+ // src/commands/coupons.ts
748
+ function registerCouponCommands(program2) {
749
+ const coupons = program2.command("coupons").description("Manage coupons");
750
+ coupons.command("list").description("List coupons").option("--limit <n>", "Items per page", "50").option("--page <n>", "Page number", "1").option("--affiliate-id <id>", "Filter by affiliate ID").option("--program-id <id>", "Filter by program ID").option("--search <query>", "Search by code").option("--expand <fields>", "Expand fields").option("--sort <field:dir>", "Sort").action(async function() {
751
+ const o = opts(this);
752
+ try {
753
+ const client = await getClient(o);
754
+ const result = await client.coupons.list({
755
+ limit: Number(o.limit),
756
+ page: Number(o.page),
757
+ affiliate_id: o.affiliateId,
758
+ program_id: o.programId,
759
+ search: o.search,
760
+ expand: o.expand,
761
+ sort: o.sort
762
+ });
763
+ output(result, o, ["id", "affiliate_id", "code", "discount_type", "discount_value", "duration", "created_at"]);
764
+ } catch (err) {
765
+ handleError(err, o.json);
766
+ }
767
+ });
768
+ coupons.command("get <id>").description("Get a coupon by ID").option("--expand <fields>", "Expand fields").action(async function(id) {
769
+ const o = opts(this);
770
+ try {
771
+ const client = await getClient(o);
772
+ const result = await client.coupons.retrieve(id, {
773
+ expand: o.expand
774
+ });
775
+ output(result, o);
776
+ } catch (err) {
777
+ handleError(err, o.json);
778
+ }
779
+ });
780
+ coupons.command("create").description("Create a coupon").requiredOption("--affiliate-id <id>", "Affiliate ID").requiredOption("--code <code>", "Coupon code").requiredOption("--discount-type <type>", "Discount type (percentage, fixed)").requiredOption("--discount-value <n>", "Discount value").requiredOption("--duration <dur>", "Duration (forever, once, repeating)").option("--duration-in-months <n>", "Duration in months (for repeating)").option("--currency <code>", "Currency code").option("--product-ids <ids>", "Product IDs (comma-separated)").action(async function() {
781
+ const o = opts(this);
782
+ try {
783
+ const client = await getClient(o);
784
+ const result = await client.coupons.create({
785
+ affiliate_id: o.affiliateId,
786
+ code: o.code,
787
+ discount_type: o.discountType,
788
+ discount_value: Number(o.discountValue),
789
+ duration: o.duration,
790
+ duration_in_months: o.durationInMonths ? Number(o.durationInMonths) : void 0,
791
+ currency: o.currency,
792
+ product_ids: o.productIds?.split(",")
793
+ });
794
+ output(result, o);
795
+ } catch (err) {
796
+ handleError(err, o.json);
797
+ }
798
+ });
799
+ coupons.command("delete <id>").description("Delete a coupon").action(async function(id) {
800
+ const o = opts(this);
801
+ try {
802
+ const client = await getClient(o);
803
+ const result = await client.coupons.del(id);
804
+ outputSuccess(result.message ?? "Coupon deleted.", o);
805
+ } catch (err) {
806
+ handleError(err, o.json);
807
+ }
808
+ });
809
+ }
810
+
811
+ // src/commands/payouts.ts
812
+ function registerPayoutCommands(program2) {
813
+ const payouts = program2.command("payouts").description("Manage payouts");
814
+ payouts.command("list").description("List payouts").option("--limit <n>", "Items per page", "50").option("--page <n>", "Page number", "1").option("--status <status>", "Filter by status (pending, processing, completed, failed, cancelled)").option("--affiliate-id <id>", "Filter by affiliate ID").option("--sort <field:dir>", "Sort").option("--date-from <date>", "Filter from date").option("--date-to <date>", "Filter to date").action(async function() {
815
+ const o = opts(this);
816
+ try {
817
+ const client = await getClient(o);
818
+ const result = await client.payouts.list({
819
+ limit: Number(o.limit),
820
+ page: Number(o.page),
821
+ status: o.status,
822
+ affiliateId: o.affiliateId,
823
+ sort: o.sort,
824
+ dateFrom: o.dateFrom,
825
+ dateTo: o.dateTo
826
+ });
827
+ output(result, o, ["id", "affiliate_id", "amount", "status", "payment_method", "created_at"]);
828
+ } catch (err) {
829
+ handleError(err, o.json);
830
+ }
831
+ });
832
+ payouts.command("get <id>").description("Get a payout by ID").action(async function(id) {
833
+ const o = opts(this);
834
+ try {
835
+ const client = await getClient(o);
836
+ const result = await client.payouts.retrieve(id);
837
+ output(result, o);
838
+ } catch (err) {
839
+ handleError(err, o.json);
840
+ }
841
+ });
842
+ payouts.command("update <id>").description("Update a payout").requiredOption("--status <status>", "Payout status").option("--payment-method <method>", "Payment method").option("--payment-reference <ref>", "Payment reference (e.g. transaction ID)").action(async function(id) {
843
+ const o = opts(this);
844
+ try {
845
+ const client = await getClient(o);
846
+ const result = await client.payouts.update(id, {
847
+ status: o.status,
848
+ paymentMethod: o.paymentMethod,
849
+ paymentReference: o.paymentReference
850
+ });
851
+ output(result, o);
852
+ } catch (err) {
853
+ handleError(err, o.json);
854
+ }
855
+ });
856
+ }
857
+
858
+ // src/commands/program.ts
859
+ function registerProgramCommands(program2) {
860
+ const prog = program2.command("program").description("Manage program settings");
861
+ prog.command("get").description("Get program settings").action(async function() {
862
+ const o = opts(this);
863
+ try {
864
+ const client = await getClient(o);
865
+ const result = await client.program.retrieve();
866
+ output(result, o);
867
+ } catch (err) {
868
+ handleError(err, o.json);
869
+ }
870
+ });
871
+ prog.command("update").description("Update program settings").option("--name <name>", "Program name").option("--tagline <text>", "Tagline").option("--category <cat>", "Category").option("--description <text>", "Description").option("--website-url <url>", "Website URL").option("--logo-url <url>", "Logo URL").option("--auto-approve", "Enable auto-approve").option("--no-auto-approve", "Disable auto-approve").option("--affiliate-links-enabled", "Enable affiliate links").option("--no-affiliate-links-enabled", "Disable affiliate links").action(async function() {
872
+ const o = opts(this);
873
+ try {
874
+ const client = await getClient(o);
875
+ const params = {};
876
+ if (o.name !== void 0) params.name = o.name;
877
+ if (o.tagline !== void 0) params.tagline = o.tagline;
878
+ if (o.category !== void 0) params.category = o.category;
879
+ if (o.description !== void 0) params.description = o.description;
880
+ if (o.websiteUrl !== void 0) params.website_url = o.websiteUrl;
881
+ if (o.logoUrl !== void 0) params.logo_url = o.logoUrl;
882
+ if (o.autoApprove !== void 0) params.auto_approve = o.autoApprove;
883
+ if (o.affiliateLinksEnabled !== void 0)
884
+ params.affiliate_links_enabled = o.affiliateLinksEnabled;
885
+ const result = await client.program.update(params);
886
+ output(result, o);
887
+ } catch (err) {
888
+ handleError(err, o.json);
889
+ }
890
+ });
891
+ registerPaymentTerms(prog);
892
+ registerTracking(prog);
893
+ registerRestrictions(prog);
894
+ registerFraudRules(prog);
895
+ registerPortal(prog);
896
+ registerNotifications(prog);
897
+ registerGroups(prog);
898
+ registerCreatives(prog);
899
+ }
900
+ function registerPaymentTerms(prog) {
901
+ const pt = prog.command("payment-terms").description("Manage payment terms");
902
+ pt.command("get").description("Get payment terms").action(async function() {
903
+ const o = opts(this);
904
+ try {
905
+ const client = await getClient(o);
906
+ const result = await client.program.paymentTerms.retrieve();
907
+ output(result, o);
908
+ } catch (err) {
909
+ handleError(err, o.json);
910
+ }
911
+ });
912
+ pt.command("update").description("Update payment terms").option("--commission-type <type>", "Commission type (percentage, fixed)").option("--commission-rate <n>", "Commission rate").option("--commission-duration <dur>", "Duration (forever, once, first_month, custom)").option("--commission-duration-value <n>", "Custom duration value").option("--payment-threshold <n>", "Minimum payout threshold").option("--payment-frequency <freq>", "Payment frequency (monthly, biweekly, weekly)").option("--cookie-lifetime <days>", "Cookie lifetime in days").option("--auto-payout", "Enable auto payout").option("--no-auto-payout", "Disable auto payout").option("--invoice-required", "Require invoices").option("--no-invoice-required", "Don't require invoices").action(async function() {
913
+ const o = opts(this);
914
+ try {
915
+ const client = await getClient(o);
916
+ const params = {};
917
+ if (o.commissionType !== void 0) params.commission_type = o.commissionType;
918
+ if (o.commissionRate !== void 0) params.commission_rate = Number(o.commissionRate);
919
+ if (o.commissionDuration !== void 0) params.commission_duration = o.commissionDuration;
920
+ if (o.commissionDurationValue !== void 0)
921
+ params.commission_duration_value = Number(o.commissionDurationValue);
922
+ if (o.paymentThreshold !== void 0) params.payment_threshold = Number(o.paymentThreshold);
923
+ if (o.paymentFrequency !== void 0) params.payment_frequency = o.paymentFrequency;
924
+ if (o.cookieLifetime !== void 0) params.cookie_lifetime = Number(o.cookieLifetime);
925
+ if (o.autoPayout !== void 0) params.auto_payout = o.autoPayout;
926
+ if (o.invoiceRequired !== void 0) params.invoice_required = o.invoiceRequired;
927
+ const result = await client.program.paymentTerms.update(params);
928
+ output(result, o);
929
+ } catch (err) {
930
+ handleError(err, o.json);
931
+ }
932
+ });
933
+ }
934
+ function registerTracking(prog) {
935
+ const tracking = prog.command("tracking").description("Manage tracking settings");
936
+ tracking.command("get").description("Get tracking settings").action(async function() {
937
+ const o = opts(this);
938
+ try {
939
+ const client = await getClient(o);
940
+ const result = await client.program.tracking.retrieve();
941
+ output(result, o);
942
+ } catch (err) {
943
+ handleError(err, o.json);
944
+ }
945
+ });
946
+ tracking.command("update").description("Update tracking settings").option("--default-referral-parameter <param>", "Default referral parameter").option("--enabled-referral-parameters <params>", "Enabled parameters (comma-separated)").option("--track-email", "Track email").option("--no-track-email", "Don't track email").option("--track-name", "Track name").option("--no-track-name", "Don't track name").action(async function() {
947
+ const o = opts(this);
948
+ try {
949
+ const client = await getClient(o);
950
+ const params = {};
951
+ if (o.defaultReferralParameter !== void 0)
952
+ params.default_referral_parameter = o.defaultReferralParameter;
953
+ if (o.enabledReferralParameters !== void 0)
954
+ params.enabled_referral_parameters = o.enabledReferralParameters.split(",");
955
+ if (o.trackEmail !== void 0) params.track_email = o.trackEmail;
956
+ if (o.trackName !== void 0) params.track_name = o.trackName;
957
+ const result = await client.program.tracking.update(params);
958
+ output(result, o);
959
+ } catch (err) {
960
+ handleError(err, o.json);
961
+ }
962
+ });
963
+ }
964
+ function registerRestrictions(prog) {
965
+ const restrictions = prog.command("restrictions").description("Manage traffic restrictions");
966
+ restrictions.command("get").description("Get restrictions").action(async function() {
967
+ const o = opts(this);
968
+ try {
969
+ const client = await getClient(o);
970
+ const result = await client.program.restrictions.retrieve();
971
+ output(result, o);
972
+ } catch (err) {
973
+ handleError(err, o.json);
974
+ }
975
+ });
976
+ restrictions.command("update").description("Update restrictions").option("--websites", "Allow websites").option("--no-websites", "Disallow websites").option("--social-marketing", "Allow social marketing").option("--no-social-marketing", "Disallow social marketing").option("--organic-social", "Allow organic social").option("--no-organic-social", "Disallow organic social").option("--email-marketing", "Allow email marketing").option("--no-email-marketing", "Disallow email marketing").option("--paid-ads", "Allow paid ads").option("--no-paid-ads", "Disallow paid ads").option("--content-marketing", "Allow content marketing").option("--no-content-marketing", "Disallow content marketing").option("--coupon-sites", "Allow coupon sites").option("--no-coupon-sites", "Disallow coupon sites").option("--review-sites", "Allow review sites").option("--no-review-sites", "Disallow review sites").option("--incentivized-traffic", "Allow incentivized traffic").option("--no-incentivized-traffic", "Disallow incentivized traffic").option("--trademark-bidding", "Allow trademark bidding").option("--no-trademark-bidding", "Disallow trademark bidding").action(async function() {
977
+ const o = opts(this);
978
+ try {
979
+ const client = await getClient(o);
980
+ const params = {};
981
+ const fieldMap = [
982
+ ["websites", "websites"],
983
+ ["socialMarketing", "social_marketing"],
984
+ ["organicSocial", "organic_social"],
985
+ ["emailMarketing", "email_marketing"],
986
+ ["paidAds", "paid_ads"],
987
+ ["contentMarketing", "content_marketing"],
988
+ ["couponSites", "coupon_sites"],
989
+ ["reviewSites", "review_sites"],
990
+ ["incentivizedTraffic", "incentivized_traffic"],
991
+ ["trademarkBidding", "trademark_bidding"]
992
+ ];
993
+ for (const [camel, snake] of fieldMap) {
994
+ const val = o[camel];
995
+ if (val !== void 0) params[snake] = val;
996
+ }
997
+ const result = await client.program.restrictions.update(params);
998
+ output(result, o);
999
+ } catch (err) {
1000
+ handleError(err, o.json);
1001
+ }
1002
+ });
1003
+ }
1004
+ function registerFraudRules(prog) {
1005
+ const fraud = prog.command("fraud-rules").description("Manage fraud detection rules");
1006
+ fraud.command("get").description("Get fraud rules").action(async function() {
1007
+ const o = opts(this);
1008
+ try {
1009
+ const client = await getClient(o);
1010
+ const result = await client.program.fraudRules.retrieve();
1011
+ output(result, o);
1012
+ } catch (err) {
1013
+ handleError(err, o.json);
1014
+ }
1015
+ });
1016
+ fraud.command("update").description("Update fraud rules").option("--self-referral <mode>", "Self-referral mode (off, detect, block)").option("--duplicate-ip <mode>", "Duplicate IP mode (off, detect, block)").option("--vpn-proxy <mode>", "VPN/Proxy mode (off, detect, block)").option("--suspicious-conversion <mode>", "Suspicious conversion mode (off, detect, block)").action(async function() {
1017
+ const o = opts(this);
1018
+ try {
1019
+ const client = await getClient(o);
1020
+ const params = {};
1021
+ if (o.selfReferral !== void 0) params.self_referral = o.selfReferral;
1022
+ if (o.duplicateIp !== void 0) params.duplicate_ip = o.duplicateIp;
1023
+ if (o.vpnProxy !== void 0) params.vpn_proxy = o.vpnProxy;
1024
+ if (o.suspiciousConversion !== void 0) params.suspicious_conversion = o.suspiciousConversion;
1025
+ const result = await client.program.fraudRules.update(params);
1026
+ output(result, o);
1027
+ } catch (err) {
1028
+ handleError(err, o.json);
1029
+ }
1030
+ });
1031
+ }
1032
+ function registerPortal(prog) {
1033
+ const portal = prog.command("portal").description("Manage affiliate portal settings");
1034
+ portal.command("get").description("Get portal settings").action(async function() {
1035
+ const o = opts(this);
1036
+ try {
1037
+ const client = await getClient(o);
1038
+ const result = await client.program.portal.retrieve();
1039
+ output(result, o);
1040
+ } catch (err) {
1041
+ handleError(err, o.json);
1042
+ }
1043
+ });
1044
+ portal.command("update").description("Update portal settings").option("--primary-color <color>", "Primary color (hex)").option("--accent-color <color>", "Accent color (hex)").option("--logo-url <url>", "Logo URL").option("--favicon-url <url>", "Favicon URL").option("--custom-domain <domain>", "Custom domain").option("--terms-url <url>", "Terms URL").option("--privacy-url <url>", "Privacy URL").option("--onboarding-enabled", "Enable onboarding").option("--no-onboarding-enabled", "Disable onboarding").option("--resources-enabled", "Enable resources").option("--no-resources-enabled", "Disable resources").action(async function() {
1045
+ const o = opts(this);
1046
+ try {
1047
+ const client = await getClient(o);
1048
+ const params = {};
1049
+ if (o.primaryColor !== void 0) params.primary_color = o.primaryColor;
1050
+ if (o.accentColor !== void 0) params.accent_color = o.accentColor;
1051
+ if (o.logoUrl !== void 0) params.logo_url = o.logoUrl;
1052
+ if (o.faviconUrl !== void 0) params.favicon_url = o.faviconUrl;
1053
+ if (o.customDomain !== void 0) params.custom_domain = o.customDomain;
1054
+ if (o.termsUrl !== void 0) params.terms_url = o.termsUrl;
1055
+ if (o.privacyUrl !== void 0) params.privacy_url = o.privacyUrl;
1056
+ if (o.onboardingEnabled !== void 0)
1057
+ params.onboarding_enabled = o.onboardingEnabled;
1058
+ if (o.resourcesEnabled !== void 0)
1059
+ params.resources_enabled = o.resourcesEnabled;
1060
+ const result = await client.program.portal.update(params);
1061
+ output(result, o);
1062
+ } catch (err) {
1063
+ handleError(err, o.json);
1064
+ }
1065
+ });
1066
+ }
1067
+ function registerNotifications(prog) {
1068
+ const notifications = prog.command("notifications").description("Manage email notifications");
1069
+ notifications.command("list").description("List notification settings").action(async function() {
1070
+ const o = opts(this);
1071
+ try {
1072
+ const client = await getClient(o);
1073
+ const result = await client.program.notifications.list();
1074
+ output(result, o, ["id", "email_type", "subject", "enabled", "recipient"]);
1075
+ } catch (err) {
1076
+ handleError(err, o.json);
1077
+ }
1078
+ });
1079
+ notifications.command("update <id>").description("Update a notification setting").option("--subject <text>", "Email subject").option("--enabled", "Enable notification").option("--no-enabled", "Disable notification").action(async function(id) {
1080
+ const o = opts(this);
1081
+ try {
1082
+ const client = await getClient(o);
1083
+ const params = {};
1084
+ if (o.subject !== void 0) params.subject = o.subject;
1085
+ if (o.enabled !== void 0) params.enabled = o.enabled;
1086
+ const result = await client.program.notifications.update(id, params);
1087
+ output(result, o);
1088
+ } catch (err) {
1089
+ handleError(err, o.json);
1090
+ }
1091
+ });
1092
+ }
1093
+ function registerGroups(prog) {
1094
+ const groups = prog.command("groups").description("Manage affiliate groups");
1095
+ groups.command("list").description("List groups").option("--expand <fields>", "Expand fields (incentives, multi_level_incentives)").action(async function() {
1096
+ const o = opts(this);
1097
+ try {
1098
+ const client = await getClient(o);
1099
+ const result = await client.program.groups.list({
1100
+ expand: o.expand
1101
+ });
1102
+ output(result, o, ["id", "name", "description", "is_default", "affiliate_count", "created_at"]);
1103
+ } catch (err) {
1104
+ handleError(err, o.json);
1105
+ }
1106
+ });
1107
+ groups.command("get <id>").description("Get a group by ID").option("--expand <fields>", "Expand fields").action(async function(id) {
1108
+ const o = opts(this);
1109
+ try {
1110
+ const client = await getClient(o);
1111
+ const result = await client.program.groups.retrieve(id, {
1112
+ expand: o.expand
1113
+ });
1114
+ output(result, o);
1115
+ } catch (err) {
1116
+ handleError(err, o.json);
1117
+ }
1118
+ });
1119
+ groups.command("create").description("Create a group").requiredOption("--name <name>", "Group name").option("--description <text>", "Group description").option("--is-default", "Set as default group").action(async function() {
1120
+ const o = opts(this);
1121
+ try {
1122
+ const client = await getClient(o);
1123
+ const result = await client.program.groups.create({
1124
+ name: o.name,
1125
+ description: o.description,
1126
+ is_default: o.isDefault
1127
+ });
1128
+ output(result, o);
1129
+ } catch (err) {
1130
+ handleError(err, o.json);
1131
+ }
1132
+ });
1133
+ groups.command("update <id>").description("Update a group").option("--name <name>", "Group name").option("--description <text>", "Group description").option("--is-default", "Set as default group").option("--no-is-default", "Unset as default group").action(async function(id) {
1134
+ const o = opts(this);
1135
+ try {
1136
+ const client = await getClient(o);
1137
+ const params = {};
1138
+ if (o.name !== void 0) params.name = o.name;
1139
+ if (o.description !== void 0) params.description = o.description;
1140
+ if (o.isDefault !== void 0) params.is_default = o.isDefault;
1141
+ const result = await client.program.groups.update(id, params);
1142
+ output(result, o);
1143
+ } catch (err) {
1144
+ handleError(err, o.json);
1145
+ }
1146
+ });
1147
+ groups.command("delete <id>").description("Delete a group").action(async function(id) {
1148
+ const o = opts(this);
1149
+ try {
1150
+ const client = await getClient(o);
1151
+ const result = await client.program.groups.del(id);
1152
+ outputSuccess(result.message ?? "Group deleted.", o);
1153
+ } catch (err) {
1154
+ handleError(err, o.json);
1155
+ }
1156
+ });
1157
+ }
1158
+ function registerCreatives(prog) {
1159
+ const creatives = prog.command("creatives").description("Manage creatives");
1160
+ creatives.command("list").description("List creatives").option("--limit <n>", "Items per page", "50").option("--page <n>", "Page number", "1").option("--type <type>", "Filter by type").option("--search <query>", "Search by name").action(async function() {
1161
+ const o = opts(this);
1162
+ try {
1163
+ const client = await getClient(o);
1164
+ const result = await client.program.creatives.list({
1165
+ limit: Number(o.limit),
1166
+ page: Number(o.page),
1167
+ type: o.type,
1168
+ search: o.search
1169
+ });
1170
+ output(result, o, ["id", "name", "type", "url", "created_at"]);
1171
+ } catch (err) {
1172
+ handleError(err, o.json);
1173
+ }
1174
+ });
1175
+ creatives.command("get <id>").description("Get a creative by ID").action(async function(id) {
1176
+ const o = opts(this);
1177
+ try {
1178
+ const client = await getClient(o);
1179
+ const result = await client.program.creatives.retrieve(id);
1180
+ output(result, o);
1181
+ } catch (err) {
1182
+ handleError(err, o.json);
1183
+ }
1184
+ });
1185
+ creatives.command("create").description("Create a creative").requiredOption("--name <name>", "Creative name").requiredOption("--type <type>", "Creative type").option("--description <text>", "Description").option("--url <url>", "URL").option("--file-url <url>", "File URL").option("--width <n>", "Width in pixels").option("--height <n>", "Height in pixels").action(async function() {
1186
+ const o = opts(this);
1187
+ try {
1188
+ const client = await getClient(o);
1189
+ const result = await client.program.creatives.create({
1190
+ name: o.name,
1191
+ type: o.type,
1192
+ description: o.description,
1193
+ url: o.url,
1194
+ file_url: o.fileUrl,
1195
+ width: o.width ? Number(o.width) : void 0,
1196
+ height: o.height ? Number(o.height) : void 0
1197
+ });
1198
+ output(result, o);
1199
+ } catch (err) {
1200
+ handleError(err, o.json);
1201
+ }
1202
+ });
1203
+ creatives.command("update <id>").description("Update a creative").option("--name <name>", "Creative name").option("--type <type>", "Creative type").option("--description <text>", "Description").option("--url <url>", "URL").option("--file-url <url>", "File URL").option("--width <n>", "Width in pixels").option("--height <n>", "Height in pixels").action(async function(id) {
1204
+ const o = opts(this);
1205
+ try {
1206
+ const client = await getClient(o);
1207
+ const params = {};
1208
+ if (o.name !== void 0) params.name = o.name;
1209
+ if (o.type !== void 0) params.type = o.type;
1210
+ if (o.description !== void 0) params.description = o.description;
1211
+ if (o.url !== void 0) params.url = o.url;
1212
+ if (o.fileUrl !== void 0) params.file_url = o.fileUrl;
1213
+ if (o.width !== void 0) params.width = Number(o.width);
1214
+ if (o.height !== void 0) params.height = Number(o.height);
1215
+ const result = await client.program.creatives.update(id, params);
1216
+ output(result, o);
1217
+ } catch (err) {
1218
+ handleError(err, o.json);
1219
+ }
1220
+ });
1221
+ creatives.command("delete <id>").description("Delete a creative").action(async function(id) {
1222
+ const o = opts(this);
1223
+ try {
1224
+ const client = await getClient(o);
1225
+ const result = await client.program.creatives.del(id);
1226
+ outputSuccess(result.message ?? "Creative deleted.", o);
1227
+ } catch (err) {
1228
+ handleError(err, o.json);
1229
+ }
1230
+ });
1231
+ }
1232
+
1233
+ // src/commands/marketplace.ts
1234
+ var import_sdk3 = require("@affonso/sdk");
1235
+ function registerMarketplaceCommands(program2) {
1236
+ const marketplace = program2.command("marketplace").description("Browse the affiliate marketplace (public, no auth required)");
1237
+ marketplace.command("list").description("List marketplace programs").option("--limit <n>", "Items per page", "50").option("--page <n>", "Page number", "1").option("--category <cat>", "Filter by category").option("--search <query>", "Search programs").option("--sort <field:dir>", "Sort").action(async function() {
1238
+ const o = opts(this);
1239
+ try {
1240
+ const baseUrl = resolveBaseUrl(o.baseUrl);
1241
+ const client = new import_sdk3.Affonso("public", { baseUrl });
1242
+ const result = await client.marketplace.list({
1243
+ limit: Number(o.limit),
1244
+ page: Number(o.page),
1245
+ category: o.category,
1246
+ search: o.search,
1247
+ sort: o.sort
1248
+ });
1249
+ output(result, o, ["id", "name", "category", "commission_type", "commission_rate", "cookie_lifetime"]);
1250
+ } catch (err) {
1251
+ handleError(err, o.json);
1252
+ }
1253
+ });
1254
+ marketplace.command("get <id>").description("Get a marketplace program by ID").action(async function(id) {
1255
+ const o = opts(this);
1256
+ try {
1257
+ const baseUrl = resolveBaseUrl(o.baseUrl);
1258
+ const client = new import_sdk3.Affonso("public", { baseUrl });
1259
+ const result = await client.marketplace.retrieve(id);
1260
+ output(result, o);
1261
+ } catch (err) {
1262
+ handleError(err, o.json);
1263
+ }
1264
+ });
1265
+ }
1266
+
1267
+ // src/commands/embed-tokens.ts
1268
+ function registerEmbedTokenCommands(program2) {
1269
+ const embedTokens = program2.command("embed-tokens").description("Generate embed tokens");
1270
+ embedTokens.command("create").description("Create an embed token").option("--affiliate-id <id>", "Affiliate ID").option("--external-user-id <id>", "External user ID").option("--email <email>", "Partner email").option("--name <name>", "Partner name").action(async function() {
1271
+ const o = opts(this);
1272
+ try {
1273
+ const client = await getClient(o);
1274
+ const result = await client.embedTokens.create({
1275
+ affiliate_id: o.affiliateId,
1276
+ external_user_id: o.externalUserId,
1277
+ email: o.email,
1278
+ name: o.name
1279
+ });
1280
+ output(result, o);
1281
+ } catch (err) {
1282
+ handleError(err, o.json);
1283
+ }
1284
+ });
1285
+ }
1286
+
1287
+ // src/commands/login.ts
1288
+ function registerLoginCommand(program2) {
1289
+ program2.command("login").description("Log in via browser (OAuth 2.1)").action(async function() {
1290
+ const o = opts(this);
1291
+ try {
1292
+ const existing = resolveAuth();
1293
+ if (existing?.source === "oauth") {
1294
+ console.log("Already logged in. Run `affonso logout` first to switch accounts.");
1295
+ return;
1296
+ }
1297
+ const baseUrl = resolveBaseUrl(o.baseUrl);
1298
+ await login(baseUrl);
1299
+ console.log("Successfully logged in!");
1300
+ } catch (err) {
1301
+ handleError(err, o.json);
1302
+ }
1303
+ });
1304
+ }
1305
+
1306
+ // src/commands/logout.ts
1307
+ function registerLogoutCommand(program2) {
1308
+ program2.command("logout").description("Log out and remove stored credentials").action(async function() {
1309
+ const o = opts(this);
1310
+ try {
1311
+ const auth = loadAuth();
1312
+ if (auth?.access_token) {
1313
+ const baseUrl = resolveBaseUrl(o.baseUrl);
1314
+ const issuer = baseUrl.replace(/\/v1\/?$/, "");
1315
+ try {
1316
+ await fetch(`${issuer}/oauth/revoke`, {
1317
+ method: "POST",
1318
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1319
+ body: new URLSearchParams({
1320
+ token: auth.access_token,
1321
+ client_id: "affonso-cli"
1322
+ })
1323
+ });
1324
+ } catch {
1325
+ }
1326
+ }
1327
+ clearAuth();
1328
+ console.log("Logged out.");
1329
+ } catch (err) {
1330
+ handleError(err, o.json);
1331
+ }
1332
+ });
1333
+ }
1334
+
1335
+ // src/commands/whoami.ts
1336
+ function registerWhoamiCommand(program2) {
1337
+ program2.command("whoami").description("Show current authentication status").action(async function() {
1338
+ const o = opts(this);
1339
+ try {
1340
+ const auth = resolveAuth(o.apiKey);
1341
+ if (!auth) {
1342
+ console.error("Not authenticated. Run `affonso login` or set AFFONSO_API_KEY.");
1343
+ process.exit(1);
1344
+ }
1345
+ const info = {
1346
+ auth_method: auth.source
1347
+ };
1348
+ if (auth.source === "oauth") {
1349
+ const stored = loadAuth();
1350
+ if (stored?.team_id) info.team_id = stored.team_id;
1351
+ if (stored?.team_name) info.team_name = stored.team_name;
1352
+ if (stored?.expires_at) {
1353
+ info.token_expires = new Date(stored.expires_at).toISOString();
1354
+ }
1355
+ } else {
1356
+ const key = auth.apiKey;
1357
+ info.api_key = `${key.slice(0, 10)}...${key.slice(-4)}`;
1358
+ }
1359
+ output(info, o);
1360
+ } catch (err) {
1361
+ handleError(err, o.json);
1362
+ }
1363
+ });
1364
+ }
1365
+
1366
+ // src/commands/config.ts
1367
+ var VALID_KEYS = ["api-key", "base-url"];
1368
+ var KEY_MAP = {
1369
+ "api-key": "api_key",
1370
+ "base-url": "base_url"
1371
+ };
1372
+ function registerConfigCommands(program2) {
1373
+ const config = program2.command("config").description("Manage CLI configuration");
1374
+ config.command("get <key>").description("Get a config value (api-key, base-url)").action(async function(key) {
1375
+ const o = opts(this);
1376
+ try {
1377
+ if (!VALID_KEYS.includes(key)) {
1378
+ console.error(`Unknown config key: ${key}. Valid keys: ${VALID_KEYS.join(", ")}`);
1379
+ process.exit(1);
1380
+ }
1381
+ const stored = loadConfig();
1382
+ const mappedKey = KEY_MAP[key];
1383
+ const value = stored[mappedKey];
1384
+ if (value) {
1385
+ if (key === "api-key") {
1386
+ const v = value;
1387
+ console.log(`${v.slice(0, 10)}...${v.slice(-4)}`);
1388
+ } else {
1389
+ console.log(value);
1390
+ }
1391
+ } else {
1392
+ console.log(`${key} is not set.`);
1393
+ }
1394
+ } catch (err) {
1395
+ handleError(err, o.json);
1396
+ }
1397
+ });
1398
+ config.command("set <key> <value>").description("Set a config value (api-key, base-url)").action(async function(key, value) {
1399
+ const o = opts(this);
1400
+ try {
1401
+ if (!VALID_KEYS.includes(key)) {
1402
+ console.error(`Unknown config key: ${key}. Valid keys: ${VALID_KEYS.join(", ")}`);
1403
+ process.exit(1);
1404
+ }
1405
+ const mappedKey = KEY_MAP[key];
1406
+ saveConfig({ [mappedKey]: value });
1407
+ console.log(`${key} saved.`);
1408
+ } catch (err) {
1409
+ handleError(err, o.json);
1410
+ }
1411
+ });
1412
+ }
1413
+
1414
+ // src/cli.ts
1415
+ function createProgram() {
1416
+ const program2 = new import_commander.Command();
1417
+ program2.name("affonso").description("Affonso CLI \u2014 manage your affiliate program from the terminal").version("0.1.0").option("--json", "Output as JSON").option("--api-key <key>", "API key for this request").option("--base-url <url>", "Custom API base URL").option("--no-color", "Disable colored output");
1418
+ registerLoginCommand(program2);
1419
+ registerLogoutCommand(program2);
1420
+ registerWhoamiCommand(program2);
1421
+ registerConfigCommands(program2);
1422
+ registerAffiliateCommands(program2);
1423
+ registerReferralCommands(program2);
1424
+ registerClickCommands(program2);
1425
+ registerCommissionCommands(program2);
1426
+ registerCouponCommands(program2);
1427
+ registerPayoutCommands(program2);
1428
+ registerProgramCommands(program2);
1429
+ registerMarketplaceCommands(program2);
1430
+ registerEmbedTokenCommands(program2);
1431
+ return program2;
1432
+ }
1433
+
1434
+ // src/index.ts
1435
+ var program = createProgram();
1436
+ program.parseAsync(process.argv).catch((err) => {
1437
+ const opts2 = program.opts();
1438
+ handleError(err, opts2.json);
1439
+ });