@calimero-network/registry-cli 1.3.0 → 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,44 +1,44 @@
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 path 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';
12
12
  import cors from '@fastify/cors';
13
- import { Buffer } from 'buffer';
14
- import fetch from 'node-fetch';
13
+ import * as tar from 'tar';
15
14
 
16
15
  var LocalConfig = class {
17
16
  constructor() {
18
- this.configPath = path.join(
17
+ this.configPath = path7.join(
19
18
  os.homedir(),
20
19
  ".calimero-registry",
21
20
  "config.json"
22
21
  );
23
- this.dataDir = path.join(os.homedir(), ".calimero-registry");
22
+ this.dataDir = path7.join(os.homedir(), ".calimero-registry");
24
23
  this.config = this.loadConfig();
25
24
  }
26
25
  loadConfig() {
27
26
  const defaultConfig = {
28
27
  server: {
29
28
  port: 8082,
30
- host: "localhost"
29
+ host: "0.0.0.0",
30
+ publicHost: "host.docker.internal"
31
31
  },
32
32
  data: {
33
- dir: path.join(os.homedir(), ".calimero-registry", "data"),
34
- artifactsDir: path.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: path.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",
@@ -50,7 +50,20 @@ var LocalConfig = class {
50
50
  const existingConfig = JSON.parse(
51
51
  fs3.readFileSync(this.configPath, "utf8")
52
52
  );
53
- return { ...defaultConfig, ...existingConfig };
53
+ return {
54
+ server: {
55
+ ...defaultConfig.server,
56
+ ...existingConfig.server || {}
57
+ },
58
+ data: {
59
+ ...defaultConfig.data,
60
+ ...existingConfig.data || {}
61
+ },
62
+ artifacts: {
63
+ ...defaultConfig.artifacts,
64
+ ...existingConfig.artifacts || {}
65
+ }
66
+ };
54
67
  } catch {
55
68
  console.warn("Failed to load existing config, using defaults");
56
69
  }
@@ -58,7 +71,7 @@ var LocalConfig = class {
58
71
  return defaultConfig;
59
72
  }
60
73
  saveConfig() {
61
- const configDir = path.dirname(this.configPath);
74
+ const configDir = path7.dirname(this.configPath);
62
75
  if (!fs3.existsSync(configDir)) {
63
76
  fs3.mkdirSync(configDir, { recursive: true });
64
77
  }
@@ -79,13 +92,20 @@ var LocalConfig = class {
79
92
  this.config.server.host = host;
80
93
  this.saveConfig();
81
94
  }
95
+ getPublicHost() {
96
+ return this.config.server.publicHost;
97
+ }
98
+ setPublicHost(host) {
99
+ this.config.server.publicHost = host;
100
+ this.saveConfig();
101
+ }
82
102
  // Data directory configuration
83
103
  getDataDir() {
84
104
  return this.config.data.dir;
85
105
  }
86
106
  setDataDir(dir) {
87
107
  this.config.data.dir = dir;
88
- this.config.artifacts.storageDir = path.join(dir, "artifacts");
108
+ this.config.artifacts.storageDir = path7.join(dir, "artifacts");
89
109
  this.saveConfig();
90
110
  }
91
111
  getArtifactsDir() {
@@ -109,8 +129,8 @@ var LocalConfig = class {
109
129
  const dirs = [
110
130
  this.getDataDir(),
111
131
  this.getArtifactsDir(),
112
- path.join(this.getDataDir(), "backups"),
113
- path.join(this.getDataDir(), "logs")
132
+ path7.join(this.getDataDir(), "backups"),
133
+ path7.join(this.getDataDir(), "logs")
114
134
  ];
115
135
  dirs.forEach((dir) => {
116
136
  if (!fs3.existsSync(dir)) {
@@ -133,15 +153,15 @@ var LocalConfig = class {
133
153
  host: "localhost"
134
154
  },
135
155
  data: {
136
- dir: path.join(os.homedir(), ".calimero-registry", "data"),
137
- artifactsDir: path.join(
156
+ dir: path7.join(os.homedir(), ".calimero-registry", "data"),
157
+ artifactsDir: path7.join(
138
158
  os.homedir(),
139
159
  ".calimero-registry",
140
160
  "artifacts"
141
161
  )
142
162
  },
143
163
  artifacts: {
144
- storageDir: path.join(os.homedir(), ".calimero-registry", "artifacts"),
164
+ storageDir: path7.join(os.homedir(), ".calimero-registry", "artifacts"),
145
165
  serveLocal: true,
146
166
  copyArtifacts: true,
147
167
  maxFileSize: "100MB",
@@ -154,12 +174,15 @@ var LocalConfig = class {
154
174
  var LocalDataStore = class {
155
175
  constructor(config) {
156
176
  this.config = config;
157
- this.appsFile = path.join(config.getDataDir(), "apps.json");
158
- this.manifestsFile = path.join(config.getDataDir(), "manifests.json");
159
- this.artifactsFile = path.join(config.getDataDir(), "artifacts.json");
177
+ this.appsFile = path7.join(config.getDataDir(), "apps.json");
178
+ this.bundleManifestsFile = path7.join(
179
+ config.getDataDir(),
180
+ "bundle_manifests.json"
181
+ );
182
+ this.artifactsFile = path7.join(config.getDataDir(), "artifacts.json");
160
183
  this.data = {
161
184
  apps: /* @__PURE__ */ new Map(),
162
- manifests: /* @__PURE__ */ new Map(),
185
+ bundleManifests: /* @__PURE__ */ new Map(),
163
186
  artifacts: /* @__PURE__ */ new Map()
164
187
  };
165
188
  this.loadData();
@@ -170,11 +193,13 @@ var LocalDataStore = class {
170
193
  const appsData = JSON.parse(fs3.readFileSync(this.appsFile, "utf8"));
171
194
  this.data.apps = new Map(Object.entries(appsData));
172
195
  }
173
- if (fs3.existsSync(this.manifestsFile)) {
174
- const manifestsData = JSON.parse(
175
- fs3.readFileSync(this.manifestsFile, "utf8")
196
+ if (fs3.existsSync(this.bundleManifestsFile)) {
197
+ const bundleManifestsData = JSON.parse(
198
+ fs3.readFileSync(this.bundleManifestsFile, "utf8")
199
+ );
200
+ this.data.bundleManifests = new Map(
201
+ Object.entries(bundleManifestsData)
176
202
  );
177
- this.data.manifests = new Map(Object.entries(manifestsData));
178
203
  }
179
204
  if (fs3.existsSync(this.artifactsFile)) {
180
205
  const artifactsData = JSON.parse(
@@ -191,10 +216,10 @@ var LocalDataStore = class {
191
216
  this.config.ensureDirectories();
192
217
  const appsObj = Object.fromEntries(this.data.apps);
193
218
  fs3.writeFileSync(this.appsFile, JSON.stringify(appsObj, null, 2));
194
- const manifestsObj = Object.fromEntries(this.data.manifests);
219
+ const bundleManifestsObj = Object.fromEntries(this.data.bundleManifests);
195
220
  fs3.writeFileSync(
196
- this.manifestsFile,
197
- JSON.stringify(manifestsObj, null, 2)
221
+ this.bundleManifestsFile,
222
+ JSON.stringify(bundleManifestsObj, null, 2)
198
223
  );
199
224
  const artifactsObj = Object.fromEntries(this.data.artifacts);
200
225
  fs3.writeFileSync(
@@ -210,12 +235,12 @@ var LocalDataStore = class {
210
235
  // Apps management
211
236
  getApps(filters) {
212
237
  let apps = Array.from(this.data.apps.values());
213
- if (filters?.dev) {
214
- apps = apps.filter((app) => app.developer_pubkey === filters.dev);
215
- }
216
238
  if (filters?.name) {
217
239
  apps = apps.filter((app) => app.name.includes(filters.name));
218
240
  }
241
+ if (filters?.dev) {
242
+ apps = apps.filter((app) => app.developer_pubkey === filters.dev);
243
+ }
219
244
  return apps;
220
245
  }
221
246
  getApp(appKey) {
@@ -225,17 +250,19 @@ var LocalDataStore = class {
225
250
  this.data.apps.set(appKey, app);
226
251
  this.saveData();
227
252
  }
228
- // Versions management
229
- getAppVersions(appId) {
253
+ // Versions management (bundles only)
254
+ getAppVersions(packageId) {
230
255
  const versions = [];
231
- for (const [key, manifest] of this.data.manifests.entries()) {
232
- if (key.startsWith(`${appId}/`)) {
256
+ for (const [key, manifest] of this.data.bundleManifests.entries()) {
257
+ if (key.startsWith(`${packageId}/`)) {
233
258
  const semver = key.split("/").pop();
259
+ const digest = manifest.wasm?.hash || "";
234
260
  versions.push({
235
261
  semver,
236
- cid: manifest.artifacts[0]?.cid || "",
262
+ digest,
263
+ cid: digest,
264
+ // Compatibility
237
265
  yanked: false
238
- // TODO: Implement yanking
239
266
  });
240
267
  }
241
268
  }
@@ -243,13 +270,14 @@ var LocalDataStore = class {
243
270
  (a, b) => a.semver.localeCompare(b.semver, void 0, { numeric: true })
244
271
  );
245
272
  }
246
- // Manifest management
247
- getManifest(appId, semver) {
248
- const manifestKey = `${appId}/${semver}`;
249
- return this.data.manifests.get(manifestKey);
273
+ // Manifest management (V2 / Bundles)
274
+ getBundleManifest(pkg, version) {
275
+ const key = `${pkg}/${version}`;
276
+ return this.data.bundleManifests.get(key);
250
277
  }
251
- setManifest(manifestKey, manifest) {
252
- this.data.manifests.set(manifestKey, manifest);
278
+ setBundleManifest(pkg, version, manifest) {
279
+ const key = `${pkg}/${version}`;
280
+ this.data.bundleManifests.set(key, manifest);
253
281
  this.saveData();
254
282
  }
255
283
  // Artifact management
@@ -264,26 +292,26 @@ var LocalDataStore = class {
264
292
  getStats() {
265
293
  return {
266
294
  publishedApps: this.data.apps.size,
267
- totalVersions: this.data.manifests.size,
295
+ totalVersions: this.data.bundleManifests.size,
268
296
  totalArtifacts: this.data.artifacts.size
269
297
  };
270
298
  }
271
299
  // Backup and restore
272
300
  async backup(outputPath) {
273
301
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
274
- const backupPath = outputPath || path.join(
302
+ const backupPath = outputPath || path7.join(
275
303
  this.config.getDataDir(),
276
304
  "backups",
277
305
  `backup-${timestamp}.json`
278
306
  );
279
- const backupDir = path.dirname(backupPath);
307
+ const backupDir = path7.dirname(backupPath);
280
308
  if (!fs3.existsSync(backupDir)) {
281
309
  fs3.mkdirSync(backupDir, { recursive: true });
282
310
  }
283
311
  const backupData = {
284
312
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
285
313
  apps: Object.fromEntries(this.data.apps),
286
- manifests: Object.fromEntries(this.data.manifests),
314
+ bundleManifests: Object.fromEntries(this.data.bundleManifests),
287
315
  artifacts: Object.fromEntries(this.data.artifacts)
288
316
  };
289
317
  fs3.writeFileSync(backupPath, JSON.stringify(backupData, null, 2));
@@ -295,83 +323,62 @@ var LocalDataStore = class {
295
323
  }
296
324
  const backupData = JSON.parse(fs3.readFileSync(backupPath, "utf8"));
297
325
  this.data.apps = new Map(Object.entries(backupData.apps || {}));
298
- this.data.manifests = new Map(Object.entries(backupData.manifests || {}));
326
+ this.data.bundleManifests = new Map(
327
+ Object.entries(backupData.bundleManifests || {})
328
+ );
299
329
  this.data.artifacts = new Map(Object.entries(backupData.artifacts || {}));
300
330
  this.saveData();
301
331
  }
302
332
  // Reset data
303
333
  reset() {
304
334
  this.data.apps.clear();
305
- this.data.manifests.clear();
335
+ this.data.bundleManifests.clear();
306
336
  this.data.artifacts.clear();
307
337
  this.saveData();
308
338
  }
339
+ /**
340
+ * Get all bundle manifests
341
+ */
342
+ getAllBundles() {
343
+ return Array.from(this.data.bundleManifests.values());
344
+ }
309
345
  // Seed with sample data
310
346
  async seed() {
311
- const sampleApps = [
312
- {
313
- name: "sample-wallet",
314
- developer_pubkey: "ed25519:5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
315
- latest_version: "1.0.0",
316
- latest_cid: "QmSampleWallet123",
317
- alias: "Sample Wallet"
347
+ const bundleManifest = {
348
+ version: "1.0",
349
+ package: "com.calimero.sample-bundle",
350
+ appVersion: "1.0.0",
351
+ metadata: {
352
+ name: "Sample Bundle App",
353
+ description: "A sample application using V2 Bundle Manifest",
354
+ tags: ["sample", "v2"],
355
+ license: "MIT"
318
356
  },
319
- {
320
- name: "demo-dex",
321
- developer_pubkey: "ed25519:5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
322
- latest_version: "2.1.0",
323
- latest_cid: "QmDemoDex456",
324
- alias: "Demo DEX"
325
- }
326
- ];
327
- const sampleManifests = [
328
- {
329
- manifest_version: "1.0",
330
- app: {
331
- name: "sample-wallet",
332
- namespace: "com.example",
333
- developer_pubkey: "ed25519:5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
334
- id: "sample-wallet-id",
335
- alias: "Sample Wallet"
336
- },
337
- version: {
338
- semver: "1.0.0"
339
- },
340
- supported_chains: ["mainnet", "testnet"],
341
- permissions: [
342
- { cap: "wallet", bytes: 1024 },
343
- { cap: "network", bytes: 512 }
344
- ],
345
- artifacts: [
346
- {
347
- type: "wasm",
348
- target: "node",
349
- path: "/artifacts/sample-wallet/1.0.0/app.wasm",
350
- size: 1024e3,
351
- sha256: "abc123def456"
352
- }
353
- ],
354
- metadata: {
355
- description: "A sample wallet application for testing",
356
- author: "Sample Developer"
357
- },
358
- distribution: "local",
359
- signature: {
360
- alg: "ed25519",
361
- sig: "sample-signature",
362
- signed_at: (/* @__PURE__ */ new Date()).toISOString()
363
- }
364
- }
365
- ];
366
- sampleApps.forEach((app) => {
367
- const appKey = `${app.developer_pubkey}/${app.name}`;
368
- this.setApp(appKey, app);
369
- });
370
- sampleManifests.forEach((manifest) => {
371
- const manifestKey = `${manifest.app.app_id}/${manifest.version.semver}`;
372
- this.setManifest(manifestKey, manifest);
373
- });
374
- this.saveData();
357
+ interfaces: {
358
+ exports: ["com.calimero.sample.v1"],
359
+ uses: []
360
+ },
361
+ links: {
362
+ frontend: "https://example.com/app",
363
+ github: "https://github.com/example/app"
364
+ },
365
+ wasm: {
366
+ path: "app.wasm",
367
+ size: 1024,
368
+ hash: null
369
+ },
370
+ abi: {
371
+ path: "abi.json",
372
+ size: 512,
373
+ hash: null
374
+ },
375
+ migrations: []
376
+ };
377
+ this.setBundleManifest(
378
+ bundleManifest.package,
379
+ bundleManifest.appVersion,
380
+ bundleManifest
381
+ );
375
382
  }
376
383
  };
377
384
  var LocalArtifactServer = class {
@@ -391,11 +398,11 @@ var LocalArtifactServer = class {
391
398
  if (!fs3.existsSync(sourcePath)) {
392
399
  throw new Error(`Source file not found: ${sourcePath}`);
393
400
  }
394
- const appVersionDir = path.join(this.artifactsDir, appId, version);
401
+ const appVersionDir = path7.join(this.artifactsDir, appId, version);
395
402
  if (!fs3.existsSync(appVersionDir)) {
396
403
  fs3.mkdirSync(appVersionDir, { recursive: true });
397
404
  }
398
- const targetPath = path.join(appVersionDir, filename);
405
+ const targetPath = path7.join(appVersionDir, filename);
399
406
  fs3.copyFileSync(sourcePath, targetPath);
400
407
  const fileHash = await this.calculateFileHash(targetPath);
401
408
  this.dataStore.setArtifactPath(fileHash, targetPath);
@@ -403,7 +410,7 @@ var LocalArtifactServer = class {
403
410
  }
404
411
  // Serve artifact by app ID, version, and filename
405
412
  async serveArtifact(appId, version, filename) {
406
- const artifactPath = path.join(this.artifactsDir, appId, version, filename);
413
+ const artifactPath = path7.join(this.artifactsDir, appId, version, filename);
407
414
  if (!fs3.existsSync(artifactPath)) {
408
415
  throw new Error(`Artifact not found: ${artifactPath}`);
409
416
  }
@@ -437,35 +444,33 @@ var LocalArtifactServer = class {
437
444
  }
438
445
  // Get artifact URL for local serving
439
446
  getArtifactUrl(appId, version, filename) {
440
- const baseUrl = `http://${this.config.getHost()}:${this.config.getPort()}`;
447
+ const baseUrl = `http://${this.config.getPublicHost()}:${this.config.getPort()}`;
441
448
  return `${baseUrl}/artifacts/${appId}/${version}/${filename}`;
442
449
  }
443
450
  // Get artifact URL by hash
444
451
  getArtifactUrlByHash(hash) {
445
- const baseUrl = `http://${this.config.getHost()}:${this.config.getPort()}`;
452
+ const baseUrl = `http://${this.config.getPublicHost()}:${this.config.getPort()}`;
446
453
  return `${baseUrl}/artifacts/${hash}`;
447
454
  }
448
- // Update manifest artifacts to use local URLs
449
- updateManifestArtifacts(manifest) {
455
+ // Update manifest artifact to use local URLs (and preserve HTTP access)
456
+ updateManifestArtifact(manifest) {
450
457
  const updatedManifest = { ...manifest };
451
- if (updatedManifest.artifacts) {
452
- updatedManifest.artifacts = updatedManifest.artifacts.map(
453
- (artifact) => {
454
- const updatedArtifact = { ...artifact };
455
- if (artifact.path) {
456
- const filename = path.basename(artifact.path);
457
- updatedArtifact.mirrors = [
458
- this.getArtifactUrl(
459
- manifest.app.id || manifest.app.name?.replace(/\s+/g, "-").toLowerCase(),
460
- manifest.version.semver,
461
- filename
462
- )
463
- ];
464
- delete updatedArtifact.path;
465
- }
466
- return updatedArtifact;
467
- }
468
- );
458
+ if (!manifest.artifact?.uri) {
459
+ return updatedManifest;
460
+ }
461
+ const uri = manifest.artifact.uri;
462
+ if (uri.startsWith("/")) {
463
+ updatedManifest.artifact = {
464
+ ...manifest.artifact,
465
+ uri: `${this.getArtifactUrl(manifest.id, manifest.version, path7.basename(manifest.artifact.uri))}`
466
+ };
467
+ } else if (uri.startsWith("file://")) {
468
+ const filePath = uri.replace("file://", "");
469
+ const filename = path7.basename(filePath);
470
+ updatedManifest.artifact = {
471
+ ...manifest.artifact,
472
+ uri: this.getArtifactUrl(manifest.id, manifest.version, filename)
473
+ };
469
474
  }
470
475
  return updatedManifest;
471
476
  }
@@ -476,7 +481,7 @@ var LocalArtifactServer = class {
476
481
  let files = [];
477
482
  const items = fs3.readdirSync(dir);
478
483
  for (const item of items) {
479
- const fullPath = path.join(dir, item);
484
+ const fullPath = path7.join(dir, item);
480
485
  const stat = fs3.statSync(fullPath);
481
486
  if (stat.isDirectory()) {
482
487
  files = files.concat(getAllFiles(fullPath));
@@ -505,7 +510,7 @@ var LocalArtifactServer = class {
505
510
  let files = [];
506
511
  const items = fs3.readdirSync(dir);
507
512
  for (const item of items) {
508
- const fullPath = path.join(dir, item);
513
+ const fullPath = path7.join(dir, item);
509
514
  const stat = fs3.statSync(fullPath);
510
515
  if (stat.isDirectory()) {
511
516
  files = files.concat(getAllFiles(fullPath));
@@ -573,24 +578,22 @@ var LocalRegistryClient = class {
573
578
  if (!manifest) {
574
579
  throw new Error("Manifest not found");
575
580
  }
576
- return this.artifactServer.updateManifestArtifacts(manifest);
581
+ return this.artifactServer.updateManifestArtifact(manifest);
577
582
  }
578
583
  async submitAppManifest(manifest) {
579
584
  if (!this.validateManifest(manifest)) {
580
585
  throw new Error("Invalid manifest structure");
581
586
  }
582
587
  const processedManifest = await this.processManifestArtifacts(manifest);
583
- const manifestKey = `${manifest.app.app_id}/${manifest.version.semver}`;
588
+ const manifestKey = `${processedManifest.id}/${processedManifest.version}`;
584
589
  this.dataStore.setManifest(manifestKey, processedManifest);
585
- const appKey = manifest.app.app_id;
586
590
  const appSummary = {
587
- name: manifest.app.name,
588
- developer_pubkey: manifest.app.developer_pubkey,
589
- latest_version: manifest.version.semver,
590
- latest_cid: manifest.artifacts[0]?.cid || "",
591
- alias: manifest.app.alias
591
+ id: processedManifest.id,
592
+ name: processedManifest.name,
593
+ latest_version: processedManifest.version,
594
+ latest_digest: processedManifest.artifact.digest
592
595
  };
593
- this.dataStore.setApp(appKey, appSummary);
596
+ this.dataStore.setApp(processedManifest.id, appSummary);
594
597
  return {
595
598
  success: true,
596
599
  message: "App version registered successfully"
@@ -600,39 +603,33 @@ var LocalRegistryClient = class {
600
603
  return { status: "ok" };
601
604
  }
602
605
  validateManifest(manifest) {
603
- return !!(manifest.manifest_version && manifest.app && manifest.app.name && manifest.app.developer_pubkey && manifest.version && manifest.version.semver && manifest.artifacts && manifest.artifacts.length > 0);
606
+ return !!(manifest.manifest_version && manifest.id && manifest.name && manifest.version && manifest.artifact && manifest.artifact.type && manifest.artifact.target && manifest.artifact.digest && manifest.artifact.uri);
604
607
  }
605
608
  async processManifestArtifacts(manifest) {
606
609
  const processedManifest = { ...manifest };
607
- if (processedManifest.artifacts) {
608
- processedManifest.artifacts = await Promise.all(
609
- processedManifest.artifacts.map(async (artifact) => {
610
- const processedArtifact = { ...artifact };
611
- if (artifact.path && fs3.existsSync(artifact.path)) {
612
- const filename = path.basename(artifact.path);
613
- const appId = manifest.app.id || manifest.app.name?.replace(/\s+/g, "-").toLowerCase();
614
- try {
615
- await this.artifactServer.copyArtifactToLocal(
616
- artifact.path,
617
- appId,
618
- manifest.version.semver,
619
- filename
620
- );
621
- processedArtifact.mirrors = [
622
- this.artifactServer.getArtifactUrl(
623
- appId,
624
- manifest.version.semver,
625
- filename
626
- )
627
- ];
628
- delete processedArtifact.path;
629
- } catch (error) {
630
- console.warn(`Failed to copy artifact ${artifact.path}:`, error);
631
- }
632
- }
633
- return processedArtifact;
634
- })
635
- );
610
+ if (manifest.artifact?.uri?.startsWith("file://")) {
611
+ const filePath = manifest.artifact.uri.replace("file://", "");
612
+ if (fs3.existsSync(filePath)) {
613
+ const filename = path7.basename(filePath);
614
+ try {
615
+ await this.artifactServer.copyArtifactToLocal(
616
+ filePath,
617
+ manifest.id,
618
+ manifest.version,
619
+ filename
620
+ );
621
+ processedManifest.artifact = {
622
+ ...manifest.artifact,
623
+ uri: this.artifactServer.getArtifactUrl(
624
+ manifest.id,
625
+ manifest.version,
626
+ filename
627
+ )
628
+ };
629
+ } catch (error) {
630
+ console.warn(`Failed to copy artifact ${filePath}:`, error);
631
+ }
632
+ }
636
633
  }
637
634
  return processedManifest;
638
635
  }
@@ -648,7 +645,7 @@ function createRegistryClient(useLocal, baseURL, timeout) {
648
645
  }
649
646
  }
650
647
  var appsCommand = new Command("apps").description("Manage SSApp applications").addCommand(
651
- new Command("list").description("List all applications").option("-d, --dev <pubkey>", "Filter by developer public key").option("-n, --name <name>", "Filter by application name").action(async (options, command) => {
648
+ new Command("list").description("List all applications").option("-n, --name <name>", "Filter by application name").action(async (options, command) => {
652
649
  const globalOpts = command.parent?.parent?.opts();
653
650
  const useLocal = globalOpts?.local || false;
654
651
  const client = createRegistryClient(
@@ -659,29 +656,28 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
659
656
  const spinner2 = ora6("Fetching applications...").start();
660
657
  try {
661
658
  const apps = await client.getApps({
662
- dev: options.dev,
663
659
  name: options.name
664
660
  });
665
661
  spinner2.succeed(`Found ${apps.length} application(s)`);
666
662
  if (apps.length === 0) {
667
- console.log(chalk5.yellow("No applications found"));
663
+ console.log(chalk6.yellow("No applications found"));
668
664
  return;
669
665
  }
670
666
  const tableData = [
671
- ["Name", "Developer", "Latest Version", "Latest CID", "Alias"],
667
+ ["ID", "Name", "Latest Version", "Digest"],
672
668
  ...apps.map((app) => [
669
+ app.id,
673
670
  app.name,
674
- app.developer_pubkey?.substring(0, 12) + "..." || "Unknown",
675
- app.latest_version || "Unknown",
676
- app.latest_cid?.substring(0, 12) + "..." || "N/A",
677
- app.alias || "-"
671
+ app.latest_version,
672
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
673
+ (app.latest_digest || app.latest_cid || "N/A").toString().substring(0, 12) + "..."
678
674
  ])
679
675
  ];
680
676
  console.log(table(tableData));
681
677
  } catch (error) {
682
678
  spinner2.fail("Failed to fetch applications");
683
679
  if (error instanceof Error) {
684
- console.error(chalk5.red(`Error: ${error.message}`));
680
+ console.error(chalk6.red(`Error: ${error.message}`));
685
681
  }
686
682
  process.exit(1);
687
683
  }
@@ -700,7 +696,7 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
700
696
  const versions = await client.getAppVersions(appId);
701
697
  spinner2.succeed(`Found ${versions.length} version(s)`);
702
698
  if (versions.length === 0) {
703
- console.log(chalk5.yellow("No versions found"));
699
+ console.log(chalk6.yellow("No versions found"));
704
700
  return;
705
701
  }
706
702
  const tableData = [
@@ -708,14 +704,14 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
708
704
  ...versions.map((version) => [
709
705
  version.semver,
710
706
  version.cid.substring(0, 12) + "...",
711
- version.yanked ? chalk5.red("Yes") : chalk5.green("No")
707
+ version.yanked ? chalk6.red("Yes") : chalk6.green("No")
712
708
  ])
713
709
  ];
714
710
  console.log(table(tableData));
715
711
  } catch (error) {
716
712
  spinner2.fail("Failed to fetch versions");
717
713
  if (error instanceof Error) {
718
- console.error(chalk5.red(`Error: ${error.message}`));
714
+ console.error(chalk6.red(`Error: ${error.message}`));
719
715
  }
720
716
  process.exit(1);
721
717
  }
@@ -735,12 +731,12 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
735
731
  try {
736
732
  const manifest = await client.getAppManifest(appId, version);
737
733
  spinner2.succeed("Manifest fetched successfully");
738
- console.log(chalk5.blue("\nApplication Manifest:"));
734
+ console.log(chalk6.blue("\nApplication Manifest:"));
739
735
  console.log(JSON.stringify(manifest, null, 2));
740
736
  } catch (error) {
741
737
  spinner2.fail("Failed to fetch manifest");
742
738
  if (error instanceof Error) {
743
- console.error(chalk5.red(`Error: ${error.message}`));
739
+ console.error(chalk6.red(`Error: ${error.message}`));
744
740
  }
745
741
  process.exit(1);
746
742
  }
@@ -756,10 +752,10 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
756
752
  );
757
753
  const spinner2 = ora6("Reading manifest file...").start();
758
754
  try {
759
- const manifestPath = path.resolve(manifestFile);
755
+ const manifestPath = path7.resolve(manifestFile);
760
756
  if (!fs3.existsSync(manifestPath)) {
761
757
  spinner2.fail("Manifest file not found");
762
- console.error(chalk5.red(`File not found: ${manifestFile}`));
758
+ console.error(chalk6.red(`File not found: ${manifestFile}`));
763
759
  process.exit(1);
764
760
  }
765
761
  const manifestContent = fs3.readFileSync(manifestPath, "utf8");
@@ -767,20 +763,20 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
767
763
  spinner2.text = "Submitting application manifest...";
768
764
  const result = await client.submitAppManifest(manifest);
769
765
  spinner2.succeed("Application submitted successfully");
770
- console.log(chalk5.green(`
766
+ console.log(chalk6.green(`
771
767
  \u2705 ${result.message}`));
772
768
  if (manifest.app?.name) {
773
- console.log(chalk5.blue(`
769
+ console.log(chalk6.blue(`
774
770
  \u{1F4F1} App: ${manifest.app.name}`));
775
771
  console.log(
776
- chalk5.blue(`\u{1F464} Developer: ${manifest.app.developer_pubkey}`)
772
+ chalk6.blue(`\u{1F464} Developer: ${manifest.app.developer_pubkey}`)
777
773
  );
778
- console.log(chalk5.blue(`\u{1F4E6} Version: ${manifest.version?.semver}`));
774
+ console.log(chalk6.blue(`\u{1F4E6} Version: ${manifest.version?.semver}`));
779
775
  }
780
776
  } catch (error) {
781
777
  spinner2.fail("Failed to submit application");
782
778
  if (error instanceof Error) {
783
- console.error(chalk5.red(`Error: ${error.message}`));
779
+ console.error(chalk6.red(`Error: ${error.message}`));
784
780
  }
785
781
  process.exit(1);
786
782
  }
@@ -797,29 +793,29 @@ var developersCommand = new Command("developers").description("Manage developer
797
793
  try {
798
794
  const profile = await client.getDeveloper(pubkey);
799
795
  spinner2.succeed("Developer profile fetched successfully");
800
- console.log(chalk5.blue("\nDeveloper Profile:"));
801
- 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);
802
798
  if (profile.website) {
803
- console.log(chalk5.green("Website:"), profile.website);
799
+ console.log(chalk6.green("Website:"), profile.website);
804
800
  }
805
801
  if (profile.proofs.length > 0) {
806
- console.log(chalk5.green("\nProofs:"));
802
+ console.log(chalk6.green("\nProofs:"));
807
803
  const tableData = [
808
804
  ["Type", "Value", "Verified"],
809
805
  ...profile.proofs.map((proof) => [
810
806
  proof.type,
811
807
  proof.value.substring(0, 20) + "...",
812
- proof.verified ? chalk5.green("Yes") : chalk5.red("No")
808
+ proof.verified ? chalk6.green("Yes") : chalk6.red("No")
813
809
  ])
814
810
  ];
815
811
  console.log(table(tableData));
816
812
  } else {
817
- console.log(chalk5.yellow("\nNo proofs found"));
813
+ console.log(chalk6.yellow("\nNo proofs found"));
818
814
  }
819
815
  } catch (error) {
820
816
  spinner2.fail("Failed to fetch developer profile");
821
817
  if (error instanceof Error) {
822
- console.error(chalk5.red(`Error: ${error.message}`));
818
+ console.error(chalk6.red(`Error: ${error.message}`));
823
819
  }
824
820
  process.exit(1);
825
821
  }
@@ -839,7 +835,7 @@ var developersCommand = new Command("developers").description("Manage developer
839
835
  proofs = JSON.parse(options.proofs);
840
836
  } catch {
841
837
  spinner2.fail("Invalid proofs JSON format");
842
- console.error(chalk5.red("Proofs must be a valid JSON array"));
838
+ console.error(chalk6.red("Proofs must be a valid JSON array"));
843
839
  process.exit(1);
844
840
  }
845
841
  }
@@ -851,18 +847,18 @@ var developersCommand = new Command("developers").description("Manage developer
851
847
  };
852
848
  const result = await client.submitDeveloperProfile(pubkey, profile);
853
849
  spinner2.succeed("Developer profile created successfully");
854
- console.log(chalk5.green(`
850
+ console.log(chalk6.green(`
855
851
  \u2705 ${result.message}`));
856
- console.log(chalk5.blue(`
852
+ console.log(chalk6.blue(`
857
853
  \u{1F464} Developer: ${displayName}`));
858
- console.log(chalk5.blue(`\u{1F511} Public Key: ${pubkey}`));
854
+ console.log(chalk6.blue(`\u{1F511} Public Key: ${pubkey}`));
859
855
  if (options.website) {
860
- console.log(chalk5.blue(`\u{1F310} Website: ${options.website}`));
856
+ console.log(chalk6.blue(`\u{1F310} Website: ${options.website}`));
861
857
  }
862
858
  } catch (error) {
863
859
  spinner2.fail("Failed to create developer profile");
864
860
  if (error instanceof Error) {
865
- console.error(chalk5.red(`Error: ${error.message}`));
861
+ console.error(chalk6.red(`Error: ${error.message}`));
866
862
  }
867
863
  process.exit(1);
868
864
  }
@@ -885,16 +881,16 @@ var attestationsCommand = new Command("attestations").description("Manage applic
885
881
  version
886
882
  );
887
883
  spinner2.succeed("Attestation fetched successfully");
888
- console.log(chalk5.blue("\nAttestation:"));
889
- console.log(chalk5.green("Status:"), attestation.status);
890
- 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);
891
887
  if (attestation.comment) {
892
- console.log(chalk5.green("Comment:"), attestation.comment);
888
+ console.log(chalk6.green("Comment:"), attestation.comment);
893
889
  }
894
890
  } catch (error) {
895
891
  spinner2.fail("Failed to fetch attestation");
896
892
  if (error instanceof Error) {
897
- console.error(chalk5.red(`Error: ${error.message}`));
893
+ console.error(chalk6.red(`Error: ${error.message}`));
898
894
  }
899
895
  process.exit(1);
900
896
  }
@@ -912,11 +908,11 @@ var healthCommand = new Command("health").description("Check the health of the S
912
908
  try {
913
909
  const health = await client.healthCheck();
914
910
  spinner2.succeed("API is healthy");
915
- console.log(chalk5.green(`Status: ${health.status}`));
911
+ console.log(chalk6.green(`Status: ${health.status}`));
916
912
  } catch (error) {
917
913
  spinner2.fail("API health check failed");
918
914
  if (error instanceof Error) {
919
- console.error(chalk5.red(`Error: ${error.message}`));
915
+ console.error(chalk6.red(`Error: ${error.message}`));
920
916
  }
921
917
  process.exit(1);
922
918
  }
@@ -1072,75 +1068,6 @@ var LocalRegistryServer = class {
1072
1068
  totalSize: artifactStats.totalSize
1073
1069
  };
1074
1070
  });
1075
- this.server.get("/apps", async (request) => {
1076
- const query = request.query;
1077
- if (query.id && query.versions === "true") {
1078
- return {
1079
- id: query.id,
1080
- versions: this.dataStore.getAppVersions(query.id)
1081
- };
1082
- }
1083
- if (query.id && query.version) {
1084
- const manifest = this.dataStore.getManifest(query.id, query.version);
1085
- if (!manifest) {
1086
- return {
1087
- statusCode: 404,
1088
- error: "Not Found",
1089
- message: "Manifest not found"
1090
- };
1091
- }
1092
- return this.artifactServer.updateManifestArtifacts(manifest);
1093
- }
1094
- return this.dataStore.getApps(query);
1095
- });
1096
- this.server.get("/apps/:appId", async (request) => {
1097
- const { appId } = request.params;
1098
- const versions = this.dataStore.getAppVersions(appId);
1099
- return {
1100
- id: appId,
1101
- versions
1102
- };
1103
- });
1104
- this.server.get("/apps/:appId/:semver", async (request) => {
1105
- const { appId, semver } = request.params;
1106
- const manifest = this.dataStore.getManifest(appId, semver);
1107
- if (!manifest) {
1108
- return {
1109
- statusCode: 404,
1110
- error: "Not Found",
1111
- message: "Manifest not found"
1112
- };
1113
- }
1114
- return this.artifactServer.updateManifestArtifacts(manifest);
1115
- });
1116
- this.server.post("/apps", async (request) => {
1117
- const manifest = request.body;
1118
- if (!this.validateManifest(manifest)) {
1119
- return {
1120
- statusCode: 400,
1121
- error: "Bad Request",
1122
- message: "Invalid manifest structure"
1123
- };
1124
- }
1125
- const processedManifest = await this.processManifestArtifacts(manifest);
1126
- const manifestKey = `${manifest.app.app_id}/${manifest.version.semver}`;
1127
- this.dataStore.setManifest(manifestKey, processedManifest);
1128
- const appKey = manifest.app.app_id;
1129
- const appSummary = {
1130
- name: manifest.app.name,
1131
- developer_pubkey: manifest.app.developer_pubkey,
1132
- latest_version: manifest.version.semver,
1133
- latest_cid: manifest.artifacts[0]?.cid || "",
1134
- alias: manifest.app.name
1135
- // Use name as alias
1136
- };
1137
- this.dataStore.setApp(appKey, appSummary);
1138
- return {
1139
- success: true,
1140
- message: "App version registered successfully",
1141
- manifest_key: manifestKey
1142
- };
1143
- });
1144
1071
  this.server.get(
1145
1072
  "/artifacts/:appId/:version/:filename",
1146
1073
  async (request, reply) => {
@@ -1201,190 +1128,119 @@ var LocalRegistryServer = class {
1201
1128
  await this.seed();
1202
1129
  return { message: "Sample data seeded successfully" };
1203
1130
  });
1204
- this.server.post("/v1/apps", async (request, reply) => {
1205
- const manifest = request.body;
1206
- if (!manifest.manifest_version || !manifest.id || !manifest.name || !manifest.version) {
1207
- return reply.code(400).send({
1208
- error: "invalid_schema",
1209
- details: "Missing required fields"
1210
- });
1211
- }
1212
- try {
1213
- const processedManifest = await this.processManifestArtifacts(manifest);
1214
- const manifestKey = `${manifest.id}/${manifest.version}`;
1215
- this.dataStore.setManifest(manifestKey, processedManifest);
1216
- const appKey = manifest.id;
1217
- const appSummary = {
1218
- id: manifest.id,
1219
- name: manifest.name,
1220
- developer_pubkey: "local-dev-key",
1221
- latest_version: manifest.version,
1222
- latest_cid: manifest.artifact?.digest || ""
1223
- };
1224
- this.dataStore.setApp(appKey, appSummary);
1225
- return reply.code(201).send({
1226
- id: manifest.id,
1227
- version: manifest.version,
1228
- canonical_uri: `/v1/apps/${manifest.id}/${manifest.version}`
1229
- });
1230
- } catch {
1231
- return reply.code(409).send({
1232
- error: "already_exists",
1233
- details: `${manifest.id}@${manifest.version}`
1234
- });
1235
- }
1236
- });
1237
- this.server.get("/v1/apps/:id", async (request, reply) => {
1238
- const { id } = request.params;
1239
- const versions = this.dataStore.getAppVersions(id);
1240
- if (!versions || versions.length === 0) {
1241
- return reply.code(404).send({ error: "not_found", message: "App not found" });
1242
- }
1243
- return {
1244
- id,
1245
- versions: versions.map((v) => v.semver)
1246
- };
1247
- });
1248
- this.server.get("/v1/apps/:id/:version", async (request, reply) => {
1249
- const { id, version } = request.params;
1250
- const { canonical } = request.query;
1251
- const oldManifest = this.dataStore.getManifest(id, version);
1252
- if (!oldManifest) {
1253
- return reply.code(404).send({ error: "not_found", message: "Manifest not found" });
1254
- }
1255
- const v1Manifest = {
1256
- manifest_version: oldManifest.manifest_version,
1257
- id: oldManifest.app.app_id,
1258
- name: oldManifest.app.name,
1259
- version: oldManifest.version.semver,
1260
- chains: oldManifest.supported_chains,
1261
- artifact: oldManifest.artifacts[0] ? {
1262
- type: oldManifest.artifacts[0].type,
1263
- target: oldManifest.artifacts[0].target,
1264
- digest: oldManifest.artifacts[0].cid || `sha256:${"0".repeat(64)}`,
1265
- uri: oldManifest.artifacts[0].path || oldManifest.artifacts[0].mirrors?.[0] || "https://example.com/artifact"
1266
- } : {
1267
- type: "wasm",
1268
- target: "node",
1269
- digest: `sha256:${"0".repeat(64)}`,
1270
- uri: "https://example.com/artifact"
1271
- },
1272
- _warnings: []
1273
- };
1274
- if (canonical === "true") {
1275
- const canonicalJCS = JSON.stringify(
1276
- v1Manifest,
1277
- Object.keys(v1Manifest).sort()
1278
- );
1279
- return {
1280
- canonical_jcs: Buffer.from(canonicalJCS, "utf8").toString("base64")
1281
- };
1131
+ this.server.get("/api/v2/bundles", async (request, reply) => {
1132
+ const query = request.query;
1133
+ const { package: pkg, version, developer } = query;
1134
+ if (pkg && version) {
1135
+ const manifest = this.dataStore.getBundleManifest(pkg, version);
1136
+ if (!manifest) {
1137
+ return reply.code(404).send({
1138
+ error: "bundle_not_found",
1139
+ message: `Bundle ${pkg}@${version} not found`
1140
+ });
1141
+ }
1142
+ return [manifest];
1282
1143
  }
1283
- return v1Manifest;
1284
- });
1285
- this.server.get("/v1/search", async (request, reply) => {
1286
- const { q } = request.query;
1287
- if (!q) {
1288
- return reply.code(400).send({
1289
- error: "bad_request",
1290
- message: 'Query parameter "q" is required'
1291
- });
1144
+ const allBundles = this.dataStore.getAllBundles();
1145
+ const bundles = [];
1146
+ for (const bundle of allBundles) {
1147
+ if (pkg && bundle.package !== pkg) {
1148
+ continue;
1149
+ }
1150
+ if (developer) {
1151
+ const bundlePubkey = bundle.signature?.pubkey;
1152
+ if (!bundlePubkey || bundlePubkey !== developer) {
1153
+ continue;
1154
+ }
1155
+ }
1156
+ bundles.push(bundle);
1292
1157
  }
1293
- const apps = this.dataStore.getApps({});
1294
- const results = apps.filter(
1295
- (app) => app.name.toLowerCase().includes(q.toLowerCase()) || app.id?.toLowerCase().includes(q.toLowerCase())
1296
- );
1297
- return results.map((app) => ({
1298
- id: app.id || app.name,
1299
- versions: [app.latest_version]
1300
- }));
1301
- });
1302
- this.server.post("/v1/resolve", async (request, reply) => {
1303
- const { root } = request.body;
1304
- if (!root || !root.id || !root.version) {
1305
- return reply.code(400).send({
1306
- error: "bad_request",
1307
- message: "Root app ID and version are required"
1158
+ bundles.sort((a, b) => {
1159
+ const pkgCompare = a.package.localeCompare(b.package);
1160
+ if (pkgCompare !== 0) return pkgCompare;
1161
+ return b.appVersion.localeCompare(a.appVersion, void 0, {
1162
+ numeric: true
1308
1163
  });
1164
+ });
1165
+ if (pkg) {
1166
+ return bundles;
1167
+ }
1168
+ const latestByPackage = /* @__PURE__ */ new Map();
1169
+ for (const bundle of bundles) {
1170
+ const existing = latestByPackage.get(bundle.package);
1171
+ if (!existing || bundle.appVersion > existing.appVersion) {
1172
+ latestByPackage.set(bundle.package, bundle);
1173
+ }
1309
1174
  }
1310
- return {
1311
- plan: [{ action: "install", id: root.id, version: root.version }],
1312
- satisfies: [],
1313
- missing: []
1314
- };
1175
+ return Array.from(latestByPackage.values());
1315
1176
  });
1316
- }
1317
- validateManifest(manifest) {
1318
- return !!(manifest.manifest_version && manifest.app && manifest.app.app_id && manifest.app.name && manifest.app.developer_pubkey && manifest.version && manifest.version.semver && manifest.artifacts && manifest.artifacts.length > 0);
1319
- }
1320
- async processManifestArtifacts(manifest) {
1321
- const processedManifest = { ...manifest };
1322
- if (manifest.artifact && manifest.artifact.uri) {
1323
- const artifact = manifest.artifact;
1324
- if (artifact.uri.startsWith("file://")) {
1325
- const filePath = artifact.uri.replace("file://", "");
1326
- if (fs3.existsSync(filePath)) {
1327
- const filename = path.basename(filePath);
1328
- const appId = manifest.id;
1329
- try {
1330
- await this.artifactServer.copyArtifactToLocal(
1331
- filePath,
1332
- appId,
1333
- manifest.version,
1334
- filename
1335
- );
1336
- processedManifest.artifact = {
1337
- ...artifact,
1338
- uri: this.artifactServer.getArtifactUrl(
1339
- appId,
1340
- manifest.version,
1341
- filename
1342
- )
1343
- };
1344
- } catch (error) {
1345
- console.warn(`Failed to copy artifact ${filePath}:`, error);
1346
- }
1177
+ this.server.get(
1178
+ "/api/v2/bundles/:package/:version",
1179
+ async (request, reply) => {
1180
+ const { package: pkg, version } = request.params;
1181
+ const manifest = this.dataStore.getBundleManifest(pkg, version);
1182
+ if (!manifest) {
1183
+ return reply.code(404).send({
1184
+ error: "manifest_not_found",
1185
+ message: `Manifest not found for ${pkg}@${version}`
1186
+ });
1347
1187
  }
1188
+ return manifest;
1348
1189
  }
1349
- }
1350
- return processedManifest;
1190
+ );
1351
1191
  }
1352
1192
  };
1353
1193
  var localCommand = new Command("local").description(
1354
1194
  "Manage local registry for development"
1355
1195
  );
1356
1196
  localCommand.addCommand(
1357
- new Command("start").description("Start local registry server").option("-p, --port <port>", "Port to run the server on", "8082").option("-h, --host <host>", "Host to bind the server to", "localhost").action(async (options) => {
1197
+ new Command("start").description("Start local registry server").option("-p, --port <port>", "Port to run the server on", "8082").option("-h, --host <host>", "Host to bind the server to", "0.0.0.0").option(
1198
+ "--public-host <host>",
1199
+ "Public host exposed in manifest artifact URLs",
1200
+ "host.docker.internal"
1201
+ ).action(async (options) => {
1358
1202
  const spinner2 = ora6("Starting local registry...").start();
1359
1203
  try {
1360
1204
  const config = new LocalConfig();
1205
+ config.setHost(options.host);
1206
+ config.setPublicHost(options.publicHost);
1207
+ config.setPort(parseInt(options.port, 10));
1361
1208
  const server = new LocalRegistryServer(config);
1362
1209
  await server.start(parseInt(options.port));
1363
1210
  spinner2.succeed(
1364
- `Local registry started on http://${options.host}:${options.port}`
1211
+ `Local registry started on http://${config.getHost()}:${config.getPort()}`
1365
1212
  );
1366
- console.log(chalk5.blue("\n\u{1F4F1} Local Registry Status:"));
1213
+ console.log(chalk6.blue("\n\u{1F4F1} Local Registry Status:"));
1367
1214
  console.log(
1368
- chalk5.green(`\u2705 Server: http://${options.host}:${options.port}`)
1215
+ chalk6.green(
1216
+ `\u2705 Server: http://${config.getHost()}:${config.getPort()}`
1217
+ )
1369
1218
  );
1370
- console.log(chalk5.green(`\u{1F4C1} Data: ${config.getDataDir()}`));
1371
1219
  console.log(
1372
- chalk5.green(
1373
- `\u{1F4CB} Health: http://${options.host}:${options.port}/healthz`
1220
+ chalk6.green(
1221
+ `\u{1F310} Public URL: http://${config.getPublicHost()}:${config.getPort()}`
1374
1222
  )
1375
1223
  );
1224
+ console.log(chalk6.green(`\u{1F4C1} Data: ${config.getDataDir()}`));
1376
1225
  console.log(
1377
- chalk5.green(`\u{1F4CA} Stats: http://${options.host}:${options.port}/stats`)
1226
+ chalk6.green(
1227
+ `\u{1F4CB} Health: http://${config.getHost()}:${config.getPort()}/healthz`
1228
+ )
1378
1229
  );
1379
1230
  console.log(
1380
- chalk5.blue(
1231
+ chalk6.green(
1232
+ `\u{1F4CA} Stats: http://${config.getHost()}:${config.getPort()}/stats`
1233
+ )
1234
+ );
1235
+ console.log(
1236
+ chalk6.blue(
1381
1237
  "\n\u{1F4A1} Use --local flag with other commands to use local registry"
1382
1238
  )
1383
1239
  );
1384
1240
  } catch (error) {
1385
1241
  spinner2.fail("Failed to start local registry");
1386
1242
  if (error instanceof Error) {
1387
- console.error(chalk5.red(`Error: ${error.message}`));
1243
+ console.error(chalk6.red(`Error: ${error.message}`));
1388
1244
  }
1389
1245
  process.exit(1);
1390
1246
  }
@@ -1401,7 +1257,7 @@ localCommand.addCommand(
1401
1257
  } catch (error) {
1402
1258
  spinner2.fail("Failed to stop local registry");
1403
1259
  if (error instanceof Error) {
1404
- console.error(chalk5.red(`Error: ${error.message}`));
1260
+ console.error(chalk6.red(`Error: ${error.message}`));
1405
1261
  }
1406
1262
  process.exit(1);
1407
1263
  }
@@ -1416,16 +1272,16 @@ localCommand.addCommand(
1416
1272
  const status = await server.getStatus();
1417
1273
  if (status.running) {
1418
1274
  spinner2.succeed("Local registry is running");
1419
- console.log(chalk5.green(`\u2705 Server: ${status.url}`));
1420
- console.log(chalk5.green(`\u{1F4C1} Data: ${status.dataDir}`));
1421
- 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`));
1422
1278
  console.log(
1423
- chalk5.green(`\u{1F4E6} Artifacts: ${status.artifactsCount} artifacts`)
1279
+ chalk6.green(`\u{1F4E6} Artifacts: ${status.artifactsCount} artifacts`)
1424
1280
  );
1425
1281
  } else {
1426
1282
  spinner2.warn("Local registry is not running");
1427
1283
  console.log(
1428
- chalk5.yellow(
1284
+ chalk6.yellow(
1429
1285
  '\u{1F4A1} Run "calimero-registry local start" to start the local registry'
1430
1286
  )
1431
1287
  );
@@ -1433,7 +1289,7 @@ localCommand.addCommand(
1433
1289
  } catch (error) {
1434
1290
  spinner2.fail("Failed to check local registry status");
1435
1291
  if (error instanceof Error) {
1436
- console.error(chalk5.red(`Error: ${error.message}`));
1292
+ console.error(chalk6.red(`Error: ${error.message}`));
1437
1293
  }
1438
1294
  process.exit(1);
1439
1295
  }
@@ -1443,9 +1299,9 @@ localCommand.addCommand(
1443
1299
  new Command("reset").description("Reset local registry data").option("-f, --force", "Force reset without confirmation").action(async (options) => {
1444
1300
  if (!options.force) {
1445
1301
  console.log(
1446
- chalk5.yellow("\u26A0\uFE0F This will delete all local registry data!")
1302
+ chalk6.yellow("\u26A0\uFE0F This will delete all local registry data!")
1447
1303
  );
1448
- console.log(chalk5.yellow(" Use --force flag to confirm"));
1304
+ console.log(chalk6.yellow(" Use --force flag to confirm"));
1449
1305
  return;
1450
1306
  }
1451
1307
  const spinner2 = ora6("Resetting local registry data...").start();
@@ -1454,11 +1310,11 @@ localCommand.addCommand(
1454
1310
  const server = new LocalRegistryServer(config);
1455
1311
  await server.reset();
1456
1312
  spinner2.succeed("Local registry data reset");
1457
- console.log(chalk5.green("\u2705 All local data has been cleared"));
1313
+ console.log(chalk6.green("\u2705 All local data has been cleared"));
1458
1314
  } catch (error) {
1459
1315
  spinner2.fail("Failed to reset local registry data");
1460
1316
  if (error instanceof Error) {
1461
- console.error(chalk5.red(`Error: ${error.message}`));
1317
+ console.error(chalk6.red(`Error: ${error.message}`));
1462
1318
  }
1463
1319
  process.exit(1);
1464
1320
  }
@@ -1472,11 +1328,11 @@ localCommand.addCommand(
1472
1328
  const server = new LocalRegistryServer(config);
1473
1329
  const backupPath = await server.backup(options.output);
1474
1330
  spinner2.succeed("Backup created successfully");
1475
- console.log(chalk5.green(`\u{1F4E6} Backup saved to: ${backupPath}`));
1331
+ console.log(chalk6.green(`\u{1F4E6} Backup saved to: ${backupPath}`));
1476
1332
  } catch (error) {
1477
1333
  spinner2.fail("Failed to create backup");
1478
1334
  if (error instanceof Error) {
1479
- console.error(chalk5.red(`Error: ${error.message}`));
1335
+ console.error(chalk6.red(`Error: ${error.message}`));
1480
1336
  }
1481
1337
  process.exit(1);
1482
1338
  }
@@ -1488,18 +1344,18 @@ localCommand.addCommand(
1488
1344
  try {
1489
1345
  if (!fs3.existsSync(backupFile)) {
1490
1346
  spinner2.fail("Backup file not found");
1491
- console.error(chalk5.red(`File not found: ${backupFile}`));
1347
+ console.error(chalk6.red(`File not found: ${backupFile}`));
1492
1348
  process.exit(1);
1493
1349
  }
1494
1350
  const config = new LocalConfig();
1495
1351
  const server = new LocalRegistryServer(config);
1496
1352
  await server.restore(backupFile);
1497
1353
  spinner2.succeed("Data restored successfully");
1498
- console.log(chalk5.green(`\u2705 Restored from: ${backupFile}`));
1354
+ console.log(chalk6.green(`\u2705 Restored from: ${backupFile}`));
1499
1355
  } catch (error) {
1500
1356
  spinner2.fail("Failed to restore from backup");
1501
1357
  if (error instanceof Error) {
1502
- console.error(chalk5.red(`Error: ${error.message}`));
1358
+ console.error(chalk6.red(`Error: ${error.message}`));
1503
1359
  }
1504
1360
  process.exit(1);
1505
1361
  }
@@ -1514,277 +1370,729 @@ localCommand.addCommand(
1514
1370
  await server.seed();
1515
1371
  spinner2.succeed("Sample data seeded successfully");
1516
1372
  console.log(
1517
- chalk5.green("\u2705 Local registry populated with sample applications")
1373
+ chalk6.green("\u2705 Local registry populated with sample applications")
1518
1374
  );
1519
1375
  console.log(
1520
- chalk5.blue(
1376
+ chalk6.blue(
1521
1377
  '\u{1F4A1} Run "calimero-registry apps list --local" to see the sample apps'
1522
1378
  )
1523
1379
  );
1524
1380
  } catch (error) {
1525
1381
  spinner2.fail("Failed to seed sample data");
1526
1382
  if (error instanceof Error) {
1527
- console.error(chalk5.red(`Error: ${error.message}`));
1383
+ console.error(chalk6.red(`Error: ${error.message}`));
1528
1384
  }
1529
1385
  process.exit(1);
1530
1386
  }
1531
1387
  })
1532
1388
  );
1533
- var v1Command = new Command("v1").description("V1 API commands for Calimero SSApp Registry").addCommand(createPushCommand()).addCommand(createGetCommand()).addCommand(createListCommand()).addCommand(createResolveCommand()).addCommand(createVerifyCommand());
1534
- function createPushCommand() {
1535
- return new Command("push").description("Submit a v1 manifest to the registry").argument("<manifest-file>", "Path to manifest JSON file").option("--local", "Use local registry").action(
1536
- async (manifestFile, options = {}, command) => {
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)) {
1537
1405
  try {
1538
- const globalOpts = command.parent?.parent?.opts();
1539
- const useLocal = globalOpts?.local || options.local || false;
1540
- if (!fs3.existsSync(manifestFile)) {
1541
- console.error(`\u274C Manifest file not found: ${manifestFile}`);
1542
- process.exit(1);
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
1506
+ var bundleCommand = new Command("bundle").description("Manage application bundles (V2)").addCommand(createCreateCommand()).addCommand(createPushCommand()).addCommand(createGetCommand());
1507
+ function createCreateCommand() {
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(
1509
+ "--export <interface>",
1510
+ "Export interface (can be specified multiple times)",
1511
+ (value, prev) => {
1512
+ return [...prev || [], value];
1513
+ }
1514
+ ).option(
1515
+ "--use <interface>",
1516
+ "Use interface (can be specified multiple times)",
1517
+ (value, prev) => {
1518
+ return [...prev || [], value];
1519
+ }
1520
+ ).action(async (wasmFile, pkg, version, options) => {
1521
+ try {
1522
+ const wasmPath = path7.resolve(wasmFile);
1523
+ if (!fs3.existsSync(wasmPath)) {
1524
+ console.error(`\u274C WASM file not found: ${wasmFile}`);
1525
+ process.exit(1);
1526
+ }
1527
+ console.log(`\u{1F4E6} Creating MPK bundle from: ${path7.basename(wasmPath)}`);
1528
+ const wasmContent = fs3.readFileSync(wasmPath);
1529
+ const wasmSize = wasmContent.length;
1530
+ const hash = crypto.createHash("sha256").update(wasmContent).digest("hex");
1531
+ const metadata = {
1532
+ name: options.name || pkg,
1533
+ description: options.description || "",
1534
+ author: options.author || "Calimero Team"
1535
+ };
1536
+ const links = {};
1537
+ if (options.frontend) links.frontend = options.frontend;
1538
+ if (options.github) links.github = options.github;
1539
+ if (options.docs) links.docs = options.docs;
1540
+ const interfaces = {
1541
+ exports: options.export || [],
1542
+ uses: options.use || []
1543
+ };
1544
+ const manifest = {
1545
+ version: "1.0",
1546
+ package: pkg,
1547
+ appVersion: version,
1548
+ metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
1549
+ interfaces: interfaces.exports.length > 0 || interfaces.uses.length > 0 ? interfaces : void 0,
1550
+ wasm: {
1551
+ path: "app.wasm",
1552
+ hash,
1553
+ size: wasmSize
1554
+ },
1555
+ abi: null,
1556
+ migrations: [],
1557
+ links: Object.keys(links).length > 0 ? links : void 0,
1558
+ signature: void 0
1559
+ };
1560
+ let outputPath = options.output;
1561
+ if (!outputPath) {
1562
+ const outputDir = path7.join(process.cwd(), pkg, version);
1563
+ if (!fs3.existsSync(outputDir)) {
1564
+ fs3.mkdirSync(outputDir, { recursive: true });
1543
1565
  }
1544
- const manifestContent = fs3.readFileSync(manifestFile, "utf8");
1545
- const manifest = JSON.parse(manifestContent);
1546
- if (manifest.manifest_version !== "1.0") {
1547
- console.error('\u274C Invalid manifest version. Must be "1.0"');
1548
- process.exit(1);
1566
+ outputPath = path7.join(outputDir, `${pkg}-${version}.mpk`);
1567
+ } else {
1568
+ outputPath = path7.resolve(outputPath);
1569
+ const outputDir = path7.dirname(outputPath);
1570
+ if (!fs3.existsSync(outputDir)) {
1571
+ fs3.mkdirSync(outputDir, { recursive: true });
1549
1572
  }
1550
- if (!manifest.id || !manifest.name || !manifest.version) {
1551
- console.error("\u274C Missing required fields: id, name, version");
1552
- process.exit(1);
1573
+ }
1574
+ const tempDir = path7.join(
1575
+ path7.dirname(outputPath),
1576
+ `.temp-bundle-${Date.now()}`
1577
+ );
1578
+ fs3.mkdirSync(tempDir, { recursive: true });
1579
+ try {
1580
+ fs3.writeFileSync(
1581
+ path7.join(tempDir, "manifest.json"),
1582
+ JSON.stringify(manifest, null, 2)
1583
+ );
1584
+ fs3.writeFileSync(path7.join(tempDir, "app.wasm"), wasmContent);
1585
+ await tar.create(
1586
+ {
1587
+ gzip: true,
1588
+ file: outputPath,
1589
+ cwd: tempDir
1590
+ },
1591
+ ["manifest.json", "app.wasm"]
1592
+ );
1593
+ const outputSize = fs3.statSync(outputPath).size;
1594
+ console.log(`\u2705 Created MPK bundle: ${outputPath}`);
1595
+ console.log(` Package: ${pkg}`);
1596
+ console.log(` Version: ${version}`);
1597
+ console.log(` Size: ${outputSize} bytes`);
1598
+ console.log(` WASM Hash: ${hash}`);
1599
+ } finally {
1600
+ if (fs3.existsSync(tempDir)) {
1601
+ fs3.rmSync(tempDir, { recursive: true, force: true });
1553
1602
  }
1554
- const baseUrl = useLocal ? "http://localhost:8082" : "http://localhost:8080";
1555
- console.log(
1556
- `\u{1F4E4} Submitting manifest: ${manifest.id}@${manifest.version}`
1603
+ }
1604
+ } catch (error) {
1605
+ const message = error instanceof Error ? error.message : "Unknown error";
1606
+ console.error("\u274C Failed to create bundle:", message);
1607
+ process.exit(1);
1608
+ }
1609
+ });
1610
+ }
1611
+ function createPushCommand() {
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) => {
1645
+ try {
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"
1557
1649
  );
1558
- console.log(` Name: ${manifest.name}`);
1559
- console.log(` Chains: ${manifest.chains?.join(", ")}`);
1560
- console.log(
1561
- ` Provides: ${manifest.provides?.join(", ") || "none"}`
1650
+ console.warn(
1651
+ " These options will be ignored. Use --remote to push to remote registry."
1562
1652
  );
1563
- console.log(
1564
- ` Requires: ${manifest.requires?.join(", ") || "none"}`
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"
1565
1663
  );
1566
- const response = await fetch(`${baseUrl}/v1/apps`, {
1567
- method: "POST",
1568
- headers: { "Content-Type": "application/json" },
1569
- body: JSON.stringify(manifest)
1570
- });
1571
- if (!response.ok) {
1572
- const error = await response.json();
1573
- throw new Error(`${error.error}: ${error.details}`);
1574
- }
1575
- const result = await response.json();
1576
- console.log("\u2705 Manifest submitted successfully!");
1577
- console.log(` ID: ${result.id}`);
1578
- console.log(` Version: ${result.version}`);
1579
- console.log(` URI: ${result.canonical_uri}`);
1580
- } catch (error) {
1581
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1582
- console.error("\u274C Failed to submit manifest:", errorMessage);
1583
1664
  process.exit(1);
1584
1665
  }
1666
+ const fullPath = path7.resolve(bundleFile);
1667
+ if (!fs3.existsSync(fullPath)) {
1668
+ console.error(`\u274C File not found: ${bundleFile}`);
1669
+ process.exit(1);
1670
+ }
1671
+ console.log(`\u{1F4E6} Processing bundle: ${path7.basename(fullPath)}`);
1672
+ const manifest = await extractManifest(fullPath);
1673
+ if (!manifest) {
1674
+ console.error("\u274C manifest.json not found in bundle");
1675
+ process.exit(1);
1676
+ }
1677
+ if (!manifest.package || !manifest.appVersion) {
1678
+ console.error("\u274C Invalid manifest: missing package or appVersion");
1679
+ process.exit(1);
1680
+ }
1681
+ console.log(` Package: ${manifest.package}`);
1682
+ console.log(` Version: ${manifest.appVersion}`);
1683
+ if (manifest.metadata) {
1684
+ console.log(` Name: ${manifest.metadata.name}`);
1685
+ }
1686
+ if (useLocal) {
1687
+ const config = new LocalConfig();
1688
+ const store = new LocalDataStore(config);
1689
+ const artifactServer = new LocalArtifactServer(config, store);
1690
+ const bundleFilename = `${manifest.package}-${manifest.appVersion}.mpk`;
1691
+ const targetPath = await artifactServer.copyArtifactToLocal(
1692
+ fullPath,
1693
+ manifest.package,
1694
+ manifest.appVersion,
1695
+ bundleFilename
1696
+ );
1697
+ console.log(` Artifact stored: ${targetPath}`);
1698
+ store.setBundleManifest(
1699
+ manifest.package,
1700
+ manifest.appVersion,
1701
+ manifest
1702
+ );
1703
+ console.log("\u2705 Bundle manifest registered locally");
1704
+ console.log(
1705
+ ` Run 'calimero-registry bundle get ${manifest.package} ${manifest.appVersion}' to verify`
1706
+ );
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);
1712
+ }
1713
+ } catch (error) {
1714
+ const message = error instanceof Error ? error.message : String(error);
1715
+ console.error("\u274C Failed to push bundle:", message);
1716
+ process.exit(1);
1585
1717
  }
1586
- );
1718
+ });
1587
1719
  }
1588
1720
  function createGetCommand() {
1589
- return new Command("get").description("Get manifest from registry").argument("<app-id>", "Application ID").argument("[version]", "Specific version (optional)").option("--local", "Use local registry").option("--canonical", "Return canonical JCS format").action(
1590
- async (appId, version, options = {}, command) => {
1591
- try {
1592
- const globalOpts = command.parent?.parent?.opts();
1593
- const useLocal = globalOpts?.local || options.local || false;
1594
- const baseUrl = useLocal ? "http://localhost:8082" : "http://localhost:8080";
1595
- if (version) {
1596
- console.log(`\u{1F4E5} Getting manifest: ${appId}@${version}`);
1597
- const url = options.canonical ? `/v1/apps/${appId}/${version}?canonical=true` : `/v1/apps/${appId}/${version}`;
1598
- const response = await fetch(`${baseUrl}${url}`);
1599
- if (!response.ok) {
1600
- if (response.status === 404) {
1601
- console.error(`\u274C Manifest not found: ${appId}@${version}`);
1602
- } else {
1603
- console.error(
1604
- `\u274C Error: ${response.status} ${response.statusText}`
1605
- );
1606
- }
1607
- process.exit(1);
1608
- }
1609
- const manifest = await response.json();
1721
+ return new Command("get").description("Get bundle manifest").argument("<package>", "Package name (e.g. com.calimero.kv)").argument("<version>", "Version (e.g. 1.0.0)").option("--local", "Use local registry", true).action(async (pkg, version, options) => {
1722
+ try {
1723
+ if (options.local) {
1724
+ const config = new LocalConfig();
1725
+ const store = new LocalDataStore(config);
1726
+ const manifest = store.getBundleManifest(pkg, version);
1727
+ if (manifest) {
1610
1728
  console.log(JSON.stringify(manifest, null, 2));
1611
1729
  } else {
1612
- console.log(`\u{1F4E5} Getting versions for: ${appId}`);
1613
- const response = await fetch(`${baseUrl}/v1/apps/${appId}`);
1614
- if (!response.ok) {
1615
- if (response.status === 404) {
1616
- console.error(`\u274C App not found: ${appId}`);
1617
- } else {
1618
- console.error(
1619
- `\u274C Error: ${response.status} ${response.statusText}`
1620
- );
1621
- }
1622
- process.exit(1);
1623
- }
1624
- const result = await response.json();
1625
- console.log(`\u{1F4F1} App: ${result.id}`);
1626
- console.log(`\u{1F4CB} Versions: ${result.versions.join(", ")}`);
1730
+ console.error(`\u274C Manifest not found: ${pkg}@${version}`);
1731
+ process.exit(1);
1627
1732
  }
1628
- } catch (error) {
1629
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1630
- console.error("\u274C Failed to get manifest:", errorMessage);
1733
+ } else {
1734
+ console.error("\u274C Remote get not implemented yet");
1631
1735
  process.exit(1);
1632
1736
  }
1737
+ } catch (error) {
1738
+ console.error("\u274C Error:", error);
1739
+ process.exit(1);
1633
1740
  }
1634
- );
1741
+ });
1635
1742
  }
1636
- function createListCommand() {
1637
- return new Command("ls").description("List applications in registry").option("--local", "Use local registry").option("--search <query>", "Search query").action(
1638
- async (options = {}, command) => {
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;
1639
1800
  try {
1640
- const globalOpts = command.parent?.parent?.opts();
1641
- const useLocal = globalOpts?.local || options.local || false;
1642
- const baseUrl = useLocal ? "http://localhost:8082" : "http://localhost:8080";
1643
- if (options.search) {
1644
- console.log(`\u{1F50D} Searching for: ${options.search}`);
1645
- const response = await fetch(
1646
- `${baseUrl}/v1/search?q=${encodeURIComponent(options.search)}`
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
+ }
1911
+ async function extractManifest(bundlePath) {
1912
+ let manifestContent = null;
1913
+ await tar.t({
1914
+ file: bundlePath,
1915
+ onentry: (entry) => {
1916
+ if (entry.path === "manifest.json") ;
1917
+ }
1918
+ });
1919
+ const extractDir = path7.join(path7.dirname(bundlePath), `.temp-${Date.now()}`);
1920
+ if (!fs3.existsSync(extractDir)) {
1921
+ fs3.mkdirSync(extractDir);
1922
+ }
1923
+ try {
1924
+ await tar.x({
1925
+ file: bundlePath,
1926
+ cwd: extractDir,
1927
+ filter: (path8) => path8 === "manifest.json"
1928
+ });
1929
+ const manifestPath = path7.join(extractDir, "manifest.json");
1930
+ if (fs3.existsSync(manifestPath)) {
1931
+ const content = fs3.readFileSync(manifestPath, "utf8");
1932
+ manifestContent = content;
1933
+ }
1934
+ } catch {
1935
+ } finally {
1936
+ if (fs3.existsSync(extractDir)) {
1937
+ fs3.rmSync(extractDir, { recursive: true, force: true });
1938
+ }
1939
+ }
1940
+ if (manifestContent) {
1941
+ try {
1942
+ return JSON.parse(manifestContent);
1943
+ } catch {
1944
+ console.error("Failed to parse manifest JSON");
1945
+ return null;
1946
+ }
1947
+ }
1948
+ return null;
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)}`)
1647
1962
  );
1648
- if (!response.ok) {
1649
- console.error(
1650
- `\u274C Error: ${response.status} ${response.statusText}`
1651
- );
1652
- process.exit(1);
1653
- }
1654
- const results = await response.json();
1655
- if (results.length === 0) {
1656
- console.log("No results found");
1657
- return;
1658
- }
1659
- console.log(`Found ${results.length} result(s):`);
1660
- results.forEach((result) => {
1661
- console.log(
1662
- ` \u{1F4F1} ${result.id}@${result.versions?.[0] || "unknown"}`
1663
- );
1664
- if (result.provides?.length > 0) {
1665
- console.log(` Provides: ${result.provides.join(", ")}`);
1666
- }
1667
- if (result.requires?.length > 0) {
1668
- console.log(` Requires: ${result.requires.join(", ")}`);
1669
- }
1670
- });
1671
- } else {
1672
- console.log("\u{1F4CB} Listing all applications...");
1963
+ break;
1964
+ case "api-key":
1965
+ case "apiKey":
1966
+ config.setApiKey(value);
1967
+ console.log(chalk6.green("\u2705 API key set successfully"));
1673
1968
  console.log(
1674
- "Note: Use --search <query> to search for specific apps"
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
+ )
1675
1972
  );
1676
- }
1677
- } catch (error) {
1678
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1679
- console.error("\u274C Failed to list applications:", errorMessage);
1680
- process.exit(1);
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);
1681
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);
1682
1985
  }
1683
- );
1986
+ });
1684
1987
  }
1685
- function createResolveCommand() {
1686
- return new Command("resolve").description("Resolve dependencies for an application").argument("<app-id>", "Application ID").argument("<version>", "Application version").option("--local", "Use local registry").action(
1687
- async (appId, version, options = {}, command) => {
1688
- try {
1689
- const globalOpts = command.parent?.parent?.opts();
1690
- const useLocal = globalOpts?.local || options.local || false;
1691
- const baseUrl = useLocal ? "http://localhost:8082" : "http://localhost:8080";
1692
- console.log(`\u{1F50D} Resolving dependencies for: ${appId}@${version}`);
1693
- const resolveRequest = {
1694
- root: { id: appId, version },
1695
- installed: []
1696
- // Could be extended to support pre-installed apps
1697
- };
1698
- const response = await fetch(`${baseUrl}/v1/resolve`, {
1699
- method: "POST",
1700
- headers: { "Content-Type": "application/json" },
1701
- body: JSON.stringify(resolveRequest)
1702
- });
1703
- if (!response.ok) {
1704
- const error = await response.json();
1705
- console.error(`\u274C Resolution failed: ${error.error}`);
1706
- console.error(` Details: ${error.details}`);
1707
- process.exit(1);
1708
- }
1709
- const result = await response.json();
1710
- console.log("\u2705 Dependencies resolved successfully!");
1711
- console.log(`\u{1F4CB} Installation plan:`);
1712
- result.plan.forEach(
1713
- (item) => {
1714
- console.log(` ${item.action}: ${item.id}@${item.version}`);
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)");
1715
2011
  }
1716
- );
1717
- if (result.satisfies?.length > 0) {
1718
- console.log(`\u2705 Satisfies: ${result.satisfies.join(", ")}`);
1719
- }
1720
- if (result.missing?.length > 0) {
1721
- console.log(`\u274C Missing: ${result.missing.join(", ")}`);
2012
+ break;
1722
2013
  }
1723
- } catch (error) {
1724
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1725
- console.error("\u274C Failed to resolve dependencies:", errorMessage);
1726
- process.exit(1);
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);
1727
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);
1728
2025
  }
1729
- );
2026
+ });
1730
2027
  }
1731
- function createVerifyCommand() {
1732
- return new Command("verify").description("Verify manifest signature and integrity").argument("<manifest-file>", "Path to manifest JSON file").action(async (manifestFile) => {
2028
+ function createConfigListCommand() {
2029
+ return new Command("list").description("List all configuration values").alias("ls").action(() => {
1733
2030
  try {
1734
- if (!fs3.existsSync(manifestFile)) {
1735
- console.error(`\u274C Manifest file not found: ${manifestFile}`);
1736
- process.exit(1);
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)")}`);
1737
2059
  }
1738
- const manifestContent = fs3.readFileSync(manifestFile, "utf8");
1739
- const manifest = JSON.parse(manifestContent);
1740
- console.log(
1741
- `\u{1F50D} Verifying manifest: ${manifest.id}@${manifest.version}`
1742
- );
1743
- if (manifest.manifest_version !== "1.0") {
1744
- console.error("\u274C Invalid manifest version");
1745
- process.exit(1);
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)"));
1746
2064
  }
1747
- const requiredFields = ["id", "name", "version", "chains", "artifact"];
1748
- const missingFields = requiredFields.filter(
1749
- (field) => !manifest[field]
1750
- );
1751
- if (missingFields.length > 0) {
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) {
1752
2077
  console.error(
1753
- `\u274C Missing required fields: ${missingFields.join(", ")}`
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
+ )
1754
2084
  );
1755
2085
  process.exit(1);
1756
2086
  }
1757
- if (manifest.artifact.type !== "wasm" || manifest.artifact.target !== "node") {
1758
- console.error("\u274C Invalid artifact type or target");
1759
- process.exit(1);
1760
- }
1761
- if (!manifest.artifact.digest.match(/^sha256:[0-9a-f]{64}$/)) {
1762
- console.error("\u274C Invalid artifact digest format");
1763
- process.exit(1);
1764
- }
1765
- if (!manifest.artifact.uri.match(/^(https:\/\/|ipfs:\/\/)/)) {
1766
- console.error("\u274C Invalid artifact URI format");
1767
- process.exit(1);
1768
- }
1769
- if (manifest.signature) {
1770
- console.log("\u{1F510} Verifying signature...");
1771
- console.log("\u26A0\uFE0F Signature verification not implemented in CLI yet");
1772
- }
1773
- console.log("\u2705 Manifest verification passed!");
1774
- console.log(` ID: ${manifest.id}`);
1775
- console.log(` Name: ${manifest.name}`);
1776
- console.log(` Version: ${manifest.version}`);
1777
- console.log(` Chains: ${manifest.chains.join(", ")}`);
1778
- console.log(` Artifact: ${manifest.artifact.uri}`);
1779
- if (manifest.provides?.length > 0) {
1780
- console.log(` Provides: ${manifest.provides.join(", ")}`);
1781
- }
1782
- if (manifest.requires?.length > 0) {
1783
- console.log(` Requires: ${manifest.requires.join(", ")}`);
1784
- }
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)");
1785
2093
  } catch (error) {
1786
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
1787
- console.error("\u274C Failed to verify manifest:", errorMessage);
2094
+ const message = error instanceof Error ? error.message : String(error);
2095
+ console.error(chalk6.red("\u274C Failed to reset configuration:"), message);
1788
2096
  process.exit(1);
1789
2097
  }
1790
2098
  });
@@ -1795,6 +2103,18 @@ var program = new Command();
1795
2103
  program.name("calimero-registry").description(
1796
2104
  "Calimero Network App Registry CLI - Command-line interface for the App Registry"
1797
2105
  ).version("1.0.0");
2106
+ program.addHelpText(
2107
+ "after",
2108
+ `
2109
+ Examples:
2110
+ $ calimero-registry apps list
2111
+ $ calimero-registry apps create --file manifest.json
2112
+ $ calimero-registry local start
2113
+ $ calimero-registry health --local
2114
+
2115
+ For more information, visit: https://github.com/calimero-network/app-registry
2116
+ `
2117
+ );
1798
2118
  program.option("-u, --url <url>", "Registry API URL", "http://localhost:8082");
1799
2119
  program.option(
1800
2120
  "-t, --timeout <timeout>",
@@ -1808,15 +2128,16 @@ program.addCommand(attestationsCommand);
1808
2128
  program.addCommand(healthCommand);
1809
2129
  program.addCommand(ipfsCommand);
1810
2130
  program.addCommand(localCommand);
1811
- program.addCommand(v1Command);
2131
+ program.addCommand(bundleCommand);
2132
+ program.addCommand(configCommand);
1812
2133
  program.exitOverride();
1813
2134
  try {
1814
2135
  program.parse();
1815
2136
  } catch (err) {
1816
2137
  if (err instanceof Error) {
1817
- console.error(chalk5.red("Error:"), err.message);
2138
+ console.error(chalk6.red("Error:"), err.message);
1818
2139
  } else {
1819
- console.error(chalk5.red("Unknown error occurred"));
2140
+ console.error(chalk6.red("Unknown error occurred"));
1820
2141
  }
1821
2142
  process.exit(1);
1822
2143
  }