kingminos 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1695 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/kingminos.ts
4
+ import { readFileSync, realpathSync } from "fs";
5
+ import { fileURLToPath } from "url";
6
+
7
+ // src/sanitize.ts
8
+ function stripSafeToWrite(value) {
9
+ if (Array.isArray(value)) {
10
+ return value.map(stripSafeToWrite);
11
+ }
12
+ if (value !== null && typeof value === "object") {
13
+ const out = {};
14
+ for (const [key, child] of Object.entries(value)) {
15
+ if (key === "safe_to_write") {
16
+ continue;
17
+ }
18
+ out[key] = stripSafeToWrite(child);
19
+ }
20
+ return out;
21
+ }
22
+ return value;
23
+ }
24
+
25
+ // src/client.ts
26
+ async function apiRequest(input) {
27
+ const headers = new Headers(input.headers);
28
+ headers.set("x-kingminos-client", "cli");
29
+ if (input.token) {
30
+ headers.set("Authorization", `Bearer ${input.token}`);
31
+ }
32
+ let body;
33
+ if (input.body !== void 0) {
34
+ headers.set("content-type", "application/json");
35
+ body = JSON.stringify(input.body);
36
+ }
37
+ const response = await input.fetch(input.url, {
38
+ method: input.method,
39
+ headers,
40
+ body
41
+ });
42
+ return {
43
+ status: response.status,
44
+ contentType: response.headers.get("content-type") ?? "",
45
+ text: await response.text()
46
+ };
47
+ }
48
+ function formatBody(text, contentType) {
49
+ const trimmed = text.replace(/^\uFEFF/, "");
50
+ if (trimmed === "") {
51
+ return "";
52
+ }
53
+ const jsonLike = contentType.includes("json") || looksLikeJson(trimmed);
54
+ if (jsonLike) {
55
+ try {
56
+ return `${JSON.stringify(stripSafeToWrite(JSON.parse(trimmed)), null, 2)}
57
+ `;
58
+ } catch {
59
+ }
60
+ }
61
+ return trimmed.endsWith("\n") ? trimmed : `${trimmed}
62
+ `;
63
+ }
64
+ function looksLikeJson(text) {
65
+ const start = text.trimStart()[0];
66
+ return start === "{" || start === "[";
67
+ }
68
+
69
+ // src/const.ts
70
+ var DEFAULT_BASE_URL = "https://api.kingminos.com";
71
+ var TOKEN_ENV = "KINGMINOS_API_KEY";
72
+ var BASE_URL_ENV = "KINGMINOS_BASE_URL";
73
+ var BYOK_PROVIDERS = [
74
+ "zoominfo",
75
+ "leadmagic",
76
+ "findymail",
77
+ "wiza",
78
+ "aiark",
79
+ "builtwith",
80
+ "prospeo",
81
+ "apollo",
82
+ "companyurlfinder",
83
+ "peopledatalabs",
84
+ "hginsights",
85
+ "brandfetch",
86
+ "enrichcrm",
87
+ "lemlist",
88
+ "snovio",
89
+ "cognism",
90
+ "lusha",
91
+ "starbridge",
92
+ "clay",
93
+ "databar",
94
+ "quickenrich",
95
+ "getleads",
96
+ "blitzapi"
97
+ ];
98
+ function isByokProvider(value) {
99
+ return BYOK_PROVIDERS.includes(value);
100
+ }
101
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["help", "h", "dry-run", "skip-cache", "json"]);
102
+ var GLOBAL_FLAGS = /* @__PURE__ */ new Set(["token", "base-url", "help", "h", "json"]);
103
+ var EXIT = {
104
+ ok: 0,
105
+ usage: 1,
106
+ unauthorized: 2,
107
+ payment: 3,
108
+ rateLimited: 4,
109
+ server: 5,
110
+ http: 6
111
+ };
112
+
113
+ // src/usage.ts
114
+ var UsageError = class extends Error {
115
+ constructor(message) {
116
+ super(message);
117
+ this.name = "UsageError";
118
+ }
119
+ };
120
+ function assertNever(value, message) {
121
+ throw new Error(message ?? `unexpected value: ${String(value)}`);
122
+ }
123
+
124
+ // src/parse.ts
125
+ var COMMAND_FLAGS = {
126
+ health: /* @__PURE__ */ new Set(),
127
+ openapi: /* @__PURE__ */ new Set(),
128
+ providers: /* @__PURE__ */ new Set(),
129
+ capabilities: /* @__PURE__ */ new Set(),
130
+ recipes: /* @__PURE__ */ new Set(),
131
+ batch: /* @__PURE__ */ new Set(["file", "priority", "explain", "schema", "dry-run", "path"]),
132
+ "company.resolve": /* @__PURE__ */ new Set([
133
+ "body",
134
+ "email",
135
+ "name",
136
+ "domain",
137
+ "website",
138
+ "dry-run",
139
+ "external-ref",
140
+ "routing",
141
+ "compliance",
142
+ "schema-version",
143
+ "idempotency-key",
144
+ "explain",
145
+ "schema",
146
+ "max-provider-credits",
147
+ "path",
148
+ "preset",
149
+ "priority"
150
+ ]),
151
+ "company.domain": /* @__PURE__ */ new Set([
152
+ "body",
153
+ "email",
154
+ "name",
155
+ "company-name",
156
+ "domain",
157
+ "website",
158
+ "city",
159
+ "state",
160
+ "country",
161
+ "postal-code",
162
+ "phone",
163
+ "linkedin-url",
164
+ "family-policy",
165
+ "path",
166
+ "preset",
167
+ "skip-cache",
168
+ "dry-run",
169
+ "external-ref",
170
+ "routing",
171
+ "compliance",
172
+ "schema-version",
173
+ "idempotency-key",
174
+ "explain",
175
+ "schema",
176
+ "max-provider-credits",
177
+ "cache",
178
+ "cache-control",
179
+ "priority"
180
+ ]),
181
+ "company.b2b_social": /* @__PURE__ */ new Set([
182
+ "body",
183
+ "email",
184
+ "name",
185
+ "domain",
186
+ "website",
187
+ "linkedin-url",
188
+ "hint",
189
+ "country",
190
+ "path",
191
+ "preset",
192
+ "dry-run",
193
+ "external-ref",
194
+ "routing",
195
+ "compliance",
196
+ "idempotency-key",
197
+ "explain",
198
+ "schema",
199
+ "max-provider-credits",
200
+ "priority"
201
+ ]),
202
+ "company.icon": /* @__PURE__ */ new Set([
203
+ "body",
204
+ "email",
205
+ "name",
206
+ "domain",
207
+ "website",
208
+ "dry-run",
209
+ "external-ref",
210
+ "compliance",
211
+ "idempotency-key",
212
+ "explain",
213
+ "schema"
214
+ ]),
215
+ "company.description": /* @__PURE__ */ new Set([
216
+ "body",
217
+ "email",
218
+ "name",
219
+ "domain",
220
+ "website",
221
+ "path",
222
+ "preset",
223
+ "max-chars",
224
+ "skip-cache",
225
+ "dry-run",
226
+ "external-ref",
227
+ "routing",
228
+ "compliance",
229
+ "idempotency-key",
230
+ "explain",
231
+ "schema",
232
+ "max-provider-credits",
233
+ "cache",
234
+ "cache-control",
235
+ "priority"
236
+ ]),
237
+ "company.hierarchy": /* @__PURE__ */ new Set([
238
+ "body",
239
+ "email",
240
+ "name",
241
+ "domain",
242
+ "website",
243
+ "country",
244
+ "path",
245
+ "preset",
246
+ "dry-run",
247
+ "external-ref",
248
+ "routing",
249
+ "compliance",
250
+ "idempotency-key",
251
+ "explain",
252
+ "schema",
253
+ "max-provider-credits",
254
+ "priority"
255
+ ]),
256
+ "person.verify_employment": /* @__PURE__ */ new Set([
257
+ "body",
258
+ "email",
259
+ "first-name",
260
+ "last-name",
261
+ "linkedin-url",
262
+ "expected-company",
263
+ "expected-company-domain",
264
+ "expected-company-name",
265
+ "expected-company-website",
266
+ "dry-run",
267
+ "external-ref",
268
+ "routing",
269
+ "compliance",
270
+ "idempotency-key",
271
+ "explain",
272
+ "schema",
273
+ "max-provider-credits",
274
+ "priority"
275
+ ]),
276
+ "person.language": /* @__PURE__ */ new Set([
277
+ "body",
278
+ "email",
279
+ "first-name",
280
+ "last-name",
281
+ "linkedin-url",
282
+ "skip-cache",
283
+ "dry-run",
284
+ "external-ref",
285
+ "compliance",
286
+ "idempotency-key",
287
+ "explain",
288
+ "schema",
289
+ "priority"
290
+ ]),
291
+ "person.phone": /* @__PURE__ */ new Set([
292
+ "body",
293
+ "email",
294
+ "first-name",
295
+ "last-name",
296
+ "linkedin-url",
297
+ "domain",
298
+ "phone-types",
299
+ "max-results",
300
+ "format",
301
+ "skip-cache",
302
+ "dry-run",
303
+ "external-ref",
304
+ "compliance",
305
+ "idempotency-key",
306
+ "explain",
307
+ "schema",
308
+ "cache",
309
+ "cache-control",
310
+ "priority"
311
+ ]),
312
+ "person.title": /* @__PURE__ */ new Set([
313
+ "body",
314
+ "email",
315
+ "first-name",
316
+ "last-name",
317
+ "linkedin-url",
318
+ "company-domain",
319
+ "fields",
320
+ "path",
321
+ "routing",
322
+ "skip-cache",
323
+ "dry-run",
324
+ "external-ref",
325
+ "compliance",
326
+ "idempotency-key",
327
+ "explain",
328
+ "schema",
329
+ "cache",
330
+ "cache-control",
331
+ "priority"
332
+ ]),
333
+ "runs.get": /* @__PURE__ */ new Set(["explain", "schema"]),
334
+ "credentials.put": /* @__PURE__ */ new Set(["body", "api-key", "client-id", "client-secret"]),
335
+ "credentials.delete": /* @__PURE__ */ new Set(),
336
+ "subjects.delete": /* @__PURE__ */ new Set(),
337
+ help: /* @__PURE__ */ new Set()
338
+ };
339
+ var KNOWN_FLAGS = /* @__PURE__ */ new Set([
340
+ ...GLOBAL_FLAGS,
341
+ ...BOOLEAN_FLAGS,
342
+ ...Object.values(COMMAND_FLAGS).flatMap((names) => [...names])
343
+ ]);
344
+ function parseArgv(argv) {
345
+ const positionals = [];
346
+ const flags = {};
347
+ for (let i = 0; i < argv.length; i += 1) {
348
+ const arg = argv[i];
349
+ if (arg === void 0) {
350
+ break;
351
+ }
352
+ if (arg === "--") {
353
+ positionals.push(...argv.slice(i + 1));
354
+ break;
355
+ }
356
+ if (arg.startsWith("--")) {
357
+ const eq = arg.indexOf("=");
358
+ const rawName = eq === -1 ? arg.slice(2) : arg.slice(2, eq);
359
+ const name = rawName.trim();
360
+ if (!name) {
361
+ throw new UsageError("empty flag");
362
+ }
363
+ if (!KNOWN_FLAGS.has(name)) {
364
+ throw new UsageError(`unknown flag --${name}`);
365
+ }
366
+ if (BOOLEAN_FLAGS.has(name)) {
367
+ if (eq !== -1) {
368
+ throw new UsageError(`--${name} does not take a value`);
369
+ }
370
+ flags[name] = true;
371
+ continue;
372
+ }
373
+ const value = eq === -1 ? argv[i + 1] : arg.slice(eq + 1);
374
+ if (eq === -1) {
375
+ i += 1;
376
+ }
377
+ if (value === void 0 || eq === -1 && value.startsWith("--")) {
378
+ throw new UsageError(`--${name} requires a value`);
379
+ }
380
+ flags[name] = value;
381
+ continue;
382
+ }
383
+ if (arg === "-h") {
384
+ flags.help = true;
385
+ continue;
386
+ }
387
+ if (arg.startsWith("-")) {
388
+ throw new UsageError(`unknown flag ${arg}`);
389
+ }
390
+ positionals.push(arg);
391
+ }
392
+ return { positionals, flags };
393
+ }
394
+ function resolveCommand(positionals) {
395
+ if (positionals.length === 0) {
396
+ return { name: "help" };
397
+ }
398
+ const [head, mid, tail, ...rest] = positionals;
399
+ if (rest.length > 0) {
400
+ throw new UsageError(`unexpected arguments: ${rest.join(" ")}`);
401
+ }
402
+ switch (head) {
403
+ case "help":
404
+ return { name: "help", topic: mid };
405
+ case "health":
406
+ case "openapi":
407
+ case "providers":
408
+ case "capabilities":
409
+ case "recipes":
410
+ if (mid !== void 0) {
411
+ throw new UsageError(`${head} does not take arguments`);
412
+ }
413
+ return { name: head };
414
+ case "batch":
415
+ return resolveBatch(mid, tail);
416
+ case "company":
417
+ return resolveCompany(mid, tail);
418
+ case "person":
419
+ if (mid !== "verify-employment" && mid !== "language" && mid !== "phone" && mid !== "title") {
420
+ throw new UsageError("person commands: verify-employment | language | phone | title");
421
+ }
422
+ if (tail !== void 0) {
423
+ throw new UsageError(`person ${mid} does not take positional arguments`);
424
+ }
425
+ if (mid === "language") {
426
+ return { name: "person.language" };
427
+ }
428
+ if (mid === "phone") {
429
+ return { name: "person.phone" };
430
+ }
431
+ if (mid === "title") {
432
+ return { name: "person.title" };
433
+ }
434
+ return { name: "person.verify_employment" };
435
+ case "runs":
436
+ if (mid !== "get" || !tail) {
437
+ throw new UsageError("runs commands: get <id>");
438
+ }
439
+ return { name: "runs.get", id: tail };
440
+ case "credentials":
441
+ if (mid !== "put" && mid !== "delete" || !tail) {
442
+ throw new UsageError("credentials commands: put <provider> | delete <provider>");
443
+ }
444
+ return mid === "put" ? { name: "credentials.put", provider: tail } : { name: "credentials.delete", provider: tail };
445
+ case "subjects":
446
+ if (mid !== "delete" || !tail) {
447
+ throw new UsageError("subjects commands: delete <subject_key>");
448
+ }
449
+ return { name: "subjects.delete", subjectKey: tail };
450
+ default:
451
+ throw new UsageError(`unknown command ${head}`);
452
+ }
453
+ }
454
+ function resolveBatch(mid, tail) {
455
+ if (mid === "company") {
456
+ const inner = resolveCompany(tail, void 0);
457
+ switch (inner.name) {
458
+ case "company.resolve":
459
+ case "company.domain":
460
+ case "company.hierarchy":
461
+ case "company.b2b_social":
462
+ case "company.icon":
463
+ case "company.description":
464
+ return { name: "batch", capability: inner.name };
465
+ default:
466
+ throw new UsageError(
467
+ "batch company commands: resolve | domain | hierarchy | b2b-social | icon | description"
468
+ );
469
+ }
470
+ }
471
+ if (mid === "person") {
472
+ if (tail !== "verify-employment" && tail !== "language" && tail !== "phone" && tail !== "title") {
473
+ throw new UsageError("batch person commands: verify-employment | language | phone | title");
474
+ }
475
+ if (tail === "language") {
476
+ return { name: "batch", capability: "person.language" };
477
+ }
478
+ if (tail === "phone") {
479
+ return { name: "batch", capability: "person.phone" };
480
+ }
481
+ if (tail === "title") {
482
+ return { name: "batch", capability: "person.title" };
483
+ }
484
+ return { name: "batch", capability: "person.verify_employment" };
485
+ }
486
+ throw new UsageError(
487
+ "batch commands: batch <company|person> <command> --file <jsonl> --priority speed|balance|accuracy|coverage"
488
+ );
489
+ }
490
+ function resolveCompany(mid, tail) {
491
+ if (tail !== void 0) {
492
+ throw new UsageError(
493
+ "company <resolve|domain|hierarchy|b2b-social|icon|description> does not take positional arguments"
494
+ );
495
+ }
496
+ switch (mid) {
497
+ case "resolve":
498
+ return { name: "company.resolve" };
499
+ case "domain":
500
+ return { name: "company.domain" };
501
+ case "hierarchy":
502
+ return { name: "company.hierarchy" };
503
+ case "b2b-social":
504
+ return { name: "company.b2b_social" };
505
+ case "icon":
506
+ return { name: "company.icon" };
507
+ case "description":
508
+ return { name: "company.description" };
509
+ default:
510
+ throw new UsageError(
511
+ "company commands: resolve | domain | hierarchy | b2b-social | icon | description"
512
+ );
513
+ }
514
+ }
515
+ function assertKnownFlags(commandName, flags) {
516
+ const allowed = COMMAND_FLAGS[commandName];
517
+ if (!allowed) {
518
+ throw new UsageError(`unknown command ${commandName}`);
519
+ }
520
+ for (const name of Object.keys(flags)) {
521
+ if (GLOBAL_FLAGS.has(name) || allowed.has(name)) {
522
+ continue;
523
+ }
524
+ throw new UsageError(`unknown flag --${name}`);
525
+ }
526
+ }
527
+ function flagString(flags, name) {
528
+ const value = flags[name];
529
+ if (value === void 0 || value === true) {
530
+ return void 0;
531
+ }
532
+ return value;
533
+ }
534
+ function flagBool(flags, name) {
535
+ return flags[name] === true ? true : void 0;
536
+ }
537
+
538
+ // src/priority.ts
539
+ var PRIORITY_HELP = "Before a bulk run, ask which path: speed, balance, accuracy, or coverage. Re-run with --priority speed|balance|accuracy|coverage. Max yield = coverage. company.resolve value is the one-step lookup, not a tier.";
540
+ function parsePriority(value) {
541
+ switch (value) {
542
+ case "speed":
543
+ case "balance":
544
+ case "accuracy":
545
+ case "coverage":
546
+ return value;
547
+ default:
548
+ throw new UsageError(PRIORITY_HELP);
549
+ }
550
+ }
551
+ function pathForPriority(priority) {
552
+ return priority;
553
+ }
554
+ function applyPriority(body, priority, _capability) {
555
+ const path = pathForPriority(priority);
556
+ const routing = body.routing && typeof body.routing === "object" && !Array.isArray(body.routing) ? body.routing : {};
557
+ return { ...body, routing: { ...routing, path } };
558
+ }
559
+ function formatDecisionNotes(body) {
560
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
561
+ return "";
562
+ }
563
+ const record = body;
564
+ const lines = [];
565
+ const confidence = record.answer?.confidence;
566
+ if (confidence === "high" || confidence === "medium" || confidence === "low") {
567
+ lines.push(`Confidence: ${confidence}`);
568
+ } else if (confidence === null) {
569
+ lines.push("Confidence: none");
570
+ }
571
+ const spent = record.usage?.credits?.spent;
572
+ if (typeof spent === "number") {
573
+ lines.push(`Credits spent: ${spent}`);
574
+ }
575
+ return lines.length > 0 ? `
576
+ ${lines.join("\n")}
577
+ ` : "";
578
+ }
579
+ function formatNextStepsHints(body) {
580
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
581
+ return "";
582
+ }
583
+ const steps = body.next_steps;
584
+ if (!Array.isArray(steps) || steps.length === 0) {
585
+ return "";
586
+ }
587
+ const lines = ["", "Next:"];
588
+ for (const raw of steps) {
589
+ if (!raw || typeof raw !== "object") {
590
+ continue;
591
+ }
592
+ const step = raw;
593
+ const credits = typeof step.est_credits === "number" ? ` (~${step.est_credits} credits)` : "";
594
+ const target = step.capability ? ` ${step.capability}` : "";
595
+ lines.push(`- ${step.action ?? "next"}${target}${credits}: ${step.reason ?? ""}`.trim());
596
+ if (step.suggested && typeof step.suggested === "object") {
597
+ lines.push(` suggested: ${JSON.stringify(step.suggested)}`);
598
+ }
599
+ }
600
+ return `${lines.join("\n")}
601
+ `;
602
+ }
603
+
604
+ // src/commands.ts
605
+ function buildRequest(command, flags, loader) {
606
+ switch (command.name) {
607
+ case "help":
608
+ throw new UsageError("help is not an HTTP call");
609
+ case "batch":
610
+ throw new UsageError("batch is not a single HTTP call");
611
+ case "health":
612
+ return { method: "GET", path: "/health", headers: {}, auth: false };
613
+ case "openapi":
614
+ return { method: "GET", path: "/openapi.yaml", headers: {}, auth: false };
615
+ case "providers":
616
+ return { method: "GET", path: "/v1/providers", headers: {}, auth: true };
617
+ case "capabilities":
618
+ return { method: "GET", path: "/v1/capabilities", headers: {}, auth: true };
619
+ case "recipes":
620
+ return { method: "GET", path: "/v1/recipes", headers: {}, auth: true };
621
+ case "company.resolve":
622
+ return capabilityPost("/v1/company/resolve", resolveBody(flags, loader), flags);
623
+ case "company.domain":
624
+ return capabilityPost("/v1/company/domain", domainBody(flags, loader), flags, {
625
+ cache: true
626
+ });
627
+ case "company.hierarchy":
628
+ return capabilityPost("/v1/company/hierarchy", hierarchyBody(flags, loader), flags);
629
+ case "company.b2b_social":
630
+ return capabilityPost("/v1/company/b2b-social", b2bSocialBody(flags, loader), flags);
631
+ case "company.icon":
632
+ return capabilityPost("/v1/company/icon", iconBody(flags, loader), flags);
633
+ case "company.description":
634
+ return capabilityPost("/v1/company/description", descriptionBody(flags, loader), flags, {
635
+ cache: true
636
+ });
637
+ case "person.verify_employment":
638
+ return capabilityPost("/v1/person/verify-employment", employmentBody(flags, loader), flags);
639
+ case "person.language":
640
+ return capabilityPost("/v1/person/language", languageBody(flags, loader), flags);
641
+ case "person.phone":
642
+ return capabilityPost("/v1/person/phone", phoneBody(flags, loader), flags, {
643
+ cache: true
644
+ });
645
+ case "person.title":
646
+ return capabilityPost("/v1/person/title", titleBody(flags, loader), flags, {
647
+ cache: true
648
+ });
649
+ case "runs.get":
650
+ return {
651
+ method: "GET",
652
+ path: `/v1/runs/${encodeURIComponent(command.id)}`,
653
+ headers: explainSchemaHeaders(flags),
654
+ auth: true
655
+ };
656
+ case "credentials.put":
657
+ return {
658
+ method: "PUT",
659
+ path: `/v1/credentials/${encodeURIComponent(parseProvider(command.provider))}`,
660
+ headers: {},
661
+ body: credentialsPutBody(command.provider, flags, loader),
662
+ auth: true
663
+ };
664
+ case "credentials.delete":
665
+ return {
666
+ method: "DELETE",
667
+ path: `/v1/credentials/${encodeURIComponent(parseProvider(command.provider))}`,
668
+ headers: {},
669
+ auth: true
670
+ };
671
+ case "subjects.delete":
672
+ return {
673
+ method: "DELETE",
674
+ path: `/v1/subjects/${encodeURIComponent(command.subjectKey)}`,
675
+ headers: {},
676
+ auth: true
677
+ };
678
+ default:
679
+ return assertNever(command);
680
+ }
681
+ }
682
+ function capabilityPost(path, body, flags, options) {
683
+ return {
684
+ method: "POST",
685
+ path,
686
+ headers: {
687
+ ...explainSchemaHeaders(flags),
688
+ ...optionalHeader("Idempotency-Key", flagString(flags, "idempotency-key")),
689
+ ...optionalHeader("X-Router-Max-Provider-Credits", flagString(flags, "max-provider-credits")),
690
+ ...options?.cache ? {
691
+ ...optionalHeader("X-Router-Cache", flagString(flags, "cache")),
692
+ ...optionalHeader("Cache-Control", flagString(flags, "cache-control"))
693
+ } : {}
694
+ },
695
+ body,
696
+ auth: true
697
+ };
698
+ }
699
+ function explainSchemaHeaders(flags) {
700
+ return {
701
+ ...optionalHeader("X-Router-Explain", flagString(flags, "explain")),
702
+ ...optionalHeader("X-Router-Schema", flagString(flags, "schema"))
703
+ };
704
+ }
705
+ function optionalHeader(name, value) {
706
+ return value === void 0 ? {} : { [name]: value };
707
+ }
708
+ function resolveBody(flags, loader) {
709
+ const body = baseBody(flags, loader);
710
+ assignString(body, "email", flags);
711
+ assignString(body, "name", flags);
712
+ assignString(body, "domain", flags);
713
+ assignString(body, "website", flags);
714
+ assignCommon(body, flags);
715
+ assignJson(body, "routing", flags);
716
+ assignJson(body, "compliance", flags);
717
+ assignString(body, "schema_version", flags, "schema-version");
718
+ assignString(body, "path", flags);
719
+ assignString(body, "preset", flags);
720
+ return withPriority(body, flags, "company.resolve");
721
+ }
722
+ function domainBody(flags, loader) {
723
+ const body = baseBody(flags, loader);
724
+ assignString(body, "email", flags);
725
+ assignString(body, "name", flags);
726
+ assignString(body, "company_name", flags, "company-name");
727
+ assignString(body, "domain", flags);
728
+ assignString(body, "website", flags);
729
+ assignString(body, "city", flags);
730
+ assignString(body, "state", flags);
731
+ assignString(body, "country", flags);
732
+ assignString(body, "postal_code", flags, "postal-code");
733
+ assignString(body, "phone", flags);
734
+ assignString(body, "linkedin_url", flags, "linkedin-url");
735
+ assignString(body, "family_policy", flags, "family-policy");
736
+ assignString(body, "path", flags);
737
+ assignString(body, "preset", flags);
738
+ if (flagBool(flags, "skip-cache")) {
739
+ body.skip_cache = true;
740
+ }
741
+ assignCommon(body, flags);
742
+ assignJson(body, "routing", flags);
743
+ assignJson(body, "compliance", flags);
744
+ assignString(body, "schema_version", flags, "schema-version");
745
+ return withPriority(body, flags, "company.domain");
746
+ }
747
+ function b2bSocialBody(flags, loader) {
748
+ const body = baseBody(flags, loader);
749
+ assignString(body, "email", flags);
750
+ assignString(body, "name", flags);
751
+ assignString(body, "domain", flags);
752
+ assignString(body, "website", flags);
753
+ assignString(body, "linkedin_url", flags, "linkedin-url");
754
+ assignString(body, "hint", flags);
755
+ assignString(body, "country", flags);
756
+ assignString(body, "path", flags);
757
+ assignString(body, "preset", flags);
758
+ assignCommon(body, flags);
759
+ assignJson(body, "routing", flags);
760
+ assignJson(body, "compliance", flags);
761
+ return withPriority(body, flags, "company.b2b_social");
762
+ }
763
+ function iconBody(flags, loader) {
764
+ const body = baseBody(flags, loader);
765
+ assignString(body, "email", flags);
766
+ assignString(body, "name", flags);
767
+ assignString(body, "domain", flags);
768
+ assignString(body, "website", flags);
769
+ assignCommon(body, flags);
770
+ assignJson(body, "compliance", flags);
771
+ return body;
772
+ }
773
+ function descriptionBody(flags, loader) {
774
+ const body = baseBody(flags, loader);
775
+ assignString(body, "email", flags);
776
+ assignString(body, "name", flags);
777
+ assignString(body, "domain", flags);
778
+ assignString(body, "website", flags);
779
+ assignString(body, "path", flags);
780
+ assignString(body, "preset", flags);
781
+ assignInteger(body, "max_chars", flags, "max-chars", 80, 500);
782
+ if (flagBool(flags, "skip-cache")) {
783
+ body.skip_cache = true;
784
+ }
785
+ assignCommon(body, flags);
786
+ assignJson(body, "routing", flags);
787
+ assignJson(body, "compliance", flags);
788
+ return withPriority(body, flags, "company.description");
789
+ }
790
+ function hierarchyBody(flags, loader) {
791
+ const body = baseBody(flags, loader);
792
+ assignString(body, "email", flags);
793
+ assignString(body, "name", flags);
794
+ assignString(body, "domain", flags);
795
+ assignString(body, "website", flags);
796
+ assignString(body, "country", flags);
797
+ assignString(body, "path", flags);
798
+ assignString(body, "preset", flags);
799
+ assignCommon(body, flags);
800
+ assignJson(body, "routing", flags);
801
+ assignJson(body, "compliance", flags);
802
+ return withPriority(body, flags, "company.hierarchy");
803
+ }
804
+ function employmentBody(flags, loader) {
805
+ const body = baseBody(flags, loader);
806
+ assignString(body, "email", flags);
807
+ assignString(body, "first_name", flags, "first-name");
808
+ assignString(body, "last_name", flags, "last-name");
809
+ assignString(body, "linkedin_url", flags, "linkedin-url");
810
+ const expected = expectedCompany(flags);
811
+ if (expected) {
812
+ body.expected_company = expected;
813
+ }
814
+ assignCommon(body, flags);
815
+ assignJson(body, "routing", flags);
816
+ assignJson(body, "compliance", flags);
817
+ return withPriority(body, flags, "person.verify_employment");
818
+ }
819
+ function languageBody(flags, loader) {
820
+ const body = baseBody(flags, loader);
821
+ assignString(body, "email", flags);
822
+ assignString(body, "linkedin_url", flags, "linkedin-url");
823
+ assignString(body, "first_name", flags, "first-name");
824
+ assignString(body, "last_name", flags, "last-name");
825
+ if (flagBool(flags, "skip-cache")) {
826
+ body.skip_cache = true;
827
+ }
828
+ assignCommon(body, flags);
829
+ assignJson(body, "compliance", flags);
830
+ return withPriority(body, flags, "person.language");
831
+ }
832
+ function phoneBody(flags, loader) {
833
+ const body = baseBody(flags, loader);
834
+ assignString(body, "email", flags);
835
+ assignString(body, "linkedin_url", flags, "linkedin-url");
836
+ assignString(body, "first_name", flags, "first-name");
837
+ assignString(body, "last_name", flags, "last-name");
838
+ assignString(body, "domain", flags);
839
+ const phoneTypes = flagString(flags, "phone-types");
840
+ if (phoneTypes !== void 0) {
841
+ const types = phoneTypes.split(",").map((type) => type.trim()).filter(Boolean);
842
+ if (types.length > 0) {
843
+ body.phone_types = types;
844
+ }
845
+ }
846
+ const maxResults = Number(flagString(flags, "max-results"));
847
+ if (Number.isInteger(maxResults) && maxResults >= 1) {
848
+ body.max_results = maxResults;
849
+ }
850
+ assignString(body, "format", flags);
851
+ if (flagBool(flags, "skip-cache")) {
852
+ body.skip_cache = true;
853
+ }
854
+ assignCommon(body, flags);
855
+ assignJson(body, "compliance", flags);
856
+ return withPriority(body, flags, "person.phone");
857
+ }
858
+ function titleBody(flags, loader) {
859
+ const body = baseBody(flags, loader);
860
+ assignString(body, "email", flags);
861
+ assignString(body, "linkedin_url", flags, "linkedin-url");
862
+ assignString(body, "first_name", flags, "first-name");
863
+ assignString(body, "last_name", flags, "last-name");
864
+ assignString(body, "company_domain", flags, "company-domain");
865
+ const fields = flagString(flags, "fields");
866
+ if (fields !== void 0) {
867
+ const list = fields.split(",").map((field) => field.trim()).filter(Boolean);
868
+ if (list.length > 0) {
869
+ body.fields = list;
870
+ }
871
+ }
872
+ if (flagBool(flags, "skip-cache")) {
873
+ body.skip_cache = true;
874
+ }
875
+ assignCommon(body, flags);
876
+ assignJson(body, "routing", flags);
877
+ const path = flagString(flags, "path");
878
+ if (path !== void 0) {
879
+ const routing = body.routing && typeof body.routing === "object" && !Array.isArray(body.routing) ? body.routing : {};
880
+ body.routing = { ...routing, path };
881
+ }
882
+ assignJson(body, "compliance", flags);
883
+ return withPriority(body, flags, "person.title");
884
+ }
885
+ function expectedCompany(flags) {
886
+ const fromJson = parseJsonFlag(flags, "expected-company");
887
+ const company = fromJson ? { ...fromJson } : {};
888
+ const domain = flagString(flags, "expected-company-domain");
889
+ const name = flagString(flags, "expected-company-name");
890
+ const website = flagString(flags, "expected-company-website");
891
+ if (domain !== void 0) {
892
+ company.domain = domain;
893
+ }
894
+ if (name !== void 0) {
895
+ company.name = name;
896
+ }
897
+ if (website !== void 0) {
898
+ company.website = website;
899
+ }
900
+ return Object.keys(company).length > 0 ? company : void 0;
901
+ }
902
+ function withPriority(body, flags, capability) {
903
+ const raw = flagString(flags, "priority");
904
+ if (raw === void 0) {
905
+ return body;
906
+ }
907
+ return applyPriority(body, parsePriority(raw), capability);
908
+ }
909
+ function assignCommon(body, flags) {
910
+ if (flagBool(flags, "dry-run")) {
911
+ body.dry_run = true;
912
+ }
913
+ assignString(body, "external_ref", flags, "external-ref");
914
+ }
915
+ function assignString(body, field, flags, flagName = field.replaceAll("_", "-")) {
916
+ const value = flagString(flags, flagName);
917
+ if (value !== void 0) {
918
+ body[field] = value;
919
+ }
920
+ }
921
+ function assignInteger(body, field, flags, flagName, min, max) {
922
+ const raw = flagString(flags, flagName);
923
+ if (raw === void 0) {
924
+ return;
925
+ }
926
+ const value = Number(raw);
927
+ if (!Number.isInteger(value)) {
928
+ throw new UsageError(`--${flagName} must be an integer`);
929
+ }
930
+ if (value < min || value > max) {
931
+ throw new UsageError(`--${flagName} must be between ${min} and ${max}`);
932
+ }
933
+ body[field] = value;
934
+ }
935
+ function assignJson(body, field, flags) {
936
+ const parsed = parseJsonFlag(flags, field);
937
+ if (parsed) {
938
+ body[field] = parsed;
939
+ }
940
+ }
941
+ function parseJsonFlag(flags, name) {
942
+ const raw = flagString(flags, name);
943
+ if (raw === void 0) {
944
+ return void 0;
945
+ }
946
+ return parseObjectJson(raw, `--${name}`);
947
+ }
948
+ function baseBody(flags, loader) {
949
+ const raw = flagString(flags, "body");
950
+ if (raw === void 0) {
951
+ return {};
952
+ }
953
+ return { ...loadBodyObject(raw, loader) };
954
+ }
955
+ function loadBodyObject(raw, loader) {
956
+ if (raw === "-") {
957
+ return parseObjectJson(loader.stdin, "--body -");
958
+ }
959
+ if (raw.startsWith("@")) {
960
+ const path = raw.slice(1);
961
+ if (!path) {
962
+ throw new UsageError("--body @file requires a path");
963
+ }
964
+ return parseObjectJson(loader.readFile(path), `--body @${path}`);
965
+ }
966
+ return parseObjectJson(raw, "--body");
967
+ }
968
+ function parseObjectJson(raw, label) {
969
+ let parsed;
970
+ try {
971
+ parsed = JSON.parse(raw);
972
+ } catch {
973
+ throw new UsageError(`${label} is not valid JSON`);
974
+ }
975
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
976
+ throw new UsageError(`${label} must be a JSON object`);
977
+ }
978
+ return parsed;
979
+ }
980
+ function parseProvider(provider) {
981
+ if (provider === "websearch") {
982
+ throw new UsageError("websearch is house-only (byok_not_supported)");
983
+ }
984
+ if (!isByokProvider(provider)) {
985
+ throw new UsageError(`unknown_provider: ${provider}`);
986
+ }
987
+ return provider;
988
+ }
989
+ function credentialsPutBody(provider, flags, loader) {
990
+ const rawBody = flagString(flags, "body");
991
+ const fromFile = rawBody ? loadBodyObject(rawBody, loader) : void 0;
992
+ if (provider === "zoominfo") {
993
+ const clientId = flagString(flags, "client-id") ?? stringField(fromFile, "clientId");
994
+ const clientSecret = flagString(flags, "client-secret") ?? stringField(fromFile, "clientSecret");
995
+ if (!clientId || !clientSecret) {
996
+ throw new UsageError(
997
+ "credentials put zoominfo requires --client-id and --client-secret (or --body { clientId, clientSecret })"
998
+ );
999
+ }
1000
+ return { clientId, clientSecret };
1001
+ }
1002
+ if (fromFile && flagString(flags, "api-key") === void 0 && (stringField(fromFile, "apiKey") || stringField(fromFile, "api_key"))) {
1003
+ return fromFile;
1004
+ }
1005
+ const apiKey = flagString(flags, "api-key") ?? stringField(fromFile, "apiKey") ?? stringField(fromFile, "api_key");
1006
+ if (!apiKey) {
1007
+ throw new UsageError(`credentials put ${provider} requires --api-key (or --body { apiKey })`);
1008
+ }
1009
+ return { apiKey };
1010
+ }
1011
+ function stringField(body, key) {
1012
+ const value = body?.[key];
1013
+ return typeof value === "string" && value !== "" ? value : void 0;
1014
+ }
1015
+
1016
+ // src/help.ts
1017
+ var ROOT_HELP = `King Minos CLI \u2014 KingMinos by OutboundSync
1018
+
1019
+ Usage:
1020
+ kingminos [--token <key>] [--base-url <url>] [--json] <command>
1021
+
1022
+ Auth:
1023
+ Pass a tenant Bearer with --token or ${TOKEN_ENV}.
1024
+ Tenant keys use the osr_ prefix. App-minted user tokens use km_.
1025
+ Mint a key at https://app.kingminos.com after sign-in.
1026
+ The CLI never stores or logs a key.
1027
+
1028
+ API:
1029
+ Default ${DEFAULT_BASE_URL}
1030
+ Override with --base-url or ${BASE_URL_ENV}
1031
+
1032
+ Global flags:
1033
+ --token <key> Bearer token (overrides ${TOKEN_ENV})
1034
+ --base-url <url> API host
1035
+ --json Print the response body only (no Next: hints)
1036
+ --help, -h This help
1037
+
1038
+ Commands (live OpenAPI only):
1039
+ health GET /health
1040
+ openapi GET /openapi.yaml
1041
+ providers GET /v1/providers
1042
+ capabilities GET /v1/capabilities
1043
+ recipes GET /v1/recipes
1044
+ company resolve POST /v1/company/resolve
1045
+ company domain POST /v1/company/domain
1046
+ company hierarchy POST /v1/company/hierarchy
1047
+ company b2b-social POST /v1/company/b2b-social
1048
+ company icon POST /v1/company/icon
1049
+ company description POST /v1/company/description
1050
+ person verify-employment POST /v1/person/verify-employment
1051
+ person language POST /v1/person/language
1052
+ person phone POST /v1/person/phone
1053
+ person title POST /v1/person/title
1054
+ runs get <id> GET /v1/runs/{id}
1055
+ credentials put <provider> PUT /v1/credentials/{provider}
1056
+ credentials delete <provider> DELETE /v1/credentials/{provider}
1057
+ subjects delete <subject_key> DELETE /v1/subjects/{subject_key}
1058
+ batch <company|person> <cmd> POST each JSONL/JSON-array row (requires --file --priority)
1059
+
1060
+ Tiers:
1061
+ --priority speed|balance|accuracy|coverage maps to routing.path.
1062
+ Ask which path before a bulk run. Max yield = coverage.
1063
+ company.resolve value is the one-step lookup, not a tier.
1064
+
1065
+ Credits:
1066
+ Billing is for delivered answers. Print usage.credits.spent from the response.
1067
+ The CLI does not hardcode prices.
1068
+
1069
+ Your Keys providers: ${BYOK_PROVIDERS.join(", ")}
1070
+ Websearch is house-only (no credentials command).
1071
+ LeadMagic, Wiza, Findymail, and AI Ark are house-key vendors. ZoomInfo, BuiltWith,
1072
+ Prospeo, Apollo, Company URL Finder, People Data Labs, HG Insights, Brandfetch,
1073
+ and Enrich-CRM stay BYOK.
1074
+
1075
+ Exit codes: 0 ok \xB7 1 usage \xB7 2 unauthorized (401) \xB7 3 out of credits (402)
1076
+ 4 rate limited (429) \xB7 5 server (5xx) \xB7 6 other HTTP error
1077
+
1078
+ See kingminos <command> --help for request fields on that call.
1079
+ `;
1080
+ function commandHelp(command) {
1081
+ switch (command) {
1082
+ case "health":
1083
+ return `kingminos health
1084
+
1085
+ GET /health (no auth)
1086
+ `;
1087
+ case "openapi":
1088
+ return `kingminos openapi
1089
+
1090
+ GET /openapi.yaml (no auth)
1091
+ Prints the live OpenAPI document.
1092
+ `;
1093
+ case "providers":
1094
+ return `kingminos providers
1095
+
1096
+ GET /v1/providers
1097
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1098
+ `;
1099
+ case "capabilities":
1100
+ return `kingminos capabilities
1101
+
1102
+ GET /v1/capabilities
1103
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1104
+ `;
1105
+ case "recipes":
1106
+ return `kingminos recipes
1107
+
1108
+ GET /v1/recipes
1109
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1110
+ Machine-readable chaining workflows. Ask speed / balance / accuracy / coverage before a bulk run. Max yield = coverage.
1111
+ `;
1112
+ case "batch":
1113
+ return `kingminos batch <company|person> <command> --file <path|-> --priority speed|balance|accuracy|coverage
1114
+
1115
+ Client-side loop over JSONL (or a JSON array) of request bodies. Not a bulk API.
1116
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1117
+ Ask speed / balance / accuracy / coverage before a list run; --priority is required. Max yield = coverage.
1118
+ `;
1119
+ case "company.resolve":
1120
+ return `kingminos company resolve
1121
+
1122
+ POST /v1/company/resolve
1123
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1124
+
1125
+ Body fields: --email --name --domain --website --dry-run --external-ref
1126
+ --path --preset --priority speed|balance|accuracy|coverage
1127
+ --routing <json> --compliance <json> --schema-version 2
1128
+ --body <json|@file|->
1129
+ Headers: --idempotency-key --explain default|full|minimal
1130
+ --schema 2 --max-provider-credits <n>
1131
+ `;
1132
+ case "company.domain":
1133
+ return `kingminos company domain
1134
+
1135
+ POST /v1/company/domain
1136
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1137
+
1138
+ Body fields: --email --name --company-name --domain --website
1139
+ --city --state --country --postal-code --phone --linkedin-url
1140
+ --family-policy collapse_brand|separate_regional
1141
+ --path --preset --priority speed|balance|accuracy|coverage
1142
+ --skip-cache --dry-run --external-ref
1143
+ --routing <json> --compliance <json> --schema-version 2
1144
+ --body <json|@file|->
1145
+ Headers: --idempotency-key --explain default|full|minimal --schema 2
1146
+ --max-provider-credits <n> --cache bypass
1147
+ --cache-control no-cache|no-store
1148
+ `;
1149
+ case "company.b2b_social":
1150
+ return `kingminos company b2b-social
1151
+
1152
+ POST /v1/company/b2b-social
1153
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1154
+
1155
+ Decide the company's B2B social page URL (result.linkedin_url).
1156
+ Pass --domain (required). --name is optional ranking only.
1157
+ --path speed is the bulk-list path (homepage-first, then ranked search).
1158
+ Delivered answers are billed; misses are not. See usage.credits.spent.
1159
+ Do not use company resolve for a B2B social page.
1160
+
1161
+ Body fields: --email --name --domain --website --linkedin-url
1162
+ --hint --country
1163
+ --path --preset --priority speed|balance|accuracy|coverage
1164
+ --dry-run --external-ref
1165
+ --routing <json> --compliance <json>
1166
+ --body <json|@file|->
1167
+ Headers: --idempotency-key --explain default|full|minimal
1168
+ --schema 2 --max-provider-credits <n>
1169
+ `;
1170
+ case "company.icon":
1171
+ return `kingminos company icon
1172
+
1173
+ POST /v1/company/icon
1174
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1175
+
1176
+ Body fields: --email --name --domain --website
1177
+ --dry-run --external-ref
1178
+ --compliance <json>
1179
+ --body <json|@file|->
1180
+ Headers: --idempotency-key --explain default|full|minimal
1181
+ --schema 2
1182
+ `;
1183
+ case "company.description":
1184
+ return `kingminos company description
1185
+
1186
+ POST /v1/company/description
1187
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1188
+
1189
+ Body fields: --email --name --domain --website
1190
+ --path --preset --priority speed|balance|accuracy|coverage
1191
+ --max-chars <80-500> --skip-cache --dry-run --external-ref
1192
+ --routing <json> --compliance <json>
1193
+ --body <json|@file|->
1194
+ Headers: --idempotency-key --explain default|full|minimal
1195
+ --schema 2 --max-provider-credits <n>
1196
+ --cache bypass --cache-control no-cache|no-store
1197
+ `;
1198
+ case "company.hierarchy":
1199
+ return `kingminos company hierarchy
1200
+
1201
+ POST /v1/company/hierarchy
1202
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1203
+
1204
+ Body fields: --email --name --domain --website --country
1205
+ --path --preset --priority speed|balance|accuracy|coverage
1206
+ --dry-run --external-ref
1207
+ --routing <json> --compliance <json>
1208
+ --body <json|@file|->
1209
+ Headers: --idempotency-key --explain default|full|minimal
1210
+ --schema 2 --max-provider-credits <n>
1211
+ `;
1212
+ case "person.verify_employment":
1213
+ return `kingminos person verify-employment
1214
+
1215
+ POST /v1/person/verify-employment
1216
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1217
+
1218
+ Body fields: --email --first-name --last-name --linkedin-url
1219
+ --expected-company <json>
1220
+ --expected-company-domain --expected-company-name
1221
+ --expected-company-website --priority speed|balance|accuracy|coverage
1222
+ --dry-run --external-ref --routing <json> --compliance <json>
1223
+ --body <json|@file|->
1224
+ Headers: --idempotency-key --explain default|full|minimal
1225
+ --schema 2 --max-provider-credits <n>
1226
+ `;
1227
+ case "person.language":
1228
+ return `kingminos person language
1229
+
1230
+ POST /v1/person/language
1231
+ Can we engage this person in English, and if not, in what language?
1232
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1233
+
1234
+ Body fields: --email | --linkedin-url (one required)
1235
+ --first-name --last-name (match-only: rejects a mismatched profile)
1236
+ --priority speed|balance|accuracy --skip-cache
1237
+ --dry-run --external-ref --compliance <json>
1238
+ --body <json|@file|->
1239
+ Headers: --idempotency-key --explain default|full|minimal --schema 2
1240
+ `;
1241
+ case "person.phone":
1242
+ return `kingminos person phone
1243
+
1244
+ POST /v1/person/phone
1245
+ One dialable phone number for a person, plus the ranked rest.
1246
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1247
+
1248
+ Body fields: --email | --linkedin-url (one required)
1249
+ --first-name --last-name (match-only: rejects a mismatched profile)
1250
+ --domain
1251
+ --phone-types mobile,landline,other,unknown (omit = any)
1252
+ --max-results <1-10> (default 3)
1253
+ --format e164|international|national|digits|digits_national
1254
+ (fills \`number\`; default e164; every phone also has \`formats\`)
1255
+ --priority speed|balance|accuracy|coverage
1256
+ --skip-cache --dry-run --external-ref --compliance <json>
1257
+ --body <json|@file|->
1258
+ Headers: --idempotency-key --explain default|full|minimal --schema 2
1259
+ --cache bypass --cache-control no-cache|no-store
1260
+ `;
1261
+ case "person.title":
1262
+ return `kingminos person title
1263
+
1264
+ POST /v1/person/title
1265
+ A person's current job title, seniority and function.
1266
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1267
+
1268
+ Body fields: --email | --linkedin-url (one required)
1269
+ --first-name --last-name (match-only: rejects a mismatched profile)
1270
+ --company-domain (match-only current employer)
1271
+ --fields title,seniority,function (omit = all three)
1272
+ --path speed|balance|accuracy|coverage
1273
+ --priority speed|balance|accuracy|coverage
1274
+ --routing <json> (only / ignore)
1275
+ --skip-cache --dry-run --external-ref --compliance <json>
1276
+ --body <json|@file|->
1277
+ Headers: --idempotency-key --explain default|full|minimal --schema 2
1278
+ --cache bypass --cache-control no-cache|no-store
1279
+ Delivered answers are billed; misses are not. See usage.credits.spent.
1280
+ `;
1281
+ case "runs.get":
1282
+ return `kingminos runs get <id>
1283
+
1284
+ GET /v1/runs/{id}
1285
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1286
+
1287
+ Headers: --explain default|full|minimal --schema 2
1288
+ `;
1289
+ case "credentials.put":
1290
+ return `kingminos credentials put <provider>
1291
+
1292
+ PUT /v1/credentials/{provider}
1293
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1294
+ Providers: ${BYOK_PROVIDERS.join(", ")}
1295
+ Websearch is house-only (byok_not_supported).
1296
+
1297
+ zoominfo: --client-id --client-secret \u2192 { clientId, clientSecret }
1298
+ others: --api-key \u2192 { apiKey }
1299
+ or --body <json|@file|-> (api_key alias accepted by the API)
1300
+
1301
+ Responses never include the raw key. This CLI does not store or log it.
1302
+ `;
1303
+ case "credentials.delete":
1304
+ return `kingminos credentials delete <provider>
1305
+
1306
+ DELETE /v1/credentials/{provider}
1307
+ Requires tenant Bearer (--token or ${TOKEN_ENV}).
1308
+ Providers: ${BYOK_PROVIDERS.join(", ")}
1309
+ `;
1310
+ case "subjects.delete":
1311
+ return `kingminos subjects delete <subject_key>
1312
+
1313
+ DELETE /v1/subjects/{subject_key}
1314
+ Requires tenant Bearer with erase scope (--token or ${TOKEN_ENV}).
1315
+ Company: registrable domain. Person: email:<addr> or li:<slug>.
1316
+ `;
1317
+ default:
1318
+ return ROOT_HELP;
1319
+ }
1320
+ }
1321
+
1322
+ // src/http-error.ts
1323
+ function classifyHttpError(status, body) {
1324
+ const extra = errorSuffix(body);
1325
+ if (status === 401) {
1326
+ return {
1327
+ exitCode: EXIT.unauthorized,
1328
+ message: `Unauthorized (401)${authDetail(body)}. Set KINGMINOS_API_KEY or pass --token.${extra}`
1329
+ };
1330
+ }
1331
+ if (status === 402) {
1332
+ return {
1333
+ exitCode: EXIT.payment,
1334
+ message: `Out of credits (402). Add credits at https://app.kingminos.com.${extra}`
1335
+ };
1336
+ }
1337
+ if (status === 429) {
1338
+ const budget = isBudgetError(body);
1339
+ return {
1340
+ exitCode: EXIT.rateLimited,
1341
+ message: budget ? `Out of credits or daily key budget (429). Retry after Retry-After, or add credits.${extra}` : `Rate limited (429). Retry after the Retry-After header.${extra}`
1342
+ };
1343
+ }
1344
+ if (status >= 500) {
1345
+ return {
1346
+ exitCode: EXIT.server,
1347
+ message: `KingMinos server error (${status}). Retry the same Idempotency-Key if you sent one.${extra}`
1348
+ };
1349
+ }
1350
+ return {
1351
+ exitCode: EXIT.http,
1352
+ message: `HTTP ${status}${extra}`
1353
+ };
1354
+ }
1355
+ function authDetail(body) {
1356
+ const detail = stringField2(body, "detail");
1357
+ switch (detail) {
1358
+ case "missing":
1359
+ return ": missing Bearer token";
1360
+ case "malformed":
1361
+ return ": malformed Bearer token (send `Authorization: Bearer <key>`, not a bare key)";
1362
+ case "mismatch":
1363
+ return ": API key did not match";
1364
+ case void 0:
1365
+ return ": missing, malformed, or mismatched API key";
1366
+ default:
1367
+ return `: ${detail}`;
1368
+ }
1369
+ }
1370
+ function isBudgetError(body) {
1371
+ const error = stringField2(body, "error") ?? "";
1372
+ return error === "key_budget_exhausted" || error === "tenant_budget_exhausted" || error.includes("budget") || error.includes("credit");
1373
+ }
1374
+ function errorSuffix(body) {
1375
+ const error = stringField2(body, "error");
1376
+ const hint = stringField2(body, "hint");
1377
+ const parts = [error, hint].filter((part) => Boolean(part));
1378
+ return parts.length > 0 ? ` ${parts.join(" \u2014 ")}` : "";
1379
+ }
1380
+ function stringField2(body, key) {
1381
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
1382
+ return void 0;
1383
+ }
1384
+ const value = body[key];
1385
+ return typeof value === "string" && value !== "" ? value : void 0;
1386
+ }
1387
+
1388
+ // src/kingminos.ts
1389
+ async function runCli(io) {
1390
+ try {
1391
+ return await runCliUnsafe(io);
1392
+ } catch (error) {
1393
+ const secrets = collectSecrets(io);
1394
+ if (error instanceof UsageError) {
1395
+ io.stderr(`${redact(error.message, secrets)}
1396
+ `);
1397
+ return EXIT.usage;
1398
+ }
1399
+ const message = error instanceof Error ? error.message : String(error);
1400
+ io.stderr(`${redact(message, secrets)}
1401
+ `);
1402
+ return EXIT.usage;
1403
+ }
1404
+ }
1405
+ async function runCliUnsafe(io) {
1406
+ const parsed = parseArgv(io.argv);
1407
+ if (parsed.flags.help === true || parsed.flags.h === true) {
1408
+ if (parsed.positionals.length === 0) {
1409
+ io.stdout(ROOT_HELP);
1410
+ return 0;
1411
+ }
1412
+ const command2 = resolveCommand(parsed.positionals);
1413
+ io.stdout(command2.name === "help" ? ROOT_HELP : commandHelp(command2.name));
1414
+ return 0;
1415
+ }
1416
+ const command = resolveCommand(parsed.positionals);
1417
+ if (command.name === "help") {
1418
+ io.stdout(command.topic ? commandHelp(helpTopic(command.topic)) : ROOT_HELP);
1419
+ return 0;
1420
+ }
1421
+ assertKnownFlags(command.name, parsed.flags);
1422
+ if (command.name === "batch") {
1423
+ return runBatch(io, command, parsed.flags);
1424
+ }
1425
+ const request = buildRequest(command, parsed.flags, {
1426
+ readFile: io.readFile ?? ((path) => readFileSync(path, "utf8")),
1427
+ stdin: io.stdin ?? ""
1428
+ });
1429
+ const token = request.auth ? requireToken(parsed.flags, io.env) : void 0;
1430
+ const baseUrl = resolveBaseUrl(parsed.flags, io.env);
1431
+ const response = await apiRequest({
1432
+ fetch: io.fetch,
1433
+ method: request.method,
1434
+ url: `${baseUrl}${request.path}`,
1435
+ token,
1436
+ headers: request.headers,
1437
+ body: request.body
1438
+ });
1439
+ const formatted = formatBody(response.text, response.contentType);
1440
+ const secrets = collectSecrets(io, parsed.flags);
1441
+ const asJson = flagBool(parsed.flags, "json") === true;
1442
+ if (response.status >= 400) {
1443
+ return writeHttpError(io, response.status, response.text, formatted, secrets, asJson);
1444
+ }
1445
+ io.stdout(redact(formatted, secrets));
1446
+ if (!asJson) {
1447
+ const parsedBody = parseJson(response.text);
1448
+ io.stdout(redact(formatDecisionNotes(parsedBody), secrets));
1449
+ io.stdout(redact(formatNextStepsHints(parsedBody), secrets));
1450
+ }
1451
+ return EXIT.ok;
1452
+ }
1453
+ function writeHttpError(io, status, rawText, formatted, secrets, asJson) {
1454
+ const classified = classifyHttpError(status, parseJson(rawText));
1455
+ if (asJson) {
1456
+ io.stderr(redact(formatted, secrets));
1457
+ } else {
1458
+ io.stderr(redact(`${classified.message}
1459
+ ${formatted}`, secrets));
1460
+ }
1461
+ return classified.exitCode;
1462
+ }
1463
+ function parseJson(text) {
1464
+ try {
1465
+ return JSON.parse(text);
1466
+ } catch {
1467
+ return null;
1468
+ }
1469
+ }
1470
+ async function runBatch(io, command, flags) {
1471
+ const rawPriority = flagString(flags, "priority");
1472
+ if (!rawPriority) {
1473
+ throw new UsageError(
1474
+ "Before a bulk run, ask which path: speed, balance, accuracy, or coverage. Re-run with --priority speed|balance|accuracy|coverage. Max yield = coverage."
1475
+ );
1476
+ }
1477
+ const priority = parsePriority(rawPriority);
1478
+ const file = flagString(flags, "file");
1479
+ if (!file) {
1480
+ throw new UsageError(
1481
+ "batch requires --file <path|-> (JSONL or a JSON array of request bodies)"
1482
+ );
1483
+ }
1484
+ const loader = {
1485
+ readFile: io.readFile ?? ((path) => readFileSync(path, "utf8")),
1486
+ stdin: io.stdin ?? ""
1487
+ };
1488
+ const raw = file === "-" ? loader.stdin : loader.readFile(file);
1489
+ const rows = parseBatchRows(raw);
1490
+ if (rows.length === 0) {
1491
+ throw new UsageError("batch file is empty");
1492
+ }
1493
+ const token = requireToken(flags, io.env);
1494
+ const baseUrl = resolveBaseUrl(flags, io.env);
1495
+ const secrets = collectSecrets(io, flags);
1496
+ const asJson = flagBool(flags, "json") === true;
1497
+ let worst = EXIT.ok;
1498
+ for (const [index, row] of rows.entries()) {
1499
+ const body = applyPriority(row, priority, command.capability);
1500
+ const request = buildRequest({ name: command.capability }, flags, {
1501
+ ...loader,
1502
+ stdin: JSON.stringify(body)
1503
+ });
1504
+ request.body = body;
1505
+ const response = await apiRequest({
1506
+ fetch: io.fetch,
1507
+ method: request.method,
1508
+ url: `${baseUrl}${request.path}`,
1509
+ token,
1510
+ headers: request.headers,
1511
+ body: request.body
1512
+ });
1513
+ const formatted = formatBody(response.text, response.contentType);
1514
+ if (response.status >= 400) {
1515
+ const prefix = asJson ? "" : `row ${index + 1} `;
1516
+ const code = writeHttpError(
1517
+ io,
1518
+ response.status,
1519
+ response.text,
1520
+ `${prefix}${formatted}`,
1521
+ secrets,
1522
+ asJson
1523
+ );
1524
+ worst = Math.max(worst, code);
1525
+ continue;
1526
+ }
1527
+ if (asJson) {
1528
+ io.stdout(redact(formatted, secrets));
1529
+ } else {
1530
+ io.stdout(redact(`# row ${index + 1}
1531
+ ${formatted}`, secrets));
1532
+ const parsedBody = parseJson(response.text);
1533
+ io.stdout(redact(formatDecisionNotes(parsedBody), secrets));
1534
+ io.stdout(redact(formatNextStepsHints(parsedBody), secrets));
1535
+ }
1536
+ }
1537
+ return worst;
1538
+ }
1539
+ function parseBatchRows(raw) {
1540
+ const trimmed = raw.trim();
1541
+ if (!trimmed) {
1542
+ return [];
1543
+ }
1544
+ if (trimmed.startsWith("[")) {
1545
+ const parsed = JSON.parse(trimmed);
1546
+ if (!Array.isArray(parsed)) {
1547
+ throw new UsageError("--file JSON array is invalid");
1548
+ }
1549
+ return parsed.map((row, index) => {
1550
+ if (!row || typeof row !== "object" || Array.isArray(row)) {
1551
+ throw new UsageError(`--file row ${index + 1} must be a JSON object`);
1552
+ }
1553
+ return row;
1554
+ });
1555
+ }
1556
+ return trimmed.split(/\r?\n/).flatMap((line, index) => {
1557
+ if (!line.trim()) {
1558
+ return [];
1559
+ }
1560
+ let parsed;
1561
+ try {
1562
+ parsed = JSON.parse(line);
1563
+ } catch {
1564
+ throw new UsageError(`--file line ${index + 1} is not valid JSON`);
1565
+ }
1566
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1567
+ throw new UsageError(`--file line ${index + 1} must be a JSON object`);
1568
+ }
1569
+ return [parsed];
1570
+ });
1571
+ }
1572
+ function helpTopic(topic) {
1573
+ switch (topic) {
1574
+ case "resolve":
1575
+ return "company.resolve";
1576
+ case "domain":
1577
+ return "company.domain";
1578
+ case "hierarchy":
1579
+ return "company.hierarchy";
1580
+ case "b2b-social":
1581
+ return "company.b2b_social";
1582
+ case "icon":
1583
+ return "company.icon";
1584
+ case "description":
1585
+ return "company.description";
1586
+ case "verify-employment":
1587
+ return "person.verify_employment";
1588
+ case "language":
1589
+ return "person.language";
1590
+ case "phone":
1591
+ return "person.phone";
1592
+ case "title":
1593
+ return "person.title";
1594
+ case "recipes":
1595
+ return "recipes";
1596
+ case "batch":
1597
+ return "batch";
1598
+ default:
1599
+ return topic;
1600
+ }
1601
+ }
1602
+ function resolveBaseUrl(flags, env) {
1603
+ const fromFlag = typeof flags["base-url"] === "string" ? flags["base-url"] : void 0;
1604
+ const raw = fromFlag?.trim() || env[BASE_URL_ENV]?.trim() || DEFAULT_BASE_URL;
1605
+ return raw.replace(/\/+$/, "");
1606
+ }
1607
+ function optionalToken(flags, env) {
1608
+ const fromFlag = typeof flags.token === "string" ? flags.token.trim() : "";
1609
+ const fromEnv = env[TOKEN_ENV]?.trim() ?? "";
1610
+ return fromFlag || fromEnv || void 0;
1611
+ }
1612
+ function requireToken(flags, env) {
1613
+ const token = optionalToken(flags, env);
1614
+ if (!token) {
1615
+ throw new UsageError(`missing tenant Bearer: set ${TOKEN_ENV} or pass --token`);
1616
+ }
1617
+ return token;
1618
+ }
1619
+ function collectSecrets(io, flags) {
1620
+ const secrets = [];
1621
+ const push = (value) => {
1622
+ const trimmed = value?.trim();
1623
+ if (trimmed) {
1624
+ secrets.push(trimmed);
1625
+ }
1626
+ };
1627
+ push(io.env[TOKEN_ENV]);
1628
+ if (typeof flags?.token === "string") {
1629
+ push(flags.token);
1630
+ }
1631
+ if (typeof flags?.["api-key"] === "string") {
1632
+ push(flags["api-key"]);
1633
+ }
1634
+ if (typeof flags?.["client-secret"] === "string") {
1635
+ push(flags["client-secret"]);
1636
+ }
1637
+ return secrets;
1638
+ }
1639
+ function redact(text, secrets) {
1640
+ return secrets.reduce((out, secret) => out.split(secret).join("[redacted]"), text);
1641
+ }
1642
+ function launchedAsCli() {
1643
+ const self = fileURLToPath(import.meta.url);
1644
+ const argv1 = process.argv[1];
1645
+ if (!argv1) {
1646
+ return false;
1647
+ }
1648
+ try {
1649
+ return realpathSync(argv1) === realpathSync(self);
1650
+ } catch {
1651
+ return argv1.endsWith("kingminos.ts") || argv1.endsWith("kingminos.js") || /(^|\/)kingminos$/.test(argv1);
1652
+ }
1653
+ }
1654
+ async function readStdin() {
1655
+ if (process.stdin.isTTY) {
1656
+ return "";
1657
+ }
1658
+ const chunks = [];
1659
+ for await (const chunk of process.stdin) {
1660
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
1661
+ }
1662
+ return Buffer.concat(chunks).toString("utf8");
1663
+ }
1664
+ if (launchedAsCli()) {
1665
+ const readBodyStdin = process.argv.includes("--body") && process.argv.includes("-");
1666
+ const stdin = readBodyStdin ? await readStdin() : "";
1667
+ const code = await runCli({
1668
+ argv: process.argv.slice(2),
1669
+ env: process.env,
1670
+ stdout: (text) => {
1671
+ process.stdout.write(text);
1672
+ },
1673
+ stderr: (text) => {
1674
+ process.stderr.write(text);
1675
+ },
1676
+ fetch,
1677
+ stdin
1678
+ });
1679
+ process.exitCode = code;
1680
+ }
1681
+ export {
1682
+ BASE_URL_ENV,
1683
+ DEFAULT_BASE_URL,
1684
+ EXIT,
1685
+ ROOT_HELP,
1686
+ TOKEN_ENV,
1687
+ assertKnownFlags,
1688
+ buildRequest,
1689
+ classifyHttpError,
1690
+ formatDecisionNotes,
1691
+ parseArgv,
1692
+ resolveCommand,
1693
+ runCli,
1694
+ stripSafeToWrite
1695
+ };