@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 CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import chalk5 from 'chalk';
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 path6 from 'path';
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 = path6.join(
17
+ this.configPath = path7.join(
18
18
  os.homedir(),
19
19
  ".calimero-registry",
20
20
  "config.json"
21
21
  );
22
- this.dataDir = path6.join(os.homedir(), ".calimero-registry");
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: path6.join(os.homedir(), ".calimero-registry", "data"),
34
- artifactsDir: path6.join(
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: path6.join(os.homedir(), ".calimero-registry", "artifacts"),
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 = path6.dirname(this.configPath);
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 = path6.join(dir, "artifacts");
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
- path6.join(this.getDataDir(), "backups"),
133
- path6.join(this.getDataDir(), "logs")
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: path6.join(os.homedir(), ".calimero-registry", "data"),
157
- artifactsDir: path6.join(
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: path6.join(os.homedir(), ".calimero-registry", "artifacts"),
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 = path6.join(config.getDataDir(), "apps.json");
178
- this.bundleManifestsFile = path6.join(
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 = path6.join(config.getDataDir(), "artifacts.json");
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 || path6.join(
302
+ const backupPath = outputPath || path7.join(
303
303
  this.config.getDataDir(),
304
304
  "backups",
305
305
  `backup-${timestamp}.json`
306
306
  );
307
- const backupDir = path6.dirname(backupPath);
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 = path6.join(this.artifactsDir, appId, version);
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 = path6.join(appVersionDir, filename);
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 = path6.join(this.artifactsDir, appId, version, filename);
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, path6.basename(manifest.artifact.uri))}`
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 = path6.basename(filePath);
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 = path6.join(dir, item);
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 = path6.join(dir, item);
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 = path6.basename(filePath);
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(chalk5.yellow("No applications found"));
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.yellow("No versions found"));
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 ? chalk5.red("Yes") : chalk5.green("No")
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.blue("\nApplication Manifest:"));
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(chalk5.red(`Error: ${error.message}`));
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 = path6.resolve(manifestFile);
755
+ const manifestPath = path7.resolve(manifestFile);
756
756
  if (!fs3.existsSync(manifestPath)) {
757
757
  spinner2.fail("Manifest file not found");
758
- console.error(chalk5.red(`File not found: ${manifestFile}`));
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(chalk5.green(`
766
+ console.log(chalk6.green(`
767
767
  \u2705 ${result.message}`));
768
768
  if (manifest.app?.name) {
769
- console.log(chalk5.blue(`
769
+ console.log(chalk6.blue(`
770
770
  \u{1F4F1} App: ${manifest.app.name}`));
771
771
  console.log(
772
- chalk5.blue(`\u{1F464} Developer: ${manifest.app.developer_pubkey}`)
772
+ chalk6.blue(`\u{1F464} Developer: ${manifest.app.developer_pubkey}`)
773
773
  );
774
- console.log(chalk5.blue(`\u{1F4E6} Version: ${manifest.version?.semver}`));
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.blue("\nDeveloper Profile:"));
797
- console.log(chalk5.green("Display Name:"), profile.display_name);
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(chalk5.green("Website:"), profile.website);
799
+ console.log(chalk6.green("Website:"), profile.website);
800
800
  }
801
801
  if (profile.proofs.length > 0) {
802
- console.log(chalk5.green("\nProofs:"));
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 ? chalk5.green("Yes") : chalk5.red("No")
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(chalk5.yellow("\nNo proofs found"));
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.red("Proofs must be a valid JSON array"));
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(chalk5.green(`
850
+ console.log(chalk6.green(`
851
851
  \u2705 ${result.message}`));
852
- console.log(chalk5.blue(`
852
+ console.log(chalk6.blue(`
853
853
  \u{1F464} Developer: ${displayName}`));
854
- console.log(chalk5.blue(`\u{1F511} Public Key: ${pubkey}`));
854
+ console.log(chalk6.blue(`\u{1F511} Public Key: ${pubkey}`));
855
855
  if (options.website) {
856
- console.log(chalk5.blue(`\u{1F310} Website: ${options.website}`));
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.blue("\nAttestation:"));
885
- console.log(chalk5.green("Status:"), attestation.status);
886
- console.log(chalk5.green("Timestamp:"), attestation.timestamp);
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(chalk5.green("Comment:"), attestation.comment);
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.green(`Status: ${health.status}`));
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.blue("\n\u{1F4F1} Local Registry Status:"));
1213
+ console.log(chalk6.blue("\n\u{1F4F1} Local Registry Status:"));
1214
1214
  console.log(
1215
- chalk5.green(
1215
+ chalk6.green(
1216
1216
  `\u2705 Server: http://${config.getHost()}:${config.getPort()}`
1217
1217
  )
1218
1218
  );
1219
1219
  console.log(
1220
- chalk5.green(
1220
+ chalk6.green(
1221
1221
  `\u{1F310} Public URL: http://${config.getPublicHost()}:${config.getPort()}`
1222
1222
  )
1223
1223
  );
1224
- console.log(chalk5.green(`\u{1F4C1} Data: ${config.getDataDir()}`));
1224
+ console.log(chalk6.green(`\u{1F4C1} Data: ${config.getDataDir()}`));
1225
1225
  console.log(
1226
- chalk5.green(
1226
+ chalk6.green(
1227
1227
  `\u{1F4CB} Health: http://${config.getHost()}:${config.getPort()}/healthz`
1228
1228
  )
1229
1229
  );
1230
1230
  console.log(
1231
- chalk5.green(
1231
+ chalk6.green(
1232
1232
  `\u{1F4CA} Stats: http://${config.getHost()}:${config.getPort()}/stats`
1233
1233
  )
1234
1234
  );
1235
1235
  console.log(
1236
- chalk5.blue(
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.green(`\u2705 Server: ${status.url}`));
1276
- console.log(chalk5.green(`\u{1F4C1} Data: ${status.dataDir}`));
1277
- console.log(chalk5.green(`\u{1F4CA} Apps: ${status.appsCount} applications`));
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
- chalk5.green(`\u{1F4E6} Artifacts: ${status.artifactsCount} artifacts`)
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
- chalk5.yellow(
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(chalk5.red(`Error: ${error.message}`));
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
- chalk5.yellow("\u26A0\uFE0F This will delete all local registry data!")
1302
+ chalk6.yellow("\u26A0\uFE0F This will delete all local registry data!")
1303
1303
  );
1304
- console.log(chalk5.yellow(" Use --force flag to confirm"));
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(chalk5.green("\u2705 All local data has been cleared"));
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.green(`\u{1F4E6} Backup saved to: ${backupPath}`));
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(chalk5.red(`Error: ${error.message}`));
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(chalk5.red(`File not found: ${backupFile}`));
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(chalk5.green(`\u2705 Restored from: ${backupFile}`));
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(chalk5.red(`Error: ${error.message}`));
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
- chalk5.green("\u2705 Local registry populated with sample applications")
1373
+ chalk6.green("\u2705 Local registry populated with sample applications")
1374
1374
  );
1375
1375
  console.log(
1376
- chalk5.blue(
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(chalk5.red(`Error: ${error.message}`));
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 = path6.resolve(wasmFile);
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: ${path6.basename(wasmPath)}`);
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 = path6.join(process.cwd(), pkg, version);
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 = path6.join(outputDir, `${pkg}-${version}.mpk`);
1566
+ outputPath = path7.join(outputDir, `${pkg}-${version}.mpk`);
1450
1567
  } else {
1451
- outputPath = path6.resolve(outputPath);
1452
- const outputDir = path6.dirname(outputPath);
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 = path6.join(
1458
- path6.dirname(outputPath),
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
- path6.join(tempDir, "manifest.json"),
1581
+ path7.join(tempDir, "manifest.json"),
1465
1582
  JSON.stringify(manifest, null, 2)
1466
1583
  );
1467
- fs3.writeFileSync(path6.join(tempDir, "app.wasm"), wasmContent);
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("Push a bundle (.mpk) to the registry").argument("<bundle-file>", "Path to the .mpk bundle file").option("--local", "Push to local registry instance", true).action(async (bundleFile, options) => {
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
- const fullPath = path6.resolve(bundleFile);
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: ${path6.basename(fullPath)}`);
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 (options.local) {
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
- console.error("\u274C Remote push not implemented yet");
1540
- process.exit(1);
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
- console.error("\u274C Failed to push bundle:", error);
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 = path6.join(path6.dirname(bundlePath), `.temp-${Date.now()}`);
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: (path7) => path7 === "manifest.json"
1927
+ filter: (path8) => path8 === "manifest.json"
1588
1928
  });
1589
- const manifestPath = path6.join(extractDir, "manifest.json");
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(chalk5.red("Error:"), err.message);
2138
+ console.error(chalk6.red("Error:"), err.message);
1648
2139
  } else {
1649
- console.error(chalk5.red("Unknown error occurred"));
2140
+ console.error(chalk6.red("Unknown error occurred"));
1650
2141
  }
1651
2142
  process.exit(1);
1652
2143
  }