@calimero-network/registry-cli 1.2.0 → 1.4.2
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 +463 -612
- package/dist/index.js.map +1 -1
- package/package.json +11 -6
package/dist/index.js
CHANGED
|
@@ -5,40 +5,40 @@ import ora6 from 'ora';
|
|
|
5
5
|
import { table } from 'table';
|
|
6
6
|
import { SSAppRegistryClient } from '@calimero-network/registry-client';
|
|
7
7
|
import fs3, { existsSync, writeFileSync } from 'fs';
|
|
8
|
-
import
|
|
8
|
+
import path6 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
|
|
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 =
|
|
17
|
+
this.configPath = path6.join(
|
|
19
18
|
os.homedir(),
|
|
20
19
|
".calimero-registry",
|
|
21
20
|
"config.json"
|
|
22
21
|
);
|
|
23
|
-
this.dataDir =
|
|
22
|
+
this.dataDir = path6.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: "
|
|
29
|
+
host: "0.0.0.0",
|
|
30
|
+
publicHost: "host.docker.internal"
|
|
31
31
|
},
|
|
32
32
|
data: {
|
|
33
|
-
dir:
|
|
34
|
-
artifactsDir:
|
|
33
|
+
dir: path6.join(os.homedir(), ".calimero-registry", "data"),
|
|
34
|
+
artifactsDir: path6.join(
|
|
35
35
|
os.homedir(),
|
|
36
36
|
".calimero-registry",
|
|
37
37
|
"artifacts"
|
|
38
38
|
)
|
|
39
39
|
},
|
|
40
40
|
artifacts: {
|
|
41
|
-
storageDir:
|
|
41
|
+
storageDir: path6.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 {
|
|
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 =
|
|
74
|
+
const configDir = path6.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 =
|
|
108
|
+
this.config.artifacts.storageDir = path6.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
|
-
|
|
113
|
-
|
|
132
|
+
path6.join(this.getDataDir(), "backups"),
|
|
133
|
+
path6.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:
|
|
137
|
-
artifactsDir:
|
|
156
|
+
dir: path6.join(os.homedir(), ".calimero-registry", "data"),
|
|
157
|
+
artifactsDir: path6.join(
|
|
138
158
|
os.homedir(),
|
|
139
159
|
".calimero-registry",
|
|
140
160
|
"artifacts"
|
|
141
161
|
)
|
|
142
162
|
},
|
|
143
163
|
artifacts: {
|
|
144
|
-
storageDir:
|
|
164
|
+
storageDir: path6.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 =
|
|
158
|
-
this.
|
|
159
|
-
|
|
177
|
+
this.appsFile = path6.join(config.getDataDir(), "apps.json");
|
|
178
|
+
this.bundleManifestsFile = path6.join(
|
|
179
|
+
config.getDataDir(),
|
|
180
|
+
"bundle_manifests.json"
|
|
181
|
+
);
|
|
182
|
+
this.artifactsFile = path6.join(config.getDataDir(), "artifacts.json");
|
|
160
183
|
this.data = {
|
|
161
184
|
apps: /* @__PURE__ */ new Map(),
|
|
162
|
-
|
|
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.
|
|
174
|
-
const
|
|
175
|
-
fs3.readFileSync(this.
|
|
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
|
|
219
|
+
const bundleManifestsObj = Object.fromEntries(this.data.bundleManifests);
|
|
195
220
|
fs3.writeFileSync(
|
|
196
|
-
this.
|
|
197
|
-
JSON.stringify(
|
|
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(
|
|
253
|
+
// Versions management (bundles only)
|
|
254
|
+
getAppVersions(packageId) {
|
|
230
255
|
const versions = [];
|
|
231
|
-
for (const [key, manifest] of this.data.
|
|
232
|
-
if (key.startsWith(`${
|
|
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
|
-
|
|
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
|
-
|
|
248
|
-
const
|
|
249
|
-
return this.data.
|
|
273
|
+
// Manifest management (V2 / Bundles)
|
|
274
|
+
getBundleManifest(pkg, version) {
|
|
275
|
+
const key = `${pkg}/${version}`;
|
|
276
|
+
return this.data.bundleManifests.get(key);
|
|
250
277
|
}
|
|
251
|
-
|
|
252
|
-
|
|
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.
|
|
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 ||
|
|
302
|
+
const backupPath = outputPath || path6.join(
|
|
275
303
|
this.config.getDataDir(),
|
|
276
304
|
"backups",
|
|
277
305
|
`backup-${timestamp}.json`
|
|
278
306
|
);
|
|
279
|
-
const backupDir =
|
|
307
|
+
const backupDir = path6.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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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 =
|
|
401
|
+
const appVersionDir = path6.join(this.artifactsDir, appId, version);
|
|
395
402
|
if (!fs3.existsSync(appVersionDir)) {
|
|
396
403
|
fs3.mkdirSync(appVersionDir, { recursive: true });
|
|
397
404
|
}
|
|
398
|
-
const targetPath =
|
|
405
|
+
const targetPath = path6.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 =
|
|
413
|
+
const artifactPath = path6.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.
|
|
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.
|
|
452
|
+
const baseUrl = `http://${this.config.getPublicHost()}:${this.config.getPort()}`;
|
|
446
453
|
return `${baseUrl}/artifacts/${hash}`;
|
|
447
454
|
}
|
|
448
|
-
// Update manifest
|
|
449
|
-
|
|
455
|
+
// Update manifest artifact to use local URLs (and preserve HTTP access)
|
|
456
|
+
updateManifestArtifact(manifest) {
|
|
450
457
|
const updatedManifest = { ...manifest };
|
|
451
|
-
if (
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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, path6.basename(manifest.artifact.uri))}`
|
|
466
|
+
};
|
|
467
|
+
} else if (uri.startsWith("file://")) {
|
|
468
|
+
const filePath = uri.replace("file://", "");
|
|
469
|
+
const filename = path6.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 =
|
|
484
|
+
const fullPath = path6.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 =
|
|
513
|
+
const fullPath = path6.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.
|
|
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 = `${
|
|
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
|
-
|
|
588
|
-
|
|
589
|
-
latest_version:
|
|
590
|
-
|
|
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(
|
|
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.
|
|
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 (
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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 = path6.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("-
|
|
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,7 +656,6 @@ 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)`);
|
|
@@ -668,13 +664,13 @@ var appsCommand = new Command("apps").description("Manage SSApp applications").a
|
|
|
668
664
|
return;
|
|
669
665
|
}
|
|
670
666
|
const tableData = [
|
|
671
|
-
["
|
|
667
|
+
["ID", "Name", "Latest Version", "Digest"],
|
|
672
668
|
...apps.map((app) => [
|
|
669
|
+
app.id,
|
|
673
670
|
app.name,
|
|
674
|
-
app.
|
|
675
|
-
|
|
676
|
-
app.latest_cid
|
|
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));
|
|
@@ -756,7 +752,7 @@ 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 =
|
|
755
|
+
const manifestPath = path6.resolve(manifestFile);
|
|
760
756
|
if (!fs3.existsSync(manifestPath)) {
|
|
761
757
|
spinner2.fail("Manifest file not found");
|
|
762
758
|
console.error(chalk5.red(`File not found: ${manifestFile}`));
|
|
@@ -1072,54 +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
|
-
return this.dataStore.getApps(query);
|
|
1078
|
-
});
|
|
1079
|
-
this.server.get("/apps/:appId", async (request) => {
|
|
1080
|
-
const { appId } = request.params;
|
|
1081
|
-
return this.dataStore.getAppVersions(appId);
|
|
1082
|
-
});
|
|
1083
|
-
this.server.get("/apps/:appId/:semver", async (request) => {
|
|
1084
|
-
const { appId, semver } = request.params;
|
|
1085
|
-
const manifest = this.dataStore.getManifest(appId, semver);
|
|
1086
|
-
if (!manifest) {
|
|
1087
|
-
return {
|
|
1088
|
-
statusCode: 404,
|
|
1089
|
-
error: "Not Found",
|
|
1090
|
-
message: "Manifest not found"
|
|
1091
|
-
};
|
|
1092
|
-
}
|
|
1093
|
-
return this.artifactServer.updateManifestArtifacts(manifest);
|
|
1094
|
-
});
|
|
1095
|
-
this.server.post("/apps", async (request) => {
|
|
1096
|
-
const manifest = request.body;
|
|
1097
|
-
if (!this.validateManifest(manifest)) {
|
|
1098
|
-
return {
|
|
1099
|
-
statusCode: 400,
|
|
1100
|
-
error: "Bad Request",
|
|
1101
|
-
message: "Invalid manifest structure"
|
|
1102
|
-
};
|
|
1103
|
-
}
|
|
1104
|
-
const processedManifest = await this.processManifestArtifacts(manifest);
|
|
1105
|
-
const manifestKey = `${manifest.app.app_id}/${manifest.version.semver}`;
|
|
1106
|
-
this.dataStore.setManifest(manifestKey, processedManifest);
|
|
1107
|
-
const appKey = manifest.app.app_id;
|
|
1108
|
-
const appSummary = {
|
|
1109
|
-
name: manifest.app.name,
|
|
1110
|
-
developer_pubkey: manifest.app.developer_pubkey,
|
|
1111
|
-
latest_version: manifest.version.semver,
|
|
1112
|
-
latest_cid: manifest.artifacts[0]?.cid || "",
|
|
1113
|
-
alias: manifest.app.name
|
|
1114
|
-
// Use name as alias
|
|
1115
|
-
};
|
|
1116
|
-
this.dataStore.setApp(appKey, appSummary);
|
|
1117
|
-
return {
|
|
1118
|
-
success: true,
|
|
1119
|
-
message: "App version registered successfully",
|
|
1120
|
-
manifest_key: manifestKey
|
|
1121
|
-
};
|
|
1122
|
-
});
|
|
1123
1071
|
this.server.get(
|
|
1124
1072
|
"/artifacts/:appId/:version/:filename",
|
|
1125
1073
|
async (request, reply) => {
|
|
@@ -1180,180 +1128,109 @@ var LocalRegistryServer = class {
|
|
|
1180
1128
|
await this.seed();
|
|
1181
1129
|
return { message: "Sample data seeded successfully" };
|
|
1182
1130
|
});
|
|
1183
|
-
this.server.
|
|
1184
|
-
const
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
const appKey = manifest.id;
|
|
1196
|
-
const appSummary = {
|
|
1197
|
-
id: manifest.id,
|
|
1198
|
-
name: manifest.name,
|
|
1199
|
-
developer_pubkey: "local-dev-key",
|
|
1200
|
-
latest_version: manifest.version,
|
|
1201
|
-
latest_cid: manifest.artifact?.digest || ""
|
|
1202
|
-
};
|
|
1203
|
-
this.dataStore.setApp(appKey, appSummary);
|
|
1204
|
-
return reply.code(201).send({
|
|
1205
|
-
id: manifest.id,
|
|
1206
|
-
version: manifest.version,
|
|
1207
|
-
canonical_uri: `/v1/apps/${manifest.id}/${manifest.version}`
|
|
1208
|
-
});
|
|
1209
|
-
} catch {
|
|
1210
|
-
return reply.code(409).send({
|
|
1211
|
-
error: "already_exists",
|
|
1212
|
-
details: `${manifest.id}@${manifest.version}`
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1215
|
-
});
|
|
1216
|
-
this.server.get("/v1/apps/:id", async (request, reply) => {
|
|
1217
|
-
const { id } = request.params;
|
|
1218
|
-
const versions = this.dataStore.getAppVersions(id);
|
|
1219
|
-
if (!versions || versions.length === 0) {
|
|
1220
|
-
return reply.code(404).send({ error: "not_found", message: "App not found" });
|
|
1221
|
-
}
|
|
1222
|
-
return {
|
|
1223
|
-
id,
|
|
1224
|
-
versions: versions.map((v) => v.semver)
|
|
1225
|
-
};
|
|
1226
|
-
});
|
|
1227
|
-
this.server.get("/v1/apps/:id/:version", async (request, reply) => {
|
|
1228
|
-
const { id, version } = request.params;
|
|
1229
|
-
const { canonical } = request.query;
|
|
1230
|
-
const oldManifest = this.dataStore.getManifest(id, version);
|
|
1231
|
-
if (!oldManifest) {
|
|
1232
|
-
return reply.code(404).send({ error: "not_found", message: "Manifest not found" });
|
|
1233
|
-
}
|
|
1234
|
-
const v1Manifest = {
|
|
1235
|
-
manifest_version: oldManifest.manifest_version,
|
|
1236
|
-
id: oldManifest.app.app_id,
|
|
1237
|
-
name: oldManifest.app.name,
|
|
1238
|
-
version: oldManifest.version.semver,
|
|
1239
|
-
chains: oldManifest.supported_chains,
|
|
1240
|
-
artifact: oldManifest.artifacts[0] ? {
|
|
1241
|
-
type: oldManifest.artifacts[0].type,
|
|
1242
|
-
target: oldManifest.artifacts[0].target,
|
|
1243
|
-
digest: oldManifest.artifacts[0].cid || `sha256:${"0".repeat(64)}`,
|
|
1244
|
-
uri: oldManifest.artifacts[0].path || oldManifest.artifacts[0].mirrors?.[0] || "https://example.com/artifact"
|
|
1245
|
-
} : {
|
|
1246
|
-
type: "wasm",
|
|
1247
|
-
target: "node",
|
|
1248
|
-
digest: `sha256:${"0".repeat(64)}`,
|
|
1249
|
-
uri: "https://example.com/artifact"
|
|
1250
|
-
},
|
|
1251
|
-
_warnings: []
|
|
1252
|
-
};
|
|
1253
|
-
if (canonical === "true") {
|
|
1254
|
-
const canonicalJCS = JSON.stringify(
|
|
1255
|
-
v1Manifest,
|
|
1256
|
-
Object.keys(v1Manifest).sort()
|
|
1257
|
-
);
|
|
1258
|
-
return {
|
|
1259
|
-
canonical_jcs: Buffer.from(canonicalJCS, "utf8").toString("base64")
|
|
1260
|
-
};
|
|
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];
|
|
1261
1143
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
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);
|
|
1271
1157
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
(
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
id: app.id || app.name,
|
|
1278
|
-
versions: [app.latest_version]
|
|
1279
|
-
}));
|
|
1280
|
-
});
|
|
1281
|
-
this.server.post("/v1/resolve", async (request, reply) => {
|
|
1282
|
-
const { root } = request.body;
|
|
1283
|
-
if (!root || !root.id || !root.version) {
|
|
1284
|
-
return reply.code(400).send({
|
|
1285
|
-
error: "bad_request",
|
|
1286
|
-
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
|
|
1287
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
|
+
}
|
|
1288
1174
|
}
|
|
1289
|
-
return
|
|
1290
|
-
plan: [{ action: "install", id: root.id, version: root.version }],
|
|
1291
|
-
satisfies: [],
|
|
1292
|
-
missing: []
|
|
1293
|
-
};
|
|
1175
|
+
return Array.from(latestByPackage.values());
|
|
1294
1176
|
});
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
if (fs3.existsSync(filePath)) {
|
|
1306
|
-
const filename = path.basename(filePath);
|
|
1307
|
-
const appId = manifest.id;
|
|
1308
|
-
try {
|
|
1309
|
-
await this.artifactServer.copyArtifactToLocal(
|
|
1310
|
-
filePath,
|
|
1311
|
-
appId,
|
|
1312
|
-
manifest.version,
|
|
1313
|
-
filename
|
|
1314
|
-
);
|
|
1315
|
-
processedManifest.artifact = {
|
|
1316
|
-
...artifact,
|
|
1317
|
-
uri: this.artifactServer.getArtifactUrl(
|
|
1318
|
-
appId,
|
|
1319
|
-
manifest.version,
|
|
1320
|
-
filename
|
|
1321
|
-
)
|
|
1322
|
-
};
|
|
1323
|
-
} catch (error) {
|
|
1324
|
-
console.warn(`Failed to copy artifact ${filePath}:`, error);
|
|
1325
|
-
}
|
|
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
|
+
});
|
|
1326
1187
|
}
|
|
1188
|
+
return manifest;
|
|
1327
1189
|
}
|
|
1328
|
-
|
|
1329
|
-
return processedManifest;
|
|
1190
|
+
);
|
|
1330
1191
|
}
|
|
1331
1192
|
};
|
|
1332
1193
|
var localCommand = new Command("local").description(
|
|
1333
1194
|
"Manage local registry for development"
|
|
1334
1195
|
);
|
|
1335
1196
|
localCommand.addCommand(
|
|
1336
|
-
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", "
|
|
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) => {
|
|
1337
1202
|
const spinner2 = ora6("Starting local registry...").start();
|
|
1338
1203
|
try {
|
|
1339
1204
|
const config = new LocalConfig();
|
|
1205
|
+
config.setHost(options.host);
|
|
1206
|
+
config.setPublicHost(options.publicHost);
|
|
1207
|
+
config.setPort(parseInt(options.port, 10));
|
|
1340
1208
|
const server = new LocalRegistryServer(config);
|
|
1341
1209
|
await server.start(parseInt(options.port));
|
|
1342
1210
|
spinner2.succeed(
|
|
1343
|
-
`Local registry started on http://${
|
|
1211
|
+
`Local registry started on http://${config.getHost()}:${config.getPort()}`
|
|
1344
1212
|
);
|
|
1345
1213
|
console.log(chalk5.blue("\n\u{1F4F1} Local Registry Status:"));
|
|
1346
1214
|
console.log(
|
|
1347
|
-
chalk5.green(
|
|
1215
|
+
chalk5.green(
|
|
1216
|
+
`\u2705 Server: http://${config.getHost()}:${config.getPort()}`
|
|
1217
|
+
)
|
|
1218
|
+
);
|
|
1219
|
+
console.log(
|
|
1220
|
+
chalk5.green(
|
|
1221
|
+
`\u{1F310} Public URL: http://${config.getPublicHost()}:${config.getPort()}`
|
|
1222
|
+
)
|
|
1348
1223
|
);
|
|
1349
1224
|
console.log(chalk5.green(`\u{1F4C1} Data: ${config.getDataDir()}`));
|
|
1350
1225
|
console.log(
|
|
1351
1226
|
chalk5.green(
|
|
1352
|
-
`\u{1F4CB} Health: http://${
|
|
1227
|
+
`\u{1F4CB} Health: http://${config.getHost()}:${config.getPort()}/healthz`
|
|
1353
1228
|
)
|
|
1354
1229
|
);
|
|
1355
1230
|
console.log(
|
|
1356
|
-
chalk5.green(
|
|
1231
|
+
chalk5.green(
|
|
1232
|
+
`\u{1F4CA} Stats: http://${config.getHost()}:${config.getPort()}/stats`
|
|
1233
|
+
)
|
|
1357
1234
|
);
|
|
1358
1235
|
console.log(
|
|
1359
1236
|
chalk5.blue(
|
|
@@ -1509,271 +1386,245 @@ localCommand.addCommand(
|
|
|
1509
1386
|
}
|
|
1510
1387
|
})
|
|
1511
1388
|
);
|
|
1512
|
-
var
|
|
1513
|
-
function
|
|
1514
|
-
return new Command("
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
if (!fs3.existsSync(manifestFile)) {
|
|
1520
|
-
console.error(`\u274C Manifest file not found: ${manifestFile}`);
|
|
1521
|
-
process.exit(1);
|
|
1522
|
-
}
|
|
1523
|
-
const manifestContent = fs3.readFileSync(manifestFile, "utf8");
|
|
1524
|
-
const manifest = JSON.parse(manifestContent);
|
|
1525
|
-
if (manifest.manifest_version !== "1.0") {
|
|
1526
|
-
console.error('\u274C Invalid manifest version. Must be "1.0"');
|
|
1527
|
-
process.exit(1);
|
|
1528
|
-
}
|
|
1529
|
-
if (!manifest.id || !manifest.name || !manifest.version) {
|
|
1530
|
-
console.error("\u274C Missing required fields: id, name, version");
|
|
1531
|
-
process.exit(1);
|
|
1532
|
-
}
|
|
1533
|
-
const baseUrl = useLocal ? "http://localhost:8082" : "http://localhost:8080";
|
|
1534
|
-
console.log(
|
|
1535
|
-
`\u{1F4E4} Submitting manifest: ${manifest.id}@${manifest.version}`
|
|
1536
|
-
);
|
|
1537
|
-
console.log(` Name: ${manifest.name}`);
|
|
1538
|
-
console.log(` Chains: ${manifest.chains?.join(", ")}`);
|
|
1539
|
-
console.log(
|
|
1540
|
-
` Provides: ${manifest.provides?.join(", ") || "none"}`
|
|
1541
|
-
);
|
|
1542
|
-
console.log(
|
|
1543
|
-
` Requires: ${manifest.requires?.join(", ") || "none"}`
|
|
1544
|
-
);
|
|
1545
|
-
const response = await fetch(`${baseUrl}/v1/apps`, {
|
|
1546
|
-
method: "POST",
|
|
1547
|
-
headers: { "Content-Type": "application/json" },
|
|
1548
|
-
body: JSON.stringify(manifest)
|
|
1549
|
-
});
|
|
1550
|
-
if (!response.ok) {
|
|
1551
|
-
const error = await response.json();
|
|
1552
|
-
throw new Error(`${error.error}: ${error.details}`);
|
|
1553
|
-
}
|
|
1554
|
-
const result = await response.json();
|
|
1555
|
-
console.log("\u2705 Manifest submitted successfully!");
|
|
1556
|
-
console.log(` ID: ${result.id}`);
|
|
1557
|
-
console.log(` Version: ${result.version}`);
|
|
1558
|
-
console.log(` URI: ${result.canonical_uri}`);
|
|
1559
|
-
} catch (error) {
|
|
1560
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1561
|
-
console.error("\u274C Failed to submit manifest:", errorMessage);
|
|
1562
|
-
process.exit(1);
|
|
1563
|
-
}
|
|
1389
|
+
var bundleCommand = new Command("bundle").description("Manage application bundles (V2)").addCommand(createCreateCommand()).addCommand(createPushCommand()).addCommand(createGetCommand());
|
|
1390
|
+
function createCreateCommand() {
|
|
1391
|
+
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(
|
|
1392
|
+
"--export <interface>",
|
|
1393
|
+
"Export interface (can be specified multiple times)",
|
|
1394
|
+
(value, prev) => {
|
|
1395
|
+
return [...prev || [], value];
|
|
1564
1396
|
}
|
|
1565
|
-
)
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
const url = options.canonical ? `/v1/apps/${appId}/${version}?canonical=true` : `/v1/apps/${appId}/${version}`;
|
|
1577
|
-
const response = await fetch(`${baseUrl}${url}`);
|
|
1578
|
-
if (!response.ok) {
|
|
1579
|
-
if (response.status === 404) {
|
|
1580
|
-
console.error(`\u274C Manifest not found: ${appId}@${version}`);
|
|
1581
|
-
} else {
|
|
1582
|
-
console.error(
|
|
1583
|
-
`\u274C Error: ${response.status} ${response.statusText}`
|
|
1584
|
-
);
|
|
1585
|
-
}
|
|
1586
|
-
process.exit(1);
|
|
1587
|
-
}
|
|
1588
|
-
const manifest = await response.json();
|
|
1589
|
-
console.log(JSON.stringify(manifest, null, 2));
|
|
1590
|
-
} else {
|
|
1591
|
-
console.log(`\u{1F4E5} Getting versions for: ${appId}`);
|
|
1592
|
-
const response = await fetch(`${baseUrl}/v1/apps/${appId}`);
|
|
1593
|
-
if (!response.ok) {
|
|
1594
|
-
if (response.status === 404) {
|
|
1595
|
-
console.error(`\u274C App not found: ${appId}`);
|
|
1596
|
-
} else {
|
|
1597
|
-
console.error(
|
|
1598
|
-
`\u274C Error: ${response.status} ${response.statusText}`
|
|
1599
|
-
);
|
|
1600
|
-
}
|
|
1601
|
-
process.exit(1);
|
|
1602
|
-
}
|
|
1603
|
-
const result = await response.json();
|
|
1604
|
-
console.log(`\u{1F4F1} App: ${result.id}`);
|
|
1605
|
-
console.log(`\u{1F4CB} Versions: ${result.versions.join(", ")}`);
|
|
1606
|
-
}
|
|
1607
|
-
} catch (error) {
|
|
1608
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1609
|
-
console.error("\u274C Failed to get manifest:", errorMessage);
|
|
1397
|
+
).option(
|
|
1398
|
+
"--use <interface>",
|
|
1399
|
+
"Use interface (can be specified multiple times)",
|
|
1400
|
+
(value, prev) => {
|
|
1401
|
+
return [...prev || [], value];
|
|
1402
|
+
}
|
|
1403
|
+
).action(async (wasmFile, pkg, version, options) => {
|
|
1404
|
+
try {
|
|
1405
|
+
const wasmPath = path6.resolve(wasmFile);
|
|
1406
|
+
if (!fs3.existsSync(wasmPath)) {
|
|
1407
|
+
console.error(`\u274C WASM file not found: ${wasmFile}`);
|
|
1610
1408
|
process.exit(1);
|
|
1611
1409
|
}
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1410
|
+
console.log(`\u{1F4E6} Creating MPK bundle from: ${path6.basename(wasmPath)}`);
|
|
1411
|
+
const wasmContent = fs3.readFileSync(wasmPath);
|
|
1412
|
+
const wasmSize = wasmContent.length;
|
|
1413
|
+
const hash = crypto.createHash("sha256").update(wasmContent).digest("hex");
|
|
1414
|
+
const metadata = {
|
|
1415
|
+
name: options.name || pkg,
|
|
1416
|
+
description: options.description || "",
|
|
1417
|
+
author: options.author || "Calimero Team"
|
|
1418
|
+
};
|
|
1419
|
+
const links = {};
|
|
1420
|
+
if (options.frontend) links.frontend = options.frontend;
|
|
1421
|
+
if (options.github) links.github = options.github;
|
|
1422
|
+
if (options.docs) links.docs = options.docs;
|
|
1423
|
+
const interfaces = {
|
|
1424
|
+
exports: options.export || [],
|
|
1425
|
+
uses: options.use || []
|
|
1426
|
+
};
|
|
1427
|
+
const manifest = {
|
|
1428
|
+
version: "1.0",
|
|
1429
|
+
package: pkg,
|
|
1430
|
+
appVersion: version,
|
|
1431
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0,
|
|
1432
|
+
interfaces: interfaces.exports.length > 0 || interfaces.uses.length > 0 ? interfaces : void 0,
|
|
1433
|
+
wasm: {
|
|
1434
|
+
path: "app.wasm",
|
|
1435
|
+
hash,
|
|
1436
|
+
size: wasmSize
|
|
1437
|
+
},
|
|
1438
|
+
abi: null,
|
|
1439
|
+
migrations: [],
|
|
1440
|
+
links: Object.keys(links).length > 0 ? links : void 0,
|
|
1441
|
+
signature: void 0
|
|
1442
|
+
};
|
|
1443
|
+
let outputPath = options.output;
|
|
1444
|
+
if (!outputPath) {
|
|
1445
|
+
const outputDir = path6.join(process.cwd(), pkg, version);
|
|
1446
|
+
if (!fs3.existsSync(outputDir)) {
|
|
1447
|
+
fs3.mkdirSync(outputDir, { recursive: true });
|
|
1448
|
+
}
|
|
1449
|
+
outputPath = path6.join(outputDir, `${pkg}-${version}.mpk`);
|
|
1450
|
+
} else {
|
|
1451
|
+
outputPath = path6.resolve(outputPath);
|
|
1452
|
+
const outputDir = path6.dirname(outputPath);
|
|
1453
|
+
if (!fs3.existsSync(outputDir)) {
|
|
1454
|
+
fs3.mkdirSync(outputDir, { recursive: true });
|
|
1655
1455
|
}
|
|
1656
|
-
} catch (error) {
|
|
1657
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1658
|
-
console.error("\u274C Failed to list applications:", errorMessage);
|
|
1659
|
-
process.exit(1);
|
|
1660
1456
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
async (appId, version, options = {}, command) => {
|
|
1457
|
+
const tempDir = path6.join(
|
|
1458
|
+
path6.dirname(outputPath),
|
|
1459
|
+
`.temp-bundle-${Date.now()}`
|
|
1460
|
+
);
|
|
1461
|
+
fs3.mkdirSync(tempDir, { recursive: true });
|
|
1667
1462
|
try {
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
console.log(`\u{1F50D} Resolving dependencies for: ${appId}@${version}`);
|
|
1672
|
-
const resolveRequest = {
|
|
1673
|
-
root: { id: appId, version },
|
|
1674
|
-
installed: []
|
|
1675
|
-
// Could be extended to support pre-installed apps
|
|
1676
|
-
};
|
|
1677
|
-
const response = await fetch(`${baseUrl}/v1/resolve`, {
|
|
1678
|
-
method: "POST",
|
|
1679
|
-
headers: { "Content-Type": "application/json" },
|
|
1680
|
-
body: JSON.stringify(resolveRequest)
|
|
1681
|
-
});
|
|
1682
|
-
if (!response.ok) {
|
|
1683
|
-
const error = await response.json();
|
|
1684
|
-
console.error(`\u274C Resolution failed: ${error.error}`);
|
|
1685
|
-
console.error(` Details: ${error.details}`);
|
|
1686
|
-
process.exit(1);
|
|
1687
|
-
}
|
|
1688
|
-
const result = await response.json();
|
|
1689
|
-
console.log("\u2705 Dependencies resolved successfully!");
|
|
1690
|
-
console.log(`\u{1F4CB} Installation plan:`);
|
|
1691
|
-
result.plan.forEach(
|
|
1692
|
-
(item) => {
|
|
1693
|
-
console.log(` ${item.action}: ${item.id}@${item.version}`);
|
|
1694
|
-
}
|
|
1463
|
+
fs3.writeFileSync(
|
|
1464
|
+
path6.join(tempDir, "manifest.json"),
|
|
1465
|
+
JSON.stringify(manifest, null, 2)
|
|
1695
1466
|
);
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1467
|
+
fs3.writeFileSync(path6.join(tempDir, "app.wasm"), wasmContent);
|
|
1468
|
+
await tar.create(
|
|
1469
|
+
{
|
|
1470
|
+
gzip: true,
|
|
1471
|
+
file: outputPath,
|
|
1472
|
+
cwd: tempDir
|
|
1473
|
+
},
|
|
1474
|
+
["manifest.json", "app.wasm"]
|
|
1475
|
+
);
|
|
1476
|
+
const outputSize = fs3.statSync(outputPath).size;
|
|
1477
|
+
console.log(`\u2705 Created MPK bundle: ${outputPath}`);
|
|
1478
|
+
console.log(` Package: ${pkg}`);
|
|
1479
|
+
console.log(` Version: ${version}`);
|
|
1480
|
+
console.log(` Size: ${outputSize} bytes`);
|
|
1481
|
+
console.log(` WASM Hash: ${hash}`);
|
|
1482
|
+
} finally {
|
|
1483
|
+
if (fs3.existsSync(tempDir)) {
|
|
1484
|
+
fs3.rmSync(tempDir, { recursive: true, force: true });
|
|
1701
1485
|
}
|
|
1702
|
-
} catch (error) {
|
|
1703
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1704
|
-
console.error("\u274C Failed to resolve dependencies:", errorMessage);
|
|
1705
|
-
process.exit(1);
|
|
1706
1486
|
}
|
|
1487
|
+
} catch (error) {
|
|
1488
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1489
|
+
console.error("\u274C Failed to create bundle:", message);
|
|
1490
|
+
process.exit(1);
|
|
1707
1491
|
}
|
|
1708
|
-
);
|
|
1492
|
+
});
|
|
1709
1493
|
}
|
|
1710
|
-
function
|
|
1711
|
-
return new Command("
|
|
1494
|
+
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) => {
|
|
1712
1496
|
try {
|
|
1713
|
-
|
|
1714
|
-
|
|
1497
|
+
const fullPath = path6.resolve(bundleFile);
|
|
1498
|
+
if (!fs3.existsSync(fullPath)) {
|
|
1499
|
+
console.error(`\u274C File not found: ${bundleFile}`);
|
|
1715
1500
|
process.exit(1);
|
|
1716
1501
|
}
|
|
1717
|
-
|
|
1718
|
-
const manifest =
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
);
|
|
1722
|
-
if (manifest.manifest_version !== "1.0") {
|
|
1723
|
-
console.error("\u274C Invalid manifest version");
|
|
1724
|
-
process.exit(1);
|
|
1725
|
-
}
|
|
1726
|
-
const requiredFields = ["id", "name", "version", "chains", "artifact"];
|
|
1727
|
-
const missingFields = requiredFields.filter(
|
|
1728
|
-
(field) => !manifest[field]
|
|
1729
|
-
);
|
|
1730
|
-
if (missingFields.length > 0) {
|
|
1731
|
-
console.error(
|
|
1732
|
-
`\u274C Missing required fields: ${missingFields.join(", ")}`
|
|
1733
|
-
);
|
|
1502
|
+
console.log(`\u{1F4E6} Processing bundle: ${path6.basename(fullPath)}`);
|
|
1503
|
+
const manifest = await extractManifest(fullPath);
|
|
1504
|
+
if (!manifest) {
|
|
1505
|
+
console.error("\u274C manifest.json not found in bundle");
|
|
1734
1506
|
process.exit(1);
|
|
1735
1507
|
}
|
|
1736
|
-
if (manifest.
|
|
1737
|
-
console.error("\u274C Invalid
|
|
1508
|
+
if (!manifest.package || !manifest.appVersion) {
|
|
1509
|
+
console.error("\u274C Invalid manifest: missing package or appVersion");
|
|
1738
1510
|
process.exit(1);
|
|
1739
1511
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1512
|
+
console.log(` Package: ${manifest.package}`);
|
|
1513
|
+
console.log(` Version: ${manifest.appVersion}`);
|
|
1514
|
+
if (manifest.metadata) {
|
|
1515
|
+
console.log(` Name: ${manifest.metadata.name}`);
|
|
1516
|
+
}
|
|
1517
|
+
if (options.local) {
|
|
1518
|
+
const config = new LocalConfig();
|
|
1519
|
+
const store = new LocalDataStore(config);
|
|
1520
|
+
const artifactServer = new LocalArtifactServer(config, store);
|
|
1521
|
+
const bundleFilename = `${manifest.package}-${manifest.appVersion}.mpk`;
|
|
1522
|
+
const targetPath = await artifactServer.copyArtifactToLocal(
|
|
1523
|
+
fullPath,
|
|
1524
|
+
manifest.package,
|
|
1525
|
+
manifest.appVersion,
|
|
1526
|
+
bundleFilename
|
|
1527
|
+
);
|
|
1528
|
+
console.log(` Artifact stored: ${targetPath}`);
|
|
1529
|
+
store.setBundleManifest(
|
|
1530
|
+
manifest.package,
|
|
1531
|
+
manifest.appVersion,
|
|
1532
|
+
manifest
|
|
1533
|
+
);
|
|
1534
|
+
console.log("\u2705 Bundle manifest registered locally");
|
|
1535
|
+
console.log(
|
|
1536
|
+
` Run 'calimero-registry bundle get ${manifest.package} ${manifest.appVersion}' to verify`
|
|
1537
|
+
);
|
|
1538
|
+
} else {
|
|
1539
|
+
console.error("\u274C Remote push not implemented yet");
|
|
1742
1540
|
process.exit(1);
|
|
1743
1541
|
}
|
|
1744
|
-
|
|
1745
|
-
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
console.error("\u274C Failed to push bundle:", error);
|
|
1544
|
+
process.exit(1);
|
|
1545
|
+
}
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
function createGetCommand() {
|
|
1549
|
+
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) => {
|
|
1550
|
+
try {
|
|
1551
|
+
if (options.local) {
|
|
1552
|
+
const config = new LocalConfig();
|
|
1553
|
+
const store = new LocalDataStore(config);
|
|
1554
|
+
const manifest = store.getBundleManifest(pkg, version);
|
|
1555
|
+
if (manifest) {
|
|
1556
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
1557
|
+
} else {
|
|
1558
|
+
console.error(`\u274C Manifest not found: ${pkg}@${version}`);
|
|
1559
|
+
process.exit(1);
|
|
1560
|
+
}
|
|
1561
|
+
} else {
|
|
1562
|
+
console.error("\u274C Remote get not implemented yet");
|
|
1746
1563
|
process.exit(1);
|
|
1747
1564
|
}
|
|
1748
|
-
if (manifest.signature) {
|
|
1749
|
-
console.log("\u{1F510} Verifying signature...");
|
|
1750
|
-
console.log("\u26A0\uFE0F Signature verification not implemented in CLI yet");
|
|
1751
|
-
}
|
|
1752
|
-
console.log("\u2705 Manifest verification passed!");
|
|
1753
|
-
console.log(` ID: ${manifest.id}`);
|
|
1754
|
-
console.log(` Name: ${manifest.name}`);
|
|
1755
|
-
console.log(` Version: ${manifest.version}`);
|
|
1756
|
-
console.log(` Chains: ${manifest.chains.join(", ")}`);
|
|
1757
|
-
console.log(` Artifact: ${manifest.artifact.uri}`);
|
|
1758
|
-
if (manifest.provides?.length > 0) {
|
|
1759
|
-
console.log(` Provides: ${manifest.provides.join(", ")}`);
|
|
1760
|
-
}
|
|
1761
|
-
if (manifest.requires?.length > 0) {
|
|
1762
|
-
console.log(` Requires: ${manifest.requires.join(", ")}`);
|
|
1763
|
-
}
|
|
1764
1565
|
} catch (error) {
|
|
1765
|
-
|
|
1766
|
-
console.error("\u274C Failed to verify manifest:", errorMessage);
|
|
1566
|
+
console.error("\u274C Error:", error);
|
|
1767
1567
|
process.exit(1);
|
|
1768
1568
|
}
|
|
1769
1569
|
});
|
|
1770
1570
|
}
|
|
1571
|
+
async function extractManifest(bundlePath) {
|
|
1572
|
+
let manifestContent = null;
|
|
1573
|
+
await tar.t({
|
|
1574
|
+
file: bundlePath,
|
|
1575
|
+
onentry: (entry) => {
|
|
1576
|
+
if (entry.path === "manifest.json") ;
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
const extractDir = path6.join(path6.dirname(bundlePath), `.temp-${Date.now()}`);
|
|
1580
|
+
if (!fs3.existsSync(extractDir)) {
|
|
1581
|
+
fs3.mkdirSync(extractDir);
|
|
1582
|
+
}
|
|
1583
|
+
try {
|
|
1584
|
+
await tar.x({
|
|
1585
|
+
file: bundlePath,
|
|
1586
|
+
cwd: extractDir,
|
|
1587
|
+
filter: (path7) => path7 === "manifest.json"
|
|
1588
|
+
});
|
|
1589
|
+
const manifestPath = path6.join(extractDir, "manifest.json");
|
|
1590
|
+
if (fs3.existsSync(manifestPath)) {
|
|
1591
|
+
const content = fs3.readFileSync(manifestPath, "utf8");
|
|
1592
|
+
manifestContent = content;
|
|
1593
|
+
}
|
|
1594
|
+
} catch {
|
|
1595
|
+
} finally {
|
|
1596
|
+
if (fs3.existsSync(extractDir)) {
|
|
1597
|
+
fs3.rmSync(extractDir, { recursive: true, force: true });
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
if (manifestContent) {
|
|
1601
|
+
try {
|
|
1602
|
+
return JSON.parse(manifestContent);
|
|
1603
|
+
} catch {
|
|
1604
|
+
console.error("Failed to parse manifest JSON");
|
|
1605
|
+
return null;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return null;
|
|
1609
|
+
}
|
|
1771
1610
|
|
|
1772
1611
|
// src/index.ts
|
|
1773
1612
|
var program = new Command();
|
|
1774
1613
|
program.name("calimero-registry").description(
|
|
1775
1614
|
"Calimero Network App Registry CLI - Command-line interface for the App Registry"
|
|
1776
1615
|
).version("1.0.0");
|
|
1616
|
+
program.addHelpText(
|
|
1617
|
+
"after",
|
|
1618
|
+
`
|
|
1619
|
+
Examples:
|
|
1620
|
+
$ calimero-registry apps list
|
|
1621
|
+
$ calimero-registry apps create --file manifest.json
|
|
1622
|
+
$ calimero-registry local start
|
|
1623
|
+
$ calimero-registry health --local
|
|
1624
|
+
|
|
1625
|
+
For more information, visit: https://github.com/calimero-network/app-registry
|
|
1626
|
+
`
|
|
1627
|
+
);
|
|
1777
1628
|
program.option("-u, --url <url>", "Registry API URL", "http://localhost:8082");
|
|
1778
1629
|
program.option(
|
|
1779
1630
|
"-t, --timeout <timeout>",
|
|
@@ -1787,7 +1638,7 @@ program.addCommand(attestationsCommand);
|
|
|
1787
1638
|
program.addCommand(healthCommand);
|
|
1788
1639
|
program.addCommand(ipfsCommand);
|
|
1789
1640
|
program.addCommand(localCommand);
|
|
1790
|
-
program.addCommand(
|
|
1641
|
+
program.addCommand(bundleCommand);
|
|
1791
1642
|
program.exitOverride();
|
|
1792
1643
|
try {
|
|
1793
1644
|
program.parse();
|