@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.
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +286 -13
- package/dist/src/cli.js.map +1 -1
- package/dist/src/client.d.ts +12 -3
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +12 -8
- package/dist/src/client.js.map +1 -1
- package/dist/src/display-format.d.ts +39 -0
- package/dist/src/display-format.d.ts.map +1 -0
- package/dist/src/display-format.js +354 -0
- package/dist/src/display-format.js.map +1 -0
- package/dist/src/server-cmd.d.ts +3 -0
- package/dist/src/server-cmd.d.ts.map +1 -1
- package/dist/src/server-cmd.js +32 -9
- package/dist/src/server-cmd.js.map +1 -1
- package/dist/src/ui-actions.d.ts +2 -0
- package/dist/src/ui-actions.d.ts.map +1 -1
- package/dist/src/ui-actions.js +123 -63
- package/dist/src/ui-actions.js.map +1 -1
- package/dist/src/ui-command.js +1 -1
- package/dist/src/ui-command.js.map +1 -1
- package/dist/src/ui-server.d.ts +0 -1
- package/dist/src/ui-server.d.ts.map +1 -1
- package/dist/src/ui-server.js +25 -9
- package/dist/src/ui-server.js.map +1 -1
- package/dist/src/ui-state.d.ts +7 -1
- package/dist/src/ui-state.d.ts.map +1 -1
- package/dist/src/ui-state.js +55 -24
- package/dist/src/ui-state.js.map +1 -1
- package/dist/src/ui-static.d.ts.map +1 -1
- package/dist/src/ui-static.js +372 -47
- package/dist/src/ui-static.js.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +326 -13
- package/src/client.ts +13 -8
- package/src/display-format.ts +398 -0
- package/src/server-cmd.ts +35 -9
- package/src/ui-actions.ts +129 -72
- package/src/ui-command.ts +1 -1
- package/src/ui-server.ts +24 -10
- package/src/ui-state.ts +64 -25
- package/src/ui-static.ts +375 -47
- 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
|
|
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
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 (
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
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 =
|
|
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("
|
|
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(`
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
56
|
-
return this.request(path, "
|
|
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
|
}
|