nemar-cli 0.3.7-PR87.032706e → 0.3.7-PR90.bec9bca

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 +27 -5
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -274,8 +274,8 @@ Description:
274
274
 
275
275
  Examples:
276
276
  $ nemar auth setup-ssh # Set up SSH access
277
- $ nemar auth setup-ssh --force # Regenerate key even if exists`).action(YGD);import{existsSync as LF}from"fs";import{basename as VGD,resolve as MF}from"path";var{spawn:AGD}=globalThis.Bun;var{spawn:P2}=globalThis.Bun;async function S$(){try{let D=P2({cmd:["deno","--version"],stdout:"pipe",stderr:"pipe"}),F=await new Response(D.stdout).text();await D.exited;let $=F.match(/deno\s+(\d+\.\d+\.\d+)/);return{installed:!0,version:$?$[1]:void 0}}catch{return{installed:!1}}}async function Gy(){try{let D=P2({cmd:["deno","run","-ERWN","jsr:@bids/validator","--version"],stdout:"pipe",stderr:"pipe"}),F=await new Response(D.stdout).text();await D.exited;let $=F.match(/(\d+\.\d+\.\d+)/);return $?$[1]:null}catch{return null}}async function S2(D,F={}){let $=["run","-ERWN","jsr:@bids/validator",D,"--json"];if(F.config)$.push("--config",F.config);if(F.ignoreWarnings)$.push("--ignoreWarnings");if(F.recursive)$.push("--recursive");if(F.prune)$.push("--prune");if(F.verbose)$.push("--verbose");let B=P2({cmd:["deno",...$],stdout:"pipe",stderr:"pipe"}),J=await new Response(B.stdout).text(),Q=await new Response(B.stderr).text(),X=await B.exited;if(X!==0&&!J.trim())throw Error(Q.includes("error:")?Q.split(`
278
- `).find((K)=>K.includes("error:"))||"Validation failed":`Validation failed with exit code ${X}`);let Y;try{Y=JSON.parse(J)}catch{throw Error(`Failed to parse validator output: ${J.slice(0,200)}`)}let G=Y.issues.issues||[],H=G.filter((K)=>K.severity==="error").length,W=G.filter((K)=>K.severity==="warning").length;return{valid:H===0,issues:G,codeMessages:Y.issues.codeMessages||{},summary:Y.summary,errorCount:H,warningCount:W}}function k2(D,F=!0){let $=[],B=D.issues.filter((Y)=>Y.severity==="error"),J=D.issues.filter((Y)=>Y.severity==="warning");if(D.valid)$.push(F?"\x1B[32m\u2713 Dataset is valid BIDS\x1B[0m":"\u2713 Dataset is valid BIDS");else $.push(F?"\x1B[31m\u2717 Dataset has validation errors\x1B[0m":"\u2717 Dataset has validation errors");if($.push(""),B.length>0){$.push(F?"\x1B[31mErrors:\x1B[0m":"Errors:");for(let Y of B){let G=F?`\x1B[31m${Y.code}\x1B[0m`:Y.code,H=D.codeMessages[Y.code]?.split(`
277
+ $ nemar auth setup-ssh --force # Regenerate key even if exists`).action(YGD);import{existsSync as LF}from"fs";import{basename as VGD,resolve as MF}from"path";var{spawn:AGD}=globalThis.Bun;var{spawn:P2}=globalThis.Bun;async function S$(){try{let D=P2({cmd:["deno","--version"],stdout:"pipe",stderr:"pipe"}),F=await new Response(D.stdout).text();await D.exited;let $=F.match(/deno\s+(\d+\.\d+\.\d+)/);return{installed:!0,version:$?$[1]:void 0}}catch{return{installed:!1}}}async function Gy(){try{let D=P2({cmd:["deno","run","-ERWN","jsr:@bids/validator","--version"],stdout:"pipe",stderr:"pipe"}),F=await new Response(D.stdout).text();await D.exited;let $=F.match(/(\d+\.\d+\.\d+)/);return $?$[1]:null}catch{return null}}async function S2(D,F={}){let $=["run","-ERWN","jsr:@bids/validator",D,"--json"],B=new Set(["config","ignoreWarnings","recursive","prune","verbose"]);if(F.config)$.push("--config",F.config);if(F.ignoreWarnings)$.push("--ignoreWarnings");if(F.recursive)$.push("--recursive");if(F.prune)$.push("--prune");if(F.verbose)$.push("--verbose");for(let[V,A]of Object.entries(F)){if(B.has(V))continue;let Z=V.replace(/([A-Z])/g,"-$1").toLowerCase();if(typeof A==="boolean"){if(A)$.push(`--${Z}`)}else if(Array.isArray(A))for(let U of A)$.push(`--${Z}`,String(U));else if(A!==void 0&&A!==null)$.push(`--${Z}`,String(A))}let J=P2({cmd:["deno",...$],stdout:"pipe",stderr:"pipe"}),Q=await new Response(J.stdout).text(),X=await new Response(J.stderr).text(),Y=await J.exited;if(Y!==0&&!Q.trim())throw Error(X.includes("error:")?X.split(`
278
+ `).find((V)=>V.includes("error:"))||"Validation failed":`Validation failed with exit code ${Y}`);let G;try{G=JSON.parse(Q)}catch{throw Error(`Failed to parse validator output: ${Q.slice(0,200)}`)}let H=G.issues.issues||[],W=H.filter((V)=>V.severity==="error").length,K=H.filter((V)=>V.severity==="warning").length;return{valid:W===0,issues:H,codeMessages:G.issues.codeMessages||{},summary:G.summary,errorCount:W,warningCount:K}}function k2(D,F=!0){let $=[],B=D.issues.filter((Y)=>Y.severity==="error"),J=D.issues.filter((Y)=>Y.severity==="warning");if(D.valid)$.push(F?"\x1B[32m\u2713 Dataset is valid BIDS\x1B[0m":"\u2713 Dataset is valid BIDS");else $.push(F?"\x1B[31m\u2717 Dataset has validation errors\x1B[0m":"\u2717 Dataset has validation errors");if($.push(""),B.length>0){$.push(F?"\x1B[31mErrors:\x1B[0m":"Errors:");for(let Y of B){let G=F?`\x1B[31m${Y.code}\x1B[0m`:Y.code,H=D.codeMessages[Y.code]?.split(`
279
279
  `)[0]||"";if($.push(` ${G}: ${H}`),$.push(` ${Y.location}`),Y.issueMessage)$.push(` ${Y.issueMessage.split(`
280
280
  `)[0]}`)}$.push("")}if(J.length>0){$.push(F?"\x1B[33mWarnings:\x1B[0m":"Warnings:");for(let Y of J){let G=F?`\x1B[33m${Y.code}\x1B[0m`:Y.code,H=D.codeMessages[Y.code]?.split(`
281
281
  `)[0]||"";$.push(` ${G}: ${H}`),$.push(` ${Y.location}`)}$.push("")}if($.push("Summary:"),$.push(` Files: ${D.summary.totalFiles}`),$.push(` Size: ${GGD(D.summary.size)}`),D.summary.subjects.length>0)$.push(` Subjects: ${D.summary.subjects.length}`);if(D.summary.sessions.length>0)$.push(` Sessions: ${D.summary.sessions.length}`);if(D.summary.tasks.length>0)$.push(` Tasks: ${D.summary.tasks.join(", ")}`);if(D.summary.modalities.length>0)$.push(` Modalities: ${D.summary.modalities.join(", ")}`);$.push(` Schema: BIDS ${D.summary.schemaVersion}`),$.push("");let Q=D.errorCount===1?"error":"errors",X=D.warningCount===1?"warning":"warnings";return $.push(`${D.errorCount} ${Q}, ${D.warningCount} ${X}`),$.join(`
@@ -299,7 +299,7 @@ Examples:
299
299
  $ nemar dataset invite johndoe nm000104 # Invite user as collaborator
300
300
 
301
301
  Learn More:
302
- https://nemar-cli.pages.dev/commands/dataset/`);gD.command("validate").description("Validate a BIDS dataset locally using the official BIDS validator").argument("[path]","Path to BIDS dataset directory",".").option("--ignore-warnings","Only report errors, not warnings").option("-c, --config <file>","Validation config file (.bidsvalidatorrc)").option("-r, --recursive","Validate derivatives subdirectories").option("--prune","Skip sourcedata and derivatives for faster validation").option("-v, --verbose","Show verbose output").option("--json","Output results as JSON (for scripting)").option("--version-info","Show BIDS validator version info").addHelpText("after",`
302
+ https://nemar-cli.pages.dev/commands/dataset/`);gD.command("validate").description("Validate a BIDS dataset locally using the official BIDS validator").argument("[path]","Path to BIDS dataset directory",".").option("--ignore-warnings","Only report errors, not warnings").option("-c, --config <file>","Validation config file (.bidsvalidatorrc)").option("-r, --recursive","Validate derivatives subdirectories").option("--prune","Skip sourcedata and derivatives for faster validation").option("-v, --verbose","Show verbose output").option("--json","Output results as JSON (for scripting)").option("--version-info","Show BIDS validator version info").allowUnknownOption().addHelpText("after",`
303
303
  Description:
304
304
  Validates a BIDS dataset using the official BIDS validator (via Deno).
305
305
  The validator checks dataset structure, file naming, and metadata.
@@ -307,6 +307,25 @@ Description:
307
307
  Requirements:
308
308
  Deno runtime must be installed: https://deno.com
309
309
 
310
+ Common Options:
311
+ -c, --config <file> Validation config file (.bidsvalidatorrc)
312
+ -r, --recursive Validate derivatives subdirectories
313
+ -p, --prune Skip sourcedata and derivatives
314
+ -v, --verbose Show verbose output
315
+ --ignore-warnings Only report errors, not warnings
316
+ --json Output results as JSON
317
+
318
+ Additional BIDS Validator Flags (pass-through):
319
+ --format <format> Output format: text, json, json_pp (default: text)
320
+ -s, --schema <URL-or-tag> Specify schema version to use
321
+ --max-rows <nrows> Max rows to validate in TSVs (default: 1000)
322
+ --ignore-nifti-headers Disregard NIfTI header content
323
+ --debug <level> Enable debug output (ERROR, WARN, INFO, DEBUG)
324
+ --dataset-types <types> Permitted types: raw, derivative, study
325
+ --blacklist-modalities <m> Error on specified modalities (mri, eeg, meg, etc)
326
+ -o, --outfile <file> File to write validation results to
327
+ --color, --no-color Enable/disable color output
328
+
310
329
  Exit Codes:
311
330
  0 - Dataset is valid
312
331
  1 - Dataset has errors or validation failed
@@ -315,7 +334,10 @@ Examples:
315
334
  $ nemar dataset validate # Validate current directory
316
335
  $ nemar dataset validate ./my-dataset # Validate specific path
317
336
  $ nemar dataset validate ./ds --prune # Fast validation (skip derivatives)
318
- $ nemar dataset validate ./ds --json > out.json`).action(async(D,F)=>{if(F.versionInfo){let Y=await S$();if(!Y.installed)console.log(E.red("Deno is not installed")),console.log("Install Deno: https://deno.com"),process.exit(1);let G=await Gy();console.log(`BIDS Validator: ${G||"unknown"}`),console.log(`Deno: ${Y.version||"unknown"}`);return}if(!D)console.log(E.red("Error: Missing required argument: path")),console.log("Usage: nemar dataset validate <path>"),process.exit(1);if(!(await S$()).installed)console.log(E.red("Error: Deno is required for BIDS validation")),console.log(),console.log("The BIDS validator runs on Deno runtime."),console.log("Install Deno with one of these commands:"),console.log(),console.log(E.cyan(" # macOS/Linux (curl)")),console.log(" curl -fsSL https://deno.land/install.sh | sh"),console.log(),console.log(E.cyan(" # macOS (Homebrew)")),console.log(" brew install deno"),console.log(),console.log(E.cyan(" # Windows (PowerShell)")),console.log(" irm https://deno.land/install.ps1 | iex"),console.log(),console.log("Learn more: https://docs.deno.com/runtime/getting_started/installation/"),process.exit(1);let B=MF(D);if(!LF(B))console.log(E.red(`Error: Path does not exist: ${B}`)),process.exit(1);let J=MF(B,"dataset_description.json");if(!LF(J))console.log(E.red("Error: Not a valid BIDS dataset")),console.log("Missing required file: dataset_description.json"),console.log(`Path: ${B}`),process.exit(1);let Q=C("Validating BIDS dataset...").start(),X;try{X=await S2(B,{config:F.config,ignoreWarnings:F.ignoreWarnings,recursive:F.recursive,prune:F.prune,verbose:F.verbose}),Q.succeed("Validation complete"),console.log()}catch(Y){Q.fail("Validation failed"),console.log(E.red(Y.message)),process.exit(1)}if(F.json)console.log(JSON.stringify(X,null,2));else console.log(k2(X));if(!X.valid)process.exit(1)});gD.command("upload").description("Upload a BIDS dataset to NEMAR").argument("<path>","Path to BIDS dataset directory").option("-n, --name <name>","Dataset name (defaults to directory name)").option("-d, --description <desc>","Dataset description").option("--skip-validation","Skip BIDS validation (not recommended)").option("--dry-run","Show what would be uploaded without doing it").option("-j, --jobs <number>","Parallel upload streams (default: 4)","4").option(xD,_D).option("--no","Skip confirmation and decline").addHelpText("after",`
337
+ $ nemar dataset validate ./ds --json > out.json
338
+ $ nemar dataset validate ./ds --format json_pp # Pretty JSON output
339
+ $ nemar dataset validate ./ds --debug INFO # Enable debug logging
340
+ $ nemar dataset validate ./ds --max-rows 0 # Validate headers only`).action(async(D,F,$)=>{if(F.versionInfo){let G=await S$();if(!G.installed)console.log(E.red("Deno is not installed")),console.log("Install Deno: https://deno.com"),process.exit(1);let H=await Gy();console.log(`BIDS Validator: ${H||"unknown"}`),console.log(`Deno: ${G.version||"unknown"}`);return}if(!D)console.log(E.red("Error: Missing required argument: path")),console.log("Usage: nemar dataset validate <path>"),process.exit(1);if(!(await S$()).installed)console.log(E.red("Error: Deno is required for BIDS validation")),console.log(),console.log("The BIDS validator runs on Deno runtime."),console.log("Install Deno with one of these commands:"),console.log(),console.log(E.cyan(" # macOS/Linux (curl)")),console.log(" curl -fsSL https://deno.land/install.sh | sh"),console.log(),console.log(E.cyan(" # macOS (Homebrew)")),console.log(" brew install deno"),console.log(),console.log(E.cyan(" # Windows (PowerShell)")),console.log(" irm https://deno.land/install.ps1 | iex"),console.log(),console.log("Learn more: https://docs.deno.com/runtime/getting_started/installation/"),process.exit(1);let J=MF(D);if(!LF(J))console.log(E.red(`Error: Path does not exist: ${J}`)),process.exit(1);let Q=MF(J,"dataset_description.json");if(!LF(Q))console.log(E.red("Error: Not a valid BIDS dataset")),console.log("Missing required file: dataset_description.json"),console.log(`Path: ${J}`),process.exit(1);let X=C("Validating BIDS dataset...").start(),Y;try{let G={config:F.config,ignoreWarnings:F.ignoreWarnings,recursive:F.recursive,prune:F.prune,verbose:F.verbose},H=new Set(["config","ignoreWarnings","recursive","prune","verbose","json","versionInfo"]);for(let[W,K]of Object.entries(F))if(!H.has(W))G[W]=K;Y=await S2(J,G),X.succeed("Validation complete"),console.log()}catch(G){X.fail("Validation failed"),console.log(E.red(G.message)),process.exit(1)}if(F.json)console.log(JSON.stringify(Y,null,2));else console.log(k2(Y));if(!Y.valid)process.exit(1)});gD.command("upload").description("Upload a BIDS dataset to NEMAR").argument("<path>","Path to BIDS dataset directory").option("-n, --name <name>","Dataset name (defaults to directory name)").option("-d, --description <desc>","Dataset description").option("--skip-validation","Skip BIDS validation (not recommended)").option("--dry-run","Show what would be uploaded without doing it").option("-j, --jobs <number>","Parallel upload streams (default: 4)","4").option(xD,_D).option("--no","Skip confirmation and decline").addHelpText("after",`
319
341
  Description:
320
342
  Upload a BIDS dataset to NEMAR. The dataset will be validated, assigned
321
343
  a unique ID (nm000XXX), and stored on GitHub (metadata) and S3 (data files).
@@ -566,7 +588,7 @@ Examples:
566
588
  $ nemar sandbox # Run sandbox training
567
589
  $ nemar sandbox status # Check if training is completed
568
590
  $ nemar sandbox reset # Reset for re-training
569
- `).action(CGD);async function CGD(){if(console.log(),console.log(E.bold("NEMAR Sandbox Training")),console.log(E.gray("Verify your setup and learn the upload workflow")),console.log(),!PD()){console.log(E.red("Not authenticated")),console.log(E.gray("Run 'nemar auth login' first"));return}let D=eD();if(D.sandboxCompleted){console.log(E.green("Sandbox training already completed!")),console.log(E.gray(`Dataset ID: ${D.sandboxDatasetId}`)),console.log(),console.log("You can upload real datasets with:"),console.log(E.cyan(" nemar dataset upload ./your-dataset")),console.log(),console.log(E.gray("To re-run training, use: nemar sandbox reset"));return}console.log(E.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(E.red("Missing requirements:"));for(let P of $.errors)console.log(E.yellow(` - ${P}`));if(!$.githubSSH.accessible)console.log(E.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 L$(D.githubUsername);if(!J.authenticated){B.fail("GitHub CLI not authenticated"),console.log(E.red(` ${J.error}`)),console.log(),console.log("GitHub CLI is required for sandbox training. Install and authenticate:"),console.log(E.cyan(" brew install gh # or visit https://cli.github.com/")),console.log(E.cyan(" gh auth login"));return}if(D.githubUsername&&!J.matches)B.warn("GitHub CLI user mismatch"),console.log(E.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(E.cyan(` gh auth login # Login as ${D.githubUsername}`)),console.log(),console.log(E.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(E.bold("Step 2/6: Generating test dataset..."));let Q=C("Creating minimal BIDS structure...").start(),X;try{let P=Vy();X=P.root;let VD=Ay(P);Q.succeed(`Test dataset created (${C$(VD)})`),console.log(E.gray(` Location: ${X}`))}catch(P){Q.fail("Failed to generate test dataset"),console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`));return}console.log(),console.log(E.bold("Step 3/6: Registering sandbox dataset..."));let Y=C("Creating dataset on NEMAR...").start(),G,H,W,K,V,A;try{let P=await z$({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=P.dataset.dataset_id,H=P.dataset.ssh_url,W=P.dataset.github_url,K=P.s3_config,V=P.dataset.s3_prefix,A=P.upload_urls||{},Y.succeed(`Sandbox dataset created: ${E.cyan(G)}`),console.log(E.gray(` GitHub: ${W}`)),await new Promise((VD)=>setTimeout(VD,1e4))}catch(P){if(Y.fail("Failed to create sandbox dataset"),P instanceof u)console.log(E.red(` ${P.message}`));else console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`));A1(X);return}let Z=C("Accepting GitHub repository invitation...").start(),U=W?.match(/github\.com\/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/),L=U?U[1].replace(/\.git$/,""):null;if(!L){Z.fail("Invalid GitHub repository URL from backend"),console.log(E.red(` Received: ${W||"(empty)"}`)),console.log(E.red(" Expected format: https://github.com/owner/repo")),console.log(),console.log("This may indicate a backend issue. Please contact support."),A1(X);return}let M=await M$(L);if(M.accepted)if(M.alreadyCollaborator)Z.succeed("Already a collaborator on this repository");else Z.succeed("GitHub invitation accepted");else Z.warn("Could not auto-accept invitation"),console.log(E.yellow(` ${M.error}`)),console.log(),console.log("You may need to accept the invitation manually:"),console.log(E.cyan(` https://github.com/${L}/invitations`)),console.log();console.log(),console.log(E.bold("Step 4/6: Initializing repository..."));let w=C("Setting up git-annex...").start(),I=D.username&&D.email?{name:D.username,email:D.email}:void 0;try{await Z$(X,{author:I}),await U$(X),await N$(X,H),w.succeed("Repository initialized")}catch(P){w.fail("Failed to initialize repository"),console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`)),A1(X);return}if(console.log(),console.log(E.bold("Step 5/6: Uploading to S3...")),Object.keys(A).length===0)console.log(E.yellow(" No data files to upload (metadata only)"));else{let P=C("Uploading test data...").start();try{let yD=0,g=Object.keys(A).length,ED=await R$(X,A,{jobs:4,onProgress:(WD)=>{if(WD.status==="completed"||WD.status==="failed")yD++,P.text=`Uploading... ${yD}/${g} files`}});if(ED.failed.length>0){P.fail(`Upload failed for ${ED.failed.length} file(s)`);for(let WD of ED.failed)console.log(E.red(` Failed: ${WD}`));if(ED.error)console.log(E.red(` Error: ${ED.error}`));console.log(),console.log(E.yellow("Sandbox training aborted due to upload failures.")),console.log(E.gray("Please check your network connection and try again.")),A1(X);return}P.succeed(`Uploaded ${ED.uploaded} file(s)`)}catch(yD){P.fail("Upload failed"),console.log(E.red(` ${yD instanceof Error?yD.message:"Unknown error"}`)),A1(X);return}let VD=C("Registering file URLs...").start();try{let yD={};for(let ED of Object.keys(A))yD[ED]=`${K.public_url}/${V}/${ED}`;let g=await w$(X,yD);if(!g.success){VD.fail(`URL registration failed for ${g.failed.length} file(s)`);for(let ED of g.failed)console.log(E.red(` Failed: ${ED}`));console.log(),console.log(E.yellow("Sandbox training aborted due to URL registration failures.")),console.log(E.gray("This may indicate a git-annex configuration issue.")),A1(X);return}VD.succeed(`Registered ${g.registered} file URLs`)}catch(yD){VD.fail("Failed to register URLs"),console.log(E.red(` ${yD instanceof Error?yD.message:"Unknown error"}`)),A1(X);return}}console.log(),console.log(E.bold("Step 6/6: Pushing to GitHub..."));let b=C("Saving and pushing...").start();try{await W5(X,"Initial sandbox training upload",I),await E5(X),b.succeed("Pushed to GitHub")}catch(P){b.fail("Failed to push to GitHub"),console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`)),A1(X);return}let T=C("Finalizing...").start();try{await V$(G),await Iv(G),q0("sandboxCompleted",!0),q0("sandboxDatasetId",G),T.succeed("Sandbox training complete!")}catch(P){T.fail("Failed to finalize"),console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`)),A1(X);return}A1(X),console.log(),console.log(E.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(E.cyan(" nemar dataset upload ./your-dataset")),console.log(),console.log(E.gray(`Sandbox dataset: ${G}`))}v$.command("status").description("Check sandbox training completion status").option("--refresh","Fetch latest status from server").action(async(D)=>{if(!PD()){console.log(E.red("Not authenticated")),console.log(E.gray("Run 'nemar auth login' first"));return}if(D.refresh){let F=C("Checking status...").start();try{let $=await Sv();if(q0("sandboxCompleted",$.sandbox_completed),$.sandbox_dataset_id)q0("sandboxDatasetId",$.sandbox_dataset_id);if(F.stop(),$.sandbox_completed){if(console.log(E.green("Sandbox training: Completed")),console.log(E.gray(` Dataset ID: ${$.sandbox_dataset_id}`)),$.sandbox_completed_at)console.log(E.gray(` Completed: ${$.sandbox_completed_at}`))}else console.log(E.yellow("Sandbox training: Not completed")),console.log(),console.log("Run sandbox training with:"),console.log(E.cyan(" nemar sandbox"))}catch($){if(F.fail("Failed to check status"),$ instanceof u)console.log(E.red(` ${$.message}`))}}else{let F=eD();if(F.sandboxCompleted)console.log(E.green("Sandbox training: Completed")),console.log(E.gray(` Dataset ID: ${F.sandboxDatasetId}`));else console.log(E.yellow("Sandbox training: Not completed")),console.log(),console.log("Run sandbox training with:"),console.log(E.cyan(" nemar sandbox"))}});v$.command("reset").description("Reset sandbox training status for re-training").option(xD,_D).option(lD,dD).action(async(D)=>{if(!PD()){console.log(E.red("Not authenticated")),console.log(E.gray("Run 'nemar auth login' first"));return}if(!eD().sandboxCompleted){console.log(E.yellow("Sandbox training not yet completed")),console.log(E.gray("Nothing to reset"));return}let $=await mD("Reset sandbox training status? You will need to complete training again.",D);if($!=="confirmed"){console.log(E.gray($==="declined"?"Skipped":"Cancelled"));return}let B=C("Resetting sandbox status...").start();try{await Pv(),z2("sandboxCompleted"),z2("sandboxDatasetId"),B.succeed("Sandbox status reset"),console.log(),console.log("Run sandbox training again with:"),console.log(E.cyan(" nemar sandbox"))}catch(J){if(B.fail("Failed to reset"),J instanceof u)console.log(E.red(` ${J.message}`));else console.log(E.red(` ${J instanceof Error?J.message:"Unknown error"}`))}});var Zy={name:"nemar-cli",version:"0.3.7-PR87.032706e",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/nemarDatasets/nemar-cli.git"},bugs:{url:"https://github.com/nemarDatasets/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 Uy=Zy.version;var Z1=new D0;Z1.name("nemar").description(`CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource)
591
+ `).action(CGD);async function CGD(){if(console.log(),console.log(E.bold("NEMAR Sandbox Training")),console.log(E.gray("Verify your setup and learn the upload workflow")),console.log(),!PD()){console.log(E.red("Not authenticated")),console.log(E.gray("Run 'nemar auth login' first"));return}let D=eD();if(D.sandboxCompleted){console.log(E.green("Sandbox training already completed!")),console.log(E.gray(`Dataset ID: ${D.sandboxDatasetId}`)),console.log(),console.log("You can upload real datasets with:"),console.log(E.cyan(" nemar dataset upload ./your-dataset")),console.log(),console.log(E.gray("To re-run training, use: nemar sandbox reset"));return}console.log(E.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(E.red("Missing requirements:"));for(let P of $.errors)console.log(E.yellow(` - ${P}`));if(!$.githubSSH.accessible)console.log(E.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 L$(D.githubUsername);if(!J.authenticated){B.fail("GitHub CLI not authenticated"),console.log(E.red(` ${J.error}`)),console.log(),console.log("GitHub CLI is required for sandbox training. Install and authenticate:"),console.log(E.cyan(" brew install gh # or visit https://cli.github.com/")),console.log(E.cyan(" gh auth login"));return}if(D.githubUsername&&!J.matches)B.warn("GitHub CLI user mismatch"),console.log(E.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(E.cyan(` gh auth login # Login as ${D.githubUsername}`)),console.log(),console.log(E.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(E.bold("Step 2/6: Generating test dataset..."));let Q=C("Creating minimal BIDS structure...").start(),X;try{let P=Vy();X=P.root;let VD=Ay(P);Q.succeed(`Test dataset created (${C$(VD)})`),console.log(E.gray(` Location: ${X}`))}catch(P){Q.fail("Failed to generate test dataset"),console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`));return}console.log(),console.log(E.bold("Step 3/6: Registering sandbox dataset..."));let Y=C("Creating dataset on NEMAR...").start(),G,H,W,K,V,A;try{let P=await z$({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=P.dataset.dataset_id,H=P.dataset.ssh_url,W=P.dataset.github_url,K=P.s3_config,V=P.dataset.s3_prefix,A=P.upload_urls||{},Y.succeed(`Sandbox dataset created: ${E.cyan(G)}`),console.log(E.gray(` GitHub: ${W}`)),await new Promise((VD)=>setTimeout(VD,1e4))}catch(P){if(Y.fail("Failed to create sandbox dataset"),P instanceof u)console.log(E.red(` ${P.message}`));else console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`));A1(X);return}let Z=C("Accepting GitHub repository invitation...").start(),U=W?.match(/github\.com\/([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)/),L=U?U[1].replace(/\.git$/,""):null;if(!L){Z.fail("Invalid GitHub repository URL from backend"),console.log(E.red(` Received: ${W||"(empty)"}`)),console.log(E.red(" Expected format: https://github.com/owner/repo")),console.log(),console.log("This may indicate a backend issue. Please contact support."),A1(X);return}let M=await M$(L);if(M.accepted)if(M.alreadyCollaborator)Z.succeed("Already a collaborator on this repository");else Z.succeed("GitHub invitation accepted");else Z.warn("Could not auto-accept invitation"),console.log(E.yellow(` ${M.error}`)),console.log(),console.log("You may need to accept the invitation manually:"),console.log(E.cyan(` https://github.com/${L}/invitations`)),console.log();console.log(),console.log(E.bold("Step 4/6: Initializing repository..."));let w=C("Setting up git-annex...").start(),I=D.username&&D.email?{name:D.username,email:D.email}:void 0;try{await Z$(X,{author:I}),await U$(X),await N$(X,H),w.succeed("Repository initialized")}catch(P){w.fail("Failed to initialize repository"),console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`)),A1(X);return}if(console.log(),console.log(E.bold("Step 5/6: Uploading to S3...")),Object.keys(A).length===0)console.log(E.yellow(" No data files to upload (metadata only)"));else{let P=C("Uploading test data...").start();try{let yD=0,g=Object.keys(A).length,ED=await R$(X,A,{jobs:4,onProgress:(WD)=>{if(WD.status==="completed"||WD.status==="failed")yD++,P.text=`Uploading... ${yD}/${g} files`}});if(ED.failed.length>0){P.fail(`Upload failed for ${ED.failed.length} file(s)`);for(let WD of ED.failed)console.log(E.red(` Failed: ${WD}`));if(ED.error)console.log(E.red(` Error: ${ED.error}`));console.log(),console.log(E.yellow("Sandbox training aborted due to upload failures.")),console.log(E.gray("Please check your network connection and try again.")),A1(X);return}P.succeed(`Uploaded ${ED.uploaded} file(s)`)}catch(yD){P.fail("Upload failed"),console.log(E.red(` ${yD instanceof Error?yD.message:"Unknown error"}`)),A1(X);return}let VD=C("Registering file URLs...").start();try{let yD={};for(let ED of Object.keys(A))yD[ED]=`${K.public_url}/${V}/${ED}`;let g=await w$(X,yD);if(!g.success){VD.fail(`URL registration failed for ${g.failed.length} file(s)`);for(let ED of g.failed)console.log(E.red(` Failed: ${ED}`));console.log(),console.log(E.yellow("Sandbox training aborted due to URL registration failures.")),console.log(E.gray("This may indicate a git-annex configuration issue.")),A1(X);return}VD.succeed(`Registered ${g.registered} file URLs`)}catch(yD){VD.fail("Failed to register URLs"),console.log(E.red(` ${yD instanceof Error?yD.message:"Unknown error"}`)),A1(X);return}}console.log(),console.log(E.bold("Step 6/6: Pushing to GitHub..."));let b=C("Saving and pushing...").start();try{await W5(X,"Initial sandbox training upload",I),await E5(X),b.succeed("Pushed to GitHub")}catch(P){b.fail("Failed to push to GitHub"),console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`)),A1(X);return}let T=C("Finalizing...").start();try{await V$(G),await Iv(G),q0("sandboxCompleted",!0),q0("sandboxDatasetId",G),T.succeed("Sandbox training complete!")}catch(P){T.fail("Failed to finalize"),console.log(E.red(` ${P instanceof Error?P.message:"Unknown error"}`)),A1(X);return}A1(X),console.log(),console.log(E.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(E.cyan(" nemar dataset upload ./your-dataset")),console.log(),console.log(E.gray(`Sandbox dataset: ${G}`))}v$.command("status").description("Check sandbox training completion status").option("--refresh","Fetch latest status from server").action(async(D)=>{if(!PD()){console.log(E.red("Not authenticated")),console.log(E.gray("Run 'nemar auth login' first"));return}if(D.refresh){let F=C("Checking status...").start();try{let $=await Sv();if(q0("sandboxCompleted",$.sandbox_completed),$.sandbox_dataset_id)q0("sandboxDatasetId",$.sandbox_dataset_id);if(F.stop(),$.sandbox_completed){if(console.log(E.green("Sandbox training: Completed")),console.log(E.gray(` Dataset ID: ${$.sandbox_dataset_id}`)),$.sandbox_completed_at)console.log(E.gray(` Completed: ${$.sandbox_completed_at}`))}else console.log(E.yellow("Sandbox training: Not completed")),console.log(),console.log("Run sandbox training with:"),console.log(E.cyan(" nemar sandbox"))}catch($){if(F.fail("Failed to check status"),$ instanceof u)console.log(E.red(` ${$.message}`))}}else{let F=eD();if(F.sandboxCompleted)console.log(E.green("Sandbox training: Completed")),console.log(E.gray(` Dataset ID: ${F.sandboxDatasetId}`));else console.log(E.yellow("Sandbox training: Not completed")),console.log(),console.log("Run sandbox training with:"),console.log(E.cyan(" nemar sandbox"))}});v$.command("reset").description("Reset sandbox training status for re-training").option(xD,_D).option(lD,dD).action(async(D)=>{if(!PD()){console.log(E.red("Not authenticated")),console.log(E.gray("Run 'nemar auth login' first"));return}if(!eD().sandboxCompleted){console.log(E.yellow("Sandbox training not yet completed")),console.log(E.gray("Nothing to reset"));return}let $=await mD("Reset sandbox training status? You will need to complete training again.",D);if($!=="confirmed"){console.log(E.gray($==="declined"?"Skipped":"Cancelled"));return}let B=C("Resetting sandbox status...").start();try{await Pv(),z2("sandboxCompleted"),z2("sandboxDatasetId"),B.succeed("Sandbox status reset"),console.log(),console.log("Run sandbox training again with:"),console.log(E.cyan(" nemar sandbox"))}catch(J){if(B.fail("Failed to reset"),J instanceof u)console.log(E.red(` ${J.message}`));else console.log(E.red(` ${J instanceof Error?J.message:"Unknown error"}`))}});var Zy={name:"nemar-cli",version:"0.3.7-PR90.bec9bca",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/nemarDatasets/nemar-cli.git"},bugs:{url:"https://github.com/nemarDatasets/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 Uy=Zy.version;var Z1=new D0;Z1.name("nemar").description(`CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource)
570
592
 
571
593
  NEMAR is a curated repository for neurophysiology data in BIDS format.
572
594
  This CLI provides tools for uploading, downloading, and managing datasets.`).version(Uy,"-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.3.7-PR87.032706e",
3
+ "version": "0.3.7-PR90.bec9bca",
4
4
  "description": "CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource) dataset management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",