@tokenbuddy/tb-admin 1.0.15 → 1.0.27

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 (43) hide show
  1. package/dist/src/cli.d.ts.map +1 -1
  2. package/dist/src/cli.js +286 -13
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/client.d.ts +12 -3
  5. package/dist/src/client.d.ts.map +1 -1
  6. package/dist/src/client.js +12 -8
  7. package/dist/src/client.js.map +1 -1
  8. package/dist/src/display-format.d.ts +39 -0
  9. package/dist/src/display-format.d.ts.map +1 -0
  10. package/dist/src/display-format.js +354 -0
  11. package/dist/src/display-format.js.map +1 -0
  12. package/dist/src/server-cmd.d.ts +3 -0
  13. package/dist/src/server-cmd.d.ts.map +1 -1
  14. package/dist/src/server-cmd.js +32 -9
  15. package/dist/src/server-cmd.js.map +1 -1
  16. package/dist/src/ui-actions.d.ts +2 -0
  17. package/dist/src/ui-actions.d.ts.map +1 -1
  18. package/dist/src/ui-actions.js +123 -63
  19. package/dist/src/ui-actions.js.map +1 -1
  20. package/dist/src/ui-command.js +1 -1
  21. package/dist/src/ui-command.js.map +1 -1
  22. package/dist/src/ui-server.d.ts +0 -1
  23. package/dist/src/ui-server.d.ts.map +1 -1
  24. package/dist/src/ui-server.js +25 -9
  25. package/dist/src/ui-server.js.map +1 -1
  26. package/dist/src/ui-state.d.ts +7 -1
  27. package/dist/src/ui-state.d.ts.map +1 -1
  28. package/dist/src/ui-state.js +55 -24
  29. package/dist/src/ui-state.js.map +1 -1
  30. package/dist/src/ui-static.d.ts.map +1 -1
  31. package/dist/src/ui-static.js +372 -47
  32. package/dist/src/ui-static.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/cli.ts +326 -13
  35. package/src/client.ts +13 -8
  36. package/src/display-format.ts +398 -0
  37. package/src/server-cmd.ts +35 -9
  38. package/src/ui-actions.ts +129 -72
  39. package/src/ui-command.ts +1 -1
  40. package/src/ui-server.ts +24 -10
  41. package/src/ui-state.ts +64 -25
  42. package/src/ui-static.ts +375 -47
  43. package/tests/admin.test.ts +573 -41
@@ -1 +1 @@
1
- {"version":3,"file":"ui-static.js","sourceRoot":"","sources":["../../src/ui-static.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW;IACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YA+CG,aAAa,EAAE;;QAEnB,CAAC;AACT,CAAC;AAED,SAAS,aAAa;IACpB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwFR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"ui-static.js","sourceRoot":"","sources":["../../src/ui-static.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,qEAAqE;AACrE,qEAAqE;AACrE,0DAA0D;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,UAAU,WAAW;IACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YA8RG,aAAa,EAAE;;QAEnB,CAAC;AACT,CAAC;AAED,SAAS,aAAa;IACpB,gFAAgF;IAChF,OAAO;EACP,mBAAmB,EAAE;EACrB,mBAAmB,EAAE;CACtB,CAAC;AACF,CAAC;AAED,SAAS,mBAAmB;IAC1B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiKR,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenbuddy/tb-admin",
3
- "version": "1.0.15",
3
+ "version": "1.0.27",
4
4
  "description": "Remote admin CLI for TokenBuddy seller apps",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
package/src/cli.ts CHANGED
@@ -143,11 +143,48 @@ function loadYamlOrJsonFile(filePath: string): any {
143
143
  return YAML.load(fs.readFileSync(filePath, "utf8"));
144
144
  }
145
145
 
146
+ function loadSellerRegistryEntryFile(filePath: string): any {
147
+ const parsed = loadYamlOrJsonFile(filePath);
148
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
149
+ throw new Error("seller registry entry document is required");
150
+ }
151
+ validateRegistryDocument({ version: 1, sellers: [parsed as any] });
152
+ return parsed;
153
+ }
154
+
155
+ function loadSellerRegistryPatchFile(filePath: string): any {
156
+ const parsed = loadYamlOrJsonFile(filePath);
157
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
158
+ throw new Error("seller registry patch document is required");
159
+ }
160
+ if ("id" in parsed) {
161
+ throw new Error("seller registry patch must not include id");
162
+ }
163
+ return parsed;
164
+ }
165
+
166
+ function withExpectedVersion(body: Record<string, any>, expectedVersion: unknown): Record<string, any> {
167
+ if (expectedVersion === undefined) {
168
+ return body;
169
+ }
170
+ const version = Number(expectedVersion);
171
+ if (!Number.isInteger(version) || version < 1) {
172
+ throw new Error("expected version must be >= 1");
173
+ }
174
+ return { ...body, expectedVersion: version };
175
+ }
176
+
146
177
  function requireUpstreamModels(data: any): any[] {
147
- if (!Array.isArray(data?.models)) {
148
- throw new Error("operator upstream summary must contain top-level models array");
178
+ if (Array.isArray(data?.models)) {
179
+ return data.models;
180
+ }
181
+ if (Array.isArray(data?.upstreams)) {
182
+ const firstWithModels = data.upstreams.find((entry: any) => Array.isArray(entry?.models));
183
+ if (firstWithModels) {
184
+ return firstWithModels.models;
185
+ }
149
186
  }
150
- return data.models;
187
+ throw new Error("operator upstream summary must contain models array");
151
188
  }
152
189
 
153
190
  /**
@@ -614,14 +651,19 @@ export function buildAdminCli(configManager: ConfigManager): Command {
614
651
  // 6. Bootstrap Registry Command
615
652
  const bootstrap = program.command("bootstrap").description("Manage wallet bootstrap service");
616
653
  const bootstrapSellers = bootstrap.command("sellers").description("Manage public seller registry");
654
+ const bootstrapDefaultSeller = bootstrap.command("default-seller").description("Manage bootstrap default seller");
655
+ const bootstrapRegistry = bootstrap.command("registry").description("Manage signed registry versions and publishing");
617
656
  const bootstrapConfig = bootstrap.command("config").description("Manage wallet bootstrap YAML config");
618
657
 
619
658
  bootstrapSellers
620
659
  .command("get")
621
- .description("Fetch public seller registry")
622
- .action(async () => {
660
+ .description("Fetch public seller registry or one seller by id")
661
+ .argument("[id]", "Seller id")
662
+ .action(async (id?: string) => {
623
663
  try {
624
- const data = await publicGet("/registry/sellers") as SellerRegistryDocument;
664
+ const data = id
665
+ ? await getClient().get(`/operator/registry/sellers/${encodeURIComponent(id)}`)
666
+ : await publicGet("/registry/sellers") as SellerRegistryDocument;
625
667
  console.log(JSON.stringify(data, null, 2));
626
668
  } catch (err: any) {
627
669
  console.error("Error:", err.message);
@@ -629,16 +671,129 @@ export function buildAdminCli(configManager: ConfigManager): Command {
629
671
  }
630
672
  });
631
673
 
674
+ bootstrapSellers
675
+ .command("list")
676
+ .description("List seller entries from the operator registry")
677
+ .action(async () => {
678
+ try {
679
+ const data = await getClient().get("/operator/registry/sellers") as { sellers: any[] };
680
+ const table = new Table({ head: ["ID", "Status", "URL", "Models", "Protocols", "Payments"] });
681
+ for (const seller of data.sellers || []) {
682
+ table.push([
683
+ seller.id,
684
+ seller.status || "active",
685
+ seller.url,
686
+ String(seller.models?.length || seller.modelsCount || 0),
687
+ (seller.supportedProtocols || []).join(","),
688
+ (seller.paymentMethods || []).join(",")
689
+ ]);
690
+ }
691
+ console.log(table.toString());
692
+ } catch (err: any) {
693
+ console.error("Error:", err.message);
694
+ process.exit(1);
695
+ }
696
+ });
697
+
698
+ bootstrapSellers
699
+ .command("add")
700
+ .description("Add one seller entry without replacing the full registry")
701
+ .requiredOption("--file <path>", "Seller entry YAML/JSON file")
702
+ .requiredOption("--expect-version <n>", "Expected current registry version")
703
+ .option("--idempotency-key <key>", "Idempotency key for retry-safe writes")
704
+ .action(async (options) => {
705
+ try {
706
+ const seller = loadSellerRegistryEntryFile(options.file);
707
+ const headers = options.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : undefined;
708
+ const response = await getClient().post(
709
+ "/operator/registry/sellers",
710
+ withExpectedVersion(seller, options.expectVersion),
711
+ headers
712
+ ) as SellerRegistryDocument;
713
+ console.log(`Added seller ${seller.id}: version=${response.version} sellers=${response.sellers.length} publish=pending`);
714
+ } catch (err: any) {
715
+ console.error("Error:", err.message);
716
+ process.exit(1);
717
+ }
718
+ });
719
+
720
+ bootstrapSellers
721
+ .command("update <id>")
722
+ .description("Patch one seller entry without replacing the full registry")
723
+ .requiredOption("--file <path>", "Seller patch YAML/JSON file")
724
+ .requiredOption("--expect-version <n>", "Expected current registry version")
725
+ .option("--idempotency-key <key>", "Idempotency key for retry-safe writes")
726
+ .action(async (id, options) => {
727
+ try {
728
+ const patch = loadSellerRegistryPatchFile(options.file);
729
+ const headers = options.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : undefined;
730
+ const response = await getClient().patch(
731
+ `/operator/registry/sellers/${encodeURIComponent(id)}`,
732
+ withExpectedVersion(patch, options.expectVersion),
733
+ headers
734
+ ) as SellerRegistryDocument;
735
+ console.log(`Updated seller ${id}: version=${response.version} publish=pending`);
736
+ } catch (err: any) {
737
+ console.error("Error:", err.message);
738
+ process.exit(1);
739
+ }
740
+ });
741
+
742
+ bootstrapSellers
743
+ .command("status <id> <status>")
744
+ .description("Set one seller registry status: active, draining, or offline")
745
+ .requiredOption("--expect-version <n>", "Expected current registry version")
746
+ .option("--idempotency-key <key>", "Idempotency key for retry-safe writes")
747
+ .action(async (id, status, options) => {
748
+ try {
749
+ const headers = options.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : undefined;
750
+ const response = await getClient().put(
751
+ `/operator/registry/sellers/${encodeURIComponent(id)}/status`,
752
+ withExpectedVersion({ status }, options.expectVersion),
753
+ headers
754
+ ) as SellerRegistryDocument;
755
+ console.log(`Set seller ${id} status=${status}: version=${response.version} publish=pending`);
756
+ } catch (err: any) {
757
+ console.error("Error:", err.message);
758
+ process.exit(1);
759
+ }
760
+ });
761
+
762
+ bootstrapSellers
763
+ .command("remove <id>")
764
+ .description("Remove one non-active seller entry")
765
+ .requiredOption("--expect-version <n>", "Expected current registry version")
766
+ .option("--force", "Allow forced removal when the service permits it")
767
+ .option("--idempotency-key <key>", "Idempotency key for retry-safe writes")
768
+ .action(async (id, options) => {
769
+ try {
770
+ const headers = options.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : undefined;
771
+ const response = await getClient().delete(
772
+ `/operator/registry/sellers/${encodeURIComponent(id)}`,
773
+ withExpectedVersion({ force: Boolean(options.force) }, options.expectVersion),
774
+ headers
775
+ ) as SellerRegistryDocument;
776
+ console.log(`Removed seller ${id}: version=${response.version} sellers=${response.sellers.length} publish=pending`);
777
+ } catch (err: any) {
778
+ console.error("Error:", err.message);
779
+ process.exit(1);
780
+ }
781
+ });
782
+
632
783
  bootstrapSellers
633
784
  .command("put")
634
- .description("Update public seller registry")
785
+ .description("Deprecated: dangerously replace public seller registry")
635
786
  .requiredOption("--file <path>", "Seller registry JSON file")
787
+ .option("--force", "Acknowledge this full replacement is dangerous")
636
788
  .action(async (options) => {
637
789
  try {
790
+ if (!options.force) {
791
+ throw new Error("bootstrap sellers put is deprecated; use bootstrap registry import --dry-run first, then --force");
792
+ }
638
793
  const document = loadRegistryFile(options.file);
639
794
  const client = getClient();
640
795
  const response = await client.put("/operator/registry/sellers", document) as SellerRegistryDocument;
641
- console.log(`Updated seller registry: version=${response.version} sellers=${response.sellers.length}`);
796
+ console.log(`Dangerously replaced seller registry: version=${response.version} sellers=${response.sellers.length} publish=pending`);
642
797
  } catch (err: any) {
643
798
  console.error("Error:", err.message);
644
799
  process.exit(1);
@@ -660,6 +815,159 @@ export function buildAdminCli(configManager: ConfigManager): Command {
660
815
  }
661
816
  });
662
817
 
818
+ bootstrapDefaultSeller
819
+ .command("set <id>")
820
+ .description("Set default seller")
821
+ .requiredOption("--expect-version <n>", "Expected current registry version")
822
+ .option("--idempotency-key <key>", "Idempotency key for retry-safe writes")
823
+ .action(async (id, options) => {
824
+ try {
825
+ const headers = options.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : undefined;
826
+ const response = await getClient().put(
827
+ "/operator/registry/default-seller",
828
+ withExpectedVersion({ sellerId: id }, options.expectVersion),
829
+ headers
830
+ ) as SellerRegistryDocument;
831
+ console.log(`Set default seller ${id}: version=${response.version} publish=pending`);
832
+ } catch (err: any) {
833
+ console.error("Error:", err.message);
834
+ process.exit(1);
835
+ }
836
+ });
837
+
838
+ bootstrapRegistry
839
+ .command("diff")
840
+ .description("Compare a local registry JSON file against the operator registry")
841
+ .requiredOption("--file <path>", "Seller registry JSON file")
842
+ .action(async (options) => {
843
+ try {
844
+ const local = loadRegistryFile(options.file);
845
+ const remote = await getClient().get("/operator/registry") as SellerRegistryDocument;
846
+ const localIds = new Set(local.sellers.map((seller) => seller.id));
847
+ const remoteIds = new Set(remote.sellers.map((seller) => seller.id));
848
+ const added = local.sellers.filter((seller) => !remoteIds.has(seller.id)).map((seller) => seller.id);
849
+ const removed = remote.sellers.filter((seller) => !localIds.has(seller.id)).map((seller) => seller.id);
850
+ const changed = local.sellers
851
+ .filter((seller) => remoteIds.has(seller.id))
852
+ .filter((seller) => JSON.stringify(seller) !== JSON.stringify(remote.sellers.find((entry) => entry.id === seller.id)))
853
+ .map((seller) => seller.id);
854
+ console.log(JSON.stringify({
855
+ remoteVersion: remote.version,
856
+ localVersion: local.version,
857
+ added,
858
+ removed,
859
+ changed,
860
+ defaultSellerChanged: remote.defaultSeller !== local.defaultSeller
861
+ }, null, 2));
862
+ } catch (err: any) {
863
+ console.error("Error:", err.message);
864
+ process.exit(1);
865
+ }
866
+ });
867
+
868
+ bootstrapRegistry
869
+ .command("import")
870
+ .description("Import a full registry JSON as a dangerous disaster-recovery operation")
871
+ .requiredOption("--file <path>", "Seller registry JSON file")
872
+ .option("--dry-run", "Only validate and show diff")
873
+ .option("--force", "Actually perform the full replacement")
874
+ .option("--expect-version <n>", "Expected current registry version before import")
875
+ .option("--force-delete-count <n>", "Maximum allowed delete count when forcing", "1")
876
+ .action(async (options) => {
877
+ try {
878
+ const local = loadRegistryFile(options.file);
879
+ const remote = await getClient().get("/operator/registry") as SellerRegistryDocument;
880
+ const localIds = new Set(local.sellers.map((seller) => seller.id));
881
+ const removed = remote.sellers.filter((seller) => !localIds.has(seller.id));
882
+ const forceDeleteCount = Number(options.forceDeleteCount);
883
+ if (!Number.isInteger(forceDeleteCount) || forceDeleteCount < 0) {
884
+ throw new Error("--force-delete-count must be >= 0");
885
+ }
886
+ console.log(`Import diff: remoteVersion=${remote.version} localVersion=${local.version} removes=${removed.length}`);
887
+ if (options.dryRun || !options.force) {
888
+ if (!options.dryRun) {
889
+ throw new Error("refusing full import without --force; run with --dry-run first");
890
+ }
891
+ return;
892
+ }
893
+ if (removed.length > forceDeleteCount) {
894
+ throw new Error(`import would remove ${removed.length} sellers; pass --force-delete-count ${removed.length} to allow`);
895
+ }
896
+ const body = withExpectedVersion(local as any, options.expectVersion);
897
+ const response = await getClient().put("/operator/registry/sellers", body) as SellerRegistryDocument;
898
+ console.log(`Imported seller registry: version=${response.version} sellers=${response.sellers.length} publish=pending`);
899
+ } catch (err: any) {
900
+ console.error("Error:", err.message);
901
+ process.exit(1);
902
+ }
903
+ });
904
+
905
+ bootstrapRegistry
906
+ .command("publish")
907
+ .description("Publish the current signed registry to R2")
908
+ .option("--version <n>", "Registry version to publish")
909
+ .action(async (options) => {
910
+ try {
911
+ const version = options.version ? Number(options.version) : undefined;
912
+ if (version !== undefined && (!Number.isInteger(version) || version < 1)) {
913
+ throw new Error("--version must be a positive integer");
914
+ }
915
+ const body = version === undefined ? {} : { version };
916
+ const response = await getClient().post("/operator/registry/publish", body) as {
917
+ version: number;
918
+ registrySha256: string;
919
+ signingKeyId: string;
920
+ artifacts?: Array<{ key: string; url: string }>;
921
+ };
922
+ console.log(`Published registry version=${response.version} sha256=${response.registrySha256} signingKey=${response.signingKeyId}`);
923
+ for (const artifact of response.artifacts || []) {
924
+ console.log(` ${artifact.key} -> ${artifact.url}`);
925
+ }
926
+ } catch (err: any) {
927
+ console.error("Error:", err.message);
928
+ process.exit(1);
929
+ }
930
+ });
931
+
932
+ bootstrapRegistry
933
+ .command("versions")
934
+ .description("List registry versions")
935
+ .option("--limit <n>", "Maximum versions to list", "20")
936
+ .action(async (options) => {
937
+ try {
938
+ const limit = Number(options.limit);
939
+ if (!Number.isInteger(limit) || limit < 1) {
940
+ throw new Error("--limit must be a positive integer");
941
+ }
942
+ const response = await getClient().get(`/operator/registry/versions?limit=${encodeURIComponent(String(limit))}`) as {
943
+ versions?: Array<{
944
+ version: number;
945
+ registrySha256: string;
946
+ signingKeyId?: string;
947
+ signed?: boolean;
948
+ sellerCount?: number;
949
+ defaultSeller?: string;
950
+ createdAt?: string;
951
+ publishedAt?: string;
952
+ }>;
953
+ };
954
+ for (const version of response.versions || []) {
955
+ console.log([
956
+ `version=${version.version}`,
957
+ `sellers=${version.sellerCount ?? "?"}`,
958
+ `default=${version.defaultSeller || "-"}`,
959
+ `signed=${version.signed ? "yes" : "no"}`,
960
+ `published=${version.publishedAt || "pending"}`,
961
+ `sha256=${version.registrySha256}`,
962
+ `key=${version.signingKeyId || "-"}`
963
+ ].join(" "));
964
+ }
965
+ } catch (err: any) {
966
+ console.error("Error:", err.message);
967
+ process.exit(1);
968
+ }
969
+ });
970
+
663
971
  bootstrapConfig
664
972
  .command("get")
665
973
  .description("Fetch wallet bootstrap runtime config")
@@ -765,14 +1073,19 @@ export function buildAdminCli(configManager: ConfigManager): Command {
765
1073
 
766
1074
  // 8. Seller Command (Fly.io)
767
1075
  const sellerCmd = program.command("seller").description("Deploy and manage seller containers on Fly.io");
768
- const flyProvider = new FlyProvider();
1076
+
1077
+ function getFlyProvider(): FlyProvider {
1078
+ const opts = program.opts();
1079
+ const mgr = opts.config ? new ConfigManager(opts.config) : configManager;
1080
+ return new FlyProvider(mgr.getSellerProvider("fly"));
1081
+ }
769
1082
 
770
1083
  sellerCmd
771
1084
  .command("ls")
772
1085
  .description("List all deployed apps on Fly.io")
773
1086
  .action(() => {
774
1087
  try {
775
- const out = flyProvider.listApps();
1088
+ const out = getFlyProvider().listApps();
776
1089
  console.log(out);
777
1090
  } catch (err: any) {
778
1091
  console.error("Error:", err.message);
@@ -796,7 +1109,7 @@ export function buildAdminCli(configManager: ConfigManager): Command {
796
1109
  .option("--dry-run", "Dry run display without actual execution")
797
1110
  .action((name, options) => {
798
1111
  try {
799
- const res = flyProvider.createSeller({
1112
+ const res = getFlyProvider().createSeller({
800
1113
  name,
801
1114
  app: options.app,
802
1115
  region: options.region,
@@ -824,7 +1137,7 @@ export function buildAdminCli(configManager: ConfigManager): Command {
824
1137
  .option("--dry-run", "Dry run")
825
1138
  .action((app, options) => {
826
1139
  try {
827
- const res = flyProvider.deploySeller({
1140
+ const res = getFlyProvider().deploySeller({
828
1141
  app,
829
1142
  image: options.image,
830
1143
  dryRun: options.dryRun
@@ -845,7 +1158,7 @@ export function buildAdminCli(configManager: ConfigManager): Command {
845
1158
  .action((name, options) => {
846
1159
  try {
847
1160
  const appName = options.app || name;
848
- const res = flyProvider.removeSeller(appName, options.dryRun);
1161
+ const res = getFlyProvider().removeSeller(appName, options.dryRun);
849
1162
  console.log(res);
850
1163
  if (options.removeProfile && !options.dryRun) {
851
1164
  const appBase = appName.includes("-") ? appName.replace(/^tb-seller-/, "") : appName;
package/src/client.ts CHANGED
@@ -11,11 +11,12 @@ export class AdminClient {
11
11
  this.token = token;
12
12
  }
13
13
 
14
- private async request(path: string, method: string, body?: any): Promise<any> {
14
+ private async request(path: string, method: string, body?: any, extraHeaders: { [key: string]: string } = {}): Promise<any> {
15
15
  const url = `${this.baseUrl}${path}`;
16
16
  const headers: { [key: string]: string } = {
17
17
  "Content-Type": "application/json",
18
- "Authorization": `Bearer ${this.token}`
18
+ "Authorization": `Bearer ${this.token}`,
19
+ ...extraHeaders
19
20
  };
20
21
 
21
22
  const options: RequestInit = {
@@ -44,15 +45,19 @@ export class AdminClient {
44
45
  return this.request(path, "GET");
45
46
  }
46
47
 
47
- public async put(path: string, body?: any): Promise<any> {
48
- return this.request(path, "PUT", body);
48
+ public async put(path: string, body?: any, headers?: { [key: string]: string }): Promise<any> {
49
+ return this.request(path, "PUT", body, headers);
49
50
  }
50
51
 
51
- public async post(path: string, body?: any): Promise<any> {
52
- return this.request(path, "POST", body);
52
+ public async post(path: string, body?: any, headers?: { [key: string]: string }): Promise<any> {
53
+ return this.request(path, "POST", body, headers);
53
54
  }
54
55
 
55
- public async delete(path: string): Promise<any> {
56
- return this.request(path, "DELETE");
56
+ public async patch(path: string, body?: any, headers?: { [key: string]: string }): Promise<any> {
57
+ return this.request(path, "PATCH", body, headers);
58
+ }
59
+
60
+ public async delete(path: string, body?: any, headers?: { [key: string]: string }): Promise<any> {
61
+ return this.request(path, "DELETE", body, headers);
57
62
  }
58
63
  }