ihub-cli 1.1.5 → 1.1.7

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.
Files changed (3) hide show
  1. package/index.js +470 -114
  2. package/package.json +3 -1
  3. package/readbuilddirs.js +0 -0
package/index.js CHANGED
@@ -10,107 +10,14 @@ import dirtoFileArray from "./recursive_file_extractor.js"
10
10
  import { PinataSDK } from "pinata";
11
11
  import { provideClient } from "./dbconnection.js"
12
12
 
13
+ import pLimit from "p-limit";
13
14
 
14
- const IHUB_DIR = path.join(os.homedir(), ".ihub");
15
- const FILE_TO_STORE_LOGIN = path.join(IHUB_DIR, "login.txt");
16
-
17
-
18
-
19
- async function UpdateRepo(cid,pinata) {
20
-
21
-
22
- const result = await pinata.gateways.public.get(cid)
23
- const Data = result.data;
24
- console.log(Data)
25
- const arrayBuffer = await Data.arrayBuffer()
26
- const buffer = Buffer.from(arrayBuffer);
27
- console.log(buffer)
28
- deleteFilesWithExtension(".history.bundle",".")
29
- let dynamicstring=crypto.randomUUID()
30
- let shortID = dynamicstring.substring(0, 5)
31
- let bpath=`${shortID}.history.bundle`
32
-
33
- fs.writeFileSync(`${bpath}`,buffer);
34
-
35
- //const absolutePath = path.resolve(folder);
36
- const absoluteBundlePath = path.resolve(bpath);
15
+ import {createRepo,uploadFiles,commit} from "@huggingface/hub"
37
16
 
38
- execSync(`git fetch ${absoluteBundlePath} refs/heads/*:refs/remotes/bundle/*`, {
39
- cwd: `.`,
40
- stdio: 'inherit'
41
- });
42
-
43
- const currentBranch = execSync(
44
- `git branch --show-current`,
45
- {
46
- cwd: '.',
47
- encoding: 'utf8',
48
- shell: true
49
- }
50
- ).trim();
51
-
52
- const bundleBranch = `bundle/${currentBranch}`;
53
-
54
- console.log(`šŸ”€ Merging ${bundleBranch} → ${currentBranch}`);
55
- execSync(
56
- `git merge ${bundleBranch} --allow-unrelated-histories --no-edit`,
57
- {
58
- cwd: '.',
59
- stdio: 'inherit',
60
- shell: true
61
- }
62
- );
63
-
64
- console.log("pulled successfully")
65
-
66
- }
67
-
68
-
69
-
70
- async function Pull(folder,pinata,client){
71
-
72
-
73
- const db = client.db("ihub_db");
74
- const coll = db.collection("ihub_col");
75
- const targetManifestId= fs.readFileSync(FILE_TO_STORE_LOGIN,'utf8');
76
- const doc = await coll.findOne({
77
- "owner": "system",
78
- "manifests.id": targetManifestId
79
- });
80
- if(doc) {
81
-
82
- let uploads=null
83
- let bundle=null
84
- let manifests=doc.manifests
85
-
86
-
87
- for(let obj of manifests){
88
-
89
- let dbfolder=String(obj.folder)
90
-
91
- if(obj.id==targetManifestId && dbfolder==folder){
92
- console.log(obj.folder)
93
- uploads=obj.uploads
94
- }
95
-
96
-
97
-
98
- }
99
- console.log(uploads)
100
-
101
- for(let obj of uploads){
102
-
103
- let name=obj.name
104
- const isIncluded = name.includes(".history.bundle");
105
- if(isIncluded) {
106
17
 
107
- bundle=obj.cid
18
+ const IHUB_DIR = path.join(os.homedir(), ".ihub");
19
+ const FILE_TO_STORE_LOGIN = path.join(IHUB_DIR, "login.txt");
108
20
 
109
- }}
110
-
111
- await UpdateRepo(bundle,pinata)
112
-
113
- }}
114
21
 
115
22
 
116
23
 
@@ -128,11 +35,31 @@ async function getcreds() {
128
35
 
129
36
  }
130
37
 
38
+
39
+ async function getToken(){
40
+
41
+ let request=await fetch("https://immutablehub-creds.hf.space/hft",{
42
+ mode:"cors",
43
+ method:"get",
44
+ headers:{
45
+ "content-type":"application/json"
46
+ }
47
+ })
48
+ let response=await request.json()
49
+ return {"hft":response.hft}
50
+
51
+ }
52
+
131
53
  async function getRuntime() {
54
+
132
55
  const { jwt, gateway } = await getcreds();
133
56
  const pinata = new PinataSDK({ pinataJwt: jwt, pinataGateway: gateway });
57
+
58
+
59
+
134
60
  const client = provideClient();
135
61
  return { pinata, client };
62
+
136
63
  }
137
64
 
138
65
 
@@ -182,11 +109,28 @@ function fileWithExtensionExists(extension,folder) {
182
109
 
183
110
 
184
111
 
185
- async function deleteManifest(targetManifestId, foldername,client) {
186
112
 
187
113
 
188
- const db = client.db("ihub_db");
189
- const coll = db.collection("ihub_col");
114
+ async function deleteManifest(targetManifestId, foldername,client,mpc,prompt) {
115
+
116
+
117
+
118
+
119
+ let db = client.db("ihub_db");
120
+ let coll = db.collection("ihub_col");
121
+
122
+ if (mcp ==true){
123
+
124
+ db = client.db("ihub_db");
125
+ coll = db.collection("mcp_registry");
126
+
127
+ }else if(prompt ==true){
128
+
129
+ db = client.db("ihub_db");
130
+ coll = db.collection("prompt_comp");
131
+ }
132
+
133
+
190
134
  await coll.updateOne(
191
135
  { owner: "system" },
192
136
  {
@@ -203,6 +147,60 @@ function fileWithExtensionExists(extension,folder) {
203
147
 
204
148
 
205
149
 
150
+
151
+ //--------------------------------------------------------------------
152
+
153
+ async function deactivateOlderVersions(targetId, targetFolder,mcp,prompt) {
154
+
155
+ let db = client.db("ihub_db");
156
+ let coll = db.collection("ihub_col");
157
+
158
+ if (mcp ==true){
159
+
160
+ db = client.db("ihub_db");
161
+ coll = db.collection("mcp_registry");
162
+
163
+ }else if(prompt ==true){
164
+ db = client.db("ihub_db");
165
+ coll = db.collection("prompt_comp");
166
+ }
167
+
168
+
169
+ try {
170
+ const filter = {
171
+ "manifests": {
172
+ $elemMatch: { id: targetId, folder: targetFolder }
173
+ }
174
+ };
175
+
176
+ const update = {
177
+ $set: { "manifests.$[elem].is_latest": false }
178
+ };
179
+ const options = {
180
+ arrayFilters: [
181
+ {
182
+ "elem.id": targetId,
183
+ "elem.folder": targetFolder
184
+ }
185
+ ]
186
+ };
187
+
188
+ const result = await coll.updateMany(filter, update, options);
189
+
190
+ console.log(`Matched ${result.matchedCount} docs and updated ${result.modifiedCount} manifests.`);
191
+ return result;
192
+ } catch (error) {
193
+ console.error("Global update failed:", error);
194
+ }
195
+ }
196
+
197
+
198
+
199
+
200
+ //-------------------------------------------------------------------------
201
+
202
+
203
+
206
204
  async function manifestExists(targetManifestId, foldername,client) {
207
205
 
208
206
  const db = client.db("ihub_db");
@@ -269,6 +267,7 @@ async function getFileNew(folder,obj,pinata) {
269
267
  const result = await pinata.gateways.public.get(obj.cid)
270
268
  const Data = result.data;
271
269
 
270
+ //fs.mkdirSync(folder, { recursive: true });
272
271
  const filePath = path.join(folder, obj.name);
273
272
  fs.writeFileSync(`${filePath}`,Data,"utf-8");
274
273
  console.log("successfully")
@@ -321,15 +320,29 @@ function gitBundler(FOLDER_TO_UPLOAD){
321
320
  }
322
321
 
323
322
 
324
- async function Clone(folder,pinata,client){
323
+ async function Clone(folder,pinata,client,mcp,prompt){
325
324
 
326
325
 
327
- const db = client.db("ihub_db");
328
- const coll = db.collection("ihub_col");
326
+
327
+ let db = client.db("ihub_db");
328
+ let coll = db.collection("ihub_col");
329
+
330
+ if (mcp ==true){
331
+
332
+ db = client.db("ihub_db");
333
+ coll = db.collection("mcp_registry");
334
+
335
+ }
336
+ else if(prompt ==true){
337
+ db = client.db("ihub_db");
338
+ coll = db.collection("prompt_comp");
339
+ }
340
+
341
+
342
+
329
343
  const targetManifestId= fs.readFileSync(FILE_TO_STORE_LOGIN,'utf8');
330
344
  const doc = await coll.findOne({
331
345
  "owner": "system",
332
- "manifests.id": targetManifestId
333
346
  });
334
347
  if(doc) {
335
348
 
@@ -412,37 +425,57 @@ async function CloneNew(folder,pinata,client){
412
425
 
413
426
 
414
427
 
428
+ async function Push(FOLDER_TO_UPLOAD,pinata,client,mcp,prompt) {
415
429
 
430
+ try {
416
431
 
417
- async function Push(FOLDER_TO_UPLOAD,pinata,client) {
432
+ let db = client.db("ihub_db");
433
+ let coll = db.collection("ihub_col");
418
434
 
419
- try {
435
+ if (mcp ==true){
420
436
 
437
+ db = client.db("ihub_db");
438
+ coll = db.collection("mcp_registry");
421
439
 
422
- const db = client.db("ihub_db");
423
- const coll = db.collection("ihub_col");
440
+ }
441
+ else if(prompt ==true){
442
+ db = client.db("ihub_db");
443
+ coll = db.collection("prompt_comp");
444
+ }
424
445
 
425
- let uploads=[]
446
+
447
+
448
+ //let uploads=[]
426
449
  const absolutePath = path.resolve(FOLDER_TO_UPLOAD);
427
450
  let files=dirtoFileArray(absolutePath)
428
451
 
452
+ /*
429
453
  for (let file of files) {
430
454
 
431
455
  const upload=await pinata.upload.public.file(file)
432
456
  uploads.push(upload)
433
457
  }
458
+ */
459
+
460
+
434
461
 
462
+ const limit = pLimit(5); // 5 concurrent uploads
463
+ const uploadPromises = files.map(file =>
464
+ limit(() => pinata.upload.public.file(file))
465
+ );
435
466
 
467
+ const uploads = await Promise.all(uploadPromises);
436
468
  console.log(uploads)
437
469
  const data = fs.readFileSync(FILE_TO_STORE_LOGIN, 'utf8');
438
470
  const lastpath = path.basename(absolutePath);
439
-
471
+
440
472
  let meta={"id":data,"folder":lastpath,"uploads":uploads,"is_latest":true}
441
473
 
442
474
 
443
475
  let docalreadyexists=await manifestExists(data,lastpath,client)
444
476
  if (docalreadyexists){
445
- await deleteManifest(data,lastpath,client)
477
+ //await deleteManifest(data,lastpath,client,mcp,prompt)
478
+ await deactivateOlderVersions(data,lastpath,mcp,prompt)
446
479
  }
447
480
 
448
481
  await coll.findOneAndUpdate(
@@ -464,6 +497,254 @@ async function Push(FOLDER_TO_UPLOAD,pinata,client) {
464
497
 
465
498
 
466
499
 
500
+ async function UpdateRepo(cid,pinata) {
501
+
502
+
503
+ const result = await pinata.gateways.public.get(cid)
504
+ const Data = result.data;
505
+ console.log(Data)
506
+ const arrayBuffer = await Data.arrayBuffer()
507
+ const buffer = Buffer.from(arrayBuffer);
508
+ console.log(buffer)
509
+ deleteFilesWithExtension(".history.bundle",".")
510
+ let dynamicstring=crypto.randomUUID()
511
+ let shortID = dynamicstring.substring(0, 5)
512
+ let bpath=`${shortID}.history.bundle`
513
+
514
+ fs.writeFileSync(`${bpath}`,buffer);
515
+
516
+ //const absolutePath = path.resolve(folder);
517
+ const absoluteBundlePath = path.resolve(bpath);
518
+
519
+ execSync(`git fetch ${absoluteBundlePath} refs/heads/*:refs/remotes/bundle/*`, {
520
+ cwd: `.`,
521
+ stdio: 'inherit'
522
+ });
523
+
524
+ const currentBranch = execSync(
525
+ `git branch --show-current`,
526
+ {
527
+ cwd: '.',
528
+ encoding: 'utf8',
529
+ shell: true
530
+ }
531
+ ).trim();
532
+
533
+ const bundleBranch = `bundle/${currentBranch}`;
534
+
535
+ console.log(`šŸ”€ Merging ${bundleBranch} → ${currentBranch}`);
536
+ execSync(
537
+ `git merge ${bundleBranch} --allow-unrelated-histories --no-edit`,
538
+ {
539
+ cwd: '.',
540
+ stdio: 'inherit',
541
+ shell: true
542
+ }
543
+ );
544
+
545
+ console.log("pulled successfully")
546
+
547
+ }
548
+
549
+
550
+
551
+ async function Pull(folder,pinata,client,mcp,prompt){
552
+
553
+
554
+ let db = client.db("ihub_db");
555
+ let coll = db.collection("ihub_col");
556
+
557
+ if (mcp == true){
558
+
559
+ db = client.db("ihub_db");
560
+ coll = db.collection("mcp_registry");
561
+
562
+ }
563
+ else if(prompt == true){
564
+ db = client.db("ihub_db");
565
+ coll = db.collection("prompt_comp");
566
+ }
567
+
568
+
569
+ const targetManifestId= fs.readFileSync(FILE_TO_STORE_LOGIN,'utf8');
570
+ const doc = await coll.findOne({
571
+ "owner": "system",
572
+ });
573
+ if(doc) {
574
+
575
+ let uploads=null
576
+ let bundle=null
577
+ let manifests=doc.manifests
578
+
579
+
580
+ for(let obj of manifests){
581
+
582
+ let dbfolder=String(obj.folder)
583
+
584
+ if(obj.id==targetManifestId && dbfolder==folder){
585
+ console.log(obj.folder)
586
+ uploads=obj.uploads
587
+ }
588
+
589
+
590
+
591
+ }
592
+ console.log(uploads)
593
+
594
+ for(let obj of uploads){
595
+
596
+ let name=obj.name
597
+ const isIncluded = name.includes(".history.bundle");
598
+ if(isIncluded) {
599
+
600
+ bundle=obj.cid
601
+
602
+ }}
603
+
604
+ await UpdateRepo(bundle,pinata)
605
+
606
+ }}
607
+
608
+
609
+
610
+
611
+
612
+ async function ensureSpaceExists(name,Token) {
613
+ try {
614
+ await createRepo({
615
+ repo: { type: "space", name: name },
616
+ accessToken: Token,
617
+ sdk: "docker", // Required for Spaces
618
+ });
619
+ return true
620
+
621
+ } catch (error) {
622
+ // Check if the error is specifically because it already exists
623
+ if (error.status !== 409) {
624
+ return true
625
+ }
626
+ else{
627
+ return false
628
+ }
629
+ }
630
+ }
631
+
632
+
633
+
634
+
635
+
636
+ async function Host(folder,client){
637
+
638
+
639
+
640
+
641
+ let token=await getToken()
642
+ const TT = Buffer.from(token.hft, 'base64').toString('utf-8');
643
+ const targetManifestId= fs.readFileSync(FILE_TO_STORE_LOGIN,'utf8');
644
+ const absolutePath = path.resolve(folder);
645
+ const lastName = path.basename(absolutePath);
646
+ console.log(lastName);
647
+ const spaceName = `immutablehub/${lastName}_${targetManifestId}`;
648
+ const files = [
649
+ {
650
+ path: "Dockerfile",
651
+ content: `
652
+ #Use a slim Node.js image for faster builds
653
+ FROM node:18-slim
654
+
655
+ #Create and define the working directory
656
+ WORKDIR /app
657
+
658
+ #Copy package.json and package-lock.json first for better caching
659
+ COPY package*.json ./
660
+
661
+ #Install dependencies
662
+ RUN npm install
663
+
664
+ #Copy the rest of your application code
665
+ COPY . .
666
+
667
+ #Hugging Face Spaces require port 7860
668
+ EXPOSE 7860
669
+
670
+ #Start the application
671
+ CMD ["node", "index.js"]
672
+
673
+ `.trim(),
674
+ },
675
+ {
676
+ path: "README.md",
677
+ content: [
678
+ '---',
679
+ 'title: Docker Space',
680
+ 'emoji: 🐳',
681
+ 'colorFrom: blue',
682
+ 'colorTo: blue',
683
+ 'sdk: docker',
684
+ 'app_port: 7860', // Highly recommended since you EXPOSE 7860
685
+ 'pinned: false',
686
+ '---'
687
+ ].join('\n')
688
+ },
689
+ ];
690
+
691
+ console.log("checking space")
692
+ let exists=await ensureSpaceExists(spaceName,TT)
693
+ if(exists) {
694
+
695
+
696
+
697
+ let rawFiles=dirtoFileArray(absolutePath)
698
+ console.log(rawFiles)
699
+
700
+ const filesToUpload = rawFiles
701
+ .filter(file => {
702
+ // Replicating your "cleanedFiles" logic
703
+ const isIgnored = file.name === "package-lock.json" || file.name.includes(".history.bundle");
704
+ return !isIgnored;
705
+ })
706
+ .map(file => {
707
+
708
+ return {
709
+ path: file.name,
710
+ content: file
711
+ };
712
+ });
713
+
714
+ //let allFiles=[...files,...filesToUpload]
715
+ const finalFiles = [...files, ...filesToUpload].map(f => ({
716
+ path: f.path,
717
+ //We wrap in a new Blob to strip the 'File' prototype that is causing the error
718
+ content: new Blob([f.content])
719
+ }));
720
+
721
+
722
+
723
+ console.log("now uploading")
724
+ await uploadFiles({
725
+ repo: { type: "space", name: spaceName },
726
+ accessToken: TT,
727
+ files: finalFiles
728
+ });
729
+
730
+
731
+
732
+ let db=client.db("ihub_db")
733
+ let coll=db.collection("hosted")
734
+
735
+
736
+ await coll.insertOne({"id":targetManifestId,"app":folder,"space":`https://huggingface.co/spaces/${spaceName}`})
737
+ console.log(`https://huggingface.co/spaces/${spaceName}`)
738
+
739
+
740
+ }
741
+ else{
742
+ console.log("error in hosting")
743
+ }
744
+ }
745
+
746
+
747
+
467
748
 
468
749
  yargs(hideBin(process.argv))
469
750
  .command(
@@ -492,7 +773,23 @@ yargs(hideBin(process.argv))
492
773
  return yargs.positional('folderpath', {
493
774
  describe: 'The local folder to push',
494
775
  type: 'string'
776
+ })
777
+ .option('mcp', {
778
+
779
+ alias: 'm',
780
+ type: 'boolean',
781
+ description: 'mcp server repo or not',
782
+ default: false
783
+ })
784
+ .option('prompt', {
785
+
786
+ alias: 'p',
787
+ type: 'boolean',
788
+ description: 'mcp server repo or not',
789
+ default: false
495
790
  });
791
+
792
+
496
793
  },
497
794
  async (argv) => {
498
795
 
@@ -501,7 +798,7 @@ yargs(hideBin(process.argv))
501
798
 
502
799
  console.log(`\nā¬†ļø Starting PUSH operation on folder: ${argv.folderpath}`);
503
800
  gitBundler(argv.folderpath);
504
- await Push(argv.folderpath,pinata,client);
801
+ await Push(argv.folderpath,pinata,client,argv.mcp,argv.prompt);
505
802
  console.log('Push operation finished.');
506
803
 
507
804
  }catch(e){
@@ -520,6 +817,20 @@ yargs(hideBin(process.argv))
520
817
  return yargs.positional('foldername', {
521
818
  describe: 'the repo/folder to pull',
522
819
  type: 'string'
820
+ })
821
+ .option('mcp', {
822
+
823
+ alias: 'm',
824
+ type: 'boolean',
825
+ description: 'mcp server repo or not',
826
+ default: false
827
+ })
828
+ .option('prompt', {
829
+
830
+ alias: 'p',
831
+ type: 'boolean',
832
+ description: 'mcp server repo or not',
833
+ default: false
523
834
  });
524
835
  },
525
836
  async (argv) => {
@@ -549,7 +860,22 @@ yargs(hideBin(process.argv))
549
860
  describe: 'The folder to clone',
550
861
  type: 'string'
551
862
  })
863
+ .option('mcp', {
864
+
865
+ alias: 'm',
866
+ type: 'boolean',
867
+ description: 'mcp server repo or not',
868
+ default: false
869
+ })
870
+ .option('prompt', {
871
+
872
+ alias: 'p',
873
+ type: 'boolean',
874
+ description: 'mcp server repo or not',
875
+ default: false
876
+ })
552
877
  .option('new', {
878
+
553
879
  alias: 'n',
554
880
  type: 'boolean',
555
881
  description: 'new repo or existing repo',
@@ -566,7 +892,8 @@ yargs(hideBin(process.argv))
566
892
 
567
893
  }
568
894
  else {
569
- await Clone(argv.foldername,pinata,client);
895
+
896
+ await Clone(argv.foldername,pinata,client,argv.mcp,argv.prompt);
570
897
  console.log('Clone operation finished.');
571
898
 
572
899
  }
@@ -580,6 +907,34 @@ yargs(hideBin(process.argv))
580
907
 
581
908
  }
582
909
  )
910
+ .command(
911
+ 'host <foldername>',
912
+ 'hosts a provided nodejs backend repo',
913
+ (yargs) => {
914
+ return yargs.positional('foldername', {
915
+ describe: 'The repo to host',
916
+ type: 'string'
917
+ });
918
+ },
919
+ async (argv) => {
920
+
921
+
922
+ const { pinata, client } = await getRuntime();
923
+
924
+ try {
925
+
926
+
927
+ await Host(argv.foldername,client);
928
+
929
+ }catch(e){
930
+ console.log(e);
931
+ }
932
+ finally{
933
+ await client.close();
934
+ }
935
+
936
+ }
937
+ )
583
938
  .demandCommand(1, 'You must provide a subcommand for "ihub" (login, push, or clone).')
584
939
  .help()
585
940
  }
@@ -590,7 +945,8 @@ yargs(hideBin(process.argv))
590
945
  .example('ihub op push ./repo', 'Push a local repository')
591
946
  .example('ihub op clone <reponame> --new true', 'Clone a new repository | name will be the reponame in UI')
592
947
  .example('ihub op clone <reponame>', 'Clone an existing repository | name will be the reponame in UI')
593
- .example('ihub op clone <reponame>', 'Pull updates in an existing repository | name will be the reponame in UI')
948
+ .example('ihub op pull <reponame>', 'Pull updates in an existing repository | name will be the reponame in UI')
949
+ .example('ihub op host <reponame>', 'Hosts a provided nodejs backend repo')
594
950
  .epilog('ImmutableHub CLI • Built with ā¤ļø')
595
951
  .help()
596
952
  .argv;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ihub-cli",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "immutablehub cli to push and clone repos",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -13,8 +13,10 @@
13
13
  "author": "immutablehub",
14
14
  "license": "ISC",
15
15
  "dependencies": {
16
+ "@huggingface/hub": "^2.7.1",
16
17
  "jest": "^30.2.0",
17
18
  "mongodb": "^7.0.0",
19
+ "p-limit": "^7.3.0",
18
20
  "pinata": "^2.5.1",
19
21
  "yargs": "^18.0.0"
20
22
  }
File without changes