@meeting-ai/cli 0.1.0-nightly.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -179
- package/dist/bin/meeting.js +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# meeting — CLI for Meeting.ai
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Access your [Meeting.ai](https://meeting.ai) meetings from the command line. Read meeting notes, search transcripts, pull key quotes, upload recordings, and send bots to live meetings — so you and your AI agents always know what happened.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -10,204 +10,80 @@ Beautiful, agent-friendly command-line interface for Meeting.ai. Wraps the Audyn
|
|
|
10
10
|
npm install -g @meeting-ai/cli
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Get Started
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
meeting # Welcome screen + recent meetings
|
|
17
|
+
meeting auth login # Log in with email OTP
|
|
18
|
+
meeting list # Browse your recent meetings
|
|
19
|
+
meeting read <id> # Read full meeting notes & action items
|
|
18
20
|
```
|
|
19
21
|
|
|
20
|
-
##
|
|
22
|
+
## What You Can Do
|
|
21
23
|
|
|
22
|
-
|
|
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
|
|
24
|
+
### 📖 Read Meeting Notes & Transcripts
|
|
41
25
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
26
|
+
| Command | What it does |
|
|
27
|
+
|---------|-------------|
|
|
28
|
+
| `meeting read <id>` | Read notes, summary, speakers, and action items |
|
|
29
|
+
| `meeting read <url> --pin <pin>` | Read a shared meeting (from a Meeting.ai link) |
|
|
30
|
+
| `meeting search <query>` | Search across all your meetings |
|
|
31
|
+
| `meeting list` | List recent meetings with IDs |
|
|
32
|
+
| `meeting transcript <id>` | See speakers + AI-generated summary |
|
|
33
|
+
| `meeting transcript <id> --full` | Full word-by-word transcript with speaker names |
|
|
34
|
+
| `meeting transcript <id> --search "query"` | Find specific moments in a transcript |
|
|
47
35
|
|
|
48
|
-
### Meetings
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
# List recent meetings — pretty card layout with emoji, duration, summaries
|
|
52
|
-
meeting list --limit 10
|
|
36
|
+
### 🤖 Record Meetings & Upload Recordings
|
|
53
37
|
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
| Command | What it does |
|
|
39
|
+
|---------|-------------|
|
|
40
|
+
| `meeting bot send --url <url>` | Send a Meeting.ai bot to join a Google Meet, Zoom, or Teams call |
|
|
41
|
+
| `meeting bot send --url <url> --wait` | Send bot and wait until notes are ready |
|
|
42
|
+
| `meeting upload recording.mp4` | Upload a local audio/video recording for transcription |
|
|
43
|
+
| `meeting upload <url>` | Upload a recording from a URL |
|
|
56
44
|
|
|
57
|
-
|
|
58
|
-
meeting search "quarterly review"
|
|
45
|
+
### 👥 Contacts & Calendar
|
|
59
46
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
| Command | What it does |
|
|
48
|
+
|---------|-------------|
|
|
49
|
+
| `meeting contacts list` | Browse contacts you've met with |
|
|
50
|
+
| `meeting contacts list --search "name"` | Find a specific contact |
|
|
51
|
+
| `meeting calendar events --upcoming` | See your upcoming meetings |
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
```
|
|
66
|
-
Recent Meetings 42 total
|
|
53
|
+
### 🔑 Account
|
|
67
54
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
55
|
+
| Command | What it does |
|
|
56
|
+
|---------|-------------|
|
|
57
|
+
| `meeting auth login` | Log in with email OTP (supports 2FA) |
|
|
58
|
+
| `meeting auth whoami` | Check who you're logged in as |
|
|
59
|
+
| `meeting update` | Update to the latest version |
|
|
71
60
|
|
|
72
|
-
|
|
73
|
-
Mar 14 · Zoom · 30 min · 6 speakers
|
|
74
|
-
Sprint progress, blockers, and deployment schedule...
|
|
75
|
-
```
|
|
61
|
+
## Built for AI Agents
|
|
76
62
|
|
|
77
|
-
|
|
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
|
-
```
|
|
63
|
+
This CLI is built to be a tool for AI agents — [OpenClaw](https://openclaw.ai), Claude Code, Codex, or any AI agent that can run shell commands.
|
|
87
64
|
|
|
88
|
-
|
|
65
|
+
Agents can use the Meeting.ai CLI to:
|
|
66
|
+
- **Read meeting notes** and answer questions about what was discussed
|
|
67
|
+
- **Pull key quotes and action items** from any meeting
|
|
68
|
+
- **Search across meetings** to find relevant context
|
|
69
|
+
- **Upload recordings** and wait for transcription
|
|
70
|
+
- **Share meeting notes** with a link + PIN
|
|
89
71
|
|
|
90
72
|
```bash
|
|
91
|
-
#
|
|
92
|
-
meeting
|
|
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>
|
|
73
|
+
meeting help # Full agent skill guide (SKILL.md format)
|
|
74
|
+
meeting schema # Machine-readable command & parameter schemas
|
|
102
75
|
```
|
|
103
76
|
|
|
104
|
-
|
|
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
|
-
```
|
|
77
|
+
Run `meeting help` to get a ready-to-use agent skill definition that any AI agent can adopt.
|
|
132
78
|
|
|
133
|
-
|
|
79
|
+
## How It Works
|
|
134
80
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
meeting
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
```
|
|
81
|
+
1. **Log in** — `meeting auth login` sends a one-time code to your email
|
|
82
|
+
2. **Browse** — `meeting list` shows your recent meetings with IDs
|
|
83
|
+
3. **Read** — `meeting read <id>` pulls the full notes, speakers, summary, and action items
|
|
84
|
+
4. **Search** — `meeting search "topic"` finds meetings across notes and transcripts
|
|
85
|
+
5. **Share** — Every meeting includes a share URL and PIN in the output
|
|
210
86
|
|
|
211
87
|
## License
|
|
212
88
|
|
|
213
|
-
|
|
89
|
+
Proprietary — © 2026 Meeting.ai. All rights reserved.
|
package/dist/bin/meeting.js
CHANGED
|
@@ -3,7 +3,7 @@ var rt=Object.defineProperty;var st=(t,e)=>()=>(t&&(e=t(t=0)),e);var at=(t,e)=>{
|
|
|
3
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
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
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.
|
|
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.2";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
7
|
|
|
8
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
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(`
|
|
@@ -75,7 +75,7 @@ meeting schema List all commands and params
|
|
|
75
75
|
## Adopt as Agent Skill
|
|
76
76
|
|
|
77
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.
|
|
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.2"}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.2";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
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
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
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
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meeting-ai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Agent-friendly CLI for Meeting.ai — read meeting notes, search, upload, and manage meetings from the terminal.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
7
|
"author": "Meeting.ai <dev@meeting.ai>",
|
|
8
8
|
"homepage": "https://meeting.ai",
|
|
9
9
|
"keywords": [
|