@calimero-network/registry-cli 1.4.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +602 -111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import
|
|
3
|
+
import chalk6 from 'chalk';
|
|
4
4
|
import ora6 from 'ora';
|
|
5
5
|
import { table } from 'table';
|
|
6
6
|
import { SSAppRegistryClient } from '@calimero-network/registry-client';
|
|
7
7
|
import fs3, { existsSync, writeFileSync } from 'fs';
|
|
8
|
-
import
|
|
8
|
+
import path7 from 'path';
|
|
9
9
|
import os from 'os';
|
|
10
10
|
import crypto from 'crypto';
|
|
11
11
|
import fastify from 'fastify';
|
|
@@ -14,12 +14,12 @@ import * as tar from 'tar';
|
|
|
14
14
|
|
|
15
15
|
var LocalConfig = class {
|
|
16
16
|
constructor() {
|
|
17
|
-
this.configPath =
|
|
17
|
+
this.configPath = path7.join(
|
|
18
18
|
os.homedir(),
|
|
19
19
|
".calimero-registry",
|
|
20
20
|
"config.json"
|
|
21
21
|
);
|
|
22
|
-
this.dataDir =
|
|
22
|
+
this.dataDir = path7.join(os.homedir(), ".calimero-registry");
|
|
23
23
|
this.config = this.loadConfig();
|
|
24
24
|
}
|
|
25
25
|
loadConfig() {
|
|
@@ -30,15 +30,15 @@ var LocalConfig = class {
|
|
|
30
30
|
publicHost: "host.docker.internal"
|
|
31
31
|
},
|
|
32
32
|
data: {
|
|
33
|
-
dir:
|
|
34
|
-
artifactsDir:
|
|
33
|
+
dir: path7.join(os.homedir(), ".calimero-registry", "data"),
|
|
34
|
+
artifactsDir: path7.join(
|
|
35
35
|
os.homedir(),
|
|
36
36
|
".calimero-registry",
|
|
37
37
|
"artifacts"
|
|
38
38
|
)
|
|
39
39
|
},
|
|
40
40
|
artifacts: {
|
|
41
|
-
storageDir:
|
|
41
|
+
storageDir: path7.join(os.homedir(), ".calimero-registry", "artifacts"),
|
|
42
42
|
serveLocal: true,
|
|
43
43
|
copyArtifacts: true,
|
|
44
44
|
maxFileSize: "100MB",
|
|
@@ -71,7 +71,7 @@ var LocalConfig = class {
|
|
|
71
71
|
return defaultConfig;
|
|
72
72
|
}
|
|
73
73
|
saveConfig() {
|
|
74
|
-
const configDir =
|
|
74
|
+
const configDir = path7.dirname(this.configPath);
|
|
75
75
|
if (!fs3.existsSync(configDir)) {
|
|
76
76
|
fs3.mkdirSync(configDir, { recursive: true });
|
|
77
77
|
}
|
|
@@ -105,7 +105,7 @@ var LocalConfig = class {
|
|
|
105
105
|
}
|
|
106
106
|
setDataDir(dir) {
|
|
107
107
|
this.config.data.dir = dir;
|
|
108
|
-
this.config.artifacts.storageDir =
|
|
108
|
+
this.config.artifacts.storageDir = path7.join(dir, "artifacts");
|
|
109
109
|
this.saveConfig();
|
|
110
110
|
}
|
|
111
111
|
getArtifactsDir() {
|
|
@@ -129,8 +129,8 @@ var LocalConfig = class {
|
|
|
129
129
|
const dirs = [
|
|
130
130
|
this.getDataDir(),
|
|
131
131
|
this.getArtifactsDir(),
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
path7.join(this.getDataDir(), "backups"),
|
|
133
|
+
path7.join(this.getDataDir(), "logs")
|
|
134
134
|
];
|
|
135
135
|
dirs.forEach((dir) => {
|
|
136
136
|
if (!fs3.existsSync(dir)) {
|
|
@@ -153,15 +153,15 @@ var LocalConfig = class {
|
|
|
153
153
|
host: "localhost"
|
|
154
154
|
},
|
|
155
155
|
data: {
|
|
156
|
-
dir:
|
|
157
|
-
artifactsDir:
|
|
156
|
+
dir: path7.join(os.homedir(), ".calimero-registry", "data"),
|
|
157
|
+
artifactsDir: path7.join(
|
|
158
158
|
os.homedir(),
|
|
159
159
|
".calimero-registry",
|
|
160
160
|
"artifacts"
|
|
161
161
|
)
|
|
162
162
|
},
|
|
163
163
|
artifacts: {
|
|
164
|
-
storageDir:
|
|
164
|
+
storageDir: path7.join(os.homedir(), ".calimero-registry", "artifacts"),
|
|
165
165
|
serveLocal: true,
|
|
166
166
|
copyArtifacts: true,
|
|
167
167
|
maxFileSize: "100MB",
|
|
@@ -174,12 +174,12 @@ var LocalConfig = class {
|
|
|
174
174
|
var LocalDataStore = class {
|
|
175
175
|
constructor(config) {
|
|
176
176
|
this.config = config;
|
|
177
|
-
this.appsFile =
|
|
178
|
-
this.bundleManifestsFile =
|
|
177
|
+
this.appsFile = path7.join(config.getDataDir(), "apps.json");
|
|
178
|
+
this.bundleManifestsFile = path7.join(
|
|
179
179
|
config.getDataDir(),
|
|
180
180
|
"bundle_manifests.json"
|
|
181
181
|
);
|
|
182
|
-
this.artifactsFile =
|
|
182
|
+
this.artifactsFile = path7.join(config.getDataDir(), "artifacts.json");
|
|
183
183
|
this.data = {
|
|
184
184
|
apps: /* @__PURE__ */ new Map(),
|
|
185
185
|
bundleManifests: /* @__PURE__ */ new Map(),
|
|
@@ -299,12 +299,12 @@ var LocalDataStore = class {
|
|
|
299
299
|
// Backup and restore
|
|
300
300
|
async backup(outputPath) {
|
|
301
301
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
302
|
-
const backupPath = outputPath ||
|
|
302
|
+
const backupPath = outputPath || path7.join(
|
|
303
303
|
this.config.getDataDir(),
|
|
304
304
|
"backups",
|
|
305
305
|
`backup-${timestamp}.json`
|
|
306
306
|
);
|
|
307
|
-
const backupDir =
|
|
307
|
+
const backupDir = path7.dirname(backupPath);
|
|
308
308
|
if (!fs3.existsSync(backupDir)) {
|
|
309
309
|
fs3.mkdirSync(backupDir, { recursive: true });
|
|
310
310
|
}
|
|
@@ -398,11 +398,11 @@ var LocalArtifactServer = class {
|
|
|
398
398
|
if (!fs3.existsSync(sourcePath)) {
|
|
399
399
|
throw new Error(`Source file not found: ${sourcePath}`);
|
|
400
400
|
}
|
|
401
|
-
const appVersionDir =
|
|
401
|
+
const appVersionDir = path7.join(this.artifactsDir, appId, version);
|
|
402
402
|
if (!fs3.existsSync(appVersionDir)) {
|
|
403
403
|
fs3.mkdirSync(appVersionDir, { recursive: true });
|
|
404
404
|
}
|
|
405
|
-
const targetPath =
|
|
405
|
+
const targetPath = path7.join(appVersionDir, filename);
|
|
406
406
|
fs3.copyFileSync(sourcePath, targetPath);
|
|
407
407
|
const fileHash = await this.calculateFileHash(targetPath);
|
|
408
408
|
this.dataStore.setArtifactPath(fileHash, targetPath);
|
|
@@ -410,7 +410,7 @@ var LocalArtifactServer = class {
|
|
|
410
410
|
}
|
|
411
411
|
// Serve artifact by app ID, version, and filename
|
|
412
412
|
async serveArtifact(appId, version, filename) {
|
|
413
|
-
const artifactPath =
|
|
413
|
+
const artifactPath = path7.join(this.artifactsDir, appId, version, filename);
|
|
414
414
|
if (!fs3.existsSync(artifactPath)) {
|
|
415
415
|
throw new Error(`Artifact not found: ${artifactPath}`);
|
|
416
416
|
}
|
|
@@ -462,11 +462,11 @@ var LocalArtifactServer = class {
|
|
|
462
462
|
if (uri.startsWith("/")) {
|
|
463
463
|
updatedManifest.artifact = {
|
|
464
464
|
...manifest.artifact,
|
|
465
|
-
uri: `${this.getArtifactUrl(manifest.id, manifest.version,
|
|
465
|
+
uri: `${this.getArtifactUrl(manifest.id, manifest.version, path7.basename(manifest.artifact.uri))}`
|
|
466
466
|
};
|
|
467
467
|
} else if (uri.startsWith("file://")) {
|
|
468
468
|
const filePath = uri.replace("file://", "");
|
|
469
|
-
const filename =
|
|
469
|
+
const filename = path7.basename(filePath);
|
|
470
470
|
updatedManifest.artifact = {
|
|
471
471
|
...manifest.artifact,
|
|
472
472
|
uri: this.getArtifactUrl(manifest.id, manifest.version, filename)
|
|
@@ -481,7 +481,7 @@ var LocalArtifactServer = class {
|
|
|
481
481
|
let files = [];
|
|
482
482
|
const items = fs3.readdirSync(dir);
|
|
483
483
|
for (const item of items) {
|
|
484
|
-
const fullPath =
|
|
484
|
+
const fullPath = path7.join(dir, item);
|
|
485
485
|
const stat = fs3.statSync(fullPath);
|
|
486
486
|
if (stat.isDirectory()) {
|
|
487
487
|
files = files.concat(getAllFiles(fullPath));
|
|
@@ -510,7 +510,7 @@ var LocalArtifactServer = class {
|
|
|
510
510
|
let files = [];
|
|
511
511
|
const items = fs3.readdirSync(dir);
|
|
512
512
|
for (const item of items) {
|
|
513
|
-
const fullPath =
|
|
513
|
+
const fullPath = path7.join(dir, item);
|
|
514
514
|
const stat = fs3.statSync(fullPath);
|
|
515
515
|
if (stat.isDirectory()) {
|
|
516
516
|
files = files.concat(getAllFiles(fullPath));
|
|
@@ -610,7 +610,7 @@ var LocalRegistryClient = class {
|
|
|
610
610
|
if (manifest.artifact?.uri?.startsWith("file://")) {
|
|
611
611
|
const filePath = manifest.artifact.uri.replace("file://", "");
|
|
612
612
|
if (fs3.existsSync(filePath)) {
|
|
613
|
-
const filename =
|
|
613
|
+
const filename = path7.basename(filePath);
|
|
614
614
|
try {
|
|
615
615
|
await this.artifactServer.copyArtifactToLocal(
|
|
616
616
|
filePath,
|
|
@@ -660,7 +660,7 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
|
|
|
660
660
|
});
|
|
661
661
|
spinner2.succeed(`Found ${apps.length} application(s)`);
|
|
662
662
|
if (apps.length === 0) {
|
|
663
|
-
console.log(
|
|
663
|
+
console.log(chalk6.yellow("No applications found"));
|
|
664
664
|
return;
|
|
665
665
|
}
|
|
666
666
|
const tableData = [
|
|
@@ -677,7 +677,7 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
|
|
|
677
677
|
} catch (error) {
|
|
678
678
|
spinner2.fail("Failed to fetch applications");
|
|
679
679
|
if (error instanceof Error) {
|
|
680
|
-
console.error(
|
|
680
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
681
681
|
}
|
|
682
682
|
process.exit(1);
|
|
683
683
|
}
|
|
@@ -696,7 +696,7 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
|
|
|
696
696
|
const versions = await client.getAppVersions(appId);
|
|
697
697
|
spinner2.succeed(`Found ${versions.length} version(s)`);
|
|
698
698
|
if (versions.length === 0) {
|
|
699
|
-
console.log(
|
|
699
|
+
console.log(chalk6.yellow("No versions found"));
|
|
700
700
|
return;
|
|
701
701
|
}
|
|
702
702
|
const tableData = [
|
|
@@ -704,14 +704,14 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
|
|
|
704
704
|
...versions.map((version) => [
|
|
705
705
|
version.semver,
|
|
706
706
|
version.cid.substring(0, 12) + "...",
|
|
707
|
-
version.yanked ?
|
|
707
|
+
version.yanked ? chalk6.red("Yes") : chalk6.green("No")
|
|
708
708
|
])
|
|
709
709
|
];
|
|
710
710
|
console.log(table(tableData));
|
|
711
711
|
} catch (error) {
|
|
712
712
|
spinner2.fail("Failed to fetch versions");
|
|
713
713
|
if (error instanceof Error) {
|
|
714
|
-
console.error(
|
|
714
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
715
715
|
}
|
|
716
716
|
process.exit(1);
|
|
717
717
|
}
|
|
@@ -731,12 +731,12 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
|
|
|
731
731
|
try {
|
|
732
732
|
const manifest = await client.getAppManifest(appId, version);
|
|
733
733
|
spinner2.succeed("Manifest fetched successfully");
|
|
734
|
-
console.log(
|
|
734
|
+
console.log(chalk6.blue("\nApplication Manifest:"));
|
|
735
735
|
console.log(JSON.stringify(manifest, null, 2));
|
|
736
736
|
} catch (error) {
|
|
737
737
|
spinner2.fail("Failed to fetch manifest");
|
|
738
738
|
if (error instanceof Error) {
|
|
739
|
-
console.error(
|
|
739
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
740
740
|
}
|
|
741
741
|
process.exit(1);
|
|
742
742
|
}
|
|
@@ -752,10 +752,10 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
|
|
|
752
752
|
);
|
|
753
753
|
const spinner2 = ora6("Reading manifest file...").start();
|
|
754
754
|
try {
|
|
755
|
-
const manifestPath =
|
|
755
|
+
const manifestPath = path7.resolve(manifestFile);
|
|
756
756
|
if (!fs3.existsSync(manifestPath)) {
|
|
757
757
|
spinner2.fail("Manifest file not found");
|
|
758
|
-
console.error(
|
|
758
|
+
console.error(chalk6.red(`File not found: ${manifestFile}`));
|
|
759
759
|
process.exit(1);
|
|
760
760
|
}
|
|
761
761
|
const manifestContent = fs3.readFileSync(manifestPath, "utf8");
|
|
@@ -763,20 +763,20 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
|
|
|
763
763
|
spinner2.text = "Submitting application manifest...";
|
|
764
764
|
const result = await client.submitAppManifest(manifest);
|
|
765
765
|
spinner2.succeed("Application submitted successfully");
|
|
766
|
-
console.log(
|
|
766
|
+
console.log(chalk6.green(`
|
|
767
767
|
\u2705 ${result.message}`));
|
|
768
768
|
if (manifest.app?.name) {
|
|
769
|
-
console.log(
|
|
769
|
+
console.log(chalk6.blue(`
|
|
770
770
|
\u{1F4F1} App: ${manifest.app.name}`));
|
|
771
771
|
console.log(
|
|
772
|
-
|
|
772
|
+
chalk6.blue(`\u{1F464} Developer: ${manifest.app.developer_pubkey}`)
|
|
773
773
|
);
|
|
774
|
-
console.log(
|
|
774
|
+
console.log(chalk6.blue(`\u{1F4E6} Version: ${manifest.version?.semver}`));
|
|
775
775
|
}
|
|
776
776
|
} catch (error) {
|
|
777
777
|
spinner2.fail("Failed to submit application");
|
|
778
778
|
if (error instanceof Error) {
|
|
779
|
-
console.error(
|
|
779
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
780
780
|
}
|
|
781
781
|
process.exit(1);
|
|
782
782
|
}
|
|
@@ -793,29 +793,29 @@ var developersCommand = new Command("developers").description("Manage developer
|
|
|
793
793
|
try {
|
|
794
794
|
const profile = await client.getDeveloper(pubkey);
|
|
795
795
|
spinner2.succeed("Developer profile fetched successfully");
|
|
796
|
-
console.log(
|
|
797
|
-
console.log(
|
|
796
|
+
console.log(chalk6.blue("\nDeveloper Profile:"));
|
|
797
|
+
console.log(chalk6.green("Display Name:"), profile.display_name);
|
|
798
798
|
if (profile.website) {
|
|
799
|
-
console.log(
|
|
799
|
+
console.log(chalk6.green("Website:"), profile.website);
|
|
800
800
|
}
|
|
801
801
|
if (profile.proofs.length > 0) {
|
|
802
|
-
console.log(
|
|
802
|
+
console.log(chalk6.green("\nProofs:"));
|
|
803
803
|
const tableData = [
|
|
804
804
|
["Type", "Value", "Verified"],
|
|
805
805
|
...profile.proofs.map((proof) => [
|
|
806
806
|
proof.type,
|
|
807
807
|
proof.value.substring(0, 20) + "...",
|
|
808
|
-
proof.verified ?
|
|
808
|
+
proof.verified ? chalk6.green("Yes") : chalk6.red("No")
|
|
809
809
|
])
|
|
810
810
|
];
|
|
811
811
|
console.log(table(tableData));
|
|
812
812
|
} else {
|
|
813
|
-
console.log(
|
|
813
|
+
console.log(chalk6.yellow("\nNo proofs found"));
|
|
814
814
|
}
|
|
815
815
|
} catch (error) {
|
|
816
816
|
spinner2.fail("Failed to fetch developer profile");
|
|
817
817
|
if (error instanceof Error) {
|
|
818
|
-
console.error(
|
|
818
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
819
819
|
}
|
|
820
820
|
process.exit(1);
|
|
821
821
|
}
|
|
@@ -835,7 +835,7 @@ var developersCommand = new Command("developers").description("Manage developer
|
|
|
835
835
|
proofs = JSON.parse(options.proofs);
|
|
836
836
|
} catch {
|
|
837
837
|
spinner2.fail("Invalid proofs JSON format");
|
|
838
|
-
console.error(
|
|
838
|
+
console.error(chalk6.red("Proofs must be a valid JSON array"));
|
|
839
839
|
process.exit(1);
|
|
840
840
|
}
|
|
841
841
|
}
|
|
@@ -847,18 +847,18 @@ var developersCommand = new Command("developers").description("Manage developer
|
|
|
847
847
|
};
|
|
848
848
|
const result = await client.submitDeveloperProfile(pubkey, profile);
|
|
849
849
|
spinner2.succeed("Developer profile created successfully");
|
|
850
|
-
console.log(
|
|
850
|
+
console.log(chalk6.green(`
|
|
851
851
|
\u2705 ${result.message}`));
|
|
852
|
-
console.log(
|
|
852
|
+
console.log(chalk6.blue(`
|
|
853
853
|
\u{1F464} Developer: ${displayName}`));
|
|
854
|
-
console.log(
|
|
854
|
+
console.log(chalk6.blue(`\u{1F511} Public Key: ${pubkey}`));
|
|
855
855
|
if (options.website) {
|
|
856
|
-
console.log(
|
|
856
|
+
console.log(chalk6.blue(`\u{1F310} Website: ${options.website}`));
|
|
857
857
|
}
|
|
858
858
|
} catch (error) {
|
|
859
859
|
spinner2.fail("Failed to create developer profile");
|
|
860
860
|
if (error instanceof Error) {
|
|
861
|
-
console.error(
|
|
861
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
862
862
|
}
|
|
863
863
|
process.exit(1);
|
|
864
864
|
}
|
|
@@ -881,16 +881,16 @@ var attestationsCommand = new Command("attestations").description("Manage applic
|
|
|
881
881
|
version
|
|
882
882
|
);
|
|
883
883
|
spinner2.succeed("Attestation fetched successfully");
|
|
884
|
-
console.log(
|
|
885
|
-
console.log(
|
|
886
|
-
console.log(
|
|
884
|
+
console.log(chalk6.blue("\nAttestation:"));
|
|
885
|
+
console.log(chalk6.green("Status:"), attestation.status);
|
|
886
|
+
console.log(chalk6.green("Timestamp:"), attestation.timestamp);
|
|
887
887
|
if (attestation.comment) {
|
|
888
|
-
console.log(
|
|
888
|
+
console.log(chalk6.green("Comment:"), attestation.comment);
|
|
889
889
|
}
|
|
890
890
|
} catch (error) {
|
|
891
891
|
spinner2.fail("Failed to fetch attestation");
|
|
892
892
|
if (error instanceof Error) {
|
|
893
|
-
console.error(
|
|
893
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
894
894
|
}
|
|
895
895
|
process.exit(1);
|
|
896
896
|
}
|
|
@@ -908,11 +908,11 @@ var healthCommand = new Command("health").description("Check the health of the S
|
|
|
908
908
|
try {
|
|
909
909
|
const health = await client.healthCheck();
|
|
910
910
|
spinner2.succeed("API is healthy");
|
|
911
|
-
console.log(
|
|
911
|
+
console.log(chalk6.green(`Status: ${health.status}`));
|
|
912
912
|
} catch (error) {
|
|
913
913
|
spinner2.fail("API health check failed");
|
|
914
914
|
if (error instanceof Error) {
|
|
915
|
-
console.error(
|
|
915
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
916
916
|
}
|
|
917
917
|
process.exit(1);
|
|
918
918
|
}
|
|
@@ -1210,37 +1210,37 @@ localCommand.addCommand(
|
|
|
1210
1210
|
spinner2.succeed(
|
|
1211
1211
|
`Local registry started on http://${config.getHost()}:${config.getPort()}`
|
|
1212
1212
|
);
|
|
1213
|
-
console.log(
|
|
1213
|
+
console.log(chalk6.blue("\n\u{1F4F1} Local Registry Status:"));
|
|
1214
1214
|
console.log(
|
|
1215
|
-
|
|
1215
|
+
chalk6.green(
|
|
1216
1216
|
`\u2705 Server: http://${config.getHost()}:${config.getPort()}`
|
|
1217
1217
|
)
|
|
1218
1218
|
);
|
|
1219
1219
|
console.log(
|
|
1220
|
-
|
|
1220
|
+
chalk6.green(
|
|
1221
1221
|
`\u{1F310} Public URL: http://${config.getPublicHost()}:${config.getPort()}`
|
|
1222
1222
|
)
|
|
1223
1223
|
);
|
|
1224
|
-
console.log(
|
|
1224
|
+
console.log(chalk6.green(`\u{1F4C1} Data: ${config.getDataDir()}`));
|
|
1225
1225
|
console.log(
|
|
1226
|
-
|
|
1226
|
+
chalk6.green(
|
|
1227
1227
|
`\u{1F4CB} Health: http://${config.getHost()}:${config.getPort()}/healthz`
|
|
1228
1228
|
)
|
|
1229
1229
|
);
|
|
1230
1230
|
console.log(
|
|
1231
|
-
|
|
1231
|
+
chalk6.green(
|
|
1232
1232
|
`\u{1F4CA} Stats: http://${config.getHost()}:${config.getPort()}/stats`
|
|
1233
1233
|
)
|
|
1234
1234
|
);
|
|
1235
1235
|
console.log(
|
|
1236
|
-
|
|
1236
|
+
chalk6.blue(
|
|
1237
1237
|
"\n\u{1F4A1} Use --local flag with other commands to use local registry"
|
|
1238
1238
|
)
|
|
1239
1239
|
);
|
|
1240
1240
|
} catch (error) {
|
|
1241
1241
|
spinner2.fail("Failed to start local registry");
|
|
1242
1242
|
if (error instanceof Error) {
|
|
1243
|
-
console.error(
|
|
1243
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
1244
1244
|
}
|
|
1245
1245
|
process.exit(1);
|
|
1246
1246
|
}
|
|
@@ -1257,7 +1257,7 @@ localCommand.addCommand(
|
|
|
1257
1257
|
} catch (error) {
|
|
1258
1258
|
spinner2.fail("Failed to stop local registry");
|
|
1259
1259
|
if (error instanceof Error) {
|
|
1260
|
-
console.error(
|
|
1260
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
1261
1261
|
}
|
|
1262
1262
|
process.exit(1);
|
|
1263
1263
|
}
|
|
@@ -1272,16 +1272,16 @@ localCommand.addCommand(
|
|
|
1272
1272
|
const status = await server.getStatus();
|
|
1273
1273
|
if (status.running) {
|
|
1274
1274
|
spinner2.succeed("Local registry is running");
|
|
1275
|
-
console.log(
|
|
1276
|
-
console.log(
|
|
1277
|
-
console.log(
|
|
1275
|
+
console.log(chalk6.green(`\u2705 Server: ${status.url}`));
|
|
1276
|
+
console.log(chalk6.green(`\u{1F4C1} Data: ${status.dataDir}`));
|
|
1277
|
+
console.log(chalk6.green(`\u{1F4CA} Apps: ${status.appsCount} applications`));
|
|
1278
1278
|
console.log(
|
|
1279
|
-
|
|
1279
|
+
chalk6.green(`\u{1F4E6} Artifacts: ${status.artifactsCount} artifacts`)
|
|
1280
1280
|
);
|
|
1281
1281
|
} else {
|
|
1282
1282
|
spinner2.warn("Local registry is not running");
|
|
1283
1283
|
console.log(
|
|
1284
|
-
|
|
1284
|
+
chalk6.yellow(
|
|
1285
1285
|
'\u{1F4A1} Run "calimero-registry local start" to start the local registry'
|
|
1286
1286
|
)
|
|
1287
1287
|
);
|
|
@@ -1289,7 +1289,7 @@ localCommand.addCommand(
|
|
|
1289
1289
|
} catch (error) {
|
|
1290
1290
|
spinner2.fail("Failed to check local registry status");
|
|
1291
1291
|
if (error instanceof Error) {
|
|
1292
|
-
console.error(
|
|
1292
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
1293
1293
|
}
|
|
1294
1294
|
process.exit(1);
|
|
1295
1295
|
}
|
|
@@ -1299,9 +1299,9 @@ localCommand.addCommand(
|
|
|
1299
1299
|
new Command("reset").description("Reset local registry data").option("-f, --force", "Force reset without confirmation").action(async (options) => {
|
|
1300
1300
|
if (!options.force) {
|
|
1301
1301
|
console.log(
|
|
1302
|
-
|
|
1302
|
+
chalk6.yellow("\u26A0\uFE0F This will delete all local registry data!")
|
|
1303
1303
|
);
|
|
1304
|
-
console.log(
|
|
1304
|
+
console.log(chalk6.yellow(" Use --force flag to confirm"));
|
|
1305
1305
|
return;
|
|
1306
1306
|
}
|
|
1307
1307
|
const spinner2 = ora6("Resetting local registry data...").start();
|
|
@@ -1310,11 +1310,11 @@ localCommand.addCommand(
|
|
|
1310
1310
|
const server = new LocalRegistryServer(config);
|
|
1311
1311
|
await server.reset();
|
|
1312
1312
|
spinner2.succeed("Local registry data reset");
|
|
1313
|
-
console.log(
|
|
1313
|
+
console.log(chalk6.green("\u2705 All local data has been cleared"));
|
|
1314
1314
|
} catch (error) {
|
|
1315
1315
|
spinner2.fail("Failed to reset local registry data");
|
|
1316
1316
|
if (error instanceof Error) {
|
|
1317
|
-
console.error(
|
|
1317
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
1318
1318
|
}
|
|
1319
1319
|
process.exit(1);
|
|
1320
1320
|
}
|
|
@@ -1328,11 +1328,11 @@ localCommand.addCommand(
|
|
|
1328
1328
|
const server = new LocalRegistryServer(config);
|
|
1329
1329
|
const backupPath = await server.backup(options.output);
|
|
1330
1330
|
spinner2.succeed("Backup created successfully");
|
|
1331
|
-
console.log(
|
|
1331
|
+
console.log(chalk6.green(`\u{1F4E6} Backup saved to: ${backupPath}`));
|
|
1332
1332
|
} catch (error) {
|
|
1333
1333
|
spinner2.fail("Failed to create backup");
|
|
1334
1334
|
if (error instanceof Error) {
|
|
1335
|
-
console.error(
|
|
1335
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
1336
1336
|
}
|
|
1337
1337
|
process.exit(1);
|
|
1338
1338
|
}
|
|
@@ -1344,18 +1344,18 @@ localCommand.addCommand(
|
|
|
1344
1344
|
try {
|
|
1345
1345
|
if (!fs3.existsSync(backupFile)) {
|
|
1346
1346
|
spinner2.fail("Backup file not found");
|
|
1347
|
-
console.error(
|
|
1347
|
+
console.error(chalk6.red(`File not found: ${backupFile}`));
|
|
1348
1348
|
process.exit(1);
|
|
1349
1349
|
}
|
|
1350
1350
|
const config = new LocalConfig();
|
|
1351
1351
|
const server = new LocalRegistryServer(config);
|
|
1352
1352
|
await server.restore(backupFile);
|
|
1353
1353
|
spinner2.succeed("Data restored successfully");
|
|
1354
|
-
console.log(
|
|
1354
|
+
console.log(chalk6.green(`\u2705 Restored from: ${backupFile}`));
|
|
1355
1355
|
} catch (error) {
|
|
1356
1356
|
spinner2.fail("Failed to restore from backup");
|
|
1357
1357
|
if (error instanceof Error) {
|
|
1358
|
-
console.error(
|
|
1358
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
1359
1359
|
}
|
|
1360
1360
|
process.exit(1);
|
|
1361
1361
|
}
|
|
@@ -1370,22 +1370,139 @@ localCommand.addCommand(
|
|
|
1370
1370
|
await server.seed();
|
|
1371
1371
|
spinner2.succeed("Sample data seeded successfully");
|
|
1372
1372
|
console.log(
|
|
1373
|
-
|
|
1373
|
+
chalk6.green("\u2705 Local registry populated with sample applications")
|
|
1374
1374
|
);
|
|
1375
1375
|
console.log(
|
|
1376
|
-
|
|
1376
|
+
chalk6.blue(
|
|
1377
1377
|
'\u{1F4A1} Run "calimero-registry apps list --local" to see the sample apps'
|
|
1378
1378
|
)
|
|
1379
1379
|
);
|
|
1380
1380
|
} catch (error) {
|
|
1381
1381
|
spinner2.fail("Failed to seed sample data");
|
|
1382
1382
|
if (error instanceof Error) {
|
|
1383
|
-
console.error(
|
|
1383
|
+
console.error(chalk6.red(`Error: ${error.message}`));
|
|
1384
1384
|
}
|
|
1385
1385
|
process.exit(1);
|
|
1386
1386
|
}
|
|
1387
1387
|
})
|
|
1388
1388
|
);
|
|
1389
|
+
var RemoteConfig = class {
|
|
1390
|
+
constructor() {
|
|
1391
|
+
this.configPath = path7.join(
|
|
1392
|
+
os.homedir(),
|
|
1393
|
+
".calimero-registry",
|
|
1394
|
+
"remote-config.json"
|
|
1395
|
+
);
|
|
1396
|
+
this.config = this.loadConfig();
|
|
1397
|
+
}
|
|
1398
|
+
loadConfig() {
|
|
1399
|
+
const defaultConfig = {
|
|
1400
|
+
registry: {
|
|
1401
|
+
url: "https://apps.calimero.network"
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
if (fs3.existsSync(this.configPath)) {
|
|
1405
|
+
try {
|
|
1406
|
+
const existingConfig = JSON.parse(
|
|
1407
|
+
fs3.readFileSync(this.configPath, "utf8")
|
|
1408
|
+
);
|
|
1409
|
+
return {
|
|
1410
|
+
registry: {
|
|
1411
|
+
...defaultConfig.registry,
|
|
1412
|
+
...existingConfig.registry || {}
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
} catch {
|
|
1416
|
+
console.warn("Failed to load existing config, using defaults");
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return defaultConfig;
|
|
1420
|
+
}
|
|
1421
|
+
saveConfig() {
|
|
1422
|
+
const configDir = path7.dirname(this.configPath);
|
|
1423
|
+
if (!fs3.existsSync(configDir)) {
|
|
1424
|
+
fs3.mkdirSync(configDir, { recursive: true });
|
|
1425
|
+
}
|
|
1426
|
+
const configToSave = {
|
|
1427
|
+
registry: {
|
|
1428
|
+
url: this.config.registry.url,
|
|
1429
|
+
// Save API key if it exists in config (regardless of env var)
|
|
1430
|
+
...this.config.registry.apiKey ? { apiKey: this.config.registry.apiKey } : {}
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
fs3.writeFileSync(this.configPath, JSON.stringify(configToSave, null, 2));
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Get registry URL with priority:
|
|
1437
|
+
* 1. Environment variable CALIMERO_REGISTRY_URL
|
|
1438
|
+
* 2. Config file value
|
|
1439
|
+
* 3. Default value
|
|
1440
|
+
*/
|
|
1441
|
+
getRegistryUrl() {
|
|
1442
|
+
return process.env.CALIMERO_REGISTRY_URL || this.config.registry.url || "https://apps.calimero.network";
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Set registry URL
|
|
1446
|
+
*/
|
|
1447
|
+
setRegistryUrl(url) {
|
|
1448
|
+
this.config.registry.url = url;
|
|
1449
|
+
this.saveConfig();
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Get API key with priority:
|
|
1453
|
+
* 1. Environment variable CALIMERO_API_KEY
|
|
1454
|
+
* 2. Config file value
|
|
1455
|
+
* 3. undefined
|
|
1456
|
+
*/
|
|
1457
|
+
getApiKey() {
|
|
1458
|
+
return process.env.CALIMERO_API_KEY || this.config.registry.apiKey;
|
|
1459
|
+
}
|
|
1460
|
+
/**
|
|
1461
|
+
* Set API key (stored in config file)
|
|
1462
|
+
*/
|
|
1463
|
+
setApiKey(apiKey) {
|
|
1464
|
+
this.config.registry.apiKey = apiKey;
|
|
1465
|
+
this.saveConfig();
|
|
1466
|
+
}
|
|
1467
|
+
/**
|
|
1468
|
+
* Remove API key from config
|
|
1469
|
+
*/
|
|
1470
|
+
removeApiKey() {
|
|
1471
|
+
delete this.config.registry.apiKey;
|
|
1472
|
+
this.saveConfig();
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Get full configuration
|
|
1476
|
+
*/
|
|
1477
|
+
getConfig() {
|
|
1478
|
+
return {
|
|
1479
|
+
registry: {
|
|
1480
|
+
url: this.getRegistryUrl(),
|
|
1481
|
+
// Don't expose API key in getConfig (security)
|
|
1482
|
+
apiKey: this.getApiKey() ? "***" : void 0
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Get config file path
|
|
1488
|
+
*/
|
|
1489
|
+
getConfigPath() {
|
|
1490
|
+
return this.configPath;
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Reset to defaults
|
|
1494
|
+
*/
|
|
1495
|
+
reset() {
|
|
1496
|
+
this.config = {
|
|
1497
|
+
registry: {
|
|
1498
|
+
url: "https://apps.calimero.network"
|
|
1499
|
+
}
|
|
1500
|
+
};
|
|
1501
|
+
this.saveConfig();
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1505
|
+
// src/commands/bundle.ts
|
|
1389
1506
|
var bundleCommand = new Command("bundle").description("Manage application bundles (V2)").addCommand(createCreateCommand()).addCommand(createPushCommand()).addCommand(createGetCommand());
|
|
1390
1507
|
function createCreateCommand() {
|
|
1391
1508
|
return new Command("create").description("Create an MPK bundle from a WASM file").argument("<wasm-file>", "Path to the WASM file").argument("<package>", "Package name (e.g. com.calimero.myapp)").argument("<version>", "Version (e.g. 1.0.0)").option("-o, --output <path>", "Output path for the MPK file").option("--name <name>", "Application name").option("--description <description>", "Application description").option("--author <author>", "Application author", "Calimero Team").option("--frontend <url>", "Frontend URL").option("--github <url>", "GitHub URL").option("--docs <url>", "Documentation URL").option(
|
|
@@ -1402,12 +1519,12 @@ function createCreateCommand() {
|
|
|
1402
1519
|
}
|
|
1403
1520
|
).action(async (wasmFile, pkg, version, options) => {
|
|
1404
1521
|
try {
|
|
1405
|
-
const wasmPath =
|
|
1522
|
+
const wasmPath = path7.resolve(wasmFile);
|
|
1406
1523
|
if (!fs3.existsSync(wasmPath)) {
|
|
1407
1524
|
console.error(`\u274C WASM file not found: ${wasmFile}`);
|
|
1408
1525
|
process.exit(1);
|
|
1409
1526
|
}
|
|
1410
|
-
console.log(`\u{1F4E6} Creating MPK bundle from: ${
|
|
1527
|
+
console.log(`\u{1F4E6} Creating MPK bundle from: ${path7.basename(wasmPath)}`);
|
|
1411
1528
|
const wasmContent = fs3.readFileSync(wasmPath);
|
|
1412
1529
|
const wasmSize = wasmContent.length;
|
|
1413
1530
|
const hash = crypto.createHash("sha256").update(wasmContent).digest("hex");
|
|
@@ -1442,29 +1559,29 @@ function createCreateCommand() {
|
|
|
1442
1559
|
};
|
|
1443
1560
|
let outputPath = options.output;
|
|
1444
1561
|
if (!outputPath) {
|
|
1445
|
-
const outputDir =
|
|
1562
|
+
const outputDir = path7.join(process.cwd(), pkg, version);
|
|
1446
1563
|
if (!fs3.existsSync(outputDir)) {
|
|
1447
1564
|
fs3.mkdirSync(outputDir, { recursive: true });
|
|
1448
1565
|
}
|
|
1449
|
-
outputPath =
|
|
1566
|
+
outputPath = path7.join(outputDir, `${pkg}-${version}.mpk`);
|
|
1450
1567
|
} else {
|
|
1451
|
-
outputPath =
|
|
1452
|
-
const outputDir =
|
|
1568
|
+
outputPath = path7.resolve(outputPath);
|
|
1569
|
+
const outputDir = path7.dirname(outputPath);
|
|
1453
1570
|
if (!fs3.existsSync(outputDir)) {
|
|
1454
1571
|
fs3.mkdirSync(outputDir, { recursive: true });
|
|
1455
1572
|
}
|
|
1456
1573
|
}
|
|
1457
|
-
const tempDir =
|
|
1458
|
-
|
|
1574
|
+
const tempDir = path7.join(
|
|
1575
|
+
path7.dirname(outputPath),
|
|
1459
1576
|
`.temp-bundle-${Date.now()}`
|
|
1460
1577
|
);
|
|
1461
1578
|
fs3.mkdirSync(tempDir, { recursive: true });
|
|
1462
1579
|
try {
|
|
1463
1580
|
fs3.writeFileSync(
|
|
1464
|
-
|
|
1581
|
+
path7.join(tempDir, "manifest.json"),
|
|
1465
1582
|
JSON.stringify(manifest, null, 2)
|
|
1466
1583
|
);
|
|
1467
|
-
fs3.writeFileSync(
|
|
1584
|
+
fs3.writeFileSync(path7.join(tempDir, "app.wasm"), wasmContent);
|
|
1468
1585
|
await tar.create(
|
|
1469
1586
|
{
|
|
1470
1587
|
gzip: true,
|
|
@@ -1492,14 +1609,66 @@ function createCreateCommand() {
|
|
|
1492
1609
|
});
|
|
1493
1610
|
}
|
|
1494
1611
|
function createPushCommand() {
|
|
1495
|
-
return new Command("push").description(
|
|
1612
|
+
return new Command("push").description(
|
|
1613
|
+
"Push a bundle (.mpk) to the registry (local or remote). Defaults to local registry."
|
|
1614
|
+
).argument("<bundle-file>", "Path to the .mpk bundle file").option("--local", "Push to local registry instance (default)").option("--remote", "Push to remote registry instance").option(
|
|
1615
|
+
"--url <registry-url>",
|
|
1616
|
+
"Registry URL for remote push (overrides config file)"
|
|
1617
|
+
).option(
|
|
1618
|
+
"--api-key <key>",
|
|
1619
|
+
"API key for authentication (overrides config file and env var)"
|
|
1620
|
+
).addHelpText(
|
|
1621
|
+
"after",
|
|
1622
|
+
`
|
|
1623
|
+
Examples:
|
|
1624
|
+
$ calimero-registry bundle push bundle.mpk --local
|
|
1625
|
+
$ calimero-registry bundle push bundle.mpk --remote
|
|
1626
|
+
$ calimero-registry bundle push bundle.mpk --remote --url https://apps.calimero.network
|
|
1627
|
+
$ calimero-registry bundle push bundle.mpk --remote --api-key your-api-key
|
|
1628
|
+
|
|
1629
|
+
Configuration:
|
|
1630
|
+
Set defaults using the config command:
|
|
1631
|
+
$ calimero-registry config set registry-url https://apps.calimero.network
|
|
1632
|
+
$ calimero-registry config set api-key your-api-key
|
|
1633
|
+
|
|
1634
|
+
Or use environment variables:
|
|
1635
|
+
$ export CALIMERO_REGISTRY_URL=https://apps.calimero.network
|
|
1636
|
+
$ export CALIMERO_API_KEY=your-api-key
|
|
1637
|
+
|
|
1638
|
+
Note:
|
|
1639
|
+
- Use --local for development/testing with local registry
|
|
1640
|
+
- Use --remote for production deployments
|
|
1641
|
+
- Config file values are used unless overridden by flags or environment variables
|
|
1642
|
+
- Priority: flag > environment variable > config file > default
|
|
1643
|
+
`
|
|
1644
|
+
).action(async (bundleFile, options) => {
|
|
1496
1645
|
try {
|
|
1497
|
-
|
|
1646
|
+
if (!options.remote && (options.url || options.apiKey)) {
|
|
1647
|
+
console.warn(
|
|
1648
|
+
"\u26A0\uFE0F Warning: --url and --api-key are only used with --remote flag"
|
|
1649
|
+
);
|
|
1650
|
+
console.warn(
|
|
1651
|
+
" These options will be ignored. Use --remote to push to remote registry."
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1654
|
+
const useLocal = options.local === false ? false : options.local === true ? true : !options.remote;
|
|
1655
|
+
const useRemote = options.remote === true;
|
|
1656
|
+
if (options.local && options.remote) {
|
|
1657
|
+
console.error("\u274C Cannot use both --local and --remote flags");
|
|
1658
|
+
process.exit(1);
|
|
1659
|
+
}
|
|
1660
|
+
if (!useLocal && !useRemote) {
|
|
1661
|
+
console.error(
|
|
1662
|
+
"\u274C No push mode specified. Use --local or --remote flag"
|
|
1663
|
+
);
|
|
1664
|
+
process.exit(1);
|
|
1665
|
+
}
|
|
1666
|
+
const fullPath = path7.resolve(bundleFile);
|
|
1498
1667
|
if (!fs3.existsSync(fullPath)) {
|
|
1499
1668
|
console.error(`\u274C File not found: ${bundleFile}`);
|
|
1500
1669
|
process.exit(1);
|
|
1501
1670
|
}
|
|
1502
|
-
console.log(`\u{1F4E6} Processing bundle: ${
|
|
1671
|
+
console.log(`\u{1F4E6} Processing bundle: ${path7.basename(fullPath)}`);
|
|
1503
1672
|
const manifest = await extractManifest(fullPath);
|
|
1504
1673
|
if (!manifest) {
|
|
1505
1674
|
console.error("\u274C manifest.json not found in bundle");
|
|
@@ -1514,7 +1683,7 @@ function createPushCommand() {
|
|
|
1514
1683
|
if (manifest.metadata) {
|
|
1515
1684
|
console.log(` Name: ${manifest.metadata.name}`);
|
|
1516
1685
|
}
|
|
1517
|
-
if (
|
|
1686
|
+
if (useLocal) {
|
|
1518
1687
|
const config = new LocalConfig();
|
|
1519
1688
|
const store = new LocalDataStore(config);
|
|
1520
1689
|
const artifactServer = new LocalArtifactServer(config, store);
|
|
@@ -1535,12 +1704,15 @@ function createPushCommand() {
|
|
|
1535
1704
|
console.log(
|
|
1536
1705
|
` Run 'calimero-registry bundle get ${manifest.package} ${manifest.appVersion}' to verify`
|
|
1537
1706
|
);
|
|
1538
|
-
} else {
|
|
1539
|
-
|
|
1540
|
-
process.
|
|
1707
|
+
} else if (useRemote) {
|
|
1708
|
+
const remoteConfig = new RemoteConfig();
|
|
1709
|
+
const registryUrl = options.url || process.env.CALIMERO_REGISTRY_URL || remoteConfig.getRegistryUrl();
|
|
1710
|
+
const apiKey = options.apiKey || process.env.CALIMERO_API_KEY || remoteConfig.getApiKey();
|
|
1711
|
+
await pushToRemote(fullPath, manifest, registryUrl, apiKey);
|
|
1541
1712
|
}
|
|
1542
1713
|
} catch (error) {
|
|
1543
|
-
|
|
1714
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1715
|
+
console.error("\u274C Failed to push bundle:", message);
|
|
1544
1716
|
process.exit(1);
|
|
1545
1717
|
}
|
|
1546
1718
|
});
|
|
@@ -1568,6 +1740,174 @@ function createGetCommand() {
|
|
|
1568
1740
|
}
|
|
1569
1741
|
});
|
|
1570
1742
|
}
|
|
1743
|
+
async function pushToRemote(bundlePath, manifest, registryUrl, apiKey) {
|
|
1744
|
+
try {
|
|
1745
|
+
console.log(`\u{1F4E4} Pushing to remote registry: ${registryUrl}`);
|
|
1746
|
+
const bundleBuffer = fs3.readFileSync(bundlePath);
|
|
1747
|
+
const bundleSize = bundleBuffer.length;
|
|
1748
|
+
console.log(` Bundle size: ${bundleSize} bytes`);
|
|
1749
|
+
const bundleHex = bundleBuffer.toString("hex");
|
|
1750
|
+
console.log(` Converted to hex (${bundleHex.length} characters)`);
|
|
1751
|
+
const payload = {
|
|
1752
|
+
version: manifest.version || "1.0",
|
|
1753
|
+
package: manifest.package,
|
|
1754
|
+
appVersion: manifest.appVersion,
|
|
1755
|
+
_binary: bundleHex,
|
|
1756
|
+
_overwrite: true
|
|
1757
|
+
};
|
|
1758
|
+
if (manifest.metadata) {
|
|
1759
|
+
payload.metadata = manifest.metadata;
|
|
1760
|
+
}
|
|
1761
|
+
if (manifest.interfaces) {
|
|
1762
|
+
payload.interfaces = manifest.interfaces;
|
|
1763
|
+
}
|
|
1764
|
+
if (manifest.wasm) {
|
|
1765
|
+
payload.wasm = manifest.wasm;
|
|
1766
|
+
}
|
|
1767
|
+
if (manifest.abi) {
|
|
1768
|
+
payload.abi = manifest.abi;
|
|
1769
|
+
}
|
|
1770
|
+
if (manifest.migrations && manifest.migrations.length > 0) {
|
|
1771
|
+
payload.migrations = manifest.migrations;
|
|
1772
|
+
}
|
|
1773
|
+
if (manifest.links) {
|
|
1774
|
+
payload.links = manifest.links;
|
|
1775
|
+
}
|
|
1776
|
+
if (manifest.signature) {
|
|
1777
|
+
payload.signature = manifest.signature;
|
|
1778
|
+
}
|
|
1779
|
+
const headers = {
|
|
1780
|
+
"Content-Type": "application/json"
|
|
1781
|
+
};
|
|
1782
|
+
const finalApiKey = apiKey || process.env.CALIMERO_API_KEY;
|
|
1783
|
+
if (finalApiKey) {
|
|
1784
|
+
headers["Authorization"] = `Bearer ${finalApiKey}`;
|
|
1785
|
+
}
|
|
1786
|
+
const apiUrl = `${registryUrl.replace(/\/$/, "")}/api/v2/bundles/push`;
|
|
1787
|
+
console.log(` POST ${apiUrl}`);
|
|
1788
|
+
const controller = new AbortController();
|
|
1789
|
+
const timeoutId = setTimeout(() => controller.abort(), 6e4);
|
|
1790
|
+
try {
|
|
1791
|
+
const response = await fetch(apiUrl, {
|
|
1792
|
+
method: "POST",
|
|
1793
|
+
headers,
|
|
1794
|
+
body: JSON.stringify(payload),
|
|
1795
|
+
signal: controller.signal
|
|
1796
|
+
});
|
|
1797
|
+
clearTimeout(timeoutId);
|
|
1798
|
+
const responseText = await response.text();
|
|
1799
|
+
let responseBody;
|
|
1800
|
+
try {
|
|
1801
|
+
responseBody = JSON.parse(responseText);
|
|
1802
|
+
} catch {
|
|
1803
|
+
responseBody = { message: responseText };
|
|
1804
|
+
}
|
|
1805
|
+
if (response.status === 201) {
|
|
1806
|
+
console.log("\u2705 Bundle manifest uploaded successfully");
|
|
1807
|
+
console.log(` Package: ${responseBody.package || manifest.package}`);
|
|
1808
|
+
console.log(
|
|
1809
|
+
` Version: ${responseBody.version || manifest.appVersion}`
|
|
1810
|
+
);
|
|
1811
|
+
await verifyRemotePush(
|
|
1812
|
+
registryUrl,
|
|
1813
|
+
manifest.package,
|
|
1814
|
+
manifest.appVersion
|
|
1815
|
+
);
|
|
1816
|
+
} else {
|
|
1817
|
+
handlePushError(response.status, responseBody);
|
|
1818
|
+
}
|
|
1819
|
+
} catch (fetchError) {
|
|
1820
|
+
clearTimeout(timeoutId);
|
|
1821
|
+
if (fetchError instanceof Error && fetchError.name === "AbortError") {
|
|
1822
|
+
console.error("\u274C Request timed out after 60 seconds");
|
|
1823
|
+
console.error(" The bundle may be too large or the server is slow");
|
|
1824
|
+
process.exit(1);
|
|
1825
|
+
}
|
|
1826
|
+
throw fetchError;
|
|
1827
|
+
}
|
|
1828
|
+
} catch (error) {
|
|
1829
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1830
|
+
console.error("\u274C Failed to push to remote registry:", message);
|
|
1831
|
+
throw error;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
async function verifyRemotePush(registryUrl, packageName, version) {
|
|
1835
|
+
try {
|
|
1836
|
+
console.log("\u{1F50D} Verifying upload to remote registry...");
|
|
1837
|
+
const verifyUrl = `${registryUrl.replace(/\/$/, "")}/api/v2/bundles/${packageName}/${version}`;
|
|
1838
|
+
const controller = new AbortController();
|
|
1839
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
1840
|
+
try {
|
|
1841
|
+
const response = await fetch(verifyUrl, {
|
|
1842
|
+
method: "GET",
|
|
1843
|
+
signal: controller.signal
|
|
1844
|
+
});
|
|
1845
|
+
clearTimeout(timeoutId);
|
|
1846
|
+
if (response.status === 200) {
|
|
1847
|
+
await response.json();
|
|
1848
|
+
console.log("\u2705 Bundle verified and accessible on remote registry");
|
|
1849
|
+
console.log(`\u{1F310} Registry: ${registryUrl}`);
|
|
1850
|
+
console.log(`\u{1F517} Endpoint: ${verifyUrl}`);
|
|
1851
|
+
} else {
|
|
1852
|
+
console.warn(
|
|
1853
|
+
`\u26A0\uFE0F Upload verification failed - bundle not found (HTTP ${response.status})`
|
|
1854
|
+
);
|
|
1855
|
+
console.warn(" The bundle may still be processing, check manually");
|
|
1856
|
+
}
|
|
1857
|
+
} catch (verifyError) {
|
|
1858
|
+
clearTimeout(timeoutId);
|
|
1859
|
+
if (verifyError instanceof Error && verifyError.name === "AbortError") {
|
|
1860
|
+
console.warn("\u26A0\uFE0F Verification request timed out");
|
|
1861
|
+
} else {
|
|
1862
|
+
const message = verifyError instanceof Error ? verifyError.message : String(verifyError);
|
|
1863
|
+
console.warn("\u26A0\uFE0F Verification request failed:", message);
|
|
1864
|
+
}
|
|
1865
|
+
console.warn(" The bundle may still be accessible, check manually");
|
|
1866
|
+
}
|
|
1867
|
+
} catch (error) {
|
|
1868
|
+
console.warn("\u26A0\uFE0F Could not verify bundle:", error);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
function handlePushError(statusCode, responseBody) {
|
|
1872
|
+
const errorMessage = responseBody.message || responseBody.error || "Unknown error";
|
|
1873
|
+
switch (statusCode) {
|
|
1874
|
+
case 400:
|
|
1875
|
+
console.error("\u274C Bad Request: Invalid manifest");
|
|
1876
|
+
console.error(` ${errorMessage}`);
|
|
1877
|
+
console.error(" Check that your bundle manifest is valid");
|
|
1878
|
+
break;
|
|
1879
|
+
case 401:
|
|
1880
|
+
console.error("\u274C Unauthorized: Authentication required");
|
|
1881
|
+
console.error(` ${errorMessage}`);
|
|
1882
|
+
console.error(
|
|
1883
|
+
" Provide an API key with --api-key or CALIMERO_API_KEY env var"
|
|
1884
|
+
);
|
|
1885
|
+
break;
|
|
1886
|
+
case 403:
|
|
1887
|
+
console.error("\u274C Forbidden: Namespace ownership required");
|
|
1888
|
+
console.error(` ${errorMessage}`);
|
|
1889
|
+
console.error(
|
|
1890
|
+
" Ensure you have permission to publish to this package namespace"
|
|
1891
|
+
);
|
|
1892
|
+
break;
|
|
1893
|
+
case 409:
|
|
1894
|
+
console.error("\u274C Conflict: Version already exists");
|
|
1895
|
+
console.error(` ${errorMessage}`);
|
|
1896
|
+
console.error(
|
|
1897
|
+
" Increment the version number and try again, or contact registry admin"
|
|
1898
|
+
);
|
|
1899
|
+
break;
|
|
1900
|
+
case 500:
|
|
1901
|
+
console.error("\u274C Internal Server Error");
|
|
1902
|
+
console.error(` ${errorMessage}`);
|
|
1903
|
+
console.error(" The registry server encountered an error");
|
|
1904
|
+
break;
|
|
1905
|
+
default:
|
|
1906
|
+
console.error(`\u274C Upload failed with HTTP ${statusCode}`);
|
|
1907
|
+
console.error(` ${errorMessage}`);
|
|
1908
|
+
}
|
|
1909
|
+
process.exit(1);
|
|
1910
|
+
}
|
|
1571
1911
|
async function extractManifest(bundlePath) {
|
|
1572
1912
|
let manifestContent = null;
|
|
1573
1913
|
await tar.t({
|
|
@@ -1576,7 +1916,7 @@ async function extractManifest(bundlePath) {
|
|
|
1576
1916
|
if (entry.path === "manifest.json") ;
|
|
1577
1917
|
}
|
|
1578
1918
|
});
|
|
1579
|
-
const extractDir =
|
|
1919
|
+
const extractDir = path7.join(path7.dirname(bundlePath), `.temp-${Date.now()}`);
|
|
1580
1920
|
if (!fs3.existsSync(extractDir)) {
|
|
1581
1921
|
fs3.mkdirSync(extractDir);
|
|
1582
1922
|
}
|
|
@@ -1584,9 +1924,9 @@ async function extractManifest(bundlePath) {
|
|
|
1584
1924
|
await tar.x({
|
|
1585
1925
|
file: bundlePath,
|
|
1586
1926
|
cwd: extractDir,
|
|
1587
|
-
filter: (
|
|
1927
|
+
filter: (path8) => path8 === "manifest.json"
|
|
1588
1928
|
});
|
|
1589
|
-
const manifestPath =
|
|
1929
|
+
const manifestPath = path7.join(extractDir, "manifest.json");
|
|
1590
1930
|
if (fs3.existsSync(manifestPath)) {
|
|
1591
1931
|
const content = fs3.readFileSync(manifestPath, "utf8");
|
|
1592
1932
|
manifestContent = content;
|
|
@@ -1607,6 +1947,156 @@ async function extractManifest(bundlePath) {
|
|
|
1607
1947
|
}
|
|
1608
1948
|
return null;
|
|
1609
1949
|
}
|
|
1950
|
+
var configCommand = new Command("config").description("Manage CLI configuration for remote registry").addCommand(createConfigSetCommand()).addCommand(createConfigGetCommand()).addCommand(createConfigListCommand()).addCommand(createConfigResetCommand());
|
|
1951
|
+
function createConfigSetCommand() {
|
|
1952
|
+
return new Command("set").description("Set a configuration value").argument("<key>", "Configuration key (registry-url, api-key)").argument("<value>", "Configuration value").action((key, value) => {
|
|
1953
|
+
try {
|
|
1954
|
+
const config = new RemoteConfig();
|
|
1955
|
+
switch (key) {
|
|
1956
|
+
case "registry-url":
|
|
1957
|
+
case "registryUrl":
|
|
1958
|
+
case "url":
|
|
1959
|
+
config.setRegistryUrl(value);
|
|
1960
|
+
console.log(
|
|
1961
|
+
chalk6.green(`\u2705 Registry URL set to: ${chalk6.bold(value)}`)
|
|
1962
|
+
);
|
|
1963
|
+
break;
|
|
1964
|
+
case "api-key":
|
|
1965
|
+
case "apiKey":
|
|
1966
|
+
config.setApiKey(value);
|
|
1967
|
+
console.log(chalk6.green("\u2705 API key set successfully"));
|
|
1968
|
+
console.log(
|
|
1969
|
+
chalk6.yellow(
|
|
1970
|
+
"\u{1F4A1} Note: API key is stored in plain text. Consider using CALIMERO_API_KEY environment variable for better security."
|
|
1971
|
+
)
|
|
1972
|
+
);
|
|
1973
|
+
break;
|
|
1974
|
+
default:
|
|
1975
|
+
console.error(chalk6.red(`\u274C Unknown configuration key: ${key}`));
|
|
1976
|
+
console.log(chalk6.blue("\nAvailable keys:"));
|
|
1977
|
+
console.log(" registry-url - Default registry URL");
|
|
1978
|
+
console.log(" api-key - API key for authentication");
|
|
1979
|
+
process.exit(1);
|
|
1980
|
+
}
|
|
1981
|
+
} catch (error) {
|
|
1982
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1983
|
+
console.error(chalk6.red("\u274C Failed to set configuration:"), message);
|
|
1984
|
+
process.exit(1);
|
|
1985
|
+
}
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
function createConfigGetCommand() {
|
|
1989
|
+
return new Command("get").description("Get a configuration value").argument("<key>", "Configuration key (registry-url, api-key)").action((key) => {
|
|
1990
|
+
try {
|
|
1991
|
+
const config = new RemoteConfig();
|
|
1992
|
+
switch (key) {
|
|
1993
|
+
case "registry-url":
|
|
1994
|
+
case "registryUrl":
|
|
1995
|
+
case "url":
|
|
1996
|
+
console.log(config.getRegistryUrl());
|
|
1997
|
+
break;
|
|
1998
|
+
case "api-key":
|
|
1999
|
+
case "apiKey": {
|
|
2000
|
+
const apiKey = config.getApiKey();
|
|
2001
|
+
if (apiKey) {
|
|
2002
|
+
const masked = apiKey.length > 8 ? `${apiKey.substring(0, 4)}...${apiKey.substring(apiKey.length - 4)}` : "***";
|
|
2003
|
+
console.log(masked);
|
|
2004
|
+
console.log(
|
|
2005
|
+
chalk6.yellow(
|
|
2006
|
+
'\u{1F4A1} Note: API key is masked. Use "config list" to see source.'
|
|
2007
|
+
)
|
|
2008
|
+
);
|
|
2009
|
+
} else {
|
|
2010
|
+
console.log("(not set)");
|
|
2011
|
+
}
|
|
2012
|
+
break;
|
|
2013
|
+
}
|
|
2014
|
+
default:
|
|
2015
|
+
console.error(chalk6.red(`\u274C Unknown configuration key: ${key}`));
|
|
2016
|
+
console.log(chalk6.blue("\nAvailable keys:"));
|
|
2017
|
+
console.log(" registry-url - Default registry URL");
|
|
2018
|
+
console.log(" api-key - API key for authentication");
|
|
2019
|
+
process.exit(1);
|
|
2020
|
+
}
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2023
|
+
console.error(chalk6.red("\u274C Failed to get configuration:"), message);
|
|
2024
|
+
process.exit(1);
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
function createConfigListCommand() {
|
|
2029
|
+
return new Command("list").description("List all configuration values").alias("ls").action(() => {
|
|
2030
|
+
try {
|
|
2031
|
+
const config = new RemoteConfig();
|
|
2032
|
+
console.log(chalk6.blue("\n\u{1F4CB} Remote Registry Configuration\n"));
|
|
2033
|
+
const configPath = config.getConfigPath();
|
|
2034
|
+
const configFileExists = fs3.existsSync(configPath);
|
|
2035
|
+
const url = config.getRegistryUrl();
|
|
2036
|
+
let urlSource;
|
|
2037
|
+
if (process.env.CALIMERO_REGISTRY_URL) {
|
|
2038
|
+
urlSource = chalk6.yellow("(from CALIMERO_REGISTRY_URL env var)");
|
|
2039
|
+
} else if (configFileExists) {
|
|
2040
|
+
urlSource = chalk6.gray("(from config file)");
|
|
2041
|
+
} else {
|
|
2042
|
+
urlSource = chalk6.gray("(default)");
|
|
2043
|
+
}
|
|
2044
|
+
console.log(` ${chalk6.bold("Registry URL:")} ${url} ${urlSource}`);
|
|
2045
|
+
const apiKey = config.getApiKey();
|
|
2046
|
+
if (apiKey) {
|
|
2047
|
+
let apiKeySource;
|
|
2048
|
+
if (process.env.CALIMERO_API_KEY) {
|
|
2049
|
+
apiKeySource = chalk6.yellow("(from CALIMERO_API_KEY env var)");
|
|
2050
|
+
} else if (configFileExists) {
|
|
2051
|
+
apiKeySource = chalk6.gray("(from config file)");
|
|
2052
|
+
} else {
|
|
2053
|
+
apiKeySource = chalk6.gray("(default)");
|
|
2054
|
+
}
|
|
2055
|
+
const masked = apiKey.length > 8 ? `${apiKey.substring(0, 4)}...${apiKey.substring(apiKey.length - 4)}` : "***";
|
|
2056
|
+
console.log(` ${chalk6.bold("API Key:")} ${masked} ${apiKeySource}`);
|
|
2057
|
+
} else {
|
|
2058
|
+
console.log(` ${chalk6.bold("API Key:")} ${chalk6.gray("(not set)")}`);
|
|
2059
|
+
}
|
|
2060
|
+
console.log(chalk6.blue(`
|
|
2061
|
+
\u{1F4C1} Config file: ${configPath}`));
|
|
2062
|
+
if (!configFileExists) {
|
|
2063
|
+
console.log(chalk6.gray(" (file does not exist, using defaults)"));
|
|
2064
|
+
}
|
|
2065
|
+
console.log();
|
|
2066
|
+
} catch (error) {
|
|
2067
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2068
|
+
console.error(chalk6.red("\u274C Failed to list configuration:"), message);
|
|
2069
|
+
process.exit(1);
|
|
2070
|
+
}
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
function createConfigResetCommand() {
|
|
2074
|
+
return new Command("reset").description("Reset configuration to defaults").option("--force", "Skip confirmation prompt").action((options) => {
|
|
2075
|
+
try {
|
|
2076
|
+
if (!options.force) {
|
|
2077
|
+
console.error(
|
|
2078
|
+
chalk6.red("\u274C Configuration reset requires --force flag")
|
|
2079
|
+
);
|
|
2080
|
+
console.log(
|
|
2081
|
+
chalk6.yellow(
|
|
2082
|
+
"\u26A0\uFE0F This will reset all configuration to defaults. Use --force to confirm."
|
|
2083
|
+
)
|
|
2084
|
+
);
|
|
2085
|
+
process.exit(1);
|
|
2086
|
+
}
|
|
2087
|
+
const config = new RemoteConfig();
|
|
2088
|
+
config.reset();
|
|
2089
|
+
console.log(chalk6.green("\u2705 Configuration reset to defaults"));
|
|
2090
|
+
console.log(chalk6.blue("\nDefault values:"));
|
|
2091
|
+
console.log(" Registry URL: https://apps.calimero.network");
|
|
2092
|
+
console.log(" API Key: (not set)");
|
|
2093
|
+
} catch (error) {
|
|
2094
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2095
|
+
console.error(chalk6.red("\u274C Failed to reset configuration:"), message);
|
|
2096
|
+
process.exit(1);
|
|
2097
|
+
}
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
1610
2100
|
|
|
1611
2101
|
// src/index.ts
|
|
1612
2102
|
var program = new Command();
|
|
@@ -1639,14 +2129,15 @@ program.addCommand(healthCommand);
|
|
|
1639
2129
|
program.addCommand(ipfsCommand);
|
|
1640
2130
|
program.addCommand(localCommand);
|
|
1641
2131
|
program.addCommand(bundleCommand);
|
|
2132
|
+
program.addCommand(configCommand);
|
|
1642
2133
|
program.exitOverride();
|
|
1643
2134
|
try {
|
|
1644
2135
|
program.parse();
|
|
1645
2136
|
} catch (err) {
|
|
1646
2137
|
if (err instanceof Error) {
|
|
1647
|
-
console.error(
|
|
2138
|
+
console.error(chalk6.red("Error:"), err.message);
|
|
1648
2139
|
} else {
|
|
1649
|
-
console.error(
|
|
2140
|
+
console.error(chalk6.red("Unknown error occurred"));
|
|
1650
2141
|
}
|
|
1651
2142
|
process.exit(1);
|
|
1652
2143
|
}
|