@calimero-network/registry-cli 1.24.0 → 1.26.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/README.md +1 -1
- package/dist/index.js +179 -156
- package/dist/index.js.map +1 -1
- package/package.json +3 -6
package/dist/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import chalk4 from 'chalk';
|
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
5
|
import ora4 from 'ora';
|
|
6
6
|
import axios from 'axios';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import fs8, { existsSync, writeFileSync } from 'fs';
|
|
8
|
+
import path7 from 'path';
|
|
9
9
|
import os2 from 'os';
|
|
10
10
|
import fastify from 'fastify';
|
|
11
11
|
import crypto2 from 'crypto';
|
|
@@ -19,7 +19,7 @@ var RemoteConfig = class _RemoteConfig {
|
|
|
19
19
|
configPath;
|
|
20
20
|
config;
|
|
21
21
|
constructor() {
|
|
22
|
-
this.configPath =
|
|
22
|
+
this.configPath = path7.join(
|
|
23
23
|
os2.homedir(),
|
|
24
24
|
".calimero-registry",
|
|
25
25
|
"remote-config.json"
|
|
@@ -28,10 +28,10 @@ var RemoteConfig = class _RemoteConfig {
|
|
|
28
28
|
}
|
|
29
29
|
loadConfig() {
|
|
30
30
|
const defaults = defaultConfig();
|
|
31
|
-
if (
|
|
31
|
+
if (fs8.existsSync(this.configPath)) {
|
|
32
32
|
try {
|
|
33
33
|
const existingConfig = JSON.parse(
|
|
34
|
-
|
|
34
|
+
fs8.readFileSync(this.configPath, "utf8")
|
|
35
35
|
);
|
|
36
36
|
return {
|
|
37
37
|
registry: {
|
|
@@ -46,9 +46,9 @@ var RemoteConfig = class _RemoteConfig {
|
|
|
46
46
|
return defaults;
|
|
47
47
|
}
|
|
48
48
|
saveConfig() {
|
|
49
|
-
const configDir =
|
|
50
|
-
if (!
|
|
51
|
-
|
|
49
|
+
const configDir = path7.dirname(this.configPath);
|
|
50
|
+
if (!fs8.existsSync(configDir)) {
|
|
51
|
+
fs8.mkdirSync(configDir, { recursive: true });
|
|
52
52
|
}
|
|
53
53
|
const configToSave = {
|
|
54
54
|
registry: {
|
|
@@ -57,11 +57,11 @@ var RemoteConfig = class _RemoteConfig {
|
|
|
57
57
|
...this.config.registry.apiKey ? { apiKey: this.config.registry.apiKey } : {}
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
|
-
|
|
60
|
+
fs8.writeFileSync(this.configPath, JSON.stringify(configToSave, null, 2), {
|
|
61
61
|
mode: 384
|
|
62
62
|
});
|
|
63
63
|
try {
|
|
64
|
-
|
|
64
|
+
fs8.chmodSync(this.configPath, 384);
|
|
65
65
|
} catch {
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -177,11 +177,11 @@ function defaultConfig2() {
|
|
|
177
177
|
publicHost: "host.docker.internal"
|
|
178
178
|
},
|
|
179
179
|
data: {
|
|
180
|
-
dir:
|
|
181
|
-
artifactsDir:
|
|
180
|
+
dir: path7.join(os2.homedir(), ".calimero-registry", "data"),
|
|
181
|
+
artifactsDir: path7.join(os2.homedir(), ".calimero-registry", "artifacts")
|
|
182
182
|
},
|
|
183
183
|
artifacts: {
|
|
184
|
-
storageDir:
|
|
184
|
+
storageDir: path7.join(os2.homedir(), ".calimero-registry", "artifacts"),
|
|
185
185
|
serveLocal: true,
|
|
186
186
|
copyArtifacts: true,
|
|
187
187
|
maxFileSize: "100MB",
|
|
@@ -194,20 +194,20 @@ var LocalConfig = class {
|
|
|
194
194
|
dataDir;
|
|
195
195
|
config;
|
|
196
196
|
constructor() {
|
|
197
|
-
this.configPath =
|
|
197
|
+
this.configPath = path7.join(
|
|
198
198
|
os2.homedir(),
|
|
199
199
|
".calimero-registry",
|
|
200
200
|
"config.json"
|
|
201
201
|
);
|
|
202
|
-
this.dataDir =
|
|
202
|
+
this.dataDir = path7.join(os2.homedir(), ".calimero-registry");
|
|
203
203
|
this.config = this.loadConfig();
|
|
204
204
|
}
|
|
205
205
|
loadConfig() {
|
|
206
206
|
const defaults = defaultConfig2();
|
|
207
|
-
if (
|
|
207
|
+
if (fs8.existsSync(this.configPath)) {
|
|
208
208
|
try {
|
|
209
209
|
const existingConfig = JSON.parse(
|
|
210
|
-
|
|
210
|
+
fs8.readFileSync(this.configPath, "utf8")
|
|
211
211
|
);
|
|
212
212
|
return {
|
|
213
213
|
server: {
|
|
@@ -230,11 +230,11 @@ var LocalConfig = class {
|
|
|
230
230
|
return defaults;
|
|
231
231
|
}
|
|
232
232
|
saveConfig() {
|
|
233
|
-
const configDir =
|
|
234
|
-
if (!
|
|
235
|
-
|
|
233
|
+
const configDir = path7.dirname(this.configPath);
|
|
234
|
+
if (!fs8.existsSync(configDir)) {
|
|
235
|
+
fs8.mkdirSync(configDir, { recursive: true });
|
|
236
236
|
}
|
|
237
|
-
|
|
237
|
+
fs8.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
|
|
238
238
|
}
|
|
239
239
|
// Server configuration
|
|
240
240
|
getPort() {
|
|
@@ -264,8 +264,8 @@ var LocalConfig = class {
|
|
|
264
264
|
}
|
|
265
265
|
setDataDir(dir) {
|
|
266
266
|
this.config.data.dir = dir;
|
|
267
|
-
this.config.data.artifactsDir =
|
|
268
|
-
this.config.artifacts.storageDir =
|
|
267
|
+
this.config.data.artifactsDir = path7.join(dir, "artifacts");
|
|
268
|
+
this.config.artifacts.storageDir = path7.join(dir, "artifacts");
|
|
269
269
|
this.saveConfig();
|
|
270
270
|
}
|
|
271
271
|
getArtifactsDir() {
|
|
@@ -289,12 +289,12 @@ var LocalConfig = class {
|
|
|
289
289
|
const dirs = [
|
|
290
290
|
this.getDataDir(),
|
|
291
291
|
this.getArtifactsDir(),
|
|
292
|
-
|
|
293
|
-
|
|
292
|
+
path7.join(this.getDataDir(), "backups"),
|
|
293
|
+
path7.join(this.getDataDir(), "logs")
|
|
294
294
|
];
|
|
295
295
|
dirs.forEach((dir) => {
|
|
296
|
-
if (!
|
|
297
|
-
|
|
296
|
+
if (!fs8.existsSync(dir)) {
|
|
297
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
298
298
|
}
|
|
299
299
|
});
|
|
300
300
|
}
|
|
@@ -430,12 +430,12 @@ var LocalDataStore = class {
|
|
|
430
430
|
artifactsFile;
|
|
431
431
|
constructor(config) {
|
|
432
432
|
this.config = config;
|
|
433
|
-
this.appsFile =
|
|
434
|
-
this.bundleManifestsFile =
|
|
433
|
+
this.appsFile = path7.join(config.getDataDir(), "apps.json");
|
|
434
|
+
this.bundleManifestsFile = path7.join(
|
|
435
435
|
config.getDataDir(),
|
|
436
436
|
"bundle_manifests.json"
|
|
437
437
|
);
|
|
438
|
-
this.artifactsFile =
|
|
438
|
+
this.artifactsFile = path7.join(config.getDataDir(), "artifacts.json");
|
|
439
439
|
this.data = {
|
|
440
440
|
apps: /* @__PURE__ */ new Map(),
|
|
441
441
|
bundleManifests: /* @__PURE__ */ new Map(),
|
|
@@ -445,21 +445,21 @@ var LocalDataStore = class {
|
|
|
445
445
|
}
|
|
446
446
|
loadData() {
|
|
447
447
|
try {
|
|
448
|
-
if (
|
|
449
|
-
const appsData = JSON.parse(
|
|
448
|
+
if (fs8.existsSync(this.appsFile)) {
|
|
449
|
+
const appsData = JSON.parse(fs8.readFileSync(this.appsFile, "utf8"));
|
|
450
450
|
this.data.apps = new Map(Object.entries(appsData));
|
|
451
451
|
}
|
|
452
|
-
if (
|
|
452
|
+
if (fs8.existsSync(this.bundleManifestsFile)) {
|
|
453
453
|
const bundleManifestsData = JSON.parse(
|
|
454
|
-
|
|
454
|
+
fs8.readFileSync(this.bundleManifestsFile, "utf8")
|
|
455
455
|
);
|
|
456
456
|
this.data.bundleManifests = new Map(
|
|
457
457
|
Object.entries(bundleManifestsData)
|
|
458
458
|
);
|
|
459
459
|
}
|
|
460
|
-
if (
|
|
460
|
+
if (fs8.existsSync(this.artifactsFile)) {
|
|
461
461
|
const artifactsData = JSON.parse(
|
|
462
|
-
|
|
462
|
+
fs8.readFileSync(this.artifactsFile, "utf8")
|
|
463
463
|
);
|
|
464
464
|
this.data.artifacts = new Map(Object.entries(artifactsData));
|
|
465
465
|
}
|
|
@@ -471,14 +471,14 @@ var LocalDataStore = class {
|
|
|
471
471
|
try {
|
|
472
472
|
this.config.ensureDirectories();
|
|
473
473
|
const appsObj = Object.fromEntries(this.data.apps);
|
|
474
|
-
|
|
474
|
+
fs8.writeFileSync(this.appsFile, JSON.stringify(appsObj, null, 2));
|
|
475
475
|
const bundleManifestsObj = Object.fromEntries(this.data.bundleManifests);
|
|
476
|
-
|
|
476
|
+
fs8.writeFileSync(
|
|
477
477
|
this.bundleManifestsFile,
|
|
478
478
|
JSON.stringify(bundleManifestsObj, null, 2)
|
|
479
479
|
);
|
|
480
480
|
const artifactsObj = Object.fromEntries(this.data.artifacts);
|
|
481
|
-
|
|
481
|
+
fs8.writeFileSync(
|
|
482
482
|
this.artifactsFile,
|
|
483
483
|
JSON.stringify(artifactsObj, null, 2)
|
|
484
484
|
);
|
|
@@ -556,14 +556,14 @@ var LocalDataStore = class {
|
|
|
556
556
|
// Backup and restore
|
|
557
557
|
async backup(outputPath) {
|
|
558
558
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
559
|
-
const backupPath = outputPath ||
|
|
559
|
+
const backupPath = outputPath || path7.join(
|
|
560
560
|
this.config.getDataDir(),
|
|
561
561
|
"backups",
|
|
562
562
|
`backup-${timestamp}.json`
|
|
563
563
|
);
|
|
564
|
-
const backupDir =
|
|
565
|
-
if (!
|
|
566
|
-
|
|
564
|
+
const backupDir = path7.dirname(backupPath);
|
|
565
|
+
if (!fs8.existsSync(backupDir)) {
|
|
566
|
+
fs8.mkdirSync(backupDir, { recursive: true });
|
|
567
567
|
}
|
|
568
568
|
const backupData = {
|
|
569
569
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -571,14 +571,14 @@ var LocalDataStore = class {
|
|
|
571
571
|
bundleManifests: Object.fromEntries(this.data.bundleManifests),
|
|
572
572
|
artifacts: Object.fromEntries(this.data.artifacts)
|
|
573
573
|
};
|
|
574
|
-
|
|
574
|
+
fs8.writeFileSync(backupPath, JSON.stringify(backupData, null, 2));
|
|
575
575
|
return backupPath;
|
|
576
576
|
}
|
|
577
577
|
async restore(backupPath) {
|
|
578
|
-
if (!
|
|
578
|
+
if (!fs8.existsSync(backupPath)) {
|
|
579
579
|
throw new Error(`Backup file not found: ${backupPath}`);
|
|
580
580
|
}
|
|
581
|
-
const backupData = JSON.parse(
|
|
581
|
+
const backupData = JSON.parse(fs8.readFileSync(backupPath, "utf8"));
|
|
582
582
|
this.data.apps = new Map(Object.entries(backupData.apps || {}));
|
|
583
583
|
this.data.bundleManifests = new Map(
|
|
584
584
|
Object.entries(backupData.bundleManifests || {})
|
|
@@ -649,8 +649,8 @@ var LocalArtifactServer = class {
|
|
|
649
649
|
this.ensureArtifactsDir();
|
|
650
650
|
}
|
|
651
651
|
ensureArtifactsDir() {
|
|
652
|
-
if (!
|
|
653
|
-
|
|
652
|
+
if (!fs8.existsSync(this.artifactsDir)) {
|
|
653
|
+
fs8.mkdirSync(this.artifactsDir, { recursive: true });
|
|
654
654
|
}
|
|
655
655
|
}
|
|
656
656
|
/**
|
|
@@ -660,24 +660,24 @@ var LocalArtifactServer = class {
|
|
|
660
660
|
* request read or write outside the artifact store (path traversal).
|
|
661
661
|
*/
|
|
662
662
|
resolveArtifactPath(...segments) {
|
|
663
|
-
const base =
|
|
664
|
-
const resolved =
|
|
665
|
-
if (resolved !== base && !resolved.startsWith(base +
|
|
663
|
+
const base = path7.resolve(this.artifactsDir);
|
|
664
|
+
const resolved = path7.resolve(base, ...segments);
|
|
665
|
+
if (resolved !== base && !resolved.startsWith(base + path7.sep)) {
|
|
666
666
|
throw new Error("Invalid artifact path: escapes artifacts directory");
|
|
667
667
|
}
|
|
668
668
|
return resolved;
|
|
669
669
|
}
|
|
670
670
|
// Copy artifact to local storage
|
|
671
671
|
async copyArtifactToLocal(sourcePath, appId, version2, filename) {
|
|
672
|
-
if (!
|
|
672
|
+
if (!fs8.existsSync(sourcePath)) {
|
|
673
673
|
throw new Error(`Source file not found: ${sourcePath}`);
|
|
674
674
|
}
|
|
675
675
|
const targetPath = this.resolveArtifactPath(appId, version2, filename);
|
|
676
|
-
const appVersionDir =
|
|
677
|
-
if (!
|
|
678
|
-
|
|
676
|
+
const appVersionDir = path7.dirname(targetPath);
|
|
677
|
+
if (!fs8.existsSync(appVersionDir)) {
|
|
678
|
+
fs8.mkdirSync(appVersionDir, { recursive: true });
|
|
679
679
|
}
|
|
680
|
-
|
|
680
|
+
fs8.copyFileSync(sourcePath, targetPath);
|
|
681
681
|
const fileHash = await this.calculateFileHash(targetPath);
|
|
682
682
|
this.dataStore.setArtifactPath(fileHash, targetPath);
|
|
683
683
|
return targetPath;
|
|
@@ -685,26 +685,26 @@ var LocalArtifactServer = class {
|
|
|
685
685
|
// Serve artifact by app ID, version, and filename
|
|
686
686
|
async serveArtifact(appId, version2, filename) {
|
|
687
687
|
const artifactPath = this.resolveArtifactPath(appId, version2, filename);
|
|
688
|
-
if (!
|
|
688
|
+
if (!fs8.existsSync(artifactPath)) {
|
|
689
689
|
throw new Error(`Artifact not found: ${artifactPath}`);
|
|
690
690
|
}
|
|
691
|
-
return
|
|
691
|
+
return fs8.readFileSync(artifactPath);
|
|
692
692
|
}
|
|
693
693
|
// Serve artifact by hash
|
|
694
694
|
async serveByHash(hash) {
|
|
695
695
|
const artifactPath = this.dataStore.getArtifactPath(hash);
|
|
696
|
-
if (!artifactPath || !
|
|
696
|
+
if (!artifactPath || !fs8.existsSync(artifactPath)) {
|
|
697
697
|
throw new Error(`Artifact not found for hash: ${hash}`);
|
|
698
698
|
}
|
|
699
|
-
return
|
|
699
|
+
return fs8.readFileSync(artifactPath);
|
|
700
700
|
}
|
|
701
701
|
// Validate artifact file
|
|
702
702
|
async validateArtifact(filePath) {
|
|
703
|
-
if (!
|
|
703
|
+
if (!fs8.existsSync(filePath)) {
|
|
704
704
|
throw new Error(`File not found: ${filePath}`);
|
|
705
705
|
}
|
|
706
|
-
const stats =
|
|
707
|
-
const fileBuffer =
|
|
706
|
+
const stats = fs8.statSync(filePath);
|
|
707
|
+
const fileBuffer = fs8.readFileSync(filePath);
|
|
708
708
|
const sha256 = crypto2.createHash("sha256").update(fileBuffer).digest("hex");
|
|
709
709
|
return {
|
|
710
710
|
size: stats.size,
|
|
@@ -713,7 +713,7 @@ var LocalArtifactServer = class {
|
|
|
713
713
|
}
|
|
714
714
|
// Calculate file hash
|
|
715
715
|
async calculateFileHash(filePath) {
|
|
716
|
-
const fileBuffer =
|
|
716
|
+
const fileBuffer = fs8.readFileSync(filePath);
|
|
717
717
|
return crypto2.createHash("sha256").update(fileBuffer).digest("hex");
|
|
718
718
|
}
|
|
719
719
|
// Get artifact URL for local serving
|
|
@@ -731,10 +731,10 @@ var LocalArtifactServer = class {
|
|
|
731
731
|
const now = Date.now();
|
|
732
732
|
const getAllFiles = (dir) => {
|
|
733
733
|
let files = [];
|
|
734
|
-
const items =
|
|
734
|
+
const items = fs8.readdirSync(dir);
|
|
735
735
|
for (const item of items) {
|
|
736
|
-
const fullPath =
|
|
737
|
-
const stat =
|
|
736
|
+
const fullPath = path7.join(dir, item);
|
|
737
|
+
const stat = fs8.statSync(fullPath);
|
|
738
738
|
if (stat.isDirectory()) {
|
|
739
739
|
files = files.concat(getAllFiles(fullPath));
|
|
740
740
|
} else {
|
|
@@ -745,10 +745,10 @@ var LocalArtifactServer = class {
|
|
|
745
745
|
};
|
|
746
746
|
const allFiles = getAllFiles(this.artifactsDir);
|
|
747
747
|
for (const file of allFiles) {
|
|
748
|
-
const stats =
|
|
748
|
+
const stats = fs8.statSync(file);
|
|
749
749
|
const age = now - stats.mtime.getTime();
|
|
750
750
|
if (age > maxAge) {
|
|
751
|
-
|
|
751
|
+
fs8.unlinkSync(file);
|
|
752
752
|
console.log(`Cleaned up old artifact: ${file}`);
|
|
753
753
|
}
|
|
754
754
|
}
|
|
@@ -760,10 +760,10 @@ var LocalArtifactServer = class {
|
|
|
760
760
|
let oldestFile = null;
|
|
761
761
|
const getAllFiles = (dir) => {
|
|
762
762
|
let files = [];
|
|
763
|
-
const items =
|
|
763
|
+
const items = fs8.readdirSync(dir);
|
|
764
764
|
for (const item of items) {
|
|
765
|
-
const fullPath =
|
|
766
|
-
const stat =
|
|
765
|
+
const fullPath = path7.join(dir, item);
|
|
766
|
+
const stat = fs8.statSync(fullPath);
|
|
767
767
|
if (stat.isDirectory()) {
|
|
768
768
|
files = files.concat(getAllFiles(fullPath));
|
|
769
769
|
} else {
|
|
@@ -772,10 +772,10 @@ var LocalArtifactServer = class {
|
|
|
772
772
|
}
|
|
773
773
|
return files;
|
|
774
774
|
};
|
|
775
|
-
if (
|
|
775
|
+
if (fs8.existsSync(this.artifactsDir)) {
|
|
776
776
|
const allFiles = getAllFiles(this.artifactsDir);
|
|
777
777
|
for (const file of allFiles) {
|
|
778
|
-
const stats =
|
|
778
|
+
const stats = fs8.statSync(file);
|
|
779
779
|
totalFiles++;
|
|
780
780
|
totalSize += stats.size;
|
|
781
781
|
if (!oldestFile || stats.mtime < oldestFile) {
|
|
@@ -913,8 +913,8 @@ var LocalRegistryServer = class {
|
|
|
913
913
|
async reset() {
|
|
914
914
|
this.dataStore.reset();
|
|
915
915
|
const artifactsDir = this.config.getArtifactsDir();
|
|
916
|
-
if (
|
|
917
|
-
|
|
916
|
+
if (fs8.existsSync(artifactsDir)) {
|
|
917
|
+
fs8.rmSync(artifactsDir, { recursive: true, force: true });
|
|
918
918
|
}
|
|
919
919
|
}
|
|
920
920
|
async backup(outputPath) {
|
|
@@ -933,9 +933,9 @@ var LocalRegistryServer = class {
|
|
|
933
933
|
* arbitrary filesystem locations.
|
|
934
934
|
*/
|
|
935
935
|
confineToDataDir(candidate) {
|
|
936
|
-
const dataDir =
|
|
937
|
-
const resolved =
|
|
938
|
-
if (resolved !== dataDir && !resolved.startsWith(dataDir +
|
|
936
|
+
const dataDir = path7.resolve(this.config.getDataDir());
|
|
937
|
+
const resolved = path7.resolve(dataDir, candidate);
|
|
938
|
+
if (resolved !== dataDir && !resolved.startsWith(dataDir + path7.sep)) {
|
|
939
939
|
throw new Error("Path escapes data directory");
|
|
940
940
|
}
|
|
941
941
|
return resolved;
|
|
@@ -1254,7 +1254,7 @@ localCommand.addCommand(
|
|
|
1254
1254
|
new Command("restore").description("Restore local registry data from backup").argument("<backup-file>", "Path to backup file").action(async (backupFile) => {
|
|
1255
1255
|
const spinner2 = ora4("Restoring from backup...").start();
|
|
1256
1256
|
try {
|
|
1257
|
-
if (!
|
|
1257
|
+
if (!fs8.existsSync(backupFile)) {
|
|
1258
1258
|
spinner2.fail("Backup file not found");
|
|
1259
1259
|
console.error(chalk4.red(`File not found: ${backupFile}`));
|
|
1260
1260
|
process.exit(1);
|
|
@@ -1298,8 +1298,6 @@ localCommand.addCommand(
|
|
|
1298
1298
|
}
|
|
1299
1299
|
})
|
|
1300
1300
|
);
|
|
1301
|
-
|
|
1302
|
-
// src/lib/services.ts
|
|
1303
1301
|
var SERVICE_NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
|
|
1304
1302
|
function validateServiceName(name) {
|
|
1305
1303
|
if (typeof name !== "string" || !SERVICE_NAME_RE.test(name)) {
|
|
@@ -1403,6 +1401,18 @@ function collectBundleFiles(manifest) {
|
|
|
1403
1401
|
files.forEach(assertSafeBundlePath);
|
|
1404
1402
|
return [...new Set(files)];
|
|
1405
1403
|
}
|
|
1404
|
+
function assertFilesInsideDir(dir, files) {
|
|
1405
|
+
const root = fs8.realpathSync(dir);
|
|
1406
|
+
for (const f of files) {
|
|
1407
|
+
const full = path7.join(dir, f);
|
|
1408
|
+
const inside = fs8.existsSync(full) && fs8.lstatSync(full).isFile() && fs8.realpathSync(full) === path7.join(root, f);
|
|
1409
|
+
if (!inside) {
|
|
1410
|
+
throw new Error(
|
|
1411
|
+
`Bundle file referenced by manifest is missing, not a regular file, or outside the bundle directory: ${f}`
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1406
1416
|
|
|
1407
1417
|
// src/commands/bundle.ts
|
|
1408
1418
|
var bundleCommand = new Command("bundle").description("Manage application bundles (V2)").addCommand(createCreateCommand()).addCommand(createPushCommand()).addCommand(createGetCommand()).addCommand(createEditCommand());
|
|
@@ -1473,13 +1483,13 @@ Note:
|
|
|
1473
1483
|
try {
|
|
1474
1484
|
let manifestConfig = {};
|
|
1475
1485
|
if (options.manifest) {
|
|
1476
|
-
const manifestPath =
|
|
1477
|
-
if (!
|
|
1486
|
+
const manifestPath = path7.resolve(options.manifest);
|
|
1487
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
1478
1488
|
console.error(`\u274C Manifest file not found: ${options.manifest}`);
|
|
1479
1489
|
process.exit(1);
|
|
1480
1490
|
}
|
|
1481
1491
|
try {
|
|
1482
|
-
const manifestContent =
|
|
1492
|
+
const manifestContent = fs8.readFileSync(manifestPath, "utf8");
|
|
1483
1493
|
const parsed = JSON.parse(manifestContent);
|
|
1484
1494
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1485
1495
|
console.error(`\u274C Invalid manifest file: ${options.manifest}`);
|
|
@@ -1550,13 +1560,13 @@ Note:
|
|
|
1550
1560
|
);
|
|
1551
1561
|
process.exit(1);
|
|
1552
1562
|
}
|
|
1553
|
-
const wasmPath =
|
|
1554
|
-
if (!
|
|
1563
|
+
const wasmPath = path7.resolve(wasmFile);
|
|
1564
|
+
if (!fs8.existsSync(wasmPath)) {
|
|
1555
1565
|
console.error(`\u274C WASM file not found: ${wasmFile}`);
|
|
1556
1566
|
process.exit(1);
|
|
1557
1567
|
}
|
|
1558
|
-
console.log(`\u{1F4E6} Creating MPK bundle from: ${
|
|
1559
|
-
const wasmContent =
|
|
1568
|
+
console.log(`\u{1F4E6} Creating MPK bundle from: ${path7.basename(wasmPath)}`);
|
|
1569
|
+
const wasmContent = fs8.readFileSync(wasmPath);
|
|
1560
1570
|
const wasmSize = wasmContent.length;
|
|
1561
1571
|
const wasmHash = crypto2.createHash("sha256").update(wasmContent).digest("hex");
|
|
1562
1572
|
let abiArtifact = void 0;
|
|
@@ -1565,17 +1575,17 @@ Note:
|
|
|
1565
1575
|
const abiPathFromManifest = typeof manifestConfig.abi === "string" ? manifestConfig.abi : null;
|
|
1566
1576
|
const abiFilePath = abiPathFromOption || abiPathFromManifest;
|
|
1567
1577
|
if (abiFilePath) {
|
|
1568
|
-
const abiResolvedPath = abiPathFromOption ?
|
|
1569
|
-
|
|
1578
|
+
const abiResolvedPath = abiPathFromOption ? path7.resolve(abiFilePath) : options.manifest && abiPathFromManifest ? path7.resolve(
|
|
1579
|
+
path7.dirname(path7.resolve(options.manifest)),
|
|
1570
1580
|
abiFilePath
|
|
1571
|
-
) :
|
|
1572
|
-
if (!
|
|
1581
|
+
) : path7.resolve(abiFilePath);
|
|
1582
|
+
if (!fs8.existsSync(abiResolvedPath)) {
|
|
1573
1583
|
console.error(`\u274C ABI file not found: ${abiFilePath}`);
|
|
1574
1584
|
console.error(` Resolved path: ${abiResolvedPath}`);
|
|
1575
1585
|
process.exit(1);
|
|
1576
1586
|
}
|
|
1577
1587
|
try {
|
|
1578
|
-
abiContent =
|
|
1588
|
+
abiContent = fs8.readFileSync(abiResolvedPath);
|
|
1579
1589
|
JSON.parse(abiContent.toString("utf8"));
|
|
1580
1590
|
const abiHash = crypto2.createHash("sha256").update(abiContent).digest("hex");
|
|
1581
1591
|
abiArtifact = {
|
|
@@ -1584,7 +1594,7 @@ Note:
|
|
|
1584
1594
|
size: abiContent.length
|
|
1585
1595
|
};
|
|
1586
1596
|
console.log(
|
|
1587
|
-
` Including ABI file: ${
|
|
1597
|
+
` Including ABI file: ${path7.basename(abiResolvedPath)}`
|
|
1588
1598
|
);
|
|
1589
1599
|
} catch (error) {
|
|
1590
1600
|
console.error(
|
|
@@ -1608,7 +1618,7 @@ Note:
|
|
|
1608
1618
|
const manifestServices = Array.isArray(
|
|
1609
1619
|
manifestConfig.services
|
|
1610
1620
|
) ? manifestConfig.services : [];
|
|
1611
|
-
const manifestDir = options.manifest ?
|
|
1621
|
+
const manifestDir = options.manifest ? path7.dirname(path7.resolve(options.manifest)) : process.cwd();
|
|
1612
1622
|
const allServiceSources = [
|
|
1613
1623
|
...cliServices.map((src) => ({ src, baseDir: process.cwd() })),
|
|
1614
1624
|
...manifestServices.map((src) => ({ src, baseDir: manifestDir }))
|
|
@@ -1642,15 +1652,15 @@ Note:
|
|
|
1642
1652
|
);
|
|
1643
1653
|
process.exit(1);
|
|
1644
1654
|
}
|
|
1645
|
-
const svcWasmPath =
|
|
1646
|
-
if (!
|
|
1655
|
+
const svcWasmPath = path7.resolve(baseDir, src.wasm);
|
|
1656
|
+
if (!fs8.existsSync(svcWasmPath)) {
|
|
1647
1657
|
console.error(
|
|
1648
1658
|
`\u274C Service "${src.name}" WASM not found: ${src.wasm}`
|
|
1649
1659
|
);
|
|
1650
1660
|
console.error(` Resolved path: ${svcWasmPath}`);
|
|
1651
1661
|
process.exit(1);
|
|
1652
1662
|
}
|
|
1653
|
-
const svcWasmContent =
|
|
1663
|
+
const svcWasmContent = fs8.readFileSync(svcWasmPath);
|
|
1654
1664
|
const svcWasmHash = crypto2.createHash("sha256").update(svcWasmContent).digest("hex");
|
|
1655
1665
|
const wasmArchivePath = serviceWasmPath(src.name);
|
|
1656
1666
|
const entry = {
|
|
@@ -1666,15 +1676,15 @@ Note:
|
|
|
1666
1676
|
content: svcWasmContent
|
|
1667
1677
|
});
|
|
1668
1678
|
if (src.abi) {
|
|
1669
|
-
const svcAbiPath =
|
|
1670
|
-
if (!
|
|
1679
|
+
const svcAbiPath = path7.resolve(baseDir, src.abi);
|
|
1680
|
+
if (!fs8.existsSync(svcAbiPath)) {
|
|
1671
1681
|
console.error(
|
|
1672
1682
|
`\u274C Service "${src.name}" ABI not found: ${src.abi}`
|
|
1673
1683
|
);
|
|
1674
1684
|
console.error(` Resolved path: ${svcAbiPath}`);
|
|
1675
1685
|
process.exit(1);
|
|
1676
1686
|
}
|
|
1677
|
-
const svcAbiContent =
|
|
1687
|
+
const svcAbiContent = fs8.readFileSync(svcAbiPath);
|
|
1678
1688
|
try {
|
|
1679
1689
|
JSON.parse(svcAbiContent.toString("utf8"));
|
|
1680
1690
|
} catch (error) {
|
|
@@ -1700,7 +1710,7 @@ Note:
|
|
|
1700
1710
|
}
|
|
1701
1711
|
serviceArtifacts.push(entry);
|
|
1702
1712
|
console.log(
|
|
1703
|
-
` Including service: ${src.name} (${
|
|
1713
|
+
` Including service: ${src.name} (${path7.basename(svcWasmPath)})`
|
|
1704
1714
|
);
|
|
1705
1715
|
}
|
|
1706
1716
|
const metadata = {
|
|
@@ -1733,30 +1743,30 @@ Note:
|
|
|
1733
1743
|
links: Object.keys(links).length > 0 ? links : void 0,
|
|
1734
1744
|
signature: void 0
|
|
1735
1745
|
};
|
|
1736
|
-
let outputDir = finalOutput ?
|
|
1737
|
-
if (!
|
|
1738
|
-
|
|
1746
|
+
let outputDir = finalOutput ? path7.resolve(finalOutput) : path7.join(process.cwd(), finalPackage, finalVersion);
|
|
1747
|
+
if (!fs8.existsSync(outputDir)) {
|
|
1748
|
+
fs8.mkdirSync(outputDir, { recursive: true });
|
|
1739
1749
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1750
|
+
fs8.writeFileSync(
|
|
1751
|
+
path7.join(outputDir, "manifest.json"),
|
|
1742
1752
|
JSON.stringify(manifest, null, 2)
|
|
1743
1753
|
);
|
|
1744
|
-
|
|
1754
|
+
fs8.writeFileSync(path7.join(outputDir, "app.wasm"), wasmContent);
|
|
1745
1755
|
if (abiContent) {
|
|
1746
|
-
|
|
1756
|
+
fs8.writeFileSync(path7.join(outputDir, "abi.json"), abiContent);
|
|
1747
1757
|
}
|
|
1748
|
-
const servicesDir =
|
|
1758
|
+
const servicesDir = path7.join(outputDir, "services");
|
|
1749
1759
|
for (const { relPath, content } of serviceFilesToWrite) {
|
|
1750
|
-
const dest =
|
|
1751
|
-
const rel =
|
|
1752
|
-
if (rel.startsWith("..") ||
|
|
1760
|
+
const dest = path7.join(outputDir, relPath);
|
|
1761
|
+
const rel = path7.relative(servicesDir, dest);
|
|
1762
|
+
if (rel.startsWith("..") || path7.isAbsolute(rel)) {
|
|
1753
1763
|
console.error(
|
|
1754
1764
|
`\u274C Refusing to write service file outside services/: ${relPath}`
|
|
1755
1765
|
);
|
|
1756
1766
|
process.exit(1);
|
|
1757
1767
|
}
|
|
1758
|
-
|
|
1759
|
-
|
|
1768
|
+
fs8.mkdirSync(path7.dirname(dest), { recursive: true });
|
|
1769
|
+
fs8.writeFileSync(dest, content);
|
|
1760
1770
|
}
|
|
1761
1771
|
console.log(`\u2705 Bundle files written to: ${outputDir}`);
|
|
1762
1772
|
console.log(` Package: ${finalPackage}`);
|
|
@@ -1773,7 +1783,7 @@ Note:
|
|
|
1773
1783
|
console.log("");
|
|
1774
1784
|
console.log("Next steps:");
|
|
1775
1785
|
console.log(
|
|
1776
|
-
` 1. Sign the manifest: mero-sign sign ${
|
|
1786
|
+
` 1. Sign the manifest: mero-sign sign ${path7.join(outputDir, "manifest.json")} --key key.json`
|
|
1777
1787
|
);
|
|
1778
1788
|
console.log(
|
|
1779
1789
|
` 2. Push the bundle: calimero-registry bundle push ${outputDir} --remote`
|
|
@@ -1806,7 +1816,7 @@ Examples:
|
|
|
1806
1816
|
Configuration:
|
|
1807
1817
|
Set defaults using the config command:
|
|
1808
1818
|
$ calimero-registry config set registry-url https://apps.calimero.network
|
|
1809
|
-
$ calimero-registry config set api-key
|
|
1819
|
+
$ calimero-registry config set api-key # paste the key when asked
|
|
1810
1820
|
|
|
1811
1821
|
Or use environment variables:
|
|
1812
1822
|
$ export CALIMERO_REGISTRY_URL=https://apps.calimero.network
|
|
@@ -1843,30 +1853,30 @@ Note:
|
|
|
1843
1853
|
);
|
|
1844
1854
|
process.exit(1);
|
|
1845
1855
|
}
|
|
1846
|
-
const fullPath =
|
|
1847
|
-
if (!
|
|
1856
|
+
const fullPath = path7.resolve(bundleFile);
|
|
1857
|
+
if (!fs8.existsSync(fullPath)) {
|
|
1848
1858
|
console.error(`\u274C File not found: ${bundleFile}`);
|
|
1849
1859
|
process.exit(1);
|
|
1850
1860
|
}
|
|
1851
1861
|
mpkPath = fullPath;
|
|
1852
|
-
if (
|
|
1853
|
-
const manifestInDir =
|
|
1854
|
-
const wasmInDir =
|
|
1855
|
-
if (!
|
|
1862
|
+
if (fs8.statSync(fullPath).isDirectory()) {
|
|
1863
|
+
const manifestInDir = path7.join(fullPath, "manifest.json");
|
|
1864
|
+
const wasmInDir = path7.join(fullPath, "app.wasm");
|
|
1865
|
+
if (!fs8.existsSync(manifestInDir)) {
|
|
1856
1866
|
console.error(
|
|
1857
1867
|
`\u274C manifest.json not found in directory: ${fullPath}`
|
|
1858
1868
|
);
|
|
1859
1869
|
process.exit(1);
|
|
1860
1870
|
}
|
|
1861
|
-
if (!
|
|
1871
|
+
if (!fs8.existsSync(wasmInDir)) {
|
|
1862
1872
|
console.error(`\u274C app.wasm not found in directory: ${fullPath}`);
|
|
1863
1873
|
process.exit(1);
|
|
1864
1874
|
}
|
|
1865
|
-
tempMpk =
|
|
1875
|
+
tempMpk = path7.join(fullPath, `.temp-push-${Date.now()}.mpk`);
|
|
1866
1876
|
let dirManifest;
|
|
1867
1877
|
try {
|
|
1868
1878
|
dirManifest = JSON.parse(
|
|
1869
|
-
|
|
1879
|
+
fs8.readFileSync(manifestInDir, "utf8")
|
|
1870
1880
|
);
|
|
1871
1881
|
} catch (e) {
|
|
1872
1882
|
console.error(
|
|
@@ -1898,13 +1908,11 @@ Note:
|
|
|
1898
1908
|
console.error(`\u274C ${e instanceof Error ? e.message : String(e)}`);
|
|
1899
1909
|
process.exit(1);
|
|
1900
1910
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
process.exit(1);
|
|
1907
|
-
}
|
|
1911
|
+
try {
|
|
1912
|
+
assertFilesInsideDir(fullPath, archiveFiles);
|
|
1913
|
+
} catch (e) {
|
|
1914
|
+
console.error(`${e instanceof Error ? e.message : String(e)}`);
|
|
1915
|
+
process.exit(1);
|
|
1908
1916
|
}
|
|
1909
1917
|
await tar.create(
|
|
1910
1918
|
{ gzip: true, file: tempMpk, cwd: fullPath },
|
|
@@ -1915,7 +1923,7 @@ Note:
|
|
|
1915
1923
|
`\u{1F4E6} Packed directory into temporary bundle (${archiveFiles.length} files)`
|
|
1916
1924
|
);
|
|
1917
1925
|
}
|
|
1918
|
-
console.log(`\u{1F4E6} Processing bundle: ${
|
|
1926
|
+
console.log(`\u{1F4E6} Processing bundle: ${path7.basename(mpkPath)}`);
|
|
1919
1927
|
const manifest = await extractManifest(mpkPath);
|
|
1920
1928
|
if (!manifest) {
|
|
1921
1929
|
console.error("\u274C manifest.json not found in bundle");
|
|
@@ -1963,8 +1971,8 @@ Note:
|
|
|
1963
1971
|
console.error("\u274C Failed to push bundle:", message);
|
|
1964
1972
|
process.exit(1);
|
|
1965
1973
|
} finally {
|
|
1966
|
-
if (tempMpk &&
|
|
1967
|
-
|
|
1974
|
+
if (tempMpk && fs8.existsSync(tempMpk)) {
|
|
1975
|
+
fs8.rmSync(tempMpk);
|
|
1968
1976
|
}
|
|
1969
1977
|
}
|
|
1970
1978
|
});
|
|
@@ -2034,12 +2042,12 @@ Examples:
|
|
|
2034
2042
|
};
|
|
2035
2043
|
if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
|
|
2036
2044
|
if (options.manifest) {
|
|
2037
|
-
const manifestPath =
|
|
2038
|
-
if (!
|
|
2045
|
+
const manifestPath = path7.resolve(options.manifest);
|
|
2046
|
+
if (!fs8.existsSync(manifestPath)) {
|
|
2039
2047
|
console.error(`\u274C Manifest file not found: ${options.manifest}`);
|
|
2040
2048
|
process.exit(1);
|
|
2041
2049
|
}
|
|
2042
|
-
const raw =
|
|
2050
|
+
const raw = fs8.readFileSync(manifestPath, "utf8");
|
|
2043
2051
|
let manifest2;
|
|
2044
2052
|
try {
|
|
2045
2053
|
manifest2 = JSON.parse(raw);
|
|
@@ -2126,8 +2134,8 @@ Examples:
|
|
|
2126
2134
|
if (!manifest.links) manifest.links = {};
|
|
2127
2135
|
manifest.links.docs = options.docs;
|
|
2128
2136
|
}
|
|
2129
|
-
const outputPath =
|
|
2130
|
-
|
|
2137
|
+
const outputPath = path7.resolve(options.output || "manifest.json");
|
|
2138
|
+
fs8.writeFileSync(outputPath, JSON.stringify(manifest, null, 2));
|
|
2131
2139
|
console.log(`\u2705 Wrote modified manifest to: ${outputPath}`);
|
|
2132
2140
|
console.log("");
|
|
2133
2141
|
console.log("Next steps:");
|
|
@@ -2147,7 +2155,7 @@ Examples:
|
|
|
2147
2155
|
async function pushToRemote(bundlePath, manifest, registryUrl, apiKey) {
|
|
2148
2156
|
try {
|
|
2149
2157
|
console.log(`\u{1F4E4} Pushing to remote registry: ${registryUrl}`);
|
|
2150
|
-
const bundleBuffer =
|
|
2158
|
+
const bundleBuffer = fs8.readFileSync(bundlePath);
|
|
2151
2159
|
const bundleSize = bundleBuffer.length;
|
|
2152
2160
|
console.log(` Bundle size: ${bundleSize} bytes`);
|
|
2153
2161
|
const bundleHex = bundleBuffer.toString("hex");
|
|
@@ -2302,25 +2310,25 @@ async function extractManifest(bundlePath) {
|
|
|
2302
2310
|
if (entry.path === "manifest.json") ;
|
|
2303
2311
|
}
|
|
2304
2312
|
});
|
|
2305
|
-
const extractDir =
|
|
2306
|
-
if (!
|
|
2307
|
-
|
|
2313
|
+
const extractDir = path7.join(path7.dirname(bundlePath), `.temp-${Date.now()}`);
|
|
2314
|
+
if (!fs8.existsSync(extractDir)) {
|
|
2315
|
+
fs8.mkdirSync(extractDir);
|
|
2308
2316
|
}
|
|
2309
2317
|
try {
|
|
2310
2318
|
await tar.x({
|
|
2311
2319
|
file: bundlePath,
|
|
2312
2320
|
cwd: extractDir,
|
|
2313
|
-
filter: (
|
|
2321
|
+
filter: (path8) => path8 === "manifest.json"
|
|
2314
2322
|
});
|
|
2315
|
-
const manifestPath =
|
|
2316
|
-
if (
|
|
2317
|
-
const content =
|
|
2323
|
+
const manifestPath = path7.join(extractDir, "manifest.json");
|
|
2324
|
+
if (fs8.existsSync(manifestPath)) {
|
|
2325
|
+
const content = fs8.readFileSync(manifestPath, "utf8");
|
|
2318
2326
|
manifestContent = content;
|
|
2319
2327
|
}
|
|
2320
2328
|
} catch {
|
|
2321
2329
|
} finally {
|
|
2322
|
-
if (
|
|
2323
|
-
|
|
2330
|
+
if (fs8.existsSync(extractDir)) {
|
|
2331
|
+
fs8.rmSync(extractDir, { recursive: true, force: true });
|
|
2324
2332
|
}
|
|
2325
2333
|
}
|
|
2326
2334
|
if (manifestContent) {
|
|
@@ -2335,13 +2343,17 @@ async function extractManifest(bundlePath) {
|
|
|
2335
2343
|
}
|
|
2336
2344
|
var configCommand = new Command("config").description("Manage CLI configuration for remote registry").addCommand(createConfigSetCommand()).addCommand(createConfigGetCommand()).addCommand(createConfigListCommand()).addCommand(createConfigResetCommand());
|
|
2337
2345
|
function createConfigSetCommand() {
|
|
2338
|
-
return new Command("set").description("Set a configuration value").argument("<key>", "Configuration key (registry-url, api-key)").argument(
|
|
2346
|
+
return new Command("set").description("Set a configuration value").argument("<key>", "Configuration key (registry-url, api-key)").argument(
|
|
2347
|
+
"[value]",
|
|
2348
|
+
"Configuration value; for api-key, omit it to read the key from stdin"
|
|
2349
|
+
).action(async (key, value) => {
|
|
2339
2350
|
try {
|
|
2340
2351
|
const config = new RemoteConfig();
|
|
2341
2352
|
switch (key) {
|
|
2342
2353
|
case "registry-url":
|
|
2343
2354
|
case "registryUrl":
|
|
2344
2355
|
case "url":
|
|
2356
|
+
if (!value) throw new Error("registry-url needs a value");
|
|
2345
2357
|
config.setRegistryUrl(value);
|
|
2346
2358
|
console.log(
|
|
2347
2359
|
chalk4.green(`\u2705 Registry URL set to: ${chalk4.bold(value)}`)
|
|
@@ -2349,7 +2361,7 @@ function createConfigSetCommand() {
|
|
|
2349
2361
|
break;
|
|
2350
2362
|
case "api-key":
|
|
2351
2363
|
case "apiKey":
|
|
2352
|
-
config.setApiKey(value);
|
|
2364
|
+
config.setApiKey(value ?? await readApiKey());
|
|
2353
2365
|
console.log(chalk4.green("\u2705 API key set successfully"));
|
|
2354
2366
|
console.log(
|
|
2355
2367
|
chalk4.yellow(
|
|
@@ -2371,6 +2383,17 @@ function createConfigSetCommand() {
|
|
|
2371
2383
|
}
|
|
2372
2384
|
});
|
|
2373
2385
|
}
|
|
2386
|
+
async function readApiKey() {
|
|
2387
|
+
if (process.stdin.isTTY) process.stderr.write("Paste the API key: ");
|
|
2388
|
+
let input = "";
|
|
2389
|
+
for await (const chunk of process.stdin) {
|
|
2390
|
+
input += chunk;
|
|
2391
|
+
if (process.stdin.isTTY && input.includes("\n")) break;
|
|
2392
|
+
}
|
|
2393
|
+
const key = input.trim();
|
|
2394
|
+
if (!key) throw new Error("No API key on stdin");
|
|
2395
|
+
return key;
|
|
2396
|
+
}
|
|
2374
2397
|
function createConfigGetCommand() {
|
|
2375
2398
|
return new Command("get").description("Get a configuration value").argument("<key>", "Configuration key (registry-url, api-key)").action((key) => {
|
|
2376
2399
|
try {
|
|
@@ -2417,7 +2440,7 @@ function createConfigListCommand() {
|
|
|
2417
2440
|
const config = new RemoteConfig();
|
|
2418
2441
|
console.log(chalk4.blue("\n\u{1F4CB} Remote Registry Configuration\n"));
|
|
2419
2442
|
const configPath = config.getConfigPath();
|
|
2420
|
-
const configFileExists =
|
|
2443
|
+
const configFileExists = fs8.existsSync(configPath);
|
|
2421
2444
|
const url = config.getRegistryUrl();
|
|
2422
2445
|
let urlSource;
|
|
2423
2446
|
if (process.env.CALIMERO_REGISTRY_URL) {
|
|
@@ -2498,7 +2521,7 @@ function getAuthHeaders(url) {
|
|
|
2498
2521
|
const apiKey = remoteConfig.getApiKey();
|
|
2499
2522
|
if (!apiKey) {
|
|
2500
2523
|
throw new Error(
|
|
2501
|
-
"API token required for org write operations.\nGet a token from the Organizations page in the web UI, then run:\n calimero-registry config set api-key
|
|
2524
|
+
"API token required for org write operations.\nGet a token from the Organizations page in the web UI, then run:\n calimero-registry config set api-key (paste the token when asked)"
|
|
2502
2525
|
);
|
|
2503
2526
|
}
|
|
2504
2527
|
RemoteConfig.assertApiKeyTransport(url, apiKey);
|