@meeting-ai/cli 0.1.0-nightly.1

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 ADDED
@@ -0,0 +1,213 @@
1
+ # meeting — CLI for Meeting.ai
2
+
3
+ ![Meeting.ai CLI](docs/cli-screenshot.jpg)
4
+
5
+ Beautiful, agent-friendly command-line interface for Meeting.ai. Wraps the Audyno REST API.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g @meeting-ai/cli
11
+ ```
12
+
13
+ Or from source:
14
+
15
+ ```bash
16
+ git clone <repo> && cd meeting-cli
17
+ npm install && npm run build && npm link
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```bash
23
+ # Show welcome banner
24
+ meeting
25
+
26
+ # Log in
27
+ meeting auth login
28
+
29
+ # List recent meetings (pretty output)
30
+ meeting list --limit 10
31
+
32
+ # Same thing, JSON output (for agents/scripts)
33
+ meeting list --limit 10 --json
34
+ ```
35
+
36
+ ## Authentication
37
+
38
+ ```bash
39
+ # Interactive login (email OTP) — beautiful styled prompts
40
+ meeting auth login
41
+
42
+ # Check who you're logged in as
43
+ meeting auth whoami
44
+ ```
45
+
46
+ ## Usage
47
+
48
+ ### Meetings
49
+
50
+ ```bash
51
+ # List recent meetings — pretty card layout with emoji, duration, summaries
52
+ meeting list --limit 10
53
+
54
+ # Read meeting notes (own or shared via URL)
55
+ meeting read <meeting_id>
56
+
57
+ # Search meetings
58
+ meeting search "quarterly review"
59
+
60
+ # Delete a meeting
61
+ meeting delete <meeting_id>
62
+ ```
63
+
64
+ **Human output:**
65
+ ```
66
+ Recent Meetings 42 total
67
+
68
+ 📋 Q1 Product Roadmap Review
69
+ Mar 17 · Google Meet · 45 min · 4 speakers
70
+ Reviewed upcoming features, timeline adjustments, and resource allocation
71
+
72
+ 🎯 Weekly Engineering Standup
73
+ Mar 14 · Zoom · 30 min · 6 speakers
74
+ Sprint progress, blockers, and deployment schedule...
75
+ ```
76
+
77
+ **JSON output** (`--json`):
78
+ ```json
79
+ {
80
+ "total_item": 42,
81
+ "items_per_page": 10,
82
+ "meeting": [
83
+ { "id": "abc123", "title": "Q1 Product Roadmap Review", "status": "done", "..." : "..." }
84
+ ]
85
+ }
86
+ ```
87
+
88
+ ### Bot
89
+
90
+ ```bash
91
+ # Send bot to a live meeting
92
+ meeting bot send --url https://meet.google.com/abc-defg-hij
93
+
94
+ # Send bot and wait for transcript
95
+ meeting bot send --url <url> --wait --json
96
+
97
+ # Check bot status
98
+ meeting bot status <meeting_id>
99
+
100
+ # Signal bot to leave
101
+ meeting bot leave <meeting_id>
102
+ ```
103
+
104
+ ### Calendar
105
+
106
+ ```bash
107
+ # List upcoming events
108
+ meeting calendar events --upcoming
109
+
110
+ # List events in a date range
111
+ meeting calendar events --from 2024-01-01 --to 2024-01-31
112
+
113
+ # Trigger calendar sync
114
+ meeting calendar sync
115
+
116
+ # List connected accounts
117
+ meeting calendar accounts
118
+ ```
119
+
120
+ ### Upload
121
+
122
+ ```bash
123
+ # Upload a local file
124
+ meeting upload recording.mp4
125
+
126
+ # Upload and wait for transcript
127
+ meeting upload recording.mp4 --wait --json
128
+
129
+ # Submit a URL
130
+ meeting upload https://example.com/recording.mp4
131
+ ```
132
+
133
+ ### Transcript
134
+
135
+ ```bash
136
+ # Get AI-processed notes and transcript
137
+ meeting transcript <meeting_id>
138
+
139
+ # Show complete raw transcript
140
+ meeting transcript <meeting_id> --full
141
+
142
+ # Search within transcript
143
+ meeting transcript <meeting_id> --search "action items"
144
+
145
+ # Filter by speaker
146
+ meeting transcript <meeting_id> --speaker "John"
147
+
148
+ # Filter by time range (minutes)
149
+ meeting transcript <meeting_id> --from-time 10 --to-time 30
150
+ ```
151
+
152
+ ### Contacts
153
+
154
+ ```bash
155
+ # List contacts — name, role, meeting count
156
+ meeting contacts list
157
+
158
+ # Search contacts
159
+ meeting contacts list --search "John"
160
+
161
+ # Get contact details
162
+ meeting contacts get <contact_id>
163
+
164
+ # List meetings with a contact
165
+ meeting contacts meetings <contact_id>
166
+ ```
167
+
168
+ ### Schema Introspection
169
+
170
+ ```bash
171
+ # List all commands and their schemas
172
+ meeting schema
173
+
174
+ # Get schema for a specific command
175
+ meeting schema meetings.list --json
176
+ ```
177
+
178
+ ## Agent-Friendly Features
179
+
180
+ Every command supports:
181
+
182
+ - **`--json`** — structured JSON output to stdout (auto-enabled when piped/non-TTY)
183
+ - **`--fields`** — select specific fields: `meeting list --json --fields meeting,total_item`
184
+ - **`--wait`** — poll async operations until complete (bot send, upload)
185
+ - **`--wait-timeout`** — configurable timeout in seconds (default 300)
186
+
187
+ ### Exit Codes
188
+
189
+ | Code | Meaning |
190
+ |------|---------|
191
+ | 0 | Success |
192
+ | 1 | General error |
193
+ | 2 | Auth error |
194
+ | 3 | Not found |
195
+ | 4 | Validation error |
196
+
197
+ ### Error Format (JSON mode)
198
+
199
+ ```json
200
+ {"error": true, "code": "AUTH_EXPIRED", "message": "Token expired, run meeting auth login", "exit_code": 2}
201
+ ```
202
+
203
+ ## Development
204
+
205
+ ```bash
206
+ npm install
207
+ npm run build # type check + esbuild bundle -> dist/bin/meeting.js
208
+ npm run dev # tsx watch
209
+ ```
210
+
211
+ ## License
212
+
213
+ MIT
package/SKILL.md ADDED
@@ -0,0 +1,63 @@
1
+ ---
2
+ name: meeting-cli
3
+ description: "Interact with Meeting.ai via the `meeting` CLI. Use when user asks about meetings, meeting notes, transcripts, login to meeting.ai, or anything related to Meeting.ai. IMPORTANT: All Meeting.ai operations (login, reading notes, uploading, etc.) are done through the `meeting` CLI command — never use a browser. Login uses `meeting auth login --email <email>` which sends an OTP to email."
4
+ ---
5
+
6
+ # Meeting.ai CLI
7
+
8
+ All Meeting.ai operations go through the `meeting` CLI. Never use a browser.
9
+
10
+ ## Authentication
11
+
12
+ ```
13
+ meeting auth login --email user@example.com # Sends OTP to email, then prompts for code
14
+ meeting auth whoami # Check who you're logged in as
15
+ meeting auth logout # Clear stored tokens
16
+ ```
17
+
18
+ Login flow: run `meeting auth login --email <email>` → OTP is sent to email → ask user for the OTP code → enter it when prompted. If OTP is wrong, it retries up to 3 times.
19
+
20
+ ## Commands
21
+
22
+ ```
23
+ meeting read <url_or_id> [--pin <pin>] Read meeting notes (own or shared)
24
+ meeting search <query> Search meetings by keyword
25
+ meeting list [--limit N] List recent meetings
26
+ meeting transcript <id> Speakers + AI summary (default)
27
+ meeting transcript <id> --full Full word-by-word transcript
28
+ meeting transcript <id> --search "query" Search within transcript
29
+ meeting upload <file_or_url> Upload recording for transcription
30
+ meeting upload <file> --wait Upload and wait for completion
31
+ meeting contacts list List contacts
32
+ meeting calendar events --upcoming View upcoming calendar events
33
+ meeting bot send --url <meeting_url> Send bot to a live meeting
34
+ meeting delete <id> Delete a meeting (irreversible)
35
+ meeting auth whoami Check current login
36
+ meeting schema List all commands and params
37
+ ```
38
+
39
+ ## Key Behaviors
40
+
41
+ - Output is human-readable by default. Use `--json` only when piping to other tools.
42
+ - `meeting read` works with both meeting IDs and `https://app.meeting.ai/m/...` URLs.
43
+ - Shared meetings require `--pin`. Own meetings don't.
44
+ - `meeting list` returns 5 most recent meetings with IDs — use these IDs for other commands.
45
+ - After `meeting upload`, the CLI prints status. If status is "created" or "processing_audio", transcription is in progress — no need to wait.
46
+ - `meeting transcript` without flags shows speakers + AI summary. Add `--full` for the raw transcript.
47
+
48
+ ## Typical Workflows
49
+
50
+ **User asks "what was discussed in my last meeting?"**
51
+ 1. `meeting list --limit 1` → get the meeting ID
52
+ 2. `meeting read <id>` → read the full notes
53
+
54
+ **User shares a meeting.ai link + PIN**
55
+ 1. `meeting read <url> --pin <pin>` → read the shared notes
56
+
57
+ **User wants to upload a recording**
58
+ 1. `meeting upload recording.mp4` → upload and let it process
59
+ 2. Wait a few minutes, then `meeting read <id>` to check notes
60
+
61
+ **User searches for a topic across meetings**
62
+ 1. `meeting search "budget review"` → find matching meetings
63
+ 2. `meeting read <id>` → read the relevant one
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ var rt=Object.defineProperty;var st=(t,e)=>()=>(t&&(e=t(t=0)),e);var at=(t,e)=>{for(var n in e)rt(t,n,{get:e[n],enumerable:!0})};var Te={};at(Te,{prompt:()=>R});import{createInterface as mt}from"readline";function R(t){let e=mt({input:process.stdin,output:process.stderr});return new Promise(n=>{e.question(t,i=>{e.close(),n(i.trim())})})}var V=st(()=>{"use strict"});import{Command as nn}from"commander";import{Command as ht}from"commander";import I from"chalk";import lt from"os";import{chmodSync as Z}from"fs";import ct from"conf";var M=new ct({projectName:"meeting",schema:{tokens:{type:"object",properties:{access:{type:"string"},refresh:{type:"string"},expires_at:{type:"number"}}},api_url:{type:"string"},email:{type:"string"}}});try{Z(M.path,384)}catch{}function ue(){return M}function D(){let t=M.get("api_url")||"https://api.meeting.ai";if(!t.startsWith("https://"))throw new Error("Refusing to connect over insecure HTTP.");return t}function Oe(){return M.get("tokens")}function X(t){M.set("tokens",t);try{Z(M.path,384)}catch{}}function Q(){M.delete("tokens"),M.delete("email");try{Z(M.path,384)}catch{}}function ee(){return M.get("email")}function te(t){M.set("email",t);try{Z(M.path,384)}catch{}}function fe(t){try{let e=JSON.parse(Buffer.from(t.split(".")[1],"base64url").toString());if(typeof e.exp=="number")return e.exp*1e3}catch{}return Date.now()+900*60*1e3}async function ne(){let t=Oe();if(!t)throw new F("Not logged in. Run: meeting auth login");return gt(t)?(await dt(t)).access:t.access}function gt(t){return Date.now()>=t.expires_at-3e4}async function dt(t){let e=`${D()}/api/v1/auth/token/refresh`,n=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access:t.access,refresh:t.refresh})});if(!n.ok)throw new F("Token refresh failed. Run: meeting auth login");let i=await n.json(),r={access:i.jwt.token,refresh:i.jwt.refreshToken,expires_at:fe(i.jwt.token)};return X(r),r}async function B(t){let e=`${D()}/api/v1/auth/otp/request`,n=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t})});if(!n.ok){let r=await n.text();throw new Error(`OTP request failed: ${r}`)}return(await n.json()).challenge_id}async function oe(t,e,n){let i=`${D()}/api/v1/auth/login/email/mobile`,r=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({login:{grantType:"mail_otp",email:t,otp:e,challenge_id:n},device_info:{device_id:lt.hostname(),os_name:"cli",app_version:"0.0.1"}})});if(!r.ok){let a=await r.text();throw new Error(`Login failed: ${a}`)}let o=await r.json();if(o.mfa_required&&o.challenge)return{mfa_required:!0,challenge_id:o.challenge.challenge_id,mfa_type:o.mfa?.type||o.challenge.challenge_type,mfa_id:o.mfa?.id||"",session_id:o.session_id||""};if(o.is_new_user)throw new Error("Account not found. Please sign up at https://meeting.ai first.");if(!o.jwt?.token||!o.jwt?.refreshToken)throw new Error("Login response missing JWT tokens");let s={access:o.jwt.token,refresh:o.jwt.refreshToken,expires_at:fe(o.jwt.token)};return X(s),{mfa_required:!1,tokens:s}}async function z(t,e){let n=`${D()}/api/v1/auth/mfa`,i=await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mfa:{id:t,session_id:e}})});if(!i.ok){let r=await i.text();throw new Error(`Failed to send 2FA email: ${r}`)}}async function ie(t,e,n,i){let r=`${D()}/api/v1/auth/mfa/verify`,o={session_id:n,challenge_id:t};i==="password"?o.password=e:o.totp=e;let s=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mfa:o})});if(!s.ok){let l=await s.text();throw new Error(`MFA verification failed: ${l}`)}let a=await s.json(),c={access:a.jwt.token,refresh:a.jwt.refreshToken,expires_at:fe(a.jwt.token)};return X(c),c}var F=class extends Error{constructor(e){super(e),this.name="AuthError"}};V();function N(t){if(!/^[a-zA-Z0-9\-]{10,30}$/.test(t)){let e=new Error("Invalid meeting ID format (expected alphanumeric+hyphen, 10-30 chars)");throw Object.assign(e,{exit_code:4,code:"VALIDATION_ERROR"}),e}}function he(t){if(!/^[a-zA-Z0-9\-]{10,30}$/.test(t)){let e=new Error("Invalid contact ID format (expected alphanumeric+hyphen, 10-30 chars)");throw Object.assign(e,{exit_code:4,code:"VALIDATION_ERROR"}),e}}function ve(t){if(!t.includes("@")){let e=new Error("Invalid email format");throw Object.assign(e,{exit_code:4,code:"VALIDATION_ERROR"}),e}}import U from"chalk";var h=U.hex("#2563EB"),yn=U.hex("#22C55E"),g=U.hex("#6B7280"),_n=U.hex("#F59E0B");function p(t){return t.json===!0||!process.stdout.isTTY}function pt(t,e){if(!e)return t;let n=e.split(",").map(i=>i.trim());return Array.isArray(t)?t.map(i=>Ce(i,n)):Ce(t,n)}function Ce(t,e){if(!t||typeof t!="object")return{};let n={};for(let i of e){let r=i.split("."),o=t;for(let s of r)if(o&&typeof o=="object"&&s in o)o=o[s];else{o=void 0;break}o!==void 0&&(n[i]=o)}return n}function w(t,e){let n=pt(t,e.fields);process.stdout.write(JSON.stringify(n,null,2)+`
3
+ `)}function ut(t){let e={error:!0,code:t.code||"UNKNOWN_ERROR",message:t.message,exit_code:t.exit_code||1};process.stderr.write(JSON.stringify(e)+`
4
+ `)}function J(t,e){if(t.length===0){console.log(g(" No results."));return}let n=e.map(r=>{let o=t.map(s=>Me(s[r.key],r.format).length);return Math.max(r.label.length,...o)}),i=e.map((r,o)=>U.bold(r.label.padEnd(n[o]))).join(" ");console.log(i),console.log(g("\u2500".repeat(i.length)));for(let r of t){let o=e.map((s,a)=>Me(r[s.key],s.format).padEnd(n[a])).join(" ");console.log(o)}}function Me(t,e){return e?e(t):t==null?"-":String(t)}function v(t){let e=Math.max(...Object.keys(t).map(n=>n.length));for(let[n,i]of Object.entries(t)){let r=U.bold(n.padEnd(e)),o=i==null?g("-"):String(i);console.log(` ${r} ${o}`)}}function b(t,e){let n=t,i=n.exit_code||1,r=n.message||"Unknown error",o=n.code||"UNKNOWN_ERROR";p(e)?ut({code:o,message:r,exit_code:i}):console.error(U.red(`Error: ${r}`)+" "+g(`(${o})`)),process.exit(i)}function E(t){if(!t||t<=0)return"-";let e=Math.floor(t/3600),n=Math.floor(t%3600/60);return e>0?`${e}h ${n}m`:`${n} min`}function P(t){return t?new Date(t).toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit"}):"-"}function re(t){return t?new Date(t).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}):"-"}function se(t){return{zoom:"\u{1F4F9}",gmeet:"\u{1F7E2}",teams:"\u{1F512}",webex:"\u{1F310}",upload:"\u{1F4E4}",livestream:"\u{1F30D}",record:"\u{1F399}\uFE0F"}[t?.toLowerCase()]||"\u{1F4CB}"}function W(t){return{"google meet":"gmeet",googlemeet:"gmeet",google:"gmeet",meet:"gmeet",gmeet:"gmeet",zoom:"zoom",teams:"teams","microsoft teams":"teams",msteams:"teams",webex:"webex",upload:"upload",livestream:"livestream",live:"livestream",record:"record",recording:"record"}[t.toLowerCase()]||t.toLowerCase()}function ae(t){return{zoom:"Zoom",gmeet:"Google Meet",teams:"Teams",webex:"Webex",upload:"Upload",livestream:"Livestream",record:"Recording"}[t?.toLowerCase()]||t||"-"}function q(t){let e=typeof t=="string"?parseInt(t,10):t;return isNaN(e)||e<5?5:e}import{Command as ft}from"commander";import A from"chalk";var ce=new Map;function k(t,e){ce.set(t,e)}function Re(){let t=new ft("schema").description("Introspect command schemas");return t.argument("[command]","Command name (e.g., meetings.list)").option("--json","Output as JSON").option("--fields <fields>","Select output fields").action((e,n)=>{try{if(!e){let r=Object.fromEntries(ce);if(p(n))w(r,n);else{console.log(A.bold(`Available commands:
5
+ `));for(let[o,s]of ce)console.log(` ${A.bold(o)} \u2014 ${s.description}`)}return}let i=ce.get(e);if(!i)throw Object.assign(new Error(`Unknown command: ${e}. Run 'meeting schema' to list all.`),{exit_code:3,code:"NOT_FOUND"});if(p(n))w({command:e,...i},n);else{if(console.log(A.bold(e)+" \u2014 "+i.description+`
6
+ `),Object.keys(i.params).length>0){console.log(A.bold("Parameters:"));for(let[r,o]of Object.entries(i.params)){let s=o.required?A.red("*"):"";console.log(` ${A.cyan(r)}${s} (${o.type}) \u2014 ${o.description}`)}console.log()}console.log(A.bold("Output schema:")),v(i.output)}}catch(i){b(i,n)}}),t}var le=I.hex("#22C55E");function Ee(){let t=new ht("auth").description("Authentication commands");return t.command("login").description("Log in with email OTP").option("--json","Output as JSON").option("--email <email>","Email address (skip prompt)").action(async e=>{try{p(e)||(console.log(),console.log(` ${h.bold("\u{1F511} Log in to meeting.ai")}`),console.log());let n=e.email||await R(` ${I.bold("Email:")} `);if(!n)throw Object.assign(new Error("Email is required"),{exit_code:4,code:"VALIDATION_ERROR"});ve(n);let i;if(p(e))i=await B(n);else{let a=(await import("ora")).default,c=a({text:"Sending OTP code...",color:"blue"}).start();i=await B(n),c.stop(),console.log(` ${le("\u2713")} OTP sent! Check your email`),console.log()}let r=3,o;for(let a=1;a<=r;a++){let c=!p(e)&&a>1?g(` (attempt ${a}/${r})`):"",l=await R(` ${I.bold("OTP Code:")}${c} `);if(!l){if(a<r){p(e)||console.log(` ${I.yellow("\u26A0")} OTP cannot be empty, try again`);continue}throw Object.assign(new Error("OTP code is required (max attempts exceeded)"),{exit_code:4,code:"VALIDATION_ERROR"})}try{o=await oe(n,l,i);break}catch(d){if(a<r){p(e)||console.log(` ${I.yellow("\u26A0")} Invalid OTP, try again`);continue}throw d}}let s;if(o.mfa_required){let a=o.mfa_type;if(a==="webauthn")throw Object.assign(new Error("WebAuthn/passkey 2FA is not supported in CLI. Please log in via the web app at https://meeting.ai"),{exit_code:1,code:"MFA_NOT_SUPPORTED"});let c;if(a==="totp")c=` ${I.bold("Authenticator code:")} `;else if(a==="password")c=` ${I.bold("Password:")} `;else{if(p(e))await z(o.mfa_id,o.session_id);else{let d=(await import("ora")).default,m=d({text:"Sending 2FA code to email...",color:"blue"}).start();await z(o.mfa_id,o.session_id),m.stop(),console.log(` ${le("\u2713")} 2FA code sent to your email`),console.log()}c=` ${I.bold("2FA code (check email):")} `}let l=await R(c);if(!l)throw Object.assign(new Error("2FA code is required"),{exit_code:4,code:"VALIDATION_ERROR"});s=await ie(o.challenge_id,l,o.session_id,a)}else s=o.tokens;te(n),p(e)?w({success:!0,email:n,expires_at:s.expires_at},e):(console.log(` ${le("\u2713")} Logged in as ${I.bold(n)}`),console.log(),console.log(g(" Your token is stored at ~/.config/meeting/config.json")),console.log())}catch(n){b(n,e)}}),t.command("whoami").description("Show current user info").option("--json","Output as JSON").option("--fields <fields>","Select output fields").action(async e=>{try{let n=await ne(),i=JSON.parse(Buffer.from(n.split(".")[1],"base64url").toString()),r={id:i.id,name:i.name,email:i.email||ee()||"unknown",session_id:i.sessionId,token_expires:new Date(i.exp*1e3).toISOString()};p(e)?w(r,e):(console.log(),console.log(` ${h.bold("\u{1F464} Current User")}`),console.log(),v({Id:r.id,Name:r.name,Email:r.email,"Token Expires":r.token_expires}),console.log())}catch(n){b(n,e)}}),t.command("logout").description("Clear stored tokens").option("--json","Output as JSON").action(e=>{Q(),p(e)?w({success:!0,message:"Logged out"},e):console.log(` ${le("\u2713")} Logged out successfully.`)}),k("auth.login",{description:"Log in with email OTP",params:{email:{type:"string",description:"Email address",required:!1}},output:{type:"object",properties:{success:{type:"boolean"},email:{type:"string"},expires_at:{type:"number"}}}}),k("auth.whoami",{description:"Show current user info (decoded from JWT)",params:{},output:{type:"object",properties:{id:{type:"string"},name:{type:"string"},email:{type:"string"},session_id:{type:"string"},token_expires:{type:"string",description:"ISO 8601 timestamp"}}}}),k("auth.logout",{description:"Clear stored tokens",params:{},output:{type:"object",properties:{success:{type:"boolean"},message:{type:"string"}}}}),t}import{Command as Ie}from"commander";import L from"chalk";var yt="cli-0.1.0-nightly.1";async function _(t,e={}){let{method:n="GET",body:i,query:r,headers:o={},auth:s=!0,multipart:a}=e,c=D(),l=new URL(t,c);if(r){for(let[f,y]of Object.entries(r))if(y!==void 0)if(Array.isArray(y))for(let $ of y)l.searchParams.append(f,$);else l.searchParams.set(f,String(y))}let d={"x-client-app-version":yt,...o};if(s)try{let f=await ne();d.Authorization=`Bearer ${f}`}catch(f){throw f instanceof F?Object.assign(f,{exit_code:2}):f}let m;a?m=a:i&&(d["Content-Type"]="application/json",m=JSON.stringify(i));let u=await fetch(l.toString(),{method:n,headers:d,body:m});if(!u.ok){let f=u.status,y;try{let S=await u.json(),T=C=>{if(typeof C=="string")return C;if(C&&typeof C=="object"&&"message"in C){let j=C.message;if(typeof j=="string")return j}};y=T(S.message)||T(S.error)||(S.message?JSON.stringify(S.message):void 0)||u.statusText}catch{y=u.statusText}let $=f===401||f===403?2:f===404?3:f===422?4:1,x=f===401?"AUTH_EXPIRED":f===403?"FORBIDDEN":f===404?"NOT_FOUND":f===422?"VALIDATION_ERROR":"API_ERROR",O=new Error(y);throw O.error=!0,O.code=x,O.message=y,O.exit_code=$,O}if(u.status!==204)return u.json()}V();function Y(t){return t.is_title_edited&&t.title?t.title:t.autogenerated_title||t.title||"(untitled)"}function ye(t){return t.autogenerated_title_emoji?t.autogenerated_title_emoji:se(t.integration)}function _t(t){return t.meeting_participants?.length?t.meeting_participants.length:t.speaker_count||0}function _e(t){return(t.meeting_participants||t.participants||[]).map(n=>n.name).filter(n=>!!n)}function we(t){return t.replace(/<em>(.*?)<\/em>/g,(e,n)=>L.bold.white(n))}function wt(t,e=200){let n=t.split(`
7
+
8
+ `)[0].trim();if(n.length<=e)return n;let i=n.slice(0,e),r=i.lastIndexOf(". ");if(r>e*.5)return i.slice(0,r+1);let o=i.lastIndexOf(" ");return o>e*.5?i.slice(0,o)+"...":i+"..."}function bt(t,e=4){let i=(t.meeting_participants||[]).filter(s=>s.name);return{names:[...i].sort((s,a)=>(a.speaking_seconds||0)-(s.speaking_seconds||0)).slice(0,e).map(s=>s.name).filter(s=>!!s),more:Math.max(0,i.length-e)}}function ge(t,e){let n=ye(t),i=Y(t),r=ae(t.integration),o=E(t.duration_in_seconds),s=P(t.started_at||t.created_at),a=_t(t),c=a?`${a} speaker${a>1?"s":""}`:"",l=[s,r,o,c].filter(Boolean).join(g(" \xB7 "));if(console.log(` ${n} ${L.bold(i)}`),console.log(` ${g(t.id)}`),t.status&&t.status!=="finished"){let y=L.yellow(`[${t.status}]`);console.log(` ${l} ${y}`)}else console.log(` ${l}`);t.role==="read"&&console.log(` ${L.cyan("[shared]")}`);let{names:d,more:m}=bt(t);if(d.length>0){let y=d.join(", ")+(m>0?` ${g(`+${m} more`)}`:"");console.log(` ${g(y)}`)}let u=t.meeting_section,f=Array.isArray(u)?u[0]?.text:u?.text;if(f){let y=wt(f);console.log(` ${g(L.italic(y))}`)}e&&e(),console.log()}function Le(){let t=new Ie("list").description("Show recent meetings").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--from <date>",'Filter from date (e.g. 2026-03-01, "last week")').option("--to <date>","Filter to date (e.g. 2026-03-18)").option("--after <date>","Alias for --from").option("--before <date>","Alias for --to").option("--role <role>","Filter by role (owner/read/all)").option("--integration <type>","Filter by integration (zoom/gmeet/teams/livestream/upload)").option("--speaker <name>","Filter by participant name").option("--sort <order>","Sort order (newest/oldest)","newest").option("--status <status>","Filter by status (finished/recording/processing)").option("--limit <n>","Items per page","10").option("--offset <n>","Cursor offset").action(async e=>{try{let n=e.from||e.after,i=e.to||e.before,r=q(e.limit),o;if(!p(e)){let a=(await import("ora")).default;o=a({text:"Loading meetings...",color:"blue"}).start()}let s=await _("/api/v1/meetings",{query:{start_date:n,end_date:i,role:e.role,integration:e.integration?W(e.integration):void 0,status:e.status,sort:e.sort==="oldest"?"asc":void 0,items_per_page:String(r),cursor:e.offset}});if(o?.stop(),p(e))w(s,e);else{let a=s.meeting||[];if(e.speaker){let c=e.speaker.toLowerCase();a=a.filter(l=>_e(l).some(m=>m.toLowerCase().includes(c)))}console.log(),console.log(` ${h.bold("Recent Meetings")}${g(` ${s.total_item.toLocaleString()} total`)}`),console.log();for(let c of a)ge(c);s.next_cursor&&(console.log(g(` More results available. Use --offset ${s.next_cursor}`)),console.log()),console.log(g(" Filters: --from, --to, --integration, --speaker, --role, --sort, --status")),console.log(g(` Search: ${h("meeting search <query>")}`)),console.log()}}catch(n){b(n,e)}});return k("list",{description:"List recent meetings",params:{from:{type:"string",description:"Filter from date (ISO 8601)",required:!1},to:{type:"string",description:"Filter to date (ISO 8601)",required:!1},role:{type:"string",description:"Filter by role (owner/read/all)",required:!1},integration:{type:"string",description:"Filter by integration (zoom/gmeet/teams/livestream/upload)",required:!1},speaker:{type:"string",description:"Filter by participant name (client-side)",required:!1},sort:{type:"string",description:"Sort order (newest/oldest)",required:!1},status:{type:"string",description:"Filter by status (finished/recording/processing)",required:!1},limit:{type:"number",description:"Items per page (default 10, min 5)",required:!1},offset:{type:"number",description:"Cursor offset",required:!1}},output:{type:"object",properties:{total_item:{type:"number"},items_per_page:{type:"number"},next_cursor:{type:"number"},prev_cursor:{type:"number"},meeting:{type:"array",items:{type:"object"}}}}}),t}function je(){let t=new Ie("delete").description("Delete a meeting").argument("[id]","Meeting ID").option("--json","Output as JSON").option("--yes","Skip confirmation prompt").action(async(e,n)=>{if(!e){let i=(await import("chalk")).default,r=i.hex("#2563EB"),o=i.hex("#6B7280");console.error(),console.error(i.red(" \u2717 Missing meeting ID.")),console.error(),console.error(` ${r("Usage:")} meeting delete ${o("<meeting_id>")}`),console.error(),console.error(` ${o("Example:")}`),console.error(` ${r("meeting delete")} 01abc...xyz`),console.error(),console.error(` ${o("\u{1F4A1} Run")} ${r("meeting list")} ${o("to see your meetings and their IDs.")}`),console.error(` ${o("\u26A0\uFE0F This action is permanent and cannot be undone.")}`),console.error(),process.exit(1)}try{if(N(e),!p(n)){let i=e;try{let r=await _(`/api/v1/meeting/${e}`);i=Y(r)}catch{}if(n.yes)console.log(L.yellow(" \u26A0\uFE0F Skipped confirmation via --yes flag"));else if(console.log(),console.log(L.yellow(` \u26A0\uFE0F Delete meeting "${i}" (${e})?`)),console.log(),console.log(" This action cannot be undone."),console.log(g(" If you are an AI agent, please ask your human to confirm this deletion.")),console.log(),await R(` Type ${L.bold("DELETE")} to confirm: `)!=="DELETE"){console.log(g(" Cancelled."));return}}await _(`/api/v1/meeting/${e}`,{method:"DELETE"}),p(n)?w({success:!0,id:e},n):console.log(L.hex("#22C55E")(` \u2713 Meeting ${e} deleted.`))}catch(i){b(i,n)}});return k("delete",{description:"Delete a meeting (soft delete)",params:{id:{type:"string",description:"Meeting ID",required:!0},yes:{type:"boolean",description:"Skip confirmation",required:!1}},output:{type:"object",properties:{success:{type:"boolean"},id:{type:"string"}}}}),t}import{Command as kt}from"commander";import K from"chalk";async function De(t){let e=[],n,r=0;for(;r<10;){let o={items_per_page:"100",cursor:n},s=await _(`/api/v1/meeting/${t}/transcript`,{query:o}),a=s.transcribe||[];if(e.push(...a),!s.next_cursor)break;n=String(s.next_cursor),r++}return e}function $t(t){let e=Math.floor(t/3600),n=Math.floor(t%3600/60),i=Math.floor(t%60);return e>0?`${e}:${String(n).padStart(2,"0")}:${String(i).padStart(2,"0")}`:`${n}:${String(i).padStart(2,"0")}`}function St(t,e){let n=new RegExp(`(${e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")})`,"gi");return t.replace(n,(i,r)=>K.bold.yellow(r))}function Pe(){let t=new kt("search").description("Search meetings (notes & transcripts)").argument("[query]","Search keyword").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--transcript","Search within transcript text").option("--limit <n>","Items per page","10").option("--offset <n>","Cursor offset").option("--from <date>","Start from date").option("--to <date>","End before date").option("--integration <type>","Filter by integration (zoom/gmeet/teams/livestream/upload)").option("--role <role>","Filter by role (owner/read)").option("--speaker <name>","Filter by participant name").option("--no-highlight","Hide search highlights").action(async(e,n)=>{e||(console.error(),console.error(K.red(" \u2717 Missing search query.")),console.error(),console.error(` ${h("Usage:")} meeting search ${g("<query>")}`),console.error(),console.error(` ${g("Examples:")}`),console.error(` ${h("meeting search")} "quarterly review"`),console.error(` ${h("meeting search")} marketing`),console.error(),console.error(` ${g("\u{1F4A1} Search looks through meeting titles, notes, and transcripts.")}`),console.error(),process.exit(1));try{let i=q(n.limit),r;if(!p(n)){let a=(await import("ora")).default;r=a({text:`Searching "${e}"...`,color:"blue"}).start()}let o=await _("/api/v1/meetings/search",{query:{keyword:e,items_per_page:String(n.transcript?Math.min(i,5):i),cursor:n.offset,start_from_date:n.from,end_before_date:n.to,integration:n.integration?W(n.integration):void 0,role:n.role}});r?.stop();let s=o.meeting||[];if(n.speaker){let a=n.speaker.toLowerCase();s=s.filter(c=>_e(c).some(d=>d.toLowerCase().includes(a)))}if(n.transcript&&!p(n)){let a=s.slice(0,5);if(a.length===0){console.log(),console.log(g(" No meetings found.")),console.log();return}if(!p(n)){let d=(await import("ora")).default;r=d({text:`Searching transcripts (${a.length} meetings)...`,color:"blue"}).start()}let c=await Promise.allSettled(a.map(d=>De(d.id).then(m=>({meeting:d,transcript:m}))));r?.stop(),console.log(),console.log(` ${h.bold("\u{1F50D} Transcript Search")}${g(` "${e}"`)}`),console.log();let l=!1;for(let d of c){if(d.status!=="fulfilled")continue;let{meeting:m,transcript:u}=d.value;if(u.length===0)continue;let f=e.toLowerCase(),y=[];for(let S=0;S<u.length;S++)u[S].text.toLowerCase().includes(f)&&y.push(S);if(y.length===0)continue;l=!0;let $=ye(m),x=Y(m),O=P(m.started_at||m.created_at);console.log(` ${$} ${K.bold(x)} ${g(`(${O})`)}`);for(let S of y.slice(0,3)){let T=u[S],C=$t(T.start),j=St(T.text.length>200?T.text.slice(0,200)+"...":T.text,e);console.log(` ${g('"')}${j}${g('"')}`),console.log(` ${g(`\u2014 ${T.speaker}, ${C}`)}`)}y.length>3&&console.log(` ${g(`+${y.length-3} more matches`)}`),console.log()}l||(console.log(g(" No transcript matches found.")),console.log());return}if(n.transcript&&p(n)){let a=s.slice(0,5),c=[],l=await Promise.allSettled(a.map(d=>De(d.id).then(m=>({meeting:d,transcript:m}))));for(let d of l){if(d.status!=="fulfilled")continue;let{meeting:m,transcript:u}=d.value,f=e.toLowerCase(),y=u.filter($=>$.text.toLowerCase().includes(f)).map($=>({speaker:$.speaker,text:$.text,start:$.start}));y.length>0&&c.push({meeting_id:m.id,title:Y(m),matches:y})}w({query:e,results:c,total_meetings_searched:a.length},n);return}if(p(n))w(o,n);else{console.log(),console.log(` ${h.bold("Search Results")}${g(` ${o.total_item} total`)}`),console.log();for(let a of s)ge(a,()=>{if(n.highlight!==!1){let c=a.highlights||[];for(let l of c.slice(0,3)){let d=l.content||l.text||"";if(!d)continue;let m=d.replace(/\[\d+\|\d+\.\d+-\d+\.\d+\]\s*/g,""),u=we(m.length>200?m.slice(0,200)+"...":m),y={transcription:"\u{1F4DD} Transcript",notes:"\u{1F4CB} Notes",template_section:"\u{1F4CB} Notes",title:"\u{1F4CC} Title"}[l.field||""]||l.field||"Unknown",$=l.reference?.timestamp_start?` ${g(`at ${Math.floor(l.reference.timestamp_start/60)}:${String(Math.floor(l.reference.timestamp_start%60)).padStart(2,"0")}`)}`:"";console.log(` ${K.cyan(y)}${$}`),console.log(` ${g("\u2192")} ${u}`)}}});if(o.next_cursor){if(console.log(g(` Showing ${s.length} of ${o.total_item} results`)),console.log(),process.stdout.isTTY){let{prompt:a}=await Promise.resolve().then(()=>(V(),Te)),c=await a(` ${h("\u2192")} Show more results? ${g("(y/n)")} `);if(c?.toLowerCase()==="y"||c?.toLowerCase()==="yes"){let l=await _("/api/v1/meetings/search",{query:{keyword:e,items_per_page:String(i),cursor:String(o.next_cursor),start_from_date:n.from,end_before_date:n.to,integration:n.integration?W(n.integration):void 0,role:n.role}}),d=l.meeting||[];console.log();for(let m of d)ge(m,()=>{let u=m.highlights||[];for(let f of u.slice(0,3)){let y=f.content||f.text||"";if(!y)continue;let $=y.replace(/\[\d+\|\d+\.\d+-\d+\.\d+\]\s*/g,""),x=we($.length>200?$.slice(0,200)+"...":$),S={transcription:"\u{1F4DD} Transcript",notes:"\u{1F4CB} Notes",template_section:"\u{1F4CB} Notes",title:"\u{1F4CC} Title"}[f.field||""]||f.field||"Unknown";console.log(` ${K.cyan(S)}`),console.log(` ${g("\u2192")} ${x}`)}});l.next_cursor&&console.log(g(` More available. Use: meeting search "${e}" --offset ${l.next_cursor}`))}}else console.log(g(` Next page: meeting search "${e}" --offset ${o.next_cursor}`));console.log()}console.log(g(` Tip: Use ${h("--transcript")} to search within transcript text`)),console.log()}}catch(i){b(i,n)}});return k("search",{description:"Search meetings by keyword (notes & transcripts)",params:{query:{type:"string",description:"Search keyword",required:!0},transcript:{type:"boolean",description:"Search within transcript text",required:!1},limit:{type:"number",description:"Items per page",required:!1},offset:{type:"number",description:"Cursor offset",required:!1},from:{type:"string",description:"Start from date",required:!1},to:{type:"string",description:"End before date",required:!1},integration:{type:"string",description:"Filter by integration",required:!1},role:{type:"string",description:"Filter by role",required:!1},speaker:{type:"string",description:"Filter by participant name",required:!1}},output:{type:"object",properties:{total_item:{type:"number"},meeting:{type:"array",items:{type:"object"}}}}}),t}import{Command as Ot}from"commander";import Tt from"chalk";async function de(t,e,n={}){let{interval:i=3e3,timeout:r=3e5}=n,o=Date.now()+r;for(;;){let s=await t();if(e(s))return s;if(Date.now()+i>o)throw new Error(`Polling timed out after ${r/1e3}s`);await xt(i)}}function xt(t){return new Promise(e=>setTimeout(e,t))}var Ne=Tt.hex("#22C55E");function Ue(){let t=new Ot("bot").description("Meeting bot commands");return t.command("send").description("Send bot to a meeting URL").requiredOption("--url <url>","Meeting URL to join").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--wait","Wait until meeting is complete").option("--wait-timeout <seconds>","Wait timeout in seconds","300").action(async e=>{try{let n;if(!p(e)){let o=(await import("ora")).default;n=o({text:"Sending bot to meeting...",color:"blue"}).start()}let i=await _("/api/v1/meeting",{method:"POST",query:{device:"cli"},body:{meeting:{url:e.url,integration:vt(e.url),started_at:new Date().toISOString(),language:"auto",participants:[]}}}),r=i.meeting||i;if(await _(`/api/v1/meeting/${r.id}/record-meeting`,{method:"POST",body:{is_user_rejoin:!1}}),n?.stop(),e.wait){if(!p(e)){let s=(await import("ora")).default;n=s({text:"Waiting for meeting to complete...",color:"blue"}).start()}let o=await de(()=>_(`/api/v1/meeting/${r.id}`),s=>s.status==="done"||s.status==="failed",{timeout:parseInt(e.waitTimeout)*1e3});n?.stop(),p(e)?w(o,e):(console.log(),v({ID:o.id,Title:o.title,Status:o.status,URL:o.url}),console.log())}else p(e)?w(r,e):(console.log(` ${Ne("\u2713")} Bot sent successfully.`),console.log(),v({ID:r.id,Title:r.title||"(pending)",Status:r.status}),console.log())}catch(n){b(n,e)}}),t.command("status <meeting_id>").description("Check bot/meeting status").option("--json","Output as JSON").option("--fields <fields>","Select output fields").action(async(e,n)=>{try{N(e);let i;if(!p(n)){let o=(await import("ora")).default;i=o({text:"Checking status...",color:"blue"}).start()}let r=await _(`/api/v1/meeting/${e}/progress`);i?.stop(),p(n)?w(r,n):(console.log(),console.log(` ${h.bold("\u{1F916} Bot Status")}`),console.log(),v({ID:r.id||e,Title:r.title,Status:r.status,Integration:r.integration}),console.log())}catch(i){b(i,n)}}),t.command("leave <meeting_id>").description("Signal bot to leave meeting").option("--json","Output as JSON").action(async(e,n)=>{try{N(e),await _("/api/v1/recall/leave",{method:"POST",body:{id:e}}),p(n)?w({success:!0,meeting_id:e},n):console.log(` ${Ne("\u2713")} Bot leaving meeting ${g(e)}.`)}catch(i){b(i,n)}}),k("bot.send",{description:"Send bot to a meeting URL",params:{url:{type:"string",description:"Meeting URL to join",required:!0},wait:{type:"boolean",description:"Wait until complete",required:!1},"wait-timeout":{type:"number",description:"Wait timeout in seconds (default 300)",required:!1}},output:{type:"object",properties:{id:{type:"string"},title:{type:"string"},status:{type:"string"},url:{type:"string"}}}}),k("bot.status",{description:"Check bot/meeting status",params:{meeting_id:{type:"string",description:"Meeting ID",required:!0}},output:{type:"object",properties:{id:{type:"string"},title:{type:"string"},status:{type:"string"},integration:{type:"string"},url:{type:"string"}}}}),k("bot.leave",{description:"Signal bot to leave meeting",params:{meeting_id:{type:"string",description:"Meeting ID",required:!0}},output:{type:"object",properties:{success:{type:"boolean"},meeting_id:{type:"string"}}}}),t}function vt(t){return t.includes("zoom.us")?"zoom":t.includes("meet.google.com")?"gmeet":t.includes("teams.microsoft.com")?"teams":t.includes("webex.com")?"webex":"gmeet"}import{Command as Ct}from"commander";import Mt from"chalk";var Rt=Mt.hex("#22C55E");function qe(){let t=new Ct("calendar").description("Calendar commands");return t.command("events").description("List calendar events").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--upcoming","Show upcoming events from today").option("--from <date>","Start date (YYYY-MM-DD)").option("--to <date>","End date (YYYY-MM-DD)").option("--limit <n>","Items per page","50").option("--timezone <tz>","Timezone (default: local)",Intl.DateTimeFormat().resolvedOptions().timeZone).action(async e=>{try{let n;if(!p(e)){let o=(await import("ora")).default;n=o({text:"Loading events...",color:"blue"}).start()}let i={items_per_page:e.limit,timezone:e.timezone};e.upcoming?i.date=new Date().toISOString().split("T")[0]:e.from&&e.to?(i.start_date=e.from,i.end_date=e.to):e.from&&(i.date=e.from);let r=await _("/api/v1/calendar/calendar_events",{query:i});if(n?.stop(),p(e))w(r,e);else{let o=r.calendar_events||[];console.log(),console.log(` ${h.bold("\u{1F4C5} Calendar Events")}${g(` ${o.length} shown`)}`),console.log(),J(o,[{key:"title",label:"Title"},{key:"status",label:"Status"},{key:"start_time",label:"Start",format:s=>s?new Date(s).toLocaleString():"-"},{key:"end_time",label:"End",format:s=>s?new Date(s).toLocaleString():"-"}]),console.log()}}catch(n){b(n,e)}}),t.command("sync").description("Trigger calendar sync").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--from <date>","Start date").option("--to <date>","End date").action(async e=>{try{let n;if(!p(e)){let r=(await import("ora")).default;n=r({text:"Syncing calendar...",color:"blue"}).start()}let i=await _("/api/v1/calendar/sync",{method:"POST",query:{start_date:e.from,end_date:e.to}});n?.stop(),p(e)?w(i,e):console.log(` ${Rt("\u2713")} Sync status: ${i.sync_status}`)}catch(n){b(n,e)}}),t.command("accounts").description("List calendar accounts").option("--json","Output as JSON").option("--fields <fields>","Select output fields").action(async e=>{try{let n;if(!p(e)){let r=(await import("ora")).default;n=r({text:"Loading accounts...",color:"blue"}).start()}let i=await _("/api/v1/calendar/accounts");if(n?.stop(),p(e))w(i,e);else{let r=i.calendar_accounts||[];console.log(),console.log(` ${h.bold("\u{1F517} Calendar Accounts")}${g(` ${r.length} connected`)}`),console.log(),J(r,[{key:"email",label:"Email"},{key:"integration_id",label:"Integration"},{key:"status",label:"Status"}]),console.log()}}catch(n){b(n,e)}}),k("calendar.events",{description:"List calendar events",params:{upcoming:{type:"boolean",description:"Show upcoming events from today",required:!1},from:{type:"string",description:"Start date (YYYY-MM-DD)",required:!1},to:{type:"string",description:"End date (YYYY-MM-DD)",required:!1},limit:{type:"number",description:"Items per page (default 50)",required:!1},timezone:{type:"string",description:"Timezone (default: local)",required:!1}},output:{type:"object",properties:{calendar_events:{type:"array",items:{type:"object"}},next_cursor:{type:"string"}}}}),k("calendar.sync",{description:"Trigger calendar sync",params:{from:{type:"string",description:"Start date (YYYY-MM-DD)",required:!1},to:{type:"string",description:"End date (YYYY-MM-DD)",required:!1}},output:{type:"object",properties:{sync_status:{type:"string"},synced_accounts:{type:"array"}}}}),k("calendar.accounts",{description:"List connected calendar accounts",params:{},output:{type:"object",properties:{user_id:{type:"string"},calendar_accounts:{type:"array",items:{type:"object"}}}}}),t}import{Command as Et}from"commander";import H from"chalk";import{open as It,stat as Lt}from"fs/promises";import{basename as Ae,extname as jt}from"path";var be=H.hex("#22C55E"),me=5*1024*1024;function Dt(t){return t.startsWith("http://")||t.startsWith("https://")}function Pt(t){let e=jt(t).toLowerCase();return{".mp3":"audio/mpeg",".wav":"audio/wav",".m4a":"audio/mp4",".aac":"audio/aac",".ogg":"audio/ogg",".flac":"audio/flac",".wma":"audio/x-ms-wma",".mp4":"video/mp4",".webm":"video/webm",".mkv":"video/x-matroska",".avi":"video/x-msvideo",".mov":"video/quicktime",".wmv":"video/x-ms-wmv",".flv":"video/x-flv"}[e]||"audio/mpeg"}function Nt(t){return t.startsWith("video/")?"video":"audio"}function ke(t){return t<1024?`${t}B`:t<1024*1024?`${(t/1024).toFixed(0)}KB`:t<1024*1024*1024?`${(t/(1024*1024)).toFixed(1)}MB`:`${(t/(1024*1024*1024)).toFixed(1)}GB`}function Ut(t,e,n){let i=Math.round(e/n*100),r=20,o=Math.round(e/n*r),s="\u2588".repeat(o)+"\u2591".repeat(r-o),a=` \u2B06 Uploading ${t} ${s} ${i}% (${ke(e)} / ${ke(n)})`;process.stdout.write(`\r${a}`)}function Fe(){let t=new Et("upload").description("Upload recording for transcription").argument("[source]","Local file path or URL").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--title <title>","Meeting title").option("--language <lang>","Language code (default: auto-detect)","auto").option("--wait","Wait until transcription is complete").option("--wait-timeout <seconds>","Wait timeout in seconds","300").action(async(e,n)=>{e||(console.error(),console.error(H.red(" \u2717 Missing file path or URL.")),console.error(),console.error(` ${H.hex("#2563EB")("Usage:")} meeting upload ${g("<file_or_url>")}`),console.error(),console.error(` ${g("Examples:")}`),console.error(` ${H.hex("#2563EB")("meeting upload")} recording.mp4`),console.error(` ${H.hex("#2563EB")("meeting upload")} https://example.com/audio.mp3`),console.error(),console.error(` ${g("\u{1F4A1} Supported formats: mp3, mp4, m4a, wav, webm, ogg, mov, avi.")}`),console.error(` ${g("\u{1F4A1} Language is auto-detected. Override with --language en.")}`),console.error(),process.exit(1));try{let i;if(Dt(e)){let r;if(!p(n)){let s=(await import("ora")).default;r=s({text:"Submitting URL for transcription...",color:"blue"}).start()}let o=await _("/api/v1/meeting",{method:"POST",query:{device:"cli"},body:{meeting:{url:e,title:n.title,integration:"upload",language:n.language,started_at:new Date().toISOString(),participants:["CLI User"]}}});i="meeting"in o&&o.meeting?o.meeting:o,r?.stop(),!p(n)&&!n.wait&&(console.log(` ${be("\u2713")} URL submitted for transcription.`),console.log())}else{let o=(await Lt(e)).size,s=Ae(e),a=Pt(e),c=Nt(a),l=Math.ceil(o/me);p(n)||(console.log(),console.log(` ${g("File:")} ${s} ${g(ke(o))} ${g(a)}`),console.log());let d;if(!p(n)){let x=(await import("ora")).default;d=x({text:"Creating meeting...",color:"blue"}).start()}let m=await _("/api/v1/meeting",{method:"POST",query:{device:"cli"},body:{meeting:{participants:["CLI User"],started_at:new Date().toISOString(),language:n.language,integration:"upload",...n.title?{title:n.title}:{}}}});i="meeting"in m&&m.meeting?m.meeting:m,d?.stop();let u=i.id;if(!p(n)){let x=(await import("ora")).default;d=x({text:"Initializing upload...",color:"blue"}).start()}let f=await _(`/api/v1/meeting/${u}/media2`,{method:"POST",body:{media:{file_name:s,file_size:o,mime_type:a,total_part:l,type:c}}});d?.stop();let y=f.meeting_file.id,$=await It(e,"r");try{let x=Buffer.alloc(me),O=0;for(let S=0;S<l;S++){let T=S*me,C=Math.min(me,o-T),{bytesRead:j}=await $.read(x,0,C,T),ot=x.subarray(0,j),it=T+j-1,xe=new FormData;xe.append("upload",new Blob([new Uint8Array(ot)])),await _(`/api/v1/meeting/${u}/media/${y}`,{method:"POST",multipart:xe,headers:{"First-Byte":String(T),"Last-Byte":String(it),"Total-Byte":String(o),"Part-Number":String(S)}}),O+=j,p(n)||Ut(s,O,o)}}finally{await $.close()}await _(`/api/v1/meeting/${u}`,{method:"PATCH",body:{meeting:{finished_at:new Date().toISOString()}}}),p(n)||(process.stdout.write(`
9
+ `),console.log(` ${be("\u2713")} Upload complete \u2014 transcription started.`),console.log())}if(n.wait){let r;if(!p(n)){let s=(await import("ora")).default;r=s({text:"Processing transcription...",color:"blue"}).start()}let o=await de(()=>_(`/api/v1/meeting/${i.id}`),s=>s.status==="done"||s.status==="finished"||s.status==="failed",{timeout:parseInt(n.waitTimeout)*1e3});r?.stop(),p(n)?w(o,n):(console.log(),v({ID:o.id,Title:o.title,Status:o.status}),console.log())}else if(p(n))w(i,n);else{v({ID:i.id,Title:i.title||Ae(e),Status:i.status}),console.log();let r=i.status;r==="created"||r==="uploading"?console.log(" \u23F3 File uploaded. Processing will start shortly \u2014 you can close this now."):r==="processing_audio"||r==="processing"?console.log(" \u23F3 Transcription in progress \u2014 you can close this now. It will take a few minutes."):r==="finished"||r==="done"?console.log(` ${be("\u2713")} Transcription complete \u2014 notes are ready!`):r==="failed"&&console.log(" \u274C Processing failed."),console.log(),console.log(g(` Check status: meeting read ${i.id}`)),console.log()}}catch(i){b(i,n)}});return k("upload",{description:"Upload a local file or URL for transcription",params:{source:{type:"string",description:"Local file path or URL",required:!0},title:{type:"string",description:"Meeting title",required:!1},language:{type:"string",description:"Language code (default: auto-detect)",required:!1},wait:{type:"boolean",description:"Wait until complete",required:!1},"wait-timeout":{type:"number",description:"Wait timeout in seconds",required:!1}},output:{type:"object",properties:{id:{type:"string"},title:{type:"string"},status:{type:"string"}}}}),t}import{Command as qt}from"commander";import G from"chalk";async function Je(t){let e=[],n;for(;;){let i={items_per_page:"100"};n&&(i.cursor=n);let r=await _(`/api/v1/meeting/${t}/transcript`,{query:i}),s=(r.transcribe||r.transcript||[]).map(a=>({...a,start:typeof a.start=="string"?parseFloat(a.start):a.start,end:typeof a.end=="string"?parseFloat(a.end):a.end,speaker:a.speaker||a.meeting_participants?.[0]?.name||a.contacts?.[0]?.name||"Unknown Speaker"}));if(e.push(...s),r.next_cursor)n=String(r.next_cursor);else break}return e}function $e(t){let e=Math.floor(t/3600),n=Math.floor(t%3600/60),i=Math.floor(t%60);return e>0?`${e}:${String(n).padStart(2,"0")}:${String(i).padStart(2,"0")}`:`${n}:${String(i).padStart(2,"0")}`}function At(t,e){let n=new RegExp(`(${e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")})`,"gi");return t.replace(n,(i,r)=>G.bold.yellow(r))}function Ft(t,e,n=5){let i=n*60,r=[];for(let c=0;c<t.length;c++)t[c].text.toLowerCase().includes(e.toLowerCase())&&r.push(c);if(r.length===0)return{sections:[],matchCount:0};let o=[];for(let c of r){let l=t[c].start;o.push({start:Math.max(0,l-i),end:l+i})}let s=[o[0]];for(let c=1;c<o.length;c++){let l=s[s.length-1];o[c].start<=l.end?l.end=Math.max(l.end,o[c].end):s.push(o[c])}let a=[];for(let c of s){let l=t.filter(d=>d.start>=c.start&&d.start<=c.end);l.length>0&&a.push(l)}return{sections:a,matchCount:r.length}}function Be(t,e){let n="";for(let i of t){i.speaker!==n&&(n&&console.log(),console.log(` ${G.bold(i.speaker)}`),console.log(g(" "+"\u2500".repeat(35))),n=i.speaker);let r=$e(i.start),o=e?At(i.text,e):i.text;console.log(` ${g(r)} ${o}`)}}function ze(){let t=new qt("transcript").description("Meeting transcript & speakers").argument("[meeting_id]","Meeting ID").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--search <query>","Search within transcript (shows \xB15min context)").option("--full","Show complete raw transcript").option("--speaker <name>","Filter by speaker name").option("--from-time <minutes>","Start from time (minutes)").option("--to-time <minutes>","End at time (minutes)").action(async(e,n)=>{e||(console.error(),console.error(G.red(" \u2717 Missing meeting ID.")),console.error(),console.error(` ${h("Usage:")} meeting transcript ${g("<meeting_id>")}`),console.error(),console.error(` ${g("Examples:")}`),console.error(` ${h("meeting transcript")} 01abc...xyz`),console.error(` ${h("meeting transcript")} 01abc...xyz --full`),console.error(` ${h("meeting transcript")} 01abc...xyz --search "action items"`),console.error(),console.error(` ${g("\u{1F4A1} Without flags, shows speakers and AI summary.")}`),console.error(` ${g("\u{1F4A1} Use --full for the complete word-by-word transcript.")}`),console.error(),process.exit(1));try{if(N(e),n.search){let s;if(!p(n)){let m=(await import("ora")).default;s=m({text:`Searching transcript for "${n.search}"...`,color:"blue"}).start()}let a=await Je(e);s?.stop();let c=a;if(n.speaker){let m=n.speaker.toLowerCase();c=c.filter(u=>u.speaker.toLowerCase().includes(m))}let{sections:l,matchCount:d}=Ft(c,n.search);if(p(n))w({matchCount:d,sections:l.flat()},n);else if(console.log(),console.log(` ${h.bold("\u{1F50D} Transcript Search")}${g(` ${d} match${d!==1?"es":""} for "${n.search}"`)}`),console.log(),l.length===0)console.log(g(" No matches found.")),console.log();else for(let m=0;m<l.length;m++){m>0&&(console.log(g(" \xB7\xB7\xB7 ")),console.log());let u=l[m][0],f=l[m][l[m].length-1];console.log(g(` [${$e(u.start)} \u2013 ${$e(f.end||f.start)}]`)),console.log(),Be(l[m],n.search),console.log()}return}if(n.full){let s;if(!p(n)){let c=(await import("ora")).default;s=c({text:"Loading transcript...",color:"blue"}).start()}let a=await Je(e);if(s?.stop(),n.speaker){let c=n.speaker.toLowerCase();a=a.filter(l=>l.speaker.toLowerCase().includes(c))}if(n.fromTime||n.toTime){let c=n.fromTime?parseFloat(n.fromTime):void 0,l=n.toTime?parseFloat(n.toTime):void 0;a=a.filter(d=>{let m=d.start/60;return!(c!==void 0&&m<c||l!==void 0&&m>l)})}p(n)?w({transcript:a,total:a.length},n):(console.log(),console.log(` ${h.bold("\u{1F399}\uFE0F Full Transcript")}`),console.log(),Be(a),console.log());return}let i;if(!p(n)){let s=(await import("ora")).default;i=s({text:"Loading transcript summary...",color:"blue"}).start()}let[r,o]=await Promise.all([_(`/api/v1/meeting/${e}/sections-complete`),_(`/api/v1/meeting/${e}/contacts`).catch(()=>null)]);if(i?.stop(),p(n))w({sections:r,speakers:o},n);else{let s=o?Array.isArray(o)?o:o.contacts||[]:[];if(s.length>0){console.log(),console.log(` ${h.bold("\u{1F465} Speakers")}`),console.log();let c=[...s].sort((l,d)=>(d.total_talk_time||0)-(l.total_talk_time||0));for(let l of c){let d=l.total_talk_time?E(l.total_talk_time):"",m=[l.role,l.company].filter(Boolean).join(" @ "),u=[d,m].filter(Boolean).join(g(" \xB7 "));console.log(` ${G.bold(l.name||"(unknown)")}${u?" "+g(u):""}`)}}let a=r.meeting_sections||r.sections||[];if(a.length>0){console.log(),console.log(` ${h.bold("\u{1F4DD} Summary")}`),console.log();for(let c of a){if(console.log(` ${G.bold(c.title||"Untitled Section")}`),console.log(g(" "+"\u2500".repeat(40))),c.content){let l=c.content.split(`
10
+ `);for(let d of l)console.log(` ${d}`)}console.log()}}console.log(g(` Use ${h("--search <query>")} to search transcript, or ${h("--full")} for raw transcript`)),console.log()}}catch(i){b(i,n)}});return k("transcript",{description:"Meeting transcript & speakers (summary by default, --search or --full for transcript)",params:{meeting_id:{type:"string",description:"Meeting ID",required:!0},search:{type:"string",description:"Search keyword (shows \xB15min context)",required:!1},full:{type:"boolean",description:"Show complete raw transcript",required:!1},speaker:{type:"string",description:"Filter by speaker name",required:!1},from_time:{type:"number",description:"Start from time in minutes",required:!1},to_time:{type:"number",description:"End at time in minutes",required:!1}},output:{type:"object",properties:{sections:{type:"object"},speakers:{type:"array",items:{type:"object"}}}}}),t}import{Command as Jt}from"commander";import Ve from"chalk";function We(){let t=new Jt("contacts").description("Manage contacts");return t.command("list").description("List contacts").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--search <keyword>","Search by name, role, or company").option("--sort <order>","Sort by (name/meetings/recent)","recent").option("--min-meetings <n>","Only contacts with at least N meetings").option("--limit <n>","Items per page","20").option("--offset <cursor>","Cursor for pagination").action(async e=>{try{let i=q(e.limit),r;if(!p(e)){let s=(await import("ora")).default;r=s({text:"Loading contacts...",color:"blue"}).start()}let o=await _("/api/v1/contacts",{query:{keyword:e.search,items_per_page:String(i),cursor:e.offset}});if(r?.stop(),p(e))w(o,e);else{let a=function(c,l){let d=l?`${g(`${l}.`)} `:" ",m=c.total_meeting_count?`${c.total_meeting_count} meetings`:"",u=c.last_meeting_at?`last met ${P(c.last_meeting_at)}`:"",f=[m,u].filter(Boolean).join(g(" \xB7 "));console.log(`${d}${Ve.bold(c.name||"(unknown)")}`),console.log(` ${g(c.id)}`),(c.role||c.company)&&console.log(` ${g([c.role,c.company].filter(Boolean).join(" @ "))}`),f&&console.log(` ${g(f)}`),console.log()};var n=a;let s=o.contacts||o.contact||[];if(e.minMeetings){let c=parseInt(e.minMeetings,10);isNaN(c)||(s=s.filter(l=>l.total_meeting_count>=c))}if(e.sort==="name"?s.sort((c,l)=>(c.name||"").localeCompare(l.name||"")):e.sort==="meetings"&&s.sort((c,l)=>(l.total_meeting_count||0)-(c.total_meeting_count||0)),console.log(),console.log(` ${h.bold("\u{1F465} Contacts")}${g(` ${o.total_item.toLocaleString()} total`)}`),!e.sort&&!e.search&&!e.minMeetings){let c=[...s].sort((d,m)=>(m.total_meeting_count||0)-(d.total_meeting_count||0)).slice(0,10),l=[...s].sort((d,m)=>{let u=d.last_meeting_at?new Date(d.last_meeting_at).getTime():0;return(m.last_meeting_at?new Date(m.last_meeting_at).getTime():0)-u}).slice(0,10);console.log(),console.log(h.bold(" \u{1F3C6} Top by Meeting Count")),console.log(g(" "+"\u2500".repeat(40))),console.log(),c.forEach((d,m)=>a(d,m+1)),console.log(h.bold(" \u{1F550} Recently Met")),console.log(g(" "+"\u2500".repeat(40))),console.log(),l.forEach((d,m)=>a(d,m+1))}else{console.log();for(let c of s)a(c)}o.next_cursor&&(console.log(g(` More results available. Use --offset ${o.next_cursor}`)),console.log()),console.log(g(" Filters: --sort (name/meetings/recent), --min-meetings, --keyword")),console.log()}}catch(i){b(i,e)}}),t.command("get <id>").description("Get contact details").option("--json","Output as JSON").option("--fields <fields>","Select output fields").action(async(e,n)=>{try{he(e);let i;if(!p(n)){let o=(await import("ora")).default;i=o({text:"Loading contact...",color:"blue"}).start()}let r=await _(`/api/v1/contact/${e}`);if(i?.stop(),p(n))w(r,n);else{let o=r.contact;console.log(),console.log(` ${Ve.bold(o.name||"(unknown)")}`),(o.role||o.company)&&console.log(` ${g([o.role,o.company].filter(Boolean).join(" @ "))}`),console.log(),v({ID:o.id,Emails:o.emails?.map(s=>s.email).join(", ")||"-","Total Meetings":String(o.total_meetings),"Total Time":o.total_meeting_seconds?E(o.total_meeting_seconds):"-"}),r.recent_meetings?.length&&(console.log(),console.log(h.bold(" Recent Meetings")),console.log(g(" "+"\u2500".repeat(40))),J(r.recent_meetings,[{key:"title",label:"Title"},{key:"status",label:"Status"},{key:"created_at",label:"Date",format:s=>s?P(s):"-"},{key:"duration_in_seconds",label:"Duration",format:s=>s?E(s):"-"}])),console.log()}}catch(i){b(i,n)}}),t.command("meetings <id>").description("List meetings for a contact").option("--json","Output as JSON").option("--fields <fields>","Select output fields").option("--limit <n>","Items per page","20").option("--offset <cursor>","Cursor for pagination").action(async(e,n)=>{try{he(e);let i=q(n.limit),r;if(!p(n)){let s=(await import("ora")).default;r=s({text:"Loading meetings...",color:"blue"}).start()}let o=await _(`/api/v1/contact/${e}/meetings`,{query:{items_per_page:String(i),cursor:n.offset}});if(r?.stop(),p(n))w(o,n);else{let s=o.meetings||[];console.log(),console.log(` ${h.bold("\u{1F4CB} Meetings with Contact")}`),console.log(),J(s,[{key:"title",label:"Title"},{key:"status",label:"Status"},{key:"started_at",label:"Date",format:a=>a?P(a):"-"},{key:"duration_in_seconds",label:"Duration",format:a=>a?E(a):"-"}]),console.log()}}catch(i){b(i,n)}}),k("contacts.list",{description:"List contacts",params:{search:{type:"string",description:"Search by name, role, or company",required:!1},sort:{type:"string",description:"Sort by (name/meetings/recent)",required:!1},min_meetings:{type:"number",description:"Only contacts with at least N meetings",required:!1},limit:{type:"number",description:"Items per page (default 20, min 5)",required:!1},offset:{type:"string",description:"Cursor for pagination",required:!1}},output:{type:"object",properties:{total_item:{type:"number"},contact:{type:"array",items:{type:"object"}},next_cursor:{type:"string"}}}}),k("contacts.get",{description:"Get contact details",params:{id:{type:"string",description:"Contact ID",required:!0}},output:{type:"object",properties:{contact:{type:"object"},recent_meetings:{type:"array"}}}}),k("contacts.meetings",{description:"List meetings for a contact",params:{id:{type:"string",description:"Contact ID",required:!0},limit:{type:"number",description:"Items per page",required:!1},offset:{type:"string",description:"Cursor for pagination",required:!1}},output:{type:"object",properties:{meetings:{type:"array",items:{type:"object"}},next_cursor:{type:"string"}}}}),t}import{Command as Bt}from"commander";import Se from"chalk";function zt(t){if(t.includes("meeting.ai/m/")){let e=t.match(/meeting\.ai\/m\/([^/?#]+)/);if(e)return e[1]}return t}function Vt(t){return t.is_title_edited&&t.title?t.title:t.autogenerated_title||t.title||"(untitled)"}function Wt(t){return t.autogenerated_title_emoji?t.autogenerated_title_emoji:se(t.integration)}function Yt(t,e=4){let i=(t.meeting_participants||[]).filter(s=>s.name);return{names:[...i].sort((s,a)=>(a.speaking_seconds||0)-(s.speaking_seconds||0)).slice(0,e).map(s=>s.name).filter(s=>!!s),more:Math.max(0,i.length-e)}}function Ke(){let t=new Bt("read").description("Read meeting notes (URL, ID, or link)").argument("[url_or_id]","Meeting URL (meeting.ai/m/...) or meeting ID").option("--pin <pin>","Access PIN for shared meetings").option("--json","Output as JSON").option("--fields <fields>","Select output fields").action(async(e,n)=>{e||(console.error(),console.error(Se.red(" \u2717 Missing meeting URL or ID.")),console.error(),console.error(` ${h("Usage:")} meeting read ${g("<url_or_id>")} ${g("[--pin <pin>]")}`),console.error(),console.error(` ${g("Examples:")}`),console.error(` ${h("meeting read")} 01abc...xyz`),console.error(` ${h("meeting read")} https://app.meeting.ai/m/01abc...xyz --pin 123456`),console.error(),console.error(` ${g("\u{1F4A1} Run")} ${h("meeting list")} ${g("to see your recent meetings and their IDs.")}`),console.error(` ${g("\u{1F4A1} To read a shared meeting, you need the URL and PIN from the sender.")}`),console.error(),process.exit(1));try{let i=zt(e),r;if(!p(n)){let o=(await import("ora")).default;r=o({text:"Loading meeting...",color:"blue"}).start()}if(n.pin){let o={"access-pin":n.pin},s=await _(`/api/v1/meeting/${i}`,{headers:o}),c=(await _(`/api/v1/meeting/${i}/sections`,{headers:o}).catch(()=>({meeting_sections:[]}))).meeting_sections||[],l=await Promise.all(c.map(d=>_(`/api/v1/meeting/${i}/section/${d.id}/result`,{headers:o}).then(m=>m.section_result||{title:d.variable_values_snapshot?.title||d.title}).catch(()=>({title:d.variable_values_snapshot?.title||d.title}))));r?.stop(),p(n)?w({...s,notes:l},n):Ye(s,l)}else{let[o,s]=await Promise.all([_(`/api/v1/meeting/${i}`),_(`/api/v1/meeting/${i}/sections-complete`).catch(()=>null)]),a=o.public_id,c=a?`https://app.meeting.ai/m/${a}`:null,d=o.is_pin_enabled?o.meeting_access_pin:null;r?.stop();let m=s?.meeting_sections||[];p(n)?w({...o,notes:m,share_url:c,pin:d||null},n):Ye(o,m,c,d)}}catch(i){b(i,n)}});return k("read",{description:"Read meeting notes (URL, ID, or link)",params:{url_or_id:{type:"string",description:"Meeting URL (meeting.ai/m/...) or meeting ID",required:!0},pin:{type:"string",description:"Access PIN for shared meetings",required:!1}},output:{type:"object",properties:{id:{type:"string"},title:{type:"string"},status:{type:"string"},integration:{type:"string"},started_at:{type:"string"},finished_at:{type:"string"},duration_in_seconds:{type:"number"},share_url:{type:"string"},pin:{type:"string"},notes:{type:"array",items:{type:"object"}}}}}),t}function Ye(t,e,n,i){let r=Wt(t),o=Vt(t),s=ae(t.integration),a=E(t.duration_in_seconds),c=re(t.started_at||t.created_at);console.log(),console.log(` ${r} ${Se.bold(o)}`),console.log(` ${g(`${c} \xB7 ${s} \xB7 ${a}`)}`),console.log();let l={ID:t.id,Status:t.status,Integration:s,"Started at":t.started_at?re(t.started_at):"-","Finished at":t.finished_at?re(t.finished_at):"-",Duration:a};n!==void 0&&(l.Share=n||"Not generated",l.PIN=i||"-"),v(l);let{names:d,more:m}=Yt(t);if(d.length>0){console.log(),console.log(h.bold(" Speakers"));let u=d.join(", ")+(m>0?` ${g(`+${m} more`)}`:"");console.log(` ${u}`)}if(e.length>0){console.log(),console.log(h.bold(" Meeting Notes")),console.log(g(" "+"\u2500".repeat(50)));for(let u of e){if(u.title&&(console.log(),console.log(` ${Se.bold(u.title)}`)),u.text){let f=u.text.split(`
11
+ `);for(let y of f)console.log(` ${y}`)}console.log()}}console.log()}import{Command as Kt}from"commander";var Ht=`---
12
+ name: meeting-cli
13
+ description: "Interact with Meeting.ai via the \`meeting\` CLI. Use when user asks about meetings, meeting notes, transcripts, login to meeting.ai, or anything related to Meeting.ai. IMPORTANT: All Meeting.ai operations (login, reading notes, uploading, etc.) are done through the \`meeting\` CLI command \u2014 never use a browser. Login uses \`meeting auth login --email <email>\` which sends an OTP to email."
14
+ ---
15
+
16
+ # Meeting.ai CLI
17
+
18
+ All Meeting.ai operations go through the \`meeting\` CLI. Never use a browser.
19
+
20
+ ## Authentication
21
+
22
+ \`\`\`
23
+ meeting auth login --email user@example.com # Sends OTP to email, then prompts for code
24
+ meeting auth whoami # Check who you're logged in as
25
+ meeting auth logout # Clear stored tokens
26
+ \`\`\`
27
+
28
+ Login flow: run \`meeting auth login --email <email>\` \u2192 OTP is sent to email \u2192 ask user for the OTP code \u2192 enter it when prompted. If OTP is wrong, it retries up to 3 times.
29
+
30
+ ## Commands
31
+
32
+ \`\`\`
33
+ meeting read <url_or_id> [--pin <pin>] Read meeting notes (own or shared)
34
+ meeting search <query> Search meetings by keyword
35
+ meeting list [--limit N] List recent meetings
36
+ meeting transcript <id> Speakers + AI summary (default)
37
+ meeting transcript <id> --full Full word-by-word transcript
38
+ meeting transcript <id> --search "query" Search within transcript
39
+ meeting upload <file_or_url> Upload recording for transcription
40
+ meeting upload <file> --wait Upload and wait for completion
41
+ meeting contacts list List contacts
42
+ meeting calendar events --upcoming View upcoming calendar events
43
+ meeting bot send --url <meeting_url> Send bot to a live meeting
44
+ meeting delete <id> Delete a meeting (irreversible)
45
+ meeting auth whoami Check current login
46
+ meeting schema List all commands and params
47
+ \`\`\`
48
+
49
+ ## Key Behaviors
50
+
51
+ - Output is human-readable by default. Use \`--json\` only when piping to other tools.
52
+ - \`meeting read\` works with both meeting IDs and \`https://app.meeting.ai/m/...\` URLs.
53
+ - Shared meetings require \`--pin\`. Own meetings don't.
54
+ - \`meeting list\` returns 5 most recent meetings with IDs \u2014 use these IDs for other commands.
55
+ - After \`meeting upload\`, the CLI prints status. If status is "created" or "processing_audio", transcription is in progress \u2014 no need to wait.
56
+ - \`meeting transcript\` without flags shows speakers + AI summary. Add \`--full\` for the raw transcript.
57
+
58
+ ## Typical Workflows
59
+
60
+ **User asks "what was discussed in my last meeting?"**
61
+ 1. \`meeting list --limit 1\` \u2192 get the meeting ID
62
+ 2. \`meeting read <id>\` \u2192 read the full notes
63
+
64
+ **User shares a meeting.ai link + PIN**
65
+ 1. \`meeting read <url> --pin <pin>\` \u2192 read the shared notes
66
+
67
+ **User wants to upload a recording**
68
+ 1. \`meeting upload recording.mp4\` \u2192 upload and let it process
69
+ 2. Wait a few minutes, then \`meeting read <id>\` to check notes
70
+
71
+ **User searches for a topic across meetings**
72
+ 1. \`meeting search "budget review"\` \u2192 find matching meetings
73
+ 2. \`meeting read <id>\` \u2192 read the relevant one
74
+
75
+ ## Adopt as Agent Skill
76
+
77
+ To use this as an agent skill, save the content above as SKILL.md in your skills directory.`;function He(){return new Kt("help").description("Full syntax and agent integration guide").action(()=>{process.stdout.write(Ht+`
78
+ `)})}import{Command as Gt}from"commander";import{execSync as Ge}from"child_process";function Ze(){return new Gt("update").description("Update meeting CLI to the latest version").action(async()=>{let e=(await import("chalk")).default,n=(await import("ora")).default,i=e.hex("#22C55E"),r=e.hex("#2563EB"),o=e.hex("#6B7280");console.log(),console.log(` ${r.bold("meeting update")}`),console.log();let s=n({text:"Checking for updates...",color:"blue"}).start();try{let a=Ge("npm update -g @meeting-ai/cli 2>&1",{encoding:"utf-8",timeout:6e4});s.stop();try{let c=Ge("npm list -g @meeting-ai/cli --depth=0 --json 2>/dev/null",{encoding:"utf-8",timeout:1e4}),d=JSON.parse(c).dependencies?.["@meeting-ai/cli"]?.version;console.log(d?` ${i("\u2713")} Updated to ${e.bold(`v${d}`)}`:` ${i("\u2713")} Up to date`)}catch{console.log(` ${i("\u2713")} Update complete`)}}catch{s.stop(),console.log(e.red(" \u2717 Update failed. Try manually:")),console.log(` ${r("npm update -g @meeting-ai/cli")}`)}console.log()})}var Zt=3600*1e3,Xt="https://registry.npmjs.org/@meeting-ai/cli/latest";function Qt(){return"0.1.0-nightly.1"}function Xe(){return ue().get("update")}function en(t){ue().set("update",t)}function tn(t,e){let n=t.split(".").map(Number),i=e.split(".").map(Number);for(let r=0;r<3;r++){if((n[r]||0)>(i[r]||0))return!0;if((n[r]||0)<(i[r]||0))return!1}return!1}function Qe(){let t=Xe();t&&Date.now()-t.checked_at<Zt||fetch(Xt).then(e=>e.ok?e.json():null).then(e=>{e?.version&&en({latest_version:e.version,checked_at:Date.now()})}).catch(()=>{})}function pe(){let t=Xe();if(!t)return null;let e=Qt();return tn(t.latest_version,e)?`Update available: ${e} \u2192 ${t.latest_version} \u2014 run: npm update -g @meeting-ai/cli`:null}V();var nt="0.1.0-nightly.1";function on(){let t=new nn;return t.name("meeting").description("CLI for Meeting.ai \u2014 your meetings, at your fingertips").version(nt),t.addCommand(Pe()),t.addCommand(Le()),t.addCommand(Ke()),t.addCommand(ze()),t.addCommand(Fe()),t.addCommand(je()),t.addCommand(Ee()),t.addCommand(Ue()),t.addCommand(qe()),t.addCommand(We()),t.addCommand(Re()),t.addCommand(He()),t.addCommand(Ze()),t.command("logout").description("Log out and clear stored tokens").action(()=>{Q(),console.log(" \u2713 Logged out successfully.")}),t}async function et(t,e){console.log(` ${e("Commands:")}`),console.log(` ${t("meeting search")} ${e("<query>")} ${e("Search meetings (notes & transcripts)")}`),console.log(` ${t("meeting list")} ${e("Show 10 recent meetings")}`),console.log(` ${t("meeting read")} ${e("<url_or_id>")} ${e("Read meeting notes (URL, ID, or link)")}`),console.log(` ${t("meeting transcript")} ${e("<id>")} ${e("Meeting transcript & speakers")}`),console.log(` ${t("meeting upload")} ${e("<file|url>")} ${e("Upload recording for transcription")}`),console.log(` ${t("meeting bot send")} ${e("--url <url>")} ${e("Send bot to a meeting")}`),console.log(` ${t("meeting calendar events")} ${e("View calendar events")}`),console.log(` ${t("meeting contacts list")} ${e("Browse contacts")}`),console.log(` ${t("meeting delete")} ${e("<id>")} ${e("Delete a meeting")}`),pe()&&console.log(` ${t("meeting update")} ${e("Update CLI")}`),console.log(),console.log(` ${e("Run")} ${t("meeting help")} ${e("for full syntax and agent integration guide.")}`),console.log();try{let r=((await _("/api/v1/meetings",{query:{items_per_page:"5"}})).meeting||[]).slice(0,5);if(r.length>0){console.log(` ${e("Recent meetings:")}`);for(let o of r){let s=o.is_title_edited&&o.title?o.title:o.autogenerated_title||o.title||"(untitled)",a=o.autogenerated_title_emoji||"",c=o.started_at?new Date(o.started_at).toLocaleDateString("en-GB",{day:"numeric",month:"short",year:"numeric"}):"",l=o.duration_in_seconds?o.duration_in_seconds>=3600?`${Math.floor(o.duration_in_seconds/3600)}h ${Math.floor(o.duration_in_seconds%3600/60)}m`:`${Math.floor(o.duration_in_seconds/60)} min`:"",d=o.integration==="gmeet"?"Google Meet":o.integration==="zoom"?"Zoom":o.integration==="livestream"?"In-person":o.integration==="upload"?"Upload":o.integration==="teams"?"Teams":o.integration||"",m=[c,d,l].filter(Boolean).join(" \xB7 ");console.log(` ${a?a+" ":""}${t(s)}`),console.log(` ${e(m)} ${e("ID:")} ${o.id}`)}console.log()}}catch{}}function rn(){let t=pe();t&&import("chalk").then(({default:e})=>{console.log(` ${e.hex("#F59E0B")(t)}`),console.log()}).catch(()=>{})}async function sn(){let t=(await import("chalk")).default,e=t.hex("#2563EB"),n=t.hex("#22C55E"),i=t.hex("#6B7280"),r=t.hex("#EA8232"),o=t.hex("#22C55E"),s=e.bold(`
79
+ \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557
80
+ \u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
81
+ \u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557
82
+ \u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
83
+ \u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D${i(".ai")}
84
+ \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D ${i("v"+nt)}
85
+ `);console.log(s);let a=pe();a&&(console.log(` ${t.hex("#F59E0B")(a)}`),console.log());let c=ee();if(c)console.log(` ${n("\u25CF")} Logged in as ${t.bold(c)}`),console.log(),await et(e,i);else{console.log(` ${r("\u25CF")} Log in to get started`),console.log();try{let l=await R(` ${t.bold("Email:")} `);if(!l){console.log(i(" No email provided. Run: meeting auth login")),console.log();return}let d=(await import("ora")).default,m=d({text:"Sending OTP code...",color:"blue"}).start(),u=await B(l);m.stop(),console.log(` ${o("\u2713")} OTP sent! Check your email`),console.log();let f=await R(` ${t.bold("OTP Code:")} `);if(!f){console.log(i(" No OTP provided. Run: meeting auth login")),console.log();return}let y=await oe(l,f,u),$;if(y.mfa_required){let x=y.mfa_type;if(x==="webauthn"){console.log(t.red(" WebAuthn/passkey 2FA is not supported in CLI.")),console.log(i(" Please log in via the web app at https://meeting.ai")),console.log();return}let O;if(x==="totp")O=` ${t.bold("Authenticator code:")} `;else if(x==="password")O=` ${t.bold("Password:")} `;else{let T=(await import("ora")).default,C=T({text:"Sending 2FA code to email...",color:"blue"}).start();await z(y.mfa_id,y.session_id),C.stop(),console.log(` ${o("\u2713")} 2FA code sent to your email`),console.log(),O=` ${t.bold("2FA code (check email):")} `}let S=await R(O);if(!S){console.log(i(" No 2FA code provided. Run: meeting auth login")),console.log();return}$=await ie(y.challenge_id,S,y.session_id,x)}else $=y.tokens;te(l),console.log(` ${o("\u2713")} Logged in as ${t.bold(l)}`),console.log(),et(e,i)}catch(l){let d=l instanceof Error?l.message:"Login failed";console.log(t.red(` Error: ${d}`)),console.log(i(" Run: meeting auth login")),console.log()}}}Qe();var tt=on();process.argv.length<=2?process.stdout.isTTY?sn():tt.help():tt.parseAsync(process.argv).then(()=>{rn()}).catch(t=>{console.error(t.message||"Unknown error"),process.exit(1)});
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@meeting-ai/cli",
3
+ "version": "0.1.0-nightly.1",
4
+ "description": "Agent-friendly CLI for Meeting.ai — read meeting notes, search, upload, and manage meetings from the terminal.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Meeting.ai <dev@meeting.ai>",
8
+ "homepage": "https://meeting.ai",
9
+ "keywords": [
10
+ "meeting",
11
+ "meeting.ai",
12
+ "cli",
13
+ "transcript",
14
+ "notes",
15
+ "ai",
16
+ "agent"
17
+ ],
18
+ "bin": {
19
+ "meeting": "dist/bin/meeting.js"
20
+ },
21
+ "scripts": {
22
+ "build": "tsc --noEmit && rm -rf dist && node scripts/bundle.js",
23
+ "build:dev": "node scripts/bundle.js",
24
+ "dev": "tsx watch src/meeting.ts",
25
+ "link": "npm link"
26
+ },
27
+ "dependencies": {
28
+ "boxen": "^8.0.1",
29
+ "chalk": "^5.6.2",
30
+ "commander": "^14.0.3",
31
+ "conf": "^15.1.0",
32
+ "ora": "^9.3.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^25.5.0",
36
+ "esbuild": "^0.27.4",
37
+ "tsx": "^4.21.0",
38
+ "typescript": "^5.9.3"
39
+ },
40
+ "files": [
41
+ "dist/bin/meeting.js",
42
+ "package.json",
43
+ "README.md",
44
+ "SKILL.md"
45
+ ],
46
+ "engines": {
47
+ "node": ">=22"
48
+ }
49
+ }