nemar-cli 0.5.2-dev.273 → 0.5.2-dev.274

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 (2) hide show
  1. package/dist/index.js +2 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -128,7 +128,7 @@ Fix one of these:
128
128
  Test with: ssh -T git@github.com
129
129
 
130
130
  2. Install and authenticate gh CLI:
131
- gh auth login`}}}try{let{stdout:J}=await l(["git","remote","get-url",$],{cwd:D});if(J.trim()){let{stderr:Q,exitCode:X}=await l(["git","remote","set-url",$,B],{cwd:D});if(X!==0)return{success:!1,error:Q.trim()}}else{let{stderr:Q,exitCode:X}=await l(["git","remote","add",$,B],{cwd:D});if(X!==0)return{success:!1,error:Q.trim()}}return{success:!0}}catch(J){return{success:!1,error:J.message}}}async function y5(D,F,$){try{let B={};if($)B.GIT_AUTHOR_NAME=$.name,B.GIT_AUTHOR_EMAIL=$.email,B.GIT_COMMITTER_NAME=$.name,B.GIT_COMMITTER_EMAIL=$.email;let{stderr:J,exitCode:Q}=await l(["git","add","-A"],{cwd:D,...Object.keys(B).length>0?{env:B}:{}});if(Q!==0)return{success:!1,error:J.trim()||"Failed to stage changes"};let{stdout:X,exitCode:Y,stderr:G}=await l(["git","status","--porcelain"],{cwd:D});if(Y!==0)return{success:!1,error:G.trim()||"Failed to check git status"};if(!X.trim())return{success:!0};let{stderr:W,exitCode:q}=await l(["git","commit","-m",F],{cwd:D,...Object.keys(B).length>0?{env:B}:{}});if(q!==0){if(W.includes("nothing to commit"))return{success:!0};return{success:!1,error:W.trim()||"Failed to commit changes"}}return{success:!0}}catch(B){return{success:!1,error:B.message}}}async function x5(D,F="origin",$){try{let B=$;if(!B){let G=await FB(D);if(!G||G==="HEAD"){let{exitCode:W}=await l(["git","log","-1","--oneline"],{cwd:D});if(W!==0)return{success:!1,error:"No commits found. The repository may not have been initialized correctly, or no changes were saved before pushing."};if(G==="HEAD")B="HEAD:main";else return{success:!1,error:"Could not detect current branch"}}else B=G}let{stderr:J,exitCode:Q}=await l(["git","push","-u",F,B],{cwd:D});if(Q!==0)return{success:!1,error:J.trim()||"Failed to push to GitHub"};let{stderr:X,exitCode:Y}=await l(["git","push",F,"git-annex"],{cwd:D});if(Y!==0)return{success:!0,warning:`Main branch pushed, but git-annex branch failed: ${X.trim()}. Clone operations may have issues.`};return{success:!0}}catch(B){return{success:!1,error:B.message}}}function h3(D){if(D===0)return"0 B";let F=1024,$=["B","KB","MB","GB","TB"],B=Math.floor(Math.log(D)/Math.log(F));return`${Number.parseFloat((D/F**B).toFixed(2))} ${$[B]}`}async function GWD(D,F,$,B){let J=B?.maxRetries??4,Q=B?.initialDelayMs??1e4;try{let X=await Bun.file(D).arrayBuffer(),Y=X.byteLength,G="";for(let W=0;W<=J;W++){let q=await fetch(F,{method:"PUT",body:X,headers:{"Content-Length":Y.toString()}});if(q.ok)return $?.(Y,Y),{success:!0};let E=await q.text();G=`Upload failed: ${q.status} ${E}`;let V=q.status===403&&E.includes("AccessDenied"),Z=q.status===503&&E.includes("SlowDown"),A=V||Z;if(!A||W===J){if(A&&W===J)console.warn(`Upload failed after ${J} retries: ${D}`);return{success:!1,error:G}}let L=Z?Math.min(4000*2**W,30000):Q+W*5000;if(Z)console.warn(`S3 rate limit hit, retrying in ${L/1000}s (attempt ${W+1}/${J})...`);else if(V)console.warn(`Waiting for S3 permissions to propagate, retrying in ${L/1000}s (attempt ${W+1}/${J})...`);await new Promise((U)=>setTimeout(U,L))}return{success:!1,error:G}}catch(X){let Y=X instanceof Error?X.message:String(X);return{success:!1,error:`Failed to upload ${D}: ${Y}`}}}async function _5(D,F,$={}){let B=$.jobs||4,J=Object.entries(F),Q=[],X=0,Y=0,G=0,W=0,q=null;function E(){if(W<B)return W++,Promise.resolve();return new Promise((L)=>{q=()=>{q=null,L()}})}function V(){if(W--,q)W++,q()}let Z=async(L,U)=>{let M=s2(D,L);$.onProgress?.({file:L,uploaded:0,total:0,status:"uploading"});let N=await GWD(M,U);if(N.success)X++,$.onProgress?.({file:L,uploaded:1,total:1,status:"completed"});else Q.push(`${L}: ${N.error||"Unknown error"}`),$.onProgress?.({file:L,uploaded:0,total:1,status:"failed",error:N.error});if(Y++,G++,G>=B)G=0,$.onBatchComplete?.();V()},A=[];for(let[L,U]of J)await E(),A.push(Z(L,U));if(await Promise.all(A),G>0)$.onBatchComplete?.();return{success:Q.length===0,uploaded:X,failed:Q,error:Q.length>0?`${Q.length} files failed to upload`:void 0}}async function WWD(D,F,$){try{if((await l(["git","annex","add",F],{cwd:D})).exitCode!==0);let J=await l(["git","annex","lookupkey",F],{cwd:D});if(J.exitCode!==0||!J.stdout.trim())return{success:!1,error:`Could not get git-annex key for ${F}`};let Q=J.stdout.trim(),X=await l(["git","annex","registerurl",Q,$],{cwd:D});if(X.exitCode!==0)return{success:!1,error:`Failed to register URL: ${X.stderr}`};return{success:!0}}catch(B){return{success:!1,error:B.message}}}async function DB(D,F,$){let B=0,J=[];for(let[Q,X]of Object.entries(F))if((await WWD(D,Q,X)).success)B++,$?.(Q,!0);else J.push(Q),$?.(Q,!1);return{success:J.length===0,registered:B,failed:J}}async function f5(){let D=await by(),F=[];if(!D.installed)F.push(`git-annex is not installed. Install: ${uy()}`);else if(D.compatible===!1)F.push(`git-annex version ${D.version} is too old. Required: >= ${D.minVersion}`);return{gitAnnex:D,allPassed:F.length===0,errors:F}}async function b3(D,F){try{let{stderr:$,exitCode:B}=await l(["git","clone",D,F]);if(B!==0)return{success:!1,error:$.trim()||"Failed to clone dataset"};let{stderr:J,exitCode:Q}=await l(["git","annex","init"],{cwd:F});if(Q!==0){let{exitCode:X}=await l(["git","annex","info"],{cwd:F});if(X!==0)return{success:!1,error:`Cloned repository but git-annex initialization failed: ${J.trim()}`};console.warn("git annex init returned non-zero but annex is initialized")}return{success:!0}}catch($){return{success:!1,error:$.message}}}async function r2(D,F={}){let $=F.jobs||4,B=F.paths&&F.paths.length>0?F.paths:["."];try{let J=["git","annex","get","-J",$.toString(),...B],{stdout:Q,stderr:X,exitCode:Y}=await l(J,{cwd:D});if(Y!==0)return{success:!1,error:X.trim()||"Failed to get dataset data"};let G=Q.match(/^get .+ ok$/gm);return{success:!0,filesDownloaded:G?G.length:0}}catch(J){return{success:!1,error:J.message}}}async function ly(D,F){let $=F&&F.length>0?F:["."];try{let B=["git","annex","drop",...$],{stdout:J,stderr:Q,exitCode:X}=await l(B,{cwd:D});if(X!==0){let W=[];for(let V of Q.split(`
131
+ gh auth login`}}}try{let{stdout:J}=await l(["git","remote","get-url",$],{cwd:D});if(J.trim()){let{stderr:Q,exitCode:X}=await l(["git","remote","set-url",$,B],{cwd:D});if(X!==0)return{success:!1,error:Q.trim()}}else{let{stderr:Q,exitCode:X}=await l(["git","remote","add",$,B],{cwd:D});if(X!==0)return{success:!1,error:Q.trim()}}return{success:!0}}catch(J){return{success:!1,error:J.message}}}async function y5(D,F,$){try{let B={};if($)B.GIT_AUTHOR_NAME=$.name,B.GIT_AUTHOR_EMAIL=$.email,B.GIT_COMMITTER_NAME=$.name,B.GIT_COMMITTER_EMAIL=$.email;let{stderr:J,exitCode:Q}=await l(["git","add","-A"],{cwd:D,...Object.keys(B).length>0?{env:B}:{}});if(Q!==0)return{success:!1,error:J.trim()||"Failed to stage changes"};let{stdout:X,exitCode:Y,stderr:G}=await l(["git","status","--porcelain"],{cwd:D});if(Y!==0)return{success:!1,error:G.trim()||"Failed to check git status"};if(!X.trim())return{success:!0};let{stderr:W,exitCode:q}=await l(["git","commit","-m",F],{cwd:D,...Object.keys(B).length>0?{env:B}:{}});if(q!==0){if(W.includes("nothing to commit"))return{success:!0};return{success:!1,error:W.trim()||"Failed to commit changes"}}return{success:!0}}catch(B){return{success:!1,error:B.message}}}async function x5(D,F="origin",$){try{let B=$;if(!B){let G=await FB(D);if(!G||G==="HEAD"){let{exitCode:W}=await l(["git","log","-1","--oneline"],{cwd:D});if(W!==0)return{success:!1,error:"No commits found. The repository may not have been initialized correctly, or no changes were saved before pushing."};if(G==="HEAD")B="HEAD:main";else return{success:!1,error:"Could not detect current branch"}}else B=G}let{stderr:J,exitCode:Q}=await l(["git","push","-u",F,B],{cwd:D});if(Q!==0)return{success:!1,error:J.trim()||"Failed to push to GitHub"};let{stderr:X,exitCode:Y}=await l(["git","push",F,"git-annex"],{cwd:D});if(Y!==0)return{success:!0,warning:`Main branch pushed, but git-annex branch failed: ${X.trim()}. Clone operations may have issues.`};return{success:!0}}catch(B){return{success:!1,error:B.message}}}function h3(D){if(D===0)return"0 B";let F=1024,$=["B","KB","MB","GB","TB"],B=Math.floor(Math.log(D)/Math.log(F));return`${Number.parseFloat((D/F**B).toFixed(2))} ${$[B]}`}async function GWD(D,F,$,B){let J=B?.maxRetries??4,Q=B?.initialDelayMs??1e4;try{let X=Bun.file(D),Y=X.size,G="";for(let W=0;W<=J;W++){let q=await fetch(F,{method:"PUT",body:X,headers:{"Content-Length":Y.toString()}});if(q.ok)return $?.(Y,Y),{success:!0};let E=await q.text();G=`Upload failed: ${q.status} ${E}`;let V=q.status===403&&E.includes("AccessDenied"),Z=q.status===503&&E.includes("SlowDown"),A=V||Z;if(!A||W===J){if(A&&W===J)console.warn(`Upload failed after ${J} retries: ${D}`);return{success:!1,error:G}}let L=Z?Math.min(4000*2**W,30000):Q+W*5000;if(Z)console.warn(`S3 rate limit hit, retrying in ${L/1000}s (attempt ${W+1}/${J})...`);else if(V)console.warn(`Waiting for S3 permissions to propagate, retrying in ${L/1000}s (attempt ${W+1}/${J})...`);await new Promise((U)=>setTimeout(U,L))}return{success:!1,error:G}}catch(X){let Y=X instanceof Error?X.message:String(X);return{success:!1,error:`Failed to upload ${D}: ${Y}`}}}async function _5(D,F,$={}){let B=$.jobs||4,J=Object.entries(F),Q=[],X=0,Y=0,G=0,W=0,q=null;function E(){if(W<B)return W++,Promise.resolve();return new Promise((L)=>{q=()=>{q=null,L()}})}function V(){if(W--,q)W++,q()}let Z=async(L,U)=>{let M=s2(D,L);$.onProgress?.({file:L,uploaded:0,total:0,status:"uploading"});let N=await GWD(M,U);if(N.success)X++,$.onProgress?.({file:L,uploaded:1,total:1,status:"completed"});else Q.push(`${L}: ${N.error||"Unknown error"}`),$.onProgress?.({file:L,uploaded:0,total:1,status:"failed",error:N.error});if(Y++,G++,G>=B)G=0,$.onBatchComplete?.();V()},A=[];for(let[L,U]of J)await E(),A.push(Z(L,U));if(await Promise.all(A),G>0)$.onBatchComplete?.();return{success:Q.length===0,uploaded:X,failed:Q,error:Q.length>0?`${Q.length} files failed to upload`:void 0}}async function WWD(D,F,$){try{if((await l(["git","annex","add",F],{cwd:D})).exitCode!==0);let J=await l(["git","annex","lookupkey",F],{cwd:D});if(J.exitCode!==0||!J.stdout.trim())return{success:!1,error:`Could not get git-annex key for ${F}`};let Q=J.stdout.trim(),X=await l(["git","annex","registerurl",Q,$],{cwd:D});if(X.exitCode!==0)return{success:!1,error:`Failed to register URL: ${X.stderr}`};return{success:!0}}catch(B){return{success:!1,error:B.message}}}async function DB(D,F,$){let B=0,J=[];for(let[Q,X]of Object.entries(F))if((await WWD(D,Q,X)).success)B++,$?.(Q,!0);else J.push(Q),$?.(Q,!1);return{success:J.length===0,registered:B,failed:J}}async function f5(){let D=await by(),F=[];if(!D.installed)F.push(`git-annex is not installed. Install: ${uy()}`);else if(D.compatible===!1)F.push(`git-annex version ${D.version} is too old. Required: >= ${D.minVersion}`);return{gitAnnex:D,allPassed:F.length===0,errors:F}}async function b3(D,F){try{let{stderr:$,exitCode:B}=await l(["git","clone",D,F]);if(B!==0)return{success:!1,error:$.trim()||"Failed to clone dataset"};let{stderr:J,exitCode:Q}=await l(["git","annex","init"],{cwd:F});if(Q!==0){let{exitCode:X}=await l(["git","annex","info"],{cwd:F});if(X!==0)return{success:!1,error:`Cloned repository but git-annex initialization failed: ${J.trim()}`};console.warn("git annex init returned non-zero but annex is initialized")}return{success:!0}}catch($){return{success:!1,error:$.message}}}async function r2(D,F={}){let $=F.jobs||4,B=F.paths&&F.paths.length>0?F.paths:["."];try{let J=["git","annex","get","-J",$.toString(),...B],{stdout:Q,stderr:X,exitCode:Y}=await l(J,{cwd:D});if(Y!==0)return{success:!1,error:X.trim()||"Failed to get dataset data"};let G=Q.match(/^get .+ ok$/gm);return{success:!0,filesDownloaded:G?G.length:0}}catch(J){return{success:!1,error:J.message}}}async function ly(D,F){let $=F&&F.length>0?F:["."];try{let B=["git","annex","drop",...$],{stdout:J,stderr:Q,exitCode:X}=await l(B,{cwd:D});if(X!==0){let W=[];for(let V of Q.split(`
132
132
  `)){let Z=V.match(/^drop (.+) \(unsafe\)/);if(Z)W.push(Z[1])}let q=J.match(/^drop .+ ok$/gm),E=q?q.length:0;return{success:!1,error:Q.trim(),dropped:E,kept:W}}let Y=J.match(/^drop .+ ok$/gm);return{success:!0,dropped:Y?Y.length:0,kept:[]}}catch(B){return{success:!1,error:(B instanceof Error?B.message:String(B))||"Unknown error during drop",dropped:0,kept:[]}}}var gy=/^[a-zA-Z0-9._-]+$/;async function dy(D){let F=[],{stdout:$,exitCode:B}=await l(["git","config","--get-regexp","^remote\\..*\\.annex-s3"],{cwd:D});if(B===0&&$.trim())for(let W of $.trim().split(`
133
133
  `)){let q=W.match(/^remote\.(.+?)\.annex-/);if(q&&gy.test(q[1]))F.push(q[1])}if(F.length>0)return[...new Set(F)];let{stdout:J,exitCode:Q,stderr:X}=await l(["git","annex","info","--json"],{cwd:D});if(Q!==0){if(X.trim())console.error(`git annex info failed: ${X.trim()}`);return[]}if(!J.trim())return[];let Y;try{Y=JSON.parse(J)}catch(W){let q=W instanceof Error?W.message:String(W);return console.error(`Failed to parse git-annex info JSON: ${q}`),[]}let G=[...Array.isArray(Y["trusted repositories"])?Y["trusted repositories"]:[],...Array.isArray(Y["semitrusted repositories"])?Y["semitrusted repositories"]:[],...Array.isArray(Y["untrusted repositories"])?Y["untrusted repositories"]:[]];for(let W of G){if(!W?.description?.includes("["))continue;let q=W.description.match(/\[(.+?)\]/);if(!q)continue;let E=q[1];if(!gy.test(E))continue;let{stdout:V}=await l(["git","config",`remote.${E}.annex-s3`],{cwd:D});if(V.trim())F.push(E)}return[...new Set(F)]}async function py(D,F,$=4){try{let B=["git","annex","copy","--to",F,"-J",$.toString(),"."],{stdout:J,stderr:Q,exitCode:X}=await l(B,{cwd:D});if(X!==0)return{success:!1,error:Q.trim()||"Failed to copy to remote",filesCopied:0};let Y=J.match(/^copy .+ ok$/gm);return{success:!0,filesCopied:Y?Y.length:0}}catch(B){return{success:!1,error:(B instanceof Error?B.message:String(B))||"Unknown error during copy",filesCopied:0}}}async function cy(D){if(!hy(D))return null;try{let{stdout:F,exitCode:$}=await l(["git","annex","info","--json"],{cwd:D});if($===0){let B=JSON.parse(F),J=0,Q=B["local annex size"]||"0 bytes",X=Q.match(/([\d.]+)\s*(bytes?|KB|MB|GB|TB)/i);if(X){let W=Number.parseFloat(X[1]),q=X[2].toLowerCase();J=W*({byte:1,bytes:1,kb:1024,mb:1048576,gb:1073741824,tb:1099511627776}[q]||1)}let Y=B["annexed files in working tree"]||0,G=B["local annex keys"]||0;return{files:Y,size:Q,sizeBytes:J,annexedFiles:Y,presentFiles:G,missingFiles:Y-G}}}catch(F){let $=F instanceof Error?F.message:String(F);console.warn(`Failed to get git-annex info for ${D}: ${$}`)}try{let{stdout:F}=await l(["find",".","-type","f","-not","-path","./.git/*"],{cwd:D}),$=F.trim().split(`
134
134
  `).filter(Boolean).length;return{files:$,size:"unknown",sizeBytes:0,annexedFiles:0,presentFiles:$,missingFiles:0}}catch(F){let $=F instanceof Error?F.message:String(F);return console.warn(`Failed to count files in ${D}: ${$}`),null}}async function ny(D){try{let{stdout:F,exitCode:$}=await l(["git","tag","-l","--sort=-version:refname","--format=%(refname:short)|%(creatordate:short)|%(objectname:short)"],{cwd:D});if($!==0||!F.trim())return[];return F.trim().split(`
@@ -754,7 +754,7 @@ Examples:
754
754
  $ nemar sandbox # Run sandbox training
755
755
  $ nemar sandbox status # Check if training is completed
756
756
  $ nemar sandbox reset # Reset for re-training
757
- `).action($HD);async function $HD(){if(console.log(),console.log(H.bold("NEMAR Sandbox Training")),console.log(H.gray("Verify your setup and learn the upload workflow")),console.log(),!xD()){console.log(H.red("Not authenticated")),console.log(H.gray("Run 'nemar auth login' first"));return}let D=oD();if(D.sandboxCompleted){console.log(H.green("Sandbox training already completed!")),console.log(H.gray(`Dataset ID: ${D.sandboxDatasetId}`)),console.log(),console.log("You can upload real datasets with:"),console.log(H.cyan(" nemar dataset upload ./your-dataset")),console.log(),console.log(H.gray("To re-run training, use: nemar sandbox reset"));return}console.log(H.bold("Step 1/6: Checking prerequisites..."));let F=C("Checking git-annex and SSH...").start(),$=await a$();if(!$.allPassed){F.fail("Prerequisites check failed"),console.log(),console.log(H.red("Missing requirements:"));for(let S of $.errors)console.log(H.yellow(` - ${S}`));if(!$.githubSSH.accessible)console.log(H.gray(" Run 'nemar auth setup-ssh' to configure SSH"));return}F.succeed("All prerequisites met");let B=C("Verifying GitHub CLI authentication...").start(),J=await r$(D.githubUsername);if(!J.authenticated){B.fail("GitHub CLI not authenticated"),console.log(H.red(` ${J.error}`)),console.log(),console.log("GitHub CLI is required for sandbox training. Install and authenticate:"),console.log(H.cyan(" brew install gh # or visit https://cli.github.com/")),console.log(H.cyan(" gh auth login"));return}if(D.githubUsername&&!J.matches)B.warn("GitHub CLI user mismatch"),console.log(H.yellow(` ${J.error}`)),console.log(),console.log("Your gh CLI is authenticated as a different GitHub account than your NEMAR account."),console.log("This may cause issues with repository access. To fix:"),console.log(H.cyan(` gh auth login # Login as ${D.githubUsername}`)),console.log(),console.log(H.yellow("WARNING: If upload fails with permission errors, this mismatch is the likely cause.")),console.log();else B.succeed(`GitHub CLI authenticated as ${J.username}`);console.log(),console.log(H.bold("Step 2/6: Generating test dataset..."));let Q=C("Creating minimal BIDS structure...").start(),X;try{let S=Tx();X=S.root;let I=Ox(S);Q.succeed(`Test dataset created (${h3(I)})`),console.log(H.gray(` Location: ${X}`))}catch(S){Q.fail("Failed to generate test dataset"),console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`));return}console.log(),console.log(H.bold("Step 3/6: Registering sandbox dataset..."));let Y=C("Creating dataset on NEMAR...").start(),G,W,q,E,V,Z;try{let S=await n$({name:"Sandbox Training Dataset",description:"Placeholder dataset for sandbox training",files:[{path:"sub-01/eeg/sub-01_task-rest_eeg.edf",size:512000,type:"data"},{path:"dataset_description.json",size:200,type:"metadata"},{path:"participants.tsv",size:50,type:"metadata"},{path:"README",size:500,type:"metadata"},{path:"sub-01/eeg/sub-01_task-rest_eeg.json",size:300,type:"metadata"}],sandbox:!0});G=S.dataset.dataset_id,W=S.dataset.ssh_url,q=S.dataset.github_url,E=S.s3_config,V=S.dataset.s3_prefix,Z=S.upload_urls||{},Y.succeed(`Sandbox dataset created: ${H.cyan(G)}`),console.log(H.gray(` GitHub: ${q}`)),await new Promise((I)=>setTimeout(I,1e4))}catch(S){if(Y.fail("Failed to create sandbox dataset"),S instanceof u)console.log(H.red(` ${S.message}`));else console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`));y1(X);return}let A=C("Accepting GitHub repository invitation...").start(),L=q?.match(/github\.com\/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/),U=L?L[1].replace(/\.git$/,""):null;if(!U){A.fail("Invalid GitHub repository URL from backend"),console.log(H.red(` Received: ${q||"(empty)"}`)),console.log(H.red(" Expected format: https://github.com/owner/repo")),console.log(),console.log("This may indicate a backend issue. Please contact support."),y1(X);return}let M=await t$(U);if(M.accepted)if(M.alreadyCollaborator)A.succeed("Already a collaborator on this repository");else A.succeed("GitHub invitation accepted");else A.warn("Could not auto-accept invitation"),console.log(H.yellow(` ${M.error}`)),console.log(),console.log("You may need to accept the invitation manually:"),console.log(H.cyan(` https://github.com/${U}/invitations`)),console.log();console.log(),console.log(H.bold("Step 4/6: Initializing repository..."));let N=C("Setting up git-annex...").start(),O=D.username&&D.email?{name:D.username,email:D.email}:void 0;try{await o$(X,{author:O}),await s$(X),await e$(X,W),N.succeed("Repository initialized")}catch(S){N.fail("Failed to initialize repository"),console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`)),y1(X);return}if(console.log(),console.log(H.bold("Step 5/6: Uploading to S3...")),Object.keys(Z).length===0)console.log(H.yellow(" No data files to upload (metadata only)"));else{let S=C("Uploading test data...").start();try{let p=0,zD=Object.keys(Z).length,HD=await _5(X,Z,{jobs:4,onProgress:(T)=>{if(T.status==="completed"||T.status==="failed")p++,S.text=`Uploading... ${p}/${zD} files`}});if(HD.failed.length>0){S.fail(`Upload failed for ${HD.failed.length} file(s)`);for(let T of HD.failed)console.log(H.red(` Failed: ${T}`));if(HD.error)console.log(H.red(` Error: ${HD.error}`));console.log(),console.log(H.yellow("Sandbox training aborted due to upload failures.")),console.log(H.gray("Please check your network connection and try again.")),y1(X);return}S.succeed(`Uploaded ${HD.uploaded} file(s)`)}catch(p){S.fail("Upload failed"),console.log(H.red(` ${p instanceof Error?p.message:"Unknown error"}`)),y1(X);return}let I=C("Registering file URLs...").start();try{let p={};for(let HD of Object.keys(Z))p[HD]=`${E.public_url}/${V}/objects/${HD}`;let zD=await DB(X,p);if(!zD.success){I.fail(`URL registration failed for ${zD.failed.length} file(s)`);for(let HD of zD.failed)console.log(H.red(` Failed: ${HD}`));console.log(),console.log(H.yellow("Sandbox training aborted due to URL registration failures.")),console.log(H.gray("This may indicate a git-annex configuration issue.")),y1(X);return}I.succeed(`Registered ${zD.registered} file URLs`)}catch(p){I.fail("Failed to register URLs"),console.log(H.red(` ${p instanceof Error?p.message:"Unknown error"}`)),y1(X);return}}console.log(),console.log(H.bold("Step 6/6: Pushing to GitHub..."));let x=C("Saving and pushing...").start();try{await y5(X,"Initial sandbox training upload",O),await x5(X),x.succeed("Pushed to GitHub")}catch(S){x.fail("Failed to push to GitHub"),console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`)),y1(X);return}let j=C("Finalizing...").start();try{await i$(G),await Cy(G),R8("sandboxCompleted",!0),R8("sandboxDatasetId",G),j.succeed("Sandbox training complete!")}catch(S){j.fail("Failed to finalize"),console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`)),y1(X);return}y1(X),console.log(),console.log(H.green.bold("Congratulations! Sandbox training completed successfully.")),console.log(),console.log("Your setup is verified and you're ready to upload real datasets:"),console.log(H.cyan(" nemar dataset upload ./your-dataset")),console.log(),console.log(H.gray(`Sandbox dataset: ${G}`))}qB.command("status").description("Check sandbox training completion status").option("--refresh","Fetch latest status from server").action(async(D)=>{if(!xD()){console.log(H.red("Not authenticated")),console.log(H.gray("Run 'nemar auth login' first"));return}if(D.refresh){let F=C("Checking status...").start();try{let $=await jy();if(R8("sandboxCompleted",$.sandbox_completed),$.sandbox_dataset_id)R8("sandboxDatasetId",$.sandbox_dataset_id);if(F.stop(),$.sandbox_completed){if(console.log(H.green("Sandbox training: Completed")),console.log(H.gray(` Dataset ID: ${$.sandbox_dataset_id}`)),$.sandbox_completed_at)console.log(H.gray(` Completed: ${$.sandbox_completed_at}`))}else console.log(H.yellow("Sandbox training: Not completed")),console.log(),console.log("Run sandbox training with:"),console.log(H.cyan(" nemar sandbox"))}catch($){if(F.fail("Failed to check status"),$ instanceof u)console.log(H.red(` ${$.message}`))}}else{let F=oD();if(F.sandboxCompleted)console.log(H.green("Sandbox training: Completed")),console.log(H.gray(` Dataset ID: ${F.sandboxDatasetId}`));else console.log(H.yellow("Sandbox training: Not completed")),console.log(),console.log("Run sandbox training with:"),console.log(H.cyan(" nemar sandbox"))}});qB.command("reset").description("Reset sandbox training status for re-training").option(RD,jD).option(bD,uD).action(async(D)=>{if(!xD()){console.log(H.red("Not authenticated")),console.log(H.gray("Run 'nemar auth login' first"));return}if(!oD().sandboxCompleted){console.log(H.yellow("Sandbox training not yet completed")),console.log(H.gray("Nothing to reset"));return}let $=await OD("Reset sandbox training status? You will need to complete training again.",D);if($!=="confirmed"){console.log(H.gray($==="declined"?"Skipped":"Cancelled"));return}let B=C("Resetting sandbox status...").start();try{await Ry(),n2("sandboxCompleted"),n2("sandboxDatasetId"),B.succeed("Sandbox status reset"),console.log(),console.log("Run sandbox training again with:"),console.log(H.cyan(" nemar sandbox"))}catch(J){if(B.fail("Failed to reset"),J instanceof u)console.log(H.red(` ${J.message}`));else console.log(H.red(` ${J instanceof Error?J.message:"Unknown error"}`))}});var wx={name:"nemar-cli",version:"0.5.2-dev.273",description:"CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource) dataset management",type:"module",main:"dist/index.js",bin:{nemar:"dist/index.js"},scripts:{dev:"bun run src/index.ts",build:"bun build src/index.ts --outdir dist --target bun --minify && sed '1s|#!/usr/bin/env node|#!/usr/bin/env bun|' dist/index.js > dist/index.js.tmp && mv dist/index.js.tmp dist/index.js",test:"bun test",lint:"biome check src/","lint:fix":"biome check --fix src/",format:"biome format --write src/",typecheck:"tsc --noEmit",prepublishOnly:"bun run build","docs:generate":"bun run scripts/generate-docs.ts","docs:serve":"mkdocs serve","docs:build":"mkdocs build"},keywords:["nemar","bids","neuroimaging","eeg","emg","datalad","cli"],author:"NEMAR Team",license:"MIT",repository:{type:"git",url:"git+https://github.com/nemarOrg/nemar-cli.git"},bugs:{url:"https://github.com/nemarOrg/nemar-cli/issues"},homepage:"https://nemar-cli.pages.dev",engines:{bun:">=1.0.0"},files:["dist","README.md","LICENSE"],dependencies:{chalk:"^5.3.0",commander:"^12.1.0",conf:"^13.0.1",inquirer:"^9.2.15",ora:"^8.0.1",zod:"^3.23.8"},devDependencies:{"@biomejs/biome":"1.9.4","@types/bcryptjs":"^3.0.0","@types/bun":"latest","@types/inquirer":"^9.0.7",bcryptjs:"^3.0.3",typescript:"^5.5.4"}};var Ix=wx.version;var H1=new q0;H1.name("nemar").description(`CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource)
757
+ `).action($HD);async function $HD(){if(console.log(),console.log(H.bold("NEMAR Sandbox Training")),console.log(H.gray("Verify your setup and learn the upload workflow")),console.log(),!xD()){console.log(H.red("Not authenticated")),console.log(H.gray("Run 'nemar auth login' first"));return}let D=oD();if(D.sandboxCompleted){console.log(H.green("Sandbox training already completed!")),console.log(H.gray(`Dataset ID: ${D.sandboxDatasetId}`)),console.log(),console.log("You can upload real datasets with:"),console.log(H.cyan(" nemar dataset upload ./your-dataset")),console.log(),console.log(H.gray("To re-run training, use: nemar sandbox reset"));return}console.log(H.bold("Step 1/6: Checking prerequisites..."));let F=C("Checking git-annex and SSH...").start(),$=await a$();if(!$.allPassed){F.fail("Prerequisites check failed"),console.log(),console.log(H.red("Missing requirements:"));for(let S of $.errors)console.log(H.yellow(` - ${S}`));if(!$.githubSSH.accessible)console.log(H.gray(" Run 'nemar auth setup-ssh' to configure SSH"));return}F.succeed("All prerequisites met");let B=C("Verifying GitHub CLI authentication...").start(),J=await r$(D.githubUsername);if(!J.authenticated){B.fail("GitHub CLI not authenticated"),console.log(H.red(` ${J.error}`)),console.log(),console.log("GitHub CLI is required for sandbox training. Install and authenticate:"),console.log(H.cyan(" brew install gh # or visit https://cli.github.com/")),console.log(H.cyan(" gh auth login"));return}if(D.githubUsername&&!J.matches)B.warn("GitHub CLI user mismatch"),console.log(H.yellow(` ${J.error}`)),console.log(),console.log("Your gh CLI is authenticated as a different GitHub account than your NEMAR account."),console.log("This may cause issues with repository access. To fix:"),console.log(H.cyan(` gh auth login # Login as ${D.githubUsername}`)),console.log(),console.log(H.yellow("WARNING: If upload fails with permission errors, this mismatch is the likely cause.")),console.log();else B.succeed(`GitHub CLI authenticated as ${J.username}`);console.log(),console.log(H.bold("Step 2/6: Generating test dataset..."));let Q=C("Creating minimal BIDS structure...").start(),X;try{let S=Tx();X=S.root;let I=Ox(S);Q.succeed(`Test dataset created (${h3(I)})`),console.log(H.gray(` Location: ${X}`))}catch(S){Q.fail("Failed to generate test dataset"),console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`));return}console.log(),console.log(H.bold("Step 3/6: Registering sandbox dataset..."));let Y=C("Creating dataset on NEMAR...").start(),G,W,q,E,V,Z;try{let S=await n$({name:"Sandbox Training Dataset",description:"Placeholder dataset for sandbox training",files:[{path:"sub-01/eeg/sub-01_task-rest_eeg.edf",size:512000,type:"data"},{path:"dataset_description.json",size:200,type:"metadata"},{path:"participants.tsv",size:50,type:"metadata"},{path:"README",size:500,type:"metadata"},{path:"sub-01/eeg/sub-01_task-rest_eeg.json",size:300,type:"metadata"}],sandbox:!0});G=S.dataset.dataset_id,W=S.dataset.ssh_url,q=S.dataset.github_url,E=S.s3_config,V=S.dataset.s3_prefix,Z=S.upload_urls||{},Y.succeed(`Sandbox dataset created: ${H.cyan(G)}`),console.log(H.gray(` GitHub: ${q}`)),await new Promise((I)=>setTimeout(I,1e4))}catch(S){if(Y.fail("Failed to create sandbox dataset"),S instanceof u)console.log(H.red(` ${S.message}`));else console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`));y1(X);return}let A=C("Accepting GitHub repository invitation...").start(),L=q?.match(/github\.com\/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/),U=L?L[1].replace(/\.git$/,""):null;if(!U){A.fail("Invalid GitHub repository URL from backend"),console.log(H.red(` Received: ${q||"(empty)"}`)),console.log(H.red(" Expected format: https://github.com/owner/repo")),console.log(),console.log("This may indicate a backend issue. Please contact support."),y1(X);return}let M=await t$(U);if(M.accepted)if(M.alreadyCollaborator)A.succeed("Already a collaborator on this repository");else A.succeed("GitHub invitation accepted");else A.warn("Could not auto-accept invitation"),console.log(H.yellow(` ${M.error}`)),console.log(),console.log("You may need to accept the invitation manually:"),console.log(H.cyan(` https://github.com/${U}/invitations`)),console.log();console.log(),console.log(H.bold("Step 4/6: Initializing repository..."));let N=C("Setting up git-annex...").start(),O=D.username&&D.email?{name:D.username,email:D.email}:void 0;try{await o$(X,{author:O}),await s$(X),await e$(X,W),N.succeed("Repository initialized")}catch(S){N.fail("Failed to initialize repository"),console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`)),y1(X);return}if(console.log(),console.log(H.bold("Step 5/6: Uploading to S3...")),Object.keys(Z).length===0)console.log(H.yellow(" No data files to upload (metadata only)"));else{let S=C("Uploading test data...").start();try{let p=0,zD=Object.keys(Z).length,HD=await _5(X,Z,{jobs:4,onProgress:(T)=>{if(T.status==="completed"||T.status==="failed")p++,S.text=`Uploading... ${p}/${zD} files`}});if(HD.failed.length>0){S.fail(`Upload failed for ${HD.failed.length} file(s)`);for(let T of HD.failed)console.log(H.red(` Failed: ${T}`));if(HD.error)console.log(H.red(` Error: ${HD.error}`));console.log(),console.log(H.yellow("Sandbox training aborted due to upload failures.")),console.log(H.gray("Please check your network connection and try again.")),y1(X);return}S.succeed(`Uploaded ${HD.uploaded} file(s)`)}catch(p){S.fail("Upload failed"),console.log(H.red(` ${p instanceof Error?p.message:"Unknown error"}`)),y1(X);return}let I=C("Registering file URLs...").start();try{let p={};for(let HD of Object.keys(Z))p[HD]=`${E.public_url}/${V}/objects/${HD}`;let zD=await DB(X,p);if(!zD.success){I.fail(`URL registration failed for ${zD.failed.length} file(s)`);for(let HD of zD.failed)console.log(H.red(` Failed: ${HD}`));console.log(),console.log(H.yellow("Sandbox training aborted due to URL registration failures.")),console.log(H.gray("This may indicate a git-annex configuration issue.")),y1(X);return}I.succeed(`Registered ${zD.registered} file URLs`)}catch(p){I.fail("Failed to register URLs"),console.log(H.red(` ${p instanceof Error?p.message:"Unknown error"}`)),y1(X);return}}console.log(),console.log(H.bold("Step 6/6: Pushing to GitHub..."));let x=C("Saving and pushing...").start();try{await y5(X,"Initial sandbox training upload",O),await x5(X),x.succeed("Pushed to GitHub")}catch(S){x.fail("Failed to push to GitHub"),console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`)),y1(X);return}let j=C("Finalizing...").start();try{await i$(G),await Cy(G),R8("sandboxCompleted",!0),R8("sandboxDatasetId",G),j.succeed("Sandbox training complete!")}catch(S){j.fail("Failed to finalize"),console.log(H.red(` ${S instanceof Error?S.message:"Unknown error"}`)),y1(X);return}y1(X),console.log(),console.log(H.green.bold("Congratulations! Sandbox training completed successfully.")),console.log(),console.log("Your setup is verified and you're ready to upload real datasets:"),console.log(H.cyan(" nemar dataset upload ./your-dataset")),console.log(),console.log(H.gray(`Sandbox dataset: ${G}`))}qB.command("status").description("Check sandbox training completion status").option("--refresh","Fetch latest status from server").action(async(D)=>{if(!xD()){console.log(H.red("Not authenticated")),console.log(H.gray("Run 'nemar auth login' first"));return}if(D.refresh){let F=C("Checking status...").start();try{let $=await jy();if(R8("sandboxCompleted",$.sandbox_completed),$.sandbox_dataset_id)R8("sandboxDatasetId",$.sandbox_dataset_id);if(F.stop(),$.sandbox_completed){if(console.log(H.green("Sandbox training: Completed")),console.log(H.gray(` Dataset ID: ${$.sandbox_dataset_id}`)),$.sandbox_completed_at)console.log(H.gray(` Completed: ${$.sandbox_completed_at}`))}else console.log(H.yellow("Sandbox training: Not completed")),console.log(),console.log("Run sandbox training with:"),console.log(H.cyan(" nemar sandbox"))}catch($){if(F.fail("Failed to check status"),$ instanceof u)console.log(H.red(` ${$.message}`))}}else{let F=oD();if(F.sandboxCompleted)console.log(H.green("Sandbox training: Completed")),console.log(H.gray(` Dataset ID: ${F.sandboxDatasetId}`));else console.log(H.yellow("Sandbox training: Not completed")),console.log(),console.log("Run sandbox training with:"),console.log(H.cyan(" nemar sandbox"))}});qB.command("reset").description("Reset sandbox training status for re-training").option(RD,jD).option(bD,uD).action(async(D)=>{if(!xD()){console.log(H.red("Not authenticated")),console.log(H.gray("Run 'nemar auth login' first"));return}if(!oD().sandboxCompleted){console.log(H.yellow("Sandbox training not yet completed")),console.log(H.gray("Nothing to reset"));return}let $=await OD("Reset sandbox training status? You will need to complete training again.",D);if($!=="confirmed"){console.log(H.gray($==="declined"?"Skipped":"Cancelled"));return}let B=C("Resetting sandbox status...").start();try{await Ry(),n2("sandboxCompleted"),n2("sandboxDatasetId"),B.succeed("Sandbox status reset"),console.log(),console.log("Run sandbox training again with:"),console.log(H.cyan(" nemar sandbox"))}catch(J){if(B.fail("Failed to reset"),J instanceof u)console.log(H.red(` ${J.message}`));else console.log(H.red(` ${J instanceof Error?J.message:"Unknown error"}`))}});var wx={name:"nemar-cli",version:"0.5.2-dev.274",description:"CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource) dataset management",type:"module",main:"dist/index.js",bin:{nemar:"dist/index.js"},scripts:{dev:"bun run src/index.ts",build:"bun build src/index.ts --outdir dist --target bun --minify && sed '1s|#!/usr/bin/env node|#!/usr/bin/env bun|' dist/index.js > dist/index.js.tmp && mv dist/index.js.tmp dist/index.js",test:"bun test",lint:"biome check src/","lint:fix":"biome check --fix src/",format:"biome format --write src/",typecheck:"tsc --noEmit",prepublishOnly:"bun run build","docs:generate":"bun run scripts/generate-docs.ts","docs:serve":"mkdocs serve","docs:build":"mkdocs build"},keywords:["nemar","bids","neuroimaging","eeg","emg","datalad","cli"],author:"NEMAR Team",license:"MIT",repository:{type:"git",url:"git+https://github.com/nemarOrg/nemar-cli.git"},bugs:{url:"https://github.com/nemarOrg/nemar-cli/issues"},homepage:"https://nemar-cli.pages.dev",engines:{bun:">=1.0.0"},files:["dist","README.md","LICENSE"],dependencies:{chalk:"^5.3.0",commander:"^12.1.0",conf:"^13.0.1",inquirer:"^9.2.15",ora:"^8.0.1",zod:"^3.23.8"},devDependencies:{"@biomejs/biome":"1.9.4","@types/bcryptjs":"^3.0.0","@types/bun":"latest","@types/inquirer":"^9.0.7",bcryptjs:"^3.0.3",typescript:"^5.5.4"}};var Ix=wx.version;var H1=new q0;H1.name("nemar").description(`CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource)
758
758
 
759
759
  NEMAR is a curated repository for neurophysiology data in BIDS format.
760
760
  This CLI provides tools for uploading, downloading, and managing datasets.`).version(Ix,"-v, --version","Output the current version").option("--no-color","Disable colored output").option("--verbose","Enable verbose output").addHelpText("after",`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nemar-cli",
3
- "version": "0.5.2-dev.273",
3
+ "version": "0.5.2-dev.274",
4
4
  "description": "CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource) dataset management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",