cornilius 1.0.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/LICENSE +5 -0
- package/README.md +77 -0
- package/dist/cornilius.mjs +22 -0
- package/package.json +37 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Cornilius CLI
|
|
2
|
+
|
|
3
|
+
The installable, branded command-line client for **Cornilius** — your Head of
|
|
4
|
+
Analytics, operated from the terminal. It's the human "front door" that compiles
|
|
5
|
+
to the **same `dispatch`** the Claude.ai MCP tools use (one vocabulary, two
|
|
6
|
+
front doors).
|
|
7
|
+
|
|
8
|
+
This is a **proprietary** client (UNLICENSED), shipped as a single **minified**
|
|
9
|
+
Node bundle — zero runtime dependencies, Node 18+. The published package is the
|
|
10
|
+
built artifact.
|
|
11
|
+
|
|
12
|
+
## Install (Node 18+)
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx cornilius # run without installing
|
|
16
|
+
npm i -g cornilius # or install globally, then just: cornilius
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Use
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cornilius # interactive — splash + prompt (sign in with /login)
|
|
23
|
+
cornilius /playbooks # one-shot mode (run one command and exit)
|
|
24
|
+
cornilius --demo # offline UX preview (no token, no network)
|
|
25
|
+
cornilius --splash # print the branded splash and exit
|
|
26
|
+
cornilius --version # print the version
|
|
27
|
+
cornilius --help # usage
|
|
28
|
+
NO_COLOR=1 cornilius # plain ASCII (no color)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Auth:** `/login` opens your browser and signs you in (RFC 8252 native-app
|
|
32
|
+
loopback: PKCE + CSRF `state`, a one-time local `127.0.0.1` callback, code →
|
|
33
|
+
token exchange). It reuses Cornilius's existing OAuth server and the cornilius.ai
|
|
34
|
+
sign-in — no new accounts. The token is stored at `~/.cornilius/credentials.json`
|
|
35
|
+
(`0600`). `CORNILIUS_TOKEN` works as a manual override (e.g. CI); the token is
|
|
36
|
+
refused over non-HTTPS, and a non-default host is warned.
|
|
37
|
+
|
|
38
|
+
Env: `CORNILIUS_API` (default `https://api.cornilius.ai`), `CORNILIUS_TOKEN`,
|
|
39
|
+
`CORNILIUS_DEMO=1`, `CORNILIUS_INSECURE=1` (allow non-HTTPS — dev only),
|
|
40
|
+
`NO_COLOR=1`.
|
|
41
|
+
|
|
42
|
+
## Command vocabulary
|
|
43
|
+
|
|
44
|
+
Modelled on **Claude Code** (natural language is the primary input; slash
|
|
45
|
+
commands handle control/meta) and peer CLIs (`gh`/Vercel/Heroku resource→verb).
|
|
46
|
+
|
|
47
|
+
**Just type your question** → Cornilius runs the analysis.
|
|
48
|
+
|
|
49
|
+
| Command | Does |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `/playbooks` | list saved playbooks (numbered) |
|
|
52
|
+
| `/run <#\|id>` | run an exact playbook — no guessing |
|
|
53
|
+
| `/recall [topic]` | what Cornilius remembers (issues, insights, history) |
|
|
54
|
+
| `/delete <type> <id>` | delete something — two-step, asks you to confirm |
|
|
55
|
+
| `/status` | tenant, session, usage |
|
|
56
|
+
| `/whoami` | your tenant id |
|
|
57
|
+
| `/login` `/logout` | sign in / sign out (browser OAuth) |
|
|
58
|
+
| `/clear` `/help` `/exit` | local / session |
|
|
59
|
+
|
|
60
|
+
## Develop & release (maintainers)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install # esbuild (build-time only)
|
|
64
|
+
npm run build # bundle + minify src/cornilius.mjs → dist/cornilius.mjs
|
|
65
|
+
node src/cornilius.mjs --demo # run the source directly during development
|
|
66
|
+
npm pack # build + pack the publishable tarball (for smoke tests)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Releases publish automatically — bump the version and push a tag:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm version patch # or minor / major
|
|
73
|
+
git push --follow-tags # the publish.yml GitHub Action builds + publishes to npm
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The first `v*` tag claims the unscoped `cornilius` name; subsequent tags publish
|
|
77
|
+
new versions. See `.github/workflows/publish.yml`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import K from"node:readline";import h from"node:fs";import J from"node:os";import A from"node:path";import R from"node:crypto";import Y from"node:http";import{spawn as G}from"node:child_process";var j=process.argv.slice(2),O=j.includes("--demo")||process.env.CORNILIUS_DEMO==="1",f=(process.env.CORNILIUS_API||"https://api.cornilius.ai").replace(/\/+$/,""),F=A.join(J.homedir(),".cornilius"),m=A.join(F,"credentials.json"),Q=process.stdout.isTTY&&!process.env.NO_COLOR,C=o=>e=>Q?`\x1B[${o}m${e}\x1B[0m`:e,S=C("38;5;208"),a=C("38;5;230"),r=C("38;5;245"),H=C("1"),l=C("38;5;203"),V={C:[" \u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D","\u2588\u2588\u2551 ","\u2588\u2588\u2551 ","\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557"," \u255A\u2550\u2550\u2550\u2550\u2550\u255D"],O:[" \u2588\u2588\u2588\u2588\u2588\u2588\u2557 ","\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557","\u2588\u2588\u2551 \u2588\u2588\u2551","\u2588\u2588\u2551 \u2588\u2588\u2551","\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D"," \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],R:["\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557","\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D","\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557","\u2588\u2588\u2551 \u2588\u2588\u2551","\u255A\u2550\u255D \u255A\u2550\u255D"],N:["\u2588\u2588\u2588\u2557 \u2588\u2588\u2557","\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551","\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551","\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551","\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551","\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D"],I:["\u2588\u2588\u2557","\u2588\u2588\u2551","\u2588\u2588\u2551","\u2588\u2588\u2551","\u2588\u2588\u2551","\u255A\u2550\u255D"],L:["\u2588\u2588\u2557 ","\u2588\u2588\u2551 ","\u2588\u2588\u2551 ","\u2588\u2588\u2551 ","\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"],U:["\u2588\u2588\u2557 \u2588\u2588\u2557","\u2588\u2588\u2551 \u2588\u2588\u2551","\u2588\u2588\u2551 \u2588\u2588\u2551","\u2588\u2588\u2551 \u2588\u2588\u2551","\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D"," \u255A\u2550\u2550\u2550\u2550\u2550\u255D "],S:["\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D","\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557","\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551","\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551","\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"]};function X(o){let e=["","","","","",""];for(let t of o){let n=V[t],i=Math.max(...n.map(s=>[...s].length));for(let s=0;s<6;s++)e[s]+=n[s].padEnd(i," ")+" "}return e.map(t=>S(t)).join(`
|
|
3
|
+
`)}function z(){let o=O?"demo mode (offline, sample data)":new URL(f).host;console.log(),console.log(X("CORNILIUS")),console.log(),console.log(" "+H(a("Head of Analytics"))+r(" \xB7 ")+a(o));let e=O?r("(demo)"):D()?S("\u25CF token loaded"):r("\u25CB not signed in \u2014 /login");console.log(" "+e),console.log(" "+r("Type your analytics question, or ")+a("/help")+r(" for commands. ")+a("/exit")+r(" to quit.")),console.log()}function D(){if(process.env.CORNILIUS_TOKEN)return process.env.CORNILIUS_TOKEN.trim();try{return(JSON.parse(h.readFileSync(m,"utf8")).token||"").trim()||null}catch{return null}}function M(){try{return JSON.parse(h.readFileSync(m,"utf8"))}catch{return{}}}function W(o){h.mkdirSync(F,{recursive:!0,mode:448});let e=`${m}.tmp`;h.writeFileSync(e,JSON.stringify(o),{mode:384}),h.renameSync(e,m);try{h.chmodSync(m,384);let t=h.statSync(m).mode&511;t!==384&&console.log(" "+l(`warning: ${m} is ${t.toString(8)}, not 600 \u2014 your token may be readable by others.`))}catch(t){console.log(" "+l(`warning: could not secure ${m} (${t.message}).`))}}function Z(){try{h.rmSync(m)}catch{}}var T="api.cornilius.ai",U=!1,ee=3e4;async function L(o){if(O)return le(o);let e=D();if(!e)return{status:401,body:{error:"no token"}};let t;try{let u=new URL(f);if(t=u.host,u.protocol!=="https:"&&process.env.CORNILIUS_INSECURE!=="1")return{status:0,networkError:`refusing to send your token over ${u.protocol}// \u2014 set CORNILIUS_INSECURE=1 to override`}}catch{return{status:0,networkError:"invalid CORNILIUS_API"}}t!==T&&!U&&(U=!0,console.log(" "+l(`sending your token to ${t}`)+r(` (not the default ${T})`)));let n=new AbortController,i=setTimeout(()=>n.abort(),ee),s;try{s=await fetch(`${f}/api/command`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify({command:o}),signal:n.signal})}catch(u){return{status:0,networkError:u.name==="AbortError"?"request timed out":String(u.cause?.code||u.message||u)}}finally{clearTimeout(i)}let c=null;try{c=await s.json()}catch{c=null}return{status:s.status,body:c}}var q=[],k=null;function E(o,e=a){let t=o&&o.content||[];for(let n of t)n&&typeof n.text=="string"&&console.log(" "+e(n.text))}function P(o,{status:e,body:t,networkError:n}){if(n!==void 0){console.log(" "+l(`Couldn't reach Cornilius (${n}).`)+r(` [${f}]`));return}if(e===0)return;if(e===401){console.log(" "+l("Not signed in.")+r(" Run ")+a("/login")+r(" (or set CORNILIUS_TOKEN)."));return}if(e===429){let c=t&&(t.message||t.content&&t.content[0]&&t.content[0].text)||"Quota exceeded.";console.log(" "+l("\u26A0 "+c)),t&&t.upgrade_url&&console.log(" "+r(t.upgrade_url));return}if(!t){console.log(" "+l(`Unexpected response (HTTP ${e}).`));return}let i=t.kind;t.structuredContent&&Array.isArray(t.structuredContent.playbooks)&&(q=t.structuredContent.playbooks);let s=t.structuredContent&&t.structuredContent.confirmation_token;if(s&&/\/delete\b/.test(o)&&!/--confirm\b/.test(o)){let c=o.match(/\/delete\s+(\S+)\s+(\S+)/);if(c){k={type:c[1],id:c[2],token:s},E(t,l),console.log(" "+a("Type the word ")+H(a("delete"))+a(" to confirm, or anything else to cancel."));return}}if(i==="error"||t.isError)return E(t,l);i!=="noop"&&E(t,a)}function te(){let o=[["just type\u2026","ask anything \u2014 Cornilius runs the analysis"],["/playbooks","list saved playbooks (numbered)"],["/run <#|id>","run an exact playbook \u2014 no guessing"],["/recall [topic]","what Cornilius remembers (issues, insights, history)"],["/delete <type> <id>","delete something \u2014 two-step, asks you to confirm"],["/status","tenant, session, usage"],["/whoami","who am I \u2014 your tenant id"],["/login /logout","sign in / sign out"],["/clear","clear the screen"],["/help","this help"],["/exit","quit (or Ctrl-D)"]];console.log();for(let[e,t]of o)console.log(" "+a(e.padEnd(22))+r(t));console.log()}function $(o){return o.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function oe(){let o=$(R.randomBytes(32)),e=$(R.createHash("sha256").update(o,"ascii").digest());return{verifier:o,challenge:e}}function ne(){return $(R.randomBytes(24))}async function re(o){let e=M();if(e.client_id&&e.api===f)return e.client_id;let t=await fetch(`${f}/register`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_name:"Cornilius CLI",redirect_uris:[o],grant_types:["authorization_code"],token_endpoint_auth_method:"none"})});if(!t.ok)throw new Error(`registration failed (${t.status}): ${(await t.text()).slice(0,200)}`);let n=await t.json();if(!n.client_id)throw new Error("registration returned no client_id");return W({...e,client_id:n.client_id,api:f}),n.client_id}function se(o){let e=process.platform==="darwin"?"open":process.platform==="win32"?"cmd":"xdg-open",t=process.platform==="win32"?["/c","start","",o]:[o];try{return G(e,t,{stdio:"ignore",detached:!0}).unref(),!0}catch{return!1}}function ie(o){return`<!doctype html><meta charset="utf-8"><title>Cornilius</title><body style="font:16px system-ui;background:#0A0A0A;color:#FFF7ED;display:grid;place-items:center;height:100vh;margin:0"><div style="max-width:32rem;padding:2rem;text-align:center"><div style="color:#F59E0B;font-size:1.4rem;font-weight:700;margin-bottom:.75rem">Cornilius</div><p>${o==="success"?"Signed in to Cornilius. You can close this tab and return to your terminal.":o==="denied"?"Sign-in was cancelled. You can close this tab.":"Something went wrong. Return to your terminal and try again."}</p></div></body>`}async function ce(){if(O){console.log(" "+r("(demo) sign-in is simulated."));return}if(process.env.CORNILIUS_TOKEN){console.log(" "+r("CORNILIUS_TOKEN is set \u2014 that token is already used; /login not needed."));return}let o="/callback",e=18e4,{verifier:t,challenge:n}=oe(),i=ne(),s,c,u=new Promise((d,g)=>{s=d,c=g}),b=Y.createServer((d,g)=>{let w=new URL(d.url,"http://127.0.0.1");if(w.pathname!==o){g.writeHead(404).end();return}let p=w.searchParams.get("error"),_=w.searchParams.get("code"),v=w.searchParams.get("state"),I=!p&&_&&v===i;g.writeHead(I?200:400,{"Content-Type":"text/html; charset=utf-8"}),g.end(ie(I?"success":p==="access_denied"?"denied":"error")),p?c(new Error(p==="access_denied"?"you cancelled sign-in.":`authorization error: ${p}`)):v!==i?c(new Error("state mismatch \u2014 possible CSRF; aborting.")):_?s(_):c(new Error("no authorization code in callback."))});await new Promise(d=>b.listen(0,"127.0.0.1",d));let x=`http://127.0.0.1:${b.address().port}${o}`;try{let d=await re(x),g=`${f}/authorize?`+new URLSearchParams({response_type:"code",client_id:d,redirect_uri:x,state:i,code_challenge:n,code_challenge_method:"S256",scope:"mcp"}).toString();console.log(" "+S("Opening your browser to sign in\u2026")),console.log(" "+r(`If it doesn't open, paste this URL:
|
|
4
|
+
`)+a(g)),se(g);let w=await Promise.race([u,new Promise((v,I)=>setTimeout(()=>I(new Error("timed out after 3 minutes waiting for sign-in.")),e))]),p=await fetch(`${f}/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:w,redirect_uri:x,client_id:d,code_verifier:t}).toString()});if(!p.ok)throw new Error(`token exchange failed (${p.status}): ${(await p.text()).slice(0,200)}`);let _=await p.json();if(!_.access_token)throw new Error("token endpoint returned no access_token");W({...M(),token:_.access_token,client_id:d,api:f}),console.log(" "+S("\u2713 Signed in.")+r(` Token stored at ${m} (0600). Run `)+a("/status")+r(" to verify."))}catch(d){console.log(" "+l(`Sign-in failed: ${d.message}`)),process.exitCode=1}finally{b.close()}}function ae(){Z(),console.log(" "+r("Signed out \u2014 local token cleared."))}async function B(o){let e=o.trim();if(!e)return;if(k){let{type:i,id:s,token:c}=k;k=null,e==="delete"?P(`/delete ${i} ${s}`,await L(`/delete ${i} ${s} --token ${c} --confirm delete`)):console.log(" "+r("cancelled \u2014 nothing was deleted."));return}if(e==="/help"||e==="/?"||e==="/")return te();if(e==="/clear"){console.clear(),z();return}if((e==="/exit"||e==="/quit")&&(console.log(r(" bye.")),process.exit(0)),e==="/logout")return ae();if(e==="/login")return ce();let t=e,n=e.match(/^\/run\s+(\d+)\s*$/);if(n){let i=q.find(s=>s.number===Number(n[1]));if(i&&i.id)t=`/run ${i.id}`;else{console.log(" "+l("Run /playbooks first, then /run <#>")+r(" (or /run <playbook-id> directly)."));return}}P(t,await L(t))}function le(o){let e=o.trim(),t=[{number:1,id:"pb-builtin-funnel-pa-step-dropoff",name:"Funnel step drop-off scan"},{number:2,id:"pb-builtin-retention-pa-cohort-curve",name:"Cohort retention curve diagnosis"},{number:3,id:"pb-builtin-activation-aha-finder",name:"Activation aha-moment finder"}];if(e==="/playbooks")return{status:200,body:{kind:"dispatch",structuredContent:{playbooks:t},content:[{type:"text",text:`\u{1F4CB} Saved playbooks (demo)
|
|
5
|
+
`+t.map(n=>` ${n.number}. ${n.name}`).join(`
|
|
6
|
+
`)}]}};if(e.startsWith("/run "))return{status:200,body:{kind:"dispatch",content:[{type:"text",text:`\u25B8 running ${e.slice(5)} (demo) \u2192 analyze(playbook_id=\u2026)`}]}};if(e.startsWith("/recall"))return{status:200,body:{kind:"dispatch",content:[{type:"text",text:"\u{1F9E0} Remembered (demo): 2 open issues \xB7 5 insights"}]}};if(e.startsWith("/status")||e.startsWith("/whoami"))return{status:200,body:{kind:"local",content:[{type:"text",text:"tenant acme-corp \xB7 ready (demo)"}]}};if(e.startsWith("/delete ")){let n=e.match(/\/delete\s+(\S+)\s+(\S+)/);return/--confirm\b/.test(e)?{status:200,body:{kind:"dispatch",content:[{type:"text",text:`\u2713 deleted ${n?n[1]+" '"+n[2]+"'":"item"} (demo) \u2014 recoverable for 30 days`}]}}:{status:200,body:{kind:"dispatch",isError:!0,structuredContent:{confirmation_token:"demo-token"},content:[{type:"text",text:`\u26A0 This will delete ${n?n[1]+" '"+n[2]+"'":"an item"} (demo).`}]}}}return e.startsWith("/")?{status:400,body:{kind:"error",content:[{type:"text",text:"unknown command (demo)"}]}}:{status:200,body:{kind:"dispatch",content:[{type:"text",text:`\u25B8 analyzing (demo) \u2192 analyze(type='custom', prompt='${e}')`}]}}}function de(){console.log(`
|
|
7
|
+
Cornilius \u2014 operate your Head of Analytics from the terminal.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
cornilius start the interactive prompt (sign in with /login)
|
|
11
|
+
cornilius <command> run one command and exit, e.g. cornilius /playbooks
|
|
12
|
+
cornilius --demo offline UX preview (no token, no network)
|
|
13
|
+
cornilius --splash print the branded splash and exit
|
|
14
|
+
cornilius --version print the version
|
|
15
|
+
cornilius --help this help
|
|
16
|
+
|
|
17
|
+
In the prompt: just type a question, or /help for the command list.
|
|
18
|
+
|
|
19
|
+
Env: CORNILIUS_API (default https://api.cornilius.ai), CORNILIUS_TOKEN (override),
|
|
20
|
+
CORNILIUS_DEMO=1, NO_COLOR=1.
|
|
21
|
+
`)}var y=j.filter(o=>o!=="--demo");(y[0]==="--version"||y[0]==="-v")&&(console.log("1.0.0"),process.exit(0));(y[0]==="--help"||y[0]==="-h")&&(de(),process.exit(0));y.length&&y[0]!=="--splash"&&(await B(y.join(" ")),k&&console.log(" "+r("Two-step delete needs interactive mode \u2014 run ")+a("cornilius")+r(" with no arguments.")),process.exit(0));z();y[0]==="--splash"&&process.exit(0);var N=K.createInterface({input:process.stdin,output:process.stdout,prompt:S("cornilius> ")});N.prompt();for await(let o of N){try{await B(o)}catch(e){console.log(" "+l("error: "+(e.message||e)))}N.prompt()}console.log(r(`
|
|
22
|
+
bye.`));process.exit(0);
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cornilius",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Cornilius CLI — operate your Head of Analytics from the terminal.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"cornilius": "dist/cornilius.mjs"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/cornilius.mjs",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "node scripts/build.mjs",
|
|
19
|
+
"prepack": "npm run build",
|
|
20
|
+
"start": "node src/cornilius.mjs"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"cornilius",
|
|
24
|
+
"analytics",
|
|
25
|
+
"cli",
|
|
26
|
+
"mcp",
|
|
27
|
+
"head-of-analytics"
|
|
28
|
+
],
|
|
29
|
+
"license": "UNLICENSED",
|
|
30
|
+
"homepage": "https://cornilius.ai",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"esbuild": "^0.28.0"
|
|
36
|
+
}
|
|
37
|
+
}
|