nemar-cli 0.10.5-dev.2687 → 0.10.5-dev.2690
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 +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -99,7 +99,7 @@ ${t}`,_Se,vSe,wSe=(e,t,r)=>{let n=r===""?"":`with ${r.trim()}() `,o=xSe.bind(nul
|
|
|
99
99
|
`)}var cE=!1;var{spawn:rLe}=globalThis.Bun;async function C(e,t={}){let r={...process.env,GIT_TERMINAL_PROMPT:"0",...t.env};for(let u of t.unsetEnv??[])delete r[u];let n=rLe({cmd:e,cwd:t.cwd,...t.stdin===void 0?{}:{stdin:new TextEncoder().encode(t.stdin)},stdout:"pipe",stderr:"pipe",env:r}),o=!1,i;if(t.timeout)i=setTimeout(()=>{o=!0,n.kill()},t.timeout);if(lE()){let u=t.cwd?` (cwd=${t.cwd})`:"";Tf(l.dim(`$ ${e.join(" ")}${u}`))}let s=await new Response(n.stdout).text(),a=await new Response(n.stderr).text(),c=await n.exited;if(i)clearTimeout(i);if(lE()){if(s.trim())Tf(l.dim(s.trimEnd()));if(a.trim())Tf(l.yellow(a.trimEnd()));Tf(l.dim(`(exit ${c})`))}if(o)return{stdout:s,stderr:a||`Command timed out after ${Math.round(t.timeout/1000)}s`,exitCode:c??1,timedOut:!0};return{stdout:s,stderr:a,exitCode:c,timedOut:!1}}var vt=O(()=>{Xe()});function oLe(){if(process.platform==="darwin")return"macos";if(process.platform==="win32")return"windows";return"linux"}function XK(e,t=oLe()){return e.installInstructions[t]}function iLe(e){let t=e.match(/(\d+\.\d+(?:\.\d+)?)/);return t?t[1]:void 0}function aLe(){let e=Number(process.env.TEST_PROBE_TIMEOUT_MS);return Number.isFinite(e)&&e>0?e:sLe}async function ZK(e){try{let{exitCode:t,stdout:r,timedOut:n}=await C(e.cmd,{timeout:aLe()});if(n)return{available:!1,timedOut:!0};if(t!==0)return{available:!1};return{available:!0,version:iLe(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 Nf(){let e=Object.entries(JK);return Promise.all(e.map(async([t,r])=>{let{available:n,version:o,timedOut:i}=await ZK(r);return{key:t,name:r.name,purpose:r.purpose,required:r.required,available:n,version:o,timedOut:i,installInstruction:XK(r)}}))}async function QK(){let t=(await Nf()).filter((r)=>r.required&&!r.available);if(t.length===0)return[];console.warn(`
|
|
100
100
|
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 Ra(e){let r=nLe[e].map((s)=>JK[s]).filter(Boolean),n=await Promise.all(r.map(async(s)=>{let a=await ZK(s);return{check:s,available:a.available,timedOut:a.timedOut}}));for(let s of n)if(s.timedOut&&s.check.required)console.warn(`Warning: ${s.check.name} did not respond to a version check in time; continuing anyway.`);let o=n.filter((s)=>!s.available&&!s.timedOut&&s.check.required).map((s)=>({tool:s.check.name,installInstruction:XK(s.check)}));if(o.length===0)return;let i=[`
|
|
101
101
|
Missing required tools:`];for(let s of o)i.push(` ${s.tool} not installed`),i.push(` Install: ${s.installInstruction}`);throw Error(i.join(`
|
|
102
|
-
`))}var JK,nLe,sLe=4000;var jf=O(()=>{vt();JK={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)"}}},nLe={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 tY;var eY=O(()=>{tY={name:"nemar-cli",version:"0.10.5-dev.
|
|
102
|
+
`))}var JK,nLe,sLe=4000;var jf=O(()=>{vt();JK={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)"}}},nLe={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 tY;var eY=O(()=>{tY={name:"nemar-cli",version:"0.10.5-dev.2690",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/","migrations:d1-check":"bun run scripts/d1-migration-check.ts","zarr:geometry-check":"bun run backend/scripts/zarr-geometry-conformance.ts","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 Di;var qf=O(()=>{eY();Di=tY.version});import{mkdirSync as lLe,readdirSync as uLe,unlinkSync as dLe,writeFileSync as fLe}from"fs";import{join as uE}from"path";function aY(){Ol=!0}function Aa(){return Ol}function cY(e,t=process.env){return e.includes("--debug")||t.NEMAR_DEBUG==="1"}function Lf(e){sY=e,$r(`Step: ${e}`)}function Bf(){oY=!0}function lY(){return oY}function fE(){iY=!0}function uY(){return iY}function $r(e){if(Ol)process.stderr.write(`[debug] ${e}
|
|
103
103
|
`)}function gLe(e){let t={};for(let[r,n]of Object.entries(e))t[r]=hLe.has(r.toLowerCase())?"[REDACTED]":n;return t}function fY(e){let t=e.indexOf("@");if(t<=0)return"***";return`${e[0]}***${e.slice(t)}`}function dE(e,t,r=0){if(r>bLe)return"[REDACTED: depth]";if(Array.isArray(t))return t.map((n)=>dE(e,n,r+1));if(t&&typeof t==="object"){let n={};for(let[o,i]of Object.entries(t))n[o]=dE(o,i,r+1);return n}if(typeof t==="string"){if(yLe.test(e))return"[REDACTED]";return t.replace(dY,fY)}return t}function pE(e){return e.replace(xLe,"Bearer [REDACTED]").replace(_Le,"[REDACTED_AWS_KEY]").replace(vLe,"$1[REDACTED]").replace(wLe,"$1$2=[REDACTED]").replace(dY,fY)}function DLe(e){return pE(e)}function rY(e,t=mLe){if(e===void 0)return;let r=e;try{let n=JSON.parse(e);r=JSON.stringify(dE("",n))}catch{}if(r=pE(r),Buffer.byteLength(r,"utf8")>t)r=`${Buffer.from(r,"utf8").subarray(0,t).toString("utf8")}... [truncated]`;return r}function A0(e){if(!Ol)return;let t=DLe(e.url);try{let r={...e,url:t,requestHeaders:e.requestHeaders?gLe(e.requestHeaders):void 0,requestBody:rY(e.requestBody),responseBody:rY(e.responseBody)};R0.push(r),$r(`${e.method} ${t} -> ${e.status??"ERROR"} (${e.durationMs}ms)`)}catch(r){let n=T(r);R0.push({method:e.method,url:t,status:e.status,durationMs:e.durationMs,note:`redaction failed: ${n}`}),$r(`redaction failed for ${e.method} ${t}: ${n}`)}}async function ELe(){let e=[];try{let t=process.versions.bun?`Bun ${process.versions.bun}`:`Node ${process.version}`;e.push(`CLI version: ${Di}`),e.push(`OS/Arch: ${process.platform}/${process.arch}`),e.push(`Runtime: ${t}`)}catch(t){e.push(`(could not read CLI/OS/runtime info: ${T(t)})`)}e.push(""),e.push("Active account:");try{let t=ot();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: ${T(t)})`)}e.push(""),e.push("External tools:");try{let t=await Nf();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: ${T(t)})`)}return e}async function mE(){return(await ELe()).join(`
|
|
104
104
|
`)}async function pY(){if(!Ol||k0!==void 0)return;try{k0=await mE()}catch(e){let t=T(e);k0=`(could not build environment report: ${t})`,$r(`environment snapshot failed: ${t}`)}}function mY(e){if(nY.has(e))return{flag:e};for(let t of nY){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 SLe(e){let t=[];for(let r=0;r<e.length;r++){let n=e[r],o=mY(n);if(!o){t.push(n);continue}if(o.kind==="long-eq")t.push(`${o.flag}=[REDACTED]`);else if(o.kind==="short-attached")t.push(`${o.flag}[REDACTED]`);else if(t.push(n),r+1<e.length)t.push("[REDACTED]"),r++}return t}function $Le(e){let t=[];for(let r=0;r<e.length;r++){let n=e[r],o=mY(n);if(!o){t.push(n);continue}if(!o.kind&&r+1<e.length)r++}return t}function CLe(e){let t=[];for(let n of $Le(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 kLe(e,t){let r=[];if(r.push("NEMAR CLI Debug Log"),r.push(`Generated: ${new Date().toISOString()}`),r.push(`Command: nemar ${pE(SLe(e).join(" "))}`),r.push(""),r.push(k0??"(environment snapshot unavailable)"),r.push(""),r.push("HTTP requests:"),R0.length===0)r.push(" (none)");else R0.forEach((n,o)=>{if(r.push(` [${o+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: ${sY??"(none recorded)"}`),r.push(`Exit code: ${t}`),`${r.join(`
|
|
105
105
|
`)}
|
|
@@ -177,10 +177,10 @@ Import and publish complete: ${e} -> ${n}`)),console.log(l.dim(` GitHub: https:
|
|
|
177
177
|
`)[0]||void 0;return o.exitCode===0?{present:a,absent:c,unconfirmed:[],method:n,detail:u}:{present:a,absent:[],unconfirmed:c,method:n,detail:u}}}async function cHe(e){let t=[];for(let r of["main","git-annex"]){let n=await C(["git","rev-parse","--verify","--quiet",`refs/heads/${r}`],{cwd:e});if(n.exitCode!==0)continue;let o=await C(["git","rev-parse","--verify","--quiet",`refs/remotes/origin/${r}`],{cwd:e});if(o.exitCode!==0||n.stdout.trim()!==o.stdout.trim())t.push(r)}return t}async function lHe(e,t){let{stdout:r,exitCode:n}=await C(["git","show","git-annex:remote.log"],{cwd:e});if(n!==0)return null;for(let o of r.split(`
|
|
178
178
|
`)){if(!o.includes(`name=${t}`))continue;let i=o.split(/\s+/)[0];if(/^[0-9a-f-]{36}$/.test(i))return i}return null}function uHe(e){return async({datasetPath:t,files:r})=>{let n=Na(e.workDir,"upload-staging");Oee(n,{recursive:!0,force:!0}),tHe(n,{recursive:!0});try{let o=new Set;for(let d of r){if(o.has(d.key))continue;o.add(d.key);let f=await C(["git","annex","contentlocation",d.key],{cwd:t});if(f.exitCode!==0||!f.stdout.trim())throw Error(`git-annex cannot locate the content it just took for ${d.path} (${d.key}). Not uploading: the key would be registered with nothing behind it.`);let m=await C(["ln",Na(t,f.stdout.trim()),Na(n,d.key)],{});if(m.exitCode!==0)throw Error(`Could not stage ${d.key} for upload: ${m.stderr.trim()}`)}let i=`s3://${e.bucket}/${e.datasetId}/objects/`,s=await C(["aws","s3","sync",n,i,"--region",e.region,"--size-only","--only-show-errors"],{});if(s.exitCode!==0)throw Error(`aws s3 sync to ${i} failed: ${(s.stderr||s.stdout).trim().slice(0,600)}. Not committing: the pushed tree would name keys with no content behind them.`);let a=await Ms(t,r.map((d)=>d.key),e.remoteUuid);if(a.failed>0)throw Error(`Uploaded ${r.length} object(s) but ${a.failed} key registration(s) failed. Not committing: clones could not find content that is actually there.`);let c=await Oi(e.bucket,`${e.datasetId}/objects/`,e.region),u=r.filter((d)=>!nn(d.key,c));if(u.length>0)throw Error(`${u.length} of ${r.length} object(s) are absent from ${i} or the wrong size (${u.slice(0,3).map((d)=>d.path).join(", ")}${u.length>3?", ...":""}). Not committing.`);return{copied:r.length}}finally{Oee(n,{recursive:!0,force:!0})}}}var Fee="nemar-s3";var k2=O(()=>{$i();Ul();Bo();vt();Ri();kn();xy();lp();Fi()});import{cpSync as dHe,existsSync as F2,mkdirSync as fHe,mkdtempSync as Tee,readFileSync as pHe,rmSync as R2,writeFileSync as A2}from"fs";import{tmpdir as Nee}from"os";import{join as Ii,resolve as O2}from"path";function mr(e,...t){if(e.verbose)console.log(" ",...t)}async function qee(e,t){let r=performance.now();try{return await t(),{name:e,passed:!0,duration_ms:Math.round(performance.now()-r)}}catch(n){let o=n instanceof Error?n.message:String(n);return{name:e,passed:!1,duration_ms:Math.round(performance.now()-r),error:o}}}async function mHe(e){let t=[];for(let{name:r,fn:n}of e){let o=await qee(r,n);if(t.push(o),!o.passed)break}return t}function on(e,t){if(!e.success)throw Error(`${t}: ${e.error}`)}function jee(e){if(!e.creds)throw Error("credentials not yet fetched");return e.creds}function hHe(){let e=[O2(__dirname,"../../test/fixtures/bids-minimal"),O2(__dirname,"../test/fixtures/bids-minimal"),O2(process.cwd(),"test/fixtures/bids-minimal")];for(let t of e)if(F2(t))return t;throw Error("Could not find test/fixtures/bids-minimal. Run from the nemar-cli root directory.")}async function Mee(e){let t=e.verbose??!1,r=performance.now(),n=Tee(Ii(Nee(),"nemar-e2e-upload-")),o=Tee(Ii(Nee(),"nemar-e2e-clone-")),i={uploadDir:n,cloneDir:o,verbose:t},s=[];if(!e.skipReset)s.push({name:"Reset nm099999",fn:async()=>{let c=await BY(ja);if(mr(i,`S3 objects deleted: ${c.steps.s3_deleted}`),mr(i,`GitHub recreated: ${c.steps.github_recreated}`),mr(i,`D1 cleaned: ${c.steps.d1_cleaned}`),!c.success){let u=[];if(!c.steps.github_recreated)u.push("GitHub");if(!c.steps.d1_cleaned)u.push("D1");throw Error(`Reset partially failed: ${u.join(", ")}`)}}});s.push({name:"Prepare upload",fn:async()=>{let c=hHe();dHe(c,n,{recursive:!0});for(let u of[".git",".nemar",".gitattributes"]){let d=Ii(n,u);if(F2(d)){let f=await C(["chmod","-R","u+w",d]);if(f.exitCode!==0)console.warn(`chmod failed for ${d} (exit ${f.exitCode}): ${f.stderr}`);R2(d,{recursive:!0,force:!0})}}mr(i,`Copied fixtures to ${n}`)}},{name:"Init git + annex",fn:async()=>{on(await Wl(n,{force:!0}),"initDataset"),on(await js(n),"configureLargefiles"),mr(i,"Git + git-annex initialized")}},{name:"Configure remotes",fn:async()=>{i.creds=await dr(ja),mr(i,`S3 prefix: ${i.creds.s3.prefix}`),on(await kr(n,{name:"nemar-s3",bucket:i.creds.s3.bucket,prefix:`${ja}/objects`,region:i.creds.s3.region,publicUrl:`https://${i.creds.s3.bucket}.s3.${i.creds.s3.region}.amazonaws.com/${ja}/objects`},Lt(i.creds.credentials)),"configureS3Remote");let c=`git@github.com:nemarDatasets/${ja}.git`;on(await qo(n,c),"configureGitHubRemote"),mr(i,"S3 + GitHub remotes configured")}},{name:"Upload to S3",fn:async()=>{on(await Ai(n),"gitAnnexAdd"),on(await ki(n,"Initial BIDS dataset"),"saveDataset");let c=await zo(n,"nemar-s3",4,Lt(jee(i).credentials));on(c,"copyToAnnexRemote"),mr(i,`Files copied to S3: ${c.filesCopied}`),await pr(n)}},{name:"Push to GitHub",fn:async()=>{let c=await Tt(n);if(on(c,"pushToGitHub"),c.warning)mr(i,`Warning: ${c.warning}`);mr(i,"Pushed to GitHub (main + git-annex branches)")}},{name:"Clone fresh",fn:async()=>{let c=`git@github.com:nemarDatasets/${ja}.git`;on(await fr(c,o),"cloneDataset");let u=await zl(o,"nemar-s3");if(!u.success)throw Error(`enableS3Remote: ${u.error}`);mr(i,`Clone at ${o}, S3 remote enabled: ${u.enabled}`)}},{name:"Download + verify",fn:async()=>{let c=Lt(jee(i).credentials),u={AWS_ACCESS_KEY_ID:c.accessKeyId,AWS_SECRET_ACCESS_KEY:c.secretAccessKey};if(c.sessionToken)u.AWS_SESSION_TOKEN=c.sessionToken;let{stdout:d,stderr:f,exitCode:m}=await C(["git","annex","get","."],{cwd:o,env:u});if(m!==0)throw Error(`getDatasetData: ${f.trim()}`);let p=d.match(/^get .+ ok$/gm);mr(i,`Files downloaded: ${p?p.length:0}`);let h=Ii(o,"sub-01/eeg/sub-01_task-rest_eeg.edf");if(!F2(h))throw Error("EDF file not found after download");let{size:y}=Bun.file(h);if(y<512)throw Error(`EDF file too small: ${y} bytes`);mr(i,`EDF file verified: ${y} bytes`)}},{name:"Update cycle",fn:async()=>{let c=Ii(o,"sub-02/eeg");fHe(c,{recursive:!0});let u=Buffer.alloc(1024);u.write("0".padEnd(8),0,"ascii"),A2(Ii(c,"sub-02_task-rest_eeg.edf"),u),A2(Ii(c,"sub-02_task-rest_eeg.json"),JSON.stringify({TaskName:"rest",SamplingFrequency:256}));let d=pHe(Ii(o,"participants.tsv"),"utf-8");A2(Ii(o,"participants.tsv"),`${d.trimEnd()}
|
|
179
179
|
sub-02 30 F
|
|
180
|
-
`);let f=`e2e-update-${Date.now()}`,{exitCode:m}=await C(["git","checkout","-b",f],{cwd:o});if(m!==0)throw Error("Failed to create update branch");on(await Ai(o),"gitAnnexAdd (update)"),on(await ki(o,"Add sub-02"),"saveDataset (update)"),i.creds=await dr(ja);let p=await zo(o,"nemar-s3",4,Lt(i.creds.credentials));on(p,"copyToAnnexRemote (update)"),mr(i,`Update files copied to S3: ${p.filesCopied}`),await pr(o);let h=await Tt(o,"origin",f);on(h,"pushToGitHub (update branch)");let{exitCode:y,stderr:b}=await C(["git","push","origin","git-annex"],{cwd:o});if(y!==0)throw Error(`Failed to push git-annex branch: ${b.trim()}`);mr(i,`Update pushed to branch: ${f}`)}});let a=await mHe(s);if(!e.skipCleanup&&a.every((c)=>c.passed))a.push(await qee("Cleanup",async()=>{await C(["chmod","-R","u+w",n]),await C(["chmod","-R","u+w",o]),R2(n,{recursive:!0,force:!0}),R2(o,{recursive:!0,force:!0}),mr(i,"Temp directories cleaned up")}));return gHe(a,r,n,o,e.skipCleanup)}function gHe(e,t,r,n,o){let i={passed:e.every((s)=>s.passed),steps:e,total_duration_ms:Math.round(performance.now()-t)};if(o)i.upload_dir=r,i.clone_dir=n;return i}var __dirname="/home/runner/work/nemar-cli/nemar-cli/src/lib",ja="nm099999";var Lee=O(()=>{Nl();$i();Bo();Mo();Ia();vt();Ri();kn()});import{existsSync as yHe,mkdtempSync as bHe,readFileSync as Uee,writeFileSync as xHe}from"fs";import{tmpdir as _He}from"os";import{join as ky}from"path";function vHe(e){let
|
|
180
|
+
`);let f=`e2e-update-${Date.now()}`,{exitCode:m}=await C(["git","checkout","-b",f],{cwd:o});if(m!==0)throw Error("Failed to create update branch");on(await Ai(o),"gitAnnexAdd (update)"),on(await ki(o,"Add sub-02"),"saveDataset (update)"),i.creds=await dr(ja);let p=await zo(o,"nemar-s3",4,Lt(i.creds.credentials));on(p,"copyToAnnexRemote (update)"),mr(i,`Update files copied to S3: ${p.filesCopied}`),await pr(o);let h=await Tt(o,"origin",f);on(h,"pushToGitHub (update branch)");let{exitCode:y,stderr:b}=await C(["git","push","origin","git-annex"],{cwd:o});if(y!==0)throw Error(`Failed to push git-annex branch: ${b.trim()}`);mr(i,`Update pushed to branch: ${f}`)}});let a=await mHe(s);if(!e.skipCleanup&&a.every((c)=>c.passed))a.push(await qee("Cleanup",async()=>{await C(["chmod","-R","u+w",n]),await C(["chmod","-R","u+w",o]),R2(n,{recursive:!0,force:!0}),R2(o,{recursive:!0,force:!0}),mr(i,"Temp directories cleaned up")}));return gHe(a,r,n,o,e.skipCleanup)}function gHe(e,t,r,n,o){let i={passed:e.every((s)=>s.passed),steps:e,total_duration_ms:Math.round(performance.now()-t)};if(o)i.upload_dir=r,i.clone_dir=n;return i}var __dirname="/home/runner/work/nemar-cli/nemar-cli/src/lib",ja="nm099999";var Lee=O(()=>{Nl();$i();Bo();Mo();Ia();vt();Ri();kn()});import{existsSync as yHe,mkdtempSync as bHe,readFileSync as Uee,writeFileSync as xHe}from"fs";import{tmpdir as _He}from"os";import{join as ky}from"path";function vHe(e,t={}){let r=typeof e.Name==="string"?e.Name:"",{DatasetDOI:n,...o}=e,i={...o,Name:`[TEST COPY] ${r}`};if(t.anonymous)i.Authors=["Anonymous"];return i}function wHe(e,t,r){let n=`${t}/`;if(!e.startsWith(n))throw Error(`Key "${e}" does not start with expected source prefix "${n}"`);return`${r}/${e.slice(n.length)}`}function DHe(e){if(!Array.isArray(e))throw Error("Exemplar fleet file must be a JSON array");let t=e.map((n,o)=>{if(typeof n!=="object"||n===null)throw Error(`Fleet entry ${o} is not an object`);let{xx_id:i,source_id:s,modality:a,note:c}=n;if(typeof i!=="string"||!Vee.test(i))throw Error(`Fleet entry ${o}: xx_id "${i}" must match xx099900-xx099999`);if(typeof s!=="string"||!Gee.test(s))throw Error(`Fleet entry ${o} (${i}): source_id "${s}" must be an nm/on dataset id`);if(typeof a!=="string"||a.length===0)throw Error(`Fleet entry ${o} (${i}): modality is required`);if(c!==void 0&&typeof c!=="string")throw Error(`Fleet entry ${o} (${i}): note must be a string when present`);let{anonymous:u}=n;if(u!==void 0&&u!==!0)throw Error(`Fleet entry ${o} (${i}): anonymous must be omitted or literally true, not ${JSON.stringify(u)}`);return{xx_id:i,source_id:s,modality:a,...u===!0?{anonymous:!0}:{},...c!==void 0?{note:c}:{}}}),r=t.filter((n)=>n.anonymous);if(r.length>1)throw Error(`Fleet declares ${r.length} anonymous exemplars (${r.map((n)=>n.xx_id).join(", ")}); exactly one is allowed`);return t}function Hee(e,t){return e.find((r)=>r.xx_id===t)?.anonymous===!0}function fp(e){let t;try{t=JSON.parse(Uee(e,"utf-8"))}catch(r){throw Error(`Failed to read/parse exemplar fleet file at ${e}: ${r instanceof Error?r.message:String(r)}`)}return DHe(t)}function EHe(){let e=process.env.AWS_ACCESS_KEY_ID,t=process.env.AWS_SECRET_ACCESS_KEY;if(!e||!t)throw Error("AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY must be set in environment for S3 operations");return{accessKeyId:e,secretAccessKey:t,sessionToken:process.env.AWS_SESSION_TOKEN}}function SHe(e){let t=ky(e,"dataset_description.json");if(!yHe(t))throw Error(`dataset_description.json not found at ${t}`);try{return JSON.parse(Uee(t,"utf-8"))}catch(r){throw Error(`Failed to parse dataset_description.json at ${t}: ${r instanceof Error?r.message:String(r)}`)}}async function $He(e){let{stdout:t,exitCode:r}=await C(["git","tag","-l"],{cwd:e});if(r!==0)return;let n=t.trim().split(`
|
|
181
181
|
`).filter(Boolean);if(n.length===0)return;let o=await C(["git","tag","-d",...n],{cwd:e});if(o.exitCode!==0)console.warn(l.yellow(` Warning: failed to strip local tags: ${o.stderr.trim()}`))}function kHe(e){return e!==CHe}async function Wee(e,t,r){let n=`${e}/${r}`;return[...(await Oi(Bee,n,qa)).keys()].filter(kHe).map((i)=>{let s=`${n}${i}`,a=wHe(s,e,t);return{key:i,source:{bucket:Bee,key:s,region:qa},httpUrl:null,destUri:`s3://${dp}/${a}`}})}function RHe(e,t){return wy(e,t,Dy(e))}async function P2(e,t,r,n){let o=await Wee(e,t,r);if(o.length===0)return console.log(l.dim(` [${n}] nothing to copy`)),[];let i=await Oi(dp,`${t}/${r}`,qa),{toCopy:s,skipped:a}=RHe(o,i);if(s.length===0)return console.log(l.green(` [${n}] nothing to copy (all ${a.length} present)`)),o.map((d)=>d.key);let c=D(`[${n}] copying ${s.length} files...`).start(),u=await ap(s,qa,zee,(d,f)=>{c.text=`[${n}] ${d}/${f}`});if(u.failed.length>0){c.fail(`[${n}] ${u.failed.length} of ${s.length} files failed`);for(let d of u.failed.slice(0,5))console.error(l.red(` ${d.key}: ${d.error}`));throw console.error(l.red(`Re-run to resume [${n}] (already-copied objects are skipped).`)),Error(`[${n}] ${u.failed.length} of ${s.length} files failed to copy: ${u.failed.slice(0,5).map((d)=>`${d.key}: ${d.error}`).join("; ")}`)}return c.succeed(`[${n}] copied ${u.copied}, skipped ${a.length}`),o.map((d)=>d.key)}async function AHe(e,t,r={}){if(!Vee.test(e))throw Error(`Invalid exemplar id "${e}". Expected xx099900-xx099999.`);if(!Gee.test(t))throw Error(`Invalid source id "${t}". Expected an nm/on dataset id.`);let n=r.workDir||bHe(ky(_He(),`nemar-exemplar-${e}-`)),o=ky(n,e);console.log(l.cyan(`
|
|
182
182
|
[prepare] ${t} -> ${e}
|
|
183
|
-
`)),console.log(l.dim(`Working directory: ${n}`));let i=D(`Cloning ${t}...`).start(),s=`https://github.com/${up}/${t}.git`,a=await fr(s,o);if(!a.success){let I=`Failed to clone: ${a.error}`;throw i.fail(I),Error(I)}i.succeed(`Cloned ${t}`),await $He(o);let c=D("Scrubbing dataset_description.json...").start(),u=SHe(o),d=vHe(u);xHe(ky(o,"dataset_description.json"),`${JSON.stringify(d,null,2)}
|
|
183
|
+
`)),console.log(l.dim(`Working directory: ${n}`));let i=D(`Cloning ${t}...`).start(),s=`https://github.com/${up}/${t}.git`,a=await fr(s,o);if(!a.success){let I=`Failed to clone: ${a.error}`;throw i.fail(I),Error(I)}i.succeed(`Cloned ${t}`),await $He(o);let c=D("Scrubbing dataset_description.json...").start(),u=SHe(o),d=vHe(u,{anonymous:r.anonymous});xHe(ky(o,"dataset_description.json"),`${JSON.stringify(d,null,2)}
|
|
184
184
|
`);let f=await C(["git","add","dataset_description.json"],{cwd:o});if(f.exitCode!==0){let I=`Failed to stage scrubbed metadata: ${f.stderr.trim()}`;throw c.fail(I),Error(I)}let m=await C(["git","commit","-m",`Scrub dataset_description.json for exemplar clone (source: ${t})`],{cwd:o});if(m.exitCode!==0&&!m.stdout.includes("nothing to commit")){let I=`Failed to commit scrubbed metadata: ${m.stderr.trim()}`;throw c.fail(I),Error(I)}c.succeed("Scrubbed dataset_description.json");let p=r.name||(typeof d.Name==="string"?d.Name:void 0),h=D("Creating exemplar dataset record...").start();try{let I=await GY({dataset_id:e,source_id:t,name:p,description:r.description,...r.anonymous?{anonymous:!0}:{}});h.succeed(`Created ${I.dataset_id} (${I.github_repo})`)}catch(I){let B=I instanceof Error?I.message:String(I),H=I instanceof q&&I.statusCode===409;if(H&&B.includes("Dataset"))h.warn(`Exemplar ${e} already exists, continuing...`);else if(H){let ie=`GitHub repo for ${e} exists but has no dataset record \u2014 a previous run failed between repo creation and the D1 insert. Delete the orphan with 'nemar admin delete-dataset ${e}' (or remove the repo manually) and re-run. Original: ${B}`;throw h.fail(ie),Error(ie)}else{let ie=`Failed to create exemplar: ${B}`;throw h.fail(ie),Error(ie)}}await fy(o,(I,B)=>{console.log(l.yellow(` Warning: could not mark ${I} as annex-ignore (${B}). Run 'git config remote.${I}.annex-ignore true' manually.`))},["nemar-s3"]);let y=D("Configuring nemar-s3-dev remote...").start(),b=EHe(),x=await kr(o,{name:"nemar-s3-dev",bucket:dp,prefix:`${e}/objects`,region:qa},b);if(!x.success){let I=`Failed to configure nemar-s3-dev: ${x.error}`;throw y.fail(I),Error(I)}let v=await qs(o,"nemar-s3-dev");if(!v)throw y.fail("Failed to get nemar-s3-dev remote UUID"),Error("Failed to get nemar-s3-dev remote UUID");y.succeed("Configured nemar-s3-dev remote");let S=D("Configuring NEMAR remote...").start(),A=await C(["git","remote","remove","origin"],{cwd:o});if(A.exitCode!==0&&!A.stderr.includes("No such remote")){let I=`Failed to remove source remote: ${A.stderr.trim()}`;throw S.fail(I),Error(I)}let E=`git@github.com:${up}/${e}.git`,P=await qo(o,E,"origin");if(!P.success){let I=`Failed to configure remote: ${P.error}`;throw S.fail(I),Error(I)}S.succeed("Configured NEMAR remote");let k=D(`Pushing to ${up}...`).start(),M=await Tt(o,"origin");if(!M.success){let I=`Failed to push: ${M.error}`;throw k.fail(I),Error(I)}if(M.warning){let I=`Pushed main but git-annex branch failed: ${M.warning}. Aborting \u2014 the exemplar would be uncloneable.`;throw k.fail(I),Error(I)}return k.succeed(`Pushed to ${up}`),console.log(l.green(`[prepare] done: ${e}`)),{datasetPath:o,xxId:e,sourceId:t,nemarS3DevUuid:v}}async function OHe(e,t={}){let{xxId:r,sourceId:n}=e;console.log(l.cyan(`
|
|
185
185
|
[copy] ${n} -> ${r}
|
|
186
186
|
`));let o=await P2(n,r,"objects/","objects");if(t.includeDerived){await P2(n,r,"zarr/","zarr"),await P2(n,r,"archives/","archives");let s=(await Wee(n,r,"version/")).filter((a)=>a.key.endsWith("-records.json"));if(s.length>0){let a=D(`[records.json] copying ${s.length} file(s)...`).start(),c=await ap(s,qa,zee);if(c.failed.length>0)throw a.fail(`[records.json] ${c.failed.length} of ${s.length} failed`),Error(`[records.json] ${c.failed.length} of ${s.length} file(s) failed to copy: ${c.failed.slice(0,5).map((u)=>`${u.key}: ${u.error}`).join("; ")}`);a.succeed(`[records.json] copied ${c.copied}`)}}return{keys:o}}function FHe(e,t){return e.filter((r)=>!nn(r,t))}async function PHe(e,t,r={}){let{datasetPath:n,xxId:o,sourceId:i,nemarS3DevUuid:s}=e;if(console.log(l.cyan(`
|
package/package.json
CHANGED