nemar-cli 0.9.5-dev.1826 → 0.9.5-dev.1842

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 +3 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -102,7 +102,7 @@ ${H.dim(`eta: ${D.eta}`)}`:"";process.stderr.write(`${F}
102
102
  ${$} ${D.message}
103
103
  ${Q}${J}
104
104
  ${F}
105
- `)}var vp=!1;var Sz=S(()=>{e0()});var xp;var yp=S(()=>{xp={name:"nemar-cli",version:"0.9.5-dev.1826",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",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","README.md","LICENSE"],dependencies:{chalk:"^5.3.0",commander:"^12.1.0",conf:"^13.0.1","fuse.js":"^7.1.0",inquirer:"^9.2.15",ora:"^8.0.1",zod:"^3.23.8"},"lint-staged":{"src/**/*.{ts,tsx}":["biome check --fix"],"backend/src/**/*.{ts,tsx}":["biome check --fix"]},devDependencies:{"@biomejs/biome":"1.9.4","@types/bcryptjs":"^3.0.0","@types/bun":"latest","@types/inquirer":"^9.0.7",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 T3;var ZX=S(()=>{yp();T3=xp.version});function d(D){if(D instanceof Error)return D.message;return String(D)}var g,w3;var Z1=S(()=>{g=class g extends Error{statusCode;details;step;constructor(D,F,$,Q){super(F);this.statusCode=D;this.details=$;this.step=Q;this.name="ApiError"}};w3=class w3 extends g{mode;eta;constructor(D,F,$,Q){super(503,F,Q);this.mode=D;this.eta=$;this.name="MaintenanceError"}}});function _p(){if(process.env.TEST_API_URL)return process.env.TEST_API_URL;if(vz)return kz;return ZD().apiUrl||kz}async function y(D,F={},$=!1,Q){let J=`${_p()}${D}`,Y={"Content-Type":"application/json","X-CLI-Version":T3,...F.headers};if($){let B=ZD();if(!B.apiKey&&$===!0)throw new g(401,"Not authenticated. Run 'nemar auth login' first.");if(B.apiKey)Y.Authorization=`Bearer ${B.apiKey}`}let X;try{X=await fetch(J,{...F,headers:Y})}catch(B){throw new g(0,`Network error: Could not connect to ${_p()}`,{originalError:B instanceof Error?B.message:String(B)})}let G;try{G=await X.json()}catch{throw new g(X.status,`Invalid response from server (status ${X.status})`)}if(!X.ok){if(X.status===503&&(G.mode==="read-only"||G.mode==="full")){let B=typeof G.message==="string"?G.message:"NEMAR is in maintenance mode. Please retry shortly.",W=typeof G.eta==="string"?G.eta:null,q=new w3(G.mode,B,W,G.details);throw zX(q),q}throw new g(X.status,G.error||G.message||"Request failed",G.details,typeof G.step==="string"?G.step:void 0)}if(Q){let B=Q.safeParse(G);if(!B.success)throw new g(X.status,`Response for ${D} did not match the expected NEMAR contract`,{issues:B.error.issues});return B.data}return G}var kz="https://nemar-api-dev.sccn-org.workers.dev",vz;var S4=S(()=>{I4();Sz();ZX();Z1();vz=kz.includes("workers.dev")});async function fp(D,F){let $=new URLSearchParams;if(D)$.set("status",D);if(F)$.set("role",F);let Q=$.toString()?`?${$.toString()}`:"";return y(`/admin/users${Q}`,{},!0)}async function bp(D){return y(`/admin/approve/${D}`,{method:"POST"},!0)}async function gp(D){return y(`/admin/revoke/${D}`,{method:"POST"},!0)}async function hp(D,F){return y(`/admin/users/${D}/role`,{method:"POST",body:JSON.stringify({role:F})},!0)}async function yz(D,F){return y(`/admin/datasets/${D}/visibility`,{method:"PATCH",body:JSON.stringify({visibility:F})},!0)}async function up(D){return y(`/datasets/${D}/publish`,{method:"POST"},!0)}async function mp(D){return y(`/admin/datasets/${D}/ci`,{},!0)}async function T6(D){return y(`/admin/datasets/${D}/ci`,{method:"POST"},!0)}async function xz(D){return y(`/admin/datasets/${D}/ci/validate`,{method:"POST"},!0)}async function _z(D){return y(`/admin/datasets/${D}/ci/sync`,{method:"POST"},!0)}async function lp(D,F){return y(`/admin/datasets/${D}/doi/concept`,{method:"POST",body:JSON.stringify(F)},!0)}async function B$(D){return y(`/admin/datasets/${D}/doi`,{},!0)}async function fz(D,F){return y(`/admin/datasets/${D}/doi/update`,{method:"POST",body:JSON.stringify(F)},!0)}async function pp(D,F){return y(`/admin/datasets/${D}/withdraw`,{method:"POST",body:JSON.stringify({reason:F.reason,dry_run:F.dryRun})},!0)}async function dp(D,F){return y(`/admin/datasets/${D}/restore`,{method:"POST",body:JSON.stringify({dry_run:F.dryRun})},!0)}async function cp(D){return y(`/admin/datasets/${D}/reset`,{method:"POST"},!0)}async function np(D,F=!1){return y(`/admin/datasets/${D}`,{method:"DELETE",headers:{"Content-Type":"application/json"},body:JSON.stringify({force:F})},!0)}async function ip(D){return y("/admin/datasets/bulk-delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({dataset_ids:D})},!0)}async function ap(D){return y("/admin/datasets/import",{method:"POST",body:JSON.stringify(D)},!0)}async function op(D){return y("/admin/datasets/exemplar",{method:"POST",body:JSON.stringify(D)},!0)}async function sp(D){return y(`/admin/datasets/${D}/exemplar/remint-dois`,{method:"POST"},!0)}async function bz(D,F){let $=new URLSearchParams;if(D)$.set("status",D);if(F!==void 0)$.set("blocklisted",F?"1":"0");let Q=$.toString();return y(`/admin/imports${Q?`?${Q}`:""}`,{},!0)}async function rp(D){return y(`/admin/imports/${D}/rollback`,{method:"POST"},!0)}async function tp(D){return y(`/admin/imports/${D}/retry`,{method:"POST"},!0)}async function gz(D){return y(`/admin/imports/${D}/verify`,{method:"POST"},!0)}async function ep(D){return y("/admin/imports/dispatch-cooldown",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({dataset_ids:D})},!0)}async function w5(D,F){return y(`/admin/datasets/${D}/reindex`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(F??{})},!0)}async function Dd(D,F){return y("/admin/datasets/reindex/bulk",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({filter:D,...F??{}})},!0)}async function Fd(D){let F=D?.limit??15;return y(`/admin/datasets/hed-sweep?limit=${encodeURIComponent(String(F))}`,{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function $d(){return y("/admin/datasets/hed-sweep?reset=1",{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Qd(D){let F=D?.limit??15,$=new URLSearchParams({limit:String(F)});if(D?.olderThan!=null)$.set("older-than",String(D.olderThan));if(D?.before!=null)$.set("before",D.before);return y(`/admin/datasets/data-integrity-sweep?${$.toString()}`,{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Jd(){return y("/admin/datasets/data-integrity-sweep?reset=1",{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Yd(D,F){let $=F?.write?"":"?dry_run=1";return y(`/admin/datasets/${D}/availability-report${$}`,{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Xd(D){let F=D?.limit??10,$=new URLSearchParams({limit:String(F)});if(D?.missingOnly)$.set("missing-only","1");return y(`/admin/datasets/availability-report-sweep?${$.toString()}`,{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Gd(){return y("/admin/datasets/availability-report-sweep?reset=1",{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Bd(D){let F=new URLSearchParams;if(D?.prefix)F.set("prefix",D.prefix);if(D?.visibility)F.set("visibility",D.visibility);if(D?.limit)F.set("limit",String(D.limit));let $=F.toString()?`?${F.toString()}`:"";return y(`/admin/fleet/drift${$}`,{},!0)}async function P5(D,F){return y(`/admin/datasets/${D}/enforce`,{method:"POST",body:JSON.stringify({dry_run:F})},!0)}async function Wd(D){return y("/admin/datasets/enforce/bulk",{method:"POST",body:JSON.stringify({prefix:D.prefix,visibility:D.visibility,limit:D.limit,dry_run:D.dryRun})},!0)}async function Hd(D,F=!1){return y(`/admin/datasets/${D}/revalidate${F?"?force=true":""}`,{method:"POST"},!0)}async function qd(D){let F=D?`?user=${encodeURIComponent(D)}`:"";return y(`/admin/email-preferences${F}`,{},!0)}async function Kd(D,F){let $=F?`?user=${encodeURIComponent(F)}`:"";return y(`/admin/email-preferences${$}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(D)},!0)}async function hz(D){return y("/admin/notify",{method:"POST",body:JSON.stringify(D)},!0)}async function zd(){return y("/admin/summary/coverage",{},!0)}async function Zd(D,F,$){return y("/admin/manifest/dispatch",{method:"POST",body:JSON.stringify({dataset_id:D,version:F,skip_canary:$?.skipCanary??!1})},!0)}var I5=S(()=>{S4()});async function s8(D,F){return y(`/datasets/${D}/upload-credentials`,{method:"POST",body:JSON.stringify({duration_seconds:F})},!0)}async function uz(D,F){return y(`/datasets/${D}/download-credentials`,{method:"POST",body:JSON.stringify({duration_seconds:F})},!0)}async function Vd(D){return y(`/admin/datasets/${D}/files`,{},!0)}async function Ed(D){let F,$=0,Q=[],J=!0;while(J){let Y=await y(`/admin/datasets/${D}/s3-lock`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({continuation_token:F})},!0);if($+=Y.locked,Y.failed?.length)Q.push(...Y.failed);if(J=Y.hasMore,F=Y.continuation_token,J&&!F)throw new g(500,"S3 lock paginated response missing continuation_token; aborting to avoid infinite loop")}return{locked:$,failed:Q}}var S5=S(()=>{S4();Z1()});async function EX(D){return y(`/datasets/${D}/ci/status`,{},!0)}async function Ud(D){return y(`/datasets/${D}/manifest`,{},!0)}async function UX(D,F){return y(`/datasets/${D}/manifest/${F}`,{},!0)}function Ld(D){let F=D;if(!F.status)F.status="active";else if(!["active","archived","deleted"].includes(F.status))throw Error(`Invalid dataset status: ${F.status}`);if(!F.visibility)F.visibility="private";else if(!["public","private"].includes(F.visibility))throw Error(`Invalid dataset visibility: ${F.visibility}`);return F}async function k5(D={}){let F=new URLSearchParams;if(D.mine)F.set("mine","true");if(D.search)F.set("search",D.search);if(D.modality)F.set("modality",D.modality);if(D.author)F.set("author",D.author);if(D.task)F.set("task",D.task);if(D.license)F.set("license",D.license);if(D.hasDoi)F.set("has_doi","true");if(D.hasHed)F.set("has_hed","1");if(D.dataComplete)F.set("data_complete","1");if(D.recent)F.set("recent",String(D.recent));if(D.sort)F.set("sort",D.sort);if(D.limit!=null)F.set("limit",String(D.limit));if(D.offset!=null)F.set("offset",String(D.offset));if(D.owner)F.set("owner",D.owner);let $=F.toString()?`?${F.toString()}`:"",Q=await y(`/datasets${$}`,{},D.mine?!0:"optional");return Q.datasets=Q.datasets.map(Ld),Q}async function Md(D){return y(`/datasets/resolve/${D}`,{},"optional")}async function Ad(D,F={}){let $=new URLSearchParams({q:D});if(F.modality)$.set("modality",F.modality);if(F.limit)$.set("limit",String(F.limit));if(F.hasHed)$.set("has_hed","1");return y(`/datasets/search?${$.toString()}`,{},"optional")}async function VD(D){let F=await y(`/datasets/${D}`,{},"optional");return Ld(F.dataset)}async function Nd(D){return y(`/datasets/${D}/versions`,{},!0)}async function LX(D){return y("/datasets",{method:"POST",body:JSON.stringify(D)},!0)}async function MX(D){return y(`/datasets/${D}/finalize`,{method:"POST"},!0)}async function mz(D){return y(`/datasets/${D}/request-access`,{method:"POST"},!0)}async function Cd(D,F){let $=F?`?status=${F}`:"";return y(`/datasets/${D}/access-requests${$}`,{},!0)}async function Od(D,F){return y(`/datasets/${D}/access-requests/${F}/approve`,{method:"POST"},!0)}async function jd(D,F){return y(`/datasets/${D}/access-requests/${F}/deny`,{method:"POST"},!0)}async function Rd(D,F){return y(`/datasets/${D}/invite`,{method:"POST",body:JSON.stringify({username:F})},!0)}async function Td(D){return y(`/datasets/${D}/collaborators`,{},!0)}async function wd(D,F){return y(`/admin/datasets/${D}/enrichment`,{method:"POST",body:JSON.stringify(F)},!0)}var VX;var P3=S(()=>{S4();VX=/^\d{4}-\d{4}-\d{4}-\d{3}[\dX]$/});var v5;var vd=S(()=>{v5=["ci_check","enrichment_check","s3_public_read","repo_public","tag_protect","doi_create","update_metadata","update_readme","create_tag","create_release","upload_to_zenodo","publish_doi","version_doi","s3_lock","sync_nemar","notify_user"]});async function y5(D){return y(`/datasets/${D}/publish/request`,{method:"POST"},!0)}async function xd(D){return y(`/datasets/${D}/publish/status`,{},!0)}async function _d(D){return y(`/datasets/${D}/publish/resend`,{method:"POST"},!0)}async function fd(D){let F=D?`?status=${D}`:"";return y(`/admin/publish/requests${F}`,{},!0)}async function bd(D,F){return y(`/admin/publish/${D}/deny`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reason:F})},!0)}function ZS0(D,F=[]){if(D){let $=v5.indexOf(D);if($>=0)return $+1}return Math.min(F.length+1,v5.length)}function VS0(D){if(!(D instanceof g))return!1;if(D.statusCode===0)return!0;if(D.statusCode===429)return!0;if(D.statusCode>=500&&D.statusCode<600)return!0;if(D.statusCode===403&&/repository has been locked/i.test(D.message))return!0;return!1}async function x5(D,F=!1,$=!1,Q=!1,J,Y){let B,W,q=0,K=!1,Z,z=F,E=[],U;function L(A,N,j,R){if(!Y)return;Y({step:A,stepIndex:ZS0(A,N),stepTotal:v5.length,s3LockLocked:j,s3LockTotal:R,s3LockResumed:A==="s3_lock"&&K?!0:void 0})}for(let A=1;A<=5;A++)try{let N,j=!0;do{if(N=await y(`/admin/publish/${D}/approve`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({resume:j?z:!0,sandbox:$,s3_lock_continuation_token:B,s3_lock_total:W,skip_ci_check:Q})},!0),j=!1,N.step_results)E.push(...N.step_results);if(N.s3_lock_total!==void 0)W=N.s3_lock_total;if(N.s3_lock_batch_count!==void 0)q+=N.s3_lock_batch_count;let R=N.step??N.step_results?.[N.step_results.length-1]?.step;if(R&&(R!==Z||R==="s3_lock"))L(R,N.steps_completed??[],R==="s3_lock"?q:void 0,R==="s3_lock"?W:void 0),Z=R;if(N.hasMore&&N.s3_lock_continuation_token!==void 0)B=N.s3_lock_continuation_token;else break}while(N.hasMore);if(E.length>0)N.step_results=yd(E);return N}catch(N){if(U=N,A===5||!VS0(N)){if(N instanceof g&&E.length>0)N.stepResults=yd(E);throw N}let R=N;if(J?.({attempt:A,maxAttempts:5,delayMs:1e4,step:R.step,error:R.message}),await new Promise((C)=>setTimeout(C,1e4)),z=!0,B!==void 0)K=!0}throw U}function yd(D){let F=new Map;for(let $ of D)F.set($.step,$);return Array.from(F.values())}var W$=S(()=>{vd();S4();Z1()});function ud(D){pz=D}function dz(){return pz}function H$(D){if(pz)process.stderr.write(`${D}
105
+ `)}var vp=!1;var Sz=S(()=>{e0()});var xp;var yp=S(()=>{xp={name:"nemar-cli",version:"0.9.5-dev.1842",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",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","README.md","LICENSE"],dependencies:{chalk:"^5.3.0",commander:"^12.1.0",conf:"^13.0.1","fuse.js":"^7.1.0",inquirer:"^9.2.15",ora:"^8.0.1",zod:"^3.23.8"},"lint-staged":{"src/**/*.{ts,tsx}":["biome check --fix"],"backend/src/**/*.{ts,tsx}":["biome check --fix"]},devDependencies:{"@biomejs/biome":"1.9.4","@types/bcryptjs":"^3.0.0","@types/bun":"latest","@types/inquirer":"^9.0.7",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 T3;var ZX=S(()=>{yp();T3=xp.version});function d(D){if(D instanceof Error)return D.message;return String(D)}var g,w3;var Z1=S(()=>{g=class g extends Error{statusCode;details;step;constructor(D,F,$,Q){super(F);this.statusCode=D;this.details=$;this.step=Q;this.name="ApiError"}};w3=class w3 extends g{mode;eta;constructor(D,F,$,Q){super(503,F,Q);this.mode=D;this.eta=$;this.name="MaintenanceError"}}});function _p(){if(process.env.TEST_API_URL)return process.env.TEST_API_URL;if(vz)return kz;return ZD().apiUrl||kz}async function y(D,F={},$=!1,Q){let J=`${_p()}${D}`,Y={"Content-Type":"application/json","X-CLI-Version":T3,...F.headers};if($){let B=ZD();if(!B.apiKey&&$===!0)throw new g(401,"Not authenticated. Run 'nemar auth login' first.");if(B.apiKey)Y.Authorization=`Bearer ${B.apiKey}`}let X;try{X=await fetch(J,{...F,headers:Y})}catch(B){throw new g(0,`Network error: Could not connect to ${_p()}`,{originalError:B instanceof Error?B.message:String(B)})}let G;try{G=await X.json()}catch{throw new g(X.status,`Invalid response from server (status ${X.status})`)}if(!X.ok){if(X.status===503&&(G.mode==="read-only"||G.mode==="full")){let B=typeof G.message==="string"?G.message:"NEMAR is in maintenance mode. Please retry shortly.",W=typeof G.eta==="string"?G.eta:null,q=new w3(G.mode,B,W,G.details);throw zX(q),q}throw new g(X.status,G.error||G.message||"Request failed",G.details,typeof G.step==="string"?G.step:void 0)}if(Q){let B=Q.safeParse(G);if(!B.success)throw new g(X.status,`Response for ${D} did not match the expected NEMAR contract`,{issues:B.error.issues});return B.data}return G}var kz="https://nemar-api-dev.sccn-org.workers.dev",vz;var S4=S(()=>{I4();Sz();ZX();Z1();vz=kz.includes("workers.dev")});async function fp(D,F){let $=new URLSearchParams;if(D)$.set("status",D);if(F)$.set("role",F);let Q=$.toString()?`?${$.toString()}`:"";return y(`/admin/users${Q}`,{},!0)}async function bp(D){return y(`/admin/approve/${D}`,{method:"POST"},!0)}async function gp(D){return y(`/admin/revoke/${D}`,{method:"POST"},!0)}async function hp(D,F){return y(`/admin/users/${D}/role`,{method:"POST",body:JSON.stringify({role:F})},!0)}async function yz(D,F){return y(`/admin/datasets/${D}/visibility`,{method:"PATCH",body:JSON.stringify({visibility:F})},!0)}async function up(D){return y(`/datasets/${D}/publish`,{method:"POST"},!0)}async function mp(D){return y(`/admin/datasets/${D}/ci`,{},!0)}async function T6(D){return y(`/admin/datasets/${D}/ci`,{method:"POST"},!0)}async function xz(D){return y(`/admin/datasets/${D}/ci/validate`,{method:"POST"},!0)}async function _z(D){return y(`/admin/datasets/${D}/ci/sync`,{method:"POST"},!0)}async function lp(D,F){return y(`/admin/datasets/${D}/doi/concept`,{method:"POST",body:JSON.stringify(F)},!0)}async function B$(D){return y(`/admin/datasets/${D}/doi`,{},!0)}async function fz(D,F){return y(`/admin/datasets/${D}/doi/update`,{method:"POST",body:JSON.stringify(F)},!0)}async function pp(D,F){return y(`/admin/datasets/${D}/withdraw`,{method:"POST",body:JSON.stringify({reason:F.reason,dry_run:F.dryRun})},!0)}async function dp(D,F){return y(`/admin/datasets/${D}/restore`,{method:"POST",body:JSON.stringify({dry_run:F.dryRun})},!0)}async function cp(D){return y(`/admin/datasets/${D}/reset`,{method:"POST"},!0)}async function np(D,F=!1){return y(`/admin/datasets/${D}`,{method:"DELETE",headers:{"Content-Type":"application/json"},body:JSON.stringify({force:F})},!0)}async function ip(D){return y("/admin/datasets/bulk-delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({dataset_ids:D})},!0)}async function ap(D){return y("/admin/datasets/import",{method:"POST",body:JSON.stringify(D)},!0)}async function op(D){return y("/admin/datasets/exemplar",{method:"POST",body:JSON.stringify(D)},!0)}async function sp(D){return y(`/admin/datasets/${D}/exemplar/remint-dois`,{method:"POST"},!0)}async function bz(D,F){let $=new URLSearchParams;if(D)$.set("status",D);if(F!==void 0)$.set("blocklisted",F?"1":"0");let Q=$.toString();return y(`/admin/imports${Q?`?${Q}`:""}`,{},!0)}async function rp(D){return y(`/admin/imports/${D}/rollback`,{method:"POST"},!0)}async function tp(D){return y(`/admin/imports/${D}/retry`,{method:"POST"},!0)}async function gz(D){return y(`/admin/imports/${D}/verify`,{method:"POST"},!0)}async function ep(D){return y("/admin/imports/dispatch-cooldown",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({dataset_ids:D})},!0)}async function w5(D,F){return y(`/admin/datasets/${D}/reindex`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(F??{})},!0)}async function Dd(D,F){return y("/admin/datasets/reindex/bulk",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({filter:D,...F??{}})},!0)}async function Fd(D){let F=D?.limit??15;return y(`/admin/datasets/hed-sweep?limit=${encodeURIComponent(String(F))}`,{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function $d(){return y("/admin/datasets/hed-sweep?reset=1",{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Qd(D){let F=D?.limit??15,$=new URLSearchParams({limit:String(F)});if(D?.olderThan!=null)$.set("older-than",String(D.olderThan));if(D?.before!=null)$.set("before",D.before);return y(`/admin/datasets/data-integrity-sweep?${$.toString()}`,{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Jd(){return y("/admin/datasets/data-integrity-sweep?reset=1",{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Yd(D,F){let $=F?.write?"":"?dry_run=1";return y(`/admin/datasets/${D}/availability-report${$}`,{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Xd(D){let F=D?.limit??10,$=new URLSearchParams({limit:String(F)});if(D?.missingOnly)$.set("missing-only","1");return y(`/admin/datasets/availability-report-sweep?${$.toString()}`,{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Gd(){return y("/admin/datasets/availability-report-sweep?reset=1",{method:"POST",headers:{"Content-Type":"application/json"}},!0)}async function Bd(D){let F=new URLSearchParams;if(D?.prefix)F.set("prefix",D.prefix);if(D?.visibility)F.set("visibility",D.visibility);if(D?.limit)F.set("limit",String(D.limit));let $=F.toString()?`?${F.toString()}`:"";return y(`/admin/fleet/drift${$}`,{},!0)}async function P5(D,F){return y(`/admin/datasets/${D}/enforce`,{method:"POST",body:JSON.stringify({dry_run:F})},!0)}async function Wd(D){return y("/admin/datasets/enforce/bulk",{method:"POST",body:JSON.stringify({prefix:D.prefix,visibility:D.visibility,limit:D.limit,dry_run:D.dryRun})},!0)}async function Hd(D,F=!1){return y(`/admin/datasets/${D}/revalidate${F?"?force=true":""}`,{method:"POST"},!0)}async function qd(D){let F=D?`?user=${encodeURIComponent(D)}`:"";return y(`/admin/email-preferences${F}`,{},!0)}async function Kd(D,F){let $=F?`?user=${encodeURIComponent(F)}`:"";return y(`/admin/email-preferences${$}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(D)},!0)}async function hz(D){return y("/admin/notify",{method:"POST",body:JSON.stringify(D)},!0)}async function zd(){return y("/admin/summary/coverage",{},!0)}async function Zd(D,F,$){return y("/admin/manifest/dispatch",{method:"POST",body:JSON.stringify({dataset_id:D,version:F,skip_canary:$?.skipCanary??!1})},!0)}var I5=S(()=>{S4()});async function s8(D,F){return y(`/datasets/${D}/upload-credentials`,{method:"POST",body:JSON.stringify({duration_seconds:F})},!0)}async function uz(D,F){return y(`/datasets/${D}/download-credentials`,{method:"POST",body:JSON.stringify({duration_seconds:F})},!0)}async function Vd(D){return y(`/admin/datasets/${D}/files`,{},!0)}async function Ed(D){let F,$=0,Q=[],J=!0;while(J){let Y=await y(`/admin/datasets/${D}/s3-lock`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({continuation_token:F})},!0);if($+=Y.locked,Y.failed?.length)Q.push(...Y.failed);if(J=Y.hasMore,F=Y.continuation_token,J&&!F)throw new g(500,"S3 lock paginated response missing continuation_token; aborting to avoid infinite loop")}return{locked:$,failed:Q}}var S5=S(()=>{S4();Z1()});async function EX(D){return y(`/datasets/${D}/ci/status`,{},!0)}async function Ud(D){return y(`/datasets/${D}/manifest`,{},!0)}async function UX(D,F){return y(`/datasets/${D}/manifest/${F}`,{},!0)}function Ld(D){let F=D;if(!F.status)F.status="active";else if(!["active","archived","deleted"].includes(F.status))throw Error(`Invalid dataset status: ${F.status}`);if(!F.visibility)F.visibility="private";else if(!["public","private"].includes(F.visibility))throw Error(`Invalid dataset visibility: ${F.visibility}`);return F}async function k5(D={}){let F=new URLSearchParams;if(D.mine)F.set("mine","true");if(D.search)F.set("search",D.search);if(D.modality)F.set("modality",D.modality);if(D.author)F.set("author",D.author);if(D.task)F.set("task",D.task);if(D.license)F.set("license",D.license);if(D.hasDoi)F.set("has_doi","true");if(D.hasHed)F.set("has_hed","1");if(D.dataComplete)F.set("data_complete","1");if(D.recent)F.set("recent",String(D.recent));if(D.sort)F.set("sort",D.sort);if(D.limit!=null)F.set("limit",String(D.limit));if(D.offset!=null)F.set("offset",String(D.offset));if(D.owner)F.set("owner",D.owner);let $=F.toString()?`?${F.toString()}`:"",Q=await y(`/datasets${$}`,{},D.mine?!0:"optional");return Q.datasets=Q.datasets.map(Ld),Q}async function Md(D){return y(`/datasets/resolve/${D}`,{},"optional")}async function Ad(D,F={}){let $=new URLSearchParams({q:D});if(F.modality)$.set("modality",F.modality);if(F.limit)$.set("limit",String(F.limit));if(F.hasHed)$.set("has_hed","1");return y(`/datasets/search?${$.toString()}`,{},"optional")}async function VD(D){let F=await y(`/datasets/${D}`,{},"optional");return Ld(F.dataset)}async function Nd(D){return y(`/datasets/${D}/versions`,{},!0)}async function LX(D){return y("/datasets",{method:"POST",body:JSON.stringify(D)},!0)}async function MX(D){return y(`/datasets/${D}/finalize`,{method:"POST"},!0)}async function mz(D){return y(`/datasets/${D}/request-access`,{method:"POST"},!0)}async function Cd(D,F){let $=F?`?status=${F}`:"";return y(`/datasets/${D}/access-requests${$}`,{},!0)}async function Od(D,F){return y(`/datasets/${D}/access-requests/${F}/approve`,{method:"POST"},!0)}async function jd(D,F){return y(`/datasets/${D}/access-requests/${F}/deny`,{method:"POST"},!0)}async function Rd(D,F){return y(`/datasets/${D}/invite`,{method:"POST",body:JSON.stringify({username:F})},!0)}async function Td(D){return y(`/datasets/${D}/collaborators`,{},!0)}async function wd(D,F){return y(`/admin/datasets/${D}/enrichment`,{method:"POST",body:JSON.stringify(F)},!0)}var VX;var P3=S(()=>{S4();VX=/^\d{4}-\d{4}-\d{4}-\d{3}[\dX]$/});var v5;var vd=S(()=>{v5=["ci_check","enrichment_check","s3_public_read","repo_public","tag_protect","doi_create","update_metadata","update_readme","create_tag","create_release","upload_to_zenodo","publish_doi","version_doi","s3_lock","sync_nemar","notify_user"]});async function y5(D){return y(`/datasets/${D}/publish/request`,{method:"POST"},!0)}async function xd(D){return y(`/datasets/${D}/publish/status`,{},!0)}async function _d(D){return y(`/datasets/${D}/publish/resend`,{method:"POST"},!0)}async function fd(D){let F=D?`?status=${D}`:"";return y(`/admin/publish/requests${F}`,{},!0)}async function bd(D,F){return y(`/admin/publish/${D}/deny`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({reason:F})},!0)}function ZS0(D,F=[]){if(D){let $=v5.indexOf(D);if($>=0)return $+1}return Math.min(F.length+1,v5.length)}function VS0(D){if(!(D instanceof g))return!1;if(D.statusCode===0)return!0;if(D.statusCode===429)return!0;if(D.statusCode>=500&&D.statusCode<600)return!0;if(D.statusCode===403&&/repository has been locked/i.test(D.message))return!0;return!1}async function x5(D,F=!1,$=!1,Q=!1,J,Y){let B,W,q=0,K=!1,Z,z=F,E=[],U;function L(A,N,j,R){if(!Y)return;Y({step:A,stepIndex:ZS0(A,N),stepTotal:v5.length,s3LockLocked:j,s3LockTotal:R,s3LockResumed:A==="s3_lock"&&K?!0:void 0})}for(let A=1;A<=5;A++)try{let N,j=!0;do{if(N=await y(`/admin/publish/${D}/approve`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({resume:j?z:!0,sandbox:$,s3_lock_continuation_token:B,s3_lock_total:W,skip_ci_check:Q})},!0),j=!1,N.step_results)E.push(...N.step_results);if(N.s3_lock_total!==void 0)W=N.s3_lock_total;if(N.s3_lock_batch_count!==void 0)q+=N.s3_lock_batch_count;let R=N.step??N.step_results?.[N.step_results.length-1]?.step;if(R&&(R!==Z||R==="s3_lock"))L(R,N.steps_completed??[],R==="s3_lock"?q:void 0,R==="s3_lock"?W:void 0),Z=R;if(N.hasMore&&N.s3_lock_continuation_token!==void 0)B=N.s3_lock_continuation_token;else break}while(N.hasMore);if(E.length>0)N.step_results=yd(E);return N}catch(N){if(U=N,A===5||!VS0(N)){if(N instanceof g&&E.length>0)N.stepResults=yd(E);throw N}let R=N;if(J?.({attempt:A,maxAttempts:5,delayMs:1e4,step:R.step,error:R.message}),await new Promise((C)=>setTimeout(C,1e4)),z=!0,B!==void 0)K=!0}throw U}function yd(D){let F=new Map;for(let $ of D)F.set($.step,$);return Array.from(F.values())}var W$=S(()=>{vd();S4();Z1()});function ud(D){pz=D}function dz(){return pz}function H$(D){if(pz)process.stderr.write(`${D}
106
106
  `)}var pz=!1;var md={};v6(md,{runCommand:()=>T});var{spawn:ES0}=globalThis.Bun;async function T(D,F={}){let $={...process.env,GIT_TERMINAL_PROMPT:"0",...F.env};for(let W of F.unsetEnv??[])delete $[W];let Q=ES0({cmd:D,cwd:F.cwd,stdout:"pipe",stderr:"pipe",env:$}),J=!1,Y;if(F.timeout)Y=setTimeout(()=>{J=!0,Q.kill()},F.timeout);if(dz()){let W=F.cwd?` (cwd=${F.cwd})`:"";H$(H.dim(`$ ${D.join(" ")}${W}`))}let X=await new Response(Q.stdout).text(),G=await new Response(Q.stderr).text(),B=await Q.exited;if(Y)clearTimeout(Y);if(dz()){if(X.trim())H$(H.dim(X.trimEnd()));if(G.trim())H$(H.yellow(G.trimEnd()));H$(H.dim(`(exit ${B})`))}if(J)return{stdout:X,stderr:G||`Command timed out after ${Math.round(F.timeout/1000)}s`,exitCode:B??1};return{stdout:X,stderr:G,exitCode:B}}var V1=S(()=>{e0()});async function US0(){try{let{exitCode:D,stderr:F}=await T(["ssh","-T","-o","BatchMode=yes","-o","ConnectTimeout=5","git@github.com"]),$=F.includes("successfully authenticated");if(!$)console.warn("SSH test to github.com failed:",{exitCode:D,stderr:F.trim().slice(0,500)});return{works:$,error:$?void 0:F.trim().slice(0,500)}}catch(D){let F=D instanceof Error?D.message:String(D);return console.warn("SSH test exception:",F),{works:!1,error:F}}}async function q$(){try{let{stdout:D,exitCode:F,stderr:$}=await T(["gh","auth","token"]);if(F!==0)return console.warn("gh CLI returned non-zero exit code:",F),console.warn("stderr:",$),{token:null,error:`gh auth token failed: ${$.trim()||"unknown error"}`};if(!D.trim())return console.warn("gh auth token returned empty output"),{token:null,error:"gh CLI returned empty token"};return{token:D.trim()}}catch(D){let F=D instanceof Error?D.message:String(D);if(console.error("Failed to get GitHub token from gh CLI:",F),F.includes("ENOENT")||F.includes("not found"))return{token:null,error:"gh CLI not installed (command not found)"};return{token:null,error:`gh CLI error: ${F}`}}}async function AX(D){try{let{stdout:F,exitCode:$,stderr:Q}=await T(["gh","api","user","--jq",".login"]);if($!==0){if(Q.includes("not logged in")||Q.includes("auth login"))return{authenticated:!1,error:"gh CLI not authenticated. Run 'gh auth login' to authenticate."};return{authenticated:!1,error:`gh CLI error: ${Q.trim()||"unknown error"}`}}let J=F.trim();if(!J)return{authenticated:!1,error:"gh CLI returned empty username"};if(D){let Y=J.toLowerCase()===D.toLowerCase();return{authenticated:!0,username:J,matches:Y,error:Y?void 0:`gh CLI authenticated as '${J}', expected '${D}'`}}return{authenticated:!0,username:J}}catch(F){let $=F instanceof Error?F.message:String(F);if($.includes("ENOENT")||$.includes("not found"))return{authenticated:!1,error:"gh CLI not installed. Install from https://cli.github.com/"};return{authenticated:!1,error:`Failed to verify gh CLI: ${$}`}}}async function NX(D){if(!/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(D))return{accepted:!1,error:`Invalid repository format: ${D}`};let{stdout:F,exitCode:$,stderr:Q}=await T(["gh","api","/user/repository_invitations"]);if($!==0){if(Q.includes("not logged in")||Q.includes("auth login"))return{accepted:!1,error:"gh CLI not authenticated. Run 'gh auth login' to authenticate."};if(Q.includes("API rate limit")||Q.includes("403"))return{accepted:!1,error:"GitHub API rate limit exceeded. Please try again in a few minutes."};if(Q.includes("ENOENT")||Q.includes("not found"))return{accepted:!1,error:"gh CLI not installed. Install from https://cli.github.com/"};return{accepted:!1,error:`Failed to list invitations: ${Q.trim()||"unknown error"}`}}let J=null;try{J=JSON.parse(F||"[]").find((W)=>W.repository.full_name===D)?.id??null}catch(G){let B=G instanceof Error?G.message:String(G);return console.error("Failed to parse GitHub invitations response:",B),console.error(" Raw response (first 500 chars):",(F||"").slice(0,500)),{accepted:!1,error:`Failed to parse GitHub API response: ${B}`}}if(!J){let{exitCode:G}=await T(["gh","api",`/repos/${D}`,"--silent"]);if(G===0)return{accepted:!0,alreadyCollaborator:!0};return{accepted:!1,error:`No pending invitation found for ${D}. You may need to accept it manually via GitHub.`}}let{exitCode:Y,stderr:X}=await T(["gh","api","--method","PATCH",`/user/repository_invitations/${J}`]);if(Y!==0)return{accepted:!1,error:`Failed to accept invitation: ${X.trim()||"unknown error"}`};return{accepted:!0}}async function r8(D,F,$="origin"){let Q=F;if(process.env.GH_TOKEN&&F.startsWith("git@github.com:")){let J=process.env.GH_TOKEN.trim();if(!J||/\s/.test(J))return{success:!1,error:"GH_TOKEN environment variable is set but appears malformed (empty or contains whitespace)"};Q=`https://github.com/${F.replace("git@github.com:","")}`,await T(["git","config","credential.https://github.com.helper",`!printf 'username=x-access-token\\npassword=${J}'`],{cwd:D})}else if(F.startsWith("git@github.com:")){let J=F.replace("git@github.com:",""),Y=await q$();if(Y.token)Q=`https://github.com/${J}`,await T(["git","config","credential.https://github.com.helper",`!printf 'username=x-access-token\\npassword=${Y.token}'`],{cwd:D});else{let X=await US0();if(X.works)Q=F;else return{success:!1,error:`GitHub authentication not configured.
107
107
 
108
108
  gh CLI failed: ${Y.error||"could not get token"}
@@ -448,7 +448,7 @@ ${X}/${Y.length} data_complete`))});_0.addCommand(MZ);var _k0=/^xx0999\d{2}$/,fk
448
448
  ${B} of ${G.length} fleet entries failed`)),process.exit(1);return}if(!D)console.error(H.red("An xx-id argument is required unless --all is passed")),process.exit(1);if(!_k0.test(D))console.error(H.red(`Invalid exemplar id "${D}". Expected xx099900-xx099999.`)),process.exit(1);let X=F.source;if(!X)try{X=Q(J).find((B)=>B.xx_id===D)?.source_id}catch(G){console.error(H.red(`Failed to load fleet file: ${d(G)}`)),process.exit(1)}if(!X)console.error(H.red(`No source dataset for ${D}. Pass --source <nm/on id> or add it to the fleet file.`)),process.exit(1);if(!fk0.test(X))console.error(H.red(`Invalid source id "${X}". Expected an nm/on dataset id.`)),process.exit(1);try{await $({xxId:D,sourceId:X,...Y})}catch(G){console.error(H.red(`
449
449
  Exemplar clone failed: ${d(G)}`)),process.exit(1)}});hX.command("status").description("Show which fleet entries have been cloned and their current state").option("--fleet-file <path>","Path to the fleet JSON (default: scripts/exemplar-fleet.json)").action(async(D)=>{if(!z0())return;let{loadExemplarFleet:F}=await Promise.resolve().then(() => (EZ(),VZ)),$=D.fleetFile||Rn(),Q;try{Q=F($)}catch(J){console.error(H.red(`Failed to load fleet file: ${d(J)}`)),process.exit(1)}console.log(H.bold(`
450
450
  Exemplar Fleet (${Q.length})
451
- `));for(let J of Q)try{let Y=await VD(J.xx_id);console.log(` ${J.xx_id.padEnd(10)} ${J.modality.padEnd(6)} <- ${J.source_id.padEnd(10)} ${H.green(Y.visibility)} doi=${Y.concept_doi??"none"}`)}catch(Y){if(Y instanceof g&&Y.statusCode===404)console.log(` ${J.xx_id.padEnd(10)} ${J.modality.padEnd(6)} <- ${J.source_id.padEnd(10)} ${H.dim("not created")}`);else console.log(` ${J.xx_id.padEnd(10)} ${J.modality.padEnd(6)} <- ${J.source_id.padEnd(10)} ${H.red(`status check failed (${d(Y)})`)}`)}});hX.command("remint-dois").description("Re-mint an exemplar's sandbox concept DOI (idempotent)").argument("<xx-id>","Exemplar dataset id (e.g., xx099900)").action(async(D)=>{if(!z0())return;let F=O(`Re-minting DOIs for ${D}...`).start();try{let $=await sp(D);F.succeed(`Re-minted ${$.concept_doi} (status: ${$.status})`);for(let Q of $.warnings??[])console.log(H.yellow(` ${Q}`))}catch($){s0($,F,"Failed to re-mint exemplar DOIs",{400:"Not an exemplar dataset",403:"Exemplar DOI re-mint is disabled in production",404:"Dataset not found"}),process.exit(1)}});_0.addCommand(hX);function En(D,F){let $=D.enrichment.status,Q=$==="ok"?H.green("enrich:ok"):$==="failed"?H.red("enrich:failed"):H.dim("enrich:skipped"),J=D.sync.status,Y=J==="ok"?H.green("sync:ok"):J==="failed"?H.red("sync:failed"):H.dim("sync:skipped"),X=D.sync.metadata_columns_written===!0?H.green("cols:written"):D.sync.metadata_columns_error?H.red("cols:failed"):"",G=F?.showRef&&D.enrichment.ref?H.dim(`@${D.enrichment.ref}`):"";if(console.log(` ${D.dataset_id.padEnd(12)} ${Q}${G} ${Y} ${X}`),D.enrichment.error)console.log(H.red(` enrichment: ${D.enrichment.error}`));if(D.sync.metadata_columns_error)console.log(H.red(` metadata columns: ${D.sync.metadata_columns_error}`))}var uX=new S0("fleet").description("Governance drift reporting and enforcement (epic #713)");uX.command("drift").description("Report dataset repos that are off the governance spec").option("--prefix <prefix>","Filter datasets by id prefix (e.g. nm, on)").option("--visibility <vis>","Filter by visibility: public or private").option("--limit <n>","Max repos to scan (default 25, max 50)","25").option("--json","Output raw JSON").action(async(D)=>{if(!m0())console.log(H.red("Error: Not authenticated")),process.exit(1);if(D.visibility&&!["public","private"].includes(D.visibility))console.log(H.red("Error: --visibility must be public or private")),process.exit(1);let F=O("Scanning fleet for drift...").start();try{let $=await Bd({prefix:D.prefix,visibility:D.visibility,limit:Number.parseInt(D.limit,10)||25});if(F.stop(),D.json){console.log(JSON.stringify($,null,2));return}console.log(),console.log(H.bold(`Fleet drift (scanned ${$.scanned}):`));let Q=["PUBLIC_UNPROTECTED","RED_REQUIRED_CHECK","CONTEXT_NAME_MISMATCH","MISSING_REQUIRED_WORKFLOW","PRIVATE_WITH_STRAY_READ","DEFAULT_BRANCH_OUTLIER","DEPRECATED_WORKFLOW_PRESENT","COMPLIANT"];for(let J of Q){let Y=$.buckets[J];if(!Y||Y.length===0)continue;let X=J==="COMPLIANT"?H.green:H.yellow;if(console.log(` ${X(J.padEnd(28))} ${Y.length}`),J!=="COMPLIANT"){let G=Y.slice(0,20).join(", ");console.log(H.dim(` ${G}${Y.length>20?` ... +${Y.length-20}`:""}`))}}console.log()}catch($){if($ instanceof g)F.fail($.message);else F.fail("Failed to scan fleet"),console.log(H.red(` ${$.message}`));process.exit(1)}});uX.command("enforce").description("Bring dataset repos to the governance spec (single or --all). Dry-run by default.").argument("[dataset-id]","Dataset to enforce (omit when using --all)").option("--all","Bulk enforce across a filtered set (owner-only)").option("--apply","Actually apply changes (default is a dry run)").option("--prefix <prefix>","Bulk: filter by id prefix").option("--visibility <vis>","Bulk: filter by visibility (public|private)").option("--limit <n>","Bulk: max repos (default 25, max 50)","25").option("--json","Output raw JSON").action(async(D,F)=>{if(!m0())console.log(H.red("Error: Not authenticated")),process.exit(1);if(!D&&!F.all)console.log(H.red("Error: provide a dataset-id or use --all")),process.exit(1);if(D&&F.all)console.log(H.red("Error: provide a dataset-id OR --all, not both")),process.exit(1);let $=!F.apply,Q=$?"[dry-run] ":"",J=O(`${Q}Enforcing spec...`).start();try{if(D){let Y=await P5(D,$);if(J.succeed(`${Q}${D} (${Y.result.visibility})`),F.json){console.log(JSON.stringify(Y,null,2));return}for(let[X,G]of Object.entries(Y.result.steps)){let B=G.status==="ok"?H.green:G.status==="skipped"?H.yellow:H.red;console.log(` ${B(G.status.padEnd(8))} ${X}${G.detail?H.dim(` (${G.detail})`):""}`)}}else{let Y=await Wd({prefix:F.prefix,visibility:F.visibility,limit:Number.parseInt(F.limit,10)||25,dryRun:$});if(J.succeed(`${Q}processed ${Y.count} repo(s)`),F.json){console.log(JSON.stringify(Y,null,2));return}for(let X of Y.results){if(X.error){console.log(` ${H.red("error")} ${X.dataset_id}: ${X.error}`);continue}let G=Object.entries(X.steps??{}).filter(([,W])=>W.status!=="ok").map(([W,q])=>`${W}=${q.status}`),B=G.length?H.yellow("drift"):H.green("ok");console.log(` ${B} ${X.dataset_id}${G.length?H.dim(` (${G.join(", ")})`):""}`)}}}catch(Y){if(Y instanceof g)J.fail(Y.message);else J.fail("Enforcement failed"),console.log(H.red(` ${Y.message}`));process.exit(1)}});var m5=(D)=>new Promise((F)=>setTimeout(F,D));async function bk0(D,F,$){let Q=Date.now()+F,J="no probe completed";while(Date.now()<Q){try{let Y=(await P5(D,!0)).result?.steps?.branch_ruleset;if(Y?.status==="ok")return{status:"green"};if(Y?.detail)J=Y.detail}catch(Y){J=Y instanceof g?Y.message:String(Y)}await m5($)}return{status:"timeout",detail:J}}uX.command("revalidate").description("Re-run BIDS validation on main HEAD for unprotected datasets, then optionally enforce (epic #713)").argument("[dataset-id]","Dataset to revalidate (omit when using --all/--prefix)").option("--all","Revalidate across unprotected public datasets").option("--prefix <prefix>","Filter by id prefix (with --all), e.g. nm000 / on").option("--enforce","After a dataset goes green, run enforce (dry-run unless --apply)").option("--apply","With --enforce, actually apply the ruleset (default is a dry run)").option("--force","Override the live-dataset guard (nm000103-107)").option("--limit <n>","Max repos for --all/--prefix (default 25)","25").option("--json","Output raw JSON").action(async(D,F)=>{if(!m0())console.log(H.red("Error: Not authenticated")),process.exit(1);let $=Boolean(F.all||!D&&F.prefix);if(!D&&!$)console.log(H.red("Error: provide a dataset-id, or --all/--prefix")),process.exit(1);let Q;if(D)Q=[D];else{let q=O("Fetching dataset list...").start();try{let K=[];for(let z=0;;z+=200){let E=await k5({limit:200,offset:z});if(K.push(...E.datasets),E.datasets.length<200||K.length>=E.total_count)break}let Z=Number.parseInt(F.limit,10)||25;Q=hd(K,{prefix:F.prefix}).slice(0,Z),q.succeed(`${Q.length} target(s) (from ${K.length} datasets)`)}catch(K){s0(K,q,"Failed to fetch datasets");return}}let J=Boolean(F.enforce),Y=Boolean(F.apply),X={revalidated:0,green:0,locked:0,stillRed:0,skipped:0,errors:0},G=[],B=[],W=0;for(let q of Q){if(W++,lz.has(q)&&!F.force){console.log(` [${W}/${Q.length}] ${H.dim(`${q}: skip (live; use --force)`)}`),X.skipped++;continue}try{let K=await Hd(q,F.force);if(G.push(K),K.skipped)console.log(` [${W}/${Q.length}] ${q}: ${H.dim(`skipped (${K.skipped})`)}`),X.skipped++;else X.revalidated++,B.push(q),console.log(` [${W}/${Q.length}] ${q}: ${H.cyan(`revalidating (${K.triggered_by})`)}`)}catch(K){X.errors++,console.log(` [${W}/${Q.length}] ${q}: ${H.red(K instanceof g?K.message:String(K))}`)}if(W<Q.length)await m5(4000)}if(J&&B.length>0)if(D){let q=B[0],K=O(`Waiting for ${q} validation to land...`).start(),Z=await bk0(q,300000,15000);if(Z.status!=="green")K.warn(`${q}: not green within 5m \u2014 last: ${Z.detail} (real errors or still running)`),X.stillRed++;else if(X.green++,!Y)K.succeed(`${q}: green (would lock; pass --apply)`);else{let z=await P5(q,!1);if(z.result?.steps?.branch_ruleset?.status==="ok")K.succeed(`${q}: LOCKED`),X.locked++;else K.warn(`${q}: enforce ${z.result?.steps?.branch_ruleset?.status??"?"}`)}}else{let q=O("Waiting ~90s for validations to drain...").start();await m5(90000),q.text="Enforcing green datasets...";for(let K of B)try{if((await P5(K,!0)).result?.steps?.branch_ruleset?.status!=="ok"){X.stillRed++;continue}if(X.green++,Y){if((await P5(K,!1)).result?.steps?.branch_ruleset?.status==="ok")X.locked++}}catch{X.errors++}q.stop()}if(F.json){console.log(JSON.stringify({tally:X,raw:G},null,2));return}console.log(),console.log(H.cyan(`Done: revalidated=${X.revalidated} green=${X.green} locked=${X.locked} still-red=${X.stillRed} skipped=${X.skipped} errors=${X.errors}`))});_0.addCommand(uX);var Tn=new S0("reindex").description("Refresh dataset metadata: enrichment + nemar.org sync + first-class D1 columns");Tn.argument("[dataset-id]","Dataset ID to reindex (e.g., nm000103)").option("--all","Reindex every dataset with a GitHub repo").option("--missing-metadata","Reindex only datasets with NULL metadata columns").option("--stale","Reindex only datasets whose metadata is older than --older-than days").option("--older-than <days>","Threshold in days. Default 30 for --stale, 1 (24h) for --missing-metadata recency guard.").option("--skip-enrichment","Skip the LLM enrichment step").option("--skip-sync","Skip the nemar.org sync + D1 column refresh step").option("--ref <ref>","Ref to enrich from (single-dataset only; default: main)").option("--dry-run","List matched datasets without firing the reindex (bulk only)").action(async(D,F)=>{if(!z0())return;let $=[F.all,F.missingMetadata,F.stale].filter(Boolean).length;if(D&&$>0)console.error(H.red("Provide either a dataset-id OR a bulk flag (--all/--missing-metadata/--stale), not both")),process.exit(1);if(!D&&$===0)console.error(H.red("Provide a dataset-id or one of: --all, --missing-metadata, --stale")),process.exit(1);if($>1)console.error(H.red("Use only one of --all, --missing-metadata, --stale")),process.exit(1);if(F.skipEnrichment&&F.skipSync)console.error(H.red("--skip-enrichment and --skip-sync cannot both be set")),process.exit(1);if(D){let K=O(`Reindexing ${D}...`).start();try{let Z={skip_enrichment:F.skipEnrichment===!0,skip_sync:F.skipSync===!0,...F.ref&&{ref:F.ref}},z=await w5(D,Z),E=z.enrichment.status!=="failed"&&z.sync.status!=="failed";if(E)K.succeed(`${D} reindexed`);else K.warn(`${D} reindexed with errors`);if(console.log(),En(z,{showRef:!0}),!E)process.exit(1)}catch(Z){K.fail(`Failed to reindex ${D}`),console.error(H.red(d(Z))),process.exit(1)}return}let Q=F.all?"all":F.missingMetadata?"missing-metadata":"stale",J={skip_enrichment:F.skipEnrichment===!0,skip_sync:F.skipSync===!0,dry_run:F.dryRun===!0};if(F.olderThan!==void 0){let K=Number.parseInt(F.olderThan,10);if(!Number.isFinite(K)||K<0)console.error(H.red(`Invalid --older-than: "${F.olderThan}"`)),process.exit(1);J.older_than_days=K}else if(Q==="missing-metadata")J.older_than_days=1;else if(Q==="stale")J.older_than_days=30;let Y=F.dryRun?`Listing ${Q} datasets...`:`Reindexing ${Q} datasets...`,X=O(Y).start(),G;try{G=await Dd(Q,J)}catch(K){X.fail("Bulk reindex failed"),console.error(H.red(d(K))),process.exit(1)}if(G.dry_run){if(X.succeed(`${G.total} datasets match filter=${Q} (dry run, no changes)`),G.datasets)for(let K of G.datasets)console.log(H.dim(` - ${K}`));return}let B=G.results??[],W=B.filter((K)=>K.enrichment.status!=="failed"&&K.sync.status!=="failed").length,q=B.length-W;if(q===0)X.succeed(`${B.length}/${B.length} datasets reindexed in ${(G.elapsed_ms/1000).toFixed(1)}s`);else X.warn(`${W}/${B.length} ok; ${q} failed in ${(G.elapsed_ms/1000).toFixed(1)}s`);console.log();for(let K of B)En(K);if(q>0)process.exit(1)});_0.addCommand(Tn);var gk0=new S0("availability-report").description("Report how much of a dataset's declared data is present in S3, and exactly which files are missing + why (#1000). Dry-run by default. --all backfills every managed dataset (#1001).").argument("[dataset-id]","Dataset ID (e.g., nm000103)").option("--write","Commit the report to .nemar/availability-report.json on main (default: preview only)").option("--all","Backfill every managed dataset instead of a single one (#1001)").option("--missing-only","With --all, sweep only datasets already known incomplete (data_complete=0)").option("--limit <n>","With --all, datasets per batch (server clamps to [1,10])","10").option("--reset","With --all, clear every stamped sweep row so it re-sweeps from scratch").option("--verbose","With --all, print per-batch progress").option("--json","Output raw JSON").action(async(D,F)=>{if(!z0())return;if(!D&&!F.all)console.error(H.red("Provide a dataset-id or --all")),process.exit(1);if(D&&(F.all||F.reset||F.missingOnly))console.error(H.red("--all/--reset/--missing-only cannot be combined with a dataset id")),process.exit(1);if(D){let W=O(F.write?`Generating and writing availability report for ${D}...`:`Previewing availability report for ${D}...`).start(),q;try{q=await Yd(D,{write:F.write===!0})}catch(U){W.fail(`Failed to generate availability report for ${D}`),console.error(H.red(d(U))),process.exit(1)}let K="report"in q?q.report:q,Z="report"in q;if(W.succeed(Z?`${D}: availability report written to .nemar/availability-report.json`:`${D}: availability report (dry run, not written)`),F.json){console.log(JSON.stringify(q,null,2));return}if(console.log(),K.version===null)console.log(H.yellow(" No published version manifest to compare against; completeness unknown."));else console.log(` Version: ${K.version}`);console.log(` Complete: ${K.complete?H.green("yes"):H.red("no")}`),console.log(` Files: ${K.completeness.files_present}/${K.completeness.files_declared} present`);let z=`${GD(K.completeness.bytes_present)}/${GD(K.completeness.bytes_declared)}`,E=K.completeness.pct_bytes!=null?` (${(K.completeness.pct_bytes*100).toFixed(1)}%)`:"";if(console.log(` Bytes: ${z}${E}`),K.blocklist_reason)console.log(H.yellow(` Blocklisted: ${K.blocklist_reason}`));if(K.missing.length>0){console.log(),console.log(H.dim(` Missing (${K.missing.length}):`));for(let U of K.missing)console.log(H.dim(` - ${U.path} [${U.reason}]`))}return}if(F.reset){let W=O("Resetting availability-report sweep state...").start();try{let q=await Gd();if(W.succeed(`Cleared ${q.reset} stamped row(s); eligible datasets are candidates again`),F.json)console.log(JSON.stringify(q,null,2))}catch(q){W.fail("Reset failed"),console.error(H.red(d(q))),process.exit(1)}return}let $=Number.parseInt(F.limit??"10",10)||10,Q=F.missingOnly===!0,J={processed:0,written:0,errors:0},Y=O("Sweeping datasets for availability reports...").start(),X=0,G=null,B=null;try{let W;do{if(W=await Xd({limit:$,missingOnly:Q}),X++,J.processed+=W.processed,J.written+=W.written,J.errors+=W.errors.length,G=W.remaining,F.verbose){Y.stop(),console.log(` [batch ${X}] processed=${W.processed} written=${W.written} errors=${W.errors.length} remaining=${G??"?"}`);for(let q of W.errors)console.log(` ${H.red(q.dataset_id)}: ${q.error}`);Y.start("Sweeping datasets for availability reports...")}else Y.text=`Sweeping datasets for availability reports... ${J.processed} processed, ${G??"?"} remaining`;if(G!=null&&B!=null&&G>=B){Y.warn(`No progress (remaining stuck at ${G}); stopping - see errors`);break}if(B=G,(G??0)>0)await m5(1000)}while((G??0)>0);Y.stop()}catch(W){Y.fail("Availability-report sweep failed"),console.error(H.red(d(W))),process.exit(1)}if(F.json)console.log(JSON.stringify({...J,batches:X,remaining:G},null,2));else{if(console.log(),G===null)console.log(H.yellow("Warning: backend returned a null remaining count; the sweep may be incomplete - check server logs."));console.log(H.cyan(`Done in ${X} batch(es): processed=${J.processed} written=${J.written} errors=${J.errors} remaining=${G??"unknown"}`))}if(J.errors>0||G===null)process.exit(1)});_0.addCommand(gk0);var wn=new S0("hed-sweep").description("Backfill HED detection (has_hed / hed_version) for existing datasets (#869)");wn.option("--limit <n>","Datasets per batch (server clamps to [1,30])","15").option("--reset","Clear every probed HED row so a corrected detector can re-sweep").option("--verbose","Print per-batch progress").option("--json","Output raw JSON instead of the human summary").action(async(D)=>{if(!z0())return;if(D.reset){let G=O("Resetting HED sweep state...").start();try{let B=await $d();if(G.succeed(`Cleared ${B.reset} probed HED row(s); eligible datasets are candidates again`),D.json)console.log(JSON.stringify(B,null,2))}catch(B){G.fail("Reset failed"),console.error(H.red(d(B))),process.exit(1)}return}let F=Number.parseInt(D.limit??"15",10)||15,$={processed:0,withHed:0,withoutHed:0,unknown:0,errors:0},Q=O("Sweeping datasets for HED...").start(),J=0,Y=null,X=null;try{let G;do{if(G=await Fd({limit:F}),J++,$.processed+=G.processed,$.withHed+=G.withHed,$.withoutHed+=G.withoutHed,$.unknown+=G.unknown,$.errors+=G.errors.length,Y=G.remaining,D.verbose){Q.stop(),console.log(` [batch ${J}] processed=${G.processed} hed=${G.withHed} no-hed=${G.withoutHed} unknown=${G.unknown} errors=${G.errors.length} remaining=${Y??"?"}`);for(let B of G.errors)console.log(` ${H.red(B.dataset_id)}: ${B.error}`);Q.start("Sweeping datasets for HED...")}else Q.text=`Sweeping datasets for HED... ${$.processed} processed, ${Y??"?"} remaining`;if(Y!=null&&X!=null&&Y>=X){Q.warn(`No progress (remaining stuck at ${Y}); stopping - see errors`);break}if(X=Y,(Y??0)>0)await m5(1000)}while((Y??0)>0);Q.stop()}catch(G){Q.fail("HED sweep failed"),console.error(H.red(d(G))),process.exit(1)}if(D.json)console.log(JSON.stringify({...$,batches:J,remaining:Y},null,2));else{if(console.log(),Y===null)console.log(H.yellow("Warning: backend returned a null remaining count; the sweep may be incomplete - check server logs."));console.log(H.cyan(`Done in ${J} batch(es): processed=${$.processed} hed=${$.withHed} no-hed=${$.withoutHed} unknown=${$.unknown} errors=${$.errors} remaining=${Y??"unknown"}`))}if($.errors>0||Y===null)process.exit(1)});_0.addCommand(wn);async function hk0(D,F,$){let Q=D(),J={processed:0,complete:0,incomplete:0,unknown:0,errors:0},Y=0,X=null,G=null,B=!1;do{let W=await F(Q);if(Y++,J.processed+=W.processed,J.complete+=W.complete,J.incomplete+=W.incomplete,J.unknown+=W.unknown,J.errors+=W.errors.length,X=W.remaining,$?.onBatch?.(W,J,Y),X!=null&&G!=null&&X>=G){B=!0;break}if(G=X,(X??0)>0)await $?.sleepBetweenBatches?.()}while((X??0)>0);return{batches:Y,totals:J,remaining:X,stalled:B}}var Pn=new S0("data-integrity-sweep").description("Audit datasets against their version manifest and backfill data_complete / bytes_present (epic #967 Phase 3, #970)");Pn.option("--limit <n>","Datasets per batch (server clamps to [1,30])","15").option("--older-than <days>","Also re-audit rows checked more than N days ago (periodic re-audit, not just the one-shot drain)").option("--reaudit","Re-verify every already-checked row using an anchored cutoff so the sweep fully converges to 0 (unlike --older-than's moving now()-relative window, which never reaches 0 on its own)").option("--reset","Clear every audited row so a corrected verifier can re-sweep").option("--verbose","Print per-batch progress").option("--json","Output raw JSON instead of the human summary").action(async(D)=>{if(!z0())return;if(D.reset){let B=O("Resetting data-integrity sweep state...").start();try{let W=await Jd();if(B.succeed(`Cleared ${W.reset} audited row(s); eligible datasets are candidates again`),D.json)console.log(JSON.stringify(W,null,2))}catch(W){B.fail("Reset failed"),console.error(H.red(d(W))),process.exit(1)}return}let F=Number.parseInt(D.limit??"15",10)||15,$=D.olderThan?Number.parseInt(D.olderThan,10):void 0,Q=O("Sweeping datasets for data integrity...").start(),J;try{if(J=await hk0(()=>D.reaudit?new Date().toISOString():void 0,async(B)=>{let W=await Qd({limit:F,olderThan:$,before:B});return{processed:W.processed,complete:W.complete,incomplete:W.incomplete,unknown:W.unknown,errors:W.errors,remaining:W.remaining}},{onBatch:(B,W,q)=>{if(D.verbose){Q.stop(),console.log(` [batch ${q}] processed=${B.processed} complete=${B.complete} incomplete=${B.incomplete} unknown=${B.unknown} errors=${B.errors.length} remaining=${B.remaining??"?"}`);for(let K of B.errors)console.log(` ${H.red(K.dataset_id)}: ${K.error}`);Q.start("Sweeping datasets for data integrity...")}else Q.text=`Sweeping datasets for data integrity... ${W.processed} processed, ${B.remaining??"?"} remaining`},sleepBetweenBatches:()=>m5(1000)}),J.stalled)Q.warn(`No progress (remaining stuck at ${J.remaining}); stopping - see errors`);else Q.stop()}catch(B){Q.fail("Data-integrity sweep failed"),console.error(H.red(d(B))),process.exit(1)}let{batches:Y,totals:X,remaining:G}=J;if(D.json)console.log(JSON.stringify({...X,batches:Y,remaining:G},null,2));else{if(console.log(),G===null)console.log(H.yellow("Warning: backend returned a null remaining count; the sweep may be incomplete - check server logs."));if(console.log(H.cyan(`Done in ${Y} batch(es): processed=${X.processed} complete=${X.complete} incomplete=${X.incomplete} unknown=${X.unknown} errors=${X.errors} remaining=${G??"unknown"}`)),X.incomplete>0)console.log(H.yellow(`${X.incomplete} dataset(s) verified incomplete -- see 'nemar dataset list --json' (data_complete=0) for details.`))}if(X.errors>0||G===null)process.exit(1)});_0.addCommand(Pn);var AZ=new S0("email-preferences").description("Manage email notification preferences");AZ.command("show").description("Show email notification preferences").option("--user <username>","(owner only) show another user's preferences").action(async(D)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);let F=O("Fetching email preferences...").start();try{let $=await qd(D.user),Q=$.username?`${$.username}'s`:"Your";F.succeed(`${Q} email notification preferences:`),console.log();let J=[{key:"user_approval",label:"User approval notifications"},{key:"publication_request",label:"Publication request notifications"},{key:"announcements",label:"Announcement emails"}];for(let Y of J){let G=$[Y.key]?H.green("enabled"):H.dim("disabled");console.log(` ${Y.label.padEnd(40)} ${G}`)}console.log(),console.log(H.dim(" Use 'nemar admin email-preferences update' to change settings."))}catch($){F.fail("Failed to fetch preferences"),console.error(H.red(d($)))}});AZ.command("update").description("Update email notification preferences").option("--user-approval <bool>","Enable/disable user approval notifications").option("--publication-request <bool>","Enable/disable publication request notifications").option("--announcements <bool>","Enable/disable announcement emails").option("--all <bool>","Enable/disable all notifications").option("--user <username>","(owner only) update another user's preferences").action(async(D)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);function F(J){if(J===void 0)return;let Y=J.toLowerCase();if(Y==="true"||Y==="1"||Y==="on"||Y==="yes")return!0;if(Y==="false"||Y==="0"||Y==="off"||Y==="no")return!1;console.error(H.red(`Invalid boolean value: "${J}". Use true/false, on/off, yes/no.`)),process.exit(1)}let $={};if(D.all!==void 0){let J=F(D.all);$.user_approval=J,$.publication_request=J,$.announcements=J}else{let J=F(D.userApproval),Y=F(D.publicationRequest),X=F(D.announcements);if(J===void 0&&Y===void 0&&X===void 0)console.error(H.red("No preferences specified.")),console.log(" --user-approval <bool> User approval notifications"),console.log(" --publication-request <bool> Publication request notifications"),console.log(" --announcements <bool> Announcement emails"),console.log(" --all <bool> All notifications"),process.exit(1);if(J!==void 0)$.user_approval=J;if(Y!==void 0)$.publication_request=Y;if(X!==void 0)$.announcements=X}let Q=O("Updating email preferences...").start();try{let J=await Kd($,D.user),Y=J.username?`${J.username}'s`:"Your";Q.succeed(`${Y} email preferences updated:`),console.log(),console.log(` User approval: ${J.user_approval?H.green("enabled"):H.dim("disabled")}`),console.log(` Publication request: ${J.publication_request?H.green("enabled"):H.dim("disabled")}`),console.log(` Announcements: ${J.announcements?H.green("enabled"):H.dim("disabled")}`)}catch(J){Q.fail("Failed to update preferences"),console.error(H.red(d(J)))}});_0.addCommand(AZ);var mX=new S0("notice").description("Manage system notices displayed to CLI users");mX.command("list").description("List all notices (including expired)").action(async()=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);let D=O("Fetching notices...").start();try{let{notices:F}=await Id();if(F.length===0){D.succeed("No notices found.");return}D.succeed(`${F.length} notice(s):`),console.log();for(let $ of F){let J={critical:H.red.bold,warning:H.yellow,info:H.blue}[$.level]||H.white,Y=$.expires_at&&new Date($.expires_at)<new Date?H.dim(" (expired)"):"";if(console.log(` ${H.dim(`#${$.id}`)} ${J(`[${$.level.toUpperCase()}]`)} ${H.dim(`scope:${$.scope}`)}${Y}`),console.log(` ${$.message}`),console.log(` ${H.dim(`Created: ${$.created_at}`)}`),$.expires_at)console.log(` ${H.dim(`Expires: ${$.expires_at}`)}`);console.log()}}catch(F){s0(F,D,"Failed to fetch notices")}});mX.command("set").description("Create a new system notice").requiredOption("-m, --message <text>","Notice message text").option("-l, --level <level>","Notice level: info, warning, critical","info").option("-s, --scope <scope>","Target scope: all, admins, members","all").option("-e, --expires <datetime>","Expiry datetime (ISO 8601)").action(async(D)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);if(!["info","warning","critical"].includes(D.level))console.error(H.red(`Invalid level: ${D.level}. Use info, warning, or critical.`)),process.exit(1);if(!["all","admins","members"].includes(D.scope))console.error(H.red(`Invalid scope: ${D.scope}. Use all, admins, or members.`)),process.exit(1);let F=O("Creating notice...").start();try{let $=await Sd({message:D.message,level:D.level,scope:D.scope,expires_at:D.expires});if(F.succeed(`Notice created (ID: ${$.id})`),console.log(` Level: ${$.level}`),console.log(` Scope: ${$.scope}`),console.log(` Message: ${$.message}`),$.expires_at)console.log(` Expires: ${$.expires_at}`)}catch($){s0($,F,"Failed to create notice")}});mX.command("clear <id>").description("Delete a notice by ID").option(P0,I0).option(f0,x0).action(async(D,F)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);let $=Number.parseInt(D,10);if(Number.isNaN($))console.error(H.red("Invalid notice ID")),process.exit(1);if(!await w0(`Delete notice #${$}?`,F,!1))return;let J=O("Deleting notice...").start();try{await kd($),J.succeed(`Notice #${$} deleted`)}catch(Y){s0(Y,J,"Failed to delete notice")}});_0.addCommand(mX);_0.command("notify").description("Send an email to a group or a single user").option("--to <group>","Recipient group: all, admins, members").option("--user <username>","Send to a single user by username").requiredOption("--subject <text>","Email subject line").option("--body <text>","Email body (markdown)").option("--body-file <path>","Read email body from file (markdown)").option("--dry-run","Preview recipients without sending").option(P0,I0).option(f0,x0).action(async(D)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);if(D.to&&D.user)console.error(H.red("--to and --user are mutually exclusive. Provide exactly one.")),process.exit(1);if(!D.to&&!D.user)console.error(H.red("Provide either --to <group> or --user <username>.")),process.exit(1);if(D.to&&!["all","admins","members"].includes(D.to))console.error(H.red(`Invalid group: ${D.to}. Use all, admins, or members.`)),process.exit(1);let F;if(D.bodyFile){if(!Ln(D.bodyFile))console.error(H.red(`File not found: ${D.bodyFile}`)),process.exit(1);F=kk0(D.bodyFile,"utf-8")}else if(D.body)F=D.body;else console.error(H.red("Email body required. Use --body or --body-file.")),process.exit(1);let $=D.user?`user:${D.user}`:D.to,Q=D.user?{user:D.user,subject:D.subject,body:F}:{to:D.to,subject:D.subject,body:F};if(D.dryRun){let G=O("Checking recipients...").start();try{let B=await hz({...Q,dry_run:!0});if("dry_run"in B){G.succeed(`Dry run: ${B.recipient_count} recipient(s) for "${B.recipient_group}"`),console.log();for(let W of B.recipients)console.log(` ${W}`)}}catch(B){s0(B,G,"Failed to check recipients")}return}console.log(H.bold("Email preview:")),console.log(` To: ${H.cyan($)}`),console.log(` Subject: ${D.subject}`),console.log(` Body: ${F.length>100?`${F.substring(0,100)}...`:F}`),console.log();let J=D.user?`Send this email to ${D.user}?`:"Send this broadcast email?";if(!await w0(J,D,!1))return;let X=O(D.user?`Sending to ${D.user}...`:"Sending broadcast...").start();try{let G=await hz(Q);if("broadcast_id"in G)if(G.failure_count>0){X.warn(`Email send: ${G.recipient_count} delivered, ${G.failure_count} failed`);for(let B of G.failed_recipients)console.log(H.red(` Failed: ${B}`))}else{let B=D.user?`Email sent to ${D.user}`:`Broadcast sent to ${G.recipient_count} recipient(s)`;X.succeed(`${B} (ID: ${G.broadcast_id})`)}}catch(G){s0(G,X,"Failed to send email")}});var uk0=1500;function mk0(D){switch(D.kind){case"ok":return H.green(`ok ${D.schema_version}`);case"stale":return H.yellow(`stale ${D.schema_version}`);case"missing":return H.red("missing");case"error":return H.red(`error ${D.status||"?"}: ${D.message}`)}}function lk0(D){return D.kind==="stale"||D.kind==="missing"}function pk0(D){let $=D.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".");return new RegExp(`^${$}$`)}var In=new S0("summary").description("summary.json coverage across published dataset versions");In.command("check").description("Report which (dataset_id, version) pairs have stale or missing summary.json").option("--fix","Dispatch generate-manifest for every stale/missing version").option("--id <id>","Limit to a single dataset_id (exact match)").option("--match <glob>","Limit to dataset_ids matching a glob pattern: `*` matches any run of chars, `?` matches one char. Examples: `on*`, `nm00010?`, `*99999`. Can be combined with --id (id wins if both passed).").option("--only-stale","Print only rows that are not ok (suppresses ok rows)").option("--json","Emit the full report as JSON instead of a table").option(P0,I0).option(f0,x0).action(async(D)=>{if(!z0())return;let F=O("Building coverage report...").start(),$;try{$=await zd()}catch(Z){s0(Z,F,"Failed to fetch coverage report");return}F.succeed(`Coverage: target=schema ${$.target_schema}, versions=${$.totals.versions}`);let Q=$.versions,J=null;if(D.id){if(Q=Q.filter((Z)=>Z.dataset_id===D.id),J=`dataset_id=${D.id}`,Q.length===0){console.log(H.yellow(`No rows for ${J}`));return}}else if(D.match){let Z=pk0(D.match);if(Q=Q.filter((z)=>Z.test(z.dataset_id)),J=`--match ${D.match}`,Q.length===0){console.log(H.yellow(`No rows match ${J}`));return}console.log(H.dim(`Scoped to ${Q.length} version(s) matching ${D.match}`))}if(D.json){console.log(JSON.stringify(J?{...$,versions:Q,totals:Un(Q)}:$,null,2));return}let Y=D.onlyStale?Q.filter((Z)=>Z.state.kind!=="ok"):Q;if(Y.length===0)console.log(H.green("All versions at target schema \u2014 no drift."));else{let Z=Math.max(...Y.map((E)=>E.dataset_id.length),10),z=Math.max(...Y.map((E)=>E.version.length),7);for(let E of Y)console.log(` ${E.dataset_id.padEnd(Z)} ${E.version.padEnd(z)} ${mk0(E.state)}`)}let X=J?Un(Q):$.totals;if(console.log(),console.log(` Total: ${X.versions} ${H.green(`ok=${X.ok}`)} ${H.yellow(`stale=${X.stale}`)} ${H.red(`missing=${X.missing}`)} ${H.red(`error=${X.error}`)}`),!D.fix){if(X.stale+X.missing>0)console.log(H.dim(`
451
+ `));for(let J of Q)try{let Y=await VD(J.xx_id);console.log(` ${J.xx_id.padEnd(10)} ${J.modality.padEnd(6)} <- ${J.source_id.padEnd(10)} ${H.green(Y.visibility)} doi=${Y.concept_doi??"none"}`)}catch(Y){if(Y instanceof g&&Y.statusCode===404)console.log(` ${J.xx_id.padEnd(10)} ${J.modality.padEnd(6)} <- ${J.source_id.padEnd(10)} ${H.dim("not created")}`);else console.log(` ${J.xx_id.padEnd(10)} ${J.modality.padEnd(6)} <- ${J.source_id.padEnd(10)} ${H.red(`status check failed (${d(Y)})`)}`)}});hX.command("remint-dois").description("Re-mint an exemplar's sandbox concept DOI (idempotent)").argument("<xx-id>","Exemplar dataset id (e.g., xx099900)").action(async(D)=>{if(!z0())return;let F=O(`Re-minting DOIs for ${D}...`).start();try{let $=await sp(D);F.succeed(`Re-minted ${$.concept_doi} (status: ${$.status})`);for(let Q of $.warnings??[])console.log(H.yellow(` ${Q}`))}catch($){s0($,F,"Failed to re-mint exemplar DOIs",{400:"Not an exemplar dataset",403:"Exemplar DOI re-mint is disabled in production",404:"Dataset not found"}),process.exit(1)}});_0.addCommand(hX);function En(D,F){let $=D.enrichment.status,Q=$==="ok"?H.green("enrich:ok"):$==="failed"?H.red("enrich:failed"):H.dim("enrich:skipped"),J=D.sync.status,Y=J==="ok"?H.green("sync:ok"):J==="failed"?H.red("sync:failed"):H.dim("sync:skipped"),X=D.sync.metadata_columns_written===!0?H.green("cols:written"):D.sync.metadata_columns_error?H.red("cols:failed"):"",G=F?.showRef&&D.enrichment.ref?H.dim(`@${D.enrichment.ref}`):"";if(console.log(` ${D.dataset_id.padEnd(12)} ${Q}${G} ${Y} ${X}`),D.enrichment.error)console.log(H.red(` enrichment: ${D.enrichment.error}`));if(D.sync.metadata_columns_error)console.log(H.red(` metadata columns: ${D.sync.metadata_columns_error}`))}var uX=new S0("fleet").description("Governance drift reporting and enforcement (epic #713)");uX.command("drift").description("Report dataset repos that are off the governance spec").option("--prefix <prefix>","Filter datasets by id prefix (e.g. nm, on)").option("--visibility <vis>","Filter by visibility: public or private").option("--limit <n>","Max repos to scan (default 25, max 50)","25").option("--json","Output raw JSON").action(async(D)=>{if(!m0())console.log(H.red("Error: Not authenticated")),process.exit(1);if(D.visibility&&!["public","private"].includes(D.visibility))console.log(H.red("Error: --visibility must be public or private")),process.exit(1);let F=O("Scanning fleet for drift...").start();try{let $=await Bd({prefix:D.prefix,visibility:D.visibility,limit:Number.parseInt(D.limit,10)||25});if(F.stop(),D.json){console.log(JSON.stringify($,null,2));return}console.log(),console.log(H.bold(`Fleet drift (scanned ${$.scanned}):`));let Q=["PUBLIC_UNPROTECTED","RED_REQUIRED_CHECK","CONTEXT_NAME_MISMATCH","MISSING_REQUIRED_WORKFLOW","PRIVATE_WITH_STRAY_READ","DEFAULT_BRANCH_OUTLIER","DEPRECATED_WORKFLOW_PRESENT","COMPLIANT"];for(let J of Q){let Y=$.buckets[J];if(!Y||Y.length===0)continue;let X=J==="COMPLIANT"?H.green:H.yellow;if(console.log(` ${X(J.padEnd(28))} ${Y.length}`),J!=="COMPLIANT"){let G=Y.slice(0,20).join(", ");console.log(H.dim(` ${G}${Y.length>20?` ... +${Y.length-20}`:""}`))}}console.log()}catch($){if($ instanceof g)F.fail($.message);else F.fail("Failed to scan fleet"),console.log(H.red(` ${$.message}`));process.exit(1)}});uX.command("enforce").description("Bring dataset repos to the governance spec (single or --all). Dry-run by default.").argument("[dataset-id]","Dataset to enforce (omit when using --all)").option("--all","Bulk enforce across a filtered set (owner-only)").option("--apply","Actually apply changes (default is a dry run)").option("--prefix <prefix>","Bulk: filter by id prefix").option("--visibility <vis>","Bulk: filter by visibility (public|private)").option("--limit <n>","Bulk: max repos (default 25, max 50)","25").option("--json","Output raw JSON").action(async(D,F)=>{if(!m0())console.log(H.red("Error: Not authenticated")),process.exit(1);if(!D&&!F.all)console.log(H.red("Error: provide a dataset-id or use --all")),process.exit(1);if(D&&F.all)console.log(H.red("Error: provide a dataset-id OR --all, not both")),process.exit(1);let $=!F.apply,Q=$?"[dry-run] ":"",J=O(`${Q}Enforcing spec...`).start();try{if(D){let Y=await P5(D,$);if(J.succeed(`${Q}${D} (${Y.result.visibility})`),F.json){console.log(JSON.stringify(Y,null,2));return}for(let[X,G]of Object.entries(Y.result.steps)){let B=G.status==="ok"?H.green:G.status==="skipped"?H.yellow:H.red;console.log(` ${B(G.status.padEnd(8))} ${X}${G.detail?H.dim(` (${G.detail})`):""}`)}}else{let Y=await Wd({prefix:F.prefix,visibility:F.visibility,limit:Number.parseInt(F.limit,10)||25,dryRun:$});if(J.succeed(`${Q}processed ${Y.count} repo(s)`),F.json){console.log(JSON.stringify(Y,null,2));return}for(let X of Y.results){if(X.error){console.log(` ${H.red("error")} ${X.dataset_id}: ${X.error}`);continue}let G=Object.entries(X.steps??{}).filter(([,W])=>W.status!=="ok").map(([W,q])=>`${W}=${q.status}`),B=G.length?H.yellow("drift"):H.green("ok");console.log(` ${B} ${X.dataset_id}${G.length?H.dim(` (${G.join(", ")})`):""}`)}}}catch(Y){if(Y instanceof g)J.fail(Y.message);else J.fail("Enforcement failed"),console.log(H.red(` ${Y.message}`));process.exit(1)}});var m5=(D)=>new Promise((F)=>setTimeout(F,D));async function bk0(D,F,$){let Q=Date.now()+F,J="no probe completed";while(Date.now()<Q){try{let Y=(await P5(D,!0)).result?.steps?.branch_ruleset;if(Y?.status==="ok")return{status:"green"};if(Y?.detail)J=Y.detail}catch(Y){J=Y instanceof g?Y.message:String(Y)}await m5($)}return{status:"timeout",detail:J}}uX.command("revalidate").description("Re-run BIDS validation on main HEAD for unprotected datasets, then optionally enforce (epic #713)").argument("[dataset-id]","Dataset to revalidate (omit when using --all/--prefix)").option("--all","Revalidate across unprotected public datasets").option("--prefix <prefix>","Filter by id prefix (with --all), e.g. nm000 / on").option("--enforce","After a dataset goes green, run enforce (dry-run unless --apply)").option("--apply","With --enforce, actually apply the ruleset (default is a dry run)").option("--force","Override the live-dataset guard (nm000103-107)").option("--limit <n>","Max repos for --all/--prefix (default 25)","25").option("--json","Output raw JSON").action(async(D,F)=>{if(!m0())console.log(H.red("Error: Not authenticated")),process.exit(1);let $=Boolean(F.all||!D&&F.prefix);if(!D&&!$)console.log(H.red("Error: provide a dataset-id, or --all/--prefix")),process.exit(1);let Q;if(D)Q=[D];else{let q=O("Fetching dataset list...").start();try{let K=[];for(let z=0;;z+=200){let E=await k5({limit:200,offset:z});if(K.push(...E.datasets),E.datasets.length<200||K.length>=E.total_count)break}let Z=Number.parseInt(F.limit,10)||25;Q=hd(K,{prefix:F.prefix}).slice(0,Z),q.succeed(`${Q.length} target(s) (from ${K.length} datasets)`)}catch(K){s0(K,q,"Failed to fetch datasets");return}}let J=Boolean(F.enforce),Y=Boolean(F.apply),X={revalidated:0,green:0,locked:0,stillRed:0,skipped:0,errors:0},G=[],B=[],W=0;for(let q of Q){if(W++,lz.has(q)&&!F.force){console.log(` [${W}/${Q.length}] ${H.dim(`${q}: skip (live; use --force)`)}`),X.skipped++;continue}try{let K=await Hd(q,F.force);if(G.push(K),K.skipped)console.log(` [${W}/${Q.length}] ${q}: ${H.dim(`skipped (${K.skipped})`)}`),X.skipped++;else X.revalidated++,B.push(q),console.log(` [${W}/${Q.length}] ${q}: ${H.cyan(`revalidating (${K.triggered_by})`)}`)}catch(K){X.errors++,console.log(` [${W}/${Q.length}] ${q}: ${H.red(K instanceof g?K.message:String(K))}`)}if(W<Q.length)await m5(4000)}if(J&&B.length>0)if(D){let q=B[0],K=O(`Waiting for ${q} validation to land...`).start(),Z=await bk0(q,300000,15000);if(Z.status!=="green")K.warn(`${q}: not green within 5m \u2014 last: ${Z.detail} (real errors or still running)`),X.stillRed++;else if(X.green++,!Y)K.succeed(`${q}: green (would lock; pass --apply)`);else{let z=await P5(q,!1);if(z.result?.steps?.branch_ruleset?.status==="ok")K.succeed(`${q}: LOCKED`),X.locked++;else K.warn(`${q}: enforce ${z.result?.steps?.branch_ruleset?.status??"?"}`)}}else{let q=O("Waiting ~90s for validations to drain...").start();await m5(90000),q.text="Enforcing green datasets...";for(let K of B)try{if((await P5(K,!0)).result?.steps?.branch_ruleset?.status!=="ok"){X.stillRed++;continue}if(X.green++,Y){if((await P5(K,!1)).result?.steps?.branch_ruleset?.status==="ok")X.locked++}}catch{X.errors++}q.stop()}if(F.json){console.log(JSON.stringify({tally:X,raw:G},null,2));return}console.log(),console.log(H.cyan(`Done: revalidated=${X.revalidated} green=${X.green} locked=${X.locked} still-red=${X.stillRed} skipped=${X.skipped} errors=${X.errors}`))});_0.addCommand(uX);var Tn=new S0("reindex").description("Refresh dataset metadata: enrichment + first-class D1 columns");Tn.argument("[dataset-id]","Dataset ID to reindex (e.g., nm000103)").option("--all","Reindex every dataset with a GitHub repo").option("--missing-metadata","Reindex only datasets with NULL metadata columns").option("--stale","Reindex only datasets whose metadata is older than --older-than days").option("--older-than <days>","Threshold in days. Default 30 for --stale, 1 (24h) for --missing-metadata recency guard.").option("--skip-enrichment","Skip the LLM enrichment step").option("--skip-sync","Skip the D1 metadata-column refresh step").option("--ref <ref>","Ref to enrich from (single-dataset only; default: main)").option("--dry-run","List matched datasets without firing the reindex (bulk only)").action(async(D,F)=>{if(!z0())return;let $=[F.all,F.missingMetadata,F.stale].filter(Boolean).length;if(D&&$>0)console.error(H.red("Provide either a dataset-id OR a bulk flag (--all/--missing-metadata/--stale), not both")),process.exit(1);if(!D&&$===0)console.error(H.red("Provide a dataset-id or one of: --all, --missing-metadata, --stale")),process.exit(1);if($>1)console.error(H.red("Use only one of --all, --missing-metadata, --stale")),process.exit(1);if(F.skipEnrichment&&F.skipSync)console.error(H.red("--skip-enrichment and --skip-sync cannot both be set")),process.exit(1);if(D){let K=O(`Reindexing ${D}...`).start();try{let Z={skip_enrichment:F.skipEnrichment===!0,skip_sync:F.skipSync===!0,...F.ref&&{ref:F.ref}},z=await w5(D,Z),E=z.enrichment.status!=="failed"&&z.sync.status!=="failed";if(E)K.succeed(`${D} reindexed`);else K.warn(`${D} reindexed with errors`);if(console.log(),En(z,{showRef:!0}),!E)process.exit(1)}catch(Z){K.fail(`Failed to reindex ${D}`),console.error(H.red(d(Z))),process.exit(1)}return}let Q=F.all?"all":F.missingMetadata?"missing-metadata":"stale",J={skip_enrichment:F.skipEnrichment===!0,skip_sync:F.skipSync===!0,dry_run:F.dryRun===!0};if(F.olderThan!==void 0){let K=Number.parseInt(F.olderThan,10);if(!Number.isFinite(K)||K<0)console.error(H.red(`Invalid --older-than: "${F.olderThan}"`)),process.exit(1);J.older_than_days=K}else if(Q==="missing-metadata")J.older_than_days=1;else if(Q==="stale")J.older_than_days=30;let Y=F.dryRun?`Listing ${Q} datasets...`:`Reindexing ${Q} datasets...`,X=O(Y).start(),G;try{G=await Dd(Q,J)}catch(K){X.fail("Bulk reindex failed"),console.error(H.red(d(K))),process.exit(1)}if(G.dry_run){if(X.succeed(`${G.total} datasets match filter=${Q} (dry run, no changes)`),G.datasets)for(let K of G.datasets)console.log(H.dim(` - ${K}`));return}let B=G.results??[],W=B.filter((K)=>K.enrichment.status!=="failed"&&K.sync.status!=="failed").length,q=B.length-W;if(q===0)X.succeed(`${B.length}/${B.length} datasets reindexed in ${(G.elapsed_ms/1000).toFixed(1)}s`);else X.warn(`${W}/${B.length} ok; ${q} failed in ${(G.elapsed_ms/1000).toFixed(1)}s`);console.log();for(let K of B)En(K);if(q>0)process.exit(1)});_0.addCommand(Tn);var gk0=new S0("availability-report").description("Report how much of a dataset's declared data is present in S3, and exactly which files are missing + why (#1000). Dry-run by default. --all backfills every managed dataset (#1001).").argument("[dataset-id]","Dataset ID (e.g., nm000103)").option("--write","Commit the report to .nemar/availability-report.json on main (default: preview only)").option("--all","Backfill every managed dataset instead of a single one (#1001)").option("--missing-only","With --all, sweep only datasets already known incomplete (data_complete=0)").option("--limit <n>","With --all, datasets per batch (server clamps to [1,10])","10").option("--reset","With --all, clear every stamped sweep row so it re-sweeps from scratch").option("--verbose","With --all, print per-batch progress").option("--json","Output raw JSON").action(async(D,F)=>{if(!z0())return;if(!D&&!F.all)console.error(H.red("Provide a dataset-id or --all")),process.exit(1);if(D&&(F.all||F.reset||F.missingOnly))console.error(H.red("--all/--reset/--missing-only cannot be combined with a dataset id")),process.exit(1);if(D){let W=O(F.write?`Generating and writing availability report for ${D}...`:`Previewing availability report for ${D}...`).start(),q;try{q=await Yd(D,{write:F.write===!0})}catch(U){W.fail(`Failed to generate availability report for ${D}`),console.error(H.red(d(U))),process.exit(1)}let K="report"in q?q.report:q,Z="report"in q;if(W.succeed(Z?`${D}: availability report written to .nemar/availability-report.json`:`${D}: availability report (dry run, not written)`),F.json){console.log(JSON.stringify(q,null,2));return}if(console.log(),K.version===null)console.log(H.yellow(" No published version manifest to compare against; completeness unknown."));else console.log(` Version: ${K.version}`);console.log(` Complete: ${K.complete?H.green("yes"):H.red("no")}`),console.log(` Files: ${K.completeness.files_present}/${K.completeness.files_declared} present`);let z=`${GD(K.completeness.bytes_present)}/${GD(K.completeness.bytes_declared)}`,E=K.completeness.pct_bytes!=null?` (${(K.completeness.pct_bytes*100).toFixed(1)}%)`:"";if(console.log(` Bytes: ${z}${E}`),K.blocklist_reason)console.log(H.yellow(` Blocklisted: ${K.blocklist_reason}`));if(K.missing.length>0){console.log(),console.log(H.dim(` Missing (${K.missing.length}):`));for(let U of K.missing)console.log(H.dim(` - ${U.path} [${U.reason}]`))}return}if(F.reset){let W=O("Resetting availability-report sweep state...").start();try{let q=await Gd();if(W.succeed(`Cleared ${q.reset} stamped row(s); eligible datasets are candidates again`),F.json)console.log(JSON.stringify(q,null,2))}catch(q){W.fail("Reset failed"),console.error(H.red(d(q))),process.exit(1)}return}let $=Number.parseInt(F.limit??"10",10)||10,Q=F.missingOnly===!0,J={processed:0,written:0,errors:0},Y=O("Sweeping datasets for availability reports...").start(),X=0,G=null,B=null;try{let W;do{if(W=await Xd({limit:$,missingOnly:Q}),X++,J.processed+=W.processed,J.written+=W.written,J.errors+=W.errors.length,G=W.remaining,F.verbose){Y.stop(),console.log(` [batch ${X}] processed=${W.processed} written=${W.written} errors=${W.errors.length} remaining=${G??"?"}`);for(let q of W.errors)console.log(` ${H.red(q.dataset_id)}: ${q.error}`);Y.start("Sweeping datasets for availability reports...")}else Y.text=`Sweeping datasets for availability reports... ${J.processed} processed, ${G??"?"} remaining`;if(G!=null&&B!=null&&G>=B){Y.warn(`No progress (remaining stuck at ${G}); stopping - see errors`);break}if(B=G,(G??0)>0)await m5(1000)}while((G??0)>0);Y.stop()}catch(W){Y.fail("Availability-report sweep failed"),console.error(H.red(d(W))),process.exit(1)}if(F.json)console.log(JSON.stringify({...J,batches:X,remaining:G},null,2));else{if(console.log(),G===null)console.log(H.yellow("Warning: backend returned a null remaining count; the sweep may be incomplete - check server logs."));console.log(H.cyan(`Done in ${X} batch(es): processed=${J.processed} written=${J.written} errors=${J.errors} remaining=${G??"unknown"}`))}if(J.errors>0||G===null)process.exit(1)});_0.addCommand(gk0);var wn=new S0("hed-sweep").description("Backfill HED detection (has_hed / hed_version) for existing datasets (#869)");wn.option("--limit <n>","Datasets per batch (server clamps to [1,30])","15").option("--reset","Clear every probed HED row so a corrected detector can re-sweep").option("--verbose","Print per-batch progress").option("--json","Output raw JSON instead of the human summary").action(async(D)=>{if(!z0())return;if(D.reset){let G=O("Resetting HED sweep state...").start();try{let B=await $d();if(G.succeed(`Cleared ${B.reset} probed HED row(s); eligible datasets are candidates again`),D.json)console.log(JSON.stringify(B,null,2))}catch(B){G.fail("Reset failed"),console.error(H.red(d(B))),process.exit(1)}return}let F=Number.parseInt(D.limit??"15",10)||15,$={processed:0,withHed:0,withoutHed:0,unknown:0,errors:0},Q=O("Sweeping datasets for HED...").start(),J=0,Y=null,X=null;try{let G;do{if(G=await Fd({limit:F}),J++,$.processed+=G.processed,$.withHed+=G.withHed,$.withoutHed+=G.withoutHed,$.unknown+=G.unknown,$.errors+=G.errors.length,Y=G.remaining,D.verbose){Q.stop(),console.log(` [batch ${J}] processed=${G.processed} hed=${G.withHed} no-hed=${G.withoutHed} unknown=${G.unknown} errors=${G.errors.length} remaining=${Y??"?"}`);for(let B of G.errors)console.log(` ${H.red(B.dataset_id)}: ${B.error}`);Q.start("Sweeping datasets for HED...")}else Q.text=`Sweeping datasets for HED... ${$.processed} processed, ${Y??"?"} remaining`;if(Y!=null&&X!=null&&Y>=X){Q.warn(`No progress (remaining stuck at ${Y}); stopping - see errors`);break}if(X=Y,(Y??0)>0)await m5(1000)}while((Y??0)>0);Q.stop()}catch(G){Q.fail("HED sweep failed"),console.error(H.red(d(G))),process.exit(1)}if(D.json)console.log(JSON.stringify({...$,batches:J,remaining:Y},null,2));else{if(console.log(),Y===null)console.log(H.yellow("Warning: backend returned a null remaining count; the sweep may be incomplete - check server logs."));console.log(H.cyan(`Done in ${J} batch(es): processed=${$.processed} hed=${$.withHed} no-hed=${$.withoutHed} unknown=${$.unknown} errors=${$.errors} remaining=${Y??"unknown"}`))}if($.errors>0||Y===null)process.exit(1)});_0.addCommand(wn);async function hk0(D,F,$){let Q=D(),J={processed:0,complete:0,incomplete:0,unknown:0,errors:0},Y=0,X=null,G=null,B=!1;do{let W=await F(Q);if(Y++,J.processed+=W.processed,J.complete+=W.complete,J.incomplete+=W.incomplete,J.unknown+=W.unknown,J.errors+=W.errors.length,X=W.remaining,$?.onBatch?.(W,J,Y),X!=null&&G!=null&&X>=G){B=!0;break}if(G=X,(X??0)>0)await $?.sleepBetweenBatches?.()}while((X??0)>0);return{batches:Y,totals:J,remaining:X,stalled:B}}var Pn=new S0("data-integrity-sweep").description("Audit datasets against their version manifest and backfill data_complete / bytes_present (epic #967 Phase 3, #970)");Pn.option("--limit <n>","Datasets per batch (server clamps to [1,30])","15").option("--older-than <days>","Also re-audit rows checked more than N days ago (periodic re-audit, not just the one-shot drain)").option("--reaudit","Re-verify every already-checked row using an anchored cutoff so the sweep fully converges to 0 (unlike --older-than's moving now()-relative window, which never reaches 0 on its own)").option("--reset","Clear every audited row so a corrected verifier can re-sweep").option("--verbose","Print per-batch progress").option("--json","Output raw JSON instead of the human summary").action(async(D)=>{if(!z0())return;if(D.reset){let B=O("Resetting data-integrity sweep state...").start();try{let W=await Jd();if(B.succeed(`Cleared ${W.reset} audited row(s); eligible datasets are candidates again`),D.json)console.log(JSON.stringify(W,null,2))}catch(W){B.fail("Reset failed"),console.error(H.red(d(W))),process.exit(1)}return}let F=Number.parseInt(D.limit??"15",10)||15,$=D.olderThan?Number.parseInt(D.olderThan,10):void 0,Q=O("Sweeping datasets for data integrity...").start(),J;try{if(J=await hk0(()=>D.reaudit?new Date().toISOString():void 0,async(B)=>{let W=await Qd({limit:F,olderThan:$,before:B});return{processed:W.processed,complete:W.complete,incomplete:W.incomplete,unknown:W.unknown,errors:W.errors,remaining:W.remaining}},{onBatch:(B,W,q)=>{if(D.verbose){Q.stop(),console.log(` [batch ${q}] processed=${B.processed} complete=${B.complete} incomplete=${B.incomplete} unknown=${B.unknown} errors=${B.errors.length} remaining=${B.remaining??"?"}`);for(let K of B.errors)console.log(` ${H.red(K.dataset_id)}: ${K.error}`);Q.start("Sweeping datasets for data integrity...")}else Q.text=`Sweeping datasets for data integrity... ${W.processed} processed, ${B.remaining??"?"} remaining`},sleepBetweenBatches:()=>m5(1000)}),J.stalled)Q.warn(`No progress (remaining stuck at ${J.remaining}); stopping - see errors`);else Q.stop()}catch(B){Q.fail("Data-integrity sweep failed"),console.error(H.red(d(B))),process.exit(1)}let{batches:Y,totals:X,remaining:G}=J;if(D.json)console.log(JSON.stringify({...X,batches:Y,remaining:G},null,2));else{if(console.log(),G===null)console.log(H.yellow("Warning: backend returned a null remaining count; the sweep may be incomplete - check server logs."));if(console.log(H.cyan(`Done in ${Y} batch(es): processed=${X.processed} complete=${X.complete} incomplete=${X.incomplete} unknown=${X.unknown} errors=${X.errors} remaining=${G??"unknown"}`)),X.incomplete>0)console.log(H.yellow(`${X.incomplete} dataset(s) verified incomplete -- see 'nemar dataset list --json' (data_complete=0) for details.`))}if(X.errors>0||G===null)process.exit(1)});_0.addCommand(Pn);var AZ=new S0("email-preferences").description("Manage email notification preferences");AZ.command("show").description("Show email notification preferences").option("--user <username>","(owner only) show another user's preferences").action(async(D)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);let F=O("Fetching email preferences...").start();try{let $=await qd(D.user),Q=$.username?`${$.username}'s`:"Your";F.succeed(`${Q} email notification preferences:`),console.log();let J=[{key:"user_approval",label:"User approval notifications"},{key:"publication_request",label:"Publication request notifications"},{key:"announcements",label:"Announcement emails"}];for(let Y of J){let G=$[Y.key]?H.green("enabled"):H.dim("disabled");console.log(` ${Y.label.padEnd(40)} ${G}`)}console.log(),console.log(H.dim(" Use 'nemar admin email-preferences update' to change settings."))}catch($){F.fail("Failed to fetch preferences"),console.error(H.red(d($)))}});AZ.command("update").description("Update email notification preferences").option("--user-approval <bool>","Enable/disable user approval notifications").option("--publication-request <bool>","Enable/disable publication request notifications").option("--announcements <bool>","Enable/disable announcement emails").option("--all <bool>","Enable/disable all notifications").option("--user <username>","(owner only) update another user's preferences").action(async(D)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);function F(J){if(J===void 0)return;let Y=J.toLowerCase();if(Y==="true"||Y==="1"||Y==="on"||Y==="yes")return!0;if(Y==="false"||Y==="0"||Y==="off"||Y==="no")return!1;console.error(H.red(`Invalid boolean value: "${J}". Use true/false, on/off, yes/no.`)),process.exit(1)}let $={};if(D.all!==void 0){let J=F(D.all);$.user_approval=J,$.publication_request=J,$.announcements=J}else{let J=F(D.userApproval),Y=F(D.publicationRequest),X=F(D.announcements);if(J===void 0&&Y===void 0&&X===void 0)console.error(H.red("No preferences specified.")),console.log(" --user-approval <bool> User approval notifications"),console.log(" --publication-request <bool> Publication request notifications"),console.log(" --announcements <bool> Announcement emails"),console.log(" --all <bool> All notifications"),process.exit(1);if(J!==void 0)$.user_approval=J;if(Y!==void 0)$.publication_request=Y;if(X!==void 0)$.announcements=X}let Q=O("Updating email preferences...").start();try{let J=await Kd($,D.user),Y=J.username?`${J.username}'s`:"Your";Q.succeed(`${Y} email preferences updated:`),console.log(),console.log(` User approval: ${J.user_approval?H.green("enabled"):H.dim("disabled")}`),console.log(` Publication request: ${J.publication_request?H.green("enabled"):H.dim("disabled")}`),console.log(` Announcements: ${J.announcements?H.green("enabled"):H.dim("disabled")}`)}catch(J){Q.fail("Failed to update preferences"),console.error(H.red(d(J)))}});_0.addCommand(AZ);var mX=new S0("notice").description("Manage system notices displayed to CLI users");mX.command("list").description("List all notices (including expired)").action(async()=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);let D=O("Fetching notices...").start();try{let{notices:F}=await Id();if(F.length===0){D.succeed("No notices found.");return}D.succeed(`${F.length} notice(s):`),console.log();for(let $ of F){let J={critical:H.red.bold,warning:H.yellow,info:H.blue}[$.level]||H.white,Y=$.expires_at&&new Date($.expires_at)<new Date?H.dim(" (expired)"):"";if(console.log(` ${H.dim(`#${$.id}`)} ${J(`[${$.level.toUpperCase()}]`)} ${H.dim(`scope:${$.scope}`)}${Y}`),console.log(` ${$.message}`),console.log(` ${H.dim(`Created: ${$.created_at}`)}`),$.expires_at)console.log(` ${H.dim(`Expires: ${$.expires_at}`)}`);console.log()}}catch(F){s0(F,D,"Failed to fetch notices")}});mX.command("set").description("Create a new system notice").requiredOption("-m, --message <text>","Notice message text").option("-l, --level <level>","Notice level: info, warning, critical","info").option("-s, --scope <scope>","Target scope: all, admins, members","all").option("-e, --expires <datetime>","Expiry datetime (ISO 8601)").action(async(D)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);if(!["info","warning","critical"].includes(D.level))console.error(H.red(`Invalid level: ${D.level}. Use info, warning, or critical.`)),process.exit(1);if(!["all","admins","members"].includes(D.scope))console.error(H.red(`Invalid scope: ${D.scope}. Use all, admins, or members.`)),process.exit(1);let F=O("Creating notice...").start();try{let $=await Sd({message:D.message,level:D.level,scope:D.scope,expires_at:D.expires});if(F.succeed(`Notice created (ID: ${$.id})`),console.log(` Level: ${$.level}`),console.log(` Scope: ${$.scope}`),console.log(` Message: ${$.message}`),$.expires_at)console.log(` Expires: ${$.expires_at}`)}catch($){s0($,F,"Failed to create notice")}});mX.command("clear <id>").description("Delete a notice by ID").option(P0,I0).option(f0,x0).action(async(D,F)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);let $=Number.parseInt(D,10);if(Number.isNaN($))console.error(H.red("Invalid notice ID")),process.exit(1);if(!await w0(`Delete notice #${$}?`,F,!1))return;let J=O("Deleting notice...").start();try{await kd($),J.succeed(`Notice #${$} deleted`)}catch(Y){s0(Y,J,"Failed to delete notice")}});_0.addCommand(mX);_0.command("notify").description("Send an email to a group or a single user").option("--to <group>","Recipient group: all, admins, members").option("--user <username>","Send to a single user by username").requiredOption("--subject <text>","Email subject line").option("--body <text>","Email body (markdown)").option("--body-file <path>","Read email body from file (markdown)").option("--dry-run","Preview recipients without sending").option(P0,I0).option(f0,x0).action(async(D)=>{if(!m0())console.error(H.red("Not authenticated. Run: nemar auth login")),process.exit(1);if(D.to&&D.user)console.error(H.red("--to and --user are mutually exclusive. Provide exactly one.")),process.exit(1);if(!D.to&&!D.user)console.error(H.red("Provide either --to <group> or --user <username>.")),process.exit(1);if(D.to&&!["all","admins","members"].includes(D.to))console.error(H.red(`Invalid group: ${D.to}. Use all, admins, or members.`)),process.exit(1);let F;if(D.bodyFile){if(!Ln(D.bodyFile))console.error(H.red(`File not found: ${D.bodyFile}`)),process.exit(1);F=kk0(D.bodyFile,"utf-8")}else if(D.body)F=D.body;else console.error(H.red("Email body required. Use --body or --body-file.")),process.exit(1);let $=D.user?`user:${D.user}`:D.to,Q=D.user?{user:D.user,subject:D.subject,body:F}:{to:D.to,subject:D.subject,body:F};if(D.dryRun){let G=O("Checking recipients...").start();try{let B=await hz({...Q,dry_run:!0});if("dry_run"in B){G.succeed(`Dry run: ${B.recipient_count} recipient(s) for "${B.recipient_group}"`),console.log();for(let W of B.recipients)console.log(` ${W}`)}}catch(B){s0(B,G,"Failed to check recipients")}return}console.log(H.bold("Email preview:")),console.log(` To: ${H.cyan($)}`),console.log(` Subject: ${D.subject}`),console.log(` Body: ${F.length>100?`${F.substring(0,100)}...`:F}`),console.log();let J=D.user?`Send this email to ${D.user}?`:"Send this broadcast email?";if(!await w0(J,D,!1))return;let X=O(D.user?`Sending to ${D.user}...`:"Sending broadcast...").start();try{let G=await hz(Q);if("broadcast_id"in G)if(G.failure_count>0){X.warn(`Email send: ${G.recipient_count} delivered, ${G.failure_count} failed`);for(let B of G.failed_recipients)console.log(H.red(` Failed: ${B}`))}else{let B=D.user?`Email sent to ${D.user}`:`Broadcast sent to ${G.recipient_count} recipient(s)`;X.succeed(`${B} (ID: ${G.broadcast_id})`)}}catch(G){s0(G,X,"Failed to send email")}});var uk0=1500;function mk0(D){switch(D.kind){case"ok":return H.green(`ok ${D.schema_version}`);case"stale":return H.yellow(`stale ${D.schema_version}`);case"missing":return H.red("missing");case"error":return H.red(`error ${D.status||"?"}: ${D.message}`)}}function lk0(D){return D.kind==="stale"||D.kind==="missing"}function pk0(D){let $=D.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".");return new RegExp(`^${$}$`)}var In=new S0("summary").description("summary.json coverage across published dataset versions");In.command("check").description("Report which (dataset_id, version) pairs have stale or missing summary.json").option("--fix","Dispatch generate-manifest for every stale/missing version").option("--id <id>","Limit to a single dataset_id (exact match)").option("--match <glob>","Limit to dataset_ids matching a glob pattern: `*` matches any run of chars, `?` matches one char. Examples: `on*`, `nm00010?`, `*99999`. Can be combined with --id (id wins if both passed).").option("--only-stale","Print only rows that are not ok (suppresses ok rows)").option("--json","Emit the full report as JSON instead of a table").option(P0,I0).option(f0,x0).action(async(D)=>{if(!z0())return;let F=O("Building coverage report...").start(),$;try{$=await zd()}catch(Z){s0(Z,F,"Failed to fetch coverage report");return}F.succeed(`Coverage: target=schema ${$.target_schema}, versions=${$.totals.versions}`);let Q=$.versions,J=null;if(D.id){if(Q=Q.filter((Z)=>Z.dataset_id===D.id),J=`dataset_id=${D.id}`,Q.length===0){console.log(H.yellow(`No rows for ${J}`));return}}else if(D.match){let Z=pk0(D.match);if(Q=Q.filter((z)=>Z.test(z.dataset_id)),J=`--match ${D.match}`,Q.length===0){console.log(H.yellow(`No rows match ${J}`));return}console.log(H.dim(`Scoped to ${Q.length} version(s) matching ${D.match}`))}if(D.json){console.log(JSON.stringify(J?{...$,versions:Q,totals:Un(Q)}:$,null,2));return}let Y=D.onlyStale?Q.filter((Z)=>Z.state.kind!=="ok"):Q;if(Y.length===0)console.log(H.green("All versions at target schema \u2014 no drift."));else{let Z=Math.max(...Y.map((E)=>E.dataset_id.length),10),z=Math.max(...Y.map((E)=>E.version.length),7);for(let E of Y)console.log(` ${E.dataset_id.padEnd(Z)} ${E.version.padEnd(z)} ${mk0(E.state)}`)}let X=J?Un(Q):$.totals;if(console.log(),console.log(` Total: ${X.versions} ${H.green(`ok=${X.ok}`)} ${H.yellow(`stale=${X.stale}`)} ${H.red(`missing=${X.missing}`)} ${H.red(`error=${X.error}`)}`),!D.fix){if(X.stale+X.missing>0)console.log(H.dim(`
452
452
  Run with --fix to dispatch generate-manifest for the ${X.stale+X.missing} drifted version(s).`));return}let G=Q.filter((Z)=>lk0(Z.state));if(G.length===0){console.log(H.green(`
453
453
  Nothing to dispatch.`));return}let B=J?` (scoped to ${J})`:"",W=await w0(`Dispatch generate-manifest for ${G.length} version(s)${B}? Each dispatch queues a runner job on nemarDatasets/.github.`,D);if(W!=="confirmed"){console.log(H.dim(W==="declined"?"Skipped":"Cancelled"));return}let q=0,K=0;console.log();for(let[Z,z]of G.entries()){let E=`${z.dataset_id}@${z.version}`,U=O(`[${Z+1}/${G.length}] dispatching ${E}...`).start();try{await Zd(z.dataset_id,z.version),U.succeed(`[${Z+1}/${G.length}] dispatched ${E}`),q++}catch(L){U.fail(`[${Z+1}/${G.length}] ${E}: ${d(L)}`),K++}if(Z<G.length-1)await new Promise((L)=>setTimeout(L,uk0))}if(console.log(),console.log(K===0?H.green(`All ${q} dispatches succeeded.`):H.yellow(`${q} succeeded, ${K} failed.`)),console.log(H.dim("Workflow runs will appear on https://github.com/nemarDatasets/.github/actions. Re-run `nemar admin summary check` once they complete to verify drift cleared.")),K>0)process.exitCode=1});function Un(D){return{versions:D.length,ok:D.filter((F)=>F.state.kind==="ok").length,stale:D.filter((F)=>F.state.kind==="stale").length,missing:D.filter((F)=>F.state.kind==="missing").length,error:D.filter((F)=>F.state.kind==="error").length}}_0.addCommand(In);e0();Z6();F8();X$();var dk0=/^v?\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\d*)?$/,ck0=/^v\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\d*)?$/;function nk0(D){return D.startsWith("v")?D:`v${D}`}var Sn=I.string().regex(dk0,"must be a semver version, optionally v-prefixed").transform(nk0),fr0=I.string().regex(ck0,"must be the canonical v-prefixed tag (vX.Y.Z)");X$();var ik0=I.object({id:I.number().int(),username:I.string().nullable(),email:I.string(),github_username:I.string().nullable(),role:I.string(),orcid:I.string().nullable().optional(),created_at:I.string().optional(),approved_at:I.string().nullable().optional(),dataset_count:I.number().int().optional(),sandbox_completed:I.boolean().optional(),sandbox_completed_at:I.string().nullable().optional(),sandbox_dataset_id:I.string().nullable().optional()}).passthrough(),ak0=I.object({prefix:I.string(),created_at:I.string(),last_used_at:I.string().nullable()}).passthrough().nullable(),kn=I.object({user:ik0,token:ak0.optional()}).passthrough();X$();var ok0="0.3.0",sk0=I.enum(["openneuro","nemar","gin","other"]),NZ=I.union([I.literal(0),I.literal(1)]).nullable(),vn=I.object({dataset_id:I.string(),id:I.string().optional(),name:I.string(),description:I.string().nullable().optional(),status:I.string(),visibility:I.string(),concept_doi:I.string().nullable().optional(),doi:I.string().nullable().optional(),created_at:I.string(),updated_at:I.string().optional(),owner_username:I.string().nullable().optional(),source:I.string().nullable().optional(),modalities:I.string().optional(),participants:I.number().int().nonnegative(),tasks:I.string().optional(),authors:I.string().optional(),license:I.string().optional(),file_size:I.number().nonnegative(),file_size_formatted:I.string().optional(),total_files:I.number().nonnegative().nullable().optional(),num_citations:I.number().int().nonnegative().optional(),n_channels:I.number().int().nullable().optional(),electrode_system:I.string().nullable().optional(),has_hed:NZ.optional(),hed_version:I.string().nullable().optional(),data_complete:NZ.optional(),bytes_present:I.number().nonnegative().nullable().optional(),latest_version:Sn.nullable().optional()}).passthrough(),rk0=vn.extend({file_size:I.number().nonnegative().nullable(),modalities:I.string().nullable().optional(),tasks:I.string().nullable().optional(),authors:I.string().nullable().optional(),license:I.string().nullable().optional(),owner_github:I.string().nullable().optional()}).passthrough(),tk0=I.object({id:I.string(),name:I.string(),modalities:I.string().nullable().optional(),participants:I.number().int().nullable().optional(),doi:I.string().nullable().optional(),tasks:I.string().nullable().optional(),authors:I.string().nullable().optional(),has_hed:NZ.optional(),score:I.number().optional(),snippet:I.string().optional()}).passthrough(),lr0=I.object({datasets:I.array(vn),count:I.number().int().nonnegative(),total_count:I.number().int().nonnegative().optional(),limit:I.number().int().optional(),offset:I.number().int().optional()}).passthrough(),pr0=I.object({results:I.array(tk0),count:I.number().int().nonnegative(),method:I.string().optional(),min_score:I.number().optional()}).passthrough(),dr0=I.object({dataset:rk0}).passthrough(),cr0=I.object({schema_version:I.literal(ok0),doc_type:I.literal("dataset"),dataset_id:I.string().regex(/^[a-z]{2}\d+$/,"neuroschema dataset_id pattern"),name:I.string(),source:sk0,recording_modality:I.array(I.string()).min(1),description:I.string().nullable().optional(),bids_version:I.string().nullable().optional(),license:I.string().nullable().optional()}).passthrough();S4();async function yn(D){return y(`/auth/check-username?username=${encodeURIComponent(D)}`)}async function xn(D){return y(`/auth/check-github?username=${encodeURIComponent(D)}`)}async function _n(D){return y("/auth/signup",{method:"POST",body:JSON.stringify(D)})}async function CZ(D){return y("/auth/login",{method:"POST",body:JSON.stringify({api_key:D})})}async function fn(D){return y("/auth/resend-verification",{method:"POST",body:JSON.stringify({email:D})})}async function bn(D,F){return y("/auth/retrieve-key",{method:"POST",body:JSON.stringify({email:D,password:F})})}async function gn(D){return y("/auth/request-key-regeneration",{method:"POST",body:JSON.stringify({email:D})})}async function lX(){return(await y("/users/me",{},!0,kn)).user}async function hn(D){return y("/sandbox/complete",{method:"POST",body:JSON.stringify({dataset_id:D})},!0)}async function un(){return y("/sandbox/reset",{method:"POST"},!0)}async function mn(){return y("/sandbox/status",{},!0)}Z1();I4();V1();var ln={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)"}}},ek0={upload:["git-annex","gh","aws","deno"],download:["gh"],clone:["git","gh"],push:["git-annex","gh"],publish:["gh","aws"],update:["git","gh"],release:["git","gh"]};function Dv0(){if(process.platform==="darwin")return"macos";if(process.platform==="win32")return"windows";return"linux"}function pn(D,F=Dv0()){return D.installInstructions[F]}function Fv0(D){let F=D.match(/(\d+\.\d+(?:\.\d+)?)/);return F?F[1]:void 0}async function dn(D){try{let{exitCode:F,stdout:$}=await T(D.cmd);if(F!==0)return{available:!1};return{available:!0,version:Fv0($)}}catch(F){let $=F instanceof Error?F.message:String(F);if(!$.includes("ENOENT")&&!$.includes("not found"))console.error(` Warning: ${D.name} check failed unexpectedly: ${$}`);return{available:!1}}}async function OZ(){let D=Object.entries(ln);return Promise.all(D.map(async([F,$])=>{let{available:Q,version:J}=await dn($);return{key:F,name:$.name,purpose:$.purpose,required:$.required,available:Q,version:J,installInstruction:pn($)}}))}async function cn(){let F=(await OZ()).filter(($)=>$.required&&!$.available);if(F.length===0)return[];console.warn(`
454
454
  Heads up: some tools NEMAR needs for uploading/validating aren't installed yet:`);for(let $ of F)console.warn(` - ${$.name} (${$.purpose})`),console.warn(` Install: ${$.installInstruction}`);return console.warn("Run `nemar doctor` anytime to re-check. You can finish signup without them.\n"),F}async function v3(D){let $=ek0[D].map((X)=>ln[X]).filter(Boolean),J=(await Promise.all($.map(async(X)=>({check:X,available:(await dn(X)).available})))).filter((X)=>!X.available&&X.check.required).map((X)=>({tool:X.check.name,installInstruction:pn(X.check)}));if(J.length===0)return;let Y=[`
@@ -740,7 +740,7 @@ Description:
740
740
  Examples:
741
741
  $ nemar dataset release nm000104 --type patch
742
742
  $ nemar dataset release nm000104 --version 2.0.0
743
- $ nemar dataset release nm000104 # interactive prompt`).action(async(D,F)=>{rD(),await v3("release");let $=O("Fetching dataset info...").start(),Q;try{Q=await VD(D),$.succeed(`Dataset: ${Q.name||D}`)}catch(C){if(C instanceof g)$.fail(C.message);else $.fail("Failed to fetch dataset");process.exit(1)}if(!Q.github_repo)console.log(H.red("Error: Dataset has no GitHub repository")),process.exit(1);let J;try{let C=await Nd(D);if(J=C.current_version,console.log(` Current version: ${H.cyan(J)}`),C.versions.length>0){console.log(` Version DOIs: ${C.versions.length}`);for(let P of C.versions.slice(0,3))console.log(` ${P.version} - ${H.dim(P.doi)}`);if(C.versions.length>3)console.log(H.dim(` ... and ${C.versions.length-3} more`))}}catch(C){let P=C instanceof g?`${C.statusCode}: ${C.message}`:String(C);console.log(H.red(`Error: Could not fetch version history (${P})`)),console.log(" Cannot determine current version. Ensure the backend is reachable."),process.exit(1)}let Y;if(F.version){if(!fZ(F.version))console.log(H.red(`Error: Invalid version: ${F.version}`)),console.log(" Expected format: X.Y.Z (e.g., 2.0.0)"),process.exit(1);Y=F.version.replace(/^v/,"")}else if(F.type){let C=F.type;if(!["patch","minor","major"].includes(C))console.log(H.red(`Error: Invalid bump type: ${F.type}`)),console.log(" Expected: patch, minor, or major"),process.exit(1);Y=_3(J,C)}else{if(!p5(J))console.log(H.red(`Error: Cannot parse current version: ${J}`)),process.exit(1);let P=[{name:`patch ${J} -> ${_3(J,"patch")}`,value:"patch"},{name:`minor ${J} -> ${_3(J,"minor")}`,value:"minor"},{name:`major ${J} -> ${_3(J,"major")}`,value:"major"},{name:"custom version",value:"custom"}],{bumpType:w}=await e.prompt([{type:"list",name:"bumpType",message:"Select version bump type:",choices:P}]);if(w==="custom"){let{customVersion:b}=await e.prompt([{type:"input",name:"customVersion",message:"Enter version (X.Y.Z):",validate:(k)=>fZ(k)||"Invalid format. Use X.Y.Z (e.g., 2.0.0)"}]);Y=b.replace(/^v/,"")}else Y=_3(J,w)}if(console.log(),console.log(` ${H.bold("Version bump:")} ${J} -> ${H.green(Y)}`),await w0(`Create release PR for ${D} v${Y}?`,{yes:F.yes})!=="confirmed"){console.log("Cancelled.");return}let G,B=!0;if(F.dir){if(!w1(F.dir))console.log(H.red(`Error: Directory not found: ${F.dir}`)),process.exit(1);G=k6(F.dir),B=!1}else G=Ky0(d5(zy0(),`nemar-release-${D}-`));if(B){let C=`https://github.com/${Q.github_repo}.git`,P=O("Cloning dataset...").start(),w=await E1(C,G);if(!w.success)P.fail(`Clone failed: ${w.error}`),process.exit(1);P.succeed("Cloned dataset")}let W=`release/v${Y}`,q=O("Creating release branch...").start(),K=kD({cmd:["git","checkout","-b",W],cwd:G,stdout:"pipe",stderr:"pipe"});if(await K.exited!==0){let C=await new Response(K.stderr).text();q.fail(`Failed to create branch: ${C.trim()}`),process.exit(1)}q.succeed(`Created branch: ${W}`);let Z=d5(G,"dataset_description.json");if(!w1(Z))console.log(H.red("Error: dataset_description.json not found in repo")),process.exit(1);let z;try{z=JSON.parse(rX(Z,"utf-8"))}catch(C){if(C instanceof SyntaxError)console.log(H.red("Error: dataset_description.json contains invalid JSON"));else console.log(H.red(`Error: Could not read dataset_description.json: ${C}`));process.exit(1)}z.Version=Y,Aa(Z,`${JSON.stringify(z,null,2)}
743
+ $ nemar dataset release nm000104 # interactive prompt`).action(async(D,F)=>{rD(),await v3("release");let $=O("Fetching dataset info...").start(),Q;try{Q=await VD(D),$.succeed(`Dataset: ${Q.name||D}`)}catch(C){if(C instanceof g)$.fail(C.message);else $.fail("Failed to fetch dataset");process.exit(1)}if(!Q.github_repo)console.log(H.red("Error: Dataset has no GitHub repository")),process.exit(1);let J;try{let C=await Nd(D);if(J=C.current_version.replace(/^v/,""),console.log(` Current version: ${H.cyan(J)}`),C.versions.length>0){console.log(` Version DOIs: ${C.versions.length}`);for(let P of C.versions.slice(0,3))console.log(` ${P.version} - ${H.dim(P.doi)}`);if(C.versions.length>3)console.log(H.dim(` ... and ${C.versions.length-3} more`))}}catch(C){let P=C instanceof g?`${C.statusCode}: ${C.message}`:String(C);console.log(H.red(`Error: Could not fetch version history (${P})`)),console.log(" Cannot determine current version. Ensure the backend is reachable."),process.exit(1)}let Y;if(F.version){if(!fZ(F.version))console.log(H.red(`Error: Invalid version: ${F.version}`)),console.log(" Expected format: X.Y.Z (e.g., 2.0.0)"),process.exit(1);Y=F.version.replace(/^v/,"")}else if(F.type){let C=F.type;if(!["patch","minor","major"].includes(C))console.log(H.red(`Error: Invalid bump type: ${F.type}`)),console.log(" Expected: patch, minor, or major"),process.exit(1);Y=_3(J,C)}else{if(!p5(J))console.log(H.red(`Error: Cannot parse current version: ${J}`)),process.exit(1);let P=[{name:`patch ${J} -> ${_3(J,"patch")}`,value:"patch"},{name:`minor ${J} -> ${_3(J,"minor")}`,value:"minor"},{name:`major ${J} -> ${_3(J,"major")}`,value:"major"},{name:"custom version",value:"custom"}],{bumpType:w}=await e.prompt([{type:"list",name:"bumpType",message:"Select version bump type:",choices:P}]);if(w==="custom"){let{customVersion:b}=await e.prompt([{type:"input",name:"customVersion",message:"Enter version (X.Y.Z):",validate:(k)=>fZ(k)||"Invalid format. Use X.Y.Z (e.g., 2.0.0)"}]);Y=b.replace(/^v/,"")}else Y=_3(J,w)}if(console.log(),console.log(` ${H.bold("Version bump:")} ${J} -> ${H.green(Y)}`),await w0(`Create release PR for ${D} v${Y}?`,{yes:F.yes})!=="confirmed"){console.log("Cancelled.");return}let G,B=!0;if(F.dir){if(!w1(F.dir))console.log(H.red(`Error: Directory not found: ${F.dir}`)),process.exit(1);G=k6(F.dir),B=!1}else G=Ky0(d5(zy0(),`nemar-release-${D}-`));if(B){let C=`https://github.com/${Q.github_repo}.git`,P=O("Cloning dataset...").start(),w=await E1(C,G);if(!w.success)P.fail(`Clone failed: ${w.error}`),process.exit(1);P.succeed("Cloned dataset")}let W=`release/v${Y}`,q=O("Creating release branch...").start(),K=kD({cmd:["git","checkout","-b",W],cwd:G,stdout:"pipe",stderr:"pipe"});if(await K.exited!==0){let C=await new Response(K.stderr).text();q.fail(`Failed to create branch: ${C.trim()}`),process.exit(1)}q.succeed(`Created branch: ${W}`);let Z=d5(G,"dataset_description.json");if(!w1(Z))console.log(H.red("Error: dataset_description.json not found in repo")),process.exit(1);let z;try{z=JSON.parse(rX(Z,"utf-8"))}catch(C){if(C instanceof SyntaxError)console.log(H.red("Error: dataset_description.json contains invalid JSON"));else console.log(H.red(`Error: Could not read dataset_description.json: ${C}`));process.exit(1)}z.Version=Y,Aa(Z,`${JSON.stringify(z,null,2)}
744
744
  `);let E=O("Committing version bump...").start(),U=kD({cmd:["git","add","dataset_description.json"],cwd:G,stdout:"pipe",stderr:"pipe"});if(await U.exited!==0){let C=await new Response(U.stderr).text();E.fail(`Failed to stage changes: ${C.trim()}`),process.exit(1)}let L=kD({cmd:["git","commit","-m",`Bump version to ${Y}`],cwd:G,stdout:"pipe",stderr:"pipe"});if(await L.exited!==0){let C=await new Response(L.stderr).text();E.fail(`Commit failed: ${C.trim()}`),process.exit(1)}E.succeed("Committed version bump");let A=O("Pushing branch...").start(),N=await K$(G,W);if(!N.success)A.fail(`Push failed: ${N.error}`),process.exit(1);A.succeed("Pushed branch");let j=!1,R=O("Creating pull request...").start();try{let C=`Release v${Y}`,P=`## Version Bump
745
745
 
746
746
  Bumps ${D} from ${J} to ${Y}.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nemar-cli",
3
- "version": "0.9.5-dev.1826",
3
+ "version": "0.9.5-dev.1842",
4
4
  "description": "CLI for NEMAR (Neuroelectromagnetic Data Archive and Tools Resource) dataset management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",