@calimero-network/registry-cli 1.1.0 → 1.3.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 +453 -47
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -10,6 +10,8 @@ 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
15
|
|
|
14
16
|
var LocalConfig = class {
|
|
15
17
|
constructor() {
|
|
@@ -227,7 +229,7 @@ var LocalDataStore = class {
|
|
|
227
229
|
getAppVersions(appId) {
|
|
228
230
|
const versions = [];
|
|
229
231
|
for (const [key, manifest] of this.data.manifests.entries()) {
|
|
230
|
-
if (
|
|
232
|
+
if (key.startsWith(`${appId}/`)) {
|
|
231
233
|
const semver = key.split("/").pop();
|
|
232
234
|
versions.push({
|
|
233
235
|
semver,
|
|
@@ -243,12 +245,8 @@ var LocalDataStore = class {
|
|
|
243
245
|
}
|
|
244
246
|
// Manifest management
|
|
245
247
|
getManifest(appId, semver) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return manifest;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return void 0;
|
|
248
|
+
const manifestKey = `${appId}/${semver}`;
|
|
249
|
+
return this.data.manifests.get(manifestKey);
|
|
252
250
|
}
|
|
253
251
|
setManifest(manifestKey, manifest) {
|
|
254
252
|
this.data.manifests.set(manifestKey, manifest);
|
|
@@ -370,7 +368,7 @@ var LocalDataStore = class {
|
|
|
370
368
|
this.setApp(appKey, app);
|
|
371
369
|
});
|
|
372
370
|
sampleManifests.forEach((manifest) => {
|
|
373
|
-
const manifestKey = `${manifest.app.
|
|
371
|
+
const manifestKey = `${manifest.app.app_id}/${manifest.version.semver}`;
|
|
374
372
|
this.setManifest(manifestKey, manifest);
|
|
375
373
|
});
|
|
376
374
|
this.saveData();
|
|
@@ -458,7 +456,7 @@ var LocalArtifactServer = class {
|
|
|
458
456
|
const filename = path.basename(artifact.path);
|
|
459
457
|
updatedArtifact.mirrors = [
|
|
460
458
|
this.getArtifactUrl(
|
|
461
|
-
manifest.app.id || manifest.app.name,
|
|
459
|
+
manifest.app.id || manifest.app.name?.replace(/\s+/g, "-").toLowerCase(),
|
|
462
460
|
manifest.version.semver,
|
|
463
461
|
filename
|
|
464
462
|
)
|
|
@@ -582,9 +580,9 @@ var LocalRegistryClient = class {
|
|
|
582
580
|
throw new Error("Invalid manifest structure");
|
|
583
581
|
}
|
|
584
582
|
const processedManifest = await this.processManifestArtifacts(manifest);
|
|
585
|
-
const manifestKey = `${manifest.app.
|
|
583
|
+
const manifestKey = `${manifest.app.app_id}/${manifest.version.semver}`;
|
|
586
584
|
this.dataStore.setManifest(manifestKey, processedManifest);
|
|
587
|
-
const appKey =
|
|
585
|
+
const appKey = manifest.app.app_id;
|
|
588
586
|
const appSummary = {
|
|
589
587
|
name: manifest.app.name,
|
|
590
588
|
developer_pubkey: manifest.app.developer_pubkey,
|
|
@@ -612,7 +610,7 @@ var LocalRegistryClient = class {
|
|
|
612
610
|
const processedArtifact = { ...artifact };
|
|
613
611
|
if (artifact.path && fs3.existsSync(artifact.path)) {
|
|
614
612
|
const filename = path.basename(artifact.path);
|
|
615
|
-
const appId = manifest.app.id || manifest.app.name;
|
|
613
|
+
const appId = manifest.app.id || manifest.app.name?.replace(/\s+/g, "-").toLowerCase();
|
|
616
614
|
try {
|
|
617
615
|
await this.artifactServer.copyArtifactToLocal(
|
|
618
616
|
artifact.path,
|
|
@@ -1076,35 +1074,65 @@ var LocalRegistryServer = class {
|
|
|
1076
1074
|
});
|
|
1077
1075
|
this.server.get("/apps", async (request) => {
|
|
1078
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
|
+
}
|
|
1079
1094
|
return this.dataStore.getApps(query);
|
|
1080
1095
|
});
|
|
1081
1096
|
this.server.get("/apps/:appId", async (request) => {
|
|
1082
1097
|
const { appId } = request.params;
|
|
1083
|
-
|
|
1098
|
+
const versions = this.dataStore.getAppVersions(appId);
|
|
1099
|
+
return {
|
|
1100
|
+
id: appId,
|
|
1101
|
+
versions
|
|
1102
|
+
};
|
|
1084
1103
|
});
|
|
1085
1104
|
this.server.get("/apps/:appId/:semver", async (request) => {
|
|
1086
1105
|
const { appId, semver } = request.params;
|
|
1087
1106
|
const manifest = this.dataStore.getManifest(appId, semver);
|
|
1088
1107
|
if (!manifest) {
|
|
1089
|
-
|
|
1108
|
+
return {
|
|
1109
|
+
statusCode: 404,
|
|
1110
|
+
error: "Not Found",
|
|
1111
|
+
message: "Manifest not found"
|
|
1112
|
+
};
|
|
1090
1113
|
}
|
|
1091
1114
|
return this.artifactServer.updateManifestArtifacts(manifest);
|
|
1092
1115
|
});
|
|
1093
1116
|
this.server.post("/apps", async (request) => {
|
|
1094
1117
|
const manifest = request.body;
|
|
1095
1118
|
if (!this.validateManifest(manifest)) {
|
|
1096
|
-
|
|
1119
|
+
return {
|
|
1120
|
+
statusCode: 400,
|
|
1121
|
+
error: "Bad Request",
|
|
1122
|
+
message: "Invalid manifest structure"
|
|
1123
|
+
};
|
|
1097
1124
|
}
|
|
1098
1125
|
const processedManifest = await this.processManifestArtifacts(manifest);
|
|
1099
|
-
const manifestKey = `${manifest.app.
|
|
1126
|
+
const manifestKey = `${manifest.app.app_id}/${manifest.version.semver}`;
|
|
1100
1127
|
this.dataStore.setManifest(manifestKey, processedManifest);
|
|
1101
|
-
const appKey =
|
|
1128
|
+
const appKey = manifest.app.app_id;
|
|
1102
1129
|
const appSummary = {
|
|
1103
1130
|
name: manifest.app.name,
|
|
1104
1131
|
developer_pubkey: manifest.app.developer_pubkey,
|
|
1105
1132
|
latest_version: manifest.version.semver,
|
|
1106
1133
|
latest_cid: manifest.artifacts[0]?.cid || "",
|
|
1107
|
-
alias: manifest.app.
|
|
1134
|
+
alias: manifest.app.name
|
|
1135
|
+
// Use name as alias
|
|
1108
1136
|
};
|
|
1109
1137
|
this.dataStore.setApp(appKey, appSummary);
|
|
1110
1138
|
return {
|
|
@@ -1130,7 +1158,11 @@ var LocalRegistryServer = class {
|
|
|
1130
1158
|
);
|
|
1131
1159
|
return artifactData;
|
|
1132
1160
|
} catch {
|
|
1133
|
-
|
|
1161
|
+
return {
|
|
1162
|
+
statusCode: 404,
|
|
1163
|
+
error: "Not Found",
|
|
1164
|
+
message: "Artifact not found"
|
|
1165
|
+
};
|
|
1134
1166
|
}
|
|
1135
1167
|
}
|
|
1136
1168
|
);
|
|
@@ -1141,7 +1173,11 @@ var LocalRegistryServer = class {
|
|
|
1141
1173
|
reply.header("Content-Type", "application/octet-stream");
|
|
1142
1174
|
return artifactData;
|
|
1143
1175
|
} catch {
|
|
1144
|
-
|
|
1176
|
+
return {
|
|
1177
|
+
statusCode: 404,
|
|
1178
|
+
error: "Not Found",
|
|
1179
|
+
message: "Artifact not found"
|
|
1180
|
+
};
|
|
1145
1181
|
}
|
|
1146
1182
|
});
|
|
1147
1183
|
this.server.get("/local/status", async () => {
|
|
@@ -1165,41 +1201,151 @@ var LocalRegistryServer = class {
|
|
|
1165
1201
|
await this.seed();
|
|
1166
1202
|
return { message: "Sample data seeded successfully" };
|
|
1167
1203
|
});
|
|
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
|
+
};
|
|
1282
|
+
}
|
|
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
|
+
});
|
|
1292
|
+
}
|
|
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"
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
return {
|
|
1311
|
+
plan: [{ action: "install", id: root.id, version: root.version }],
|
|
1312
|
+
satisfies: [],
|
|
1313
|
+
missing: []
|
|
1314
|
+
};
|
|
1315
|
+
});
|
|
1168
1316
|
}
|
|
1169
1317
|
validateManifest(manifest) {
|
|
1170
|
-
return !!(manifest.manifest_version && manifest.app && manifest.app.name && manifest.app.developer_pubkey && manifest.version && manifest.version.semver && manifest.artifacts && manifest.artifacts.length > 0);
|
|
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);
|
|
1171
1319
|
}
|
|
1172
1320
|
async processManifestArtifacts(manifest) {
|
|
1173
1321
|
const processedManifest = { ...manifest };
|
|
1174
|
-
if (
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
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(
|
|
1184
1339
|
appId,
|
|
1185
|
-
manifest.version
|
|
1340
|
+
manifest.version,
|
|
1186
1341
|
filename
|
|
1187
|
-
)
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
manifest.version.semver,
|
|
1192
|
-
filename
|
|
1193
|
-
)
|
|
1194
|
-
];
|
|
1195
|
-
delete processedArtifact.path;
|
|
1196
|
-
} catch (error) {
|
|
1197
|
-
console.warn(`Failed to copy artifact ${artifact.path}:`, error);
|
|
1198
|
-
}
|
|
1342
|
+
)
|
|
1343
|
+
};
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
console.warn(`Failed to copy artifact ${filePath}:`, error);
|
|
1199
1346
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1203
1349
|
}
|
|
1204
1350
|
return processedManifest;
|
|
1205
1351
|
}
|
|
@@ -1384,6 +1530,265 @@ localCommand.addCommand(
|
|
|
1384
1530
|
}
|
|
1385
1531
|
})
|
|
1386
1532
|
);
|
|
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) => {
|
|
1537
|
+
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);
|
|
1543
|
+
}
|
|
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);
|
|
1549
|
+
}
|
|
1550
|
+
if (!manifest.id || !manifest.name || !manifest.version) {
|
|
1551
|
+
console.error("\u274C Missing required fields: id, name, version");
|
|
1552
|
+
process.exit(1);
|
|
1553
|
+
}
|
|
1554
|
+
const baseUrl = useLocal ? "http://localhost:8082" : "http://localhost:8080";
|
|
1555
|
+
console.log(
|
|
1556
|
+
`\u{1F4E4} Submitting manifest: ${manifest.id}@${manifest.version}`
|
|
1557
|
+
);
|
|
1558
|
+
console.log(` Name: ${manifest.name}`);
|
|
1559
|
+
console.log(` Chains: ${manifest.chains?.join(", ")}`);
|
|
1560
|
+
console.log(
|
|
1561
|
+
` Provides: ${manifest.provides?.join(", ") || "none"}`
|
|
1562
|
+
);
|
|
1563
|
+
console.log(
|
|
1564
|
+
` Requires: ${manifest.requires?.join(", ") || "none"}`
|
|
1565
|
+
);
|
|
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
|
+
process.exit(1);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
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();
|
|
1610
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
1611
|
+
} 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(", ")}`);
|
|
1627
|
+
}
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1630
|
+
console.error("\u274C Failed to get manifest:", errorMessage);
|
|
1631
|
+
process.exit(1);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
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) => {
|
|
1639
|
+
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)}`
|
|
1647
|
+
);
|
|
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...");
|
|
1673
|
+
console.log(
|
|
1674
|
+
"Note: Use --search <query> to search for specific apps"
|
|
1675
|
+
);
|
|
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);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
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}`);
|
|
1715
|
+
}
|
|
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(", ")}`);
|
|
1722
|
+
}
|
|
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);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
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) => {
|
|
1733
|
+
try {
|
|
1734
|
+
if (!fs3.existsSync(manifestFile)) {
|
|
1735
|
+
console.error(`\u274C Manifest file not found: ${manifestFile}`);
|
|
1736
|
+
process.exit(1);
|
|
1737
|
+
}
|
|
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);
|
|
1746
|
+
}
|
|
1747
|
+
const requiredFields = ["id", "name", "version", "chains", "artifact"];
|
|
1748
|
+
const missingFields = requiredFields.filter(
|
|
1749
|
+
(field) => !manifest[field]
|
|
1750
|
+
);
|
|
1751
|
+
if (missingFields.length > 0) {
|
|
1752
|
+
console.error(
|
|
1753
|
+
`\u274C Missing required fields: ${missingFields.join(", ")}`
|
|
1754
|
+
);
|
|
1755
|
+
process.exit(1);
|
|
1756
|
+
}
|
|
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
|
+
}
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
1787
|
+
console.error("\u274C Failed to verify manifest:", errorMessage);
|
|
1788
|
+
process.exit(1);
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1387
1792
|
|
|
1388
1793
|
// src/index.ts
|
|
1389
1794
|
var program = new Command();
|
|
@@ -1403,6 +1808,7 @@ program.addCommand(attestationsCommand);
|
|
|
1403
1808
|
program.addCommand(healthCommand);
|
|
1404
1809
|
program.addCommand(ipfsCommand);
|
|
1405
1810
|
program.addCommand(localCommand);
|
|
1811
|
+
program.addCommand(v1Command);
|
|
1406
1812
|
program.exitOverride();
|
|
1407
1813
|
try {
|
|
1408
1814
|
program.parse();
|