@modeldesk/cli 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/modeldesk.js +43 -37
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ not render, host, or publish documents.
|
|
|
7
7
|
|
|
8
8
|
## Install
|
|
9
9
|
|
|
10
|
-
The CLI runs on Node.js (version
|
|
10
|
+
The CLI runs on Node.js (version 22 or newer), on Linux, macOS, and Windows. It is plain JavaScript
|
|
11
11
|
with no native dependencies, so the Node version is the only requirement. Run it on demand with
|
|
12
12
|
`npx`, or install it globally.
|
|
13
13
|
|
package/dist/modeldesk.js
CHANGED
|
@@ -1,43 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'module'; const require = createRequire(import.meta.url);
|
|
3
|
-
import{chmodSync as
|
|
4
|
-
`,{mode:
|
|
5
|
-
`)},
|
|
6
|
-
${
|
|
7
|
-
`)},
|
|
8
|
-
`);let n=
|
|
9
|
-
`),
|
|
10
|
-
`),0};var
|
|
11
|
-
`);let n=
|
|
3
|
+
import{chmodSync as xt,existsSync as hr,mkdirSync as gr,readFileSync as yr,writeFileSync as wr}from"node:fs";import{homedir as kr}from"node:os";import{join as vt}from"node:path";var St=448,Ct=384,S=class{dir;file;constructor(t=vt(kr(),".modeldesk")){this.dir=t,this.file=vt(t,"credentials.json")}getRefreshToken(t){let n=this.read().hosts[t]?.refreshToken;return typeof n=="string"?n:void 0}saveRefreshToken(t,n){let r=this.read();r.hosts[t]={refreshToken:n},this.write(r)}saveRefreshTokenIfPresent(t,n){n&&this.saveRefreshToken(t,n)}clearHost(t){let n=this.read();return n.hosts[t]?(delete n.hosts[t],this.write(n),!0):!1}read(){if(!hr(this.file))return{hosts:{}};try{let t=JSON.parse(yr(this.file,"utf8"));if(t&&typeof t=="object"&&t.hosts&&typeof t.hosts=="object")return t}catch{}return{hosts:{}}}write(t){gr(this.dir,{recursive:!0,mode:St}),wr(this.file,`${JSON.stringify(t,null,2)}
|
|
4
|
+
`,{mode:Ct}),xt(this.dir,St),xt(this.file,Ct)}};var Le=e=>{let t=e.split(".");if(t.length===3)try{let n=Buffer.from(t[1],"base64url").toString("utf8"),r=JSON.parse(n);return r&&typeof r=="object"&&!Array.isArray(r)?r:void 0}catch{return}},ae=e=>{let t=Le(e);if(t)return t.name&&t.email?`${t.name} <${t.email}>`:t.email??t.name??t.sub};import{spawn as Er}from"node:child_process";import{platform as br}from"node:process";var It=e=>{if(!Tr(e))return;let{command:t,args:n,verbatim:r}=xr(e);try{let o=Er(t,n,{stdio:"ignore",detached:!0,windowsVerbatimArguments:r});o.on("error",()=>{}),o.unref()}catch{}},Tr=e=>{try{let{protocol:t}=new URL(e);return t==="http:"||t==="https:"}catch{return!1}},xr=(e,t=br)=>{switch(t){case"darwin":return{command:"open",args:[e]};case"win32":return{command:"cmd",args:["/c","start",'""',vr(e)],verbatim:!0};default:return{command:"xdg-open",args:[e]}}},vr=e=>e.replace(/[&|<>^()%"]/g,t=>`^${t}`);var Y="modeldesk-cli",$t="modeldesk-federation",Rt="openid",Pe="/callback";var a=class extends Error{exitCode;constructor(t,n=1){super(t),this.name="CliError",this.exitCode=n}};var P=async e=>{let t=`${e.replace(/\/+$/,"")}/.well-known/openid-configuration`,n;try{n=await fetch(t,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(3e4)})}catch(o){throw new a(`Could not reach the auth server at ${e} (${o.message}).`)}if(!n.ok)throw new a(`OpenID discovery failed at ${t}: ${n.status} ${n.statusText}`);let r=await n.json();if(!r.authorization_endpoint||!r.token_endpoint)throw new a(`OpenID discovery at ${t} did not return the authorization and token endpoints.`);return{authorizationEndpoint:r.authorization_endpoint,tokenEndpoint:r.token_endpoint}};import{createServer as Sr}from"node:http";var Cr=3e5,At=(e,t=Cr)=>new Promise((n,r)=>{let o,s=new Promise((m,f)=>{o={resolve:m,reject:f}}),i=Sr((m,f)=>{let p=new URL(m.url??"/","http://127.0.0.1");if(p.pathname!==Pe){f.writeHead(404,{"Content-Type":"text/plain",Connection:"close"}).end("Not found");return}Ir(p,f,e,o)}),d=setTimeout(()=>o.reject(new a("Timed out waiting for the browser login to complete.")),t);d.unref();let c=!1,l=()=>{c||(c=!0,clearTimeout(d),i.close())};s.then(l,l),i.once("error",m=>r(new a(`Could not start the loopback listener: ${m.message}`))),i.listen(0,"127.0.0.1",()=>{let{port:m}=i.address();n({redirectUri:`http://127.0.0.1:${m}${Pe}`,waitForCode:()=>s,close:l})})}),Ir=(e,t,n,r)=>{if(e.searchParams.get("state")!==n){t.writeHead(404,{"Content-Type":"text/plain",Connection:"close"}).end("Unexpected request");return}let o=e.searchParams.get("error");if(o){let i=e.searchParams.get("error_description");Me(t,"Sign-in failed","Sign-in failed. You can close this window and return to the terminal."),r.reject(new a(`Authorization failed: ${i?`${o} (${i})`:o}`));return}let s=e.searchParams.get("code");if(!s){Me(t,"Sign-in failed","No authorization code was returned. You can close this window."),r.reject(new a("Login failed: no authorization code was returned."));return}Me(t,"Signed in","You are signed in to the ModelDesk CLI. You can close this window and return to the terminal."),r.resolve(s)},Me=(e,t,n)=>{let r=`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${t}</title></head><body style="font-family: system-ui, sans-serif; text-align: center; padding: 3rem;"><h1>${t}</h1><p>${n}</p></body></html>`;e.writeHead(200,{"Content-Type":"text/html; charset=utf-8",Connection:"close"}).end(r)};import{createHash as $r,randomBytes as _t}from"node:crypto";var Ne=e=>e.toString("base64url"),Lt=()=>{let e=Ne(_t(32)),t=Ne($r("sha256").update(e).digest());return{verifier:e,challenge:t,method:"S256"}},Pt=()=>Ne(_t(16));var W=class extends a{oauthError;errorDescription;constructor(t,n,r){super(t),this.name="TokenEndpointError",this.oauthError=n,this.errorDescription=r}},Mt=e=>e instanceof W&&e.oauthError==="invalid_grant",Nt=(e,t)=>ce(e,new URLSearchParams({grant_type:"authorization_code",code:t.code,redirect_uri:t.redirectUri,client_id:Y,code_verifier:t.codeVerifier})),de=(e,t)=>ce(e,new URLSearchParams({grant_type:"refresh_token",refresh_token:t,client_id:Y})),Dt=(e,t)=>ce(e,new URLSearchParams({grant_type:"client_credentials",client_id:t.clientId,client_secret:t.clientSecret})),Ot=(e,t)=>ce(e,new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:token-exchange",subject_token:t,subject_token_type:"urn:ietf:params:oauth:token-type:jwt",client_id:$t})),ce=async(e,t)=>{let n;try{n=await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:t.toString(),signal:AbortSignal.timeout(3e4)})}catch(o){throw new a(`Could not reach the token endpoint (${o.message}).`)}if(!n.ok){let{message:o,code:s,description:i}=await Rr(n);throw new W(`Token request failed: ${o}`,s,i)}let r=await n.json();if(!r.access_token)throw new a("The token response did not include an access token.");return{accessToken:r.access_token,refreshToken:r.refresh_token,expiresInSeconds:r.expires_in??0}},Rr=async e=>{try{if((e.headers.get("content-type")??"").includes("json")){let n=await e.json();if(n.error)return{message:n.error_description?`${n.error} (${n.error_description})`:n.error,code:n.error,description:n.error_description}}}catch{}return{message:`${e.status} ${e.statusText}`.trim()}};var le={info:e=>process.stderr.write(`${e}
|
|
5
|
+
`)},me=async(e,t)=>{let{authorizationEndpoint:n,tokenEndpoint:r}=await P(e.issuer),o=Lt(),s=Pt(),i=await At(s);try{let d=Ar(n,i.redirectUri,o.challenge,s);t.info(`Opening your browser to sign in to ${e.host}...`),t.info(`If it does not open automatically, visit:
|
|
6
|
+
${d}`),It(d);let c=await i.waitForCode();return await Nt(r,{code:c,redirectUri:i.redirectUri,codeVerifier:o.verifier})}finally{i.close()}},Ar=(e,t,n,r)=>{let o=new URL(e);return o.searchParams.set("response_type","code"),o.searchParams.set("client_id",Y),o.searchParams.set("redirect_uri",t),o.searchParams.set("scope",Rt),o.searchParams.set("state",r),o.searchParams.set("code_challenge",n),o.searchParams.set("code_challenge_method","S256"),o.toString()};var De="modeldesk.io",u={host:"MODELDESK_HOST",model:"MODELDESK_MODEL",stack:"MODELDESK_STACK",template:"MODELDESK_TEMPLATE",version:"MODELDESK_VERSION",clientId:"MODELDESK_CLIENT_ID",clientSecret:"MODELDESK_CLIENT_SECRET",apiKey:"MODELDESK_API_KEY"},X=(...e)=>e.find(t=>t!==void 0&&t!==""),Oe=(e,t,n)=>({host:X(e.host,t[u.host],n.host)??De,model:X(e.model,t[u.model],n.model),stack:X(e.stack,t[u.stack],n.stack),template:X(e.template,t[u.template],n.template),version:X(e.version,t[u.version],n.version)});var Ht={requestUrl:"ACTIONS_ID_TOKEN_REQUEST_URL",requestToken:"ACTIONS_ID_TOKEN_REQUEST_TOKEN"},M=e=>e!==void 0&&e!=="",Ft=e=>e==="true"||e==="1",_r=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,He="mdsk_",Ut=57,F=(e,t)=>({mode:"error",reason:e,message:t}),N="org settings \u2192 Service accounts",J=e=>{if(M(e[u.apiKey]))return F("legacy-api-key",`${u.apiKey} is no longer supported: the CI credential shipped as service accounts, not bearer keys. Use secret mode (set ${u.clientId} and ${u.clientSecret} from ${N}) or, on GitHub Actions, federated auth (grant 'permissions: id-token: write' and add a trust rule). Unset ${u.apiKey}.`);let t=e[u.clientId],n=e[u.clientSecret];if(M(t)!==M(n)){let[s,i]=M(t)?[u.clientId,u.clientSecret]:[u.clientSecret,u.clientId];return F("partial-secret-config",`${s} is set but ${i} is not \u2014 both are required for secret-mode auth. Check the pipeline's secret configuration.`)}if(M(t)&&M(n))return _r.test(t)?n.startsWith(He)?n.length!==Ut?F("invalid-client-secret",`${u.clientSecret} has the wrong length: expected ${Ut} characters, got ${n.length}. Check for truncation in the pipeline's secret store.`):{mode:"secret",clientId:t,clientSecret:n}:F("invalid-client-secret",`${u.clientSecret} does not start with '${He}' (an issued secret always does). Check for quoting or truncation in the pipeline's secret store.`):F("invalid-client-id",`${u.clientId} is not a service-account id: expected a UUID, got a ${t.length}-character value. Copy the account id from ${N}.`);let r=e[Ht.requestUrl],o=e[Ht.requestToken];return M(r)&&M(o)?{mode:"federated",oidcRequestUrl:r,oidcRequestToken:o}:Ft(e.GITHUB_ACTIONS)?F("headless-ci",`Running on GitHub Actions without machine credentials. Grant 'permissions: id-token: write' to use federated auth (with a trust rule in ${N}), or set ${u.clientId} and ${u.clientSecret}. The CLI never opens a browser login in CI.`):Ft(e.CI)?F("headless-ci",`Running in CI without machine credentials. Set ${u.clientId} and ${u.clientSecret} (${N}). The CLI never opens a browser login in CI.`):{mode:"interactive"}},D=e=>{let t=J(e);return t.mode==="secret"||t.mode==="federated"?t.mode:void 0},Wt=e=>`${e.slice(0,He.length+8)}\u2026`,zt=`Check ${u.clientId} and ${u.clientSecret}; issue or rotate a secret in ${N}.`,Bt=`The service account may be disabled or deleted \u2014 check ${N}.`,jt={issuer:"the auth server does not trust this token issuer \u2014 federated auth currently supports GitHub Actions only.",signature:"the OIDC token's signature could not be verified \u2014 re-run the job; if it persists, the issuer's keys and the auth server disagree.",audience:`the workflow's OIDC audience does not match this host \u2014 check that --host/${u.host} names the ModelDesk instance the trust rule lives on.`,expired:"the OIDC token expired before the exchange \u2014 re-run the job.",repository:"no active trust rule matches this repository. This is also the failure for a pull-request-triggered run unless the rule enables 'Allow pull requests', and for a revoked rule.",binding:"the trust rule pins a branch or environment that does not match this run."},pe={unauthorized:`The machine token was rejected. A disabled or deleted service account fails on the next request; a revoked secret within five minutes. Check ${N}.`,forbidden:`The service account's scopes do not permit this \u2014 pulling content needs the 'model:content' scope (${N}).`,notFound:"Check the model id. A machine credential reads SHARED models only (creator-private models are not visible), and the model's metamodel must be entitled under the organization's subscription."};var Lr="http://localhost:8080",Pr="http://localhost:8082",Mr="localhost",Nr=e=>{let t=e.trim(),n=/:\/\//.test(t)?t:`https://${t}`;try{return new URL(n).hostname}catch{return t.replace(/^[a-z][a-z0-9+.-]*:\/\//i,"").split("/")[0].split(":")[0]}},Dr=e=>e==="localhost"||e==="127.0.0.1"||e==="::1"||e==="[::1]",Fe=e=>{let t=Nr(e);return Dr(t)?{host:Mr,apiBase:Lr,issuer:Pr}:{host:t,apiBase:`https://api.${t}`,issuer:`https://auth.${t}`}};import{existsSync as Or,readFileSync as Hr}from"node:fs";import{join as Fr}from"node:path";var fe=".modeldesk",Ur="config.json",ue=e=>e.configPath!==void 0,Ue=e=>{let t=Fr(e,fe,Ur);return Or(t)?{config:Wr(t),configPath:t,repoRoot:e}:{config:{},repoRoot:e}},Wr=e=>{let t;try{t=Hr(e,"utf8")}catch(o){throw new a(`Could not read ${e}: ${o.message}`)}let n;try{n=JSON.parse(t)}catch(o){throw new a(`${e} is not valid JSON: ${o.message}`)}if(n===null||typeof n!="object"||Array.isArray(n))throw new a(`${e} must contain a JSON object.`);let r=n;return{host:q(r,"host",e),model:q(r,"model",e),stack:q(r,"stack",e),template:q(r,"template",e),version:q(r,"version",e)}},q=(e,t,n)=>{let r=e[t];if(r!==void 0){if(typeof r!="string")throw new a(`${n}: "${t}" must be a string.`);return r}};import{parseArgs as zr}from"node:util";var z=(e,t)=>{try{return zr({args:e,options:t,allowPositionals:!1,strict:!0})}catch(n){throw new a(n.message)}};var Gt=(e,t,n)=>{let r=n[u.host];return!(t!==void 0&&t!==""||r!==void 0&&r!=="")&&ue(e)&&!!e.config.host},Vt=(e,t,n)=>{Gt(e,t,n)&&e.config.host!==De&&process.stderr.write(`note: using host '${e.config.host}' from ${e.configPath} (it drives login and API calls).
|
|
7
|
+
`)},Kt=(e,t,n)=>{if(D(n)&&Gt(e,t,n))throw new a(`In a machine-auth run the host must be set explicitly: pass --host or set ${u.host}. The committed ${e.configPath??".modeldesk/config.json"} is not a trusted source for where machine credentials are sent.`)},B=(e,t={})=>{let{values:n}=z(e.argv,{host:{type:"string"}}),r=Ue(e.cwd),o=Oe({host:n.host},e.env,r.config);return t.gateMachineHost&&Kt(r,n.host,e.env),Vt(r,n.host,e.env),Fe(o.host)},he=(e,t)=>{let n=Ue(e.cwd),r=Oe(t,e.env,n.config);if(Kt(n,t.host,e.env),Vt(n,t.host,e.env),!r.model)throw new a(`No model specified. Pass --model, set ${u.model}, or add "model" to .modeldesk/config.json.`);return{instance:Fe(r.host),model:r.model,stack:r.stack,template:r.template,version:r.version,loaded:n}},We=(e,t,n,r)=>{let o=e?.trim();if(!o)throw new a(`No ${t} specified. Pass ${r}, set ${n}, or add "${t}" to .modeldesk/config.json. Run 'modeldesk bundle templates' to list a model's stacks and templates.`);return o};var Yt=async e=>{let t=D(e.env);t&&process.stderr.write(`warning: machine auth (${t} mode) is active in this environment and takes precedence for API commands; this login only affects interactive use.
|
|
8
|
+
`);let n=B(e),r=await me(n,le),o=new S;r.refreshToken?o.saveRefreshToken(n.host,r.refreshToken):o.clearHost(n.host);let s=ae(r.accessToken);return process.stdout.write(`Logged in to ${n.host}${s?` as ${s}`:""}.
|
|
9
|
+
`),r.refreshToken||process.stderr.write(`warning: the server returned no refresh token, so later commands will re-open the browser each time instead of running unattended.
|
|
10
|
+
`),0};var Xt=async e=>{let t=D(e.env);t&&process.stderr.write(`warning: machine auth (${t} mode) is active in this environment and takes precedence for API commands; this logout does not remove machine credentials.
|
|
11
|
+
`);let n=B(e),r=new S().clearHost(n.host);return process.stdout.write(r?`Logged out of ${n.host}.
|
|
12
12
|
`:`No stored credential for ${n.host}.
|
|
13
|
-
`),0};var
|
|
14
|
-
`)},Ze=async e=>{let t=_(e.env);if(t.mode==="error")throw new i(t.message);let n=t.mode==="secret"||t.mode==="federated",o=x(e,{gateMachineHost:n});return g(`Host: ${o.host}`),g(`API: ${o.apiBase}`),t.mode==="secret"||t.mode==="federated"?fn(o,t,e.env):gn(o)},fn=async(e,t,n)=>{t.mode==="secret"?g(`Auth: service account (secret mode), client ${t.clientId}, secret ${Je(t.clientSecret)}`):g(`Auth: federated (GitHub Actions), repository ${n.GITHUB_REPOSITORY??"unknown"}, ref ${n.GITHUB_REF??"unknown"}`);let o=await q(e,t).getAccessToken(),r=te(o)??{};g(`Status: credential valid. Account ${r.sub??"unknown"}, organization ${r.mdsk_org??"unknown"}.`);let s=typeof r.scope=="string"?r.scope.trim():"";return g(s?`Scopes: ${s}`:"Scopes: enforced server-side from the account's current scopes."),0},gn=async e=>{let t=new f,n=t.getRefreshToken(e.host);if(!n)return g("Status: not logged in. Run 'modeldesk auth login'."),1;try{let{tokenEndpoint:o}=await w(e.issuer),r=await N(o,n);t.saveRefreshTokenIfPresent(e.host,r.refreshToken);let s=D(r.accessToken);return g(`Status: logged in${s?` as ${s}`:""} (credential valid).`),0}catch{return g("Status: the stored credential could not be verified (it may be expired or revoked, or the server is unreachable). Run 'modeldesk auth login'."),1}};import{existsSync as he,lstatSync as et,realpathSync as yn}from"node:fs";import{homedir as wn}from"node:os";import{basename as kn,dirname as vn,isAbsolute as W,join as tt,relative as ue,resolve as $}from"node:path";var V=(e,t,n)=>{if(W(n))throw new i(`Refusing an absolute path from the bundle manifest: ${n}`);let o=$(e,t),r=$(e,n),s=ue(o,r);if(s===""||s.startsWith("..")||W(s))throw new i(`Refusing a bundle path that escapes the content root: ${n}`);return r},Tn=e=>{try{return et(e)}catch{return}},K=e=>{let t=[],n=e;for(;!he(n);){let r=vn(n);if(r===n)return e;t.unshift(kn(n)),n=r}let o=yn(n);return t.length>0?tt(o,...t):o},fe=e=>{if(Tn(e)?.isSymbolicLink())throw new i(`Refusing to write through an existing symlink at a managed path: ${e}`)},Y=(e,t)=>{let n=K(e),o=K(t),r=ue(n,o);if(r!==""&&(r.startsWith("..")||W(r)))throw new i(`Refusing a path that escapes the content root through a symlink: ${t}`)},ge=(e,t)=>{let n=$(e,t);if(n===$(e))throw new i(`The content root resolves to the repository root (${n}); refusing to sync it.`);if(n===$(wn()))throw new i("The content root resolves to your home directory; refusing to sync it.");if(he(n)){if(et(n).isSymbolicLink())throw new i(`The content root ${n} is a symlink; refusing to sync it.`);if(he(tt(n,".git")))throw new i(`The content root ${n} contains a .git directory (it looks like a repo root); refusing.`)}let o=K($(e)),r=ue(o,K(n));if(r!==""&&(r.startsWith("..")||W(r)))throw new i(`The content root ${n} resolves outside the repository via a symlinked ancestor; refusing.`);return n};var Cn={respec:"data/model"},nt=e=>{let t=Cn[e];if(!t)throw new i(`The CLI does not know the content root for stack '${e}'.`);return t};var En=1500,bn=6e5,Sn=e=>new Promise(t=>setTimeout(t,e)),xn=e=>e==="COMPLETED"||e==="COMPLETED_WITH_ERRORS"||e==="FAILED",ot=async(e,t,n)=>{let o=await e.postForJson(`/models/${t}/content-bundle/diagrams`,{template:n.template,version:n.version});if(!o.commit)throw new i("The diagram render did not return a resolved commit; cannot pin the pull.");return o},rt=async(e,t,n,o,r,s={})=>{let a=s.sleep??Sn,c=s.now??Date.now,l=c()+bn;for(o.update(0,r);;){let d=await e.getJson(`/models/${t}/export-jobs/${n}`);if(d.progress&&o.update(d.progress.completed,d.progress.total,d.progress.currentItem),xn(d.status))return o.done(),d;if(c()>=l)throw new i("Timed out waiting for the diagram render to finish.");await a(En)}},st=(e,t,n,o)=>e.getBytes(`/models/${t}/content-bundle/media`,{path:n,template:o.template,commit:o.commit});var it=e=>{let t=new Map;for(let n of e.fragments??[])n.realization?.path&&t.set(n.elementId,n.realization.path);return t},at=(e,t)=>{let n=it(e),o=[];for(let[r,s]of it(t)){let a=n.get(r);a&&a!==s&&o.push({elementId:r,oldPath:a,newPath:s})}return o};var ye=e=>{let t=e.payloads??{},n=[];for(let o of e.manifest.fragments??[]){let r=o.realization?.path;if(!r)continue;let s=t[o.address];s!==void 0&&n.push({path:r,content:s})}return n},we=e=>{let t=[];for(let n of e.fragments??[])for(let o of n.realization?.media??[])t.push(o);return[...new Set(t)]};var ct=e=>`${JSON.stringify(e,null,2)}
|
|
15
|
-
|
|
16
|
-
`),n=o}}
|
|
17
|
-
`)}}};import{
|
|
18
|
-
`)
|
|
19
|
-
`)
|
|
20
|
-
`)
|
|
21
|
-
`),
|
|
22
|
-
`)
|
|
23
|
-
`),
|
|
24
|
-
`),y(
|
|
25
|
-
`)
|
|
26
|
-
`)
|
|
27
|
-
`)
|
|
28
|
-
`)
|
|
29
|
-
`),r.
|
|
30
|
-
`)
|
|
31
|
-
`)
|
|
32
|
-
`);
|
|
33
|
-
`)
|
|
34
|
-
`)
|
|
35
|
-
`),
|
|
36
|
-
`);
|
|
13
|
+
`),0};var ze=6e4,ge=class{constructor(t,n={}){this.instance=t;this.store=n.store??new S,this.io=n.io??le,this.now=n.now??Date.now}instance;store;io;now;accessToken;accessTokenExpiresAt=0;endpoints;async getAccessToken(){if(this.accessToken&&this.now()<this.accessTokenExpiresAt-ze)return this.accessToken;let t=this.store.getRefreshToken(this.instance.host);if(t){let n=await this.tryRefresh(t);if(n)return n}return this.login()}async tryRefresh(t){let{tokenEndpoint:n}=await this.getEndpoints();try{return this.apply(await de(n,t))}catch(r){if(Mt(r))return;throw r}}async login(){return this.apply(await me(this.instance,this.io))}apply(t){return this.accessToken=t.accessToken,this.accessTokenExpiresAt=this.now()+t.expiresInSeconds*1e3,this.store.saveRefreshTokenIfPresent(this.instance.host,t.refreshToken),t.accessToken}async getEndpoints(){return this.endpoints||(this.endpoints=await P(this.instance.issuer)),this.endpoints}};var Be=class{constructor(t,n,r,o={}){this.instance=t;this.mode=n;this.mint=r;this.now=o.now??Date.now,this.discover=o.discover??P}instance;mode;mint;now;discover;accessToken;accessTokenExpiresAt=0;endpoints;inflight;async getAccessToken(){return this.accessToken&&this.now()<this.accessTokenExpiresAt-ze?this.accessToken:(this.inflight||(this.inflight=this.mintFresh().finally(()=>{this.inflight=void 0})),this.inflight)}async mintFresh(){let{tokenEndpoint:t}=await this.getEndpoints(),n;try{n=await this.mint(t)}catch(r){throw jr(r,this.mode)}if(n.expiresInSeconds<=0)throw new a("The token response did not include a token lifetime (expires_in), so the machine token cannot be cached. This indicates a server problem.");return this.accessToken=n.accessToken,this.accessTokenExpiresAt=this.now()+n.expiresInSeconds*1e3,n.accessToken}async getEndpoints(){return this.endpoints||(this.endpoints=await this.discover(this.instance.issuer)),this.endpoints}},Br=async(e,t,n)=>{let r;try{r=await fetch(`${e}&audience=${encodeURIComponent(n)}`,{headers:{Authorization:`bearer ${t}`,Accept:"application/json"},signal:AbortSignal.timeout(3e4)})}catch(s){throw new a(`Could not reach the GitHub OIDC token endpoint (${s.message}).`)}if(!r.ok)throw new a(`The GitHub OIDC token request failed (${r.status} ${r.statusText}). Check that the workflow grants 'permissions: id-token: write'.`);let o=await r.json().catch(()=>{});if(!o?.value)throw new a("The GitHub OIDC token response did not include a token value. Check that the workflow grants 'permissions: id-token: write'.");return o.value},ye=(e,t,n={})=>{let r=t.mode==="secret"?o=>Dt(o,{clientId:t.clientId,clientSecret:t.clientSecret}):async o=>Ot(o,await Br(t.oidcRequestUrl,t.oidcRequestToken,e.issuer));return new Be(e,t.mode,r,n)},jr=(e,t)=>{if(!(e instanceof W)||!e.oauthError)return e;if(t==="secret")return e.oauthError==="invalid_client"?new a(`${e.message} ${zt}`):e;if(e.errorDescription===void 0)return e.oauthError==="invalid_grant"?new a(`${e.message} ${Bt}`):e;let n=jt[e.errorDescription];return n?new a(`${e.message} \u2014 ${n}`):e};var $=e=>{process.stdout.write(`${e}
|
|
14
|
+
`)},Jt=async e=>{let t=J(e.env);if(t.mode==="error")throw new a(t.message);let n=t.mode==="secret"||t.mode==="federated",r=B(e,{gateMachineHost:n});return $(`Host: ${r.host}`),$(`API: ${r.apiBase}`),t.mode==="secret"||t.mode==="federated"?Gr(r,t,e.env):Vr(r)},Gr=async(e,t,n)=>{t.mode==="secret"?$(`Auth: service account (secret mode), client ${t.clientId}, secret ${Wt(t.clientSecret)}`):$(`Auth: federated (GitHub Actions), repository ${n.GITHUB_REPOSITORY??"unknown"}, ref ${n.GITHUB_REF??"unknown"}`);let r=await ye(e,t).getAccessToken(),o=Le(r)??{};$(`Status: credential valid. Account ${o.sub??"unknown"}, organization ${o.mdsk_org??"unknown"}.`);let s=typeof o.scope=="string"?o.scope.trim():"";return $(s?`Scopes: ${s}`:"Scopes: enforced server-side from the account's current scopes."),0},Vr=async e=>{let t=new S,n=t.getRefreshToken(e.host);if(!n)return $("Status: not logged in. Run 'modeldesk auth login'."),1;try{let{tokenEndpoint:r}=await P(e.issuer),o=await de(r,n);t.saveRefreshTokenIfPresent(e.host,o.refreshToken);let s=ae(o.accessToken);return $(`Status: logged in${s?` as ${s}`:""} (credential valid).`),0}catch{return $("Status: the stored credential could not be verified (it may be expired or revoked, or the server is unreachable). Run 'modeldesk auth login'."),1}};var Kr=/[ \t\n\r\f]+/g,Yr=/^[ \t\n\r\f]+|[ \t\n\r\f]+$/g,Xr=/[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,Z=e=>e.replace(Xr,"");var Jr=(e,t)=>{if(!Number.isFinite(e))throw new Error(`Cannot write ${e} as an SVG number`);let n=10**t,r=Math.sign(e)*Math.round(Math.abs(e)*n)/n;return String(r===0?0:r)},je=e=>Jr(e,2);var qr=e=>e.replace(Kr," ").replace(Yr,""),Ge=e=>qr(Z(e)).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");var Q="http://www.w3.org/2000/svg",Qt="Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",Ve="group",Ke="spacingAndGlyphs",Ye="italic",Xe="underline",j="none";var we="hidden",ke="stroke",Je="auto";var Zr="start",Ee="clip",Qr=/^[A-Za-z0-9_-]+$/,eo=/[^A-Za-z0-9_-]+/g,qe=e=>Qr.test(e),en=e=>e.replace(eo,"-");var Ze=(e,t)=>t?`${e}-${Zr}`:e,qt=String.raw`-?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?`,Zt=String.raw`[a-zA-Z]+\(\s*(?:${qt}(?:[\s,]+${qt})*)?\s*\)`,to=new RegExp(String.raw`^${Zt}(?:\s+${Zt})*$`),tn=e=>to.test(e);var no=/[\u0000- \u007F]/g,ro=/^([a-zA-Z][a-zA-Z0-9+.-]*):/,oo=new Set(["http","https","mailto"]),be=e=>{let t=Z(e).replace(no,"");if(t==="")return!1;let n=ro.exec(t);return n===null||oo.has(n[1].toLowerCase())};var g="#4b5563";var x="white";var Qe="strokeWidth",Te=[{id:"triangle-filled",markerWidth:20,markerHeight:20,refX:20,refY:10,shapes:[{element:"line",x1:16,y1:10,x2:20,y2:10,stroke:"white",strokeWidth:2},{element:"path",d:"M 6 4 L 19 10 L 6 16 L 10 10 Z",fill:g,stroke:g,strokeWidth:1}]},{id:"triangle-hollow",markerWidth:24,markerHeight:24,refX:24,refY:12,shapes:[{element:"line",x1:20,y1:12,x2:24,y2:12,stroke:"white",strokeWidth:2},{element:"path",d:"M 6 2 L 22 12 L 6 22 Z",fill:x,stroke:g,strokeWidth:2}]},{id:"diamond-filled",markerWidth:20,markerHeight:20,refX:20,refY:10,shapes:[{element:"path",d:"M 10 5 L 19 10 L 10 15 L 1 10 Z",fill:g,stroke:g,strokeWidth:1.5}]},{id:"diamond-hollow",markerWidth:20,markerHeight:20,refX:20,refY:10,shapes:[{element:"path",d:"M 10 5 L 19 10 L 10 15 L 1 10 Z",fill:x,stroke:g,strokeWidth:1.5}]},{id:"arrow-open",markerWidth:20,markerHeight:20,refX:20,refY:10,shapes:[{element:"path",d:"M 6 4 L 19 10 L 6 16",fill:"none",stroke:g,strokeWidth:2}]},{id:"bar",markerWidth:18,markerHeight:12,refX:18,refY:6,shapes:[{element:"line",x1:13,y1:1,x2:13,y2:11,stroke:g,strokeWidth:1.5}]},{id:"circle",markerWidth:18,markerHeight:12,refX:18,refY:6,shapes:[{element:"circle",cx:13.5,cy:6,r:2.75,fill:x,stroke:g,strokeWidth:1.5}]},{id:"crowsfoot",markerWidth:18,markerHeight:12,refX:18,refY:6,shapes:[{element:"path",d:"M 18 1 L 8 6 L 18 11 M 8 6 L 18 6",fill:"none",stroke:g,strokeWidth:1.5}]},{id:"crowsfoot-bar",markerWidth:18,markerHeight:12,refX:18,refY:6,shapes:[{element:"path",d:"M 18 1 L 8 6 L 18 11 M 8 6 L 18 6",fill:"none",stroke:g,strokeWidth:1.5},{element:"line",x1:4,y1:1,x2:4,y2:11,stroke:g,strokeWidth:1.5}]},{id:"crowsfoot-circle",markerWidth:18,markerHeight:12,refX:18,refY:6,shapes:[{element:"path",d:"M 18 1 L 8 6 L 18 11 M 8 6 L 18 6",fill:"none",stroke:g,strokeWidth:1.5},{element:"circle",cx:4,cy:6,r:2.75,fill:x,stroke:g,strokeWidth:1.5}]},{id:"bar-circle",markerWidth:18,markerHeight:12,refX:18,refY:6,shapes:[{element:"line",x1:13,y1:1,x2:13,y2:11,stroke:g,strokeWidth:1.5},{element:"circle",cx:4,cy:6,r:2.75,fill:x,stroke:g,strokeWidth:1.5}]},{id:"crowsfoot-triangle-circle",markerWidth:24,markerHeight:12,refX:24,refY:6,shapes:[{element:"path",d:"M 24 3 L 19 3 M 24 6 L 19 6 M 24 9 L 19 9",fill:"none",stroke:g,strokeWidth:1.5},{element:"path",d:"M 19 1 L 19 11 L 11 6 Z",fill:x,stroke:g,strokeWidth:1.5,strokeLinejoin:"round"},{element:"circle",cx:8.25,cy:6,r:2.75,fill:x,stroke:g,strokeWidth:1.5}]},{id:"crowsfoot-triangle-bar",markerWidth:24,markerHeight:12,refX:24,refY:6,shapes:[{element:"path",d:"M 24 3 L 19 3 M 24 6 L 19 6 M 24 9 L 19 9",fill:"none",stroke:g,strokeWidth:1.5},{element:"path",d:"M 19 1 L 19 11 L 11 6 Z",fill:x,stroke:g,strokeWidth:1.5,strokeLinejoin:"round"},{element:"line",x1:8,y1:1,x2:8,y2:11,stroke:g,strokeWidth:1.5}]},{id:"triangle-circle",markerWidth:24,markerHeight:12,refX:24,refY:6,shapes:[{element:"path",d:"M 19 1 L 19 11 L 11 6 Z",fill:x,stroke:g,strokeWidth:1.5,strokeLinejoin:"round"},{element:"circle",cx:8.25,cy:6,r:2.75,fill:x,stroke:g,strokeWidth:1.5}]},{id:"triangle-bar",markerWidth:24,markerHeight:12,refX:24,refY:6,shapes:[{element:"path",d:"M 19 1 L 19 11 L 11 6 Z",fill:x,stroke:g,strokeWidth:1.5,strokeLinejoin:"round"},{element:"line",x1:8,y1:1,x2:8,y2:11,stroke:g,strokeWidth:1.5}]},{id:"cup",markerWidth:18,markerHeight:12,refX:18,refY:6,shapes:[{element:"path",d:"M 13 1 A 5 5 0 0 0 13 11",fill:"none",stroke:g,strokeWidth:1.5}]}],ua=new Map(Te.map(e=>[e.id,e]));var lo=" ",et=["fill","fill-opacity","stroke","stroke-opacity","stroke-width","stroke-dasharray"],mo={svg:["xmlns","viewBox","width","height","role","aria-label","font-family"],defs:[],marker:["id","markerWidth","markerHeight","refX","refY","orient","markerUnits"],clipPath:["id"],g:["data-md-element","clip-path","transform"],a:["href"],title:[],desc:[],rect:["x","y","width","height","rx",...et],path:["d",...et,"stroke-linejoin","marker-start","marker-end","visibility","pointer-events"],polygon:["points",...et],line:["x1","y1","x2","y2","fill","stroke","stroke-opacity","stroke-width","stroke-dasharray"],circle:["cx","cy","r","fill","stroke","stroke-width"],text:["x","y","font-size","font-weight","font-style","text-decoration","fill","fill-opacity","textLength","lengthAdjust"]},dn=new Map(Object.entries(mo).map(([e,t])=>[e,new Set(t)])),po=new Set(["title","desc","text"]),nn="svg",G="g",R="a",cn="title",ee="data-md-element",ln="path",mn=["visibility","pointer-events"],pn=new Set(["rect","text","polygon","line"]),fo={"clip-path":"clipPath","marker-start":"marker","marker-end":"marker"},fn=/&(amp|lt|gt|quot|apos);/g,uo={amp:"&",lt:"<",gt:">",quot:'"',apos:"'"},ho=/[<>"']/,go=/[\u0000-\u001F]/,yo=/^<([^\s/>]+)/,wo=/^<\/([^\s/>]+)>/,tt=/ ([^\s=/>"'<]+)=/y,nt=/"([^"]*)"/y,ko=/^on/i,Eo=/^ */,bo=64,rn=/[\p{Cc}\p{Cf}\u2028\u2029]/gu,A=String.raw`-?\d+(?:\.\d+)?(?:e\+\d+)?`,To=new RegExp(`^${A}$`),xo=/^(?:[1-9]\d{0,2}|1000)$/,vo=new RegExp(`^0 0 ${A} ${A}$`),So=new RegExp(`^${A}(?:[ ,]${A})*$`),Co=new RegExp(`^(?:${A},${A}(?: ${A},${A})*)?$`),Io=/^[MmZzLlHhVvCcSsQqTtAa0-9eE.,+\- ]*$/,un=/^url\(#([A-Za-z0-9_-]+)\)$/,hn=/^#[0-9a-f]{6}$/,on=Array.from(new Set([j,...Te.flatMap(({shapes:e})=>e.flatMap(t=>["fill"in t?t.fill:j,t.stroke])).filter(e=>!hn.test(e))])),gn=Te.flatMap(({id:e})=>[Ze(e,!1),Ze(e,!0)]),$o=new RegExp(`^${Ee}-[1-9]\\d*$`),Ro=new RegExp(`-${Ee}-[1-9]\\d*$`),st=`${"md"}-`,it=e=>`U+${e.codePointAt(0).toString(16).toUpperCase().padStart(4,"0")}`,k=e=>{let t=[];for(let n of e){if(t.length===bo)return`${t.join("").replace(rn,it)}\u2026`;t.push(n)}return e.replace(rn,it)},yn=e=>{if(e.includes("&#"))return"a numeric character reference is not allowed";let t=e.replace(fn,"");if(t.includes("&"))return"& does not start one of the five XML entities";let n=ho.exec(t);return n===null?void 0:`an unescaped ${n[0]} is not allowed`},wn=e=>e.replace(fn,(t,n)=>uo[n]),U=(e,t)=>n=>e.test(n)?void 0:`"${k(n)}" is not ${t}`,v=(e,t)=>n=>e.includes(wn(n))?void 0:`"${k(n)}" is not ${t}`,sn=()=>{},w=U(To,"a number as the serializer writes one"),an=e=>hn.test(e)||on.includes(e)?void 0:`"${k(e)}" is not a paint the serializer writes (#rrggbb, ${on.join(", ")})`,rt=U(un,"a reference to a definition in the file, as url(#\u2026)"),Ao=e=>$o.test(e)?"clipPath":gn.includes(e)?"marker":void 0,_o=(e,t)=>{if(!e.startsWith(st))return;let n=e.slice(st.length);if(t!==void 0)return n.startsWith(`${t}-`)?Ao(n.slice(t.length+1)):void 0;if(qe(n))return(Ro.exec(n)?.index??0)>0?"clipPath":gn.some(r=>n.length>r.length+1&&n.endsWith(`-${r}`))?"marker":void 0},Lo=(e,t,{idPrefix:n})=>{if(_o(e,n)===t)return;let r=n??"<prefix>",o=t==="marker"?"<marker>[-start]":`${Ee}-<n>`;return`"${k(e)}" is not an id the serializer gives a <${t}> (${st}${r}-${o})`},Po=e=>{let t=wn(e);return t.startsWith("#")&&be(t)?void 0:`"${k(e)}" does not point at a section of the page (#\u2026)`},Mo={xmlns:v([Q],"the SVG namespace"),viewBox:U(vo,"a viewBox the serializer writes (0 0 width height)"),width:w,height:w,role:v([Ve],`the role the serializer writes (${Ve})`),"aria-label":sn,"font-family":v([Qt],"the serializer's font stack"),id:Lo,markerWidth:w,markerHeight:w,refX:w,refY:w,orient:v([Je],`the marker orientation the serializer writes (${Je})`),markerUnits:v([Qe],`the marker units the serializer writes (${Qe})`),"data-md-element":sn,"clip-path":rt,transform:e=>tn(e)?void 0:`"${k(e)}" is not a transform of numbers the serializer writes`,href:Po,x:w,y:w,rx:w,fill:an,"fill-opacity":w,stroke:an,"stroke-opacity":w,"stroke-width":w,"stroke-dasharray":U(So,"a dash pattern of numbers"),d:U(Io,"path data (commands and numbers only)"),"stroke-linejoin":v(["round"],"the line join the serializer writes (round)"),"marker-start":rt,"marker-end":rt,points:U(Co,"polygon points as the serializer writes them (x,y x,y \u2026)"),x1:w,y1:w,x2:w,y2:w,cx:w,cy:w,r:w,"font-size":w,"font-weight":U(xo,"a numeric font weight (1 to 1000)"),"font-style":v([Ye],`the font style the serializer writes (${Ye})`),"text-decoration":v([Xe],`the decoration the serializer writes (${Xe})`),textLength:w,lengthAdjust:v([Ke],`the length adjustment the serializer writes (${Ke})`),visibility:v([we],`the visibility of a hit area (${we})`),"pointer-events":v([ke],`the pointer events of a hit area (${ke})`)},No=(e,t)=>{if(ko.test(t))return`the event-handler attribute ${k(t)} is not allowed`;if(t.toLowerCase()==="style")return"a style attribute is not allowed";if(t.toLowerCase()==="class")return"a class attribute is not allowed";if(t.includes(":"))return`the namespaced attribute ${k(t)} is not allowed`;if(!dn.get(e).has(t))return`<${e}> does not take the attribute ${k(t)}`},Do=(e,t,n,r)=>{let o=yn(n)??Mo[t](n,e,r);return o===void 0?void 0:`${t}: ${o}`},Oo=(e,t,n,r)=>{let o=new Map,s=n;for(;t[s]!==">"&&!t.startsWith("/>",s);){tt.lastIndex=s;let i=tt.exec(t)?.[1];if(i===void 0)return{problem:'a tag the serializer would not write: expected ` name="value"`, `>` or `/>`'};nt.lastIndex=tt.lastIndex;let d=nt.exec(t)?.[1];if(d===void 0)return{problem:`the value of ${k(i)} is not double-quoted`};let c=No(e,i)??Do(e,i,d,r);if(c!==void 0)return{problem:c};if(o.has(i))return{problem:`<${e}> repeats the attribute ${i}`};o.set(i,d),s=nt.lastIndex}return{attributes:o,end:s}},ot=e=>e.startsWith("<")?"more than one element on one line":"text outside <title> and <text>",Ho=(e,t)=>{if(e.includes("<!"))return{problem:"a markup declaration (<!\u2026>: a DOCTYPE, an ENTITY, a comment or CDATA) is not allowed"};if(e.includes("<?"))return{problem:"a processing instruction (<?\u2026?>) is not allowed"};if(e.startsWith("</")){let f=wo.exec(e);if(f===null)return{problem:"a closing tag the serializer would not write"};let p=e.slice(f[0].length);return p!==""?{problem:ot(p)}:{line:{kind:"close",name:f[1]}}}let n=yo.exec(e)?.[1];if(n===void 0)return{problem:"the line does not start with a tag"};if(!dn.has(n))return{problem:`<${k(n)}> is not an element the diagram serializer writes`};let r=Oo(n,e,n.length+1,t);if("problem"in r)return r;let{attributes:o,end:s}=r,i=e.startsWith("/>",s),d=e.slice(s+(i?2:1));if(!po.has(n))return d!==""?{problem:ot(d)}:{line:{kind:i?"empty":"open",name:n,attributes:o}};if(i||d==="")return{problem:`<${n}> has to hold its text on one line, as <${n}>\u2026</${n}>`};let c=`</${n}>`,l=d.indexOf(c);if(l===-1)return{problem:`<${n}> is not closed on its line`};if(l+c.length!==d.length)return{problem:ot(d.slice(l+c.length))};let m=yn(d.slice(0,l));return m===void 0?{line:{kind:"text",name:n,attributes:o}}:{problem:`text: ${m}`}},Fo=(e,t)=>{if(e.name===nn!=(t===0))return t===0?`the file opens with <${e.name}>, not the serializer's <svg xmlns="${Q}"> root`:"<svg> is only the root, never nested";if(e.name===nn&&(e.kind!=="open"||e.attributes.get("xmlns")!==Q))return`the root is not the serializer's <svg xmlns="${Q}">`;if(e.name===R&&!e.attributes.has("href"))return"<a> without an href"},Uo=(e,t,n)=>{if(t.name===R&&t.children===0&&e.name!==cn)return"<a> has to open with the <title> of the group it links";if(e.name===R)return e.kind!=="open"?"<a> is only written open, around the title and body of the group it links":t.name!==G||!t.stamped||t.children!==0?`<a> is only written as the first child of a <g ${ee}>`:n>0?"an <a> inside another <a>":void 0},kn=e=>mn.some(t=>e.attributes.has(t)),Wo=(e,t)=>e.size===t.size&&Array.from(t).every(([n,r])=>e.get(n)===r),En=e=>e.findLast(t=>t.stamped),bn="a line's group draws no box: it holds no <rect>, <text>, <polygon> or <line>",Tn="a line's group holds no stamped group of its own",xe="desc",zo=e=>e!==void 0&&(e.hitArea||e.described),Bo={d:()=>!0,fill:e=>e===j,stroke:e=>e===g,"stroke-opacity":e=>e==="0","stroke-width":e=>e===je(1),"stroke-dasharray":e=>e==="6 4","marker-start":()=>!0,"marker-end":()=>!0},xn=e=>e.name===ln&&e.attributes.get("fill")===j&&e.attributes.get("stroke")===g&&Array.from(e.attributes).every(([t,n])=>Bo[t]?.(n)??!1),vn=e=>{let t=e.at(-1),n=t?.name===R?e.at(-2):t;return n?.name===G&&n.stamped?n:void 0},jo=(e,t)=>{if(!kn(e))return;let n=t.at(-1),r=n.name===R?t.at(-2):n;if(r?.name!==G||!r.stamped)return`a hit area (${mn.join(", ")}) is only written in a <g ${ee}>`;if(r.hitArea)return"a stamped group holds one hit area";if(r===n&&r.linked)return"in a linked group, the hit area is inside the link";if(r.drawsBox)return bn;if(r.nestsStamp)return Tn;let o=n.previous;if(o===void 0||o!==n.firstPath||!xn(o))return"a hit area follows the line's route, the first path of its group";let s=new Map([["d",o.attributes.get("d")??""],["fill",j],["stroke",g],["stroke-width",je(10)],["visibility",we],["pointer-events",ke]]);return Wo(e.attributes,s)?void 0:`a hit area is the route before it as <path ${Array.from(s.keys()).join(" ")}>, with the serializer's values`},Go=(e,t)=>{let n=En(t);if(zo(n))return pn.has(e.name)?bn:e.name===G&&e.attributes.has(ee)?Tn:void 0},Vo=(e,t)=>{if(e.name!==xe)return;let n=t.at(-1);return vn(t)===void 0||n.children!==1||n.previous?.name!==cn?`a <${xe}> is only written right after the <title> that opens a stamped group`:void 0},at=(e,t={})=>{if(t.idPrefix!==void 0&&!qe(t.idPrefix))return`the id prefix "${k(t.idPrefix)}" is not one the serializer accepts`;let n=e.split(`
|
|
15
|
+
`);if(n.at(-1)===""&&n.pop(),n.length===0)return"the file is empty";let r=[],o=new Map,s=[],i=0,d=!1;for(let[m,f]of n.entries()){let p=m+1,y=ie=>`line ${p}: ${ie}`;if(f==="")return y("a blank line");let E=go.exec(f);if(E!==null)return y(`the control character ${it(E[0])} is not allowed`);if(Z(f)!==f)return y("a code point XML cannot carry is not allowed");if(d)return y("content after the root element");let L=f.replace(Eo,""),H=Ho(L,t);if("problem"in H)return y(H.problem);let{line:h}=H,b=h.kind==="close"?r.pop():void 0;if(h.kind==="close"&&b?.name!==h.name)return y(b===void 0?`</${k(h.name)}> closes nothing`:`</${k(h.name)}> closes <${b.name}>, opened on line ${b.lineNumber}`);let K=f.length-L.length,re=r.length*lo.length;if(K!==re)return y(`indented ${K} spaces where the nesting calls for ${re}`);if(h.kind==="close"){if(b.name===R){if(b.children===0)return y("<a> has to open with the <title> of the group it links");i-=1}if(b.described&&!b.drawsRoute)return y(`a <${xe}> belongs to a line's route group, and this group draws no route`);d=r.length===0;continue}let T=r.at(-1),bt=Fo(h,r.length)??(T&&(Uo(h,T,i)??jo(h,r)??Vo(h,r)??Go(h,r)));if(bt!==void 0)return y(bt);let oe=h.attributes.get("id");if(oe!==void 0){if(o.has(oe))return y(`the id ${k(oe)} is declared twice`);o.set(oe,h.name)}for(let[ie,ur]of h.attributes){let Tt=fo[ie];Tt!==void 0&&s.push({attribute:ie,id:un.exec(ur)[1],kind:Tt,lineNumber:p})}T!==void 0&&(T.children+=1,T.previous=h,T.firstPath??=h.name===ln?h:void 0,T.linked||=T.stamped&&h.name===R),kn(h)&&((T.name===R?r.at(-2):T).hitArea=!0);let _e=vn(r);_e!==void 0&&(_e.described||=h.name===xe,_e.drawsRoute||=T.firstPath===h&&xn(h));let se=En(r);se!==void 0&&pn.has(h.name)&&(se.drawsBox=!0),se!==void 0&&h.name===G&&h.attributes.has(ee)&&(se.nestsStamp=!0),h.kind==="open"&&(r.push({name:h.name,lineNumber:p,stamped:h.name===G&&h.attributes.has(ee),children:0,linked:!1,hitArea:!1,drawsBox:!1,described:!1,drawsRoute:!1,nestsStamp:!1}),i+=h.name===R?1:0)}let c=r.at(-1);if(c!==void 0)return`<${c.name}> opened on line ${c.lineNumber} is never closed`;let l=s.find(({id:m,kind:f})=>o.get(m)!==f);return l===void 0?void 0:`line ${l.lineNumber}: ${l.attribute}: url(#${k(l.id)}) names no <${l.kind}> the file declares`};var dt=" ",Ko=/^( *)<g data-md-element="([^"]*)">$/,Yo=/^<title>[^<]*<\/title>$/,Sn=".svg",Cn=".png",In=[137,80,78,71,13,10,26,10],Xo=e=>{let t=new Map,n=(r,o)=>{o&&!t.has(r)&&t.set(r,`#${o}`)};for(let r of e.fragments??[]){n(r.elementId,r.realization?.anchor);for(let o of r.anchors??[])n(o.elementId,o.anchor)}return t},Jo=e=>{let t=new Map;for(let[n,r]of e){let o=Ge(n);!t.has(o)&&be(r)&&t.set(o,r)}return t},qo=(e,t,n)=>{let r=n+dt,o=`${n}</g>`,s=`${r}<g data-md-element="`;for(let i=t+1;i<e.length;i+=1){let d=e[i];if(d===o||d.startsWith(s))return i;if(!d.startsWith(r))return}},Zo=(e,t)=>{if(t.size===0)return e;let n=e.split(`
|
|
16
|
+
`),r=[],o=0;for(;o<n.length;){let s=n[o];r.push(s),o+=1;let i=Ko.exec(s),d=i?t.get(i[2]):void 0;if(!i||d===void 0)continue;let c=i[1],l=c+dt,m=n[o];if(m===void 0||!m.startsWith(l)||!Yo.test(m.slice(l.length)))continue;let f=qo(n,o-1,c);if(f!==void 0){for(r.push(`${l}<a href="${Ge(d)}">`);o<f;o+=1)r.push(dt+n[o]);r.push(`${l}</a>`)}}return r.join(`
|
|
17
|
+
`)};var C=class extends Error{path;problem;constructor(t,n){super(`Refused the diagram file ${t}: ${n}`),this.name="RefusedMediaError",this.path=t,this.problem=n}},Qo=e=>e.length>=In.length&&In.every((t,n)=>e[n]===t),es=(e,t)=>{let n=e!==void 0&&Object.hasOwn(e,t)?e[t]:void 0;return typeof n=="string"?{idPrefix:en(n)}:{}},$n=({manifest:e,mediaIndex:t})=>{let n=Jo(Xo(e)),r=new TextDecoder("utf-8",{fatal:!0,ignoreBOM:!0}),o=new TextEncoder,s=(i,d)=>{let c;try{c=r.decode(d)}catch{throw new C(i,"the bytes are not UTF-8 text")}let l=es(t,i),m=at(c,l);if(m!==void 0)throw new C(i,m);let f=Zo(c,n);if(f===c)return d;let p=at(f,l);if(p!==void 0)throw new C(i,`once linked, ${p}`);return o.encode(f)};return(i,d)=>{let c=i.toLowerCase();if(c.endsWith(Sn))return s(i,d);if(!c.endsWith(Cn))throw new C(i,`a diagram file is an ${Sn} or a ${Cn}, and this is neither`);if(!Qo(d))throw new C(i,"the bytes are not a PNG image");return d}};import{existsSync as ct,lstatSync as Rn,realpathSync as ts}from"node:fs";import{homedir as ns}from"node:os";import{basename as rs,dirname as os,isAbsolute as ve,join as An,relative as lt,resolve as V}from"node:path";var Ce=(e,t,n)=>{if(ve(n))throw new a(`Refusing an absolute path from the bundle manifest: ${n}`);let r=V(e,t),o=V(e,n),s=lt(r,o);if(s===""||s.startsWith("..")||ve(s))throw new a(`Refusing a bundle path that escapes the content root: ${n}`);return o},ss=e=>{try{return Rn(e)}catch{return}},Se=e=>{let t=[],n=e;for(;!ct(n);){let o=os(n);if(o===n)return e;t.unshift(rs(n)),n=o}let r=ts(n);return t.length>0?An(r,...t):r},mt=e=>{if(ss(e)?.isSymbolicLink())throw new a(`Refusing to write through an existing symlink at a managed path: ${e}`)},Ie=(e,t)=>{let n=Se(e),r=Se(t),o=lt(n,r);if(o!==""&&(o.startsWith("..")||ve(o)))throw new a(`Refusing a path that escapes the content root through a symlink: ${t}`)},pt=(e,t)=>{let n=V(e,t);if(n===V(e))throw new a(`The content root resolves to the repository root (${n}); refusing to sync it.`);if(n===V(ns()))throw new a("The content root resolves to your home directory; refusing to sync it.");if(ct(n)){if(Rn(n).isSymbolicLink())throw new a(`The content root ${n} is a symlink; refusing to sync it.`);if(ct(An(n,".git")))throw new a(`The content root ${n} contains a .git directory (it looks like a repo root); refusing.`)}let r=Se(V(e)),o=lt(r,Se(n));if(o!==""&&(o.startsWith("..")||ve(o)))throw new a(`The content root ${n} resolves outside the repository via a symlinked ancestor; refusing.`);return n};var is={respec:"data/model"},_n=e=>{let t=is[e];if(!t)throw new a(`The CLI does not know the content root for stack '${e}'.`);return t};var as=1500,ds=6e5,cs=e=>new Promise(t=>setTimeout(t,e)),ls=e=>e==="COMPLETED"||e==="COMPLETED_WITH_ERRORS"||e==="FAILED",Ln=async(e,t,n)=>{let r=await e.postForJson(`/models/${t}/content-bundle/diagrams`,{template:n.template,version:n.version});if(!r.commit)throw new a("The diagram render did not return a resolved commit; cannot pin the pull.");return r},Pn=async(e,t,n,r,o,s={})=>{let i=s.sleep??cs,d=s.now??Date.now,c=d()+ds;for(r.update(0,o);;){let l=await e.getJson(`/models/${t}/export-jobs/${n}`);if(l.progress&&r.update(l.progress.completed,l.progress.total,l.progress.currentItem),ls(l.status))return r.done(),l;if(d()>=c)throw new a("Timed out waiting for the diagram render to finish.");await i(as)}},Mn=(e,t,n,r)=>e.getBytes(`/models/${t}/content-bundle/media`,{path:n,template:r.template,commit:r.commit});var Nn=e=>{let t=new Map;for(let n of e.fragments??[])n.realization?.path&&t.set(n.elementId,n.realization.path);return t},Dn=(e,t)=>{let n=Nn(e),r=[];for(let[o,s]of Nn(t)){let i=n.get(o);i&&i!==s&&r.push({elementId:o,oldPath:i,newPath:s})}return r};var ft=e=>{let t=e.payloads??{},n=[];for(let r of e.manifest.fragments??[]){let o=r.realization?.path;if(!o)continue;let s=t[r.address];s!==void 0&&n.push({path:o,content:s})}return n},On="image/svg+xml, image/png",ut=e=>{let t=[];for(let n of e.fragments??[])for(let r of n.realization?.media??[])t.push(r);return[...new Set(t)]},ht=(e,t={})=>{let n=new Set,r=new Set;for(let o of e)Object.hasOwn(t,o)?n.add(t[o]):r.add(o);return n.size+r.size};var Hn=e=>`${JSON.stringify(e,null,2)}
|
|
18
|
+
`;import{existsSync as ms,mkdirSync as ps,readFileSync as fs,writeFileSync as us}from"node:fs";import{join as Fn}from"node:path";var Un=(e,t)=>Fn(e,fe,`${t}.manifest.json`),Wn=(e,t)=>{let n=Un(e,t);if(!ms(n))return;let r;try{r=JSON.parse(fs(n,"utf8"))}catch{return}return hs(r)?r:void 0},hs=e=>{if(e===null||typeof e!="object"||Array.isArray(e))return!1;let{fragments:t}=e;return t===void 0?!0:Array.isArray(t)&&t.every(n=>n!==null&&typeof n=="object")},zn=(e,t,n)=>{ps(Fn(e,fe),{recursive:!0}),us(Un(e,t),Hn(n))},Bn=e=>{let t=[];for(let n of e.fragments??[]){n.realization?.path&&t.push(n.realization.path);for(let r of n.realization?.media??[])t.push(r)}return t};import{existsSync as jn,mkdirSync as Gn,readdirSync as gs,rmSync as ys,writeFileSync as Vn}from"node:fs";import{dirname as Kn,join as te,relative as ws,sep as ks}from"node:path";var Yn=(e,t)=>{let n=te(e,t);if(!jn(n))return[];let r=[],o=s=>{for(let i of gs(s,{withFileTypes:!0})){let d=te(s,i.name);i.isDirectory()?o(d):i.isFile()&&r.push(ws(e,d).split(ks).join("/"))}};return o(n),r},Xn=(e,t,n)=>{let r=te(e,t);for(let o of n){let s=Ce(e,t,o.path);mt(s),Ie(r,s),Gn(Kn(s),{recursive:!0}),Vn(s,o.content)}},Es=(e,t,n,r)=>{let o=te(e,t),s=Ce(e,t,n);mt(s),Ie(o,s),Gn(Kn(s),{recursive:!0}),Vn(s,r)},Jn=(e,t,n,r,o)=>Es(e,t,n,o(n,r)),gt=(e,t,n)=>{let r=Ce(e,t,n);return Ie(te(e,t),r),jn(r)?(ys(r),!0):!1},qn=(e,t,n)=>{for(let r of n)gt(e,t,r)};var bs=100,Qn=e=>{if(e.newTextPaths.length===0&&!e.allowEmpty)throw new a("The content fetch returned no fragments (usually the wrong model or format, or a server error). Nothing was written or deleted. Re-run with --allow-empty only if the model is genuinely empty.");let t=Zn([...e.newTextPaths,...e.newMediaPaths]).sort(),n=new Set(t),r=new Set(e.existingFiles),o=t.filter(m=>r.has(m)),s=e.priorManifestPaths!==void 0?"manifest-tracked":"full-sync",i=e.priorManifestPaths??e.existingFiles,d=Zn(i).filter(m=>!n.has(m)).sort(),c=d.length>0&&!e.adopted,l=d.length>0&&(d.length>t.length||d.length>bs);return{writes:t,overwrites:o,orphanCandidates:d,mode:s,adoptionNeeded:c,outsized:l}},yt=(e,t)=>{if(e.orphanCandidates.length===0)return{deletes:[],warnings:[]};if(e.adoptionNeeded&&!t.adopt)return{deletes:[],warnings:[`${e.orphanCandidates.length} file(s) under the content root are not tracked by the CLI and were left in place. Pass --adopt to let the CLI manage (and prune) this directory.`]};if(e.outsized&&!t.yes)throw new a(`This pull would remove ${e.orphanCandidates.length} file(s), more than the ${e.writes.length} it writes. That often signals a wrong model or format. Nothing was changed. Re-run with --yes if intended.`);return{deletes:e.orphanCandidates,warnings:[]}},Zn=e=>[...new Set(e)];var er=(e,t)=>{let n=J(t);switch(n.mode){case"error":throw new a(n.message);case"secret":case"federated":return ye(e,n);case"interactive":return new ge(e)}};var Ts=6e4,tr={unauthorized:"Run 'modeldesk auth login' and try again.",forbidden:"Your account may lack access to this model."},xs={unauthorized:pe.unauthorized,forbidden:pe.forbidden,notFound:pe.notFound},O=class extends a{status;constructor(t,n){super(t),this.name="ApiError",this.status=n}},$e=(e,t)=>new wt(e.apiBase,er(e,t),D(t)?xs:tr),wt=class{constructor(t,n,r=tr){this.apiBase=t;this.tokens=n;this.hints=r}apiBase;tokens;hints;async getJson(t,n){return await(await this.request(t,{method:"GET"},n)).json()}async postForJson(t,n){return await(await this.request(t,{method:"POST"},n)).json()}async getBytes(t,n){let r=await this.request(t,{method:"GET",headers:{Accept:On}},n);return new Uint8Array(await r.arrayBuffer())}async request(t,n,r){let o=await this.tokens.getAccessToken(),s=vs(this.apiBase,t,r),i;try{i=await fetch(s,{...n,headers:{Authorization:`Bearer ${o}`,Accept:"application/json",...n.headers??{}},signal:AbortSignal.timeout(Ts)})}catch(d){throw new a(`Could not reach ${this.apiBase} (${d.message}). Check the host and that the ModelDesk server is reachable.`)}if(!i.ok)throw await Ss(i,t,this.hints);return i}},vs=(e,t,n)=>{let r=new URL(t,e);if(n)for(let[o,s]of Object.entries(n))s!==void 0&&r.searchParams.set(o,s);return r.toString()},Ss=async(e,t,n)=>{let r=`${e.status} ${e.statusText}`.trim();try{if((e.headers.get("content-type")??"").includes("json")){let s=await e.json(),i=s.detail??s.title;i&&(r=`${e.status} ${i}`)}}catch{}return e.status===401?new O(`Authentication failed (${r}). ${n.unauthorized}`,e.status):e.status===403?new O(`Not authorized (${r}). ${n.forbidden}`,e.status):e.status===404&&n.notFound?new O(`Not found (${r}). ${n.notFound}`,e.status):new O(`Request to ${t} failed: ${r}`,e.status)};var Cs=(e,t)=>{let n=t>0?Math.min(1,e/t):0,r=Math.round(n*24);return`[${"#".repeat(r)}${"-".repeat(24-r)}]`},nr=(e,t=n=>{process.stderr.write(n)})=>{let n=-1;return{update(r,o,s){if(e){let i=s?` ${s}`:"";t(`\r${Cs(r,o)} ${r}/${o}${i}\x1B[K`)}else if(r!==n){let i=s?` (${s})`:"";t(`Rendering diagrams: ${r}/${o}${i}
|
|
19
|
+
`),n=r}},done(){e&&t(`
|
|
20
|
+
`)}}};import{createInterface as Is}from"node:readline";var Re=()=>!!(process.stdin.isTTY&&process.stdout.isTTY),kt=async e=>{if(!Re())return!1;let t=Is({input:process.stdin,output:process.stderr});try{let n=await new Promise(r=>t.question(`${e} [y/N] `,r));return/^y(es)?$/i.test(n.trim())}finally{t.close()}};var _=e=>{process.stdout.write(e)},I=e=>{process.stderr.write(e)},rr=e=>{for(let t of e)I(` ${t}
|
|
21
|
+
`)},sr=async e=>{let{values:t}=z(e.argv,{host:{type:"string"},model:{type:"string"},stack:{type:"string"},template:{type:"string"},version:{type:"string"},"no-diagrams":{type:"boolean"},"dry-run":{type:"boolean"},"allow-empty":{type:"boolean"},adopt:{type:"boolean"},yes:{type:"boolean"}}),n=he(e,{host:t.host,model:t.model,stack:t.stack,template:t.template,version:t.version}),r={noDiagrams:!!t["no-diagrams"],dryRun:!!t["dry-run"],allowEmpty:!!t["allow-empty"],adopt:!!t.adopt,yes:!!t.yes},o=$e(n.instance,e.env),s=We(n.stack,"stack",u.stack,"--stack"),i=We(n.template,"template",u.template,"--template"),c=!r.noDiagrams&&!r.dryRun?await Ln(o,n.model,{template:i,version:n.version}):void 0,l=c?.commit,m=await o.getJson(`/models/${n.model}/content-bundle`,{template:i,commit:l,version:l?void 0:n.version});if(m.manifest.stack!==s)throw new a(`Requested stack '${s}', but template '${i}' renders under stack '${m.manifest.stack}'. Run 'modeldesk bundle templates' to see valid stack/template pairs.`);let f=_n(s);pt(n.loaded.repoRoot,f);let p=ft(m),y=ut(m.manifest),E=ht(y,m.mediaIndex),L=Wn(n.loaded.repoRoot,s),H=Qn({newTextPaths:p.map(re=>re.path),newMediaPaths:y,priorManifestPaths:L?Bn(L):void 0,existingFiles:Yn(n.loaded.repoRoot,f),adopted:L!==void 0||ue(n.loaded),allowEmpty:r.allowEmpty}),h=L?Dn(L,m.manifest):[],b={stack:s,version:m.manifest.stackVersion};if(r.dryRun)return or(n,i,b,l,H,void 0,h,E,!0),0;let K=await $s(H,r);return or(n,i,b,l,H,K,h,E,!1),pt(n.loaded.repoRoot,f),Xn(n.loaded.repoRoot,f,p),qn(n.loaded.repoRoot,f,K.deletes),zn(n.loaded.repoRoot,s,m.manifest),y.length===0||!c?0:Rs(o,n,i,l,c,m,{media:y,diagramCount:E},f)},$s=async(e,t)=>{let{adopt:n,yes:r}=t;if(e.orphanCandidates.length===0)return yt(e,{adopt:n,yes:r});let o=Re();return e.adoptionNeeded&&!n&&(I(`The content root has ${e.orphanCandidates.length} file(s) the CLI does not track:
|
|
22
|
+
`),rr(e.orphanCandidates),o&&(n=await kt("Adopt this directory and allow the CLI to remove these files?"))),e.outsized&&!r&&(n||!e.adoptionNeeded)&&(I(`This pull would remove ${e.orphanCandidates.length} file(s), more than the ${e.writes.length} it writes:
|
|
23
|
+
`),rr(e.orphanCandidates),o&&(r=await kt("Proceed with removing them?"))),yt(e,{adopt:n,yes:r})},Rs=async(e,t,n,r,o,s,{media:i,diagramCount:d},c)=>{let l=nr(Re()),m=await Pn(e,t.model,o.id,l,d);if(m.status==="FAILED")return I(`warning: the diagram render failed. Text fragments are written; media was not fetched. Re-run to retry.
|
|
24
|
+
`),1;let f=$n(s),p=0;for(let y of i)try{let E=await Mn(e,t.model,y,{template:n,commit:r});Jn(t.loaded.repoRoot,c,y,E,f)}catch(E){p+=1,E instanceof C?I(`warning: refused the diagram file ${y} and did not write it: ${E.problem}
|
|
25
|
+
`):I(`warning: could not fetch media ${y}: ${ir(E)}
|
|
26
|
+
`),_s(E)&&Ls(t.loaded.repoRoot,c,y)}return m.status==="COMPLETED_WITH_ERRORS"||p>0?(I(`warning: ${p} media file(s) could not be written; the pull is re-runnable.
|
|
27
|
+
`),1):0},ir=e=>e instanceof Error?e.message:String(e),As=404,_s=e=>e instanceof C||e instanceof O&&e.status===As,Ls=(e,t,n)=>{try{gt(e,t,n)&&I(`warning: removed the copy of ${n} an earlier pull wrote.
|
|
28
|
+
`)}catch(r){I(`warning: could not remove the copy of ${n} an earlier pull wrote: ${ir(r)}
|
|
29
|
+
`)}},or=(e,t,n,r,o,s,i,d,c)=>{let l=r?`commit ${r}`:e.version?`version ${e.version}`:"HEAD",m=`${n.stack} ${n.version}`;_(`${c?"Dry run: ":""}Pulling ${e.model} (stack ${m}, template ${t}) at ${l} into ${e.instance.host}.
|
|
30
|
+
`),_(` Write: ${o.writes.length} file(s)${o.overwrites.length?` (${o.overwrites.length} overwritten)`:""}
|
|
31
|
+
`),_(` Diagrams: ${d}
|
|
32
|
+
`);let f=c?o.orphanCandidates:s?.deletes??[];_(` Remove: ${f.length} file(s) [${o.mode}]
|
|
33
|
+
`);for(let p of f)_(` - ${p}
|
|
34
|
+
`);if(c)o.adoptionNeeded&&_(` note: removing these needs --adopt (the root is not CLI-managed yet).
|
|
35
|
+
`),o.outsized&&_(` note: this removal is larger than the write set and needs --yes.
|
|
36
|
+
`);else for(let p of s?.warnings??[])I(`warning: ${p}
|
|
37
|
+
`);for(let p of i)_(` moved: ${p.oldPath} -> ${p.newPath}
|
|
38
|
+
`);if(c)for(let p of o.writes)_(` write ${p}
|
|
39
|
+
`)};var Ae=e=>{let t=[];for(let n of e)n.forEach((r,o)=>{t[o]=Math.max(t[o]??0,r.length)});return t},ne=(e,{indent:t="",gap:n=2,widths:r}={})=>{let s=Ae(e).map((d,c)=>r?.[c]??d),i=" ".repeat(n);return e.map(d=>t+d.map((c,l)=>l===d.length-1?c:c.padEnd(s[l])).join(i))};var Ps=["STACK","TEMPLATE","NAME"],ar=async e=>{let{values:t}=z(e.argv,{host:{type:"string"},model:{type:"string"},stack:{type:"string"}}),n=he(e,{host:t.host,model:t.model}),r=$e(n.instance,e.env),o=await r.getJson(`/models/${n.model}`),s=(o.metamodelExtensions??[]).map(p=>p.id),i=s.length>0?{metamodelExtensions:s.join(",")}:void 0,{items:d}=await r.getJson(`/metamodels/${o.metamodel.id}/templates`,i),c=d??[];if(c.length===0)return process.stdout.write(`This model has no document templates.
|
|
40
|
+
`),0;let l=t.stack,m=l?c.filter(p=>p.stack===l):c;if(m.length===0)return process.stdout.write(`No document templates for stack '${l}'.
|
|
41
|
+
`),0;let f=m.map(p=>[p.stack??"",p.key??p.id,p.name]);for(let p of ne([Ps,...f]))process.stdout.write(`${p}
|
|
42
|
+
`);return 0};var Et={bundle:{pull:{summary:"Materialize a model's content bundle into a local folder",usage:"bundle pull --model <id> --stack <id> --template <slug> [--host <host>] [--version <name>] [--no-diagrams] [--dry-run]",handler:sr},templates:{summary:"List a model's document templates",usage:"bundle templates --model <id> [--stack <id>] [--host <host>]",handler:ar}},auth:{login:{summary:"Log in through the browser (OAuth authorization code + PKCE)",usage:"auth login [--host <host>]",handler:Yt},logout:{summary:"Remove the stored credential for the active host",usage:"auth logout [--host <host>]",handler:Xt},status:{summary:"Show the active host, logged-in identity, and token validity",usage:"auth status [--host <host>]",handler:Jt}}};var dr={name:"@modeldesk/cli",version:"0.4.0",description:"Command-line interface (CLI) for the ModelDesk platform",keywords:["modeldesk","cli","documentation","content-bundle","respec","mim","information-model"],homepage:"https://modeldesk.io",repository:{type:"git",url:"git+https://github.com/model-desk/modeldesk.git",directory:"packages/cli"},license:"MIT",type:"module",bin:{modeldesk:"dist/modeldesk.js"},files:["dist"],publishConfig:{access:"public"},engines:{node:">=22"},scripts:{build:"node esbuild.config.mjs",prepublishOnly:"npm run build",typecheck:"tsc --noEmit",lint:"eslint","lint:fix":"eslint --fix",test:"vitest run","test:watch":"vitest"},devDependencies:{"@eslint/js":"^10.0.1","@modeldesk/content-bundle":"*","@types/node":"^24.19.1",esbuild:"^0.28.2",eslint:"^10.12.0","eslint-config-prettier":"^10.1.5","eslint-plugin-prettier":"^5.5.6",globals:"^17.13.0",prettier:"^3.9.9",typescript:"^6.0.3","typescript-eslint":"^8.71.1",vitest:"^5.0.3"}};var cr=dr.version;var Ns={out:e=>process.stdout.write(e),err:e=>process.stderr.write(e)},mr=new Set(["--help","-h","help"]),fr=async(e,t=Ns)=>{try{return await Ds(e,t)}catch(n){return n instanceof a?(t.err(`error: ${n.message}
|
|
37
43
|
`),n.exitCode):(t.err(`error: ${n instanceof Error?n.message:String(n)}
|
|
38
|
-
`),1)}},
|
|
39
|
-
`),0;let
|
|
44
|
+
`),1)}},Ds=async(e,t)=>{let[n,...r]=e;if(n===void 0||mr.has(n))return t.out(Os()),0;if(n==="--version"||n==="-v")return t.out(`${cr}
|
|
45
|
+
`),0;let o=Et[n];if(!o)throw new a(`Unknown command '${n}'. Run 'modeldesk --help' to see available commands.`);let[s,...i]=r;if(s===void 0){let m=Object.keys(o).join(", ");throw new a(`'${n}' requires a subcommand (${m}). Run 'modeldesk ${n} --help'.`)}if(mr.has(s))return t.out(Hs(n,o)),0;let d=o[s];if(!d)throw new a(`Unknown '${n}' command '${s}'. Run 'modeldesk ${n} --help'.`);let c={argv:i,env:process.env,cwd:process.cwd()},l=await d.handler(c);return typeof l=="number"?l:0},pr=[["--help, -h","Show help"],["--version, -v","Show the CLI version"]],Os=()=>{let e=[];for(let[r,o]of Object.entries(Et))for(let[s,i]of Object.entries(o))e.push([`${r} ${s}`,i.summary]);let[t]=Ae([...e,...pr]);return`${["ModelDesk CLI","","Usage: modeldesk <command> <subcommand> [options]","","Commands:",...ne(e,{indent:" ",widths:[t]}),"","Global options:",...ne(pr,{indent:" ",widths:[t]}),""].join(`
|
|
40
46
|
`)}
|
|
41
|
-
`},
|
|
47
|
+
`},Hs=(e,t)=>{let[n=0]=Ae(Object.keys(t).map(o=>[o])),r=[`Usage: modeldesk ${e} <subcommand> [options]`,"","Subcommands:"];for(let[o,s]of Object.entries(t))r.push(` ${o.padEnd(n)}${" ".repeat(2)}${s.summary}`),r.push(` ${" ".repeat(n+2)}modeldesk ${s.usage}`);return r.push(""),`${r.join(`
|
|
42
48
|
`)}
|
|
43
|
-
`};var
|
|
49
|
+
`};var Fs=await fr(process.argv.slice(2));process.exitCode=Fs;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modeldesk/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Command-line interface (CLI) for the ModelDesk platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"modeldesk",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
|
-
"node": ">=
|
|
32
|
+
"node": ">=22"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "node esbuild.config.mjs",
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@eslint/js": "^10.0.1",
|
|
45
45
|
"@modeldesk/content-bundle": "*",
|
|
46
|
-
"@types/node": "^24.
|
|
47
|
-
"esbuild": "^0.28.
|
|
48
|
-
"eslint": "^10.
|
|
46
|
+
"@types/node": "^24.19.1",
|
|
47
|
+
"esbuild": "^0.28.2",
|
|
48
|
+
"eslint": "^10.12.0",
|
|
49
49
|
"eslint-config-prettier": "^10.1.5",
|
|
50
50
|
"eslint-plugin-prettier": "^5.5.6",
|
|
51
|
-
"globals": "^17.
|
|
52
|
-
"prettier": "^3.9.
|
|
51
|
+
"globals": "^17.13.0",
|
|
52
|
+
"prettier": "^3.9.9",
|
|
53
53
|
"typescript": "^6.0.3",
|
|
54
|
-
"typescript-eslint": "^8.
|
|
55
|
-
"vitest": "^
|
|
54
|
+
"typescript-eslint": "^8.71.1",
|
|
55
|
+
"vitest": "^5.0.3"
|
|
56
56
|
}
|
|
57
57
|
}
|