@crossmint/cli 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -0
- package/build-pkg.js +13 -0
- package/dist/bin/crossmint.d.ts +3 -0
- package/dist/bin/crossmint.d.ts.map +1 -0
- package/dist/bin/crossmint.js +18 -0
- package/dist/commands/keys/create.d.ts +2 -0
- package/dist/commands/keys/create.d.ts.map +1 -0
- package/dist/commands/keys/create.js +2 -0
- package/dist/commands/keys/create.test.d.ts +2 -0
- package/dist/commands/keys/create.test.d.ts.map +1 -0
- package/dist/commands/keys/delete.d.ts +2 -0
- package/dist/commands/keys/delete.d.ts.map +1 -0
- package/dist/commands/keys/delete.js +1 -0
- package/dist/commands/keys/delete.test.d.ts +2 -0
- package/dist/commands/keys/delete.test.d.ts.map +1 -0
- package/dist/commands/keys/edit.d.ts +2 -0
- package/dist/commands/keys/edit.d.ts.map +1 -0
- package/dist/commands/keys/edit.js +2 -0
- package/dist/commands/keys/edit.test.d.ts +2 -0
- package/dist/commands/keys/edit.test.d.ts.map +1 -0
- package/dist/commands/keys/index.d.ts +5 -0
- package/dist/commands/keys/index.d.ts.map +1 -0
- package/dist/commands/keys/index.js +11 -0
- package/dist/commands/keys/list.d.ts +3 -0
- package/dist/commands/keys/list.d.ts.map +1 -0
- package/dist/commands/keys/list.js +10 -0
- package/dist/commands/keys/list.test.d.ts +2 -0
- package/dist/commands/keys/list.test.d.ts.map +1 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +1 -0
- package/dist/commands/login.test.d.ts +2 -0
- package/dist/commands/login.test.d.ts.map +1 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +1 -0
- package/dist/commands/projects/create.d.ts +6 -0
- package/dist/commands/projects/create.d.ts.map +1 -0
- package/dist/commands/projects/create.js +6 -0
- package/dist/commands/projects/create.test.d.ts +2 -0
- package/dist/commands/projects/create.test.d.ts.map +1 -0
- package/dist/commands/projects/details.d.ts +2 -0
- package/dist/commands/projects/details.d.ts.map +1 -0
- package/dist/commands/projects/details.js +2 -0
- package/dist/commands/projects/details.test.d.ts +2 -0
- package/dist/commands/projects/details.test.d.ts.map +1 -0
- package/dist/commands/projects/index.d.ts +4 -0
- package/dist/commands/projects/index.d.ts.map +1 -0
- package/dist/commands/projects/index.js +7 -0
- package/dist/commands/projects/select.d.ts +2 -0
- package/dist/commands/projects/select.d.ts.map +1 -0
- package/dist/commands/projects/select.js +1 -0
- package/dist/commands/projects/select.test.d.ts +2 -0
- package/dist/commands/projects/select.test.d.ts.map +1 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +1 -0
- package/dist/types/keys.d.ts +11 -0
- package/dist/types/keys.d.ts.map +1 -0
- package/dist/types/keys.js +0 -0
- package/dist/utils/Pager.d.ts +9 -0
- package/dist/utils/Pager.d.ts.map +1 -0
- package/dist/utils/Pager.js +2 -0
- package/dist/utils/Pager.test.d.ts +2 -0
- package/dist/utils/Pager.test.d.ts.map +1 -0
- package/dist/utils/fetch.d.ts +5 -0
- package/dist/utils/fetch.d.ts.map +1 -0
- package/dist/utils/fetch.js +1 -0
- package/dist/utils/getProject.d.ts +4 -0
- package/dist/utils/getProject.d.ts.map +1 -0
- package/dist/utils/getProject.js +1 -0
- package/dist/utils/getWhitelistedOriginsAndAppIdentifiersPrompt.d.ts +6 -0
- package/dist/utils/getWhitelistedOriginsAndAppIdentifiersPrompt.d.ts.map +1 -0
- package/dist/utils/getWhitelistedOriginsAndAppIdentifiersPrompt.js +2 -0
- package/dist/utils/keytar.d.ts +4 -0
- package/dist/utils/keytar.d.ts.map +1 -0
- package/dist/utils/keytar.js +1 -0
- package/dist/utils/oauth/codeChallenge.d.ts +3 -0
- package/dist/utils/oauth/codeChallenge.d.ts.map +1 -0
- package/dist/utils/oauth/codeChallenge.js +1 -0
- package/dist/utils/oauth/getStytchConfig.d.ts +6 -0
- package/dist/utils/oauth/getStytchConfig.d.ts.map +1 -0
- package/dist/utils/oauth/getStytchConfig.js +1 -0
- package/dist/utils/oauth/server.d.ts +3 -0
- package/dist/utils/oauth/server.d.ts.map +1 -0
- package/dist/utils/oauth/server.js +1 -0
- package/dist/utils/scopes.d.ts +14 -0
- package/dist/utils/scopes.d.ts.map +1 -0
- package/dist/utils/scopes.js +1 -0
- package/dist/utils/spinner.d.ts +6 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +1 -0
- package/dist/utils/store.d.ts +11 -0
- package/dist/utils/store.d.ts.map +1 -0
- package/dist/utils/store.js +1 -0
- package/dist/utils/urls.d.ts +9 -0
- package/dist/utils/urls.d.ts.map +1 -0
- package/dist/utils/urls.js +1 -0
- package/package.json +50 -0
- package/shims/prelude.js +33 -0
- package/src/bin/crossmint.ts +34 -0
- package/src/commands/keys/create.test.ts +230 -0
- package/src/commands/keys/create.ts +96 -0
- package/src/commands/keys/delete.test.ts +87 -0
- package/src/commands/keys/delete.ts +36 -0
- package/src/commands/keys/edit.test.ts +159 -0
- package/src/commands/keys/edit.ts +98 -0
- package/src/commands/keys/index.ts +4 -0
- package/src/commands/keys/list.test.ts +123 -0
- package/src/commands/keys/list.ts +83 -0
- package/src/commands/login.test.ts +87 -0
- package/src/commands/login.ts +59 -0
- package/src/commands/logout.ts +9 -0
- package/src/commands/projects/create.test.ts +82 -0
- package/src/commands/projects/create.ts +45 -0
- package/src/commands/projects/details.test.ts +64 -0
- package/src/commands/projects/details.ts +36 -0
- package/src/commands/projects/index.ts +3 -0
- package/src/commands/projects/select.test.ts +86 -0
- package/src/commands/projects/select.ts +50 -0
- package/src/commands/whoami.ts +19 -0
- package/src/types/keys.ts +13 -0
- package/src/utils/Pager.test.ts +70 -0
- package/src/utils/Pager.ts +34 -0
- package/src/utils/fetch.ts +63 -0
- package/src/utils/getProject.ts +10 -0
- package/src/utils/getWhitelistedOriginsAndAppIdentifiersPrompt.ts +52 -0
- package/src/utils/keytar.ts +15 -0
- package/src/utils/oauth/codeChallenge.ts +18 -0
- package/src/utils/oauth/getStytchConfig.ts +28 -0
- package/src/utils/oauth/server.ts +66 -0
- package/src/utils/scopes.ts +66 -0
- package/src/utils/spinner.ts +20 -0
- package/src/utils/store.ts +38 -0
- package/src/utils/urls.ts +11 -0
- package/tsconfig.json +35 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsup.config.ts +11 -0
- package/vitest.config.ts +9 -0
- package/vitest.setup.ts +8 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HttpMethods } from "@crossmint/common-consts";
|
|
2
|
+
export declare function fetchAuthenticated(path: string, method?: HttpMethods, body?: any): Promise<any>;
|
|
3
|
+
export declare function fetchUnauthenticated(path: string, method?: HttpMethods, body?: any): Promise<any>;
|
|
4
|
+
export declare function fetchJSON(url: string, method?: HttpMethods, body?: any, headers?: Record<string, string>): Promise<any>;
|
|
5
|
+
//# sourceMappingURL=fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAIvD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,WAA6B,EAAE,IAAI,CAAC,EAAE,GAAG,gBAkBvG;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,WAA6B,EAAE,IAAI,CAAC,EAAE,GAAG,gBAUzG;AAED,wBAAsB,SAAS,CAC3B,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,WAA6B,EACrC,IAAI,CAAC,EAAE,GAAG,EACV,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,gBAsBnC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var y=Object.defineProperty;var n=(t,e)=>y(t,"name",{value:e,configurable:!0});import{HttpMethods as g}from"@crossmint/common-consts";import x from"keytar";var E="crossmint-cli";async function p(t){return x.getPassword(E,t)}n(p,"get");async function u(){return p("environment")}n(u,"getEnvironment");async function f(){return p("sessionToken")}n(f,"getSessionToken");var h={production:"https://www.crossmint.com",staging:"https://staging.crossmint.com",local:"http://localhost:3000"};function m(t){return h[t]}n(m,"getCrossmintUrl");async function R(t,e=g.GET,s){let o=await f(),r=await u();o||(console.error("\u274C Session not found, please run crossmint login"),process.exit(1)),r||(console.error("\u274C Environment not found, please run crossmint login"),process.exit(1));let i=`${m(r)}${t}`;return l(i,e,s,{Cookie:`sessionToken=${o}`})}n(R,"fetchAuthenticated");async function A(t,e=g.GET,s){let o=await u();o||(console.error("\u274C Environment not found"),process.exit(1));let r=`${m(o)}${t}`;return l(r,e,s)}n(A,"fetchUnauthenticated");async function l(t,e=g.GET,s,o){let r={method:e,headers:{"Content-Type":"application/json",...o}};s&&(r.body=JSON.stringify(s));let i=await fetch(t,r);if(i.status>=400){let d=await i.json();throw new Error(`${d.message}`)}return await i.json()}n(l,"fetchJSON");export{R as fetchAuthenticated,l as fetchJSON,A as fetchUnauthenticated};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { DeveloperProject } from "@crossmint/common-types";
|
|
2
|
+
import { PrimitiveProperties } from "@crossmint/data-dbs-types";
|
|
3
|
+
export declare function getProject(projectId: string): Promise<PrimitiveProperties<DeveloperProject> | undefined>;
|
|
4
|
+
//# sourceMappingURL=getProject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getProject.d.ts","sourceRoot":"","sources":["../../src/utils/getProject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEhE,wBAAsB,UAAU,CAAC,SAAS,EAAE,MAAM,8DAIjD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var v=Object.defineProperty;var t=(e,n)=>v(e,"name",{value:n,configurable:!0});import{HttpMethods as f}from"@crossmint/common-consts";import y from"keytar";var x="crossmint-cli";async function p(e){return y.getPassword(x,e)}t(p,"get");async function m(){return p("environment")}t(m,"getEnvironment");async function u(){return p("sessionToken")}t(u,"getSessionToken");var h={production:"https://www.crossmint.com",staging:"https://staging.crossmint.com",local:"http://localhost:3000"};function g(e){return h[e]}t(g,"getCrossmintUrl");async function l(e,n=f.GET,r){let o=await u(),s=await m();o||(console.error("\u274C Session not found, please run crossmint login"),process.exit(1)),s||(console.error("\u274C Environment not found, please run crossmint login"),process.exit(1));let i=`${g(s)}${e}`;return j(i,n,r,{Cookie:`sessionToken=${o}`})}t(l,"fetchAuthenticated");async function j(e,n=f.GET,r,o){let s={method:n,headers:{"Content-Type":"application/json",...o}};r&&(s.body=JSON.stringify(r));let i=await fetch(e,s);if(i.status>=400){let d=await i.json();throw new Error(`${d.message}`)}return await i.json()}t(j,"fetchJSON");import{routes as P}from"@crossmint/common-consts";async function b(e){return(await l(P.api.projects)).find(o=>o.id===e)}t(b,"getProject");export{b as getProject};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ClientPlatform } from "../types/keys";
|
|
2
|
+
export declare function getWhitelistedOriginsAndAppIdentifiersPrompt(clientPlatform: ClientPlatform, values?: string[]): Promise<{
|
|
3
|
+
whitelistedOrigins: string[];
|
|
4
|
+
whitelistedAppIdentifiers: string[];
|
|
5
|
+
}>;
|
|
6
|
+
//# sourceMappingURL=getWhitelistedOriginsAndAppIdentifiersPrompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getWhitelistedOriginsAndAppIdentifiersPrompt.d.ts","sourceRoot":"","sources":["../../src/utils/getWhitelistedOriginsAndAppIdentifiersPrompt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAW9C,wBAAsB,4CAA4C,CAAC,cAAc,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE;;;GAuCnH"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var c=Object.defineProperty;var n=(e,t)=>c(e,"name",{value:t,configurable:!0});import{input as d}from"@inquirer/prompts";function f(e){try{return new URL(e),!0}catch{return!1}}n(f,"isValidUrl");async function h(e,t){let s=[],m=[],p=t?`
|
|
2
|
+
Press [tab] to edit or [enter] to confirm current value`:"";if(e==="web"){let o=await d({default:t==null?void 0:t.join(","),message:`Enter whitelisted domain or localhost (e.g., https://www.yourdomain.com) (separate multiple domains with a comma):${p}`,validate:n(i=>{let a=i.split(",");for(let r of a)if(!f(r))return`Invalid domain: ${r}. Please enter a valid URL (e.g., https://www.yourdomain.com)`;return!0},"validate")});s.push(...o.split(",").map(i=>i.trim()))}else if(e==="mobile"){let o=await d({default:t==null?void 0:t.join(","),message:`Enter iOS bundle ID or Android package name (e.g., com.company.appname) (separate multiple domains with a comma):${p}`,validate:n(i=>{let a=i.split(",");for(let r of a)if(r.trim().length===0)return"App identifier cannot be empty";return!0},"validate")});m.push(...o.split(",").map(i=>i.trim()))}return{whitelistedOrigins:s,whitelistedAppIdentifiers:m}}n(h,"getWhitelistedOriginsAndAppIdentifiersPrompt");export{h as getWhitelistedOriginsAndAppIdentifiersPrompt};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keytar.d.ts","sourceRoot":"","sources":["../../src/utils/keytar.ts"],"names":[],"mappings":"AAIA,wBAAsB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAExD;AAED,wBAAsB,GAAG,CAAC,QAAQ,EAAE,MAAM,0BAEzC;AAED,wBAAsB,GAAG,CAAC,QAAQ,EAAE,MAAM,oBAEzC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var o=Object.defineProperty;var r=(t,s)=>o(t,"name",{value:s,configurable:!0});import e from"keytar";var n="crossmint-cli";async function g(t,s){return e.setPassword(n,t,s)}r(g,"set");async function p(t){return e.getPassword(n,t)}r(p,"get");async function u(t){return e.deletePassword(n,t)}r(u,"del");export{u as del,p as get,g as set};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codeChallenge.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/codeChallenge.ts"],"names":[],"mappings":"AAGA,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU7D;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE9D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var g=Object.defineProperty;var t=(e,r)=>g(e,"name",{value:r,configurable:!0});import a from"crypto";import s from"keytar";var o="crossmint-cli";async function n(e,r){return s.setPassword(o,e,r)}t(n,"set");async function i(e){return s.getPassword(o,e)}t(i,"get");async function V(){let r=a.randomBytes(32).toString("base64url"),c=a.createHash("sha256").update(r).digest().toString("base64url");return await n("pkceCodeVerifier",r),c}t(V,"generateCodeChallenge");async function w(){return await i("pkceCodeVerifier")}t(w,"getCodeVerifier");export{V as generateCodeChallenge,w as getCodeVerifier};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getStytchConfig.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/getStytchConfig.ts"],"names":[],"mappings":"AAEA,wBAAsB,eAAe;;;;GAyBpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=Object.defineProperty;var t=(e,c)=>i(e,"name",{value:c,configurable:!0});import a from"keytar";var d="crossmint-cli";async function o(e){return a.getPassword(d,e)}t(o,"get");async function s(){return o("environment")}t(s,"getEnvironment");async function I(){let e=await s();return e||(console.error("\u274C Environment not found"),process.exit(1)),e==="local"?{uri:"test.stytch.com",projectId:"project-test-8eb55d3d-949f-4e0f-aea6-c9ba740f7813",clientId:"connected-app-test-4d27d6d8-3491-45c8-9bb0-b05f89cb7a1a"}:e==="staging"?{uri:"api.stytch.com",projectId:"project-live-b7c564af-0c0d-4662-bfc0-72bed1049cfe",clientId:"connected-app-live-47cd5bf1-c155-42c1-9f36-5d35853b34a7"}:{uri:"api.stytch.com",projectId:"project-live-7a1cb98d-b8b7-4471-aaf6-f8af67a0df94",clientId:"connected-app-live-a72edd7e-6ff3-4002-a13a-e7ca66354bd1"}}t(I,"getStytchConfig");export{I as getStytchConfig};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/utils/oauth/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAmB,MAAM,SAAS,CAAC;AAQvD,wBAA8B,MAAM,CAAC,WAAW,EAAE,WAAW,oBAwD5D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var $=Object.defineProperty;var e=(t,n)=>$(t,"name",{value:n,configurable:!0});import U from"http";var H={production:"https://www.crossmint.com",staging:"https://staging.crossmint.com",local:"http://localhost:3000"};function f(t){return H[t]}e(f,"getCrossmintUrl");import x from"keytar";var w="crossmint-cli";async function g(t,n){return x.setPassword(w,t,n)}e(g,"set");async function d(t){return x.getPassword(w,t)}e(d,"get");async function k(){return await d("pkceCodeVerifier")}e(k,"getCodeVerifier");async function u(){return d("environment")}e(u,"getEnvironment");function b(t){return g("sessionToken",t)}e(b,"setSessionToken");import{HttpMethods as T}from"@crossmint/common-consts";async function C(t,n=T.GET,s){let o=await u();o||(console.error("\u274C Environment not found"),process.exit(1));let p=`${f(o)}${t}`;return l(p,n,s)}e(C,"fetchUnauthenticated");async function l(t,n=T.GET,s,o){let p={method:n,headers:{"Content-Type":"application/json",...o}};s&&(p.body=JSON.stringify(s));let i=await fetch(t,p);if(i.status>=400){let r=await i.json();throw new Error(`${r.message}`)}return await i.json()}e(l,"fetchJSON");import{HttpMethods as j}from"@crossmint/common-consts";import{routes as I}from"@crossmint/common-consts";async function S(){let t=await u();return t||(console.error("\u274C Environment not found"),process.exit(1)),t==="local"?{uri:"test.stytch.com",projectId:"project-test-8eb55d3d-949f-4e0f-aea6-c9ba740f7813",clientId:"connected-app-test-4d27d6d8-3491-45c8-9bb0-b05f89cb7a1a"}:t==="staging"?{uri:"api.stytch.com",projectId:"project-live-b7c564af-0c0d-4662-bfc0-72bed1049cfe",clientId:"connected-app-live-47cd5bf1-c155-42c1-9f36-5d35853b34a7"}:{uri:"api.stytch.com",projectId:"project-live-7a1cb98d-b8b7-4471-aaf6-f8af67a0df94",clientId:"connected-app-live-a72edd7e-6ff3-4002-a13a-e7ca66354bd1"}}e(S,"getStytchConfig");async function _(t){return new Promise((n,s)=>{let o=U.createServer(async(i,r)=>{try{let y=new URL(i.url||"",`http://${i.headers.host}`).searchParams.get("code");if(!y){r.writeHead(400,{"Content-Type":"text/plain"}),r.end("No code provided");return}let m=await S(),v=await l(`https://${m.uri}/v1/public/${m.projectId}/oauth2/token`,j.POST,{grant_type:"authorization_code",code:y,client_id:m.clientId,redirect_uri:"http://127.0.0.1:3456/callback",code_verifier:await k()}),P=await C(I.authentication.exchangeAccessToken,j.POST,{accessToken:v.access_token});await b(P.sessionToken),r.writeHead(302,{Location:`${f(t)}${I.console.authorizeDevice.success}`}),r.end(),o.close(),n(v)}catch(h){r.writeHead(500,{"Content-Type":"text/plain"}),r.end("Error during authentication"),o.close(),s(h)}});o.listen(3456)})}e(_,"server");export{_ as default};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Separator } from "@inquirer/prompts";
|
|
2
|
+
import { APIKeyScopes, APIKeyUsageOrigin } from "@crossmint/products-console-types";
|
|
3
|
+
import { Environment } from "./urls";
|
|
4
|
+
import { DeveloperProject } from "@crossmint/common-types";
|
|
5
|
+
import { PrimitiveProperties } from "@crossmint/data-dbs-types";
|
|
6
|
+
type ScopeChoice = {
|
|
7
|
+
name: string;
|
|
8
|
+
value: APIKeyScopes;
|
|
9
|
+
description: string;
|
|
10
|
+
checked: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function getScopeChoices(keyType: APIKeyUsageOrigin, enviornment: Environment, activeProject: PrimitiveProperties<DeveloperProject>, keyScopes?: APIKeyScopes[]): (Separator | ScopeChoice)[];
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=scopes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scopes.d.ts","sourceRoot":"","sources":["../../src/utils/scopes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAEH,YAAY,EACZ,iBAAiB,EAKpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEhE,KAAK,WAAW,GAAG;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CACpB,CAAC;AAIF,wBAAgB,eAAe,CAC3B,OAAO,EAAE,iBAAiB,EAC1B,WAAW,EAAE,WAAW,EACxB,aAAa,EAAE,mBAAmB,CAAC,gBAAgB,CAAC,EACpD,SAAS,GAAE,YAAY,EAAO,+BAyBjC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var S=Object.defineProperty;var n=(e,i)=>S(e,"name",{value:i,configurable:!0});import{Separator as I}from"@inquirer/prompts";import{CLIENT_API_KEY_PUBLIC_SCOPES as l,SERVER_API_KEY_PUBLIC_SCOPES as g,APIKeyCategoryNames as A}from"@crossmint/products-console-types";function B(e,i,o,c=[]){let a=(e==="server"?g:l).reduce((r,s)=>{let{title:y,description:P,value:t,category:p}=s;return r[p]||(r[p]=[]),C(s,i,o)||r[p].push({name:`${y} (${t})`,value:t,description:P,checked:c.includes(t)}),r},{});return Object.entries(a).flatMap(([r,s])=>[new I(A[r]),...s])}n(B,"getScopeChoices");function C(e,i,o){return i==="production"&&e.projectPermission!=null&&(o==null?void 0:o.addons)!=null&&!o.addons[e.projectPermission]}n(C,"isScopeDisabled");export{B as getScopeChoices};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/utils/spinner.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,cAAc,CAAC,EAAE,MAAM;sBAG3B,MAAM;uBAIL,MAAM;oBAIT,MAAM;EAM7B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=Object.defineProperty;var n=(s,r)=>i(s,"name",{value:r,configurable:!0});import{createSpinner as c}from"nanospinner";function g(s){let r=c(s),e={start:n(t=>(r.start({text:t}),e),"start"),succeed:n(t=>(r.success({text:t}),e),"succeed"),fail:n(t=>(r.error({text:t}),e),"fail")};return e}n(g,"createSpinner");export{g as createSpinner};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Environment } from "./urls";
|
|
2
|
+
export declare function setEnvironment(environment: Environment): Promise<void>;
|
|
3
|
+
export declare function getEnvironment(): Promise<"production" | "staging" | "local" | null>;
|
|
4
|
+
export declare function deleteEnvironment(): Promise<boolean>;
|
|
5
|
+
export declare function setSessionToken(sessionToken: string): Promise<void>;
|
|
6
|
+
export declare function getSessionToken(): Promise<string | null>;
|
|
7
|
+
export declare function deleteSessionToken(): Promise<boolean>;
|
|
8
|
+
export declare function setProjectId(projectId: string): Promise<void>;
|
|
9
|
+
export declare function getProjectId(): Promise<string | null>;
|
|
10
|
+
export declare function deleteProjectId(): Promise<boolean>;
|
|
11
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/utils/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAGrC,wBAAgB,cAAc,CAAC,WAAW,EAAE,WAAW,iBAEtD;AAED,wBAAsB,cAAc,uDAEnC;AAED,wBAAsB,iBAAiB,qBAEtC;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,iBAEnD;AAED,wBAAsB,eAAe,2BAEpC;AAED,wBAAsB,kBAAkB,qBAEvC;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,iBAE7C;AAED,wBAAsB,YAAY,2BAEjC;AAED,wBAAsB,eAAe,qBAEpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var a=Object.defineProperty;var e=(n,s)=>a(n,"name",{value:s,configurable:!0});import i from"keytar";var c="crossmint-cli";async function t(n,s){return i.setPassword(c,n,s)}e(t,"set");async function r(n){return i.getPassword(c,n)}e(r,"get");async function o(n){return i.deletePassword(c,n)}e(o,"del");function v(n){return t("environment",n)}e(v,"setEnvironment");async function x(){return r("environment")}e(x,"getEnvironment");async function E(){return o("environment")}e(E,"deleteEnvironment");function y(n){return t("sessionToken",n)}e(y,"setSessionToken");async function P(){return r("sessionToken")}e(P,"getSessionToken");async function k(){return o("sessionToken")}e(k,"deleteSessionToken");function I(n){return t("projectId",n)}e(I,"setProjectId");async function j(){return r("projectId")}e(j,"getProjectId");async function T(){return o("projectId")}e(T,"deleteProjectId");export{E as deleteEnvironment,T as deleteProjectId,k as deleteSessionToken,x as getEnvironment,j as getProjectId,P as getSessionToken,v as setEnvironment,I as setProjectId,y as setSessionToken};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare const crossmintUrls: {
|
|
2
|
+
production: string;
|
|
3
|
+
staging: string;
|
|
4
|
+
local: string;
|
|
5
|
+
};
|
|
6
|
+
export type Environment = keyof typeof crossmintUrls;
|
|
7
|
+
export declare function getCrossmintUrl(environment: Environment): string;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=urls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urls.d.ts","sourceRoot":"","sources":["../../src/utils/urls.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,aAAa;;;;CAIlB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,aAAa,CAAC;AAErD,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,UAEvD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var s=Object.defineProperty;var o=(t,n)=>s(t,"name",{value:n,configurable:!0});var r={production:"https://www.crossmint.com",staging:"https://staging.crossmint.com",local:"http://localhost:3000"};function m(t){return r[t]}o(m,"getCrossmintUrl");export{m as getCrossmintUrl};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crossmint/cli",
|
|
3
|
+
"version": "1.1.2",
|
|
4
|
+
"description": "",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"author": "",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "dist/bin/crossmint.js",
|
|
10
|
+
"bin": {
|
|
11
|
+
"crossmint": "dist/bin/crossmint.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup --env.NODE_ENV production",
|
|
15
|
+
"build:pkg": "node ./build-pkg.js",
|
|
16
|
+
"bump": "node ./scripts/bump.mjs",
|
|
17
|
+
"dev": "tsup --watch --env.NODE_ENV development",
|
|
18
|
+
"start": "pnpm build && node dist/bin/crossmint.js",
|
|
19
|
+
"test:vitest": "vitest run",
|
|
20
|
+
"test:vitest:watch": "vitest"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@crossmint/common-consts": "workspace:*",
|
|
24
|
+
"@crossmint/common-string-utils": "workspace:*",
|
|
25
|
+
"@crossmint/common-types": "workspace:*",
|
|
26
|
+
"@crossmint/data-dbs-types": "workspace:*",
|
|
27
|
+
"@crossmint/products-console-types": "workspace:*",
|
|
28
|
+
"@inquirer/prompts": "7.4.0",
|
|
29
|
+
"chalk": "4.1.2",
|
|
30
|
+
"commander": "12.0.0",
|
|
31
|
+
"keytar": "7.9.0",
|
|
32
|
+
"nanospinner": "1.2.2",
|
|
33
|
+
"open": "9.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@crossmint/tsconfig": "workspace:*",
|
|
37
|
+
"@crossmint/tsupconfig": "workspace:*",
|
|
38
|
+
"@crossmint/vitestconfig": "workspace:*",
|
|
39
|
+
"@types/node": "20.14.8",
|
|
40
|
+
"@yao-pkg/pkg": "6.3.2",
|
|
41
|
+
"esbuild": "0.25.2",
|
|
42
|
+
"vitest": "2.1.9"
|
|
43
|
+
},
|
|
44
|
+
"pkg": {
|
|
45
|
+
"scripts": "dist/bin/crossmint.js",
|
|
46
|
+
"assets": [],
|
|
47
|
+
"targets": ["node20-macos-arm64", "node20-linux-x64", "node20-macos-x64"],
|
|
48
|
+
"outputPath": "build"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/shims/prelude.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
|
|
4
|
+
if (typeof process.pkg !== "undefined") {
|
|
5
|
+
global.__filename = process.execPath;
|
|
6
|
+
global.__dirname = path.dirname(process.execPath);
|
|
7
|
+
global.fileURLToPath = (url) => {
|
|
8
|
+
if (typeof url === "string" || url instanceof URL) {
|
|
9
|
+
return path.resolve(".");
|
|
10
|
+
}
|
|
11
|
+
throw new TypeError("fileURLToPath: expected a string or URL");
|
|
12
|
+
};
|
|
13
|
+
const originalReadFileSync = fs.readFileSync;
|
|
14
|
+
fs.readFileSync = function (...args) {
|
|
15
|
+
try {
|
|
16
|
+
return originalReadFileSync.apply(this, args);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
return Buffer.from(""); // or return "" if expected
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const urlModule = require("url");
|
|
24
|
+
urlModule.fileURLToPath = function (pathArg) {
|
|
25
|
+
if (typeof pathArg === "string" || pathArg instanceof URL) {
|
|
26
|
+
return path.resolve(".");
|
|
27
|
+
}
|
|
28
|
+
return path.resolve(".");
|
|
29
|
+
};
|
|
30
|
+
} catch {
|
|
31
|
+
// Do nothing
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { program } from "commander";
|
|
3
|
+
import login from "@/commands/login";
|
|
4
|
+
import whoami from "@/commands/whoami";
|
|
5
|
+
import logout from "@/commands/logout";
|
|
6
|
+
import * as projectsCommands from "@/commands/projects";
|
|
7
|
+
import * as keysCommands from "@/commands/keys";
|
|
8
|
+
|
|
9
|
+
program.name("crossmint").description("Crossmint CLI Tool").version(require("../../package.json").version);
|
|
10
|
+
|
|
11
|
+
const keysCommand = program.command("keys").description("API Keys management commands");
|
|
12
|
+
keysCommand.command("create").description("Create a new API Key").action(keysCommands.create);
|
|
13
|
+
keysCommand.command("delete <key-id>").description("Delete an API Key").action(keysCommands.delete);
|
|
14
|
+
keysCommand.command("edit <key-id>").description("Edit the scopes of an API Key").action(keysCommands.edit);
|
|
15
|
+
keysCommand
|
|
16
|
+
.command("list [type]")
|
|
17
|
+
.description("List all API Keys (optionally filter by type: server|client)")
|
|
18
|
+
.action(keysCommands.list);
|
|
19
|
+
|
|
20
|
+
program.command("login").description("Login to Crossmint").action(login);
|
|
21
|
+
program.command("logout").description("Logout from Crossmint").action(logout);
|
|
22
|
+
|
|
23
|
+
const projectsCommand = program.command("projects").description("Project management commands");
|
|
24
|
+
projectsCommand
|
|
25
|
+
.command("create")
|
|
26
|
+
.description("Create a new project")
|
|
27
|
+
.action(projectsCommands.create)
|
|
28
|
+
.option("-n, --name <name>", "Project name");
|
|
29
|
+
projectsCommand.command("details").description("Show project details").action(projectsCommands.details);
|
|
30
|
+
projectsCommand.command("select").description("Select a project").action(projectsCommands.select);
|
|
31
|
+
|
|
32
|
+
program.command("whoami").description("Show current user and environment").action(whoami);
|
|
33
|
+
|
|
34
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { input, select, checkbox } from "@inquirer/prompts";
|
|
3
|
+
import { fetchAuthenticated } from "@/utils/fetch";
|
|
4
|
+
import { getEnvironment, getProjectId } from "@/utils/store";
|
|
5
|
+
import createKey from "./create";
|
|
6
|
+
import { APIKeyUsageOrigin, APIKeyScopes } from "@crossmint/products-console-types";
|
|
7
|
+
import { createSpinner } from "@/utils/spinner";
|
|
8
|
+
import { getProject } from "@/utils/getProject";
|
|
9
|
+
|
|
10
|
+
vi.mock("@inquirer/prompts");
|
|
11
|
+
vi.mock("@/utils/fetch");
|
|
12
|
+
vi.mock("@/utils/store");
|
|
13
|
+
vi.mock("@/utils/spinner");
|
|
14
|
+
vi.mock("@/utils/getProject");
|
|
15
|
+
|
|
16
|
+
describe("createKey", () => {
|
|
17
|
+
const mockProjectId = "test-project-id";
|
|
18
|
+
const mockSpinner = {
|
|
19
|
+
start: vi.fn(),
|
|
20
|
+
succeed: vi.fn(),
|
|
21
|
+
fail: vi.fn(),
|
|
22
|
+
};
|
|
23
|
+
let consoleLogSpy: ReturnType<typeof vi.spyOn>;
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
vi.mocked(getProjectId).mockResolvedValue(mockProjectId);
|
|
27
|
+
vi.mocked(createSpinner).mockReturnValue(mockSpinner);
|
|
28
|
+
vi.mocked(getEnvironment).mockResolvedValue("production");
|
|
29
|
+
vi.mocked(getProject).mockResolvedValue({} as any);
|
|
30
|
+
consoleLogSpy = vi.spyOn(console, "log");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should create a server API key successfully", async () => {
|
|
34
|
+
vi.mocked(select).mockResolvedValueOnce("server" as APIKeyUsageOrigin);
|
|
35
|
+
vi.mocked(checkbox).mockResolvedValueOnce([APIKeyScopes.NFTS_READ]);
|
|
36
|
+
|
|
37
|
+
const mockResponse = {
|
|
38
|
+
apiKey: {
|
|
39
|
+
clientSecret: "test-client-secret",
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
vi.mocked(fetchAuthenticated).mockResolvedValueOnce(mockResponse);
|
|
43
|
+
|
|
44
|
+
await createKey();
|
|
45
|
+
|
|
46
|
+
expect(fetchAuthenticated).toHaveBeenCalledWith(
|
|
47
|
+
expect.stringContaining(`/api/console/projects/${mockProjectId}/apiKeys`),
|
|
48
|
+
"POST",
|
|
49
|
+
{
|
|
50
|
+
usageOrigin: "server",
|
|
51
|
+
scopes: [APIKeyScopes.NFTS_READ],
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
expect(createSpinner).toHaveBeenCalledWith("Creating key...");
|
|
56
|
+
expect(mockSpinner.succeed).toHaveBeenCalledWith("✅ API Key created successfully!");
|
|
57
|
+
expect(consoleLogSpy).toHaveBeenCalledWith("🔒 Key Secret:", mockResponse.apiKey.clientSecret);
|
|
58
|
+
});
|
|
59
|
+
it("should warn the user that the key secret will not be shown again when creating a server API key in production", async () => {
|
|
60
|
+
vi.mocked(select).mockResolvedValueOnce("server" as APIKeyUsageOrigin);
|
|
61
|
+
vi.mocked(checkbox).mockResolvedValueOnce([APIKeyScopes.NFTS_READ]);
|
|
62
|
+
vi.mocked(getEnvironment).mockResolvedValueOnce("production");
|
|
63
|
+
|
|
64
|
+
const mockResponse = {
|
|
65
|
+
apiKey: {
|
|
66
|
+
clientSecret: "test-client-secret",
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
vi.mocked(fetchAuthenticated).mockResolvedValueOnce(mockResponse);
|
|
70
|
+
|
|
71
|
+
await createKey();
|
|
72
|
+
|
|
73
|
+
expect(fetchAuthenticated).toHaveBeenCalledWith(
|
|
74
|
+
expect.stringContaining(`/api/console/projects/${mockProjectId}/apiKeys`),
|
|
75
|
+
"POST",
|
|
76
|
+
{
|
|
77
|
+
usageOrigin: "server",
|
|
78
|
+
scopes: [APIKeyScopes.NFTS_READ],
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
83
|
+
"Please save these credentials securely. The key secret will not be shown again."
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("should create a client API key for web platform successfully", async () => {
|
|
88
|
+
vi.mocked(select)
|
|
89
|
+
.mockResolvedValueOnce("client" as APIKeyUsageOrigin)
|
|
90
|
+
.mockResolvedValueOnce("web");
|
|
91
|
+
vi.mocked(input).mockResolvedValueOnce("https://example.com");
|
|
92
|
+
vi.mocked(checkbox).mockResolvedValueOnce([APIKeyScopes.WALLET_READ]);
|
|
93
|
+
|
|
94
|
+
const mockResponse = {
|
|
95
|
+
apiKey: {
|
|
96
|
+
clientSecret: "test-client-secret",
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
vi.mocked(fetchAuthenticated).mockResolvedValueOnce(mockResponse);
|
|
100
|
+
|
|
101
|
+
await createKey();
|
|
102
|
+
|
|
103
|
+
expect(fetchAuthenticated).toHaveBeenCalledWith(
|
|
104
|
+
expect.stringContaining(`/api/console/projects/${mockProjectId}/apiKeys`),
|
|
105
|
+
"POST",
|
|
106
|
+
{
|
|
107
|
+
usageOrigin: "client",
|
|
108
|
+
scopes: [APIKeyScopes.WALLET_READ],
|
|
109
|
+
whitelistedOrigins: ["https://example.com"],
|
|
110
|
+
whitelistedAppIdentifiers: [],
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
expect(createSpinner).toHaveBeenCalledWith("Creating key...");
|
|
114
|
+
expect(mockSpinner.succeed).toHaveBeenCalledWith("✅ API Key created successfully!");
|
|
115
|
+
expect(consoleLogSpy).toHaveBeenCalledWith("🔒 Key Secret:", mockResponse.apiKey.clientSecret);
|
|
116
|
+
expect(consoleLogSpy).not.toHaveBeenCalledWith(
|
|
117
|
+
"Please save these credentials securely. The key secret will not be shown again."
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should create a client API key for mobile platform successfully", async () => {
|
|
122
|
+
vi.mocked(select)
|
|
123
|
+
.mockResolvedValueOnce("client" as APIKeyUsageOrigin)
|
|
124
|
+
.mockResolvedValueOnce("mobile");
|
|
125
|
+
vi.mocked(input).mockResolvedValueOnce("com.example.app");
|
|
126
|
+
vi.mocked(checkbox).mockResolvedValueOnce([APIKeyScopes.WALLET_READ]);
|
|
127
|
+
|
|
128
|
+
const mockResponse = {
|
|
129
|
+
apiKey: {
|
|
130
|
+
clientId: "test-client-id",
|
|
131
|
+
clientSecret: "test-client-secret",
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
vi.mocked(fetchAuthenticated).mockResolvedValueOnce(mockResponse);
|
|
135
|
+
|
|
136
|
+
await createKey();
|
|
137
|
+
|
|
138
|
+
expect(fetchAuthenticated).toHaveBeenCalledWith(
|
|
139
|
+
expect.stringContaining(`/api/console/projects/${mockProjectId}/apiKeys`),
|
|
140
|
+
"POST",
|
|
141
|
+
{
|
|
142
|
+
usageOrigin: "client",
|
|
143
|
+
scopes: [APIKeyScopes.WALLET_READ],
|
|
144
|
+
whitelistedOrigins: [],
|
|
145
|
+
whitelistedAppIdentifiers: ["com.example.app"],
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should create a client API key with multiple valid domains", async () => {
|
|
151
|
+
vi.mocked(select)
|
|
152
|
+
.mockResolvedValueOnce("client" as APIKeyUsageOrigin)
|
|
153
|
+
.mockResolvedValueOnce("web");
|
|
154
|
+
vi.mocked(input).mockResolvedValueOnce("https://example.com, https://api.example.com, http://localhost:3000");
|
|
155
|
+
vi.mocked(checkbox).mockResolvedValueOnce([APIKeyScopes.WALLET_READ]);
|
|
156
|
+
|
|
157
|
+
const mockResponse = {
|
|
158
|
+
apiKey: {
|
|
159
|
+
clientSecret: "test-client-secret",
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
vi.mocked(fetchAuthenticated).mockResolvedValueOnce(mockResponse);
|
|
163
|
+
|
|
164
|
+
await createKey();
|
|
165
|
+
|
|
166
|
+
expect(fetchAuthenticated).toHaveBeenCalledWith(
|
|
167
|
+
expect.stringContaining(`/api/console/projects/${mockProjectId}/apiKeys`),
|
|
168
|
+
"POST",
|
|
169
|
+
{
|
|
170
|
+
usageOrigin: "client",
|
|
171
|
+
scopes: [APIKeyScopes.WALLET_READ],
|
|
172
|
+
whitelistedOrigins: ["https://example.com", "https://api.example.com", "http://localhost:3000"],
|
|
173
|
+
whitelistedAppIdentifiers: [],
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("should create a client API key with multiple valid app identifiers", async () => {
|
|
179
|
+
vi.mocked(select)
|
|
180
|
+
.mockResolvedValueOnce("client" as APIKeyUsageOrigin)
|
|
181
|
+
.mockResolvedValueOnce("mobile");
|
|
182
|
+
vi.mocked(input).mockResolvedValueOnce("com.example.app,com.example.app2, com.example.app3");
|
|
183
|
+
vi.mocked(checkbox).mockResolvedValueOnce([APIKeyScopes.WALLET_READ]);
|
|
184
|
+
|
|
185
|
+
const mockResponse = {
|
|
186
|
+
apiKey: {
|
|
187
|
+
clientId: "test-client-id",
|
|
188
|
+
clientSecret: "test-client-secret",
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
vi.mocked(fetchAuthenticated).mockResolvedValueOnce(mockResponse);
|
|
192
|
+
|
|
193
|
+
await createKey();
|
|
194
|
+
|
|
195
|
+
expect(fetchAuthenticated).toHaveBeenCalledWith(
|
|
196
|
+
expect.stringContaining(`/api/console/projects/${mockProjectId}/apiKeys`),
|
|
197
|
+
"POST",
|
|
198
|
+
{
|
|
199
|
+
usageOrigin: "client",
|
|
200
|
+
scopes: [APIKeyScopes.WALLET_READ],
|
|
201
|
+
whitelistedOrigins: [],
|
|
202
|
+
whitelistedAppIdentifiers: ["com.example.app", "com.example.app2", "com.example.app3"],
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should handle API errors gracefully", async () => {
|
|
208
|
+
vi.mocked(select).mockResolvedValueOnce("server" as APIKeyUsageOrigin);
|
|
209
|
+
vi.mocked(checkbox).mockResolvedValueOnce([APIKeyScopes.NFTS_READ]);
|
|
210
|
+
|
|
211
|
+
const error = new Error("API Error");
|
|
212
|
+
vi.mocked(fetchAuthenticated).mockRejectedValueOnce(error);
|
|
213
|
+
|
|
214
|
+
await expect(createKey()).rejects.toThrow();
|
|
215
|
+
|
|
216
|
+
expect(mockSpinner.fail).toHaveBeenCalledWith("❌ Failed to create key: API Error");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("should exit if no project is selected", async () => {
|
|
220
|
+
vi.mocked(getProjectId).mockResolvedValueOnce(null);
|
|
221
|
+
|
|
222
|
+
const consoleErrorSpy = vi.spyOn(console, "error");
|
|
223
|
+
|
|
224
|
+
await expect(createKey()).rejects.toThrow();
|
|
225
|
+
|
|
226
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
227
|
+
"There is no project selected, please first run `crossmint project select`"
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { select, checkbox } from "@inquirer/prompts";
|
|
2
|
+
import { routes, HttpMethods } from "@crossmint/common-consts";
|
|
3
|
+
import { getScopeChoices } from "@/utils/scopes";
|
|
4
|
+
import { APIKeyUsageOrigin } from "@crossmint/products-console-types";
|
|
5
|
+
import { fetchAuthenticated } from "@/utils/fetch";
|
|
6
|
+
import { getProjectId, getEnvironment } from "@/utils/store";
|
|
7
|
+
import { createSpinner } from "@/utils/spinner";
|
|
8
|
+
import { getProject } from "@/utils/getProject";
|
|
9
|
+
import { ClientPlatform } from "@/types/keys";
|
|
10
|
+
import { getWhitelistedOriginsAndAppIdentifiersPrompt } from "@/utils/getWhitelistedOriginsAndAppIdentifiersPrompt";
|
|
11
|
+
import { APIKeyPayload } from "@/types/keys";
|
|
12
|
+
|
|
13
|
+
export default async function createKey() {
|
|
14
|
+
const environment = await getEnvironment();
|
|
15
|
+
const projectId = await getProjectId();
|
|
16
|
+
if (!environment) {
|
|
17
|
+
console.error("There is no environment selected, please first run `crossmint login`");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (!projectId) {
|
|
21
|
+
console.error("There is no project selected, please first run `crossmint project select`");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const spinner = createSpinner("Creating key...");
|
|
25
|
+
try {
|
|
26
|
+
const keyType = await select<APIKeyUsageOrigin>({
|
|
27
|
+
message: "Select Key Usage:",
|
|
28
|
+
choices: [
|
|
29
|
+
{ name: "Server side", value: "server" },
|
|
30
|
+
{ name: "Client side", value: "client" },
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
let clientPlatform: ClientPlatform | undefined;
|
|
35
|
+
let whitelistedOrigins: string[] | undefined;
|
|
36
|
+
let whitelistedAppIdentifiers: string[] | undefined;
|
|
37
|
+
|
|
38
|
+
if (keyType === "client") {
|
|
39
|
+
clientPlatform = await select<ClientPlatform>({
|
|
40
|
+
message: "Select client platform:",
|
|
41
|
+
choices: [
|
|
42
|
+
{ name: "Web", value: "web" },
|
|
43
|
+
{ name: "Mobile", value: "mobile" },
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
({ whitelistedOrigins, whitelistedAppIdentifiers } =
|
|
47
|
+
await getWhitelistedOriginsAndAppIdentifiersPrompt(clientPlatform));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const activeProject = await getProject(projectId);
|
|
51
|
+
if (!activeProject) {
|
|
52
|
+
console.error("❌ Project not found, please run `crossmint project select`");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const choices = getScopeChoices(keyType, environment, activeProject);
|
|
56
|
+
|
|
57
|
+
const selectedScopes = await checkbox({
|
|
58
|
+
message: `Select scopes for ${keyType} key:`,
|
|
59
|
+
choices,
|
|
60
|
+
validate: (input) => {
|
|
61
|
+
if (input.length === 0) {
|
|
62
|
+
return "At least one scope must be selected";
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const payload: APIKeyPayload = {
|
|
69
|
+
usageOrigin: keyType,
|
|
70
|
+
scopes: selectedScopes,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (keyType === "client") {
|
|
74
|
+
payload.whitelistedOrigins = whitelistedOrigins;
|
|
75
|
+
payload.whitelistedAppIdentifiers = whitelistedAppIdentifiers;
|
|
76
|
+
}
|
|
77
|
+
spinner.start();
|
|
78
|
+
|
|
79
|
+
const response = await fetchAuthenticated(
|
|
80
|
+
routes.api.console.projects.apiKeys(projectId),
|
|
81
|
+
HttpMethods.POST,
|
|
82
|
+
payload
|
|
83
|
+
);
|
|
84
|
+
spinner.succeed("✅ API Key created successfully!");
|
|
85
|
+
console.log("Key ID:", response.apiKey._id);
|
|
86
|
+
console.log("🔒 Key Secret:", response.apiKey.clientSecret);
|
|
87
|
+
if (keyType === "server" && environment === "production") {
|
|
88
|
+
console.log("Please save these credentials securely. The key secret will not be shown again.");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
process.exit(0);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
spinner.fail(`❌ Failed to create key: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|