@calimero-network/registry-cli 1.22.2 → 1.22.4
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 +167 -72
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,10 +5,9 @@ import { createRequire } from 'module';
|
|
|
5
5
|
import ora4 from 'ora';
|
|
6
6
|
import axios from 'axios';
|
|
7
7
|
import fs7, { existsSync, writeFileSync } from 'fs';
|
|
8
|
-
import
|
|
8
|
+
import path6 from 'path';
|
|
9
9
|
import os2 from 'os';
|
|
10
10
|
import fastify from 'fastify';
|
|
11
|
-
import cors from '@fastify/cors';
|
|
12
11
|
import crypto2 from 'crypto';
|
|
13
12
|
import * as tar from 'tar';
|
|
14
13
|
|
|
@@ -16,11 +15,11 @@ var DEFAULT_REGISTRY_URL = "https://apps.calimero.network";
|
|
|
16
15
|
function defaultConfig() {
|
|
17
16
|
return { registry: { url: DEFAULT_REGISTRY_URL } };
|
|
18
17
|
}
|
|
19
|
-
var RemoteConfig = class {
|
|
18
|
+
var RemoteConfig = class _RemoteConfig {
|
|
20
19
|
configPath;
|
|
21
20
|
config;
|
|
22
21
|
constructor() {
|
|
23
|
-
this.configPath =
|
|
22
|
+
this.configPath = path6.join(
|
|
24
23
|
os2.homedir(),
|
|
25
24
|
".calimero-registry",
|
|
26
25
|
"remote-config.json"
|
|
@@ -47,7 +46,7 @@ var RemoteConfig = class {
|
|
|
47
46
|
return defaults;
|
|
48
47
|
}
|
|
49
48
|
saveConfig() {
|
|
50
|
-
const configDir =
|
|
49
|
+
const configDir = path6.dirname(this.configPath);
|
|
51
50
|
if (!fs7.existsSync(configDir)) {
|
|
52
51
|
fs7.mkdirSync(configDir, { recursive: true });
|
|
53
52
|
}
|
|
@@ -58,7 +57,13 @@ var RemoteConfig = class {
|
|
|
58
57
|
...this.config.registry.apiKey ? { apiKey: this.config.registry.apiKey } : {}
|
|
59
58
|
}
|
|
60
59
|
};
|
|
61
|
-
fs7.writeFileSync(this.configPath, JSON.stringify(configToSave, null, 2)
|
|
60
|
+
fs7.writeFileSync(this.configPath, JSON.stringify(configToSave, null, 2), {
|
|
61
|
+
mode: 384
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
fs7.chmodSync(this.configPath, 384);
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
62
67
|
}
|
|
63
68
|
/**
|
|
64
69
|
* Get registry URL with priority:
|
|
@@ -73,9 +78,30 @@ var RemoteConfig = class {
|
|
|
73
78
|
* Set registry URL
|
|
74
79
|
*/
|
|
75
80
|
setRegistryUrl(url) {
|
|
81
|
+
if (_RemoteConfig.isInsecureRemoteUrl(url)) {
|
|
82
|
+
console.warn(
|
|
83
|
+
"\u26A0\uFE0F Registry URL uses http:// on a non-local host. Your API key would be sent in cleartext \u2014 prefer https:// for remote registries."
|
|
84
|
+
);
|
|
85
|
+
}
|
|
76
86
|
this.config.registry.url = url;
|
|
77
87
|
this.saveConfig();
|
|
78
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* True when `url` is http:// to a host other than localhost/loopback, i.e. a
|
|
91
|
+
* Bearer token would traverse the network in cleartext.
|
|
92
|
+
*/
|
|
93
|
+
static isInsecureRemoteUrl(url) {
|
|
94
|
+
let parsed;
|
|
95
|
+
try {
|
|
96
|
+
parsed = new URL(url);
|
|
97
|
+
} catch {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (parsed.protocol !== "http:") return false;
|
|
101
|
+
const host = parsed.hostname.toLowerCase();
|
|
102
|
+
const isLocal = host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".localhost");
|
|
103
|
+
return !isLocal;
|
|
104
|
+
}
|
|
79
105
|
/**
|
|
80
106
|
* Get API key with priority:
|
|
81
107
|
* 1. Environment variable CALIMERO_API_KEY
|
|
@@ -128,20 +154,20 @@ var RemoteConfig = class {
|
|
|
128
154
|
function defaultConfig2() {
|
|
129
155
|
return {
|
|
130
156
|
server: {
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
// host.docker.internal
|
|
135
|
-
host: "
|
|
157
|
+
// Hardening: default to loopback so the dev server is not reachable from
|
|
158
|
+
// the LAN or a browsing website. Docker users who need container access
|
|
159
|
+
// opt in explicitly with `local start --host 0.0.0.0`; publicHost stays
|
|
160
|
+
// host.docker.internal for the URLs baked into manifests in that case.
|
|
161
|
+
host: "127.0.0.1",
|
|
136
162
|
port: 8082,
|
|
137
163
|
publicHost: "host.docker.internal"
|
|
138
164
|
},
|
|
139
165
|
data: {
|
|
140
|
-
dir:
|
|
141
|
-
artifactsDir:
|
|
166
|
+
dir: path6.join(os2.homedir(), ".calimero-registry", "data"),
|
|
167
|
+
artifactsDir: path6.join(os2.homedir(), ".calimero-registry", "artifacts")
|
|
142
168
|
},
|
|
143
169
|
artifacts: {
|
|
144
|
-
storageDir:
|
|
170
|
+
storageDir: path6.join(os2.homedir(), ".calimero-registry", "artifacts"),
|
|
145
171
|
serveLocal: true,
|
|
146
172
|
copyArtifacts: true,
|
|
147
173
|
maxFileSize: "100MB",
|
|
@@ -154,12 +180,12 @@ var LocalConfig = class {
|
|
|
154
180
|
dataDir;
|
|
155
181
|
config;
|
|
156
182
|
constructor() {
|
|
157
|
-
this.configPath =
|
|
183
|
+
this.configPath = path6.join(
|
|
158
184
|
os2.homedir(),
|
|
159
185
|
".calimero-registry",
|
|
160
186
|
"config.json"
|
|
161
187
|
);
|
|
162
|
-
this.dataDir =
|
|
188
|
+
this.dataDir = path6.join(os2.homedir(), ".calimero-registry");
|
|
163
189
|
this.config = this.loadConfig();
|
|
164
190
|
}
|
|
165
191
|
loadConfig() {
|
|
@@ -190,7 +216,7 @@ var LocalConfig = class {
|
|
|
190
216
|
return defaults;
|
|
191
217
|
}
|
|
192
218
|
saveConfig() {
|
|
193
|
-
const configDir =
|
|
219
|
+
const configDir = path6.dirname(this.configPath);
|
|
194
220
|
if (!fs7.existsSync(configDir)) {
|
|
195
221
|
fs7.mkdirSync(configDir, { recursive: true });
|
|
196
222
|
}
|
|
@@ -224,8 +250,8 @@ var LocalConfig = class {
|
|
|
224
250
|
}
|
|
225
251
|
setDataDir(dir) {
|
|
226
252
|
this.config.data.dir = dir;
|
|
227
|
-
this.config.data.artifactsDir =
|
|
228
|
-
this.config.artifacts.storageDir =
|
|
253
|
+
this.config.data.artifactsDir = path6.join(dir, "artifacts");
|
|
254
|
+
this.config.artifacts.storageDir = path6.join(dir, "artifacts");
|
|
229
255
|
this.saveConfig();
|
|
230
256
|
}
|
|
231
257
|
getArtifactsDir() {
|
|
@@ -249,8 +275,8 @@ var LocalConfig = class {
|
|
|
249
275
|
const dirs = [
|
|
250
276
|
this.getDataDir(),
|
|
251
277
|
this.getArtifactsDir(),
|
|
252
|
-
|
|
253
|
-
|
|
278
|
+
path6.join(this.getDataDir(), "backups"),
|
|
279
|
+
path6.join(this.getDataDir(), "logs")
|
|
254
280
|
];
|
|
255
281
|
dirs.forEach((dir) => {
|
|
256
282
|
if (!fs7.existsSync(dir)) {
|
|
@@ -390,12 +416,12 @@ var LocalDataStore = class {
|
|
|
390
416
|
artifactsFile;
|
|
391
417
|
constructor(config) {
|
|
392
418
|
this.config = config;
|
|
393
|
-
this.appsFile =
|
|
394
|
-
this.bundleManifestsFile =
|
|
419
|
+
this.appsFile = path6.join(config.getDataDir(), "apps.json");
|
|
420
|
+
this.bundleManifestsFile = path6.join(
|
|
395
421
|
config.getDataDir(),
|
|
396
422
|
"bundle_manifests.json"
|
|
397
423
|
);
|
|
398
|
-
this.artifactsFile =
|
|
424
|
+
this.artifactsFile = path6.join(config.getDataDir(), "artifacts.json");
|
|
399
425
|
this.data = {
|
|
400
426
|
apps: /* @__PURE__ */ new Map(),
|
|
401
427
|
bundleManifests: /* @__PURE__ */ new Map(),
|
|
@@ -516,12 +542,12 @@ var LocalDataStore = class {
|
|
|
516
542
|
// Backup and restore
|
|
517
543
|
async backup(outputPath) {
|
|
518
544
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
519
|
-
const backupPath = outputPath ||
|
|
545
|
+
const backupPath = outputPath || path6.join(
|
|
520
546
|
this.config.getDataDir(),
|
|
521
547
|
"backups",
|
|
522
548
|
`backup-${timestamp}.json`
|
|
523
549
|
);
|
|
524
|
-
const backupDir =
|
|
550
|
+
const backupDir = path6.dirname(backupPath);
|
|
525
551
|
if (!fs7.existsSync(backupDir)) {
|
|
526
552
|
fs7.mkdirSync(backupDir, { recursive: true });
|
|
527
553
|
}
|
|
@@ -613,16 +639,30 @@ var LocalArtifactServer = class {
|
|
|
613
639
|
fs7.mkdirSync(this.artifactsDir, { recursive: true });
|
|
614
640
|
}
|
|
615
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* Hardening: build a path under artifactsDir from untrusted segments (request
|
|
644
|
+
* params, or bundle-provided package/version/filename) and guarantee it stays
|
|
645
|
+
* inside artifactsDir. A `..` or absolute segment would otherwise let a
|
|
646
|
+
* request read or write outside the artifact store (path traversal).
|
|
647
|
+
*/
|
|
648
|
+
resolveArtifactPath(...segments) {
|
|
649
|
+
const base = path6.resolve(this.artifactsDir);
|
|
650
|
+
const resolved = path6.resolve(base, ...segments);
|
|
651
|
+
if (resolved !== base && !resolved.startsWith(base + path6.sep)) {
|
|
652
|
+
throw new Error("Invalid artifact path: escapes artifacts directory");
|
|
653
|
+
}
|
|
654
|
+
return resolved;
|
|
655
|
+
}
|
|
616
656
|
// Copy artifact to local storage
|
|
617
657
|
async copyArtifactToLocal(sourcePath, appId, version2, filename) {
|
|
618
658
|
if (!fs7.existsSync(sourcePath)) {
|
|
619
659
|
throw new Error(`Source file not found: ${sourcePath}`);
|
|
620
660
|
}
|
|
621
|
-
const
|
|
661
|
+
const targetPath = this.resolveArtifactPath(appId, version2, filename);
|
|
662
|
+
const appVersionDir = path6.dirname(targetPath);
|
|
622
663
|
if (!fs7.existsSync(appVersionDir)) {
|
|
623
664
|
fs7.mkdirSync(appVersionDir, { recursive: true });
|
|
624
665
|
}
|
|
625
|
-
const targetPath = path5.join(appVersionDir, filename);
|
|
626
666
|
fs7.copyFileSync(sourcePath, targetPath);
|
|
627
667
|
const fileHash = await this.calculateFileHash(targetPath);
|
|
628
668
|
this.dataStore.setArtifactPath(fileHash, targetPath);
|
|
@@ -630,7 +670,7 @@ var LocalArtifactServer = class {
|
|
|
630
670
|
}
|
|
631
671
|
// Serve artifact by app ID, version, and filename
|
|
632
672
|
async serveArtifact(appId, version2, filename) {
|
|
633
|
-
const artifactPath =
|
|
673
|
+
const artifactPath = this.resolveArtifactPath(appId, version2, filename);
|
|
634
674
|
if (!fs7.existsSync(artifactPath)) {
|
|
635
675
|
throw new Error(`Artifact not found: ${artifactPath}`);
|
|
636
676
|
}
|
|
@@ -679,7 +719,7 @@ var LocalArtifactServer = class {
|
|
|
679
719
|
let files = [];
|
|
680
720
|
const items = fs7.readdirSync(dir);
|
|
681
721
|
for (const item of items) {
|
|
682
|
-
const fullPath =
|
|
722
|
+
const fullPath = path6.join(dir, item);
|
|
683
723
|
const stat = fs7.statSync(fullPath);
|
|
684
724
|
if (stat.isDirectory()) {
|
|
685
725
|
files = files.concat(getAllFiles(fullPath));
|
|
@@ -708,7 +748,7 @@ var LocalArtifactServer = class {
|
|
|
708
748
|
let files = [];
|
|
709
749
|
const items = fs7.readdirSync(dir);
|
|
710
750
|
for (const item of items) {
|
|
711
|
-
const fullPath =
|
|
751
|
+
const fullPath = path6.join(dir, item);
|
|
712
752
|
const stat = fs7.statSync(fullPath);
|
|
713
753
|
if (stat.isDirectory()) {
|
|
714
754
|
files = files.concat(getAllFiles(fullPath));
|
|
@@ -759,9 +799,27 @@ var LocalRegistryServer = class {
|
|
|
759
799
|
level: "info"
|
|
760
800
|
}
|
|
761
801
|
});
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
802
|
+
const allowedHosts = /* @__PURE__ */ new Set();
|
|
803
|
+
for (const name of [
|
|
804
|
+
"localhost",
|
|
805
|
+
"127.0.0.1",
|
|
806
|
+
"[::1]",
|
|
807
|
+
this.config.getPublicHost(),
|
|
808
|
+
host === "0.0.0.0" || host === "::" ? "" : host
|
|
809
|
+
]) {
|
|
810
|
+
if (!name) continue;
|
|
811
|
+
allowedHosts.add(name);
|
|
812
|
+
allowedHosts.add(`${name}:${serverPort}`);
|
|
813
|
+
}
|
|
814
|
+
this.server.addHook("onRequest", async (request, reply) => {
|
|
815
|
+
const hostHeader = (request.headers.host ?? "").toLowerCase();
|
|
816
|
+
if (!allowedHosts.has(hostHeader)) {
|
|
817
|
+
return reply.code(403).send({
|
|
818
|
+
statusCode: 403,
|
|
819
|
+
error: "Forbidden",
|
|
820
|
+
message: "Invalid Host header"
|
|
821
|
+
});
|
|
822
|
+
}
|
|
765
823
|
});
|
|
766
824
|
await this.registerRoutes();
|
|
767
825
|
await this.server.listen({ port: serverPort, host });
|
|
@@ -805,6 +863,20 @@ var LocalRegistryServer = class {
|
|
|
805
863
|
async seed() {
|
|
806
864
|
await this.dataStore.seed();
|
|
807
865
|
}
|
|
866
|
+
/**
|
|
867
|
+
* Hardening: resolve a caller-supplied backup path relative to the CLI data
|
|
868
|
+
* dir and verify it stays inside it. Rejects absolute paths and `..` segments
|
|
869
|
+
* that would escape, so the HTTP backup/restore routes cannot read or write
|
|
870
|
+
* arbitrary filesystem locations.
|
|
871
|
+
*/
|
|
872
|
+
confineToDataDir(candidate) {
|
|
873
|
+
const dataDir = path6.resolve(this.config.getDataDir());
|
|
874
|
+
const resolved = path6.resolve(dataDir, candidate);
|
|
875
|
+
if (resolved !== dataDir && !resolved.startsWith(dataDir + path6.sep)) {
|
|
876
|
+
throw new Error("Path escapes data directory");
|
|
877
|
+
}
|
|
878
|
+
return resolved;
|
|
879
|
+
}
|
|
808
880
|
async registerRoutes() {
|
|
809
881
|
if (!this.server) {
|
|
810
882
|
throw new Error("Server not initialized");
|
|
@@ -868,14 +940,37 @@ var LocalRegistryServer = class {
|
|
|
868
940
|
await this.reset();
|
|
869
941
|
return { message: "Local registry data reset successfully" };
|
|
870
942
|
});
|
|
871
|
-
this.server.
|
|
943
|
+
this.server.post("/local/backup", async (request, reply) => {
|
|
872
944
|
const query = request.query;
|
|
873
|
-
|
|
945
|
+
let output;
|
|
946
|
+
if (query.output) {
|
|
947
|
+
try {
|
|
948
|
+
output = this.confineToDataDir(query.output);
|
|
949
|
+
} catch {
|
|
950
|
+
return reply.code(400).send({
|
|
951
|
+
error: "invalid_path",
|
|
952
|
+
message: "output escapes data dir"
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
const backupPath = await this.backup(output);
|
|
874
957
|
return { backupPath };
|
|
875
958
|
});
|
|
876
|
-
this.server.post("/local/restore", async (request) => {
|
|
877
|
-
const { backupPath } = request.body;
|
|
878
|
-
|
|
959
|
+
this.server.post("/local/restore", async (request, reply) => {
|
|
960
|
+
const { backupPath } = request.body ?? {};
|
|
961
|
+
if (!backupPath) {
|
|
962
|
+
return reply.code(400).send({ error: "invalid_path", message: "backupPath is required" });
|
|
963
|
+
}
|
|
964
|
+
let resolved;
|
|
965
|
+
try {
|
|
966
|
+
resolved = this.confineToDataDir(backupPath);
|
|
967
|
+
} catch {
|
|
968
|
+
return reply.code(400).send({
|
|
969
|
+
error: "invalid_path",
|
|
970
|
+
message: "backupPath escapes data dir"
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
await this.restore(resolved);
|
|
879
974
|
return { message: "Data restored successfully" };
|
|
880
975
|
});
|
|
881
976
|
this.server.post("/local/seed", async () => {
|
|
@@ -948,7 +1043,7 @@ var localCommand = new Command("local").description(
|
|
|
948
1043
|
"Manage local registry for development"
|
|
949
1044
|
);
|
|
950
1045
|
localCommand.addCommand(
|
|
951
|
-
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", "
|
|
1046
|
+
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", "127.0.0.1").option(
|
|
952
1047
|
"--public-host <host>",
|
|
953
1048
|
"Public host exposed in manifest artifact URLs",
|
|
954
1049
|
"host.docker.internal"
|
|
@@ -1315,7 +1410,7 @@ Note:
|
|
|
1315
1410
|
try {
|
|
1316
1411
|
let manifestConfig = {};
|
|
1317
1412
|
if (options.manifest) {
|
|
1318
|
-
const manifestPath =
|
|
1413
|
+
const manifestPath = path6.resolve(options.manifest);
|
|
1319
1414
|
if (!fs7.existsSync(manifestPath)) {
|
|
1320
1415
|
console.error(`\u274C Manifest file not found: ${options.manifest}`);
|
|
1321
1416
|
process.exit(1);
|
|
@@ -1392,12 +1487,12 @@ Note:
|
|
|
1392
1487
|
);
|
|
1393
1488
|
process.exit(1);
|
|
1394
1489
|
}
|
|
1395
|
-
const wasmPath =
|
|
1490
|
+
const wasmPath = path6.resolve(wasmFile);
|
|
1396
1491
|
if (!fs7.existsSync(wasmPath)) {
|
|
1397
1492
|
console.error(`\u274C WASM file not found: ${wasmFile}`);
|
|
1398
1493
|
process.exit(1);
|
|
1399
1494
|
}
|
|
1400
|
-
console.log(`\u{1F4E6} Creating MPK bundle from: ${
|
|
1495
|
+
console.log(`\u{1F4E6} Creating MPK bundle from: ${path6.basename(wasmPath)}`);
|
|
1401
1496
|
const wasmContent = fs7.readFileSync(wasmPath);
|
|
1402
1497
|
const wasmSize = wasmContent.length;
|
|
1403
1498
|
const wasmHash = crypto2.createHash("sha256").update(wasmContent).digest("hex");
|
|
@@ -1407,10 +1502,10 @@ Note:
|
|
|
1407
1502
|
const abiPathFromManifest = typeof manifestConfig.abi === "string" ? manifestConfig.abi : null;
|
|
1408
1503
|
const abiFilePath = abiPathFromOption || abiPathFromManifest;
|
|
1409
1504
|
if (abiFilePath) {
|
|
1410
|
-
const abiResolvedPath = abiPathFromOption ?
|
|
1411
|
-
|
|
1505
|
+
const abiResolvedPath = abiPathFromOption ? path6.resolve(abiFilePath) : options.manifest && abiPathFromManifest ? path6.resolve(
|
|
1506
|
+
path6.dirname(path6.resolve(options.manifest)),
|
|
1412
1507
|
abiFilePath
|
|
1413
|
-
) :
|
|
1508
|
+
) : path6.resolve(abiFilePath);
|
|
1414
1509
|
if (!fs7.existsSync(abiResolvedPath)) {
|
|
1415
1510
|
console.error(`\u274C ABI file not found: ${abiFilePath}`);
|
|
1416
1511
|
console.error(` Resolved path: ${abiResolvedPath}`);
|
|
@@ -1426,7 +1521,7 @@ Note:
|
|
|
1426
1521
|
size: abiContent.length
|
|
1427
1522
|
};
|
|
1428
1523
|
console.log(
|
|
1429
|
-
` Including ABI file: ${
|
|
1524
|
+
` Including ABI file: ${path6.basename(abiResolvedPath)}`
|
|
1430
1525
|
);
|
|
1431
1526
|
} catch (error) {
|
|
1432
1527
|
console.error(
|
|
@@ -1450,7 +1545,7 @@ Note:
|
|
|
1450
1545
|
const manifestServices = Array.isArray(
|
|
1451
1546
|
manifestConfig.services
|
|
1452
1547
|
) ? manifestConfig.services : [];
|
|
1453
|
-
const manifestDir = options.manifest ?
|
|
1548
|
+
const manifestDir = options.manifest ? path6.dirname(path6.resolve(options.manifest)) : process.cwd();
|
|
1454
1549
|
const allServiceSources = [
|
|
1455
1550
|
...cliServices.map((src) => ({ src, baseDir: process.cwd() })),
|
|
1456
1551
|
...manifestServices.map((src) => ({ src, baseDir: manifestDir }))
|
|
@@ -1484,7 +1579,7 @@ Note:
|
|
|
1484
1579
|
);
|
|
1485
1580
|
process.exit(1);
|
|
1486
1581
|
}
|
|
1487
|
-
const svcWasmPath =
|
|
1582
|
+
const svcWasmPath = path6.resolve(baseDir, src.wasm);
|
|
1488
1583
|
if (!fs7.existsSync(svcWasmPath)) {
|
|
1489
1584
|
console.error(
|
|
1490
1585
|
`\u274C Service "${src.name}" WASM not found: ${src.wasm}`
|
|
@@ -1508,7 +1603,7 @@ Note:
|
|
|
1508
1603
|
content: svcWasmContent
|
|
1509
1604
|
});
|
|
1510
1605
|
if (src.abi) {
|
|
1511
|
-
const svcAbiPath =
|
|
1606
|
+
const svcAbiPath = path6.resolve(baseDir, src.abi);
|
|
1512
1607
|
if (!fs7.existsSync(svcAbiPath)) {
|
|
1513
1608
|
console.error(
|
|
1514
1609
|
`\u274C Service "${src.name}" ABI not found: ${src.abi}`
|
|
@@ -1542,7 +1637,7 @@ Note:
|
|
|
1542
1637
|
}
|
|
1543
1638
|
serviceArtifacts.push(entry);
|
|
1544
1639
|
console.log(
|
|
1545
|
-
` Including service: ${src.name} (${
|
|
1640
|
+
` Including service: ${src.name} (${path6.basename(svcWasmPath)})`
|
|
1546
1641
|
);
|
|
1547
1642
|
}
|
|
1548
1643
|
const metadata = {
|
|
@@ -1575,29 +1670,29 @@ Note:
|
|
|
1575
1670
|
links: Object.keys(links).length > 0 ? links : void 0,
|
|
1576
1671
|
signature: void 0
|
|
1577
1672
|
};
|
|
1578
|
-
let outputDir = finalOutput ?
|
|
1673
|
+
let outputDir = finalOutput ? path6.resolve(finalOutput) : path6.join(process.cwd(), finalPackage, finalVersion);
|
|
1579
1674
|
if (!fs7.existsSync(outputDir)) {
|
|
1580
1675
|
fs7.mkdirSync(outputDir, { recursive: true });
|
|
1581
1676
|
}
|
|
1582
1677
|
fs7.writeFileSync(
|
|
1583
|
-
|
|
1678
|
+
path6.join(outputDir, "manifest.json"),
|
|
1584
1679
|
JSON.stringify(manifest, null, 2)
|
|
1585
1680
|
);
|
|
1586
|
-
fs7.writeFileSync(
|
|
1681
|
+
fs7.writeFileSync(path6.join(outputDir, "app.wasm"), wasmContent);
|
|
1587
1682
|
if (abiContent) {
|
|
1588
|
-
fs7.writeFileSync(
|
|
1683
|
+
fs7.writeFileSync(path6.join(outputDir, "abi.json"), abiContent);
|
|
1589
1684
|
}
|
|
1590
|
-
const servicesDir =
|
|
1685
|
+
const servicesDir = path6.join(outputDir, "services");
|
|
1591
1686
|
for (const { relPath, content } of serviceFilesToWrite) {
|
|
1592
|
-
const dest =
|
|
1593
|
-
const rel =
|
|
1594
|
-
if (rel.startsWith("..") ||
|
|
1687
|
+
const dest = path6.join(outputDir, relPath);
|
|
1688
|
+
const rel = path6.relative(servicesDir, dest);
|
|
1689
|
+
if (rel.startsWith("..") || path6.isAbsolute(rel)) {
|
|
1595
1690
|
console.error(
|
|
1596
1691
|
`\u274C Refusing to write service file outside services/: ${relPath}`
|
|
1597
1692
|
);
|
|
1598
1693
|
process.exit(1);
|
|
1599
1694
|
}
|
|
1600
|
-
fs7.mkdirSync(
|
|
1695
|
+
fs7.mkdirSync(path6.dirname(dest), { recursive: true });
|
|
1601
1696
|
fs7.writeFileSync(dest, content);
|
|
1602
1697
|
}
|
|
1603
1698
|
console.log(`\u2705 Bundle files written to: ${outputDir}`);
|
|
@@ -1615,7 +1710,7 @@ Note:
|
|
|
1615
1710
|
console.log("");
|
|
1616
1711
|
console.log("Next steps:");
|
|
1617
1712
|
console.log(
|
|
1618
|
-
` 1. Sign the manifest: mero-sign sign ${
|
|
1713
|
+
` 1. Sign the manifest: mero-sign sign ${path6.join(outputDir, "manifest.json")} --key key.json`
|
|
1619
1714
|
);
|
|
1620
1715
|
console.log(
|
|
1621
1716
|
` 2. Push the bundle: calimero-registry bundle push ${outputDir} --remote`
|
|
@@ -1684,15 +1779,15 @@ Note:
|
|
|
1684
1779
|
);
|
|
1685
1780
|
process.exit(1);
|
|
1686
1781
|
}
|
|
1687
|
-
const fullPath =
|
|
1782
|
+
const fullPath = path6.resolve(bundleFile);
|
|
1688
1783
|
if (!fs7.existsSync(fullPath)) {
|
|
1689
1784
|
console.error(`\u274C File not found: ${bundleFile}`);
|
|
1690
1785
|
process.exit(1);
|
|
1691
1786
|
}
|
|
1692
1787
|
mpkPath = fullPath;
|
|
1693
1788
|
if (fs7.statSync(fullPath).isDirectory()) {
|
|
1694
|
-
const manifestInDir =
|
|
1695
|
-
const wasmInDir =
|
|
1789
|
+
const manifestInDir = path6.join(fullPath, "manifest.json");
|
|
1790
|
+
const wasmInDir = path6.join(fullPath, "app.wasm");
|
|
1696
1791
|
if (!fs7.existsSync(manifestInDir)) {
|
|
1697
1792
|
console.error(
|
|
1698
1793
|
`\u274C manifest.json not found in directory: ${fullPath}`
|
|
@@ -1703,7 +1798,7 @@ Note:
|
|
|
1703
1798
|
console.error(`\u274C app.wasm not found in directory: ${fullPath}`);
|
|
1704
1799
|
process.exit(1);
|
|
1705
1800
|
}
|
|
1706
|
-
tempMpk =
|
|
1801
|
+
tempMpk = path6.join(fullPath, `.temp-push-${Date.now()}.mpk`);
|
|
1707
1802
|
let dirManifest;
|
|
1708
1803
|
try {
|
|
1709
1804
|
dirManifest = JSON.parse(
|
|
@@ -1740,7 +1835,7 @@ Note:
|
|
|
1740
1835
|
process.exit(1);
|
|
1741
1836
|
}
|
|
1742
1837
|
for (const f of archiveFiles) {
|
|
1743
|
-
if (!fs7.existsSync(
|
|
1838
|
+
if (!fs7.existsSync(path6.join(fullPath, f))) {
|
|
1744
1839
|
console.error(
|
|
1745
1840
|
`\u274C Bundle file referenced by manifest not found: ${f}`
|
|
1746
1841
|
);
|
|
@@ -1756,7 +1851,7 @@ Note:
|
|
|
1756
1851
|
`\u{1F4E6} Packed directory into temporary bundle (${archiveFiles.length} files)`
|
|
1757
1852
|
);
|
|
1758
1853
|
}
|
|
1759
|
-
console.log(`\u{1F4E6} Processing bundle: ${
|
|
1854
|
+
console.log(`\u{1F4E6} Processing bundle: ${path6.basename(mpkPath)}`);
|
|
1760
1855
|
const manifest = await extractManifest(mpkPath);
|
|
1761
1856
|
if (!manifest) {
|
|
1762
1857
|
console.error("\u274C manifest.json not found in bundle");
|
|
@@ -1872,7 +1967,7 @@ Examples:
|
|
|
1872
1967
|
};
|
|
1873
1968
|
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
|
1874
1969
|
if (options.manifest) {
|
|
1875
|
-
const manifestPath =
|
|
1970
|
+
const manifestPath = path6.resolve(options.manifest);
|
|
1876
1971
|
if (!fs7.existsSync(manifestPath)) {
|
|
1877
1972
|
console.error(`\u274C Manifest file not found: ${options.manifest}`);
|
|
1878
1973
|
process.exit(1);
|
|
@@ -1964,7 +2059,7 @@ Examples:
|
|
|
1964
2059
|
if (!manifest.links) manifest.links = {};
|
|
1965
2060
|
manifest.links.docs = options.docs;
|
|
1966
2061
|
}
|
|
1967
|
-
const outputPath =
|
|
2062
|
+
const outputPath = path6.resolve(options.output || "manifest.json");
|
|
1968
2063
|
fs7.writeFileSync(outputPath, JSON.stringify(manifest, null, 2));
|
|
1969
2064
|
console.log(`\u2705 Wrote modified manifest to: ${outputPath}`);
|
|
1970
2065
|
console.log("");
|
|
@@ -2140,7 +2235,7 @@ async function extractManifest(bundlePath) {
|
|
|
2140
2235
|
if (entry.path === "manifest.json") ;
|
|
2141
2236
|
}
|
|
2142
2237
|
});
|
|
2143
|
-
const extractDir =
|
|
2238
|
+
const extractDir = path6.join(path6.dirname(bundlePath), `.temp-${Date.now()}`);
|
|
2144
2239
|
if (!fs7.existsSync(extractDir)) {
|
|
2145
2240
|
fs7.mkdirSync(extractDir);
|
|
2146
2241
|
}
|
|
@@ -2148,9 +2243,9 @@ async function extractManifest(bundlePath) {
|
|
|
2148
2243
|
await tar.x({
|
|
2149
2244
|
file: bundlePath,
|
|
2150
2245
|
cwd: extractDir,
|
|
2151
|
-
filter: (
|
|
2246
|
+
filter: (path7) => path7 === "manifest.json"
|
|
2152
2247
|
});
|
|
2153
|
-
const manifestPath =
|
|
2248
|
+
const manifestPath = path6.join(extractDir, "manifest.json");
|
|
2154
2249
|
if (fs7.existsSync(manifestPath)) {
|
|
2155
2250
|
const content = fs7.readFileSync(manifestPath, "utf8");
|
|
2156
2251
|
manifestContent = content;
|