@kanonak-protocol/cli 4.13.0 → 4.15.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/dist/index.js +39 -39
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as Ic}from"commander";import{readFileSync as Ec}from"fs";import{fileURLToPath as Tc}from"url";import{dirname as Dc,join as Ac}from"path";import{KanonakParser as jc,PublisherIndex as Oc,CredentialStore as Nc,createAuthenticatedFetch as Lc}from"@kanonak-protocol/sdk";import{readFileSync as xo,statSync as _o}from"fs";import{resolve as Ko,dirname as Io}from"path";import{KanonakParser as Eo,KanonakObjectValidator as To,ValidationSeverity as
|
|
2
|
+
import{Command as Ic}from"commander";import{readFileSync as Ec}from"fs";import{fileURLToPath as Tc}from"url";import{dirname as Dc,join as Ac}from"path";import{KanonakParser as jc,PublisherIndex as Oc,CredentialStore as Nc,createAuthenticatedFetch as Lc}from"@kanonak-protocol/sdk";import{readFileSync as xo,statSync as _o}from"fs";import{resolve as Ko,dirname as Io}from"path";import{KanonakParser as Eo,KanonakObjectValidator as To,ValidationSeverity as je}from"@kanonak-protocol/sdk";import{readdirSync as So}from"fs";import{join as Po,dirname as Co,basename as kn}from"path";import{LocalFirstRepository as Yc,buildLocalFirstRepository as Ro}from"@kanonak-protocol/sdk";import{mkdirSync as ko,readFileSync as gn,writeFileSync as hn,existsSync as De,appendFileSync as yo}from"fs";import{join as wo,dirname as bo,isAbsolute as vo}from"path";import{getGlobalCachePath as $o}from"@kanonak-protocol/sdk";var I=class{constructor(e=$o()){this.cacheDir=e;this.isProjectLocal=!vo(e)}cacheDir;gitignoreChecked=!1;isProjectLocal;get(e,t,o){let r=this.getPath(e,t,o);return De(r)?gn(r,"utf-8"):null}put(e,t,o,r){let a=this.getPath(e,t,o);ko(bo(a),{recursive:!0}),hn(a,r,"utf-8"),this.isProjectLocal&&this.ensureGitignore()}has(e,t,o){return De(this.getPath(e,t,o))}ensureGitignore(){if(this.gitignoreChecked)return;this.gitignoreChecked=!0;let e=".gitignore",t=this.cacheDir;if(De(e)){if(gn(e,"utf-8").split(`
|
|
3
3
|
`).some(r=>r.trim()===t||r.trim()===t+"/"))return;yo(e,`
|
|
4
4
|
${t}/
|
|
5
|
-
`)}else
|
|
6
|
-
`)}getPath(e,t,o){return wo(this.cacheDir,e,`${t}@${o}.kan.yml`)}};function F(n){let e=n;for(;;){let t=Co(e);if(t===e)break;if(
|
|
7
|
-
${
|
|
8
|
-
${
|
|
9
|
-
${
|
|
10
|
-
${i.length} file(s) validated. ${c} error(s), ${l} warning(s).`),c>0&&process.exit(1)}import{KanonakParser as Do,PublisherIndex as Ao,CredentialStore as jo,createAuthenticatedFetch as Oo}from"@kanonak-protocol/sdk";import{loadLockFile as No,saveLockFile as Lo,computeIntegrity as Fo,assertPackageIdentity as Uo}from"@kanonak-protocol/sdk";function T(n){if(!n)return null;let e=n.indexOf("/");if(e===-1)return null;let t=n.substring(0,e),o=n.substring(e+1);if(!t||!o)return null;let r,a=null,s=null,i=o.indexOf("@");if(i!==-1){r=o.substring(0,i);let c=o.substring(i+1),l=c.indexOf("/");l===-1?a=c||null:(a=c.substring(0,l)||null,s=c.substring(l+1)||null)}else{let c=o.indexOf("/");c===-1?r=o:(r=o.substring(0,c),s=o.substring(c+1)||null)}return!r||s&&s.includes("/")||a&&a.includes("@")?null:{publisher:t,packageName:r,version:a,instanceName:s}}async function wn(n){let e=new I,t=new Do,o=new jo,r=Oo(o),a=new Ao({fetchFn:r}),s=No()??{version:"1",lastUpdated:new Date().toISOString(),packages:{}};if(!n){Object.keys(s.packages).length===0&&(console.error("No kanonak.lock file found or lock file is empty."),console.error("Usage: kanonak install {publisher}/{package}[@{version}]"),process.exit(1)),console.log(`Installing ${Object.keys(s.packages).length} package(s) from kanonak.lock...`);for(let[
|
|
11
|
-
Installed ${Object.keys(s.packages).length} package(s) from lock file.`);return}let i=T(n);i||(console.error(`Invalid package reference: "${n}"`),console.error("Expected format: {publisher}/{package}[@{version}]"),process.exit(1));let{publisher:c,packageName:l,version:p,instanceName:u}=i;u&&(console.error(`\`kanonak install\` works on whole packages \u2014 got an instance suffix "/${u}".`),console.error(`Try: kanonak install ${c}/${l}${p?"@"+p:""}`),console.error("(Use `kanonak <capability> add` if you want to install one named instance.)"),process.exit(1));let d=p??await a.getHighestVersion(c,l);d||(console.error(`Could not resolve version for "${c}/${l}".`),console.error("Check the spelling, or run a search to see what the publisher offers."),process.exit(1)),console.log(`Installing ${c}/${l}@${d}...`);let
|
|
12
|
-
Installed ${
|
|
5
|
+
`)}else hn(e,`${t}/
|
|
6
|
+
`)}getPath(e,t,o){return wo(this.cacheDir,e,`${t}@${o}.kan.yml`)}};function F(n){let e=n;for(;;){let t=Co(e);if(t===e)break;if(kn(e).includes(".")||kn(t).includes(".")){e=t;continue}break}return e}function Ae(n,e){for(let t of So(n,{withFileTypes:!0})){let o=Po(n,t.name);t.isDirectory()&&t.name!=="node_modules"&&t.name!==".kanonak"?Ae(o,e):t.name.endsWith(".kan.yml")&&e.push(o)}}async function S(n,e){let t=new I;return Ro(n,e,{httpCache:{getFromCache:(o,r,a)=>t.get(o,r,a),onFetch:(o,r,a,s)=>t.put(o,r,a,s)}})}async function yn(n){let e=Ko(n),t=_o(e),o=t.isDirectory()?F(e):F(Io(e)),r=new Eo,a=new To(r),s=await S(o,r),i=[];if(t.isDirectory()?Ae(e,i):i.push(e),i.length===0){console.log("No .kan.yml files found.");return}let c=0,l=0,p=0,u=[];for(let m of i){let k=xo(m,"utf-8"),h=r.parseWithErrors(k);if(h.errors&&h.errors.length>0){console.log(`
|
|
7
|
+
${m}:`);for(let y of h.errors)console.log(` ERROR (parse): ${y.message} [line ${y.line}:${y.column}]`),c++;continue}if(!h.document){console.log(`
|
|
8
|
+
${m}:`),console.log(" ERROR: Failed to parse document"),c++;continue}u.push({file:m,document:h.document})}let d=u.map(m=>m.document),f=new Map(u.map(m=>[m.document,m.file])),g=await a.validateDocumentsAsync(d,s);for(let{document:m,result:k}of g){let h=k.errors.filter(b=>b.severity===je.Error),y=k.warnings.filter(b=>b.severity===je.Warning),w=k.infos.filter(b=>b.severity===je.Info);if(h.length>0||y.length>0||w.length>0){let b=m.metadata.namespace_,v=b?`${b.publisher}/${b.package_}@${b.version}`:f.get(m)??"(unknown)";console.log(`
|
|
9
|
+
${v}:`);for(let $ of h)console.log(` ERROR: ${$.message}`),$.suggestion&&console.log(` -> ${$.suggestion}`);for(let $ of y)console.log(` WARN: ${$.message}`);for(let $ of w)console.log(` INFO: ${$.message}`)}c+=h.length,l+=y.length,p+=w.length}console.log(`
|
|
10
|
+
${i.length} file(s) validated. ${c} error(s), ${l} warning(s), ${p} info(s).`),c>0&&process.exit(1)}import{KanonakParser as Do,PublisherIndex as Ao,CredentialStore as jo,createAuthenticatedFetch as Oo}from"@kanonak-protocol/sdk";import{loadLockFile as No,saveLockFile as Lo,computeIntegrity as Fo,assertPackageIdentity as Uo}from"@kanonak-protocol/sdk";function T(n){if(!n)return null;let e=n.indexOf("/");if(e===-1)return null;let t=n.substring(0,e),o=n.substring(e+1);if(!t||!o)return null;let r,a=null,s=null,i=o.indexOf("@");if(i!==-1){r=o.substring(0,i);let c=o.substring(i+1),l=c.indexOf("/");l===-1?a=c||null:(a=c.substring(0,l)||null,s=c.substring(l+1)||null)}else{let c=o.indexOf("/");c===-1?r=o:(r=o.substring(0,c),s=o.substring(c+1)||null)}return!r||s&&s.includes("/")||a&&a.includes("@")?null:{publisher:t,packageName:r,version:a,instanceName:s}}async function wn(n){let e=new I,t=new Do,o=new jo,r=Oo(o),a=new Ao({fetchFn:r}),s=No()??{version:"1",lastUpdated:new Date().toISOString(),packages:{}};if(!n){Object.keys(s.packages).length===0&&(console.error("No kanonak.lock file found or lock file is empty."),console.error("Usage: kanonak install {publisher}/{package}[@{version}]"),process.exit(1)),console.log(`Installing ${Object.keys(s.packages).length} package(s) from kanonak.lock...`);for(let[g,m]of Object.entries(s.packages)){let k=g.indexOf("/"),h=g.substring(0,k),y=g.substring(k+1),w=e.get(h,y,m.version);if(w)console.log(` ${g}@${m.version} (cached)`);else{let b=m.resolved,v=await r(b,h);if(!v.ok)throw new Error(`Failed to fetch ${b} (${v.status} ${v.statusText})`);w=await v.text(),e.put(h,y,m.version,w),console.log(` ${g}@${m.version}`)}}console.log(`
|
|
11
|
+
Installed ${Object.keys(s.packages).length} package(s) from lock file.`);return}let i=T(n);i||(console.error(`Invalid package reference: "${n}"`),console.error("Expected format: {publisher}/{package}[@{version}]"),process.exit(1));let{publisher:c,packageName:l,version:p,instanceName:u}=i;u&&(console.error(`\`kanonak install\` works on whole packages \u2014 got an instance suffix "/${u}".`),console.error(`Try: kanonak install ${c}/${l}${p?"@"+p:""}`),console.error("(Use `kanonak <capability> add` if you want to install one named instance.)"),process.exit(1));let d=p??await a.getHighestVersion(c,l);d||(console.error(`Could not resolve version for "${c}/${l}".`),console.error("Check the spelling, or run a search to see what the publisher offers."),process.exit(1)),console.log(`Installing ${c}/${l}@${d}...`);let f=new Set;await bn(c,l,d,e,t,a,r,f,s),Lo(s),console.log(`
|
|
12
|
+
Installed ${f.size} package(s).`)}async function bn(n,e,t,o,r,a,s,i,c){let l=`${n}/${e}@${t}`;if(i.has(l))return;i.add(l);let p=`${n}/${e}`,u=o.get(n,e,t),d=await a.getPackageUrl(n,e,t),f,g=!1;if(u)f=u;else{let h=await s(d,n);if(!h.ok)throw new Error(`Failed to fetch ${d} (${h.status} ${h.statusText})`);f=await h.text(),g=!0}let m=r.parse(f);Uo(m,n,e,t),g&&o.put(n,e,t,f),console.log(` ${l}${g?"":" (cached)"}`);let k={};if(m.metadata.imports)for(let[h,y]of Object.entries(m.metadata.imports))for(let w of y){let b=await a.resolveVersion(h,w);b?(k[`${h}/${w.packageName}`]=b,await bn(h,w.packageName,b,o,r,a,s,i,c)):console.error(` WARNING: Could not resolve ${h}/${w.packageName} ${w.package_}`)}c.packages[p]={version:t,resolved:d,integrity:Fo(f),dependencies:k}}import{readFileSync as Mo}from"fs";import{resolve as Vo,dirname as Ho}from"path";import{KanonakParser as zo}from"@kanonak-protocol/sdk";async function vn(n){let e=Vo(n),t=Mo(e,"utf-8"),o=new zo,r=o.parse(t),a=r.metadata.namespace_,s=a?`${a.publisher}/${a.package_}@${a.version}`:n;if(console.log(s),!r.metadata.imports){console.log(" (no imports)");return}let i=F(Ho(e)),c=await S(i,o),l=new Set;for(let[p,u]of Object.entries(r.metadata.imports))for(let d of u)await $n(p,d,c,l," ")}async function $n(n,e,t,o,r){let a=`${n}/${e.packageName}`;if(o.has(a)){console.log(`${r}${a} (${e.package_}) [circular]`);return}o.add(a);let s;try{s=await t.getHighestCompatibleVersionAsync(n,e)}catch{console.log(`${r}${a} (${e.package_}) [fetch failed]`);return}if(!s){console.log(`${r}${a} (${e.package_}) [not found]`);return}let i=s.metadata.namespace_?.version?.toString()??"?";if(console.log(`${r}${n}/${e.packageName}@${i}`),s.metadata.imports)for(let[c,l]of Object.entries(s.metadata.imports))for(let p of l)await $n(c,p,t,o,r+" ")}import{CredentialStore as Yo,generateDPoPKeyPair as Xo,createDPoPProof as Cn,serverSupportsDPoP as Zo}from"@kanonak-protocol/sdk";import{normalizeHost as Bo}from"@kanonak-protocol/sdk";var Sn="key_thumbprint",D=class{cache=new Map;async discover(e){let t=Bo(e);if(this.cache.has(t))return this.cache.get(t);let o=`https://${t}/.well-known/oauth-authorization-server`,r=await this.tryEndpoint(o);if(!r){let a=`https://${t}/.well-known/openid-configuration`;r=await this.tryEndpoint(a)}return this.cache.set(t,r),r}async supportsOAuth(e){return await this.discover(e)!==null}static supportsPkceS256(e){return e.codeChallengeMethodsSupported?.some(t=>t.toUpperCase()==="S256")??!1}static supportsDynamicRegistration(e){return!!e.registrationEndpoint}static supportsAuthorizationCode(e){return e.responseTypesSupported?.some(t=>t.toLowerCase()==="code")??!1}static supportsEnrollment(e){return!!e.enrollmentEndpoint}async tryEndpoint(e){let t;try{t=await fetch(e)}catch(r){let a=r instanceof Error?r.message:String(r);return a.includes("ENOTFOUND")||a.includes("ECONNREFUSED")?(console.error(` OAuth discovery: ${e} \u2014 host unreachable (${a})`),console.error(" If the server is behind a VPN, ensure you are connected.")):a.includes("CERT")||a.includes("SSL")||a.includes("TLS")?(console.error(` OAuth discovery: ${e} \u2014 TLS error (${a})`),console.error(" If using a custom CA certificate, set NODE_EXTRA_CA_CERTS=/path/to/ca.pem")):console.error(` OAuth discovery: ${e} \u2014 network error: ${a}`),null}if(!t.ok)return t.status===404||(t.status===403?(console.error(` OAuth discovery: ${e} \u2014 HTTP 403 Forbidden`),console.error(" Access may be blocked by a firewall, proxy, or WAF.")):t.status>=500&&(console.error(` OAuth discovery: ${e} \u2014 HTTP ${t.status} server error`),console.error(" The authorization server returned an internal error. Contact your IDP administrator."))),null;let o;try{o=await t.json()}catch{return console.error(` OAuth discovery: ${e} \u2014 response is not valid JSON`),console.error(" The endpoint may be returning HTML instead of JSON. Check IDP configuration."),null}return{issuer:U(o.issuer),authorizationEndpoint:U(o.authorization_endpoint),tokenEndpoint:U(o.token_endpoint),registrationEndpoint:U(o.registration_endpoint),revocationEndpoint:U(o.revocation_endpoint),scopesSupported:O(o.scopes_supported),responseTypesSupported:O(o.response_types_supported),grantTypesSupported:O(o.grant_types_supported),codeChallengeMethodsSupported:O(o.code_challenge_methods_supported),tokenEndpointAuthMethodsSupported:O(o.token_endpoint_auth_methods_supported),dpopSigningAlgValuesSupported:O(o.dpop_signing_alg_values_supported),enrollmentEndpoint:U(o.enrollment_endpoint),enrollmentIssuanceProtocols:O(o.enrollment_issuance_protocols),enrollmentKeyTypes:O(o.enrollment_key_types),enrollmentScopesSupported:O(o.enrollment_scopes_supported),enrollmentConsentBindingParam:U(o.enrollment_consent_binding_param)}}};function U(n){return typeof n=="string"?n:null}function O(n){return Array.isArray(n)?n.filter(e=>typeof e=="string"):null}import{createHash as qo,randomBytes as Pn}from"crypto";import{createServer as Wo}from"http";import{execFile as Oe}from"child_process";function se(){return Pn(32).toString("base64url")}function ae(n){return qo("sha256").update(n).digest("base64url")}function ie(){return Pn(16).toString("base64url")}function ce(n){let e=new URLSearchParams({client_id:n.clientId,response_type:"code",redirect_uri:n.redirectUri,scope:n.scopes.join(" "),state:n.state,code_challenge:n.codeChallenge,code_challenge_method:"S256",...n.extraParams??{}});return`${n.endpoint}?${e}`}var Go=300*1e3;async function le(){return new Promise(n=>{let e,t,o=Wo((a,s)=>{let i=new URL(a.url,"http://localhost"),c=i.searchParams.get("code")??void 0,l=i.searchParams.get("state")??void 0,p=i.searchParams.get("error")??i.searchParams.get("error_description")??void 0,u=p?Jo(p):null,d=u?`<html><body><h1>Authorization failed</h1><p>${u}</p></body></html>`:"<html><body><h1>Authorization successful!</h1><p>You can close this window.</p></body></html>";s.writeHead(200,{"Content-Type":"text/html"}),s.end(d),t({code:c,state:l,error:p})}),r=new Promise(a=>{t=s=>{clearTimeout(e),a(s)},e=setTimeout(()=>t(null),Go),e.unref?.()});o.listen(0,"127.0.0.1",()=>{let a=o.address();n({redirectUri:`http://localhost:${a.port}/callback`,port:a.port,waitForCallback:()=>r,close:()=>{clearTimeout(e),o.close()}})})})}function Jo(n){return n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function pe(n){try{process.platform==="win32"?Oe("cmd",["/c","start","",n]):process.platform==="darwin"?Oe("open",[n]):Oe("xdg-open",[n])}catch{console.log(" Could not open browser automatically. Please navigate to the URL above manually.")}}var G=class{discovery;credentialStore;constructor(e,t){this.discovery=e??new D,this.credentialStore=t??new Yo}async authorize(e,t=[]){console.log(`Starting OAuth flow for ${e}...`);let o=await this.discovery.discover(e);if(!o)return K(`No OAuth discovery endpoint found for '${e}'.
|
|
13
13
|
The server must expose one of:
|
|
14
14
|
- https://${e}/.well-known/oauth-authorization-server (RFC 8414)
|
|
15
15
|
- https://${e}/.well-known/openid-configuration (OpenID Connect)
|
|
16
16
|
Verify the hostname is correct and HTTPS is configured.
|
|
17
17
|
If using a corporate proxy, ensure it is not blocking the discovery request.`);let r=[];if(o.authorizationEndpoint||r.push("authorization_endpoint"),o.tokenEndpoint||r.push("token_endpoint"),r.length>0)return K(`OAuth metadata for '${e}' is missing required fields: ${r.join(", ")}.
|
|
18
18
|
Contact the IDP administrator to ensure these are included in the discovery document.`);let a=Zo(o.dpopSigningAlgValuesSupported),s=null;if(a){console.log(" Server supports DPoP (RFC 9449) \u2014 generating key pair...");try{s=Xo()}catch(v){return K(`Failed to generate DPoP key pair: ${W(v)}
|
|
19
|
-
Ensure your Node.js installation supports EC P-256 curves.`)}}let{redirectUri:i,port:c,waitForCallback:l,close:p}=await le(),u=await this.credentialStore.getCredential(e),d=u?.clientId??null,
|
|
19
|
+
Ensure your Node.js installation supports EC P-256 curves.`)}}let{redirectUri:i,port:c,waitForCallback:l,close:p}=await le(),u=await this.credentialStore.getCredential(e),d=u?.clientId??null,f=u?.clientSecret??null;if(!d&&o.registrationEndpoint){console.log(" Registering dynamic OAuth client (RFC 7591)...");let v=await this.registerClient(o.registrationEndpoint,i);if(!v.success)return p(),K(v.error);d=v.clientId,f=v.clientSecret??null}if(!d)return p(),K(`No OAuth client credentials for '${e}' and the server does not support dynamic client registration (no registration_endpoint in metadata).
|
|
20
20
|
You must pre-register a client with your IDP and configure the client_id.
|
|
21
21
|
Ask your IDP administrator to enable dynamic client registration (RFC 7591),
|
|
22
|
-
or add a registration_endpoint to the OAuth metadata.`);let
|
|
22
|
+
or add a registration_endpoint to the OAuth metadata.`);let g=se(),m=ae(g),k=ie(),h=ce({endpoint:o.authorizationEndpoint,clientId:d,redirectUri:i,scopes:t,state:k,codeChallenge:m});console.log(` Opening browser for authorization on port ${c}...`),console.log(" If the browser doesn't open, navigate to:"),console.log(` ${h}`),pe(h);let y=await l();if(p(),!y)return K(`Authorization timed out after 5 minutes.
|
|
23
23
|
The browser authorization was not completed in time.
|
|
24
24
|
Re-run 'kanonak login ${e}' and complete the browser authorization promptly.`);if(!y.code){let v=y.error?`: ${y.error}`:"";return K(`No authorization code received${v}.
|
|
25
25
|
The IDP may have rejected the authorization request.
|
|
26
|
-
Check the IDP admin console for error details.`)}if(y.state!==
|
|
26
|
+
Check the IDP admin console for error details.`)}if(y.state!==k)return K(`Authorization state mismatch \u2014 possible CSRF attack.
|
|
27
27
|
The response did not match the expected session.
|
|
28
|
-
Re-run 'kanonak login ${e}' to start a fresh authorization flow.`);console.log(" Exchanging authorization code for tokens...");let w=await this.exchangeCode(o.tokenEndpoint,d,
|
|
28
|
+
Re-run 'kanonak login ${e}' to start a fresh authorization flow.`);console.log(" Exchanging authorization code for tokens...");let w=await this.exchangeCode(o.tokenEndpoint,d,f,y.code,i,g,s);if(!w.success)return K(w.error);let b={clientId:d,clientSecret:f,accessToken:w.tokens.accessToken,refreshToken:w.tokens.refreshToken,expiresAt:w.tokens.expiresIn?new Date(Date.now()+w.tokens.expiresIn*1e3).toISOString():null,tokenEndpoint:o.tokenEndpoint,dpopKeyPair:s};return await this.credentialStore.store(e,b),console.log(` Successfully authenticated with ${e}`),a&&console.log(" DPoP proof-of-possession is active"),{success:!0,host:e}}async refresh(e){let t=await this.credentialStore.getCredential(e);if(!t)return K(`No stored credentials for '${e}'. Run 'kanonak login ${e}' to authenticate first.`);if(!t.refreshToken)return K(`No refresh token available for '${e}'.
|
|
29
29
|
The authorization server may not issue refresh tokens for this client.
|
|
30
30
|
Run 'kanonak login ${e}' to re-authenticate.`);if(!t.clientId)return K(`No client ID in stored credentials for '${e}'.
|
|
31
31
|
The stored credential may be corrupted. Run 'kanonak logout ${e}' then 'kanonak login ${e}'.`);let o=t.tokenEndpoint;if(!o){let a=await this.discovery.discover(e);if(!a?.tokenEndpoint)return K(`Cannot find token endpoint for '${e}'.
|
|
@@ -34,17 +34,17 @@ Installed ${g.size} package(s).`)}async function bn(n,e,t,o,r,a,s,i,c){let l=`${
|
|
|
34
34
|
The refresh token may have expired. Run 'kanonak login ${e}' to re-authenticate.`)}async logout(e){let t=await this.credentialStore.getCredential(e);if(!t)return K(`No stored credentials for '${e}'.
|
|
35
35
|
You may not be logged in, or credentials may be stored in a different backend.`);let o=await this.discovery.discover(e);if(o?.revocationEndpoint&&t.accessToken&&t.clientId)try{await this.revokeToken(o.revocationEndpoint,t.accessToken,t.clientId)}catch{console.warn(` Warning: Token revocation failed for '${e}'. The token has been removed locally but may still be valid on the server until it expires.`)}return await this.credentialStore.remove(e),{success:!0,host:e}}async registerClient(e,t){let o;try{o=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_name:"Kanonak CLI",redirect_uris:[t],grant_types:["authorization_code","refresh_token"],response_types:["code"],token_endpoint_auth_method:"none"})})}catch(s){return{success:!1,error:`Dynamic client registration request to ${e} failed: ${W(s)}
|
|
36
36
|
Ensure the IDP is reachable and HTTPS is properly configured.
|
|
37
|
-
If using a corporate proxy or custom CA, set NODE_EXTRA_CA_CERTS.`}}if(!o.ok){let s=await
|
|
37
|
+
If using a corporate proxy or custom CA, set NODE_EXTRA_CA_CERTS.`}}if(!o.ok){let s=await Ne(o);return{success:!1,error:`Dynamic client registration (RFC 7591) failed.
|
|
38
38
|
Endpoint: ${e}
|
|
39
39
|
HTTP ${o.status}: ${s}
|
|
40
40
|
The IDP may not support dynamic registration for public clients.
|
|
41
41
|
Ask your IDP administrator to enable RFC 7591 support, or pre-register a client manually.`}}let r=await o.json(),a=r.client_id;return a?{success:!0,clientId:a,clientSecret:r.client_secret}:{success:!1,error:`Dynamic client registration response from ${e} did not contain a client_id.
|
|
42
42
|
The IDP response may be malformed. Contact your IDP administrator.`}}async exchangeCode(e,t,o,r,a,s,i){let c=new URLSearchParams({grant_type:"authorization_code",client_id:t,code:r,redirect_uri:a,code_verifier:s});o&&c.set("client_secret",o);let l={"Content-Type":"application/x-www-form-urlencoded"};if(i)try{l.DPoP=Cn(i.privateKey,i.publicKey,"POST",e)}catch(u){return{success:!1,error:`Failed to create DPoP proof for token exchange: ${W(u)}
|
|
43
43
|
The stored key pair may be corrupted. Run 'kanonak login' to re-authenticate.`}}let p;try{p=await fetch(e,{method:"POST",headers:l,body:c.toString()})}catch(u){return{success:!1,error:`Token exchange request to ${e} failed: ${W(u)}
|
|
44
|
-
Ensure the token endpoint is reachable.`}}if(!p.ok){let u=await
|
|
44
|
+
Ensure the token endpoint is reachable.`}}if(!p.ok){let u=await Ne(p),d=Qo(u);return{success:!1,error:`Token exchange failed.
|
|
45
45
|
Endpoint: ${e}
|
|
46
46
|
HTTP ${p.status}: ${u}`+(d?`
|
|
47
|
-
${d}`:"")}}return{success:!0,tokens:Rn(await p.json())}}async refreshTokenRequest(e,t,o,r,a){let s=new URLSearchParams({grant_type:"refresh_token",client_id:t,refresh_token:r});o&&s.set("client_secret",o);let i={"Content-Type":"application/x-www-form-urlencoded"};if(a)try{i.DPoP=Cn(a.privateKey,a.publicKey,"POST",e)}catch(l){return{success:!1,error:`Failed to create DPoP proof for token refresh: ${W(l)}`}}let c;try{c=await fetch(e,{method:"POST",headers:i,body:s.toString()})}catch(l){return{success:!1,error:`Token refresh request to ${e} failed: ${W(l)}`}}if(!c.ok){let l=await
|
|
47
|
+
${d}`:"")}}return{success:!0,tokens:Rn(await p.json())}}async refreshTokenRequest(e,t,o,r,a){let s=new URLSearchParams({grant_type:"refresh_token",client_id:t,refresh_token:r});o&&s.set("client_secret",o);let i={"Content-Type":"application/x-www-form-urlencoded"};if(a)try{i.DPoP=Cn(a.privateKey,a.publicKey,"POST",e)}catch(l){return{success:!1,error:`Failed to create DPoP proof for token refresh: ${W(l)}`}}let c;try{c=await fetch(e,{method:"POST",headers:i,body:s.toString()})}catch(l){return{success:!1,error:`Token refresh request to ${e} failed: ${W(l)}`}}if(!c.ok){let l=await Ne(c);return{success:!1,error:`Token refresh failed \u2014 HTTP ${c.status}: ${l}`}}return{success:!0,tokens:Rn(await c.json())}}async revokeToken(e,t,o){await fetch(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({token:t,client_id:o}).toString()})}};function K(n){return{success:!1,error:n}}function Rn(n){return{accessToken:n.access_token,refreshToken:n.refresh_token,expiresIn:typeof n.expires_in=="number"?n.expires_in:void 0}}function W(n){return n instanceof Error?n.message:String(n)}async function Ne(n){try{return await n.text()}catch{return"(could not read response body)"}}function Qo(n){try{let e=JSON.parse(n);switch(e.error){case"invalid_grant":return"Hint: The authorization code may have expired or already been used. Re-run the login flow.";case"invalid_client":return"Hint: The client credentials were rejected. The client_id may be invalid or the client_secret may be wrong.";case"redirect_uri_mismatch":return"Hint: The redirect_uri does not match what was registered with the IDP.";case"unsupported_grant_type":return"Hint: The IDP does not support authorization_code grants. Contact your IDP administrator.";case"invalid_scope":return"Hint: One or more requested scopes are not allowed. Check the scopes configured on the IDP.";case"use_dpop_nonce":return"Hint: The server requires a DPoP nonce. This should be handled automatically \u2014 please report this as a bug.";default:return e.error_description?`IDP says: ${e.error_description}`:null}}catch{return null}}import{CredentialStore as er}from"@kanonak-protocol/sdk";async function xn(n){try{let e=new D,t=new er,r=await new G(e,t).authorize(n);r.success?console.log(`
|
|
48
48
|
Authenticated with ${n}.`):(console.error(`
|
|
49
49
|
Authentication failed:
|
|
50
50
|
${r.error}`),process.exit(1))}catch(e){let t=e instanceof Error?e.message:String(e);console.error(`
|
|
@@ -55,9 +55,9 @@ Logout failed:
|
|
|
55
55
|
${r.error}`),process.exit(1))}catch(e){let t=e instanceof Error?e.message:String(e);console.error(`
|
|
56
56
|
Logout failed unexpectedly:
|
|
57
57
|
${t}`),process.exit(1)}}import{Command as Rr}from"commander";import{KanonakParser as xr,KanonakObjectParser as _r,PublisherIndex as Kr,CredentialStore as Ir,createAuthenticatedFetch as Er,computeIntegrity as Tr}from"@kanonak-protocol/sdk";import{readFileSync as tr,writeFileSync as or,existsSync as rr}from"fs";import{join as sr}from"path";import Kn from"js-yaml";import{getGlobalCachePath as ar}from"@kanonak-protocol/sdk";var ir=`# This file is generated by Kanonak CLI. Do not edit manually.
|
|
58
|
-
`;function In(){let n=ar();return sr(n,"..","capabilities.lock")}function J(){let n=In();if(!rr(n))return{version:"1",lastUpdated:new Date().toISOString(),capabilities:{}};let e=tr(n,"utf-8"),t=Kn.load(e);return!t||typeof t!="object"||t.version!=="1"?{version:"1",lastUpdated:new Date().toISOString(),capabilities:{}}:{version:"1",lastUpdated:t.lastUpdated??new Date().toISOString(),capabilities:t.capabilities??{}}}function ee(n){n.lastUpdated=new Date().toISOString();let e={};for(let o of Object.keys(n.capabilities).sort())e[o]=n.capabilities[o];n.capabilities=e;let t=Kn.dump(n,{lineWidth:-1,sortKeys:!1,quotingType:'"'});or(In(),ir+t,"utf-8")}import{SingleDocumentRepository as Dr}from"@kanonak-protocol/sdk/uri-helpers";import{existsSync as cr,readdirSync as lr,readFileSync as pr}from"fs";import{join as En}from"path";import{KanonakObjectParser as ur,EmbeddedKanonak as jn,ReferenceKanonak as On,getGlobalCachePath as dr}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as Nn}from"@kanonak-protocol/sdk/uri-helpers";import{uriKey as
|
|
58
|
+
`;function In(){let n=ar();return sr(n,"..","capabilities.lock")}function J(){let n=In();if(!rr(n))return{version:"1",lastUpdated:new Date().toISOString(),capabilities:{}};let e=tr(n,"utf-8"),t=Kn.load(e);return!t||typeof t!="object"||t.version!=="1"?{version:"1",lastUpdated:new Date().toISOString(),capabilities:{}}:{version:"1",lastUpdated:t.lastUpdated??new Date().toISOString(),capabilities:t.capabilities??{}}}function ee(n){n.lastUpdated=new Date().toISOString();let e={};for(let o of Object.keys(n.capabilities).sort())e[o]=n.capabilities[o];n.capabilities=e;let t=Kn.dump(n,{lineWidth:-1,sortKeys:!1,quotingType:'"'});or(In(),ir+t,"utf-8")}import{SingleDocumentRepository as Dr}from"@kanonak-protocol/sdk/uri-helpers";import{existsSync as cr,readdirSync as lr,readFileSync as pr}from"fs";import{join as En}from"path";import{KanonakObjectParser as ur,EmbeddedKanonak as jn,ReferenceKanonak as On,getGlobalCachePath as dr}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as Nn}from"@kanonak-protocol/sdk/uri-helpers";import{uriKey as Le,findSubjectsByType as mr,getStringValue as L,getReferenceUri as Fe,getListValues as Ln}from"@kanonak-protocol/sdk/uri-helpers";var R="kanonak.org",x="capabilities",Ue={publisher:R,package_:x,name:"Capability"},fr={publisher:R,package_:x,name:"commandName"},Fn={publisher:R,package_:x,name:"description"},gr={publisher:R,package_:x,name:"managesType"},hr={publisher:R,package_:x,name:"deploymentTarget"},kr={publisher:R,package_:x,name:"hasCommand"},yr={publisher:R,package_:x,name:"subcommandName"},wr={publisher:R,package_:x,name:"performs"},br={publisher:R,package_:x,name:"hasArgument"},vr={publisher:R,package_:x,name:"argumentName"},Tn={publisher:R,package_:x,name:"isRequired"},Dn={publisher:R,package_:x,name:"isOption"},$r={publisher:R,package_:x,name:"defaultValue"};async function Un(n,e,t){let o=J(),r=new ur,a=[],s=!1;for(let[i,c]of Object.entries(o.capabilities)){let l=c,p=n.get(l.publisher,l.package_,l.version);if(!p){let u=await Sr(i,l,e,r,t);if(u)console.error(` Healed capability "${i}": ${l.publisher}/${l.package_}@${l.version} \u2192 ${u.entry.publisher}/${u.entry.package_}@${u.entry.version} (package was renamed)`),o.capabilities[i]=u.entry,l=u.entry,p=u.content,s=!0;else{console.error(` WARNING: Cached content missing for capability "${i}" (${l.publisher}/${l.package_}@${l.version}). Reinstall it with: kanonak capability remove ${i} && kanonak capability add ${l.publisher}/${l.package_}`);continue}}try{let u=e.parse(p),d=new Nn(u,t),f=await r.parseKanonaks(d),g=Mn(f,l.publisher,l.package_,l.version);g?a.push(g):console.error(` WARNING: No Capability instance found in "${i}" (${l.publisher}/${l.package_}@${l.version})`)}catch(u){console.error(` WARNING: Failed to parse capability "${i}": ${u}`)}}return s&&ee(o),a}async function Sr(n,e,t,o,r){let a=En(dr(),e.publisher);if(!cr(a))return;let s=[];for(let i of lr(a)){if(!i.endsWith(".kan.yml"))continue;let c=i.slice(0,-8),l=c.lastIndexOf("@");if(l===-1)continue;let p=c.substring(0,l),u=c.substring(l+1);if(p===e.package_&&u===e.version)continue;let d;try{d=pr(En(a,i),"utf-8")}catch{continue}let f;try{let g=t.parse(d),m=new Nn(g,r),k=await o.parseKanonaks(m);f=Mn(k,e.publisher,p,u)?.commandName}catch{continue}f===n&&s.push({entry:{publisher:e.publisher,package_:p,version:u,resolved:`kanonak://${e.publisher}/${p}@${u}`,integrity:""},content:d})}if(s.length===1)return s[0]}function Mn(n,e,t,o){let r=mr(n,Ue);if(r.length===0)return;let a=r[0],s=L(a,fr)??"",i=L(a,Fn)??"",c=Fe(a,gr),l=Fe(a,hr),p=[];for(let u of Ln(a,kr)){let d=Pr(u,n);d&&p.push(d)}return{commandName:s,description:i,managesTypeKey:c?Le(c):"",deploymentTargetKey:l?Le(l):"",commands:p,publisher:e,package_:t,version:o}}function Pr(n,e){let t;if(n instanceof jn)t=n;else if(n instanceof On){let c=Vn(e,n.subject.publisher,n.subject.package_,n.subject.name);c&&(t=c)}if(!t)return;let o=L(t,yr)??"",r=L(t,Fn)??"",a=Fe(t,wr),s=Ln(t,br),i=[];for(let c of s){let l=Cr(c,e);l&&i.push(l)}return{subcommandName:o,description:r,arguments:i,actionKey:a?Le(a):""}}function Cr(n,e){let t;if(n instanceof jn)t=n;else if(n instanceof On){let c=Vn(e,n.subject.publisher,n.subject.package_,n.subject.name);c&&(t=c)}if(!t)return;let o=L(t,vr)??"",r=L(t,Tn)==="true"||An(t,Tn)===!0,a=L(t,Dn)==="true"||An(t,Dn)===!0,s=L(t,$r),i={argumentName:o,required:r,isOption:a};return s!==void 0&&(i.defaultValue=s),i}function An(n,e){for(let t of n.statement){let o=t,r=o.predicate;if(r?.subject&&r.subject.publisher===e.publisher&&r.subject.package_===e.package_&&r.subject.name===e.name)return typeof o.object=="boolean"?o.object:void 0}}function Vn(n,e,t,o){for(let r of n){if(!("name"in r)||!("namespace"in r))continue;let a=r;if(a.name===o&&!(!a.namespace||typeof a.namespace!="string")&&a.namespace.startsWith(`${e}/${t}@`))return r}}import{findSubjectsByType as Ar,getStringValue as jr}from"@kanonak-protocol/sdk/uri-helpers";var Or={publisher:R,package_:x,name:"commandName"};function Hn(){let n=new Rr("capability").description("Manage Kanonak CLI capabilities (pluggable command groups)");return n.command("add <package>").description("Install a capability from a Kanonak publisher").action(async e=>{await Nr(e)}),n.command("remove <name>").description("Remove an installed capability").action(async e=>{await Lr(e)}),n.command("list").description("List installed capabilities").action(async()=>{await Fr()}),n}async function Nr(n){let e=T(n);e||(console.error(`Invalid package reference: "${n}"`),console.error("Expected format: {publisher}/{package}[@{version}]"),process.exit(1));let{publisher:t,packageName:o,version:r,instanceName:a}=e;a&&(console.error(`\`kanonak capability add\` installs whole packages \u2014 got an instance suffix "/${a}".`),console.error(`Try: kanonak capability add ${t}/${o}${r?"@"+r:""}`),process.exit(1));let s=new I,i=new xr,c=new _r,l=new Ir,p=Er(l),u=new Kr({fetchFn:p}),d=r??await u.getHighestVersion(t,o);d||(console.error(`Could not resolve version for "${t}/${o}".`),process.exit(1)),console.log(`Installing capability ${t}/${o}@${d}...`);let f=s.get(t,o,d);if(!f){let $=await u.getPackageUrl(t,o,d),E=await p($,t);if(!E.ok)throw new Error(`Failed to fetch ${$} (${E.status} ${E.statusText})`);f=await E.text(),s.put(t,o,d,f)}let g=i.parse(f),m=await S(process.cwd(),i),k=new Dr(g,m),h=await c.parseKanonaks(k),y=Ar(h,Ue);y.length===0&&(console.error(`Package ${t}/${o}@${d} does not contain a ${R}/${x}/Capability instance.`),process.exit(1));let w=y[0],b=jr(w,Or);b||(console.error(`Capability instance in ${t}/${o}@${d} has no commandName property.`),process.exit(1));let v=J();v.capabilities[b]={publisher:t,package_:o,version:d,resolved:await u.getPackageUrl(t,o,d),integrity:Tr(f)},ee(v),console.log(`
|
|
59
59
|
Installed capability "${b}".`),console.log(`Run "kanonak ${b} --help" to get started.`)}async function Lr(n){let e=J();e.capabilities[n]||(console.error(`Capability "${n}" is not installed.`),process.exit(1));let t=e.capabilities[n];delete e.capabilities[n],ee(e),console.log(`Removed capability "${n}" (${t.publisher}/${t.package_}@${t.version}).`)}async function Fr(){let n=J(),e=Object.entries(n.capabilities);if(e.length===0){console.log("No capabilities installed."),console.log("Install one with: kanonak capability add {publisher}/{package}");return}console.log(`Installed capabilities:
|
|
60
|
-
`);for(let[t,o]of e)console.log(` ${t} ${o.publisher}/${o.package_}@${o.version}`)}import{Command as Gn}from"commander";import"reflect-metadata";import*as Wn from"@peculiar/x509";import{CredentialStore as Br,DeviceCertificateStore as qr,normalizeHost as Wr}from"@kanonak-protocol/sdk";import{webcrypto as Y}from"crypto";var
|
|
60
|
+
`);for(let[t,o]of e)console.log(` ${t} ${o.publisher}/${o.package_}@${o.version}`)}import{Command as Gn}from"commander";import"reflect-metadata";import*as Wn from"@peculiar/x509";import{CredentialStore as Br,DeviceCertificateStore as qr,normalizeHost as Wr}from"@kanonak-protocol/sdk";import{webcrypto as Y}from"crypto";var Me={name:"ECDSA",namedCurve:"P-256"},ue=class{id="software";async generate(e){zn(e);let t=await Y.subtle.generateKey(Me,!0,["sign","verify"]);return Bn(t,e)}async importKeyMaterial(e,t){zn(t);let o=e;if(o.d===void 0)throw new Error("SoftwareKeyProvider.importKeyMaterial: missing private key component (d)");let r=await Y.subtle.importKey("jwk",o,Me,!0,["sign"]),a={kty:o.kty,crv:o.crv,x:o.x,y:o.y,key_ops:["verify"],ext:!0},s=await Y.subtle.importKey("jwk",a,Me,!0,["verify"]);return Bn({privateKey:r,publicKey:s},t)}};function zn(n){if(n!=="ec-p256")throw new Error(`Unsupported key type '${n}'. The software provider implements 'ec-p256'.`)}async function Bn(n,e){let t=await Y.subtle.exportKey("jwk",n.publicKey),o=await Ur(t);return{keyType:e,publicKeyJwk:t,thumbprint:o,keyPair:n,exportKeyMaterial:async()=>await Y.subtle.exportKey("jwk",n.privateKey)}}async function Ur(n){if(n.kty!=="EC"||!n.crv||!n.x||!n.y)throw new Error(`jwkThumbprint: expected an EC public JWK with crv/x/y, got kty='${n.kty}'`);let e=JSON.stringify({crv:n.crv,kty:n.kty,x:n.x,y:n.y}),t=await Y.subtle.digest("SHA-256",new TextEncoder().encode(e));return Buffer.from(new Uint8Array(t)).toString("base64url")}import"reflect-metadata";import{webcrypto as Mr}from"crypto";import*as de from"@peculiar/x509";de.cryptoProvider.set(Mr);async function qn(n,e){if(!e.trim())throw new Error("buildCsr: subjectCommonName must be non-empty");return{csrPem:(await de.Pkcs10CertificateRequestGenerator.create({name:`CN=${Vr(e)}`,keys:n.keyPair,signingAlgorithm:{name:"ECDSA",hash:"SHA-256"}})).toString("pem"),thumbprint:n.thumbprint}}function Vr(n){return n.replace(/([,+"\\<>;=])/g,"\\$1").replace(/^ | $/g,"\\ ")}var me=class{constructor(e,t){this.enrollmentEndpoint=e;this.fetchImpl=t}enrollmentEndpoint;fetchImpl;protocol="native";async enroll(e,t,o){let r;try{r=await this.fetchImpl(this.enrollmentEndpoint,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({csr:e,scopes:o.scopes,...o.deviceName?{device_name:o.deviceName}:{}})})}catch(s){throw new Error(`Enrollment request to ${this.enrollmentEndpoint} failed: ${Hr(s)}
|
|
61
61
|
Ensure the enrollment endpoint is reachable and HTTPS is configured.`)}if(!r.ok)throw new Error(`CSR issuance failed.
|
|
62
62
|
Endpoint: ${this.enrollmentEndpoint}
|
|
63
63
|
HTTP ${r.status}: ${await zr(r)}
|
|
@@ -65,14 +65,14 @@ Installed capability "${b}".`),console.log(`Run "kanonak ${b} --help" to get sta
|
|
|
65
65
|
The server must expose /.well-known/oauth-authorization-server (RFC 8414).`);if(!D.supportsEnrollment(s))return N(`'${e}' does not advertise device-certificate enrollment.
|
|
66
66
|
Its discovery document has no 'enrollment_endpoint'.`);if(!s.authorizationEndpoint||!s.tokenEndpoint)return N(`'${e}' enrollment requires authorization_endpoint and token_endpoint in its metadata.`);if(s.enrollmentKeyTypes&&!s.enrollmentKeyTypes.includes(o))return N(`'${e}' does not accept key type '${o}'.
|
|
67
67
|
Supported: ${s.enrollmentKeyTypes.join(", ")}.`);let i=s.enrollmentIssuanceProtocols??["native"];if(!i.includes("native"))return N(`'${e}' requires issuance protocol(s) ${i.join(", ")}, but this CLI implements 'native'.
|
|
68
|
-
EST (RFC 7030) and ACME (RFC 8555) adapters are not yet available.`);let c=await this.keyProvider.generate(o),{csrPem:l,thumbprint:p}=await qn(c,t.deviceName??a),{redirectUri:u,port:d,waitForCallback:
|
|
69
|
-
Run 'kanonak login ${e}' first, or ask the operator to enable dynamic client registration.`);let
|
|
70
|
-
${b}`),this.openBrowser(b);let v=await
|
|
68
|
+
EST (RFC 7030) and ACME (RFC 8555) adapters are not yet available.`);let c=await this.keyProvider.generate(o),{csrPem:l,thumbprint:p}=await qn(c,t.deviceName??a),{redirectUri:u,port:d,waitForCallback:f,close:g}=await le();try{let m=await this.resolveClientId(e,s,u);if(!m)return N(`No client_id for '${e}' and the server has no registration_endpoint (RFC 7591).
|
|
69
|
+
Run 'kanonak login ${e}' first, or ask the operator to enable dynamic client registration.`);let k=se(),h=ae(k),y=ie(),w=s.enrollmentConsentBindingParam??Sn,b=ce({endpoint:s.authorizationEndpoint,clientId:m,redirectUri:u,scopes:r,state:y,codeChallenge:h,extraParams:{[w]:p}});console.log(` Opening browser for consent on port ${d}...`),console.log(` If the browser doesn't open, navigate to:
|
|
70
|
+
${b}`),this.openBrowser(b);let v=await f();if(!v)return N("Consent timed out after 5 minutes. Re-run the enrollment and approve promptly.");if(!v.code)return N(`Consent did not return an authorization code${v.error?`: ${v.error}`:""}.`);if(v.state!==y)return N("Consent state mismatch \u2014 possible CSRF. Re-run the enrollment.");let $=await this.exchangeCode(s.tokenEndpoint,m,v.code,u,k),q=await new me(s.enrollmentEndpoint,this.fetchImpl).enroll(l,$,{host:a,scopes:r,...t.deviceName?{deviceName:t.deviceName}:{}}),re=new Wn.X509Certificate(q.certificatePem).notAfter.toISOString(),Q={keyMaterial:await c.exportKeyMaterial(),thumbprint:p,certificatePem:q.certificatePem,chainPem:q.chainPem,issuedAt:new Date().toISOString(),expiresAt:re,scopes:r,...t.deviceName?{deviceName:t.deviceName}:{}};return await this.deviceStore.store(a,Q),{success:!0,host:a,thumbprint:p}}finally{g()}}async resolveClientId(e,t,o){let r=await this.credentialStore.getCredential(e);if(r?.clientId)return r.clientId;if(!t.registrationEndpoint)return null;let a=await this.fetchImpl(t.registrationEndpoint,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_name:"Kanonak CLI (device enrollment)",redirect_uris:[o],grant_types:["authorization_code"],response_types:["code"],token_endpoint_auth_method:"none"})});if(!a.ok)throw new Error(`Dynamic client registration (RFC 7591) failed at ${t.registrationEndpoint}: HTTP ${a.status}.`);let s=await a.json();if(typeof s.client_id!="string"||!s.client_id)throw new Error(`Registration response from ${t.registrationEndpoint} had no client_id.`);return s.client_id}async exchangeCode(e,t,o,r,a){let s=await this.fetchImpl(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:t,code:o,redirect_uri:r,code_verifier:a}).toString()});if(!s.ok)throw new Error(`Consent-token exchange at ${e} failed: HTTP ${s.status}.`);let i=await s.json();if(typeof i.access_token!="string"||!i.access_token)throw new Error(`Consent-token exchange at ${e} returned no access_token.`);return i.access_token}};function N(n){return{success:!1,error:n}}function Jn(){let n=new Gn("device").description("Manage device certificates for registry authentication.");return n.addCommand(Gr()),n}function Gr(){return new Gn("enroll").description("Enroll this device with a registry: generate a key + CSR, consent via SSO, install the signed certificate.").argument("<host>","Registry/publisher host to enroll with.").option("--scope <scope>","A scope to request (repeatable).",Yr,[]).option("--key-type <type>","Device key type.","ec-p256").option("--name <deviceName>","A human-facing label for this device (shown on the consent screen).").action(async(n,e)=>{try{await Jr(n,e)}catch(t){console.error(`
|
|
71
71
|
Enrollment failed unexpectedly:
|
|
72
72
|
${t.message}`),process.exit(1)}})}async function Jr(n,e){let o=await new fe().enroll(n,{scopes:e.scope,keyType:e.keyType,...e.name?{deviceName:e.name}:{}});o.success?(console.log(`
|
|
73
73
|
Enrolled device with ${o.host}.`),console.log(` Key thumbprint: ${o.thumbprint}`),console.log(" The signed certificate is installed in your device store.")):(console.error(`
|
|
74
74
|
Enrollment failed:
|
|
75
|
-
${o.error}`),process.exit(1))}function Yr(n,e){return[...e,n]}import{Command as te}from"commander";import{readFileSync as Xr,writeFileSync as Zr,mkdirSync as Qr,existsSync as Yn,statSync as es}from"fs";import{join as ns,resolve as ts}from"path";import{KanonakParser as
|
|
75
|
+
${o.error}`),process.exit(1))}function Yr(n,e){return[...e,n]}import{Command as te}from"commander";import{readFileSync as Xr,writeFileSync as Zr,mkdirSync as Qr,existsSync as Yn,statSync as es}from"fs";import{join as ns,resolve as ts}from"path";import{KanonakParser as ze,KanonakObjectParser as Be,SubjectKanonak as os,Reasoner as rs,KanonakVocabulary as ss,makeUriKey as Qn,pickHighestDocument as qe,formatVersion as et}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as ne}from"@kanonak-protocol/sdk/uri-helpers";import{findSubjectsByType as Ve,findSubjectByUri as as}from"@kanonak-protocol/sdk/uri-helpers";import{TX_V3 as Xn}from"@kanonak-protocol/sdk/transformations";import{TransformationRunnerV3 as is,SUPPORTED_FORMATS_V3 as nt}from"@kanonak-protocol/sdk/transformations";import{compileTransformationV3 as We}from"@kanonak-protocol/sdk/transformations";function He(n){return[...Ve(n,Xn.InstanceTransformation),...Ve(n,Xn.SetTransformation)]}function tt(){let n=new te("transform").description("Run ontology-driven transformations against Kanonak packages. Transformations are declarative YAML rules (instances of `kanonak.org/transformations/Transformation`) that convert ontology instances into output files. The runner produces one or more Artifacts per matched input; format backends serialize them to Markdown, HTML, SVG, TOML, JSON, or (with post-processing) PDF.").addHelpText("after",`
|
|
76
76
|
Examples:
|
|
77
77
|
|
|
78
78
|
# List every Transformation available in the local workspace
|
|
@@ -110,9 +110,9 @@ Examples:
|
|
|
110
110
|
|
|
111
111
|
# List the registered format backends
|
|
112
112
|
$ kanonak transform formats
|
|
113
|
-
`);return n.addCommand(cs()),n.addCommand(ds()),n.addCommand(fs()),n.addCommand(hs()),n}function cs(){return new te("run").description("Run a transformation against an input scope and write the resulting Artifacts to disk.").argument("<transformation>","Transformation URI (`publisher/package/name`, optional `@version`) or just the local name if unique in the workspace.").requiredOption("--scope <scope>","Input source \u2014 either a package URI `publisher/package[@version]` (resolved via the local workspace + cache + HTTP tiers) or a file path to a `.kan.yml` document. Repeatable: pass `--scope X --scope Y` to aggregate matching instances across multiple packages, useful for index pages and other cross-package SetTransformations.",ls,[]).option("--format <name>","OutputFormat name to target (e.g. `markdown-with-frontmatter`, `html`, `svg`, `json`). If omitted and the transformation declares exactly one output, that single format is used.").option("--out <dir>","Directory to write artifacts into.",".").action(async(n,e)=>{try{await ps(n,e)}catch(t){console.error(`Error: ${t.message}`),process.exit(1)}})}function ls(n,e){return[...e,n]}async function ps(n,e){let t=new
|
|
113
|
+
`);return n.addCommand(cs()),n.addCommand(ds()),n.addCommand(fs()),n.addCommand(hs()),n}function cs(){return new te("run").description("Run a transformation against an input scope and write the resulting Artifacts to disk.").argument("<transformation>","Transformation URI (`publisher/package/name`, optional `@version`) or just the local name if unique in the workspace.").requiredOption("--scope <scope>","Input source \u2014 either a package URI `publisher/package[@version]` (resolved via the local workspace + cache + HTTP tiers) or a file path to a `.kan.yml` document. Repeatable: pass `--scope X --scope Y` to aggregate matching instances across multiple packages, useful for index pages and other cross-package SetTransformations.",ls,[]).option("--format <name>","OutputFormat name to target (e.g. `markdown-with-frontmatter`, `html`, `svg`, `json`). If omitted and the transformation declares exactly one output, that single format is used.").option("--out <dir>","Directory to write artifacts into.",".").action(async(n,e)=>{try{await ps(n,e)}catch(t){console.error(`Error: ${t.message}`),process.exit(1)}})}function ls(n,e){return[...e,n]}async function ps(n,e){let t=new ze,o=new Be,r=await S(process.cwd(),t),a=await ot(n,r,o),{transformation:s,transformationKanonaks:i}=a;console.log(`Transformation: ${he(s)}`);let c=We(s,i);e.scope.length===0&&(console.error("At least one --scope is required."),process.exit(1));let l=[],p=new Map;for(let y of e.scope){let w=await ks(y,r,t,o);l.push(...w);let b=await bs(w,c.inputPattern.matchesClass,r);for(let v of b){let $=vs(v);$&&!p.has($)&&p.set($,v)}console.log(`Scope: ${y} \u2014 found ${b.length} instance(s) of ${c.inputPattern.matchesClass.publisher}/${c.inputPattern.matchesClass.package_}/${c.inputPattern.matchesClass.name}`)}let u=[...p.values()],d=l;e.scope.length>1&&console.log(`Total across ${e.scope.length} scope(s): ${u.length} unique instance(s)`),u.length===0&&c.kind==="instance"&&(console.error("No matching input instances across the supplied scope(s). Check the transformation's inputPattern.matchesClass vs. the scope package(s)."),process.exit(1));let f=us(c.outputs,e.format);console.log(`Output format: ${f}`);let g=nt[f];if(!g)throw new Error(`Unknown format "${f}". Run \`kanonak transform formats\` to see what the runner supports.`);let k=await new is().run({transformation:s,instances:u,allKanonaks:d,transformationKanonaks:i,repository:r,parser:t,objectParser:o,outputFormat:f}),h=ts(e.out);Qr(h,{recursive:!0});for(let y of k){let w=ws(y.fileName),b=ns(h,`${w}${g.extension}`);Zr(b,y.content,"utf-8"),console.log(` wrote ${b}`)}console.log(`Done. ${k.length} artifact(s) written to ${h}`)}function us(n,e){if(e){if(!n.has(e))throw new Error(`Transformation does not declare --format "${e}" in its outputs. Declared outputs: ${Array.from(n).sort().join(", ")}`);return e}if(n.size===1)return Array.from(n)[0];throw new Error(`Transformation supports multiple output formats (${Array.from(n).sort().join(", ")}); pass --format <name> to select one.`)}function ds(){return new te("list").description("Scan the local workspace + cache for every Transformation instance and print a one-line summary per hit.").action(async()=>{try{await ms()}catch(n){console.error(`Error: ${n.message}`),process.exit(1)}})}async function ms(){let n=new ze,e=new Be,t=await S(process.cwd(),n),o=await t.getAllDocumentsAsync(),r=new Map;for(let s of o)try{let i=new ne(s,t),c=await e.parseKanonaks(i),l=He(c);for(let p of l){let u=Ge(p);if(!u)continue;let d=r.get(u)??[];d.push({tx:p,kanonaks:c,doc:s}),r.set(u,d)}}catch{}let a=[];for(let s of r.values()){if(s.length===1){a.push(Zn(s[0].tx,s[0].kanonaks));continue}let i=qe(s.map(l=>l.doc));if(!i.chosen)continue;let c=s.find(l=>l.doc===i.chosen);c&&a.push(Zn(c.tx,c.kanonaks))}if(a.length===0){console.log("No Transformation instances found in the current workspace.");return}a.sort((s,i)=>s.uri.localeCompare(i.uri));for(let s of a)console.log(`${s.uri}`),console.log(` matches: ${s.matchesClass}`),console.log(` outputs: ${s.outputs.join(", ")}`)}function fs(){return new te("show").description("Compile a transformation and print a human-readable summary (input pattern, outputs, format overrides, rule shape).").argument("<transformation>","Transformation URI or local name").action(async n=>{try{await gs(n)}catch(e){console.error(`Error: ${e.message}`),process.exit(1)}})}async function gs(n){let e=new ze,t=new Be,o=await S(process.cwd(),e),{transformation:r,transformationKanonaks:a}=await ot(n,o,t),s=We(r,a);if(console.log(`Transformation: ${he(r)}`),console.log(),console.log("Input pattern:"),console.log(` Matches class: ${ge(s.inputPattern.matchesClass)}`),s.inputPattern.requires.length>0){console.log(" Required properties:");for(let i of s.inputPattern.requires)console.log(` - ${ge(i)}`)}else console.log(" Required properties: (none)");if(console.log(),console.log(`Outputs: ${Array.from(s.outputs).sort().join(", ")}`),console.log(),s.overrides.size>0){console.log("Format overrides:");for(let[i,c]of s.overrides){if(console.log(` ${i}:`),c.metadataKeys&&console.log(` metadata keys: ${c.metadataKeys.join(", ")}`),c.metadataRenames.size>0){let l=[];for(let[p,u]of c.metadataRenames)l.push(`${p} \u2192 ${u}`);console.log(` metadata renames: ${l.join(", ")}`)}c.trailingNewline!==void 0&&console.log(` trailing newline: ${c.trailingNewline}`)}console.log()}console.log(`Rule root kind: ${s.rule.kind}`),s.rule.kind==="build-ast-node"&&(console.log(`Rule root AST class: ${ge(s.rule.astClass)}`),console.log(`Rule root bindings: ${s.rule.set.length} field(s)`)),console.log(`Artifact name root kind: ${s.artifactName.kind}`)}function hs(){return new te("formats").description("List the OutputFormat names the runner can serialize to.").action(()=>{console.log("Registered format backends:");let n=Object.entries(nt).sort(([e],[t])=>e.localeCompare(t));for(let[e,t]of n)console.log(` ${e.padEnd(28)} ${t.extension.padEnd(6)} ${t.formatUri}`)})}async function ot(n,e,t){let o,r,a;if(n.includes("/")){let d=n.indexOf("@"),f=n;if(d!==-1){let m=n.indexOf("/",d);if(m===-1)throw new Error(`Transformation URI "${n}" has @version but no trailing /<name>`);f=n.substring(0,d)+n.substring(m)}let g=f.split("/");if(g.length<3)throw new Error(`Invalid transformation URI "${n}" \u2014 expected publisher/package/name or publisher/package@version/name`);o=g[0],r=g[1],a=g.slice(2).join("/")}else a=n;if(o&&r){let d=await e.getDocumentsByNamespaceAsync(o,r);if(d.length===0)throw new Error(`Package ${o}/${r} not found in the local workspace or cache.`);let f=ys(n),g=rt(d,f,`${o}/${r}`),m=new ne(g,e),k=await t.parseKanonaks(m),h=as(k,{publisher:o,package_:r,name:a,version:""});if(!h){let y=He(k),w=y.find(b=>b.name===a);if(!w)throw new Error(`Transformation "${a}" not found in package ${o}/${r}. Package contains ${y.length} Transformation(s): ${y.map(b=>b.name).join(", ")||"(none)"}`);return{transformation:w,transformationKanonaks:k}}return{transformation:h,transformationKanonaks:k}}let s=await e.getAllDocumentsAsync(),i=new Map;for(let d of s)try{let f=new ne(d,e),g=await t.parseKanonaks(f),m=He(g);for(let k of m){if(k.name!==a)continue;let h=Ge(k);if(!h)continue;let y=i.get(h)??[];y.push({tx:k,kanonaks:g,doc:d}),i.set(h,y)}}catch{}if(i.size===0)throw new Error(`No Transformation named "${a}" found in the workspace. Try \`kanonak transform list\` to see available transformations.`);if(i.size>1){let d=Array.from(i.keys()).sort().join(`
|
|
114
114
|
`);throw new Error(`Multiple Transformations named "${a}" in the workspace \u2014 disambiguate with a full publisher/package/name URI:
|
|
115
|
-
${d}`)}let c=Array.from(i.values())[0];if(c.length===1)return{transformation:c[0].tx,transformationKanonaks:c[0].kanonaks};let l=new Map(c.map(d=>[d.doc,d])),p=
|
|
115
|
+
${d}`)}let c=Array.from(i.values())[0];if(c.length===1)return{transformation:c[0].tx,transformationKanonaks:c[0].kanonaks};let l=new Map(c.map(d=>[d.doc,d])),p=qe(c.map(d=>d.doc));if(!p.chosen)throw new Error(`Could not select a Transformation version for "${a}".`);let u=l.get(p.chosen);if(p.multipleVersionsPresent){let d=m=>{let k=m.metadata.namespace_?.version;return k?et(k):""},f=u.tx.namespace??"",g=f.includes("@")?f.substring(0,f.indexOf("@")):f;console.warn(`Warning: Transformation "${a}" has ${c.length} versions present (${c.map(m=>d(m.doc)).filter(Boolean).join(", ")}); using @${d(p.chosen)}. Pin with ${g}@<version>/${a} to suppress this warning.`)}return{transformation:u.tx,transformationKanonaks:u.kanonaks}}async function ks(n,e,t,o){if(/^[.\/\\]/.test(n)||/^[A-Za-z]:/.test(n)||n.endsWith(".kan.yml")||Yn(n)&&es(n).isFile()){if(!Yn(n))throw new Error(`Scope file not found: ${n}`);let g=Xr(n,"utf-8"),m=t.parse(g),k=new ne(m,e);return o.parseKanonaks(k)}let a=n.indexOf("@"),s=a===-1?n:n.substring(0,a),i=a===-1?void 0:n.substring(a+1),c=s.indexOf("/");if(c===-1)throw new Error(`Invalid scope "${n}" \u2014 expected a file path or publisher/package[@version] URI`);let l=s.substring(0,c),p=s.substring(c+1),u=await e.getDocumentsByNamespaceAsync(l,p);if(u.length===0)throw new Error(`Scope package ${l}/${p} not found in the local workspace or cache.`);let d=rt(u,i,`${l}/${p}`),f=new ne(d,e);return o.parseKanonaks(f)}function ys(n){let e=n.indexOf("@");if(e===-1)return;let t=n.substring(e+1),o=t.indexOf("/");return o===-1?t:t.substring(0,o)}function rt(n,e,t){let o=a=>{let s=a.metadata.namespace_?.version;return s?et(s):""},r=qe(n,{requestedVersion:e});if(r.exactRequestedVersionMissing){let a=n.map(o).filter(Boolean).sort().join(", ");throw new Error(`${t}@${e} not found in the local workspace or cache. Available versions: ${a||"(none with parseable namespace)"}`)}if(!r.chosen)throw new Error(`${t} not found in the local workspace or cache.`);return!e&&r.multipleVersionsPresent&&console.warn(`Warning: ${t} has ${n.length} versions present (${n.map(o).filter(Boolean).join(", ")}); using @${o(r.chosen)}. Pin with ${t}@<version> to suppress this warning.`),r.chosen}function Zn(n,e){try{let t=We(n,e);return{uri:he(n),matchesClass:ge(t.inputPattern.matchesClass),outputs:Array.from(t.outputs).sort()}}catch(t){return{uri:he(n),matchesClass:`(compile error: ${t.message})`,outputs:[]}}}function he(n){let e=n.namespace||"",t=e.indexOf("@");return`${t===-1?e:e.substring(0,t)}/${n.name}`}function ge(n){return`${n.publisher}/${n.package_}/${n.name}`}function ws(n){return n.replace(/[\\/:*?"<>|\s]+/g,"_").replace(/^_+|_+$/g,"")}async function bs(n,e,t){let o=new Map;for(let s of n){if(!(s instanceof os))continue;let i=Ge(s);i&&o.set(i,s)}let r=[];try{r=(await new rs({vocabulary:new ss}).reason(t)).getInstancesOfClass(Qn(e.publisher,e.package_,e.name))}catch{}let a=[];for(let s of r){let i=o.get(s);i&&a.push(i)}return a.length>0?a:Ve(n,e)}function Ge(n){let e=n.namespace??"",t=e.indexOf("@"),o=t===-1?e:e.substring(0,t),r=o.indexOf("/");return r===-1||!n.name?null:Qn(o.substring(0,r),o.substring(r+1),n.name)}function vs(n){let e=n.namespace??"";return!e||!n.name?null:`${e}/${n.name}`}import{Command as $s}from"commander";import{writeFileSync as Ss,mkdirSync as Ps}from"fs";import{join as Cs,resolve as Rs}from"path";import{KanonakParser as xs,KanonakObjectParser as _s,SubjectKanonak as it,findDerivation as Ks}from"@kanonak-protocol/sdk";import{TransformationRunnerV3 as Is}from"@kanonak-protocol/sdk/transformations";var Es={publisher:"kanonak.org",package_:"formats",name:"html"},Ts={publisher:"kanonak.org",package_:"derivation",name:"default"},Ds={html:".html",markdown:".md",json:".json",yaml:".yaml",xml:".xml",svg:".svg",typescript:".ts","c-sharp":".cs",rust:".rs",python:".py"};function As(n){return n==="markdown"?"markdown-with-frontmatter":n}function ct(){return new $s("derive").description("Materialize a derived artifact from a Kanonak resource by walking the polymorphic derivation discovery: instance overrides \u2192 class hierarchy \u2192 universal defaults. The result is written to disk.").argument("<resource>","Resource URI in the form publisher/package[@version]/name. Identifies the specific instance to derive.").option("--format <name>","Format instance name (e.g. html, markdown, json). Defaults to html.","html").option("--variant <name>","Variant instance name (e.g. default, compact, summary). Defaults to default.","default").option("--out <dir>","Directory to write the artifact into.",".").addHelpText("after",`
|
|
116
116
|
Examples:
|
|
117
117
|
|
|
118
118
|
# Render a resource as HTML using whatever derivation the discovery
|
|
@@ -123,7 +123,7 @@ Examples:
|
|
|
123
123
|
|
|
124
124
|
# Materialize the JSON view (universal default unless overridden).
|
|
125
125
|
$ kanonak derive my-publisher.com/my-package/my-resource --format json
|
|
126
|
-
`).action(async(n,e)=>{try{await js(n,e)}catch(t){console.error(`Error: ${t.message}`),process.exit(1)}})}async function js(n,e){let t=T(n);if(!t||!t.instanceName)throw new Error(`Invalid resource URI "${n}". Expected publisher/package[@version]/instance.`);let o=new xs,r=new _s,a=await
|
|
126
|
+
`).action(async(n,e)=>{try{await js(n,e)}catch(t){console.error(`Error: ${t.message}`),process.exit(1)}})}async function js(n,e){let t=T(n);if(!t||!t.instanceName)throw new Error(`Invalid resource URI "${n}". Expected publisher/package[@version]/instance.`);let o=new xs,r=new _s,a=await S(process.cwd(),o),s=await r.parseKanonaks(a),i=Os(s,t);if(!i)throw new Error(`Resource ${t.publisher}/${t.packageName}${t.version?`@${t.version}`:""}/${t.instanceName} not found in the workspace or cache.`);console.log(`Resource: ${st(i)}`);let c=Ls(e.format),l=Fs(e.variant),p=Ks(i,c,l,s);if(!p)throw new Error(`No derivation found for resource ${st(i)} at (format=${e.format}, variant=${e.variant}). The class hierarchy declares no binding and no universal default applies.`);console.log(`Derivation: ${Us(p)} \u2192 ${at(p.transformation)}`);let u=Ns(s,p.transformation);if(!u)throw new Error(`Transformation ${at(p.transformation)} not found in the workspace or cache. The binding pointed at a transformation the resolver cannot locate.`);let d=As(e.format),g=await new Is().run({transformation:u,instances:[i],allKanonaks:s,repository:a,parser:o,objectParser:r,outputFormat:d}),m=Rs(e.out);Ps(m,{recursive:!0});let k=Ds[e.format]??"";for(let h of g){let y=Ms(h.fileName),w=Cs(m,`${y}${k}`);Ss(w,h.content,"utf-8"),console.log(` wrote ${w}`)}console.log(`Done. ${g.length} artifact(s) written to ${m}`)}function Os(n,e){for(let t of n){if(!(t instanceof it)||t.name!==e.instanceName)continue;let o=t.namespace||"",r=`${e.publisher}/${e.packageName}@`;if(o.startsWith(r))if(e.version){if(o===`${e.publisher}/${e.packageName}@${e.version}`)return t}else return t}}function Ns(n,e){let t=`${e.publisher}/${e.package_}@${e.version}`;for(let o of n)if(o instanceof it&&o.name===e.name&&o.namespace===t)return o}function Ls(n){if(n.includes("/")){let e=T(n);if(!e||!e.instanceName)throw new Error(`Invalid --format URI "${n}".`);return{publisher:e.publisher,package_:e.packageName,name:e.instanceName}}return{...Es,name:n}}function Fs(n){if(n.includes("/")){let e=T(n);if(!e||!e.instanceName)throw new Error(`Invalid --variant URI "${n}".`);return{publisher:e.publisher,package_:e.packageName,name:e.instanceName}}return{...Ts,name:n}}function st(n){return`${n.namespace}/${n.name}`}function at(n){return`${n.publisher}/${n.package_}@${n.version}/${n.name}`}function Us(n){return n.source==="instance"?"instance override":`class ${n.source.publisher}/${n.source.package_}/${n.source.name}`}function Ms(n){return n.replace(/[^a-zA-Z0-9._@-]+/g,"_")}import{Command as Vs}from"commander";import{writeFileSync as Hs}from"fs";import{join as zs,resolve as Bs}from"path";import{KanonakParser as qs,ViewMaterializer as Ws,LockAwareRepository as Gs,loadLockFile as Js}from"@kanonak-protocol/sdk";function lt(){let n=new Vs("view").description("Run and materialize Views \u2014 the producer-agnostic query primitive. A View selects instances of its bind class, filters by its where predicates, and projects each into its produces output class.");return n.command("run").description("Materialize a view.View against the local catalog into an EphemeralPackage (content-addressed, versionless). Prints the result as .kan.yml, or writes it with --out.").argument("<view>","View URI in the form publisher/package[@version]/name. May name a view.View directly or a view.ViewPackage (its rootView is followed).").option("--out <file>","Write the EphemeralPackage to this file instead of stdout.").option("--id <id>","Record a per-invocation id on the result (provenance, not identity).").addHelpText("after",`
|
|
127
127
|
Examples:
|
|
128
128
|
|
|
129
129
|
# Materialize a published view and print the EphemeralPackage.
|
|
@@ -131,29 +131,29 @@ Examples:
|
|
|
131
131
|
|
|
132
132
|
# Write the result to a file.
|
|
133
133
|
$ kanonak view run example.org/authors/AuthorView --out ./result.kan.yml
|
|
134
|
-
`).action(async(e,t)=>{try{await Ys(e,t)}catch(o){console.error(`Error: ${o.message}`),process.exit(1)}}),n}async function Ys(n,e){let t=T(n);if(!t||!t.instanceName)throw new Error(`Invalid view URI "${n}". Expected publisher/package[@version]/name.`);let o=new qs,r=await
|
|
135
|
-
${l.publisher}/${l.packageName} (${l.contentHash})`)}else process.stdout.write(l.yaml)}import{Command as Xs}from"commander";import{writeFileSync as _,mkdirSync as M}from"fs";import{join as
|
|
134
|
+
`).action(async(e,t)=>{try{await Ys(e,t)}catch(o){console.error(`Error: ${o.message}`),process.exit(1)}}),n}async function Ys(n,e){let t=T(n);if(!t||!t.instanceName)throw new Error(`Invalid view URI "${n}". Expected publisher/package[@version]/name.`);let o=new qs,r=await S(process.cwd(),o),a=Js(zs(process.cwd(),"kanonak.lock")),s=a?new Gs(r,a):r,i=new Ws(s,o),c={resolvedAt:new Date().toISOString()};e.id&&(c.invocationId=e.id);let l=await i.materialize({publisher:t.publisher,package_:t.packageName,name:t.instanceName},c);if(e.out){let p=Bs(e.out);Hs(p,l.yaml,"utf-8"),console.error(`Materialized ${l.rowCount} result(s) \u2192 ${p}
|
|
135
|
+
${l.publisher}/${l.packageName} (${l.contentHash})`)}else process.stdout.write(l.yaml)}import{Command as Xs}from"commander";import{writeFileSync as _,mkdirSync as M}from"fs";import{join as P,resolve as pt,dirname as Zs}from"path";import{SubjectKanonak as Qs}from"@kanonak-protocol/sdk";import{loadServerModel as ea,route as na}from"@kanonak-protocol/sdk/server";function ut(){return new Xs("publish").description("Generate a static Kanonak site from the local workspace \u2014 the same origin `kanonak serve` produces (it shares serve's model + renderer), frozen to disk under --out.").option("--workspace <dir>","Workspace directory containing .kan.yml package files. Defaults to current directory.",process.cwd()).option("--out <dir>","Output directory for the generated site. Defaults to _site.","_site").option("--publisher <domain>","Publisher to publish (required only if the workspace has more than one).").action(async n=>{await ta(n)})}async function ta(n){let e=pt(n.workspace),t=pt(n.out),o=await ea(e,{...n.publisher?{publisher:n.publisher}:{},log:console.log}),{catalog:r,lookRenderer:a,localNamespaces:s,rawByNsKey:i,publisher:c}=o;o.renderStack.warnings.length>0&&console.error(`[render-stack] \u2717 ${o.renderStack.warnings.length} problem(s) \u2014 the published site will render unstyled or incomplete. Run \`kanonak serve\` and open /.well-known/kanonak-render to diagnose.`),M(t,{recursive:!0}),console.log(`Publishing ${c} \u2014 ${s.size} package version(s) to ${t}`);let l=r.filter(g=>g instanceof Qs&&s.has(g.namespace||""));console.log(`Rendering ${l.length} resource(s).`);let p=0,u=0;for(let g of l){let m=(g.namespace||"").split("/")[1]??"",[k,h]=m.split("@");if(!k||!h)continue;let y=P(t,k,h);M(y,{recursive:!0});try{let w=a.renderRawMarkdown(g);w!==void 0&&(_(P(y,`${g.name}.md`),w,"utf-8"),p+=1),_(P(y,`${g.name}.html`),await a.renderDocument(g),"utf-8"),_(P(y,`${g.name}.css`),a.renderStylesheet(g),"utf-8"),_(P(y,`${g.name}.svg`),await a.renderSvg(g),"utf-8"),p+=3}catch(w){console.warn(` ${g.namespace}/${g.name}: ${w.message}`),u+=1}}console.log(`Done. ${p} artifact(s) written, ${u} skipped.`);let d=await ra(t,l,a);console.log(`Wrote ${d} package overview page(s).`),await sa(t,l,a);let f=aa(t,l);console.log(`Wrote ${f} version-form redirect stub(s).`),await oa(t,o)}async function oa(n,e){if(e.rawByNsKey.size===0)return;let t=async(o,r)=>{let a=P(n,r);M(Zs(a),{recursive:!0}),_(a,(await na(e,o)).body,"utf-8")};await t("/.well-known/kanonak.json",".well-known/kanonak.json"),await t("/index.txt","index.txt");for(let o of e.rawByNsKey.keys()){let[r,a]=o.split("@");!r||!a||await t(`/${r}/${a}.kan.yml`,`${r}/${a}.kan.yml`)}console.log(`Wrote publisher endpoints: index.txt, .well-known/kanonak.json, and ${e.rawByNsKey.size} raw package source(s).`)}async function ra(n,e,t){let o=new Map,r=new Map;for(let i of e){let c=i.namespace||"",[,l]=c.split("/");if(!l)continue;let[p,u]=l.split("@");if(!p||!u||i.name!==p)continue;let d=u.split(".").map(Number);if(d.length!==3||d.some(m=>Number.isNaN(m)))continue;let f={verStr:u,major:d[0],minor:d[1],patch:d[2]};o.has(p)||o.set(p,[]),o.get(p).push({resource:i,ver:f});let g=r.get(p);(!g||f.major>g.ver.major||f.major===g.ver.major&&f.minor>g.ver.minor||f.major===g.ver.major&&f.minor===g.ver.minor&&f.patch>g.ver.patch)&&r.set(p,{resource:i,ver:f})}let a=async(i,c)=>{try{return M(i,{recursive:!0}),_(P(i,"index.html"),await t.renderDocument(c),"utf-8"),_(P(i,`${c.name}.css`),t.renderStylesheet(c),"utf-8"),!0}catch{return!1}},s=0;for(let[i,c]of o)for(let{resource:l,ver:p}of c)await a(P(n,i,p.verStr),l)&&(s+=1);for(let[i,{resource:c}]of r){let l=P(n,i);try{if(M(l,{recursive:!0}),t.hasDeclaredView(c))_(P(l,"index.html"),await t.renderDocument(c,{bareOverview:!0}),"utf-8"),_(P(l,`${c.name}.css`),t.renderStylesheet(c),"utf-8");else{let p=(o.get(i)??[]).slice().sort((u,d)=>d.ver.major-u.ver.major||d.ver.minor-u.ver.minor||d.ver.patch-u.ver.patch).map(u=>u.ver.verStr);_(P(l,"index.html"),t.renderPackageVersionList(i,p,c),"utf-8"),_(P(l,`${c.name}.css`),t.renderStylesheet(c),"utf-8")}s+=1}catch{}}return s}async function sa(n,e,t){let o="",r=new Map;for(let s of e){let c=(s.namespace||"").split("/"),[l]=(c[1]??"").split("@");if(!c[0]||!l||(o||(o=c[0]),s.name!==l))continue;let p=(c[1]??"").split("@")[1]??"",u=p.split(".").map(Number);if(u.length!==3||u.some(g=>Number.isNaN(g)))continue;let d={verStr:p,major:u[0],minor:u[1],patch:u[2]},f=r.get(l);(!f||d.major>f.ver.major||d.major===f.ver.major&&d.minor>f.ver.minor||d.major===f.ver.major&&d.minor===f.ver.minor&&d.patch>f.ver.patch)&&r.set(l,{resource:s,ver:d})}if(!o||r.size===0)return;let a=t.publisherSubject(o);_(P(n,"index.html"),await t.renderDocument(a,{rootIndex:!0}),"utf-8"),_(P(n,"index.css"),t.renderStylesheet(a),"utf-8")}function aa(n,e){let t=new Map;for(let r of e){let a=r.namespace||"",[,s]=a.split("/");if(!s)continue;let[i,c]=s.split("@");if(!i||!c)continue;let l=c.split(".").map(Number);if(l.length!==3||l.some(u=>Number.isNaN(u)))continue;let p=`${i}/${r.name}`;t.has(p)||t.set(p,[]),t.get(p).push({verStr:c,major:l[0],minor:l[1],patch:l[2]})}let o=0;for(let[r,a]of t){let[s,i]=r.split("/");a.sort((u,d)=>d.major-u.major||d.minor-u.minor||d.patch-u.patch);let c=a[0];_(P(n,s,`${i}.html`),Je(`/${s}/${c.verStr}/${i}`),"utf-8"),o+=1;let l=new Set;for(let u of a){if(l.has(u.major))continue;l.add(u.major);let d=P(n,s,String(u.major));M(d,{recursive:!0}),_(P(d,`${i}.html`),Je(`/${s}/${u.verStr}/${i}`),"utf-8"),o+=1}let p=new Set;for(let u of a){let d=`${u.major}.${u.minor}`;if(p.has(d))continue;p.add(d);let f=P(n,s,d);M(f,{recursive:!0}),_(P(f,`${i}.html`),Je(`/${s}/${u.verStr}/${i}`),"utf-8"),o+=1}}return o}function Je(n){let e=n.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""");return`<!doctype html>
|
|
136
136
|
<meta charset="utf-8">
|
|
137
137
|
<title>Redirecting\u2026</title>
|
|
138
138
|
<meta http-equiv="refresh" content="0; url=${e}">
|
|
139
139
|
<link rel="canonical" href="${e}">
|
|
140
140
|
<p>If you are not redirected automatically, follow this <a href="${e}">link</a>.</p>
|
|
141
|
-
`}import{Command as ht}from"commander";import{writeFileSync as Je,mkdirSync as ke}from"fs";import{join as Ye,resolve as ia,dirname as Xe}from"path";import{KanonakParser as ca,KanonakObjectParser as la,SubjectKanonak as kt,ReferenceStatement as pa,ReferenceKanonak as ua,ListStatement as da,StringStatement as yt,EmbeddedStatement as ma,findDerivation as dt}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as fa}from"@kanonak-protocol/sdk/uri-helpers";import{TransformationRunnerV3 as ga}from"@kanonak-protocol/sdk/transformations";import{findSubjectsByType as ye}from"@kanonak-protocol/sdk/uri-helpers";var A="kanonak.org",j="site",mt={publisher:"kanonak.org",package_:"formats",name:"html"},ft={publisher:"kanonak.org",package_:"derivation",name:"default"};function wt(){let n=new ht("site").description("Materialize a publisher's site \u2014 every StaticPage and AggregateView in the workspace gets rendered to its declared output path under the --out directory.");return n.addCommand(ha()),n}function ha(){return new ht("build").description("Walk the workspace, find every StaticPage and AggregateView, and write each materialized artifact to its declared output path under the --out directory.").option("--out <dir>","Output base directory.","./_site").action(async n=>{try{await ka(n)}catch(e){console.error(`Error: ${e.message}`),process.exit(1)}})}async function ka(n){let e=new ca,t=new la,o=await $(process.cwd(),e),r=await t.parseKanonaks(o),a=ia(n.out);ke(a,{recursive:!0});let s=await o.getAllDocumentsAsync(),i=new Set;for(let m of s){let h=m.metadata.namespace_?.toString();h&&i.add(h)}let c={publisher:A,package_:j,name:"StaticPage"},l={publisher:A,package_:j,name:"ResourcePage"},p={publisher:A,package_:j,name:"AggregateView"},u=ye(r,c).filter(m=>i.has(m.namespace||"")),d=ye(r,l).filter(m=>i.has(m.namespace||"")),g=ye(r,p).filter(m=>i.has(m.namespace||""));console.log(`Found ${u.length} StaticPage(s), ${d.length} ResourcePage(s), and ${g.length} AggregateView(s) in the workspace.`);let f=new ga;for(let m of u){let h=Ze(m,A,j,"outputPath")??`${m.name}.html`,k=dt(m,mt,ft,r);if(!k){console.warn(` [skip] ${m.name}: no HTML/default derivation found (check class hierarchy or universal-derivations is loaded)`);continue}let y=Qe(r,k.transformation);if(!y){console.warn(` [skip] ${m.name}: transformation ${en(k.transformation)} not found`);continue}let w=await f.run({transformation:y,instances:[m],allKanonaks:r,repository:o,parser:e,objectParser:t,outputFormat:"html"});if(w.length===0){console.warn(` [skip] ${m.name}: transformation produced no artifacts`);continue}let b=Ye(a,h);ke(Xe(b),{recursive:!0}),Je(b,w[0].content,"utf-8"),console.log(` wrote ${b} (StaticPage ${m.name})`)}for(let m of d){let h=Ze(m,A,j,"outputPath")??`${m.name}.html`,k=gt(m,A,j,"pageOf");if(!k){console.warn(` [skip] ${m.name}: no pageOf declared`);continue}let y=r.find(E=>E instanceof kt&&E.name===k.name&&(E.namespace||"").startsWith(`${k.publisher}/${k.package_}@`));if(!y){console.warn(` [skip] ${m.name}: pageOf target ${k.publisher}/${k.package_}/${k.name} not found`);continue}let w=dt(y,mt,ft,r);if(!w){console.warn(` [skip] ${m.name}: no derivation for target ${y.name}`);continue}let b=Qe(r,w.transformation);if(!b){console.warn(` [skip] ${m.name}: transformation ${en(w.transformation)} not found`);continue}let v=await f.run({transformation:b,instances:[y],allKanonaks:r,repository:o,parser:e,objectParser:t,outputFormat:"html"});if(v.length===0){console.warn(` [skip] ${m.name}: transformation produced no artifacts`);continue}let P=Ye(a,h);ke(Xe(P),{recursive:!0}),Je(P,v[0].content,"utf-8"),console.log(` wrote ${P} (ResourcePage ${m.name} \u2192 ${y.name})`)}for(let m of g){let h=Ze(m,A,j,"outputPath");if(!h){console.warn(` [skip] ${m.name}: no outputPath declared`);continue}let k=gt(m,A,j,"scopeClass");if(!k){console.warn(` [skip] ${m.name}: no scopeClass declared`);continue}let y={publisher:k.publisher,package_:k.package_,name:k.name},w=ya(m,A,j,"scopeSources"),b=wa(m);if(!b){console.warn(` [skip] ${m.name}: no transformation declared`);continue}let v=Qe(r,b);if(!v){console.warn(` [skip] ${m.name}: transformation ${en(b)} not found`);continue}let P=await va(w,o),E=[],q=new Set;for(let fo of P){let go=await $a(fo,o,e,t),ho=ye(go,y);for(let Te of ho){let mn=`${Te.namespace}/${Te.name}`;q.has(mn)||(q.add(mn),E.push(Te))}}console.log(` ${m.name}: ${E.length} instance(s) of ${y.publisher}/${y.package_}/${y.name} across ${P.length} scope(s)`);let re=await f.run({transformation:v,instances:E,allKanonaks:r,repository:o,parser:e,objectParser:t,outputFormat:"html"});if(re.length===0){console.warn(` [skip] ${m.name}: transformation produced no artifacts`);continue}let Q=Ye(a,h);ke(Xe(Q),{recursive:!0}),Je(Q,re[0].content,"utf-8"),console.log(` wrote ${Q} (AggregateView ${m.name})`)}console.log("Site build complete.")}function Ze(n,e,t,o){for(let r of n.statement)if(r instanceof yt&&we(r,e,t,o))return r.object}function ya(n,e,t,o){for(let r of n.statement){if(!(r instanceof da)||!we(r,e,t,o))continue;let a=[];for(let s of r.object??[]){let i=s.value;typeof i=="string"&&a.push(i)}return a}return[]}function gt(n,e,t,o){for(let r of n.statement)if(r instanceof pa&&we(r,e,t,o)&&r.object instanceof ua)return{publisher:r.object.subject.publisher,package_:r.object.subject.package_,name:r.object.subject.name}}function wa(n){for(let e of n.statement)if(e instanceof ma&&we(e,A,j,"transformation"))return ba(e.object)}function ba(n){let e,t,o,r;for(let a of n.statement){if(!(a instanceof yt))continue;let s=a.predicate?.subject?.name;s==="publisher"?e=a.object:s==="package"?t=a.object:s==="version"?o=a.object:s==="name"&&(r=a.object)}if(!(!e||!t||!o||!r))return{publisher:e,package_:t,version:o,name:r}}function we(n,e,t,o){let r=n.predicate?.subject;return r?r.publisher===e&&r.package_===t&&r.name===o:!1}function Qe(n,e){let t=`${e.publisher}/${e.package_}@${e.version}`;for(let o of n)if(o instanceof kt&&o.name===e.name&&o.namespace===t)return o}function en(n){return`${n.publisher}/${n.package_}@${n.version}/${n.name}`}async function va(n,e){let t=[];for(let o of n){let r=o.indexOf("@*");if(r===-1){t.push(o);continue}let a=o.substring(0,r),s=a.indexOf("/");if(s===-1){t.push(o);continue}let i=a.substring(0,s),c=a.substring(s+1),l=await e.getDocumentsByNamespaceAsync(i,c);for(let p of l){let u=p.metadata.namespace_;if(!u)continue;let d=u.version;t.push(`${u.publisher}/${u.package_}@${d.major}.${d.minor}.${d.patch}`)}}return t}async function $a(n,e,t,o){let r=n.indexOf("/"),a=n.indexOf("@");if(r===-1||a===-1)return[];let s=n.substring(0,r),i=n.substring(r+1,a),c=n.substring(a+1),l=await e.getDocumentsByNamespaceAsync(s,i);for(let p of l){let u=p.metadata.namespace_?.version;if(u&&`${u.major}.${u.minor}.${u.patch}`===c){let d=new fa(p,e);return o.parseKanonaks(d)}}return[]}import{Command as Sa}from"commander";import{createServer as vt}from"http";import{watch as $t}from"fs";import{resolve as Pa}from"path";import{loadServerModel as St,route as Ca,RequestRouter as Ra}from"@kanonak-protocol/sdk/server";var nn={"Access-Control-Allow-Origin":"*"};function Pt(n,e){n.writeHead(e.status,{...e.headers,...nn}),n.end(e.body)}function bt(n,e){let t=n.renderStack,o=t.packages.filter(r=>r.origin==="missing").length;t.warnings.length===0?console.log(`[render-stack] \u2713 all ${t.packages.length} look-stack package(s) resolved \u2014 pages will render styled`):(console.error(`[render-stack] \u2717 ${t.warnings.length} problem(s)`+(o>0?` (${o} package(s) missing)`:"")+" \u2014 pages may render unstyled or incomplete"),console.error(`[render-stack] diagnose: http://localhost:${e}/.well-known/kanonak-render`))}function Ct(){return new Sa("serve").description("Serve Kanonak resources as a live origin \u2014 the same rendering `kanonak publish` produces, plus the machine-facing publisher endpoints, from one process. With --publisher it serves that single publisher (closed world, like the published site); without it, it serves any publisher per request path and links across publishers (open world). Use --watch for a dev preview.").option("--root <dir>","Workspace root to serve.",".").option("--port <number>","Port to listen on.","8080").option("--publisher <domain>","Serve ONLY this publisher (closed world \u2014 no links to other publishers, mirroring the published site). Omit for the open-world model.").option("--watch","Re-load on .kan.yml changes.",!1).action(async n=>{let e=Pa(n.root),t=Number(n.port);if(!Number.isInteger(t)||t<=0||t>65535)throw new Error(`Invalid --port "${n.port}".`);if(n.publisher){let o;try{o=await St(e,{publisher:n.publisher,log:console.log})}catch(r){console.error(`[error] ${r.message}`),process.exitCode=1;return}xa(e,t,n,o)}else _a(e,t,n)})}function xa(n,e,t,o){let r=o;bt(r,e);let a=async s=>{let i=Date.now();r=await St(n,{...t.publisher?{publisher:t.publisher}:{},log:console.log});let c=r.availablePublishers.filter(l=>l!==r.publisher);console.log(`[model] serving ${r.publisher} \u2014 ${r.pkgVersions.size} package(s)${s?` (${s})`:""} in ${Date.now()-i}ms`+(c.length?` [also in workspace: ${c.join(", ")} \u2014 switch with --publisher]`:"")),bt(r,e)};if(t.watch){let s;$t(n,{recursive:!0},(i,c)=>{!c||!String(c).endsWith(".kan.yml")||(clearTimeout(s),s=setTimeout(()=>{a(`changed: ${c}`).catch(l=>console.error("[reload error]",l.message))},150))}),console.log("[watch] re-loading on .kan.yml changes")}vt(async(s,i)=>{let c="/";try{let l=new URL(s.url??"/","http://localhost");c=decodeURIComponent(l.pathname);let p=await Ca(r,c,s.headers.accept??"");Pt(i,p),console.log(`${p.status} ${s.method} ${c}${p.headers.Location?` -> ${p.headers.Location}`:""}`)}catch(l){i.writeHead(500,{"Content-Type":"text/plain; charset=utf-8",...nn}),i.end(String(l?.stack??l)),console.error(`500 ${c}: ${l?.message??l}`)}}).listen(e,()=>{console.log(`kanonak origin server \u2192 http://localhost:${e}/ (root=${n}, publisher=${r.publisher})`),console.log(`render diagnostics \u2192 http://localhost:${e}/.well-known/kanonak-render`)})}function _a(n,e,t){let o=new Ra({root:n,log:console.log});if(t.watch){let r;$t(n,{recursive:!0},(a,s)=>{!s||!String(s).endsWith(".kan.yml")||(clearTimeout(r),r=setTimeout(()=>{o.clearCache(),console.log("[watch] cleared model cache")},150))}),console.log("[watch] clearing model cache on .kan.yml changes")}vt(async(r,a)=>{let s="/";try{let i=new URL(r.url??"/","http://localhost");s=decodeURIComponent(i.pathname);let c=await o.handle(s,r.headers.accept??"");Pt(a,c),console.log(`${c.status} ${r.method} ${s}${c.headers.Location?` -> ${c.headers.Location}`:""}`)}catch(i){a.writeHead(502,{"Content-Type":"text/plain; charset=utf-8",...nn}),a.end(String(i?.message??i)),console.error(`502 ${s}: ${i?.message??i}`)}}).listen(e,()=>{console.log(`kanonak request-driven origin \u2192 http://localhost:${e}/ (root=${n})`),console.log("[request-driven] no publisher pinned \u2014 derive per path: /{publisher}/{package}/{version}/{resource}"),console.log("[request-driven] render diagnostics per publisher: /{publisher}/.well-known/kanonak-render")})}import{Command as Ka}from"commander";import{resolve as Ia,join as Ea}from"path";import{loadLockFile as Ta,computeIntegrity as tn}from"@kanonak-protocol/sdk";function Rt(){return new Ka("verify").description("Verify cached packages against kanonak.lock (the cache is untrusted). With --remote, also re-fetch each package from its origin and compare. Exits non-zero on any integrity mismatch.").option("--workspace <dir>","Workspace directory containing kanonak.lock.",process.cwd()).option("--remote","Also re-fetch each package from its recorded origin and verify.",!1).action(async n=>{await Aa(n)})}function Da(n){let e=n.indexOf("/");if(!(e<0))return{publisher:n.slice(0,e),package_:n.slice(e+1)}}async function Aa(n){let e=Ia(n.workspace),t=Ta(Ea(e,"kanonak.lock"));t||(console.error(`No kanonak.lock found in ${e} \u2014 nothing to verify (run \`kanonak install\` first).`),process.exit(1));let o=new I,r=Object.entries(t.packages);console.log(`Verifying ${r.length} locked package(s) against kanonak.lock${n.remote?" (cache + origin)":" (cache)"}\u2026`);let a=0,s=0,i=0,c=0;for(let[p,u]of r){let d=Da(p);if(!d){console.error(` \u2717 ${p} \u2014 unparseable lock key`),s+=1;continue}let{publisher:g,package_:f}=d,m=o.get(g,f,u.version);if(m===null?(console.warn(` ? ${p} \u2014 not in cache`),i+=1):tn(m)===u.integrity?a+=1:(console.error(` \u2717 ${p} \u2014 CACHE does not match lock`),console.error(` lock: ${u.integrity}`),console.error(` cache: ${tn(m)}`),s+=1),n.remote){let h=await ja(p,u);h?(console.error(h),s+=1):c+=1}}let l=[`${a} cache OK`];n.remote&&l.push(`${c} origin OK`),i&&l.push(`${i} not cached`),l.push(`${s} mismatch`),console.log(`
|
|
142
|
-
${l.join(", ")}.`),s>0&&(console.error("Integrity verification FAILED \u2014 a package does not match the committed lock."),process.exit(1)),console.log("Integrity verified.")}async function ja(n,e){try{let t=await fetch(e.resolved);if(!t.ok)return` \u2717 ${n} \u2014 origin fetch ${t.status} from ${e.resolved}`;let o=await t.text(),r=
|
|
141
|
+
`}import{Command as ht}from"commander";import{writeFileSync as Ye,mkdirSync as ke}from"fs";import{join as Xe,resolve as ia,dirname as Ze}from"path";import{KanonakParser as ca,KanonakObjectParser as la,SubjectKanonak as kt,ReferenceStatement as pa,ReferenceKanonak as ua,ListStatement as da,StringStatement as yt,EmbeddedStatement as ma,findDerivation as dt}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as fa}from"@kanonak-protocol/sdk/uri-helpers";import{TransformationRunnerV3 as ga}from"@kanonak-protocol/sdk/transformations";import{findSubjectsByType as ye}from"@kanonak-protocol/sdk/uri-helpers";var A="kanonak.org",j="site",mt={publisher:"kanonak.org",package_:"formats",name:"html"},ft={publisher:"kanonak.org",package_:"derivation",name:"default"};function wt(){let n=new ht("site").description("Materialize a publisher's site \u2014 every StaticPage and AggregateView in the workspace gets rendered to its declared output path under the --out directory.");return n.addCommand(ha()),n}function ha(){return new ht("build").description("Walk the workspace, find every StaticPage and AggregateView, and write each materialized artifact to its declared output path under the --out directory.").option("--out <dir>","Output base directory.","./_site").action(async n=>{try{await ka(n)}catch(e){console.error(`Error: ${e.message}`),process.exit(1)}})}async function ka(n){let e=new ca,t=new la,o=await S(process.cwd(),e),r=await t.parseKanonaks(o),a=ia(n.out);ke(a,{recursive:!0});let s=await o.getAllDocumentsAsync(),i=new Set;for(let m of s){let k=m.metadata.namespace_?.toString();k&&i.add(k)}let c={publisher:A,package_:j,name:"StaticPage"},l={publisher:A,package_:j,name:"ResourcePage"},p={publisher:A,package_:j,name:"AggregateView"},u=ye(r,c).filter(m=>i.has(m.namespace||"")),d=ye(r,l).filter(m=>i.has(m.namespace||"")),f=ye(r,p).filter(m=>i.has(m.namespace||""));console.log(`Found ${u.length} StaticPage(s), ${d.length} ResourcePage(s), and ${f.length} AggregateView(s) in the workspace.`);let g=new ga;for(let m of u){let k=Qe(m,A,j,"outputPath")??`${m.name}.html`,h=dt(m,mt,ft,r);if(!h){console.warn(` [skip] ${m.name}: no HTML/default derivation found (check class hierarchy or universal-derivations is loaded)`);continue}let y=en(r,h.transformation);if(!y){console.warn(` [skip] ${m.name}: transformation ${nn(h.transformation)} not found`);continue}let w=await g.run({transformation:y,instances:[m],allKanonaks:r,repository:o,parser:e,objectParser:t,outputFormat:"html"});if(w.length===0){console.warn(` [skip] ${m.name}: transformation produced no artifacts`);continue}let b=Xe(a,k);ke(Ze(b),{recursive:!0}),Ye(b,w[0].content,"utf-8"),console.log(` wrote ${b} (StaticPage ${m.name})`)}for(let m of d){let k=Qe(m,A,j,"outputPath")??`${m.name}.html`,h=gt(m,A,j,"pageOf");if(!h){console.warn(` [skip] ${m.name}: no pageOf declared`);continue}let y=r.find(E=>E instanceof kt&&E.name===h.name&&(E.namespace||"").startsWith(`${h.publisher}/${h.package_}@`));if(!y){console.warn(` [skip] ${m.name}: pageOf target ${h.publisher}/${h.package_}/${h.name} not found`);continue}let w=dt(y,mt,ft,r);if(!w){console.warn(` [skip] ${m.name}: no derivation for target ${y.name}`);continue}let b=en(r,w.transformation);if(!b){console.warn(` [skip] ${m.name}: transformation ${nn(w.transformation)} not found`);continue}let v=await g.run({transformation:b,instances:[y],allKanonaks:r,repository:o,parser:e,objectParser:t,outputFormat:"html"});if(v.length===0){console.warn(` [skip] ${m.name}: transformation produced no artifacts`);continue}let $=Xe(a,k);ke(Ze($),{recursive:!0}),Ye($,v[0].content,"utf-8"),console.log(` wrote ${$} (ResourcePage ${m.name} \u2192 ${y.name})`)}for(let m of f){let k=Qe(m,A,j,"outputPath");if(!k){console.warn(` [skip] ${m.name}: no outputPath declared`);continue}let h=gt(m,A,j,"scopeClass");if(!h){console.warn(` [skip] ${m.name}: no scopeClass declared`);continue}let y={publisher:h.publisher,package_:h.package_,name:h.name},w=ya(m,A,j,"scopeSources"),b=wa(m);if(!b){console.warn(` [skip] ${m.name}: no transformation declared`);continue}let v=en(r,b);if(!v){console.warn(` [skip] ${m.name}: transformation ${nn(b)} not found`);continue}let $=await va(w,o),E=[],q=new Set;for(let fo of $){let go=await $a(fo,o,e,t),ho=ye(go,y);for(let Te of ho){let fn=`${Te.namespace}/${Te.name}`;q.has(fn)||(q.add(fn),E.push(Te))}}console.log(` ${m.name}: ${E.length} instance(s) of ${y.publisher}/${y.package_}/${y.name} across ${$.length} scope(s)`);let re=await g.run({transformation:v,instances:E,allKanonaks:r,repository:o,parser:e,objectParser:t,outputFormat:"html"});if(re.length===0){console.warn(` [skip] ${m.name}: transformation produced no artifacts`);continue}let Q=Xe(a,k);ke(Ze(Q),{recursive:!0}),Ye(Q,re[0].content,"utf-8"),console.log(` wrote ${Q} (AggregateView ${m.name})`)}console.log("Site build complete.")}function Qe(n,e,t,o){for(let r of n.statement)if(r instanceof yt&&we(r,e,t,o))return r.object}function ya(n,e,t,o){for(let r of n.statement){if(!(r instanceof da)||!we(r,e,t,o))continue;let a=[];for(let s of r.object??[]){let i=s.value;typeof i=="string"&&a.push(i)}return a}return[]}function gt(n,e,t,o){for(let r of n.statement)if(r instanceof pa&&we(r,e,t,o)&&r.object instanceof ua)return{publisher:r.object.subject.publisher,package_:r.object.subject.package_,name:r.object.subject.name}}function wa(n){for(let e of n.statement)if(e instanceof ma&&we(e,A,j,"transformation"))return ba(e.object)}function ba(n){let e,t,o,r;for(let a of n.statement){if(!(a instanceof yt))continue;let s=a.predicate?.subject?.name;s==="publisher"?e=a.object:s==="package"?t=a.object:s==="version"?o=a.object:s==="name"&&(r=a.object)}if(!(!e||!t||!o||!r))return{publisher:e,package_:t,version:o,name:r}}function we(n,e,t,o){let r=n.predicate?.subject;return r?r.publisher===e&&r.package_===t&&r.name===o:!1}function en(n,e){let t=`${e.publisher}/${e.package_}@${e.version}`;for(let o of n)if(o instanceof kt&&o.name===e.name&&o.namespace===t)return o}function nn(n){return`${n.publisher}/${n.package_}@${n.version}/${n.name}`}async function va(n,e){let t=[];for(let o of n){let r=o.indexOf("@*");if(r===-1){t.push(o);continue}let a=o.substring(0,r),s=a.indexOf("/");if(s===-1){t.push(o);continue}let i=a.substring(0,s),c=a.substring(s+1),l=await e.getDocumentsByNamespaceAsync(i,c);for(let p of l){let u=p.metadata.namespace_;if(!u)continue;let d=u.version;t.push(`${u.publisher}/${u.package_}@${d.major}.${d.minor}.${d.patch}`)}}return t}async function $a(n,e,t,o){let r=n.indexOf("/"),a=n.indexOf("@");if(r===-1||a===-1)return[];let s=n.substring(0,r),i=n.substring(r+1,a),c=n.substring(a+1),l=await e.getDocumentsByNamespaceAsync(s,i);for(let p of l){let u=p.metadata.namespace_?.version;if(u&&`${u.major}.${u.minor}.${u.patch}`===c){let d=new fa(p,e);return o.parseKanonaks(d)}}return[]}import{Command as Sa}from"commander";import{createServer as vt}from"http";import{watch as $t}from"fs";import{resolve as Pa}from"path";import{loadServerModel as St,route as Ca,RequestRouter as Ra}from"@kanonak-protocol/sdk/server";var tn={"Access-Control-Allow-Origin":"*"};function Pt(n,e){n.writeHead(e.status,{...e.headers,...tn}),n.end(e.body)}function bt(n,e){let t=n.renderStack,o=t.packages.filter(r=>r.origin==="missing").length;t.warnings.length===0?console.log(`[render-stack] \u2713 all ${t.packages.length} look-stack package(s) resolved \u2014 pages will render styled`):(console.error(`[render-stack] \u2717 ${t.warnings.length} problem(s)`+(o>0?` (${o} package(s) missing)`:"")+" \u2014 pages may render unstyled or incomplete"),console.error(`[render-stack] diagnose: http://localhost:${e}/.well-known/kanonak-render`))}function Ct(){return new Sa("serve").description("Serve Kanonak resources as a live origin \u2014 the same rendering `kanonak publish` produces, plus the machine-facing publisher endpoints, from one process. With --publisher it serves that single publisher (closed world, like the published site); without it, it serves any publisher per request path and links across publishers (open world). Use --watch for a dev preview.").option("--root <dir>","Workspace root to serve.",".").option("--port <number>","Port to listen on.","8080").option("--publisher <domain>","Serve ONLY this publisher (closed world \u2014 no links to other publishers, mirroring the published site). Omit for the open-world model.").option("--watch","Re-load on .kan.yml changes.",!1).action(async n=>{let e=Pa(n.root),t=Number(n.port);if(!Number.isInteger(t)||t<=0||t>65535)throw new Error(`Invalid --port "${n.port}".`);if(n.publisher){let o;try{o=await St(e,{publisher:n.publisher,log:console.log})}catch(r){console.error(`[error] ${r.message}`),process.exitCode=1;return}xa(e,t,n,o)}else _a(e,t,n)})}function xa(n,e,t,o){let r=o;bt(r,e);let a=async s=>{let i=Date.now();r=await St(n,{...t.publisher?{publisher:t.publisher}:{},log:console.log});let c=r.availablePublishers.filter(l=>l!==r.publisher);console.log(`[model] serving ${r.publisher} \u2014 ${r.pkgVersions.size} package(s)${s?` (${s})`:""} in ${Date.now()-i}ms`+(c.length?` [also in workspace: ${c.join(", ")} \u2014 switch with --publisher]`:"")),bt(r,e)};if(t.watch){let s;$t(n,{recursive:!0},(i,c)=>{!c||!String(c).endsWith(".kan.yml")||(clearTimeout(s),s=setTimeout(()=>{a(`changed: ${c}`).catch(l=>console.error("[reload error]",l.message))},150))}),console.log("[watch] re-loading on .kan.yml changes")}vt(async(s,i)=>{let c="/";try{let l=new URL(s.url??"/","http://localhost");c=decodeURIComponent(l.pathname);let p=await Ca(r,c,s.headers.accept??"");Pt(i,p),console.log(`${p.status} ${s.method} ${c}${p.headers.Location?` -> ${p.headers.Location}`:""}`)}catch(l){i.writeHead(500,{"Content-Type":"text/plain; charset=utf-8",...tn}),i.end(String(l?.stack??l)),console.error(`500 ${c}: ${l?.message??l}`)}}).listen(e,()=>{console.log(`kanonak origin server \u2192 http://localhost:${e}/ (root=${n}, publisher=${r.publisher})`),console.log(`render diagnostics \u2192 http://localhost:${e}/.well-known/kanonak-render`)})}function _a(n,e,t){let o=new Ra({root:n,log:console.log});if(t.watch){let r;$t(n,{recursive:!0},(a,s)=>{!s||!String(s).endsWith(".kan.yml")||(clearTimeout(r),r=setTimeout(()=>{o.clearCache(),console.log("[watch] cleared model cache")},150))}),console.log("[watch] clearing model cache on .kan.yml changes")}vt(async(r,a)=>{let s="/";try{let i=new URL(r.url??"/","http://localhost");s=decodeURIComponent(i.pathname);let c=await o.handle(s,r.headers.accept??"");Pt(a,c),console.log(`${c.status} ${r.method} ${s}${c.headers.Location?` -> ${c.headers.Location}`:""}`)}catch(i){a.writeHead(502,{"Content-Type":"text/plain; charset=utf-8",...tn}),a.end(String(i?.message??i)),console.error(`502 ${s}: ${i?.message??i}`)}}).listen(e,()=>{console.log(`kanonak request-driven origin \u2192 http://localhost:${e}/ (root=${n})`),console.log("[request-driven] no publisher pinned \u2014 derive per path: /{publisher}/{package}/{version}/{resource}"),console.log("[request-driven] render diagnostics per publisher: /{publisher}/.well-known/kanonak-render")})}import{Command as Ka}from"commander";import{resolve as Ia,join as Ea}from"path";import{loadLockFile as Ta,computeIntegrity as on}from"@kanonak-protocol/sdk";function Rt(){return new Ka("verify").description("Verify cached packages against kanonak.lock (the cache is untrusted). With --remote, also re-fetch each package from its origin and compare. Exits non-zero on any integrity mismatch.").option("--workspace <dir>","Workspace directory containing kanonak.lock.",process.cwd()).option("--remote","Also re-fetch each package from its recorded origin and verify.",!1).action(async n=>{await Aa(n)})}function Da(n){let e=n.indexOf("/");if(!(e<0))return{publisher:n.slice(0,e),package_:n.slice(e+1)}}async function Aa(n){let e=Ia(n.workspace),t=Ta(Ea(e,"kanonak.lock"));t||(console.error(`No kanonak.lock found in ${e} \u2014 nothing to verify (run \`kanonak install\` first).`),process.exit(1));let o=new I,r=Object.entries(t.packages);console.log(`Verifying ${r.length} locked package(s) against kanonak.lock${n.remote?" (cache + origin)":" (cache)"}\u2026`);let a=0,s=0,i=0,c=0;for(let[p,u]of r){let d=Da(p);if(!d){console.error(` \u2717 ${p} \u2014 unparseable lock key`),s+=1;continue}let{publisher:f,package_:g}=d,m=o.get(f,g,u.version);if(m===null?(console.warn(` ? ${p} \u2014 not in cache`),i+=1):on(m)===u.integrity?a+=1:(console.error(` \u2717 ${p} \u2014 CACHE does not match lock`),console.error(` lock: ${u.integrity}`),console.error(` cache: ${on(m)}`),s+=1),n.remote){let k=await ja(p,u);k?(console.error(k),s+=1):c+=1}}let l=[`${a} cache OK`];n.remote&&l.push(`${c} origin OK`),i&&l.push(`${i} not cached`),l.push(`${s} mismatch`),console.log(`
|
|
142
|
+
${l.join(", ")}.`),s>0&&(console.error("Integrity verification FAILED \u2014 a package does not match the committed lock."),process.exit(1)),console.log("Integrity verified.")}async function ja(n,e){try{let t=await fetch(e.resolved);if(!t.ok)return` \u2717 ${n} \u2014 origin fetch ${t.status} from ${e.resolved}`;let o=await t.text(),r=on(o);return r===e.integrity?void 0:` \u2717 ${n} \u2014 ORIGIN does not match lock (${e.resolved})
|
|
143
143
|
lock: ${e.integrity}
|
|
144
144
|
origin: ${r}`}catch(t){return` \u2717 ${n} \u2014 origin fetch failed: ${t.message}`}}import{Command as Oa}from"commander";import{resolve as Na,join as La}from"path";import{homedir as Fa}from"os";import{readFileSync as Ua}from"fs";import{KanonakParser as xt,KanonakObjectParser as Ma,PublisherIndex as _t,CredentialStore as Kt,createAuthenticatedFetch as It,FileSystemKanonakDocumentRepository as Va,Reasoner as Ha,KanonakVocabulary as za,getGlobalCachePath as Ba,makeUriKey as qa,resolveDisplayValue as Wa,subjectUri as Ga,contextTypesOf as Ja,collectKanonakFiles as Ya}from"@kanonak-protocol/sdk";import{SearchIndex as Xa}from"@kanonak-protocol/sdk/search";function Za(n){let e=n.split("/");if(e.length!==3)return null;let[t,o,r]=e;return!t||!o||!r?null:{publisher:t,package_:o,name:r}}function Et(){return new Oa("search").description("Semantic-search your workspace (-q), browse a publisher's catalogue, or find instances of a class").argument("[publisher]","Publisher domain (e.g. kanonak.org) \u2014 for the catalogue / --type modes").option("-q, --query <text>","Semantically search the LOCAL workspace for resources matching free-text keywords").option("--root <dir>","Workspace root for -q",".").option("--scope <prefix>","Limit -q to namespaces starting with this prefix (e.g. kanonak.org/look@)").option("--limit <n>","Max results for -q","20").option("--format <fmt>","Output for -q: text | json","text").option("--all","For -q: index imported packages too (default: only your workspace's own resources)",!1).option("--type <uri>","Class URI to search for, in publisher/package/Name form").action(async(n,e)=>{if(e.query){await Qa(e);return}n||(console.error(`Nothing to search. Either:
|
|
145
145
|
\u2022 semantic-search your workspace: kanonak search -q "<keywords>"
|
|
146
146
|
\u2022 browse a publisher's catalogue: kanonak search <publisher>
|
|
147
|
-
\u2022 find instances of a class: kanonak search <publisher> --type <pub>/<pkg>/<Name>`),process.exit(1)),e.type?await ni(n,e.type):await ei(n)})}async function Qa(n){let e=Na(n.root??"."),t=new xt,o;try{let p=await
|
|
148
|
-
`)}catch(p){console.error(p.message),process.exit(1)}s.size===0&&(console.error(n.all?`No resources found in the workspace at ${e}.`:`No resources defined in the workspace at ${e}. (It imports packages but defines none of its own \u2014 use --all to also search imported packages.)`),process.exit(1));let i=Number(n.limit)||20,l=(await a.query(n.query,{scope:n.scope,limit:i})).map(p=>{let u=Wa(o,p.subject),d=Ga(p.subject),
|
|
147
|
+
\u2022 find instances of a class: kanonak search <publisher> --type <pub>/<pkg>/<Name>`),process.exit(1)),e.type?await ni(n,e.type):await ei(n)})}async function Qa(n){let e=Na(n.root??"."),t=new xt,o;try{let p=await S(e,t);o=await new Ma().parseKanonaks(p)}catch(p){console.error(`Failed to load workspace at ${e}: ${p.message}`),process.exit(1)}let r={cacheDir:La(Fa(),".kanonak","search"),onProgress:(p,u)=>process.stderr.write(`\r embedding ${p}/${u}\u2026 `)};if(!n.all){let p=new Set;for(let u of Ya(e))try{let d=t.parse(Ua(u,"utf-8")).metadata?.namespace_;d?.version&&p.add(`${d.publisher}/${d.package_}@${d.version.major}.${d.version.minor}.${d.version.patch}`)}catch{}r.include=u=>p.has(u.namespace??"")}let a=new Xa,s;try{s=await a.build(o,r),s.size>0&&!s.cached&&process.stderr.write(`
|
|
148
|
+
`)}catch(p){console.error(p.message),process.exit(1)}s.size===0&&(console.error(n.all?`No resources found in the workspace at ${e}.`:`No resources defined in the workspace at ${e}. (It imports packages but defines none of its own \u2014 use --all to also search imported packages.)`),process.exit(1));let i=Number(n.limit)||20,l=(await a.query(n.query,{scope:n.scope,limit:i})).map(p=>{let u=Wa(o,p.subject),d=Ga(p.subject),f=Ja(p.subject)[0]?.name;return{uri:d?`${d.publisher}/${d.package_}/${d.name}`:p.subject.name,label:u.label,type:f??null,score:Number(p.score.toFixed(4))}});if(n.format==="json"){console.log(JSON.stringify(l,null,2));return}if(l.length===0){console.log(`No matches for "${n.query}".`);return}console.log(`Results for "${n.query}":
|
|
149
149
|
`);for(let p of l)console.log(` ${p.score.toFixed(3)} ${p.label}${p.type?` (${p.type})`:""}`),console.log(` ${p.uri}`)}async function ei(n){let e=new Kt,t=It(e),o=new _t({fetchFn:t}),r;try{r=await o.listLatestPackages(n)}catch(s){console.error(`Failed to fetch publisher index for ${n}: ${s.message}`),process.exit(1)}if(r.length===0){console.log(`No packages found at ${n}.`);return}console.log(`Packages published by ${n}:
|
|
150
|
-
`);let a=r.reduce((s,i)=>Math.max(s,i.packageName.length),0);for(let{packageName:s,version:i}of r)console.log(` ${s.padEnd(a)} @${i}`)}async function ni(n,e){let t=Za(e);t||(console.error(`Invalid --type value: ${e}`),console.error("Expected: publisher/package/Name (e.g. kanonak.org/capabilities/Capability)"),process.exit(1));let o=new xt,r=new Kt,a=It(r),s=new _t({fetchFn:a}),i=new I,c=new Set,l=[];try{l=await s.listLatestPackages(n)}catch(
|
|
151
|
-
`),m.sort();for(let
|
|
152
|
-
`),process.stderr.write(
|
|
153
|
-
`)}}async function hi(n,e){if(n.endsWith(".kan.yml")){let i=ai(n);if(!ti(i))throw new Error(`kanonak hash: file not found: ${i}`);let c=oi(i,"utf-8");return{document:e.parse(c),source:i}}let t=ui(n);if(t.kind!=="package")throw new Error(`kanonak hash: expected a package address (publisher/package[@version]) or a path ending in .kan.yml; got ${t.kind} address "${n}"`);let r=await(await
|
|
150
|
+
`);let a=r.reduce((s,i)=>Math.max(s,i.packageName.length),0);for(let{packageName:s,version:i}of r)console.log(` ${s.padEnd(a)} @${i}`)}async function ni(n,e){let t=Za(e);t||(console.error(`Invalid --type value: ${e}`),console.error("Expected: publisher/package/Name (e.g. kanonak.org/capabilities/Capability)"),process.exit(1));let o=new xt,r=new Kt,a=It(r),s=new _t({fetchFn:a}),i=new I,c=new Set,l=[];try{l=await s.listLatestPackages(n)}catch(k){console.error(`Failed to fetch publisher index for ${n}: ${k.message}`),process.exit(1)}for(let{packageName:k,version:h}of l)c.add(`${n}|${k}|${h}`);await rn(s,i,a,"kanonak.org","core-rdf",c),await rn(s,i,a,"kanonak.org","core-owl",c),await rn(s,i,a,t.publisher,t.package_,c);for(let k of c){let[h,y,w]=k.split("|");if(!i.has(h,y,w))try{let b=await s.getPackageUrl(h,y,w),v=await a(b,h);if(!v.ok){console.error(`Warning: failed to fetch ${h}/${y}@${w}: ${v.status} ${v.statusText}`);continue}let $=await v.text();i.put(h,y,w,$)}catch(b){console.error(`Warning: failed to fetch ${h}/${y}@${w}: ${b.message}`)}}let p=new Va(Ba(),!0,o),d=await new Ha({vocabulary:new za}).reason(p),f=qa(t.publisher,t.package_,t.name),m=d.getInstancesOfClass(f).filter(k=>k!==f);if(m.length===0){console.log(`No instances of ${f} found in ${n}'s catalogue.`);return}console.log(`Instances of ${f} in ${n}'s catalogue:
|
|
151
|
+
`),m.sort();for(let k of m)console.log(` ${k}`)}async function rn(n,e,t,o,r,a){try{let s=await n.getHighestVersion(o,r);if(!s)return;a.add(`${o}|${r}|${s}`)}catch{}}import{existsSync as ti,readFileSync as oi}from"fs";import{dirname as ri,join as si,resolve as ai}from"path";import{KanonakObjectParser as ii,KanonakParser as ci,canonicalForm as li,canonicalHash as pi,parseKanonakAddress as ui,pickHighestDocument as di,loadLockFile as mi,LockAwareRepository as fi}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as gi}from"@kanonak-protocol/sdk/uri-helpers";async function Tt(n,e={}){let t=new ci,o=new ii,{document:r,source:a}=await hi(n,t),s=yi(a),i=await S(s,t),c=si(s,"kanonak.lock"),l=mi(c);if(e.frozen){if(!l)throw new Error(`kanonak hash --frozen: no kanonak.lock found at ${c}. Run 'kanonak install' to generate one.`);ki(r,l,c)}let p=l?new fi(i,l):i,u=new gi(r,p),d=await o.parseKanonaks(u),f=pi(d);if(console.log(f),e.verbose){let g=li(d);process.stderr.write(`# canonical form for ${a}
|
|
152
|
+
`),process.stderr.write(g),process.stderr.write(`
|
|
153
|
+
`)}}async function hi(n,e){if(n.endsWith(".kan.yml")){let i=ai(n);if(!ti(i))throw new Error(`kanonak hash: file not found: ${i}`);let c=oi(i,"utf-8");return{document:e.parse(c),source:i}}let t=ui(n);if(t.kind!=="package")throw new Error(`kanonak hash: expected a package address (publisher/package[@version]) or a path ending in .kan.yml; got ${t.kind} address "${n}"`);let r=await(await S(process.cwd(),e)).getDocumentsByNamespaceAsync(t.publisher,t.package_);if(r.length===0)throw new Error(`kanonak hash: could not find ${n} in local workspace, global cache, or publisher HTTP`);let a=t.version?`${t.version.major}.${t.version.minor}.${t.version.patch}`:void 0,s=di(r,a?{requestedVersion:a}:{});if(!s.chosen){let i=a?`@${a}`:"";throw new Error(`kanonak hash: no version of ${t.publisher}/${t.package_}${i} available across local/cache/HTTP`)}return{document:s.chosen,source:n}}function ki(n,e,t){let o=new Set(["kanonak.org/core-kanonak"]);for(let a of n.metadata.allImports??[])o.add(`${a.publisher??"kanonak.org"}/${a.packageName}`);let r=[...o].filter(a=>!e.packages[a]);if(r.length>0)throw new Error(`kanonak hash --frozen: kanonak.lock (${t}) is missing pins for: ${r.join(", ")}. Run 'kanonak install' to refresh the lock.`)}function yi(n){return n.endsWith(".kan.yml")?F(ri(n)):process.cwd()}import{Command as Dt}from"commander";function X(n){return`${R}/${x}/${n}`}var sn={[X("fetch-and-deploy")]:(n,e,t,o)=>n.add(e,t[0],o),[X("remove-deployed")]:(n,e,t,o)=>n.remove(e,t[0],o),[X("list-deployed")]:(n,e,t,o)=>n.list(e,o),[X("update-deployed")]:(n,e,t,o)=>n.update(e,t[0],o),[X("search-available")]:(n,e,t,o)=>n.search(e,o),[X("show-info")]:(n,e,t,o)=>n.info(e,t[0],o)};function At(n,e,t){for(let o of e){let r=new Dt(o.commandName).description(o.description.trim());for(let a of o.commands){let s=r.command(wi(a));s.description(a.description);for(let i of a.arguments)if(i.isOption){let c=i.defaultValue!=null?`--${i.argumentName} [value]`:`--${i.argumentName} <value>`;s.option(c,"",i.defaultValue)}s.action(async(...i)=>{let c=bi(i);await vi(t,o,a,i,c)})}n.addCommand(r)}}function wi(n){let e=n.arguments.filter(t=>!t.isOption).map(t=>t.required?`<${t.argumentName}>`:`[${t.argumentName}]`);return[n.subcommandName,...e].join(" ")}function bi(n){for(let e=n.length-1;e>=0;e--)if(n[e]&&typeof n[e]=="object"&&!(n[e]instanceof Dt))return n[e];return{}}async function vi(n,e,t,o,r){let a=[];for(let i of o)if(typeof i=="string")a.push(i);else break;t.actionKey||(console.error(`Capability subcommand '${t.subcommandName}' has no resolved \`performs:\` Action. Every CapabilityCommand must declare a performs value pointing at a named Action instance from the capabilities ontology. Known actions: ${Object.keys(sn).join(", ")}.`),process.exit(1));let s=sn[t.actionKey];s||(console.error(`Capability subcommand '${t.subcommandName}' references unknown Action '${t.actionKey}'. Known actions: ${Object.keys(sn).join(", ")}.`),process.exit(1)),await s(n,e,a,r)}import{existsSync as jt,readFileSync as $i,statSync as Ot}from"fs";import{resolve as Si}from"path";import{KanonakObjectParser as Pi,PublisherIndex as an,PublisherConfigResolver as Ci,formatVersion as Nt,pickHighestDocument as Ri}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as Lt}from"@kanonak-protocol/sdk/uri-helpers";import{uriKey as Z,findSubjectsByType as Ft}from"@kanonak-protocol/sdk/uri-helpers";var be=class{constructor(e,t,o,r,a,s){this.fileCache=e;this.parser=t;this.publisherIndex=o;this.fetchFn=r;this.handlerRegistry=a;this.repository=s}fileCache;parser;publisherIndex;fetchFn;handlerRegistry;repository;objectParser=new Pi;async add(e,t,o){let r=this.tryLoadFromLocalFile(t);if(r){this.fileCache.put(r.publisher,r.packageName,r.version,r.content),await this.deployFromDoc(e,r.doc,r.publisher,r.packageName,r.version,void 0,`local file ${t}`,o);return}let a=T(t);a||(console.error(`Invalid package reference: "${t}"`),console.error(""),console.error("Accepted forms:"),console.error(" publisher/package (install all instances, latest version)"),console.error(" publisher/package@version (install all instances, pinned)"),console.error(" publisher/package/instance (install one instance, latest version)"),console.error(" publisher/package@version/instance (install one instance, pinned)"),console.error(" ./path/to/file.kan.yml (install all instances from a local document)"),console.error(""),console.error("Tip: copy a URI directly from `kanonak "+e.commandName+" search`."),process.exit(1));let{publisher:s,packageName:i,version:c,instanceName:l}=a,p=await this.repository.getDocumentsByNamespaceAsync(s,i);if(p.length>0){let f=Ri(p,{requestedVersion:c??void 0});if(f.chosen){let g=f.chosen.metadata.namespace_;(!g||!g.version)&&(console.error(`Document for ${s}/${i} is missing namespace metadata.`),process.exit(1));let m=Nt(g.version);this.fileCache.put(s,i,m,this.parser.save(f.chosen)),await this.deployFromDoc(e,f.chosen,s,i,m,l??void 0,`${s}/${i}@${m}`,o);return}}let u=c??await this.publisherIndex.getHighestVersion(s,i);u||(console.error(`Could not resolve version for "${s}/${i}".`),await this.printPackageSuggestions(s,i,e.commandName),process.exit(1));let d=this.fileCache.get(s,i,u);if(!d){let f=await this.publisherIndex.getPackageUrl(s,i,u),g=await this.fetchFn(f,s);g.ok||(g.status===404?(console.error(`Package ${s}/${i}@${u} not found at ${f}.`),console.error(`The publisher index lists this version but the file is missing \u2014 it may have been unpublished. Try \`kanonak ${e.commandName} search\` to see the current catalogue.`)):console.error(`Failed to fetch ${f} (${g.status} ${g.statusText}).`),process.exit(1)),d=await g.text(),this.fileCache.put(s,i,u,d)}await this.deployFromDoc(e,this.parser.parse(d),s,i,u,l??void 0,`${s}/${i}@${u}`,o)}tryLoadFromLocalFile(e){if(!(/^[.\/\\]/.test(e)||/^[A-Za-z]:[\\/]/.test(e)||e.endsWith(".kan.yml")||jt(e)&&Ot(e).isFile()))return;let o=Si(e);(!jt(o)||!Ot(o).isFile())&&(console.error(`Local file not found: ${e}`),process.exit(1));let r=$i(o,"utf-8"),a=this.parser.parse(r),s=a.metadata.namespace_;return(!s||!s.version)&&(console.error(`${e} has no namespace metadata; cannot determine publisher/package/version. A Kanonak document must declare \`type: Package\` with publisher and version.`),process.exit(1)),{doc:a,content:r,publisher:s.publisher,packageName:s.package_,version:Nt(s.version)}}async deployFromDoc(e,t,o,r,a,s,i,c){let l=new Lt(t,this.repository),p=await this.objectParser.parseKanonaks(l),u=Ut(e.managesTypeKey);u||(console.error(`Capability "${e.commandName}" has no resolved managesType.`),process.exit(1));let d=Ft(p,u);d.length===0&&(console.error(`${i} does not contain any ${Z(u)} instances.`),process.exit(1));let f=d;if(s&&(f=d.filter(k=>k.name===s),f.length===0)){console.error(`Instance "${s}" not found in ${i}.`),console.error(""),console.error(`Available ${Z(u)} instance(s):`);for(let k of d)console.error(` - ${k.name}`);console.error(""),console.error("To install all of them, drop the instance suffix:"),console.error(` kanonak ${e.commandName} add ${o}/${r}`),process.exit(1)}let g=s?`${s} from ${i}`:`${f.length} ${Z(u)} instance(s) from ${i}`;console.log(`Installing ${g}...`);let m=this.handlerRegistry.get(e.deploymentTargetKey);m?await m.deploy({instances:f,allKanonaks:p,repository:this.repository,options:c}):console.log(`Cached ${o}/${r}@${a} (no deployment handler for "${e.deploymentTargetKey}")`)}async printPackageSuggestions(e,t,o){let r;try{r=await this.publisherIndex.listLatestPackages(e)}catch{return}let a=r.map(s=>({name:s.packageName,version:s.version,score:xi(t,s.packageName)})).filter(s=>s.score>0).sort((s,i)=>i.score-s.score).slice(0,5);if(a.length!==0){console.error(""),console.error("Did you mean one of:");for(let s of a)console.error(` kanonak ${o} add ${e}/${s.name}`)}}async remove(e,t,o){await this.requireHandler(e).undeploy(t,{repository:this.repository,options:o})}async list(e,t){let r=await this.requireHandler(e).list({repository:this.repository,options:t}),a=e.managesTypeKey||"managed";if(r.length===0){console.log(`No ${a} instances installed.`);return}console.log(`Installed ${a} instances:
|
|
154
154
|
`);for(let s of r){let i=s.publisher?`${s.publisher}/${s.package_}@${s.version}`:"unmanaged";console.log(` ${s.name} (${i}) ${s.path}`)}}async update(e,t,o){let a=await this.requireHandler(e).list({repository:this.repository,options:o}),s=t?a.filter(c=>c.name===t):a.filter(c=>c.publisher);if(s.length===0){console.log(t?`"${t}" not found.`:"No managed instances to update.");return}let i=0;for(let c of s){let l=await this.publisherIndex.getHighestVersion(c.publisher,c.package_);!l||l===c.version||(console.log(`Updating ${c.name}: ${c.version} \u2192 ${l}`),await this.add(e,`${c.publisher}/${c.package_}@${l}`,o),i++)}console.log(i===0?"All instances are up to date.":`
|
|
155
155
|
Updated ${i} instance(s).`)}async search(e,t){let o=t.publisher??"kanonak.org",r=Ut(e.managesTypeKey);if(!r){console.error("Capability has no resolved managesType; nothing to search for.");return}console.log(`Searching ${o} for ${Z(r)} instances...
|
|
156
|
-
`);let a=new Ci(this.fetchFn),s=await a.getConfig(o),i=a.resolveIndexUrl(o,s),c=await this.fetchFn(i,o);if(!c.ok){console.error(`Failed to fetch package index from ${o}.`);return}let l=await c.text(),p=
|
|
157
|
-
`);let
|
|
158
|
-
`;function Pe(n){let e=Yt(n,Zt);if(!Li(e))return{version:"1",lastUpdated:new Date().toISOString(),skills:{}};let t=Oi(e,"utf-8"),o=Xt.load(t);return!o||typeof o!="object"||o.version!=="1"?{version:"1",lastUpdated:new Date().toISOString(),skills:{}}:{version:"1",lastUpdated:o.lastUpdated??new Date().toISOString(),skills:o.skills??{}}}function
|
|
159
|
-
`;function Ke(n){let e=co(n,po);if(!mc(e))return{version:"1",lastUpdated:new Date().toISOString(),agents:{}};let t=uc(e,"utf-8"),o=lo.load(t);return!o||typeof o!="object"||o.version!=="1"?{version:"1",lastUpdated:new Date().toISOString(),agents:{}}:{version:"1",lastUpdated:o.lastUpdated??new Date().toISOString(),agents:o.agents??{}}}function
|
|
156
|
+
`);let a=new Ci(this.fetchFn),s=await a.getConfig(o),i=a.resolveIndexUrl(o,s),c=await this.fetchFn(i,o);if(!c.ok){console.error(`Failed to fetch package index from ${o}.`);return}let l=await c.text(),p=an.parseIndex(l);if(p.size===0){console.error(`No packages found for publisher "${o}".`);return}let u=new Map;for(let[m,k]of p){let h=[...k].sort((y,w)=>{let b=an.parseVersion(y),v=an.parseVersion(w);return!b||!v?0:b.major!==v.major?v.major-b.major:b.minor!==v.minor?v.minor-b.minor:v.patch-b.patch});u.set(m,h[0])}let d=[],f=0;for(let[m,k]of u){let h=this.fileCache.get(o,m,k);if(!h)try{let y=await this.publisherIndex.getPackageUrl(o,m,k),w=await this.fetchFn(y,o);if(!w.ok)continue;h=await w.text(),this.fileCache.put(o,m,k,h)}catch{continue}try{let y=this.parser.parse(h),w=new Lt(y,this.repository),b=await this.objectParser.parseKanonaks(w),v=Ft(b,r);for(let $ of v)d.push({publisher:o,package_:m,version:k,subject:$});f++}catch{continue}}if(d.length===0){console.log(`No ${Z(r)} instances found across ${f} package(s).`);return}console.log(`Found ${d.length} ${Z(r)} instance(s) in ${f} package(s):
|
|
157
|
+
`);let g=new Map;for(let m of d){let k=`${m.publisher}/${m.package_}@${m.version}`,h=g.get(k);h?h.push(m):g.set(k,[m])}for(let[m,k]of g){let h=k[0],y=`${h.publisher}/${h.package_}`,w=k.length===1;console.log(` ${m}`),console.log(` ${k.length} skill${w?"":"s"}:`);for(let b of k)console.log(` - ${b.subject.name}`);if(console.log(""),w)console.log(" install:"),console.log(` kanonak ${e.commandName} add ${y}/${h.subject.name}`);else{console.log(" install one of them:");for(let b of k)console.log(` kanonak ${e.commandName} add ${y}/${b.subject.name}`);console.log(" install all of them:"),console.log(` kanonak ${e.commandName} add ${y}`)}console.log("")}}async info(e,t,o){let s=(await this.requireHandler(e).list({repository:this.repository,options:o})).find(i=>i.name===t);s||(console.error(`"${t}" is not installed.`),process.exit(1)),console.log(`Name: ${s.name}`),console.log(`Type: ${e.managesTypeKey||"n/a"}`),console.log(`Publisher: ${s.publisher||"unmanaged"}`),console.log(`Package: ${s.package_||"n/a"}`),console.log(`Version: ${s.version||"n/a"}`),console.log(`Path: ${s.path}`)}requireHandler(e){let t=this.handlerRegistry.get(e.deploymentTargetKey);return t||(console.error(`No deployment handler registered for "${e.deploymentTargetKey}". Ensure the capability's deploymentTarget resolves to a DeploymentTarget instance whose URI is registered in the CLI at startup.`),process.exit(1)),t}};function Ut(n){if(!n)return;let e=n.split("/");if(e.length===3)return{publisher:e[0],package_:e[1],name:e[2]}}function xi(n,e){let t=n.toLowerCase(),o=e.toLowerCase();if(t===o)return 100;if(o.includes(t)||t.includes(o))return 50;let r=new Set(t.split("-").filter(Boolean)),a=new Set(o.split("-").filter(Boolean)),s=0;for(let c of r)a.has(c)&&s++;if(s>0)return s*10;let i=_i(t,o);return i===0?100:i<=2?Math.max(1,20-i*5):0}function _i(n,e){if(n===e)return 0;if(n.length===0)return e.length;if(e.length===0)return n.length;let t=new Array(e.length+1),o=new Array(e.length+1);for(let r=0;r<=e.length;r++)t[r]=r;for(let r=0;r<n.length;r++){o[0]=r+1;for(let a=0;a<e.length;a++){let s=n[r]===e[a]?0:1;o[a+1]=Math.min(o[a]+1,t[a+1]+1,t[a]+s)}for(let a=0;a<=e.length;a++)t[a]=o[a]}return t[e.length]}var ve=class{handlers=new Map;register(e,t){this.handlers.set(e,t)}get(e){return this.handlers.get(e)??null}};import{mkdirSync as Ui,rmSync as Mi,existsSync as Ce,readdirSync as Vi,writeFileSync as Hi}from"fs";import{join as Re}from"path";import{KanonakObjectParser as zi,KanonakParser as Bi,computeIntegrity as qi,compareVersions as Wi}from"@kanonak-protocol/sdk";import{findSubjectsByType as Gi}from"@kanonak-protocol/sdk/uri-helpers";import{SingleDocumentRepository as Ji}from"@kanonak-protocol/sdk/uri-helpers";import{TransformationRunnerV3 as Yi}from"@kanonak-protocol/sdk/transformations";import{TX_V3 as Xi}from"@kanonak-protocol/sdk/transformations";import{join as cn}from"path";import{homedir as Mt}from"os";import{KanonakObjectParser as Ki}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as Ii}from"@kanonak-protocol/sdk/uri-helpers";import{findSubjectsByType as Ht,getStringValue as $e}from"@kanonak-protocol/sdk/uri-helpers";var V="kanonak.org",H="agent-skills",zt={publisher:V,package_:H,name:"Client"},Bt={publisher:V,package_:H,name:"clientId"},Ei={publisher:V,package_:H,name:"projectSkillDir"},Ti={publisher:V,package_:H,name:"userSkillDir"},Di={publisher:V,package_:H,name:"skillFileName"},qt="agents";async function oe(n,e){let t=(n.client??Wt()??qt).toLowerCase(),o=n.scope??"project",r=await Jt(t,e);if(!r){let i=await Ai(e);throw new Error(`Unknown client "${t}". Known clients: ${i.join(", ")||"(none)"}. Add a Client instance to a package that imports kanonak.org/agent-skills and install that package to make it available.`)}let s=$e(r,o==="user"?Ti:Ei);if(!s)throw new Error(`Client "${t}" has no ${o==="user"?"userSkillDir":"projectSkillDir"} declared.`);return ji(s)}async function Se(n,e,t){let o=await oe(e,t);return cn(o,n)}async function ln(n,e){let t=(n.client??Wt()??qt).toLowerCase(),o=await Jt(t,e);return o?$e(o,Di)??"SKILL.md":"SKILL.md"}function Wt(){if(process.env.CLAUDE_CODE||process.env.CLAUDE_PROJECT_DIR)return"claude-code"}var Vt=new WeakMap;async function Gt(n){let e=Vt.get(n);if(e)return e;let t=await n.getDocumentsByNamespaceAsync(V,H);if(t.length===0)throw new Error(`Could not find ${V}/${H} in the repository. Install a skill capability (which transitively imports agent-skills) or fetch agent-skills directly to populate the cache.`);let r=[...t].sort((c,l)=>{let p=c.metadata.namespace_?.version,u=l.metadata.namespace_?.version;return!p||!u?0:p.major!==u.major?u.major-p.major:p.minor!==u.minor?u.minor-p.minor:u.patch-p.patch})[0],a=new Ki,s=new Ii(r,n),i=await a.parseKanonaks(s);return Vt.set(n,i),i}async function Jt(n,e){let t=await Gt(e),o=Ht(t,zt);for(let r of o)if($e(r,Bt)===n)return r}async function Ai(n){let e=await Gt(n),t=Ht(e,zt),o=[];for(let r of t){let a=$e(r,Bt);a&&o.push(a)}return o}function ji(n){return n.startsWith("~/")?cn(Mt(),n.slice(2)):n==="~"?Mt():n.startsWith("/")||/^[A-Za-z]:[\\/]/.test(n)?n:cn(process.cwd(),n)}import{readFileSync as Oi,writeFileSync as Ni,existsSync as Li}from"fs";import{join as Yt}from"path";import Xt from"js-yaml";var Zt="skills.lock",Fi=`# This file is generated by Kanonak CLI. Do not edit manually.
|
|
158
|
+
`;function Pe(n){let e=Yt(n,Zt);if(!Li(e))return{version:"1",lastUpdated:new Date().toISOString(),skills:{}};let t=Oi(e,"utf-8"),o=Xt.load(t);return!o||typeof o!="object"||o.version!=="1"?{version:"1",lastUpdated:new Date().toISOString(),skills:{}}:{version:"1",lastUpdated:o.lastUpdated??new Date().toISOString(),skills:o.skills??{}}}function pn(n,e){e.lastUpdated=new Date().toISOString();let t={};for(let a of Object.keys(e.skills).sort())t[a]=e.skills[a];e.skills=t;let o=Yt(n,Zt),r=Xt.dump(e,{lineWidth:-1,sortKeys:!1,quotingType:'"'});Ni(o,Fi+r,"utf-8")}var Qt="kanonak.org",eo="skill-to-skill-md",xe=class{parser=new Bi;objectParser=new zi;runner=new Yi;async deploy(e){let{instances:t,allKanonaks:o,repository:r,options:a}=e;if(t.length===0){console.error("No Skill instances to deploy.");return}let s=await Zi(r,this.objectParser);if(!s){console.error(`Cannot deploy: ${Qt}/${eo}@3.0.0 is not installed in the workspace or cache.`);return}let i=await this.runner.run({transformation:s,instances:t,allKanonaks:o,repository:r,parser:this.parser,objectParser:this.objectParser,outputFormat:"markdown-with-frontmatter"});if(i.length===0){console.error("No skills could be transformed from the package.");return}let c=await oe(a,r),l=await ln(a,r),p=Pe(c),u=t[0].namespace,{publisher:d,package_:f,version:g}=Qi(u);for(let m of i){let k=await Se(m.fileName,a,r),h=Ce(Re(k,l));Ui(k,{recursive:!0}),Hi(Re(k,l),m.content,"utf-8"),p.skills[m.fileName]={publisher:d,package_:f,version:g,resolved:d?`kanonak://${d}/${f}@${g}`:"",integrity:qi(m.content)},console.log(` ${h?"Updated":"Installed"} skill "${m.fileName}" \u2192 ${k}`)}pn(c,p)}async undeploy(e,t){let{repository:o,options:r}=t,a=await Se(e,r,o);Ce(a)||(console.error(`Skill "${e}" is not installed at ${a}`),process.exit(1)),Mi(a,{recursive:!0,force:!0});let s=await oe(r,o),i=Pe(s);delete i.skills[e],pn(s,i),console.log(`Removed skill "${e}" from ${a}`)}async list(e){let{repository:t,options:o}=e,r=await oe(o,t),a=Pe(r),s=[];for(let[c,l]of Object.entries(a.skills))s.push({name:c,publisher:l.publisher,package_:l.package_,version:l.version,path:await Se(c,o,t)});let i=await ln(o,t);if(Ce(r))try{let c=Vi(r,{withFileTypes:!0});for(let l of c){if(!l.isDirectory()||a.skills[l.name])continue;let p=Re(r,l.name,i);Ce(p)&&s.push({name:l.name,publisher:"",package_:"",version:"",path:Re(r,l.name)})}}catch{}return s}};async function Zi(n,e){let t=await n.getDocumentsByNamespaceAsync(Qt,eo);if(t.length===0)return;let o=[...t].sort((r,a)=>{let s=r.metadata.namespace_?.version,i=a.metadata.namespace_?.version;return!s||!i?0:Wi(i,s)});for(let r of o){let a=new Ji(r,n),s=await e.parseKanonaks(a),i=Gi(s,Xi.InstanceTransformation);if(i[0])return i[0]}}function Qi(n){if(!n)return{publisher:"",package_:"",version:""};let e=n.indexOf("@");if(e===-1)return{publisher:"",package_:"",version:""};let t=n.substring(0,e),o=n.substring(e+1),r=t.indexOf("/");return r===-1?{publisher:"",package_:"",version:""}:{publisher:t.substring(0,r),package_:t.substring(r+1),version:o}}import{mkdirSync as gc,rmSync as hc,existsSync as mn,readdirSync as kc,writeFileSync as yc,statSync as wc}from"fs";import{join as Ie}from"path";import{KanonakObjectParser as bc,KanonakParser as vc,computeIntegrity as $c}from"@kanonak-protocol/sdk";import{findSubjectsByType as Sc}from"@kanonak-protocol/sdk/uri-helpers";import{SingleDocumentRepository as Pc}from"@kanonak-protocol/sdk/uri-helpers";import{TransformationRunnerV3 as Cc}from"@kanonak-protocol/sdk/transformations";import{TX_V3 as Rc}from"@kanonak-protocol/sdk/transformations";import{join as no}from"path";import{homedir as to}from"os";import{KanonakObjectParser as ec}from"@kanonak-protocol/sdk";import{SingleDocumentRepository as nc}from"@kanonak-protocol/sdk/uri-helpers";import{findSubjectsByType as ro,getStringValue as un,getReferenceUri as tc}from"@kanonak-protocol/sdk/uri-helpers";var z="kanonak.org",B="agent-skills",so={publisher:z,package_:B,name:"Client"},ao={publisher:z,package_:B,name:"clientId"},oc={publisher:z,package_:B,name:"projectAgentDir"},rc={publisher:z,package_:B,name:"userAgentDir"},sc={publisher:z,package_:B,name:"agentFileFormat"},ac="agents";function ic(){if(process.env.CLAUDE_CODE||process.env.CLAUDE_PROJECT_DIR)return"claude-code"}async function _e(n,e){let t=(n.client??ic()??ac).toLowerCase(),o=n.scope??"project",r=await cc(t,e);if(!r){let p=await lc(e);throw new Error(`Unknown client "${t}". Known clients: ${p.join(", ")||"(none)"}. Add a Client instance to a package that imports kanonak.org/agent-skills and install that package to make it available.`)}let s=un(r,o==="user"?rc:oc);if(!s)throw new Error(`Client "${t}" has no ${o==="user"?"userAgentDir":"projectAgentDir"} declared.`);let c=tc(r,sc)?.name==="toml"?"toml":"markdown-frontmatter",l=c==="toml"?".toml":".md";return{agentsDir:pc(s),format:c,fileExtension:l}}var oo=new WeakMap;async function io(n){let e=oo.get(n);if(e)return e;let t=await n.getDocumentsByNamespaceAsync(z,B);if(t.length===0)throw new Error(`Could not find ${z}/${B} in the repository. Install a capability that imports agent-skills to populate the cache.`);let r=[...t].sort((c,l)=>{let p=c.metadata.namespace_?.version,u=l.metadata.namespace_?.version;return!p||!u?0:p.major!==u.major?u.major-p.major:p.minor!==u.minor?u.minor-p.minor:u.patch-p.patch})[0],a=new ec,s=new nc(r,n),i=await a.parseKanonaks(s);return oo.set(n,i),i}async function cc(n,e){let t=await io(e),o=ro(t,so);for(let r of o)if(un(r,ao)===n)return r}async function lc(n){let e=await io(n),t=ro(e,so),o=[];for(let r of t){let a=un(r,ao);a&&o.push(a)}return o}function pc(n){return n.startsWith("~/")?no(to(),n.slice(2)):n==="~"?to():n.startsWith("/")||/^[A-Za-z]:[\\/]/.test(n)?n:no(process.cwd(),n)}import{readFileSync as uc,writeFileSync as dc,existsSync as mc}from"fs";import{join as co}from"path";import lo from"js-yaml";var po="agents.lock",fc=`# This file is generated by Kanonak CLI. Do not edit manually.
|
|
159
|
+
`;function Ke(n){let e=co(n,po);if(!mc(e))return{version:"1",lastUpdated:new Date().toISOString(),agents:{}};let t=uc(e,"utf-8"),o=lo.load(t);return!o||typeof o!="object"||o.version!=="1"?{version:"1",lastUpdated:new Date().toISOString(),agents:{}}:{version:"1",lastUpdated:o.lastUpdated??new Date().toISOString(),agents:o.agents??{}}}function dn(n,e){e.lastUpdated=new Date().toISOString();let t={};for(let a of Object.keys(e.agents).sort())t[a]=e.agents[a];e.agents=t;let o=co(n,po),r=lo.dump(e,{lineWidth:-1,sortKeys:!1,quotingType:'"'});dc(o,fc+r,"utf-8")}var uo="kanonak.org",mo="agent-to-agent-file",Ee=class{parser=new vc;objectParser=new bc;runner=new Cc;async deploy(e){let{instances:t,allKanonaks:o,repository:r,options:a}=e;if(t.length===0){console.error("No Agent instances to deploy.");return}let{agentsDir:s,format:i,fileExtension:c}=await _e(a,r),l=await xc(r,this.objectParser);if(!l){console.error(`Cannot deploy: ${uo}/${mo}@3.0.0 is not installed in the workspace or cache.`);return}let p=i==="toml"?"toml":"markdown-with-frontmatter",u=await this.runner.run({transformation:l,instances:t,allKanonaks:o,repository:r,parser:this.parser,objectParser:this.objectParser,outputFormat:p});if(u.length===0){console.error("No agents could be transformed from the package.");return}gc(s,{recursive:!0});let d=Ke(s),f=t[0].namespace,{publisher:g,package_:m,version:k}=Kc(f);for(let h of u){let y=`${h.fileName}${c}`,w=Ie(s,y),b=mn(w);yc(w,h.content,"utf-8"),d.agents[h.fileName]={publisher:g,package_:m,version:k,resolved:g?`kanonak://${g}/${m}@${k}`:"",integrity:$c(h.content),fileName:y},console.log(` ${b?"Updated":"Installed"} agent "${h.fileName}" \u2192 ${w}`)}dn(s,d)}async undeploy(e,t){let{repository:o,options:r}=t,{agentsDir:a}=await _e(r,o),s=Ke(a),i=s.agents[e];i||(console.error(`Agent "${e}" is not installed at ${a}`),process.exit(1));let c=Ie(a,i.fileName);mn(c)&&hc(c,{force:!0}),delete s.agents[e],dn(a,s),console.log(`Removed agent "${e}" from ${c}`)}async list(e){let{repository:t,options:o}=e,{agentsDir:r}=await _e(o,t),a=Ke(r),s=[];for(let[i,c]of Object.entries(a.agents))s.push({name:i,publisher:c.publisher,package_:c.package_,version:c.version,path:Ie(r,c.fileName)});if(mn(r))try{let i=kc(r,{withFileTypes:!0});for(let c of i){if(!c.isFile()||c.name==="agents.lock")continue;let l=Ie(r,c.name),p=_c(c.name);if(!a.agents[p]){try{if(!wc(l).isFile())continue}catch{continue}s.push({name:p,publisher:"",package_:"",version:"",path:l})}}}catch{}return s}};async function xc(n,e){let t=await n.getDocumentsByNamespaceAsync(uo,mo);if(t.length===0)return;let o=new Pc(t[0],n),r=await e.parseKanonaks(o);return Sc(r,Rc.InstanceTransformation)[0]}function _c(n){let e=n.lastIndexOf(".");return e===-1?n:n.substring(0,e)}function Kc(n){if(!n)return{publisher:"",package_:"",version:""};let e=n.indexOf("@");if(e===-1)return{publisher:"",package_:"",version:""};let t=n.substring(0,e),o=n.substring(e+1),r=t.indexOf("/");return r===-1?{publisher:"",package_:"",version:""}:{publisher:t.substring(0,r),package_:t.substring(r+1),version:o}}var Fc=Dc(Tc(import.meta.url)),Uc=JSON.parse(Ec(Ac(Fc,"..","package.json"),"utf-8")),C=new Ic;C.name("kanonak").description("Kanonak Protocol CLI - Validate and resolve Kanonak ontology packages").version(Uc.version);C.command("validate <path>").description("Validate .kan.yml file(s). Resolves imports via HTTP from publisher domains.").action(async n=>{await yn(n)});C.command("install [package]").description("Install a package and its dependencies, or install all from kanonak.lock.").action(async n=>{await wn(n)});C.command("deps <path>").description("Show resolved dependency tree for a .kan.yml file.").action(async n=>{await vn(n)});C.command("login <publisher>").description("Authenticate with a package publisher using OAuth 2.0.").action(async n=>{await xn(n)});C.command("logout <publisher>").description("Revoke tokens and remove stored credentials for a publisher.").action(async n=>{await _n(n)});C.command("hash <target>").description("Print the canonical structural hash (sha256:\u2026) of a Kanonak package.").option("-v, --verbose","Also print the canonical form on stderr.").option("--frozen","Resolve identity strictly through kanonak.lock; error if the lock or any needed pin is missing.").action(async(n,e)=>{await Tt(n,e)});C.addCommand(Hn());C.addCommand(Jn());C.addCommand(tt());C.addCommand(ct());C.addCommand(lt());C.addCommand(ut());C.addCommand(wt());C.addCommand(Ct());C.addCommand(Et());C.addCommand(Rt());async function Mc(){try{let n=new I,e=new jc,t=new Nc,o=Lc(t),r=new Oc({fetchFn:o}),a=await S(process.cwd(),e),s=new ve;s.register("kanonak.org/capabilities/agent-skill-deployment",new xe),s.register("kanonak.org/agent-capabilities/agent-instance-deployment",new Ee);let i=await Un(n,e,a),c=new be(n,e,r,o,s,a);At(C,i,c)}catch{}}await Mc();C.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanonak-protocol/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.15.0",
|
|
4
4
|
"description": "Command-line tool for the Kanonak Protocol — validate, resolve, preview, and publish semantic ontology packages.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"semantic-web"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@kanonak-protocol/sdk": "^4.
|
|
39
|
-
"@kanonak-protocol/types": "^4.
|
|
38
|
+
"@kanonak-protocol/sdk": "^4.15.0",
|
|
39
|
+
"@kanonak-protocol/types": "^4.15.0",
|
|
40
40
|
"@peculiar/x509": "^2.0.0",
|
|
41
41
|
"commander": "^13.0.0",
|
|
42
42
|
"js-yaml": "^4.1.1",
|