nemar-cli 0.9.16-PR1262.78ffbfa → 0.9.16-PR1263.7a841c6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +12 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -101,7 +101,7 @@ ${t}`,E1e,S1e,$1e=(e,t,r)=>{let n=r===""?"":`with ${r.trim()}() `,i=D1e.bind(nul
|
|
|
101
101
|
`)}var aw=!1;var{spawn:ENe}=globalThis.Bun;async function R(e,t={}){let r={...process.env,GIT_TERMINAL_PROMPT:"0",...t.env};for(let u of t.unsetEnv??[])delete r[u];let n=ENe({cmd:e,cwd:t.cwd,stdout:"pipe",stderr:"pipe",env:r}),i=!1,s;if(t.timeout)s=setTimeout(()=>{i=!0,n.kill()},t.timeout);if(cw()){let u=t.cwd?` (cwd=${t.cwd})`:"";Ld(l.dim(`$ ${e.join(" ")}${u}`))}let o=await new Response(n.stdout).text(),a=await new Response(n.stderr).text(),c=await n.exited;if(s)clearTimeout(s);if(cw()){if(o.trim())Ld(l.dim(o.trimEnd()));if(a.trim())Ld(l.yellow(a.trimEnd()));Ld(l.dim(`(exit ${c})`))}if(i)return{stdout:o,stderr:a||`Command timed out after ${Math.round(t.timeout/1000)}s`,exitCode:c??1,timedOut:!0};return{stdout:o,stderr:a,exitCode:c,timedOut:!1}}var Xt=O(()=>{Je()});function $Ne(){if(process.platform==="darwin")return"macos";if(process.platform==="win32")return"windows";return"linux"}function dG(e,t=$Ne()){return e.installInstructions[t]}function CNe(e){let t=e.match(/(\d+\.\d+(?:\.\d+)?)/);return t?t[1]:void 0}function ONe(){let e=Number(process.env.TEST_PROBE_TIMEOUT_MS);return Number.isFinite(e)&&e>0?e:RNe}async function fG(e){try{let{exitCode:t,stdout:r,timedOut:n}=await R(e.cmd,{timeout:ONe()});if(n)return{available:!1,timedOut:!0};if(t!==0)return{available:!1};return{available:!0,version:CNe(r)}}catch(t){let r=t instanceof Error?t.message:String(t);if(!r.includes("ENOENT")&&!r.includes("not found"))console.error(` Warning: ${e.name} check failed unexpectedly: ${r}`);return{available:!1}}}async function Ud(){let e=Object.entries(lG);return Promise.all(e.map(async([t,r])=>{let{available:n,version:i,timedOut:s}=await fG(r);return{key:t,name:r.name,purpose:r.purpose,required:r.required,available:n,version:i,timedOut:s,installInstruction:dG(r)}}))}async function pG(){let t=(await Ud()).filter((r)=>r.required&&!r.available);if(t.length===0)return[];console.warn(`
|
|
102
102
|
Heads up: some tools NEMAR needs for uploading/validating aren't installed yet:`);for(let r of t)console.warn(` - ${r.name} (${r.purpose})`),console.warn(` Install: ${r.installInstruction}`);return console.warn("Run `nemar doctor` anytime to re-check. You can finish signup without them.\n"),t}async function Jo(e){let r=SNe[e].map((o)=>lG[o]).filter(Boolean),n=await Promise.all(r.map(async(o)=>{let a=await fG(o);return{check:o,available:a.available,timedOut:a.timedOut}}));for(let o of n)if(o.timedOut&&o.check.required)console.warn(`Warning: ${o.check.name} did not respond to a version check in time; continuing anyway.`);let i=n.filter((o)=>!o.available&&!o.timedOut&&o.check.required).map((o)=>({tool:o.check.name,installInstruction:dG(o.check)}));if(i.length===0)return;let s=[`
|
|
103
103
|
Missing required tools:`];for(let o of i)s.push(` ${o.tool} not installed`),s.push(` Install: ${o.installInstruction}`);throw Error(s.join(`
|
|
104
|
-
`))}var lG,SNe,RNe=4000;var Vd=O(()=>{Xt();lG={git:{name:"git",purpose:"version control for dataset metadata",cmd:["git","--version"],required:!0,installInstructions:{macos:"brew install git (or install Xcode Command Line Tools)",linux:"sudo apt-get install git OR sudo dnf install git",windows:"winget install Git.Git (or https://git-scm.com/download/win)"}},"git-annex":{name:"git-annex",purpose:"large-file data transfer (upload/download)",cmd:["git-annex","version"],required:!0,installInstructions:{macos:"brew install git-annex",linux:"sudo apt-get install git-annex OR sudo dnf install git-annex",windows:"Download the installer from https://git-annex.branchable.com/install/Windows/"}},gh:{name:"GitHub CLI (gh)",purpose:"GitHub authentication + repository operations",cmd:["gh","--version"],required:!0,installInstructions:{macos:"brew install gh",linux:"See https://cli.github.com/manual/installation",windows:"winget install GitHub.cli (or: scoop install gh)"}},aws:{name:"AWS CLI",purpose:"S3 data upload/download",cmd:["aws","--version"],required:!0,installInstructions:{macos:"brew install awscli",linux:"pip install awscli OR sudo snap install aws-cli --classic",windows:"winget install Amazon.AWSCLI (or the MSI from https://aws.amazon.com/cli/)"}},deno:{name:"Deno",purpose:"BIDS validation",cmd:["deno","--version"],required:!0,installInstructions:{macos:"brew install deno (or: curl -fsSL https://deno.land/install.sh | sh)",linux:"curl -fsSL https://deno.land/install.sh | sh",windows:"winget install DenoLand.Deno (or: irm https://deno.land/install.ps1 | iex)"}}},SNe={upload:["git-annex","gh","aws","deno"],download:["gh"],clone:["git","gh"],push:["git-annex","gh"],publish:["gh","aws"],update:["git","gh"],release:["git","gh"]}});var hG;var mG=O(()=>{hG={name:"nemar-cli",version:"0.9.16-
|
|
104
|
+
`))}var lG,SNe,RNe=4000;var Vd=O(()=>{Xt();lG={git:{name:"git",purpose:"version control for dataset metadata",cmd:["git","--version"],required:!0,installInstructions:{macos:"brew install git (or install Xcode Command Line Tools)",linux:"sudo apt-get install git OR sudo dnf install git",windows:"winget install Git.Git (or https://git-scm.com/download/win)"}},"git-annex":{name:"git-annex",purpose:"large-file data transfer (upload/download)",cmd:["git-annex","version"],required:!0,installInstructions:{macos:"brew install git-annex",linux:"sudo apt-get install git-annex OR sudo dnf install git-annex",windows:"Download the installer from https://git-annex.branchable.com/install/Windows/"}},gh:{name:"GitHub CLI (gh)",purpose:"GitHub authentication + repository operations",cmd:["gh","--version"],required:!0,installInstructions:{macos:"brew install gh",linux:"See https://cli.github.com/manual/installation",windows:"winget install GitHub.cli (or: scoop install gh)"}},aws:{name:"AWS CLI",purpose:"S3 data upload/download",cmd:["aws","--version"],required:!0,installInstructions:{macos:"brew install awscli",linux:"pip install awscli OR sudo snap install aws-cli --classic",windows:"winget install Amazon.AWSCLI (or the MSI from https://aws.amazon.com/cli/)"}},deno:{name:"Deno",purpose:"BIDS validation",cmd:["deno","--version"],required:!0,installInstructions:{macos:"brew install deno (or: curl -fsSL https://deno.land/install.sh | sh)",linux:"curl -fsSL https://deno.land/install.sh | sh",windows:"winget install DenoLand.Deno (or: irm https://deno.land/install.ps1 | iex)"}}},SNe={upload:["git-annex","gh","aws","deno"],download:["gh"],clone:["git","gh"],push:["git-annex","gh"],publish:["gh","aws"],update:["git","gh"],release:["git","gh"]}});var hG;var mG=O(()=>{hG={name:"nemar-cli",version:"0.9.16-PR1263.7a841c6",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 && tsc --noEmit -p backend","generate-openapi":"bun run scripts/generate-openapi.ts",prepublishOnly:"bun run build",prepare:"husky",postinstall:"bun scripts/postinstall-check.mjs",deploy:"wrangler deploy",preview:"wrangler dev"},keywords:["nemar","bids","neuroimaging","eeg","emg","datalad","cli"],author:"NEMAR Team",license:"CC-BY-NC-ND-4.0",repository:{type:"git",url:"git+https://github.com/nemarOrg/nemar-cli.git"},bugs:{url:"https://github.com/nemarOrg/nemar-cli/issues"},homepage:"https://docs.nemar.org",engines:{bun:">=1.0.0"},files:["dist","scripts/postinstall-check.mjs","scripts/recover-datasets.json","scripts/withdrawn-datasets.json","README.md","LICENSE"],dependencies:{chalk:"^5.3.0",commander:"^12.1.0",conf:"^13.0.1",inquirer:"^9.2.15",ora:"^8.0.1",semver:"^7.8.5",zod:"^3.23.8"},"lint-staged":{"src/**/*.{ts,tsx}":["biome check --fix"],"backend/src/**/*.{ts,tsx}":["biome check --fix"]},devDependencies:{"@asteasolutions/zod-to-openapi":"^7.3.4","@biomejs/biome":"1.9.4","@types/bun":"latest","@types/inquirer":"^9.0.7","@types/semver":"^7.8.0",ajv:"^8.20.0","ajv-formats":"^3.0.1",bcryptjs:"^3.0.3",husky:"^9.1.7","lint-staged":"^16.3.2",typescript:"^5.5.4",wrangler:"^4.70.0",yaml:"^2.8.4"}}});var Yi;var zd=O(()=>{mG();Yi=hG.version});import{mkdirSync as ANe,readdirSync as kNe,unlinkSync as PNe,writeFileSync as INe}from"fs";import{join as uw}from"path";function _G(){zc=!0}function Xo(){return zc}function vG(e,t=process.env){return e.includes("--debug")||t.NEMAR_DEBUG==="1"}function Gd(e){bG=e,Xs(`Step: ${e}`)}function wG(){xG=!0}function DG(){return xG}function Xs(e){if(zc)process.stderr.write(`[debug] ${e}
|
|
105
105
|
`)}function jNe(e){let t={};for(let[r,n]of Object.entries(e))t[r]=qNe.has(r.toLowerCase())?"[REDACTED]":n;return t}function SG(e){let t=e.indexOf("@");if(t<=0)return"***";return`${e[0]}***${e.slice(t)}`}function lw(e,t,r=0){if(r>BNe)return"[REDACTED: depth]";if(Array.isArray(t))return t.map((n)=>lw(e,n,r+1));if(t&&typeof t==="object"){let n={};for(let[i,s]of Object.entries(t))n[i]=lw(i,s,r+1);return n}if(typeof t==="string"){if(MNe.test(e))return"[REDACTED]";return t.replace(EG,SG)}return t}function dw(e){return e.replace(LNe,"Bearer [REDACTED]").replace(UNe,"[REDACTED_AWS_KEY]").replace(VNe,"$1[REDACTED]").replace(zNe,"$1$2=[REDACTED]").replace(EG,SG)}function HNe(e){return dw(e)}function gG(e,t=NNe){if(e===void 0)return;let r=e;try{let n=JSON.parse(e);r=JSON.stringify(lw("",n))}catch{}if(r=dw(r),Buffer.byteLength(r,"utf8")>t)r=`${Buffer.from(r,"utf8").subarray(0,t).toString("utf8")}... [truncated]`;return r}function p0(e){if(!zc)return;let t=HNe(e.url);try{let r={...e,url:t,requestHeaders:e.requestHeaders?jNe(e.requestHeaders):void 0,requestBody:gG(e.requestBody),responseBody:gG(e.responseBody)};f0.push(r),Xs(`${e.method} ${t} -> ${e.status??"ERROR"} (${e.durationMs}ms)`)}catch(r){let n=N(r);f0.push({method:e.method,url:t,status:e.status,durationMs:e.durationMs,note:`redaction failed: ${n}`}),Xs(`redaction failed for ${e.method} ${t}: ${n}`)}}async function GNe(){let e=[];try{let t=process.versions.bun?`Bun ${process.versions.bun}`:`Node ${process.version}`;e.push(`CLI version: ${Yi}`),e.push(`OS/Arch: ${process.platform}/${process.arch}`),e.push(`Runtime: ${t}`)}catch(t){e.push(`(could not read CLI/OS/runtime info: ${N(t)})`)}e.push(""),e.push("Active account:");try{let t=mt();if(t.apiKey){if(t.username)e.push(` Username: ${t.username}`);if(e.push(` API URL: ${t.apiUrl}`),t.role)e.push(` Role: ${t.role}`);e.push(` Sandbox completed: ${!!t.sandboxCompleted}`)}else e.push(" (not authenticated)")}catch(t){e.push(` (could not read account config: ${N(t)})`)}e.push(""),e.push("External tools:");try{let t=await Ud();for(let r of t){let n=r.available?r.version??"installed":r.timedOut?"(timed out)":"not installed";e.push(` ${r.name}: ${n}`)}}catch(t){e.push(` (could not probe external tools: ${N(t)})`)}return e}async function fw(){return(await GNe()).join(`
|
|
106
106
|
`)}async function $G(){if(!zc||d0!==void 0)return;try{d0=await fw()}catch(e){let t=N(e);d0=`(could not build environment report: ${t})`,Xs(`environment snapshot failed: ${t}`)}}function CG(e){if(yG.has(e))return{flag:e};for(let t of yG){if(!e.startsWith(t)||e.length<=t.length)continue;if(t.length===2)return{flag:t,kind:"short-attached"};if(e[t.length]==="=")return{flag:t,kind:"long-eq"}}return null}function WNe(e){let t=[];for(let r=0;r<e.length;r++){let n=e[r],i=CG(n);if(!i){t.push(n);continue}if(i.kind==="long-eq")t.push(`${i.flag}=[REDACTED]`);else if(i.kind==="short-attached")t.push(`${i.flag}[REDACTED]`);else if(t.push(n),r+1<e.length)t.push("[REDACTED]"),r++}return t}function KNe(e){let t=[];for(let r=0;r<e.length;r++){let n=e[r],i=CG(n);if(!i){t.push(n);continue}if(!i.kind&&r+1<e.length)r++}return t}function YNe(e){let t=[];for(let n of KNe(e)){if(n.startsWith("-"))continue;if(t.push(n),t.length===2)break}return(t.length>0?t.join("-"):"nemar").replace(/[^a-zA-Z0-9_-]/g,"_")}function JNe(e,t){let r=[];if(r.push("NEMAR CLI Debug Log"),r.push(`Generated: ${new Date().toISOString()}`),r.push(`Command: nemar ${dw(WNe(e).join(" "))}`),r.push(""),r.push(d0??"(environment snapshot unavailable)"),r.push(""),r.push("HTTP requests:"),f0.length===0)r.push(" (none)");else f0.forEach((n,i)=>{if(r.push(` [${i+1}] ${n.method} ${n.url} -> ${n.status??"ERROR"} (${n.durationMs}ms)`),n.error)r.push(` Error: ${n.error}`);if(n.note)r.push(` Note: ${n.note}`);if(n.requestHeaders)r.push(` Request headers: ${JSON.stringify(n.requestHeaders)}`);if(n.requestBody)r.push(` Request body: ${n.requestBody}`);if(n.responseBody)r.push(` Response body: ${n.responseBody}`)});return r.push(""),r.push(`Failing step: ${bG??"(none recorded)"}`),r.push(`Exit code: ${t}`),`${r.join(`
|
|
107
107
|
`)}
|
|
@@ -483,20 +483,19 @@ Host github.com
|
|
|
483
483
|
`;try{if(oo(e.configFile))CBe(e.configFile,t);else RBe(e.configFile,`${t.trim()}
|
|
484
484
|
`,{mode:384});return{success:!0}}catch(r){return{success:!1,error:`Failed to update SSH config: ${r.message}`}}}async function kD(){try{let e=BX({cmd:["ssh","-T","-o","BatchMode=yes","-o","StrictHostKeyChecking=accept-new","-o","ConnectTimeout=10","git@github.com"],stdout:"pipe",stderr:"pipe"}),t=await new Response(e.stdout).text(),r=await new Response(e.stderr).text();await e.exited;let n=t+r,i=n.match(/Hi ([^!]+)!/);if(i)return{success:!0,username:i[1]};if(n.includes("Permission denied"))return{success:!1,error:"Permission denied - SSH key not recognized by GitHub"};if(n.includes("successfully authenticated"))return{success:!0};return{success:!1,error:"Could not authenticate with GitHub"}}catch(e){return{success:!1,error:`SSH connection failed: ${e.message}`}}}var Vr=new he("auth").description("Authentication management");co(Vr,`
|
|
485
485
|
Description:
|
|
486
|
-
Manage your NEMAR account authentication. New users register
|
|
487
|
-
their email
|
|
488
|
-
sandbox training). Uploading datasets additionally needs upload access,
|
|
489
|
-
a one-time admin approval you request once your account is active.
|
|
486
|
+
Manage your NEMAR account authentication. New users must register, verify
|
|
487
|
+
their email, and be approved by an admin before they can upload datasets.
|
|
490
488
|
|
|
491
489
|
Workflow:
|
|
492
490
|
1. nemar auth signup - Register a new account
|
|
493
491
|
2. Verify your email - Click the link in the verification email
|
|
494
|
-
3.
|
|
495
|
-
4. nemar auth
|
|
492
|
+
3. Wait for approval - Admin will review your request
|
|
493
|
+
4. nemar auth retrieve-key - Retrieve your API key (requires password)
|
|
494
|
+
5. nemar auth login - Log in with your API key
|
|
496
495
|
|
|
497
496
|
Examples:
|
|
498
497
|
$ nemar auth signup # Start registration
|
|
499
|
-
$ nemar auth retrieve-key # Get your API key
|
|
498
|
+
$ nemar auth retrieve-key # Get your API key after approval
|
|
500
499
|
$ nemar auth login # Interactive login
|
|
501
500
|
$ nemar auth login -k <api-key> # Login with API key
|
|
502
501
|
$ nemar auth regenerate-key # Get a new API key (revokes old)
|
|
@@ -504,7 +503,7 @@ Examples:
|
|
|
504
503
|
$ nemar auth whoami # Alias for status
|
|
505
504
|
$ nemar auth switch # Switch between accounts
|
|
506
505
|
$ nemar auth logout # Clear active account
|
|
507
|
-
$ nemar auth logout --all # Clear all accounts`);function ABe(e){if(!e.hasStoredKey)return{kind:"fresh"};let t=e.username||"unknown";return e.storedKeyState==="invalid"?{kind:"stale",username:t}:{kind:"active",username:t}}async function PD(e){if(Ge()){let n=mt(),i=n.apiKey,s="unknown";if(i){let a=D("Checking your saved credentials...").start();try{s=(await AD(i)).valid?"valid":"invalid",a.stop()}catch(c){if(a.stop(),c instanceof Yo)return;s=c instanceof q&&c.statusCode===401?"invalid":"unknown"}}let o=ABe({hasStoredKey:!0,storedKeyState:s,username:n.username});if(o.kind==="active"){if(console.log(l.yellow(`Already logged in as ${o.username}`)),console.log(l.dim(" This will add another account (use 'nemar auth switch' to switch)")),await ke("Add a different account?",e)!=="confirmed")return}else if(o.kind==="stale")console.log(l.yellow(`Your saved API key for ${o.username} is no longer valid.`)),console.log(l.dim(" It may have expired or been revoked (e.g. via 'nemar auth regenerate-key').")),console.log(l.dim(" Enter your new key below to re-authenticate."))}let t=e.key||process.env.NEMAR_API_KEY;if(!t)t=(await ee.prompt([{type:"password",name:"apiKey",message:"Enter your API key:",mask:"*",validate:(i)=>{if(!i||i.length<32)return"Please enter a valid API key";return!0}}])).apiKey;if(!t){console.log(l.red("No API key provided")),process.exitCode=1;return}let r=D("Validating API key...").start();try{let n=await AD(t);if(!n.valid){Lr(r,"Invalid API key","Check that your API key is correct"),process.exitCode=1;return}if(aG(n.user.username,{apiKey:t,apiUrl:Ko,username:n.user.username,email:n.user.email,githubUsername:n.user.github_username,sandboxCompleted:n.user.sandbox_completed,role:n.user.role,...n.user.sandbox_dataset_id?{sandboxDatasetId:n.user.sandbox_dataset_id}:{}}),r.succeed("Login successful"),console.log(),console.log(` Welcome back, ${l.cyan(n.user.username)}!`),n.user.role==="owner")console.log(` ${l.red("Owner access enabled")}`);else if(n.user.role==="admin")console.log(` ${l.magenta("Admin access enabled")}`);if(!n.user.sandbox_completed)console.log(),console.log(l.yellow(" Note: Sandbox training required before uploading datasets")),console.log(l.dim(" Run 'nemar sandbox' to complete training"))}catch(n){if(process.exitCode=1,n instanceof q){if(r.fail(n.message),Gd(n.message),n.statusCode===401)console.log(l.dim(" Check that your API key is correct"));else if(n.statusCode===403)console.log(l.dim(" Your account
|
|
506
|
+
$ nemar auth logout --all # Clear all accounts`);function ABe(e){if(!e.hasStoredKey)return{kind:"fresh"};let t=e.username||"unknown";return e.storedKeyState==="invalid"?{kind:"stale",username:t}:{kind:"active",username:t}}async function PD(e){if(Ge()){let n=mt(),i=n.apiKey,s="unknown";if(i){let a=D("Checking your saved credentials...").start();try{s=(await AD(i)).valid?"valid":"invalid",a.stop()}catch(c){if(a.stop(),c instanceof Yo)return;s=c instanceof q&&c.statusCode===401?"invalid":"unknown"}}let o=ABe({hasStoredKey:!0,storedKeyState:s,username:n.username});if(o.kind==="active"){if(console.log(l.yellow(`Already logged in as ${o.username}`)),console.log(l.dim(" This will add another account (use 'nemar auth switch' to switch)")),await ke("Add a different account?",e)!=="confirmed")return}else if(o.kind==="stale")console.log(l.yellow(`Your saved API key for ${o.username} is no longer valid.`)),console.log(l.dim(" It may have expired or been revoked (e.g. via 'nemar auth regenerate-key').")),console.log(l.dim(" Enter your new key below to re-authenticate."))}let t=e.key||process.env.NEMAR_API_KEY;if(!t)t=(await ee.prompt([{type:"password",name:"apiKey",message:"Enter your API key:",mask:"*",validate:(i)=>{if(!i||i.length<32)return"Please enter a valid API key";return!0}}])).apiKey;if(!t){console.log(l.red("No API key provided")),process.exitCode=1;return}let r=D("Validating API key...").start();try{let n=await AD(t);if(!n.valid){Lr(r,"Invalid API key","Check that your API key is correct"),process.exitCode=1;return}if(aG(n.user.username,{apiKey:t,apiUrl:Ko,username:n.user.username,email:n.user.email,githubUsername:n.user.github_username,sandboxCompleted:n.user.sandbox_completed,role:n.user.role,...n.user.sandbox_dataset_id?{sandboxDatasetId:n.user.sandbox_dataset_id}:{}}),r.succeed("Login successful"),console.log(),console.log(` Welcome back, ${l.cyan(n.user.username)}!`),n.user.role==="owner")console.log(` ${l.red("Owner access enabled")}`);else if(n.user.role==="admin")console.log(` ${l.magenta("Admin access enabled")}`);if(!n.user.sandbox_completed)console.log(),console.log(l.yellow(" Note: Sandbox training required before uploading datasets")),console.log(l.dim(" Run 'nemar sandbox' to complete training"))}catch(n){if(process.exitCode=1,n instanceof q){if(r.fail(n.message),Gd(n.message),n.statusCode===401)console.log(l.dim(" Check that your API key is correct"));else if(n.statusCode===403)console.log(l.dim(" Your account may not be approved yet"))}else r.fail("Connection failed"),Gd("Connection failed"),console.log(l.dim(" Check your internet connection"))}}var kBe=Vr.command("login").description("Authenticate with your NEMAR API key").option("-k, --key <key>","API key (alternative: set NEMAR_API_KEY env var)").option(Pe,Ie).option(Ue,Me).action(PD);co(kBe,`
|
|
508
507
|
Environment Variables:
|
|
509
508
|
NEMAR_API_KEY Your API key (alternative to -k flag)
|
|
510
509
|
|
|
@@ -512,7 +511,7 @@ Examples:
|
|
|
512
511
|
$ nemar auth login # Interactive prompt
|
|
513
512
|
$ nemar auth login -k nemar_abc123... # Provide key directly
|
|
514
513
|
$ NEMAR_API_KEY=nemar_abc... nemar auth login`);async function PBe(e){try{return await AX(e)}catch{return{status:"lookup_failed",given_name:null,family_name:null}}}async function IBe(e){let t=D("Reading your name from ORCID...").start(),r=await PBe(e);if(r.status==="found")return t.succeed(`Name from your ORCID record: ${r.given_name} ${r.family_name}`),{};if(r.status==="lookup_failed")t.warn("ORCID is unreachable right now, so NEMAR could not read your name. Please enter it; if your ORCID record publishes a name, that name wins.");else t.info("Your ORCID record does not publish a name; NEMAR needs it for DOI citations.");let n=await ee.prompt([{type:"input",name:"given_name",message:"Given (first) name:",validate:(i)=>i?.trim()?!0:"A given name is required: DOIs cite you by name, not by username"},{type:"input",name:"family_name",message:"Family (last) name:",validate:(i)=>i?.trim()?!0:"A family name is required: DOIs cite you by name, not by username"}]);return{given_name:n.given_name.trim(),family_name:n.family_name.trim()}}async function pg(){console.log(l.cyan("NEMAR Account Registration")),console.log(l.dim(`Create an account to upload and manage datasets
|
|
515
|
-
`)),await pG();let e=await ee.prompt([{type:"input",name:"username",message:"Choose a username:",validate:async(i)=>{if(!i||i.length<3)return"Username must be at least 3 characters";if(i.length>30)return"Username must be at most 30 characters";if(!/^[a-zA-Z0-9_-]+$/.test(i))return"Username can only contain letters, numbers, underscores, and hyphens";try{let s=await OX(i);if(!s.available)return s.reason||`Username "${i}" is already taken`}catch(s){if(s instanceof q&&s.statusCode===0)return!0;return!0}return!0}},{type:"input",name:"email",message:"Email address:",validate:(i)=>{if(!i||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(i))return"Please enter a valid email address";return!0}},{type:"password",name:"password",message:"Password (min 12 characters):",mask:"*",validate:(i)=>{if(!i||i.length<12)return"Password must be at least 12 characters";if(i.length>128)return"Password must be at most 128 characters";return!0}},{type:"password",name:"confirmPassword",message:"Confirm password:",mask:"*",validate:(i,s)=>{if(i!==s?.password)return"Passwords do not match";return!0}},{type:"input",name:"github_username",message:"GitHub username (for PR collaboration):",validate:async(i)=>{if(!i||i.length<1)return"GitHub username is required for PR collaboration";if(i.length>39)return"GitHub username is too long";try{if(!(await FX(i)).valid)return`GitHub user "${i}" not found. Please check the username.`}catch(s){if(s instanceof q&&s.statusCode===0)return!0;return!0}return!0}},{type:"input",name:"orcid",message:"ORCID iD (e.g. 0000-0002-1825-0097):",validate:(i)=>{let s=i?.trim();if(!s)return"ORCID iD is required \u2014 it's how NEMAR gets your name";if(!/^\d{4}-\d{4}-\d{4}-\d{3}[\dX]$/.test(s))return"ORCID must be in format 0000-0000-0000-000X";return!0}}]),t=await IBe(e.orcid.trim()),r=await ee.prompt([{type:"input",name:"affiliation",message:"Affiliation / institution (optional):",validate:(i)=>!i||i.trim().length<=200?!0:"Affiliation must be at most 200 characters"},{type:"input",name:"city",message:"City (required for export-control screening):",validate:(i)=>i?.trim()?!0:"City is required"},{type:"input",name:"country",message:"Country (required for export-control screening):",validate:(i)=>i?.trim()?!0:"Country is required"},{type:"input",name:"description",message:"Why do you need access to NEMAR? (1-2 sentences):",validate:(i)=>{let s=i?.trim();if(!s||s.length<20)return"Please provide at least 20 characters describing why you need NEMAR access";if(s.length>500)return"Description must be at most 500 characters";return!0}}]),n=D("Creating account...").start();try{let i=await kX({username:e.username,email:e.email,password:e.password,github_username:e.github_username,description:r.description.trim(),orcid:e.orcid.trim(),...t,affiliation:r.affiliation?.trim()||void 0,city:r.city.trim(),country:r.country.trim()});if(n.succeed("Account created"),console.log(),console.log(l.green("Registration successful!")),console.log(),console.log("Next steps:"),i.next_steps.forEach((s,o)=>{console.log(` ${o+1}. ${s}`)}),i.researcher_name==="missing")console.log(),console.log(l.yellow("No researcher name is on file for this account. DOIs cite depositors by name, never by username, so publishing stays blocked until one is recorded.")),console.log(l.dim(" Make your name public on your ORCID record, then sign in again so NEMAR can read it."));console.log(),console.log(l.dim("
|
|
514
|
+
`)),await pG();let e=await ee.prompt([{type:"input",name:"username",message:"Choose a username:",validate:async(i)=>{if(!i||i.length<3)return"Username must be at least 3 characters";if(i.length>30)return"Username must be at most 30 characters";if(!/^[a-zA-Z0-9_-]+$/.test(i))return"Username can only contain letters, numbers, underscores, and hyphens";try{let s=await OX(i);if(!s.available)return s.reason||`Username "${i}" is already taken`}catch(s){if(s instanceof q&&s.statusCode===0)return!0;return!0}return!0}},{type:"input",name:"email",message:"Email address:",validate:(i)=>{if(!i||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(i))return"Please enter a valid email address";return!0}},{type:"password",name:"password",message:"Password (min 12 characters):",mask:"*",validate:(i)=>{if(!i||i.length<12)return"Password must be at least 12 characters";if(i.length>128)return"Password must be at most 128 characters";return!0}},{type:"password",name:"confirmPassword",message:"Confirm password:",mask:"*",validate:(i,s)=>{if(i!==s?.password)return"Passwords do not match";return!0}},{type:"input",name:"github_username",message:"GitHub username (for PR collaboration):",validate:async(i)=>{if(!i||i.length<1)return"GitHub username is required for PR collaboration";if(i.length>39)return"GitHub username is too long";try{if(!(await FX(i)).valid)return`GitHub user "${i}" not found. Please check the username.`}catch(s){if(s instanceof q&&s.statusCode===0)return!0;return!0}return!0}},{type:"input",name:"orcid",message:"ORCID iD (e.g. 0000-0002-1825-0097):",validate:(i)=>{let s=i?.trim();if(!s)return"ORCID iD is required \u2014 it's how NEMAR gets your name";if(!/^\d{4}-\d{4}-\d{4}-\d{3}[\dX]$/.test(s))return"ORCID must be in format 0000-0000-0000-000X";return!0}}]),t=await IBe(e.orcid.trim()),r=await ee.prompt([{type:"input",name:"affiliation",message:"Affiliation / institution (optional):",validate:(i)=>!i||i.trim().length<=200?!0:"Affiliation must be at most 200 characters"},{type:"input",name:"city",message:"City (required for export-control screening):",validate:(i)=>i?.trim()?!0:"City is required"},{type:"input",name:"country",message:"Country (required for export-control screening):",validate:(i)=>i?.trim()?!0:"Country is required"},{type:"input",name:"description",message:"Why do you need access to NEMAR? (1-2 sentences):",validate:(i)=>{let s=i?.trim();if(!s||s.length<20)return"Please provide at least 20 characters describing why you need NEMAR access";if(s.length>500)return"Description must be at most 500 characters";return!0}}]),n=D("Creating account...").start();try{let i=await kX({username:e.username,email:e.email,password:e.password,github_username:e.github_username,description:r.description.trim(),orcid:e.orcid.trim(),...t,affiliation:r.affiliation?.trim()||void 0,city:r.city.trim(),country:r.country.trim()});if(n.succeed("Account created"),console.log(),console.log(l.green("Registration successful!")),console.log(),console.log("Next steps:"),i.next_steps.forEach((s,o)=>{console.log(` ${o+1}. ${s}`)}),i.researcher_name==="missing")console.log(),console.log(l.yellow("No researcher name is on file for this account. DOIs cite depositors by name, never by username, so publishing stays blocked until one is recorded.")),console.log(l.dim(" Make your name public on your ORCID record, then sign in again so NEMAR can read it."));console.log(),console.log(l.dim("Once approved, use 'nemar auth retrieve-key' to get your API key"))}catch(i){if(i instanceof q){if(n.fail(i.message),i.details&&Array.isArray(i.details))i.details.forEach((s)=>{console.log(l.dim(` - ${s}`))});if(i.message.includes("already taken"))console.log(l.dim(" Try a different username"));else if(i.message.includes("already registered"))console.log(l.dim(" Use 'nemar auth resend-verification' if you need a new verification link"))}else n.fail("Registration failed"),console.log(l.dim(` ${i instanceof Error?i.message:"Unknown error"}`))}}Vr.command("signup").description("Register for a new NEMAR account").action(pg);async function mg(e){if(!Ge()){console.log(l.yellow("Not authenticated")),console.log(),console.log(" Run 'nemar auth login' to authenticate"),console.log(" Run 'nemar auth signup' to create an account");return}let t,r;if(e.refresh){let o=D("Fetching user info...").start();try{let a=await lg();if(Mr("username",a.username??void 0),Mr("email",a.email),Mr("githubUsername",a.github_username??void 0),a.service_access!==void 0)Mr("serviceAccess",a.service_access);Mr("role",a.role),t=a.role,o.stop()}catch(a){let c=a instanceof Error?a.message:String(a);if(o.fail(`Could not refresh user info: ${c}`),r=c,a instanceof q&&a.statusCode===401){console.log(l.yellow(" Your saved API key is no longer valid (expired or revoked).")),console.log(l.dim(" Run 'nemar auth login' with a new key to re-authenticate."));return}if(a instanceof q&&a.statusCode===403){console.log(l.yellow(" Your account is not active (pending approval or suspended).")),console.log(l.dim(" Contact a NEMAR admin if you believe this is an error."));return}}}let n=mt();if(console.log(l.green("Authenticated")),console.log(),n.username)console.log(` Username: ${l.cyan(n.username)}`);if(n.email)console.log(` Email: ${n.email}`);if(n.githubUsername)console.log(` GitHub: @${n.githubUsername}`);if(t){let o=t==="owner"?l.red("Owner"):t==="admin"?l.magenta("Admin"):l.white("Member");console.log(` Role: ${o}`)}if(r)console.log(` Upload access: ${l.dim("unknown (refresh failed; showing cached account)")}`);else if(n.serviceAccess===!0)console.log(` Upload access: ${l.green("granted")}`);else if(n.serviceAccess===!1)console.log(` Upload access: ${l.yellow("not granted")} ${l.dim("(one-time admin approval; see https://nemar.org/support)")}`);else console.log(` Upload access: ${l.dim("unknown (run 'nemar auth status --refresh')")}`);console.log(` Config: ${l.dim(oG())}`);let s=Bd().filter((o)=>!o.active);if(s.length>0)console.log(),console.log(` Other accounts: ${s.map((o)=>l.dim(o.username)).join(", ")}`),console.log(l.dim(" Run 'nemar auth switch' to switch accounts"))}Vr.command("status").description("Check current authentication status").option("--refresh","Refresh user info from server").action(mg);Vr.command("whoami").description("Show current user (alias for status)").option("--refresh","Refresh user info from server").action(mg);async function TBe(e){try{let t=Bun.which("gh");if(!t){console.log(l.dim(" GitHub CLI (gh) not found in PATH, skipping"));return}let r=Bun.spawn({cmd:[t,"auth","switch","--user",e],stdout:"pipe",stderr:"pipe"}),n=await new Response(r.stderr).text(),i=await r.exited;if(i===0)console.log(` GitHub CLI switched to ${l.cyan(`@${e}`)}`);else{let s=n.trim();if(s.includes("not found")||s.includes("no accounts"))console.log(l.dim(` GitHub CLI: @${e} not logged in (run 'gh auth login')`));else console.log(l.dim(` GitHub CLI switch failed: ${s||`exit code ${i}`}`))}}catch(t){let r=t instanceof Error?t.message:String(t);console.log(l.dim(` GitHub CLI switch skipped: ${r}`))}}async function ID(e){let t=Bd();if(t.length===0){console.log(l.yellow("No stored accounts")),console.log(" Run 'nemar auth login' to add an account");return}if(t.length===1){let s=t[0];if(s.active){console.log(l.yellow(`Only one account stored: ${s.username}`)),console.log(" Run 'nemar auth login' to add another account");return}}let r;if(e)r=e;else{let s=t.map((a)=>({name:`${a.username}${a.githubUsername?` (@${a.githubUsername})`:""}${a.active?l.green(" (active)"):""}`,value:a.username,short:a.username})),{selected:o}=await ee.prompt([{type:"list",name:"selected",message:"Switch to account:",choices:s,default:t.find((a)=>a.active)?.username}]);r=o}let n=t.find((s)=>s.active);if(n&&n.username===r){console.log(l.yellow(`Already using account ${r}`));return}let i=cG(r);if(!i){console.log(l.red(`Account not found: ${r}`)),console.log(l.dim(" Provide a NEMAR username or GitHub username")),console.log(l.dim(` Available: ${t.map((s)=>s.username).join(", ")}`));return}if(console.log(l.green(`Switched to ${l.cyan(i.username||r)}`)),i.githubUsername)await TBe(i.githubUsername)}var NBe=Vr.command("switch [username]").description("Switch between stored accounts").action(ID);co(NBe,`
|
|
516
515
|
Description:
|
|
517
516
|
Switch the active NEMAR account. You can specify a NEMAR username or
|
|
518
517
|
GitHub username. If no username is given, an interactive picker is shown.
|
|
@@ -535,12 +534,11 @@ Description:
|
|
|
535
534
|
|
|
536
535
|
Examples:
|
|
537
536
|
$ nemar auth setup-ssh # Set up SSH access
|
|
538
|
-
$ nemar auth setup-ssh --force # Regenerate key even if exists`);var MBe=Vr.command("retrieve-key").description("Retrieve your API key
|
|
537
|
+
$ nemar auth setup-ssh --force # Regenerate key even if exists`);var MBe=Vr.command("retrieve-key").description("Retrieve your API key after account approval (requires email and password)").action(async()=>{let e=await ee.prompt([{type:"input",name:"email",message:"Email address:",validate:(r)=>{if(!r||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(r))return"Please enter a valid email address";return!0}},{type:"password",name:"password",message:"Password:",mask:"*",validate:(r)=>{if(!r)return"Password is required";return!0}}]),t=D("Retrieving API key...").start();try{let r=await IX(e.email,e.password);t.succeed("API key retrieved"),console.log(),console.log(l.yellow("Your API Key (store this securely):")),console.log(l.dim(` ${r.api_key}`)),console.log(),console.log("Next step:"),console.log(` Run ${l.cyan("nemar auth login")} and paste your API key`)}catch(r){if(r instanceof q){if(r.statusCode===409){let n=r.details;if(t.info("API key already issued"),n?.api_key_prefix)console.log(),console.log(` Key prefix: ${l.dim(n.api_key_prefix)}`);console.log(),console.log(" If you lost your API key, regenerate it:"),console.log(` ${l.cyan("nemar auth regenerate-key")}`)}else if(t.fail(r.message),r.statusCode===401)console.log(l.dim(" Check your email and password"));else if(r.statusCode===403)console.log(l.dim(" Your account may not be approved yet"))}else{let n=r instanceof Error?r.message:"Unknown error";t.fail(`Failed to retrieve API key: ${n}`)}}});co(MBe,`
|
|
539
538
|
Description:
|
|
540
|
-
|
|
539
|
+
After an admin approves your account, use this command to securely
|
|
541
540
|
retrieve your API key. You will need the email and password you used
|
|
542
|
-
during signup.
|
|
543
|
-
separate, one-time grant that lets you upload datasets.
|
|
541
|
+
during signup.
|
|
544
542
|
|
|
545
543
|
API keys are not sent via email for security. This is the only way
|
|
546
544
|
to obtain your key.
|
package/package.json
CHANGED