appoiint 4.7.0 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +1 -1
- package/dist/commands/admin.js +1 -1
- package/dist/commands/auth.js +1 -1
- package/dist/commands/bookings.js +1 -1
- package/dist/commands/channels.js +1 -1
- package/dist/commands/ice.js +1 -0
- package/dist/commands/meeting.js +1 -1
- package/dist/commands/wall.js +1 -0
- package/dist/format.js +1 -1
- package/dist/lib/turnProbe.js +1 -0
- package/dist/main.js +1 -1
- package/dist/mcp-tools.js +1 -1
- package/package.json +3 -2
package/dist/api.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const https=require("https"),http=require("http"),BASE="/saas/api/v1/sdk";function request(e,t,s,
|
|
1
|
+
const https=require("https"),http=require("http"),BASE="/saas/api/v1/sdk";function request(e,t,n,s,a){return new Promise((i,o)=>{const r=new URL(s,e),p="https:"===r.protocol,c=p?https:http,u={"Content-Type":"application/json"};t&&(u.Authorization=`Bearer ${t}`);const E=a?JSON.stringify(a):null;E&&(u["Content-Length"]=Buffer.byteLength(E));const d=c.request({hostname:r.hostname,port:r.port||(p?443:80),path:r.pathname+r.search,method:n,headers:u,timeout:3e4},e=>{let t="";e.on("data",e=>t+=e),e.on("end",()=>{try{const n=JSON.parse(t);e.statusCode>=400?o(new Error(n.message||n.error||`HTTP ${e.statusCode}`)):i(n.data||n)}catch{e.statusCode>=400?o(new Error(`HTTP ${e.statusCode}: ${t.slice(0,200)}`)):i(t)}})});d.on("error",o),d.on("timeout",()=>{d.destroy(),o(new Error("Request timeout (30s)"))}),E&&d.write(E),d.end()})}function api(e){const{baseUrl:t,apiKey:n}=e,s=(e,s,a)=>request(t,n,e,s,a);return{signup:e=>request(t,"","POST",`${BASE}/tai/agent/signup`,e),signin:e=>request(t,"","POST",`${BASE}/auth/signin`,e),activate:e=>request(t,"","POST",`${BASE}/tai/agent/activate`,e),createRoom:e=>s("POST",`${BASE}/tai/agent/rooms`,e),listRooms:()=>s("GET",`${BASE}/tai/agent/rooms`),roomStatus:e=>s("GET",`${BASE}/tai/agent/rooms/${e}`),endRoom:e=>s("DELETE",`${BASE}/tai/agent/rooms/${e}`),invite:(e,t)=>s("POST",`${BASE}/tai/agent/rooms/${e}/invite`,t),transcript:(e,t)=>s("GET",`${BASE}/tai/agent/rooms/${e}/transcript?format=${t||"json"}`),joinMeeting:e=>s("POST",`${BASE}/tai/agent/meeting/join`,e),leaveMeeting:e=>s("POST",`${BASE}/tai/agent/meeting/leave`,e),events:()=>s("GET",e.userId?`${BASE}/scheduling/event-types/by-user/${e.userId}`:`${BASE}/scheduling/event-types/public`),slots:(e,t,n)=>s("GET",`${BASE}/availability/slots/publicinverse?email=${e}&date=${t}&timezone=${n||"UTC"}`),book:e=>s("POST",`${BASE}/scheduling/bookings`,e),bookingsByHost:e=>s("GET",`${BASE}/scheduling/bookings/by-host/${e}`),getBooking:e=>s("GET",`${BASE}/scheduling/bookings/${e}`),cancelBooking:(e,t)=>s("POST",`${BASE}/scheduling/bookings/${e}/cancel`,t),rescheduleBooking:(e,t)=>s("POST",`${BASE}/scheduling/bookings/${e}/reschedule`,t),synthesize:e=>s("POST",`${BASE}/tts/synthesize`,e),voices:()=>s("GET",`${BASE}/tts/voices`),status:()=>request(t,"","GET",`${BASE.replace("/sdk","/public")}/tai/status`),sttStatus:()=>s("GET",`${BASE}/stt/status`),adminUsers:()=>s("GET","/superadmin/api/users"),adminUserCreate:e=>s("POST","/superadmin/api/users",e),adminUserReset:(e,t)=>s("POST","/superadmin/api/users/reset-password",{email:e,...t}),adminUserDelete:e=>s("DELETE",`/superadmin/api/users/${encodeURIComponent(e)}`),adminTenants:()=>s("GET","/superadmin/api/tenants"),adminTenantView:e=>s("GET",`/superadmin/api/tenants/${e}`),adminTenantCreate:e=>s("POST","/superadmin/api/tenants",e),adminTenantPlan:(e,t)=>s("POST",`/superadmin/api/tenants/${e}/plan`,{plan:t}),adminTenantVerify:e=>s("POST",`/superadmin/api/tenants/${e}/verify`),adminTenantSuspend:e=>s("POST",`/superadmin/api/tenants/${e}/suspend`),adminTenantActivate:e=>s("POST",`/superadmin/api/tenants/${e}/activate`),adminTenantDelete:e=>s("DELETE",`/superadmin/api/tenants/${e}`),adminBilling:()=>s("GET","/superadmin/api/revenue"),adminDiscount:e=>s("POST","/superadmin/api/discount",e),platformStats:()=>s("GET","/superadmin/api/stats"),funnel:()=>s("GET","/superadmin/api/funnel"),signups:e=>s("GET","/superadmin/api/signups"+(e?"?days="+e:"")),revenue:()=>s("GET","/superadmin/api/revenue"),tickets:e=>s("GET","/superadmin/api/tickets"+(e?"?status="+e:"")),ticketShow:e=>s("GET",`/superadmin/api/tickets/${e}`),ticketReply:(e,t)=>s("POST",`/superadmin/api/tickets/${e}/reply`,{message:t}),ticketClose:e=>s("POST",`/superadmin/api/tickets/${e}/close`),audit:e=>s("GET","/superadmin/api/audit"+(e?"?"+e:"")),auditExport:e=>s("GET",`/superadmin/api/audit/export?format=${e||"json"}`),notifications:e=>s("GET","/superadmin/api/notifications"+(e?"?since="+e:"")),costs:()=>s("GET",`${BASE}/costs`),costsByRoom:e=>s("GET",`${BASE}/costs/${e}`),costsPricing:()=>s("GET",`${BASE}/costs/pricing`),costsModels:()=>s("GET",`${BASE}/costs/models`),costsUsers:()=>s("GET",`${BASE}/costs/users`),costsTimeseries:e=>s("GET",`${BASE}/costs/timeseries${e?"?bucket="+e:""}`),agentCreate:e=>s("POST",`${BASE}/tai/agent/create`,e),agentList:()=>s("GET",`${BASE}/tai/agent/list`),agentRevoke:e=>s("DELETE",`${BASE}/tai/agent/${encodeURIComponent(e)}`),channelSend:e=>s("POST",`${BASE}/channels/send`,e),channelInbox:()=>s("GET",`${BASE}/channels/inbox`),channelHistory:(e,t)=>s("GET",`${BASE}/channels/history/${e}${t?"?limit="+t:""}`),channelAck:e=>s("POST",`${BASE}/channels/ack/${e}`),channelAgents:()=>s("GET",`${BASE}/channels/agents`),wall:()=>s("GET",`${BASE}/channels/wall`),notificationsDigest:()=>s("GET",`${BASE}/channels/notifications`),groupCreate:(e,t)=>s("POST",`${BASE}/channels/channel/create`,{name:e,topic:t}),groupJoin:e=>s("POST",`${BASE}/channels/channel/join`,{name:e}),groupLeave:e=>s("POST",`${BASE}/channels/channel/leave`,{name:e}),groupList:()=>s("GET",`${BASE}/channels/channel/list`),groupSend:(e,t)=>s("POST",`${BASE}/channels/channel/${encodeURIComponent(e)}/send`,t),groupHistory:(e,t)=>s("GET",`${BASE}/channels/channel/${encodeURIComponent(e)}/history${t?"?limit="+t:""}`)}}function streamChannels(e,t){const n=new URL(`${BASE}/channels/stream`,e.baseUrl),s="https:"===n.protocol,a=(s?https:http).request({hostname:n.hostname,port:n.port||(s?443:80),path:n.pathname,method:"GET",headers:{Authorization:`Bearer ${e.apiKey}`,Accept:"text/event-stream"}},e=>{if(e.statusCode>=400)return void t("error",{status:e.statusCode});let n="";e.setEncoding("utf8"),e.on("data",e=>{let s;for(n+=e;-1!==(s=n.indexOf("\n\n"));){const e=n.slice(0,s);n=n.slice(s+2);let a="message",i="";for(const t of e.split("\n"))t.startsWith("event: ")?a=t.slice(7).trim():t.startsWith("data: ")&&(i+=t.slice(6));if(i)try{t(a,JSON.parse(i))}catch{t(a,{raw:i})}}}),e.on("end",()=>t("end",{}))});return a.on("error",e=>t("error",{message:e.message})),a.end(),a}function rawApi(e){return(t,n,s)=>request(e.baseUrl,e.apiKey,t,n,s)}module.exports={api:api,request:request,rawApi:rawApi,streamChannels:streamChannels};
|
package/dist/commands/admin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const{getFlag:getFlag,positionalArgs:positionalArgs}=require("../config"),fmt=require("../format");async function run(t,e,o){const
|
|
1
|
+
const{getFlag:getFlag,positionalArgs:positionalArgs}=require("../config"),fmt=require("../format");async function run(t,e,o,n){const s=positionalArgs(e),a=s[0];if("ice"===a||"turn"===a)return require("./ice").run(n,e,o);if("tenants"===a){const e=await t.adminTenants();if(o)return console.log(JSON.stringify(e,null,2));const n=e.tenants||e.data||e;return Array.isArray(n)&&n.length?(console.log(`\n[1mTenants (${n.length})[0m\n`),void n.forEach(t=>{const e=t.plan||"free",o=t.suspended?"[31msuspended[0m":"[32mactive[0m";console.log(` ${(t.slug||t.name||"?").padEnd(20)} ${e.padEnd(12)} ${o} ${t.email||""}`)})):fmt.info("No tenants.")}if("tenant-view"===a){const e=s[1];e||(fmt.err("Usage: apo admin tenant-view SLUG"),process.exit(1));const n=await t.adminTenantView(e);if(o)return console.log(JSON.stringify(n,null,2));const a=n.tenant||n.data||n;return console.log(`\n[1m${a.name||a.slug}[0m`),console.log(` Slug: ${a.slug}`),console.log(` Plan: ${a.plan||"free"}`),console.log(` Email: ${a.email||"—"}`),console.log(" Status: "+(a.suspended?"suspended":"active")),console.log(` Created: ${a.createdAt||"—"}`),void(a.stats&&(console.log(` Rooms: ${a.stats.rooms||0}`),console.log(` Bookings: ${a.stats.bookings||0}`),console.log(` Users: ${a.stats.users||0}`)))}if("tenant-create"===a){const n=s[1],a=getFlag(e,"plan")||"starter",r=getFlag(e,"email");n||(fmt.err("Usage: apo admin tenant-create SLUG [--plan X] [--email X]"),process.exit(1));const l=await t.adminTenantCreate({slug:n,plan:a,email:r});return o?console.log(JSON.stringify(l,null,2)):(fmt.ok(`Tenant created: ${n} (${a})`),void(l.apiKey&&console.log(` API Key: ${l.apiKey}`)))}if("tenant-plan"===a){const o=s[1],n=getFlag(e,"plan");return o&&n||(fmt.err("Usage: apo admin tenant-plan SLUG --plan X"),process.exit(1)),await t.adminTenantPlan(o,n),void fmt.ok(`Plan changed: ${o} → ${n}`)}if("tenant-verify"===a){const e=s[1];return e||(fmt.err("Usage: apo admin tenant-verify SLUG"),process.exit(1)),await t.adminTenantVerify(e),void fmt.ok(`Payment verified for: ${e}`)}if("tenant-suspend"===a){const e=s[1];return e||(fmt.err("Usage: apo admin tenant-suspend SLUG"),process.exit(1)),await t.adminTenantSuspend(e),void fmt.ok(`Tenant suspended: ${e}`)}if("tenant-activate"===a){const e=s[1];return e||(fmt.err("Usage: apo admin tenant-activate SLUG"),process.exit(1)),await t.adminTenantActivate(e),void fmt.ok(`Tenant activated: ${e}`)}if("tenant-delete"===a){const e=s[1];return e||(fmt.err("Usage: apo admin tenant-delete SLUG"),process.exit(1)),await t.adminTenantDelete(e),void fmt.ok(`Tenant deleted: ${e}`)}if("users"===a){const e=await t.adminUsers();if(o)return console.log(JSON.stringify(e,null,2));const n=e.users||e.data||e;return Array.isArray(n)&&n.length?(console.log(`\n[1mUsers (${n.length})[0m\n`),void n.forEach(t=>{const e=t.role||"user",o=t.locked?"[31mlocked[0m":"[32mactive[0m";console.log(` ${(t.email||"?").padEnd(30)} ${e.padEnd(10)} ${o}`)})):fmt.info("No users.")}if("user-create"===a){const n=getFlag(e,"email"),s=getFlag(e,"password")||getFlag(e,"p"),a=getFlag(e,"tenant"),r=getFlag(e,"role")||"user";n&&s||(fmt.err("Usage: apo admin user-create --email X --password X [--tenant SLUG] [--role X]"),process.exit(1));const l=await t.adminUserCreate({email:n,password:s,tenant:a,role:r});return o?console.log(JSON.stringify(l,null,2)):void fmt.ok(`User created: ${n}`)}if("user-reset"===a){const o=s[1],n=getFlag(e,"password")||getFlag(e,"p");return o&&n||(fmt.err("Usage: apo admin user-reset EMAIL --password NEWPASS"),process.exit(1)),await t.adminUserReset(o,{password:n}),void fmt.ok(`Password reset for: ${o}`)}if("user-delete"===a){const e=s[1];return e||(fmt.err("Usage: apo admin user-delete EMAIL"),process.exit(1)),await t.adminUserDelete(e),void fmt.ok(`User deleted: ${e}`)}if("billing"===a){const e=await t.adminBilling();if(o)return console.log(JSON.stringify(e,null,2));const n=e.data||e;return console.log("\n[1mBilling[0m\n"),console.log(` MRR: $${n.mrr||0}`),console.log(` ARR: $${n.arr||0}`),console.log(` Revenue: $${n.totalRevenue||0}`),console.log(` Fees: $${n.fees||0}`),void console.log(` Payouts: $${n.payouts||0}`)}if("discount"===a){const n=s[1],a=getFlag(e,"plan"),r=getFlag(e,"months")||"1";n||(fmt.err("Usage: apo admin discount SLUG [--plan X] [--months N]"),process.exit(1));const l=await t.adminDiscount({slug:n,plan:a,months:parseInt(r)});return o?console.log(JSON.stringify(l,null,2)):void fmt.ok(`Discount created: ${l.code||"done"}`)}if("costs"===a){const e=s[1];if(e){const n=await t.costsByRoom(e);if(o)return console.log(JSON.stringify(n,null,2));const s=n.data||n;return console.log(`\n[1mCosts: ${e}[0m\n`),console.log(` STT: $${(s.stt?.cost||0).toFixed(6)} (${s.stt?.requests||0} requests, ${s.stt?.provider||"—"})`),console.log(` TTS: $${(s.tts?.cost||0).toFixed(6)} (${s.tts?.requests||0} requests, ${s.tts?.provider||"—"})`),console.log(` LLM: $${(s.llm?.cost||0).toFixed(6)} (${s.llm?.requests||0} requests, ${s.llm?.provider||"—"})`),void console.log(` [1mTotal: $${(s.total||0).toFixed(6)}[0m`)}const n=await t.costs();if(o)return console.log(JSON.stringify(n,null,2));const a=n.data||n;return console.log("\n[1mPlatform Costs[0m\n"),console.log(` Rooms tracked: ${a.rooms||0}`),console.log(` STT total: $${(a.stt||0).toFixed(6)}`),console.log(` TTS total: $${(a.tts||0).toFixed(6)}`),console.log(` LLM total: $${(a.llm||0).toFixed(6)}`),console.log(` [1mTotal: $${(a.total||0).toFixed(6)}[0m`),void(a.byRoom?.length&&(console.log("\n Top rooms:"),a.byRoom.slice(0,10).forEach(t=>{console.log(` ${t.roomId.slice(0,12)}... $${t.total.toFixed(6)} (STT:$${t.stt.toFixed(6)} TTS:$${t.tts.toFixed(6)} LLM:$${t.llm.toFixed(6)})`)})))}if("models"===a){const e=await t.costsModels();if(o)return console.log(JSON.stringify(e,null,2));const n=e.data||e,s=n.models||[];return console.log(`\n[1mSpend by Provider / Model[0m Total: $${(n.total||0).toFixed(6)}\n`),s.length?(console.log(` ${"TYPE".padEnd(5)} ${"PROVIDER".padEnd(14)} ${"MODEL".padEnd(20)} ${"REQ".padStart(6)} ${"IN TOK".padStart(10)} ${"OUT TOK".padStart(10)} ${"COST".padStart(12)}`),void s.forEach(t=>{console.log(` ${(t.type||"").toUpperCase().padEnd(5)} ${(t.provider||"—").padEnd(14)} ${(t.model||"—").padEnd(20)} ${String(t.requests||0).padStart(6)} ${String(t.inputTokens||0).padStart(10)} ${String(t.outputTokens||0).padStart(10)} ${("$"+(t.cost||0).toFixed(6)).padStart(12)}`)})):fmt.info("No usage recorded yet.")}if("users-cost"===a||"usage"===a){const e=await t.costsUsers();if(o)return console.log(JSON.stringify(e,null,2));const n=e.data||e,s=n.users||[];return console.log(`\n[1mSpend by User[0m Total: $${(n.total||0).toFixed(6)}\n`),s.length?(console.log(` ${"USER".padEnd(28)} ${"REQ".padStart(6)} ${"LLM".padStart(12)} ${"STT".padStart(12)} ${"TTS".padStart(12)} ${"TOTAL".padStart(12)}`),void s.forEach(t=>{console.log(` ${String(t.userId||"—").padEnd(28)} ${String(t.requests||0).padStart(6)} ${("$"+(t.llm||0).toFixed(6)).padStart(12)} ${("$"+(t.stt||0).toFixed(6)).padStart(12)} ${("$"+(t.tts||0).toFixed(6)).padStart(12)} ${("$"+(t.cost||0).toFixed(6)).padStart(12)}`)})):fmt.info("No usage recorded yet.")}if("timeseries"===a){const n=getFlag(e,"bucket")||"hour",s=await t.costsTimeseries(n);if(o)return console.log(JSON.stringify(s,null,2));const a=s.data||s,r=a.buckets||[];return console.log(`\n[1mSpend Over Time (${a.bucket||n})[0m\n`),r.length?(console.log(` ${"WHEN".padEnd(20)} ${"LLM".padStart(12)} ${"STT".padStart(12)} ${"TTS".padStart(12)} ${"TOTAL".padStart(12)}`),void r.forEach(t=>{const e=new Date(t.t).toISOString().replace("T"," ").slice(0,16);console.log(` ${e.padEnd(20)} ${("$"+(t.llm||0).toFixed(6)).padStart(12)} ${("$"+(t.stt||0).toFixed(6)).padStart(12)} ${("$"+(t.tts||0).toFixed(6)).padStart(12)} ${("$"+(t.total||0).toFixed(6)).padStart(12)}`)})):fmt.info("No usage recorded yet.")}if("pricing"===a){const e=await t.costsPricing();if(o)return console.log(JSON.stringify(e,null,2));const n=e.data||e;return console.log("\n[1mPricing Table[0m\n"),console.log(" STT:"),Object.entries(n.stt||{}).forEach(([t,e])=>console.log(` ${t.padEnd(16)} $${e.perMinute}/min`)),console.log(" TTS:"),Object.entries(n.tts||{}).forEach(([t,e])=>console.log(` ${t.padEnd(16)} $${e.perChar}/char`)),console.log(" LLM (per MTok, in/out):"),void Object.entries(n.llm||{}).forEach(([t,e])=>{Object.entries(e||{}).forEach(([e,o])=>{if(!o||null==o.perInputMTok)return;const n="_default"===e?`${t} (default)`:`${t}/${e}`;console.log(` ${n.padEnd(28)} $${o.perInputMTok}/$${o.perOutputMTok}`)})})}fmt.err("Usage: apo admin [tenants|tenant-view|tenant-create|tenant-plan|tenant-verify|tenant-suspend|tenant-activate|tenant-delete|users|user-create|user-reset|user-delete|billing|discount|costs|costs ROOM_ID|models|usage|timeseries|pricing|ice]"),process.exit(1)}module.exports={run:run};
|
package/dist/commands/auth.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const{saveConfig:saveConfig,clearConfig:clearConfig,getFlag:getFlag}=require("../config"),{api:api}=require("../api"),fmt=require("../format");async function signup(e,o){const t=getFlag(e,"email")||getFlag(e,"e"),
|
|
1
|
+
const{saveConfig:saveConfig,clearConfig:clearConfig,getFlag:getFlag}=require("../config"),{api:api}=require("../api"),fmt=require("../format");async function signup(e,o){const t=getFlag(e,"email")||getFlag(e,"e"),s=getFlag(e,"password")||getFlag(e,"p"),a=getFlag(e,"agent")||getFlag(e,"a")||"CLI Agent",i=getFlag(e,"type")||getFlag(e,"t")||"cli";t&&s||(fmt.err("Usage: apo signup --email you@co.com --password YourPass123!"),process.exit(1));try{const e=api({baseUrl:o,apiKey:""}),n=await e.signup({email:t,password:s,agentName:a,agentType:i,firstName:a,lastName:"Agent"}),r=n.data||n,l=r.apiKey||r.token;let c=r.userId||r.user?.id||r.user?._id;if(!c&&l&&l.includes("."))try{const e=JSON.parse(Buffer.from(l.split(".")[1],"base64").toString());c=e.userId||e.sub||null}catch{}if(n.paymentRequired){const e=!!n.subscriptionId,s=saveConfig({userId:c,baseUrl:o,email:t,subscriptionId:n.subscriptionId,paymentIntentId:n.paymentIntentId},!0);if(fmt.ok(`Signed up: ${t}`),c&&console.log(` userId: ${c}`),console.log(` config: ${s}`),console.log(""),e){const e=n.trialDays||7;fmt.info(`Free ${e}-day trial — card required, $0 today. Cancel anytime.`),fmt.info(`After day ${e} your plan is charged unless you cancel.`)}else fmt.info("$1 one-time verification to activate your API key."),fmt.info("Not a trial, not a subscription. Just keeps out bots.");return n.paymentUrl&&fmt.info(`Open in browser: ${n.paymentUrl}`),void fmt.info(e?"After adding your card, run: apo activate":"After paying, run: apo activate")}const g=saveConfig({apiKey:l,userId:c,baseUrl:o,email:t},!0);fmt.ok(`Signed up: ${t}`),c&&console.log(` userId: ${c}`),l&&console.log(` apiKey: ${l.slice(0,20)}...`),console.log(` config: ${g}`),console.log(""),fmt.info('Run `apo room "My Meeting"` to create your first room.')}catch(e){fmt.err(`Signup failed: ${e.message}`),process.exit(1)}}async function activate(e,o){const t=e.find(e=>!e.startsWith("-"))||o.subscriptionId||o.paymentIntentId;t||(fmt.err("No signup reference found. Usage: apo activate [SUBSCRIPTION_OR_INTENT_ID]"),fmt.err("Run `apo signup` first, or pass the ID from your card-setup page."),process.exit(1));try{const e=api({baseUrl:o.baseUrl,apiKey:""}),s=String(t).startsWith("sub_")?{subscriptionId:t}:{paymentIntentId:t},a=await e.activate(s),i=a.data||a,n=i.apiKey;n||(fmt.err(a.message||a.error||"Activation failed: no API key returned."),process.exit(1));const r={apiKey:n,userId:i.userId||o.userId,paymentIntentId:void 0,subscriptionId:void 0};i.email&&(r.email=i.email);const l=saveConfig(r,!0);fmt.ok(`Activated: ${i.email||""}`),console.log(` apiKey: ${n.slice(0,20)}...`),console.log(` config: ${l}`),console.log(""),fmt.info('Run `apo room "My Meeting"` to create your first room.')}catch(e){fmt.err(`Activation failed: ${e.message}`),fmt.err("If you have not completed card setup yet, open the link from `apo signup` first."),process.exit(1)}}function login(e){const o=getFlag(e,"key")||getFlag(e,"k"),t=getFlag(e,"user")||getFlag(e,"u"),s=e.includes("--local");o||(fmt.err("Usage: apo login --key YOUR_API_KEY [--user YOUR_USER_ID] [--local]"),process.exit(1));const a=saveConfig({apiKey:o,userId:t||null},s);fmt.ok(`API key saved to ${a}`)}async function signin(e,o){const t=getFlag(e,"email")||getFlag(e,"e"),s=getFlag(e,"password")||getFlag(e,"p"),a=e.includes("--local");t&&s||(fmt.err("Usage: apo signin --email you@co.com --password YourPass [--local]"),process.exit(1));try{const e=api({baseUrl:o,apiKey:""}),i=await e.signin({email:t,password:s}),n=i.data||i,r=n.token||n.accessToken||n.apiKey;let l=n.userId||n.user?.id||n.user?._id;if(!l&&r&&r.includes("."))try{const e=JSON.parse(Buffer.from(r.split(".")[1],"base64").toString());l=e.userId||e.id||e.sub||null}catch{}r||(fmt.err("Sign-in failed: no session token returned."),process.exit(1));const c=saveConfig({apiKey:r,userId:l||null,email:t,baseUrl:o},a);fmt.ok(`Signed in: ${t}`),l&&console.log(` userId: ${l}`),console.log(` config: ${c}`),console.log(""),fmt.info("Session token saved. Run `apo book`, `apo events`, or `apo join` as this user.")}catch(e){fmt.err(`Sign-in failed: ${e.message}`),process.exit(1)}}function logout(e){clearConfig(!0),clearConfig(!1),fmt.ok("Logged out. Config cleared.")}function showConfig(e){console.log(` source: ${e.source}`),console.log(" apiKey: "+(e.apiKey?e.apiKey.slice(0,20)+"...":"(none)")),console.log(` userId: ${e.userId||"(none)"}`),console.log(` baseUrl: ${e.baseUrl}`)}module.exports={signup:signup,signin:signin,activate:activate,login:login,logout:logout,showConfig:showConfig};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const{getFlag:getFlag,loadConfig:loadConfig}=require("../config"),fmt=require("../format");async function book(e,t
|
|
1
|
+
const{getFlag:getFlag,loadConfig:loadConfig}=require("../config"),fmt=require("../format");function unwrap(o){let e=o;for(;e&&"object"==typeof e&&e.data&&!Array.isArray(e)&&!e.bookingId&&!e.bookings;)e=e.data;return e||{}}function bookingList(o){const e=unwrap(o);return Array.isArray(e)?e:e.bookings||(Array.isArray(e.data)?e.data:[])||[]}function roomNameOf(o){const e=(o.customer&&o.customer.customFields||[]).find(o=>"meetingRoomName"===o.name);return e?e.value:null}function hostUrlOf(o){const e=(o.customer&&o.customer.customFields||[]).find(o=>"meetingHostUrl"===o.name);return e?e.value:null}async function book(o,e,t){const n=getFlag(e,"event")||getFlag(e,"e"),s=getFlag(e,"date")||getFlag(e,"d"),a=getFlag(e,"email"),l=getFlag(e,"name")||getFlag(e,"n")||"Guest",i=getFlag(e,"timezone")||getFlag(e,"z")||"UTC";s&&a||(fmt.err("Usage: apo book --event EVENT_ID --date 2026-06-15T14:00:00Z --email jane@co.com [--host host@co.com]"),process.exit(1));const r=getFlag(e,"host"),g=r?void 0:loadConfig([]).userId;r||g||(fmt.err("No host: pass --host host@co.com or run `apo init` (userId missing from config)"),process.exit(1));const c=await o.book({eventTypeId:n,hostEmail:r,hostId:g,scheduledAt:s,duration:30,customer:{name:l,email:a},timezone:i,location:{type:"video"}});if(t)return console.log(JSON.stringify(c,null,2));const u=unwrap(c);fmt.ok(`Booking created: ${u.bookingId||u.id||u._id||"(id unavailable)"}`);const m=roomNameOf(u);m&&console.log(fmt.dim(` room: ${m} — join with: apo join ${u.bookingId||m}`))}async function bookings(o,e,t){const n=getFlag(e,"host")||loadConfig([]).userId;n||(fmt.err("No host: run `apo signin` / `apo init` or pass --host USER_ID"),process.exit(1));let s=bookingList(await o.bookingsByHost(n));const a=getFlag(e,"status")||getFlag(e,"s");return a&&(s=s.filter(o=>(o.status||"").toLowerCase()===a.toLowerCase())),t?console.log(JSON.stringify(s,null,2)):s.length?void s.sort((o,e)=>new Date(o.scheduledAt||0)-new Date(e.scheduledAt||0)).forEach(o=>console.log(fmt.bookingLine(o))):fmt.info("No bookings found.")}async function bookingShow(o,e,t){const n=e.find(o=>!o.startsWith("-"));n||(fmt.err("Usage: apo booking BOOKING_ID"),process.exit(1));const s=unwrap(await o.getBooking(n));if(t)return console.log(JSON.stringify(s,null,2));console.log(fmt.bold(s.bookingId||n)),console.log(` when: ${s.scheduledAt} (${s.timezone||"UTC"}, ${s.duration||"?"}min)`),console.log(` status: ${s.status||"?"}`),s.customer&&console.log(` customer: ${s.customer.name||""} <${s.customer.email||""}>`),s.eventType&&console.log(` event: ${s.eventType.name||s.eventType.slug||""}`);const a=roomNameOf(s);a&&console.log(` room: ${a}`),s.location&&s.location.joinUrl&&console.log(` join: ${s.location.joinUrl}`);const l=hostUrlOf(s);l&&console.log(` hostUrl: ${l}`),a&&console.log(fmt.dim(`\n Jump in: apo join ${s.bookingId||a}`))}async function cancel(o,e,t){const n=e.find(o=>!o.startsWith("-"));n||(fmt.err('Usage: apo cancel BOOKING_ID [--reason "text"]'),process.exit(1));const s=getFlag(e,"reason")||getFlag(e,"r")||"Cancelled via CLI",a=await o.cancelBooking(n,{reason:s});if(t)return console.log(JSON.stringify(a,null,2));fmt.ok(`Cancelled ${n}`),s&&console.log(fmt.dim(` reason: ${s}`))}async function reschedule(o,e,t){const n=e.find(o=>!o.startsWith("-")),s=getFlag(e,"date")||getFlag(e,"d");n&&s||(fmt.err('Usage: apo reschedule BOOKING_ID --date 2026-08-25T14:00:00Z [--reason "text"]'),process.exit(1));const a=getFlag(e,"reason")||getFlag(e,"r"),l={newDate:s,newScheduledAt:s};a&&(l.reason=a);const i=await o.rescheduleBooking(n,l);if(t)return console.log(JSON.stringify(i,null,2));fmt.ok(`Rescheduled ${n} → ${s}`)}async function events(o,e,t){const n=await o.events(),s=Array.isArray(n)?n:n.eventTypes||n.data&&(Array.isArray(n.data)?n.data:n.data.eventTypes)||[];return t?console.log(JSON.stringify(s,null,2)):s.length?void s.forEach(o=>console.log(fmt.eventLine(o))):fmt.info("No event types found.")}async function slots(o,e,t){const n=getFlag(e,"email")||getFlag(e,"e"),s=getFlag(e,"date")||getFlag(e,"d"),a=getFlag(e,"timezone")||getFlag(e,"z")||"UTC";n&&s||(fmt.err("Usage: apo slots --email host@co.com --date 2026-06-15"),process.exit(1));const l=await o.slots(n,s,a),i=l.slots||l.data||[];return t?console.log(JSON.stringify(i,null,2)):i.length?void i.forEach(o=>console.log(` ${o.startTime||o.start} — ${o.endTime||o.end}`)):fmt.info("No available slots.")}module.exports={book:book,bookings:bookings,bookingShow:bookingShow,cancel:cancel,reschedule:reschedule,events:events,slots:slots,roomNameOf:roomNameOf,unwrap:unwrap};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const{getFlag:getFlag,positionalArgs:positionalArgs}=require("../config"),fmt=require("../format");async function run(e,o,t){const n=positionalArgs(o),
|
|
1
|
+
const{getFlag:getFlag,positionalArgs:positionalArgs}=require("../config"),fmt=require("../format");async function run(e,o,t){const n=positionalArgs(o),s=n[0];if("send"===s){const s=(n[1]||"").replace(/^@/,""),a=n.slice(2).join(" ")||getFlag(o,"message")||getFlag(o,"m"),i=getFlag(o,"action")||getFlag(o,"a"),l=getFlag(o,"payload");s||(fmt.err('Usage: apo channel send @AGENT_ID "message" [--action X] [--payload JSON]'),process.exit(1));const r={to:s,type:i?"request":"notify"};if(a&&(r.payload={message:a}),i&&(r.action=i),l)try{r.payload=JSON.parse(l)}catch{r.payload={raw:l}}"request"===r.type&&(r.requestId=`req_${Date.now()}`);const g=await e.channelSend(r);if(t)return console.log(JSON.stringify(g,null,2));const c=g.data||g;return fmt.ok(`Sent to @${s}: ${i||a||"(payload)"}`),void(c.id&&console.log(` id: ${c.id}`))}if("inbox"===s){const o=await e.channelInbox();if(t)return console.log(JSON.stringify(o,null,2));const n=(o.data||o).messages||[];return n.length?(console.log(`\n[1mInbox (${n.length} unread)[0m\n`),void n.forEach(e=>{const o=new Date(e.timestamp).toLocaleTimeString(),t=e.action?` [${e.action}]`:"",n=e.payload?.message||JSON.stringify(e.payload||"").slice(0,80);console.log(` [36m@${e.from}[0m${t} [2m${o}[0m`),console.log(` ${n}`)})):fmt.info("No unread messages.")}if("create"===s){const s=n[1];s||(fmt.err('Usage: apo channel create NAME [--topic "..."]'),process.exit(1));const a=await e.groupCreate(s,getFlag(o,"topic"));if(t)return console.log(JSON.stringify(a,null,2));const i=a.data||a;return void fmt.ok(`Created #${i.slug}${i.topic?` — ${i.topic}`:""} (you joined)`)}if("join"===s||"leave"===s){const o=n[1];o||(fmt.err(`Usage: apo channel ${s} NAME`),process.exit(1));const a=await("join"===s?e.groupJoin(o):e.groupLeave(o));if(t)return console.log(JSON.stringify(a,null,2));const i=a.data||a;return void fmt.ok("join"===s?`Joined #${i.slug}`:`Left #${i.slug}`)}if("list"===s){const o=await e.groupList();if(t)return console.log(JSON.stringify(o,null,2));const n=(o.data||o).channels||[];return n.length?(console.log(`\n[1mChannels (${n.length})[0m\n`),void n.forEach(e=>{const o=e.unread>0?` [1m[38;5;208m● ${e.unread}[0m`:"",t=e.joined?"[32m✓[0m":" ";console.log(` ${t} [36m#${e.slug}[0m${o} [2m${e.members.length} member${1===e.members.length?"":"s"}${e.topic?` · ${e.topic}`:""}[0m`)})):fmt.info("No channels yet. Create one: apo channel create NAME")}if("post"===s){const s=n[1],a=n.slice(2).join(" ")||getFlag(o,"message")||getFlag(o,"m");s&&a||(fmt.err('Usage: apo channel post NAME "message"'),process.exit(1));const i=await e.groupSend(s,{text:a,payload:{message:a}});if(t)return console.log(JSON.stringify(i,null,2));const l=i.data||i;return void fmt.ok(`→ #${l.room||s}: ${a}${l.id?` [2m(${l.id})[0m`:""}`)}if("history"===s&&n[1]&&!n[1].startsWith("@")){const s=getFlag(o,"limit")||"50",a=await e.groupHistory(n[1],s);if(t)return console.log(JSON.stringify(a,null,2));const i=a.data||a,l=i.messages||[];return l.length?(console.log(`\n[1m#${i.slug||n[1]} (${l.length})[0m\n`),void l.forEach(e=>{const o=e.ts?new Date(e.ts).toLocaleTimeString():"",t=e.text||e.payload?.message||JSON.stringify(e.payload||"").slice(0,80);console.log(` [36m@${e.from}[0m [2m${o}[0m`),console.log(` ${t}`)})):fmt.info(`No messages in #${i.slug||n[1]}.`)}if("history"===s){const s=(n[1]||"").replace(/^@/,""),a=getFlag(o,"limit")||"50";s||(fmt.err("Usage: apo channel history <@AGENT_ID|NAME> [--limit N]"),process.exit(1));const i=await e.channelHistory(s,a);if(t)return console.log(JSON.stringify(i,null,2));const l=(i.data||i).messages||[];return l.length?(console.log(`\n[1mHistory with @${s} (${l.length})[0m\n`),void l.forEach(e=>{const o=new Date(e.timestamp).toLocaleTimeString(),t=e.from===s?"[36m←[0m":"[32m→[0m",n=e.action?` [${e.action}]`:"",a=e.payload?.message||JSON.stringify(e.payload||"").slice(0,80);console.log(` ${t} ${n} ${a} [2m${o}[0m`)})):fmt.info(`No messages with @${s}.`)}if("agents"===s){const o=await e.channelAgents();if(t)return console.log(JSON.stringify(o,null,2));const n=(o.data||o).peers||[];return n.length?(console.log(`\n[1mAgent Contacts (${n.length})[0m\n`),void n.forEach(e=>{const o=new Date(e.lastMessage).toLocaleString();console.log(` [36m@${e.agentId}[0m last: ${o} ${e.direction}`)})):fmt.info("No agent contacts yet.")}fmt.err("Usage: apo channel [send|inbox|history|agents|create|join|leave|list|post]"),process.exit(1)}module.exports={run:run};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const{getFlag:getFlag,hasFlag:hasFlag}=require("../config"),fmt=require("../format"),{probe:probe,runControl:runControl,verdict:verdict,parseIceUrl:parseIceUrl}=require("../lib/turnProbe"),{rawApi:rawApi}=require("../api"),STATUS_STYLE={pass:["[32m","PASS"],fail:["[31m","FAIL"],warn:["[33m","WARN"],inconclusive:["[2m","INCONCLUSIVE"]};function badge(e){const[r,o]=STATUS_STYLE[e]||["",e.toUpperCase()];return`${r}${o.padEnd(12)}[0m`}async function resolveIceServers(e,r){const o=getFlag(r,"url");if(o){const e=getFlag(r,"user")||process.env.TURN_SERVER_USERNAME,s=getFlag(r,"pass")||process.env.TURN_SERVER_CREDENTIAL;return{source:"command line",servers:o.split(",").map(r=>({urls:r.trim(),username:e,credential:s}))}}try{const r=await rawApi(e)("GET","/stt/config"),o=r&&r.iceServers||[];if(o.length)return{source:`${e.baseUrl}/stt/config`,servers:o}}catch(e){hasFlag(r,"json")||fmt.info(`Could not read /stt/config (${e.message}) — falling back to local env`)}const s=[],t=process.env.TURN_SERVER_USERNAME,n=process.env.TURN_SERVER_CREDENTIAL;for(const[e,r]of[["STUN_SERVERS",!1],["TURN_SERVERS",!0]])if(process.env[e])try{JSON.parse(process.env[e]).forEach(e=>{s.push(r?{urls:e.urls||e,username:t,credential:n}:{urls:e.urls||e})})}catch{}return{source:"local environment",servers:s}}function flatten(e){const r=[];return e.forEach(e=>{(Array.isArray(e.urls)?e.urls:[e.urls]).filter(Boolean).forEach(o=>r.push({url:o,username:e.username,credential:e.credential}))}),r}async function run(e,r,o){const s=parseInt(getFlag(r,"timeout")||"6000",10),{source:t,servers:n}=await resolveIceServers(e,r),a=flatten(n);a.length||(fmt.err("No ICE servers found. Pass --url turn:HOST:PORT [--user U --pass P], or check STUN/TURN env."),process.exit(1));const l=a.map(e=>({...e,ice:parseIceUrl(e.url)})).filter(e=>!!e.ice||(o||fmt.err(`Unparseable ICE url, skipped: ${e.url}`),!1)),c=[...new Set(l.filter(e=>"udp"===e.ice.transport).map(e=>e.ice.port))],i={};for(const e of c)i[e]=await runControl({port:e,timeoutMs:s});const u=[];for(const e of l){const r=e.ice.needsAuth;if(r&&(!e.username||!e.credential)){u.push({url:e.url,status:"warn",reason:"TURN url has no credentials — cannot test allocate",probe:null});continue}const o=await probe({host:e.ice.host,port:e.ice.port,transport:e.ice.transport,username:r?e.username:void 0,password:r?e.credential:void 0,timeoutMs:s}),t=verdict({...o,needsAuth:r},i[e.ice.port]||null);u.push({url:e.url,status:t.status,reason:t.reason,probe:o})}const p=u.reduce((e,r)=>({...e,[r.status]:(e[r.status]||0)+1}),{});if(o)return console.log(JSON.stringify({source:t,controls:i,results:u,counts:p,verdict:p.fail?"fail":p.inconclusive?"inconclusive":"pass"},null,2));console.log(`\n${fmt.bold("ICE reachability")} ${fmt.dim("source: "+t)}\n`),console.log(fmt.bold(" Control")),c.length||console.log(` ${fmt.dim("no UDP targets — nothing to control against")}`);for(const e of c){const r=i[e];r.valid&&r.samePort?console.log(` udp/${e} ${STATUS_STYLE.pass[0]}ok[0m via ${r.via} ${fmt.dim("you appear as "+r.mappedAddress)}`):r.valid?console.log(` udp/${e} ${STATUS_STYLE.warn[0]}weak[0m — only udp/${r.port} reachable (${r.via}); this port is unproven`):console.log(` udp/${e} ${STATUS_STYLE.fail[0]}none[0m — no public STUN server answered. This host is blind.`)}console.log(`\n${fmt.bold(" Targets")}`),u.forEach(e=>{const r=e.probe,o=r&&null!=r.ms?fmt.dim(` ${r.ms}ms`):"";console.log(` ${badge(e.status)} ${e.url}${o}`),console.log(` ${fmt.dim(e.reason)}`),r&&r.relayAddress&&console.log(` ${fmt.dim("relay "+r.relayAddress+(r.realm?" realm "+r.realm:""))}`)});const m=["pass","fail","warn","inconclusive"].filter(e=>p[e]).map(e=>`${p[e]} ${e}`).join(", ");console.log(`\n ${m}`),p.inconclusive&&(console.log(fmt.dim("\n INCONCLUSIVE means this machine could not prove it can reach the port at all.")),console.log(fmt.dim(" Do NOT report an outage on that basis — re-run from a host with a working control."))),console.log(""),p.fail&&process.exit(1)}module.exports={run:run};
|
package/dist/commands/meeting.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const{getFlag:getFlag}=require("../config"),fmt=require("../format"),{MeetingSession:MeetingSession}=require("../meetingSession");async function join(e,t,
|
|
1
|
+
const{getFlag:getFlag}=require("../config"),fmt=require("../format"),{MeetingSession:MeetingSession}=require("../meetingSession");async function join(e,o,t,n){let a=o[0];if(a||(fmt.err('Usage: apo join ROOM_ID|BOOKING_ID [--name N] [--role R] [--say "msg"]'),process.exit(1)),/^bkg_/.test(a)&&e)try{const{unwrap:o,roomNameOf:n}=require("./bookings"),s=n(o(await e.getBooking(a)));s||(fmt.err(`Booking ${a} has no video room (location may not be video).`),process.exit(1)),t||console.log(fmt.dim(` resolved ${a} → room ${s}`)),a=s}catch(e){fmt.err(`Could not resolve booking ${a}: ${e.message}`),process.exit(1)}const s=getFlag(o,"name")||"Claude Code",i=getFlag(o,"role")||"assistant",r=getFlag(o,"say"),l=getFlag(o,"voice"),c=new MeetingSession({baseUrl:n.baseUrl,roomId:a,agentName:s,agentRole:i,onHearingChange:e=>{t||r||(e.active?console.log(fmt.dim(` 🎙 hearing via ${e.transcriber}`)):console.log(fmt.dim(" 🔇 deaf: no human transcriber in room (agent cannot hear speech; chat still works)")))}});async function g(o){if(c.sendChat(o),l&&e)try{const t=await e.synthesize({text:o,voice:l,agentRole:l});t&&t.audio&&(c.sendAudio({audio:t.audio,format:t.format}),await new Promise(e=>setTimeout(e,800)))}catch(e){t||fmt.err(`Voice synth failed (text sent): ${e.message}`)}}let m;try{m=await c.join()}catch(e){fmt.err(`Join failed: ${e.message}`),process.exit(1)}if(t&&r)return await g(r),c.leave(),console.log(JSON.stringify({...m,sent:{text:r,voice:l||null}},null,2));if(t||(fmt.ok(`Joined ${a} as ${fmt.bold(s)} (${i})`),m.peerCount?console.log(fmt.dim(` ${m.peerCount} participant(s): ${m.peers.map(e=>e.name).join(", ")}`)):m.note&&console.log(fmt.dim(` ${m.note}`))),r)return await g(r),t||fmt.info(`Sent: "${r}"${l?` (voice: ${l})`:""}`),void c.leave();if(t)return c.leave(),console.log(JSON.stringify(m,null,2));console.log(fmt.dim(" Listening for transcript + chat. Press Ctrl-C to leave.\n"));const f=setInterval(()=>{c.getTranscripts().forEach(e=>console.log(`${fmt.bold(e.speaker||"?")}: ${e.text}`)),c.getChats().forEach(e=>console.log(`${fmt.green("💬 "+(e.from||"?"))}: ${e.message}`))},1500),d=()=>{clearInterval(f),c.leave(),fmt.ok(`Left ${a}`),process.exit(0)};process.on("SIGINT",d),process.on("SIGTERM",d),await new Promise(()=>{})}module.exports={join:join};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const{getFlag:getFlag,positionalArgs:positionalArgs}=require("../config"),{streamChannels:streamChannels}=require("../api"),fmt=require("../format"),COLORS=[31,32,33,34,35,36,91,92,93,94,95,96];function authorColor(t){let n=0;for(let e=0;e<String(t).length;e++)n=31*n+String(t).charCodeAt(e)|0;return COLORS[Math.abs(n)%COLORS.length]}const paint=t=>`[${authorColor(t)}m${t}[0m`,dim=t=>`[2m${t}[0m`,bold=t=>`[1m${t}[0m`,badge=t=>t>0?` [1m[38;5;208m● ${t}[0m`:"";function msgText(t){return t.text||t.payload&&(t.payload.message||JSON.stringify(t.payload).slice(0,100))||(t.action?`[${t.action}]`:"(empty)")}function msgTime(t){const n=t.ts?new Date(t.ts):t.timestamp?new Date(t.timestamp):null;return n?n.toLocaleTimeString():""}function printMsg(t,n=" "){const e=t.action?` ${dim(`[${t.action}]`)}`:"";console.log(`${n}${paint("@"+t.from)}${e} ${dim(msgTime(t))}`),console.log(`${n} ${msgText(t)}`)}async function wall(t,n,e){const o=await t.wall();if(e)return console.log(JSON.stringify(o,null,2));const a=o.data||o,i=a.threads||[];if(console.log(`\n${bold("Wall")} ${dim("·")} ${a.unread>0?`${bold(String(a.unread))} unread`:"all read"}\n`),!i.length)return fmt.info('No threads yet. Start one: apo dm @agent "hi" · apo channel create NAME');i.forEach(t=>{const n="dm"===t.kind?paint("@"+t.peer):paint("#"+(t.slug||t.name)),e=t.lastActivity?dim(new Date(t.lastActivity).toLocaleString()):"";console.log(` ${t.unread>0?"[38;5;208m●[0m":dim("·")} ${n}${badge(t.unread)} ${e}`)}),console.log(`\n${dim("Open: apo dm @agent · apo channel history NAME · apo dm @agent --watch")}`)}async function dm(t,n,e,o){const a=positionalArgs(n),i=(a[0]||"").replace(/^@/,""),s=a.slice(1).join(" ")||getFlag(n,"message")||getFlag(n,"m"),l=n.includes("--watch")||n.includes("-w");if(i||(fmt.err('Usage: apo dm @AGENT ["message"] [--watch]'),process.exit(1)),s){const n=await t.channelSend({to:i,text:s,payload:{message:s}});if(e&&!l)return console.log(JSON.stringify(n,null,2));const o=n.data||n;if(fmt.ok(`→ @${i}: ${s}${o.id?dim(` (${o.id})`):""}`),!l)return}const r=await t.channelHistory(i,getFlag(n,"limit")||"50"),c=(r.data||r).messages||[];if(e&&!l)return console.log(JSON.stringify(r,null,2));if(console.log(`\n${bold(`DM with ${paint("@"+i)}`)} ${dim(`(${c.length} messages)`)}\n`),c.length||fmt.info("No messages yet."),c.forEach(t=>printMsg(t)),l){console.log(`\n${dim("Watching for new messages… Ctrl-C to exit.")}\n`);const t=streamChannels(o,(t,n)=>{if("message"===t&&n.message){const t=n.message;"dm"!==t.kind||t.from!==i&&t.to!==i||printMsg(t)}else"error"===t&&(fmt.err(`Stream error: ${n.message||n.status}`),process.exit(1))});process.on("SIGINT",()=>{t.destroy(),console.log(),process.exit(0)}),await new Promise(()=>{})}}async function notifications(t,n,e){const o=await t.notificationsDigest();if(e)return console.log(JSON.stringify(o,null,2));const a=o.data||o,i=a.digest||[];if(!i.length)return fmt.ok("No unread messages — all caught up.");console.log(`\n${bold("Notifications")} ${dim("·")} ${bold(String(a.unread))} unread\n`),i.forEach(t=>{const n=t.lastActivity?dim(new Date(t.lastActivity).toLocaleString()):"";console.log(` [38;5;208m●[0m ${paint(t.label)}${badge(t.unread)} ${n}`)}),console.log(`\n${dim("Read to ack: apo dm @agent · apo channel history NAME")}`)}module.exports={wall:wall,dm:dm,notifications:notifications};
|
package/dist/format.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const ok=e=>console.log(`[32m✓[0m ${e}`),err=e=>console.error(`[31m✗[0m ${e}`),info=e=>console.log(`[36mℹ[0m ${e}`),dim=e=>`[2m${e}[0m`,bold=e=>`[1m${e}[0m`,green=e=>`[32m${e}[0m`;function roomLine(e){return` ${bold(e.roomId||e._id||"?")} ${e.title||"Untitled"} ${dim(e.status||"")} ${dim(e.participants+" participants"||"")}`}function eventLine(e){const
|
|
1
|
+
const ok=e=>console.log(`[32m✓[0m ${e}`),err=e=>console.error(`[31m✗[0m ${e}`),info=e=>console.log(`[36mℹ[0m ${e}`),dim=e=>`[2m${e}[0m`,bold=e=>`[1m${e}[0m`,green=e=>`[32m${e}[0m`;function roomLine(e){return` ${bold(e.roomId||e._id||"?")} ${e.title||"Untitled"} ${dim(e.status||"")} ${dim(e.participants+" participants"||"")}`}function eventLine(e){const o=void 0!==e.active?e.active:e.isActive;return` ${bold(e._id||e.id||"?")} ${e.name||e.title||"?"} ${dim(e.duration+"min")} ${dim(o?"active":"inactive")}`}function bookingLine(e){const o=e.scheduledAt?new Date(e.scheduledAt).toISOString().replace(".000Z","Z"):"?",i=e.customer&&(e.customer.name||e.customer.email)||"?",n=e.status||"",t="cancelled"===n?"[31m":"confirmed"===n?"[32m":"[33m";return` ${bold(e.bookingId||e.id||"?")} ${o} ${i} ${t}${n}[0m`}module.exports={ok:ok,err:err,info:info,dim:dim,bold:bold,green:green,roomLine:roomLine,eventLine:eventLine,bookingLine:bookingLine};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const dgram=require("dgram"),net=require("net"),tls=require("tls"),crypto=require("crypto"),MAGIC_COOKIE=554869826,BINDING_REQUEST=1,BINDING_SUCCESS=257,ALLOCATE_REQUEST=3,ALLOCATE_SUCCESS=259,ATTR_MAPPED_ADDRESS=1,ATTR_USERNAME=6,ATTR_MESSAGE_INTEGRITY=8,ATTR_ERROR_CODE=9,ATTR_REALM=20,ATTR_NONCE=21,ATTR_XOR_RELAYED_ADDRESS=22,ATTR_REQUESTED_TRANSPORT=25,ATTR_XOR_MAPPED_ADDRESS=32,UDP_CONTROLS=[{host:"stun.cloudflare.com",port:3478},{host:"stun.nextcloud.com",port:3478},{host:"stun.sipgate.net",port:3478},{host:"stun.l.google.com",port:19302},{host:"stun1.l.google.com",port:19302}];function buildHeader(e,t,r){const n=Buffer.alloc(20);return n.writeUInt16BE(e,0),n.writeUInt16BE(t,2),n.writeUInt32BE(554869826,4),r.copy(n,8),n}function buildAttr(e,t){const r=(4-t.length%4)%4,n=Buffer.alloc(4+t.length+r);return n.writeUInt16BE(e,0),n.writeUInt16BE(t.length,2),t.copy(n,4),n}function buildBindingRequest(e){return buildHeader(1,0,e)}function buildAllocateRequest(e,t){const r=[buildAttr(25,Buffer.from([17,0,0,0]))];if(!t){const t=Buffer.concat(r);return Buffer.concat([buildHeader(3,t.length,e),t])}r.push(buildAttr(6,Buffer.from(t.username,"utf8"))),r.push(buildAttr(20,t.realm)),r.push(buildAttr(21,t.nonce));const n=Buffer.concat(r),o=crypto.createHash("md5").update(`${t.username}:${t.realm.toString("utf8")}:${t.password}`).digest(),s=buildHeader(3,n.length+24,e),a=crypto.createHmac("sha1",o).update(Buffer.concat([s,n])).digest(),l=Buffer.concat([n,buildAttr(8,a)]);return Buffer.concat([buildHeader(3,l.length,e),l])}function parseMessage(e){if(e.length<20)return null;const t={type:e.readUInt16BE(0),attrs:{}},r=Math.min(20+e.readUInt16BE(2),e.length);let n=20;for(;n+4<=r;){const r=e.readUInt16BE(n),o=e.readUInt16BE(n+2);if(n+4+o>e.length)break;t.attrs[r]=e.slice(n+4,n+4+o),n+=4+o+(4-o%4)%4}return t}function decodeAddress(e,t){if(!e||e.length<8)return null;if(1!==e.readUInt8(1))return"(ipv6)";const r=t?8466^e.readUInt16BE(2):e.readUInt16BE(2),n=e.slice(4,8);let o;if(t){const e=Buffer.alloc(4);e.writeUInt32BE(554869826,0),o=Array.from(n).map((t,r)=>t^e[r]).join(".")}else o=Array.from(n).join(".");return`${o}:${r}`}function decodeError(e){return!e||e.length<4?null:{code:100*e.readUInt8(2)+e.readUInt8(3),reason:e.slice(4).toString("utf8")}}function createTransport(e,t,r,n){if("udp"===e){const e=dgram.createSocket("udp4");return{send:n=>e.send(n,r,t),onMessage:t=>e.on("message",t),onError:t=>e.on("error",t),close:()=>{try{e.close()}catch{}},connect:e=>e()}}const o=0!==net.isIP(t),s="tls"===e?tls.connect({host:t,port:r,...o?{}:{servername:t},rejectUnauthorized:!1}):new net.Socket;let a=Buffer.alloc(0),l=()=>{};return s.setTimeout(n),s.on("data",e=>{for(a=Buffer.concat([a,e]);a.length>=20;){const e=20+a.readUInt16BE(2);if(a.length<e)return;const t=a.slice(0,e);a=a.slice(e),l(t)}}),{send:e=>s.write(e),onMessage:e=>{l=e},onError:e=>{s.on("error",t=>e(t)),s.on("timeout",()=>e(Object.assign(new Error("timeout"),{code:"ETIMEDOUT"})))},close:()=>{try{s.destroy()}catch{}},connect:n=>{"tls"===e?s.on("secureConnect",n):s.connect(r,t,n)},peerCert:()=>{if("tls"!==e)return null;try{const e=s.getPeerCertificate();return e&&e.subject?{subject:e.subject.CN||null,issuer:e.issuer&&e.issuer.CN,validTo:e.valid_to}:null}catch{return null}}}}function probe({host:e,port:t,transport:r="udp",username:n,password:o,timeoutMs:s=5e3}){return new Promise(a=>{const l={host:e,port:t,transport:r,reachable:!1,binding:null,mappedAddress:null,realm:null,allocate:null,relayAddress:null,tlsCert:null,error:null,ms:null},c=Date.now();let u,i="binding",d=crypto.randomBytes(12),p=!1;try{u=createTransport(r,e,t,s)}catch(e){return l.ms=Date.now()-c,l.error=e.code||e.message,a(l)}const f=e=>{p||(p=!0,clearTimeout(h),u.peerCert&&(l.tlsCert=u.peerCert()),u.close(),l.ms=Date.now()-c,a(Object.assign(l,e||{})))},h=setTimeout(()=>f({error:l.reachable?`timeout during ${i}`:"timeout (no response)"}),s);u.onError(e=>f({error:e.code||e.message})),u.onMessage(e=>{const t=parseMessage(e);if(t){if(l.reachable=!0,"binding"===i)return 257!==t.type?f({binding:"unexpected response"}):(l.binding="ok",l.mappedAddress=decodeAddress(t.attrs[32],!0)||decodeAddress(t.attrs[1],!1),n?(i="allocate-challenge",d=crypto.randomBytes(12),u.send(buildAllocateRequest(d,null))):f());if("allocate-challenge"===i){const e=decodeError(t.attrs[9]);return l.realm=t.attrs[20]?t.attrs[20].toString("utf8"):null,e&&(401===e.code||438===e.code)&&t.attrs[20]&&t.attrs[21]?(i="allocate-auth",d=crypto.randomBytes(12),u.send(buildAllocateRequest(d,{username:n,password:o,realm:t.attrs[20],nonce:t.attrs[21]}))):259===t.type?f({allocate:"ok (no auth required)",relayAddress:decodeAddress(t.attrs[22],!0)}):f({allocate:e?`error ${e.code} ${e.reason}`:"unexpected response"})}if("allocate-auth"===i){if(259===t.type)return f({allocate:"ok",relayAddress:decodeAddress(t.attrs[22],!0)});const e=decodeError(t.attrs[9]);return f({allocate:e?`error ${e.code} ${e.reason}`:"unexpected response"})}}}),u.connect(()=>u.send(buildBindingRequest(d)))})}async function runControl({port:e=null,timeoutMs:t=5e3}={}){const r=e?[...UDP_CONTROLS.filter(t=>t.port===e),...UDP_CONTROLS.filter(t=>t.port!==e)]:UDP_CONTROLS,n=[];for(const o of r){const r=await probe({host:o.host,port:o.port,transport:"udp",timeoutMs:t});if(n.push({target:`${o.host}:${o.port}`,ok:"ok"===r.binding,detail:r.error||r.binding}),"ok"===r.binding)return{valid:!0,samePort:e?o.port===e:null,via:`${o.host}:${o.port}`,port:o.port,mappedAddress:r.mappedAddress,attempts:n}}return{valid:!1,samePort:!1,via:null,port:null,attempts:n}}function parseIceUrl(e){const t=/^(stun|stuns|turn|turns):([^:?]+)(?::(\d+))?(?:\?transport=(\w+))?$/i.exec(String(e).trim());if(!t)return null;const r=t[1].toLowerCase(),n="turns"===r||"stuns"===r;let o=(t[4]||"").toLowerCase();return o||(o="udp"),n&&(o="tls"),{scheme:r,host:t[2],port:parseInt(t[3],10)||(n?5349:3478),transport:o,needsAuth:"turn"===r||"turns"===r,raw:e}}function verdict(e,t){const r=Boolean(e.username||e.needsAuth);return"ok"!==e.binding||r&&"ok"!==e.allocate?e.reachable&&e.allocate&&/error 401|error 403/.test(e.allocate)?{status:"fail",reason:`credentials rejected (${e.allocate})`}:e.reachable&&e.allocate?{status:"warn",reason:`allocate: ${e.allocate}`}:e.reachable?{status:"warn",reason:e.binding||e.error||"unexpected response"}:e.tlsCert?{status:"fail",reason:`TLS answered by a non-STUN service (cert CN=${e.tlsCert.subject||"?"}) — ingress is holding this port`}:/ECONNREFUSED/.test(e.error||"")?{status:"fail",reason:"connection refused — nothing listening on this port"}:"udp"!==e.transport?{status:"inconclusive",reason:`no response over ${e.transport} and there is no public STUN-over-${e.transport} control to prove this host can do it at all`}:t&&t.valid?!1===t.samePort?{status:"inconclusive",reason:`control only passed on udp/${t.port}, not udp/${e.port} — this port may be filtered locally`}:{status:"fail",reason:`no response, yet udp/${e.port} works to ${t.via}`}:{status:"inconclusive",reason:"no public STUN server is reachable from here — this host is blind, not the target"}:{status:"pass",reason:r?"binding + authenticated allocate":"binding ok"}}module.exports={probe:probe,runControl:runControl,verdict:verdict,parseIceUrl:parseIceUrl,UDP_CONTROLS:UDP_CONTROLS};
|
package/dist/main.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const{loadConfig:loadConfig,hasFlag:hasFlag,getFlag:getFlag}=require("./config"),{api:api,rawApi:rawApi}=require("./api"),fmt=require("./format"),args=process.argv.slice(2),command=args[0],subArgs=args.slice(1),jsonMode=hasFlag(args,"json"),NO_AUTH=["signup","activate","login","logout","config","help","--help","-h","status","version","--version","-v","mcp-serve","man","renderer","init"],config=loadConfig(args);NO_AUTH.includes(command)||config.apiKey||(fmt.err("No API key. Run: apo signup --email you@co.com --password Pass123!"),fmt.err("Or: apo login --key YOUR_KEY --user YOUR_USERID"),fmt.err("Or set APO_API_KEY environment variable."),process.exit(1));const client=config.apiKey?api(config):null,raw=config.apiKey?rawApi(config):null,HELP='\n[1mAppoiint CLI[0m — Agent-first meeting platform\n\n[1mAuth:[0m\n apo signup --email E --password P Sign up (free 7-day trial, card required)\n apo activate [SUBSCRIPTION_ID] Get API key after card setup\n apo login --key KEY [--user USERID] Save API key to config\n apo logout Clear config\n apo config Show active config\n\n[1mRooms:[0m\n apo room "Title" Create meeting room\n apo rooms List active rooms\n apo room-status ROOM_ID Room status + participants\n apo room-end ROOM_ID End meeting\n apo invite ROOM_ID --email E --name N Invite human or agent\n apo transcript ROOM_ID [--format F] Get transcript (json|text|srt)\n\n[1mMeetings:[0m\n apo join ROOM_ID [--name N --role R] Join room as live agent (streams transcript, Ctrl-C to leave)\n apo join ROOM_ID --say "msg" Join, send one message, leave\n apo join ROOM_ID --say "msg" --voice cfo Speak out loud (assistant.bz TTS) + leave\n apo leave ROOM_ID Leave room\n\n[1mScheduling:[0m\n apo book --date D --email E Create booking\n apo events List event types\n apo slots --email E --date D Check availability\n\n[1mTTS:[0m\n apo tts "text" --voice cfo Synthesize speech\n apo voices List available voices\n\n[1mAdmin:[0m\n apo admin tenants List all tenants\n apo admin tenant-view SLUG Full tenant detail\n apo admin tenant-create SLUG Create tenant\n apo admin tenant-suspend SLUG Suspend tenant\n apo admin tenant-activate SLUG Reactivate tenant\n apo admin tenant-delete SLUG Delete tenant\n apo admin users List all users\n apo admin user-reset EMAIL -p PASS Reset user password\n apo admin billing MRR, ARR, revenue\n apo admin costs Platform cost summary (STT/TTS/LLM)\n apo admin costs ROOM_ID Per-meeting cost breakdown\n apo admin models Spend by provider/model + tokens\n apo admin usage Spend by user\n apo admin timeseries [--bucket day] Spend over time (hour|day)\n apo admin pricing Current provider pricing table\n\n[1mAnalytics:[0m\n apo stats Platform overview\n apo revenue Revenue breakdown\n apo funnel Signup funnel\n apo signups [--days 7] Recent signups\n\n[1mSupport:[0m\n apo tickets [--status open] List support tickets\n apo ticket show NUMBER Ticket detail\n apo ticket reply NUMBER MESSAGE Reply to ticket\n apo ticket close NUMBER Close ticket\n\n[1mAudit:[0m\n apo audit [--action X] [--limit N] Activity trail\n apo audit export [--format csv] Export audit log\n\n[1mHelpdesk (+$4.99/mo):[0m\n apo helpdesk tickets List tickets\n apo helpdesk ticket show ID Ticket detail + messages\n apo helpdesk ticket create "Subject" Create ticket\n apo helpdesk ticket reply ID "msg" Reply to ticket\n apo helpdesk ticket close ID Close ticket\n apo helpdesk ticket assign ID --agent X Assign ticket\n apo helpdesk kb List KB articles\n apo helpdesk stats Helpdesk statistics\n\n[1mCRM (+$4.99/mo):[0m\n apo crm leads List leads\n apo crm lead show ID Lead detail\n apo crm lead create "Name" --email X Create lead\n apo crm lead convert ID Convert lead → opportunity\n apo crm opps List opportunities\n apo crm opp show ID Opportunity detail\n apo crm opp create "Deal" --value X Create opportunity\n apo crm pipelines List pipelines\n apo crm stats CRM statistics\n\n[1mAgents (multi-agent identities):[0m\n apo agent create ci-bot Create named agent under your account\n apo agent list List your agents (plan quota: 3/5/10/∞)\n apo agent use ci-bot Act as @ci-bot (channels route by handle)\n apo agent use --account Switch back to account identity\n apo agent whoami Show active identity\n apo agent revoke ci-bot Revoke agent key\n --agent HANDLE / APO_AGENT env One-off identity override for any command\n\n[1mChannels (agent-to-agent):[0m\n apo channel send @AGENT "msg" Send 1:1 message to agent\n apo channel inbox Unread messages\n apo channel history @AGENT Conversation history\n apo channel agents List agent contacts\n\n[1mDiagnostics:[0m\n apo diagnose ROOM_ID Full room diagnostic (peers, events, relay)\n\n[1mManual:[0m\n apo man renderer Chat bubble renderer capabilities + formats\n apo renderer Same as above (shorthand)\n\n[1mOptions:[0m\n --json Output JSON (for agents)\n --key KEY Override API key\n --help Show this help\n\n[2mhttps://appoiint.com/docs/quickstart.html[0m\n';async function run(){switch(command){case"signup":return require("./commands/auth").signup(subArgs,config.baseUrl);case"activate":return require("./commands/auth").activate(subArgs,config);case"login":return require("./commands/auth").login(subArgs);case"logout":return require("./commands/auth").logout(subArgs);case"config":return require("./commands/auth").showConfig(config);case"room":return require("./commands/rooms").create(client,subArgs,jsonMode,config);case"rooms":return require("./commands/rooms").list(client,subArgs,jsonMode);case"room-status":return require("./commands/rooms").status(client,subArgs,jsonMode);case"room-end":return require("./commands/rooms").end(client,subArgs,jsonMode);case"invite":return require("./commands/rooms").invite(client,subArgs,jsonMode);case"transcript":return require("./commands/rooms").transcript(client,subArgs,jsonMode);case"join":return require("./commands/meeting").join(client,subArgs,jsonMode,config);case"leave":return subArgs[0]||(fmt.err("Usage: apo leave ROOM_ID"),process.exit(1)),client.leaveMeeting({roomId:subArgs[0],agentId:"cli-agent"}).then(e=>jsonMode?console.log(JSON.stringify(e,null,2)):fmt.ok(`Left room ${subArgs[0]}`));case"book":return require("./commands/bookings").book(client,subArgs,jsonMode);case"events":return require("./commands/bookings").events(client,subArgs,jsonMode);case"slots":return require("./commands/bookings").slots(client,subArgs,jsonMode);case"tts":return require("./commands/tts").synthesize(client,subArgs,jsonMode);case"voices":return require("./commands/tts").voices(client,subArgs,jsonMode);case"status":{const e=await api({baseUrl:config.baseUrl,apiKey:""}).status();return jsonMode?console.log(JSON.stringify(e,null,2)):void fmt.ok(`Appoiint ${e.version||"?"} — ${e.available?"online":"offline"}`)}case"version":case"--version":case"-v":return void console.log(require("../package.json").version);case"admin":return require("./commands/admin").run(client,subArgs,jsonMode);case"stats":return require("./commands/stats").run(client,jsonMode);case"funnel":return require("./commands/stats").funnel(client,jsonMode);case"signups":return require("./commands/stats").signups(client,subArgs,jsonMode);case"revenue":return require("./commands/stats").revenue(client,jsonMode);case"tickets":return require("./commands/tickets").list(client,subArgs,jsonMode);case"ticket":const e=subArgs[0];if("show"===e)return require("./commands/tickets").show(client,subArgs.slice(1),jsonMode);if("reply"===e)return require("./commands/tickets").reply(client,subArgs.slice(1),jsonMode);if("close"===e)return require("./commands/tickets").close(client,subArgs.slice(1),jsonMode);fmt.err("Usage: apo ticket [show|reply|close] NUMBER"),process.exit(1);case"audit":return require("./commands/audit").run(client,subArgs,jsonMode);case"helpdesk":return require("./commands/helpdesk").run(client,subArgs,jsonMode,raw);case"crm":return require("./commands/crm").run(client,subArgs,jsonMode,raw);case"agent":return require("./commands/agent").run(config,subArgs,jsonMode);case"channel":return require("./commands/channels").run(client,subArgs,jsonMode);case"diagnose":return require("./commands/diagnose").run(client,subArgs,jsonMode,config);case"init":return require("./commands/init").run(subArgs);case"man":case"renderer":const n="man"===command?subArgs[0]:command;return"renderer"===n?require("./commands/renderer").run(subArgs.slice("man"===command?1:0),jsonMode):void fmt.err("Unknown manual page: "+(n||"(none)")+". Available: renderer");case"mcp-serve":return require("./mcp").serve();case"help":case"--help":case"-h":case void 0:return void console.log(HELP);default:fmt.err(`Unknown command: ${command}`),console.log(HELP),process.exit(1)}}run().catch(e=>{fmt.err(e.message),process.exit(1)});
|
|
1
|
+
const{loadConfig:loadConfig,hasFlag:hasFlag,getFlag:getFlag}=require("./config"),{api:api,rawApi:rawApi}=require("./api"),fmt=require("./format"),args=process.argv.slice(2),command=args[0],subArgs=args.slice(1),jsonMode=hasFlag(args,"json"),NO_AUTH=["signup","signin","activate","login","logout","config","help","--help","-h","status","version","--version","-v","mcp-serve","man","renderer","init"],config=loadConfig(args);NO_AUTH.includes(command)||config.apiKey||(fmt.err("No API key. Run: apo signup --email you@co.com --password Pass123!"),fmt.err("Or: apo login --key YOUR_KEY --user YOUR_USERID"),fmt.err("Or set APO_API_KEY environment variable."),process.exit(1));const client=config.apiKey?api(config):null,raw=config.apiKey?rawApi(config):null,HELP='\n[1mAppoiint CLI[0m — Agent-first meeting platform\n\n[1mAuth:[0m\n apo signup --email E --password P Sign up (free 7-day trial, card required)\n apo signin --email E --password P Sign in as a human seat (email/password)\n apo activate [SUBSCRIPTION_ID] Get API key after card setup\n apo login --key KEY [--user USERID] Save API key to config\n apo logout Clear config\n apo config Show active config\n\n[1mRooms:[0m\n apo room "Title" Create meeting room\n apo rooms List active rooms\n apo room-status ROOM_ID Room status + participants\n apo room-end ROOM_ID End meeting\n apo invite ROOM_ID --email E --name N Invite human or agent\n apo transcript ROOM_ID [--format F] Get transcript (json|text|srt)\n\n[1mMeetings:[0m\n apo join ROOM_ID|BOOKING_ID [--name N] Join room (or bkg_ id → resolves room), streams transcript, Ctrl-C to leave\n apo join ROOM_ID --say "msg" Join, send one message, leave\n apo join ROOM_ID --say "msg" --voice cfo Speak out loud (assistant.bz TTS) + leave\n apo leave ROOM_ID Leave room\n\n[1mScheduling:[0m\n apo book --date D --email E Create booking\n apo bookings [--status S] [--host ID] List bookings (as host)\n apo booking BOOKING_ID Show booking (room + join links)\n apo cancel BOOKING_ID [--reason R] Cancel a booking\n apo reschedule BOOKING_ID --date D Move a booking to a new time\n apo events List event types\n apo slots --email E --date D Check availability\n\n[1mTTS:[0m\n apo tts "text" --voice cfo Synthesize speech\n apo voices List available voices\n\n[1mAdmin:[0m\n apo admin tenants List all tenants\n apo admin tenant-view SLUG Full tenant detail\n apo admin tenant-create SLUG Create tenant\n apo admin tenant-suspend SLUG Suspend tenant\n apo admin tenant-activate SLUG Reactivate tenant\n apo admin tenant-delete SLUG Delete tenant\n apo admin users List all users\n apo admin user-reset EMAIL -p PASS Reset user password\n apo admin billing MRR, ARR, revenue\n apo admin costs Platform cost summary (STT/TTS/LLM)\n apo admin costs ROOM_ID Per-meeting cost breakdown\n apo admin models Spend by provider/model + tokens\n apo admin usage Spend by user\n apo admin timeseries [--bucket day] Spend over time (hour|day)\n apo admin pricing Current provider pricing table\n apo admin ice Probe live STUN/TURN (control-gated)\n apo admin ice --url turn:IP:3478 Probe one server ad-hoc (--user --pass)\n\n[1mAnalytics:[0m\n apo stats Platform overview\n apo revenue Revenue breakdown\n apo funnel Signup funnel\n apo signups [--days 7] Recent signups\n\n[1mSupport:[0m\n apo tickets [--status open] List support tickets\n apo ticket show NUMBER Ticket detail\n apo ticket reply NUMBER MESSAGE Reply to ticket\n apo ticket close NUMBER Close ticket\n\n[1mAudit:[0m\n apo audit [--action X] [--limit N] Activity trail\n apo audit export [--format csv] Export audit log\n\n[1mHelpdesk (+$4.99/mo):[0m\n apo helpdesk tickets List tickets\n apo helpdesk ticket show ID Ticket detail + messages\n apo helpdesk ticket create "Subject" Create ticket\n apo helpdesk ticket reply ID "msg" Reply to ticket\n apo helpdesk ticket close ID Close ticket\n apo helpdesk ticket assign ID --agent X Assign ticket\n apo helpdesk kb List KB articles\n apo helpdesk stats Helpdesk statistics\n\n[1mCRM (+$4.99/mo):[0m\n apo crm leads List leads\n apo crm lead show ID Lead detail\n apo crm lead create "Name" --email X Create lead\n apo crm lead convert ID Convert lead → opportunity\n apo crm opps List opportunities\n apo crm opp show ID Opportunity detail\n apo crm opp create "Deal" --value X Create opportunity\n apo crm pipelines List pipelines\n apo crm stats CRM statistics\n\n[1mAgents (multi-agent identities):[0m\n apo agent create ci-bot Create named agent under your account\n apo agent list List your agents (plan quota: 3/5/10/∞)\n apo agent use ci-bot Act as @ci-bot (channels route by handle)\n apo agent use --account Switch back to account identity\n apo agent whoami Show active identity\n apo agent revoke ci-bot Revoke agent key\n --agent HANDLE / APO_AGENT env One-off identity override for any command\n\n[1mMessaging (agent-to-agent):[0m\n apo wall All threads (DMs + channels) w/ unread badges\n apo dm @AGENT ["msg"] [--watch] Read/send a DM (--watch = live stream)\n apo notifications Unread digest across all threads\n apo channel create NAME [--topic T] Create a named channel\n apo channel join|leave NAME Join / leave a channel\n apo channel list List channels (membership + unread)\n apo channel post NAME "msg" Post to a channel\n apo channel history <NAME|@AGENT> Channel or DM history (reading = ack)\n apo channel send @AGENT "msg" Send 1:1 message (legacy form of apo dm)\n apo channel inbox Unread messages\n apo channel agents List agent contacts\n\n[1mDiagnostics:[0m\n apo diagnose ROOM_ID Full room diagnostic (peers, events, relay)\n\n[1mManual:[0m\n apo man renderer Chat bubble renderer capabilities + formats\n apo renderer Same as above (shorthand)\n\n[1mOptions:[0m\n --json Output JSON (for agents)\n --key KEY Override API key\n --help Show this help\n\n[2mhttps://appoiint.com/docs/quickstart.html[0m\n';async function run(){switch(command){case"signup":return require("./commands/auth").signup(subArgs,config.baseUrl);case"signin":return require("./commands/auth").signin(subArgs,config.baseUrl);case"activate":return require("./commands/auth").activate(subArgs,config);case"login":return require("./commands/auth").login(subArgs);case"logout":return require("./commands/auth").logout(subArgs);case"config":return require("./commands/auth").showConfig(config);case"room":return require("./commands/rooms").create(client,subArgs,jsonMode,config);case"rooms":return require("./commands/rooms").list(client,subArgs,jsonMode);case"room-status":return require("./commands/rooms").status(client,subArgs,jsonMode);case"room-end":return require("./commands/rooms").end(client,subArgs,jsonMode);case"invite":return require("./commands/rooms").invite(client,subArgs,jsonMode);case"transcript":return require("./commands/rooms").transcript(client,subArgs,jsonMode);case"join":return require("./commands/meeting").join(client,subArgs,jsonMode,config);case"leave":return subArgs[0]||(fmt.err("Usage: apo leave ROOM_ID"),process.exit(1)),client.leaveMeeting({roomId:subArgs[0],agentId:"cli-agent"}).then(e=>jsonMode?console.log(JSON.stringify(e,null,2)):fmt.ok(`Left room ${subArgs[0]}`));case"book":return require("./commands/bookings").book(client,subArgs,jsonMode);case"bookings":return require("./commands/bookings").bookings(client,subArgs,jsonMode);case"booking":return require("./commands/bookings").bookingShow(client,subArgs,jsonMode);case"cancel":return require("./commands/bookings").cancel(client,subArgs,jsonMode);case"reschedule":return require("./commands/bookings").reschedule(client,subArgs,jsonMode);case"events":return require("./commands/bookings").events(client,subArgs,jsonMode);case"slots":return require("./commands/bookings").slots(client,subArgs,jsonMode);case"tts":return require("./commands/tts").synthesize(client,subArgs,jsonMode);case"voices":return require("./commands/tts").voices(client,subArgs,jsonMode);case"status":{const e=await api({baseUrl:config.baseUrl,apiKey:""}).status();return jsonMode?console.log(JSON.stringify(e,null,2)):void fmt.ok(`Appoiint ${e.version||"?"} — ${e.available?"online":"offline"}`)}case"version":case"--version":case"-v":return void console.log(require("../package.json").version);case"admin":return require("./commands/admin").run(client,subArgs,jsonMode,config);case"stats":return require("./commands/stats").run(client,jsonMode);case"funnel":return require("./commands/stats").funnel(client,jsonMode);case"signups":return require("./commands/stats").signups(client,subArgs,jsonMode);case"revenue":return require("./commands/stats").revenue(client,jsonMode);case"tickets":return require("./commands/tickets").list(client,subArgs,jsonMode);case"ticket":const e=subArgs[0];if("show"===e)return require("./commands/tickets").show(client,subArgs.slice(1),jsonMode);if("reply"===e)return require("./commands/tickets").reply(client,subArgs.slice(1),jsonMode);if("close"===e)return require("./commands/tickets").close(client,subArgs.slice(1),jsonMode);fmt.err("Usage: apo ticket [show|reply|close] NUMBER"),process.exit(1);case"audit":return require("./commands/audit").run(client,subArgs,jsonMode);case"helpdesk":return require("./commands/helpdesk").run(client,subArgs,jsonMode,raw);case"crm":return require("./commands/crm").run(client,subArgs,jsonMode,raw);case"agent":return require("./commands/agent").run(config,subArgs,jsonMode);case"channel":return require("./commands/channels").run(client,subArgs,jsonMode);case"wall":return require("./commands/wall").wall(client,subArgs,jsonMode);case"dm":return require("./commands/wall").dm(client,subArgs,jsonMode,config);case"notifications":return require("./commands/wall").notifications(client,subArgs,jsonMode);case"diagnose":return require("./commands/diagnose").run(client,subArgs,jsonMode,config);case"init":return require("./commands/init").run(subArgs);case"man":case"renderer":const n="man"===command?subArgs[0]:command;return"renderer"===n?require("./commands/renderer").run(subArgs.slice("man"===command?1:0),jsonMode):void fmt.err("Unknown manual page: "+(n||"(none)")+". Available: renderer");case"mcp-serve":return require("./mcp").serve();case"help":case"--help":case"-h":case void 0:return void console.log(HELP);default:fmt.err(`Unknown command: ${command}`),console.log(HELP),process.exit(1)}}run().catch(e=>{fmt.err(e.message),process.exit(1)});
|
package/dist/mcp-tools.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const TOOLS=[{name:"appoiint_status",description:"Get Appoiint platform status and version.",inputSchema:{type:"object",properties:{}},handler:async e=>e.status(),readOnly:!0},{name:"appoiint_create_room",description:"Create a video meeting room. Returns roomId and join URL.",inputSchema:{type:"object",properties:{title:{type:"string",description:'Meeting title (e.g. "Team Standup")'}},required:["title"]},handler:async(e,t)=>{const i=require("./config").loadConfig([]);return e.createRoom({title:t.title,hostId:i.userId})},readOnly:!1},{name:"appoiint_list_rooms",description:"List active meeting rooms.",inputSchema:{type:"object",properties:{}},handler:async e=>e.listRooms(),readOnly:!0},{name:"appoiint_room_status",description:"Get room status and participants.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t)=>e.roomStatus(t.roomId),readOnly:!0},{name:"appoiint_end_room",description:"End a meeting room.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t)=>e.endRoom(t.roomId),readOnly:!1},{name:"appoiint_invite",description:"Invite a human or agent to a meeting room.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"},email:{type:"string",description:"Invitee email"},name:{type:"string",description:"Invitee name"},type:{type:"string",description:"human or agent (default: human)"}},required:["roomId","email"]},handler:async(e,t)=>e.invite(t.roomId,{email:t.email,name:t.name||"Guest",type:t.type||"human"}),readOnly:!1},{name:"appoiint_transcript",description:"Get meeting transcript.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"},format:{type:"string",description:"json, text, or srt (default: json)"}},required:["roomId"]},handler:async(e,t)=>e.transcript(t.roomId,t.format||"json"),readOnly:!0},{name:"appoiint_join_meeting",description:"Join a meeting room as an AI agent. Creates persistent Socket.IO connection. You will hear transcripts and can send chat messages. Call appoiint_get_transcripts to read what was said, appoiint_send_chat to respond.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID to join"},agentName:{type:"string",description:"Your display name in the meeting (default: Claude Code)"},agentRole:{type:"string",description:"Your role badge (default: assistant)"}},required:["roomId"]},handler:async(e,t,i)=>{const{MeetingSession:n}=require("./meetingSession"),a=new n({baseUrl:i.config.baseUrl,roomId:t.roomId,agentName:t.agentName||"Claude Code",agentRole:t.agentRole||"assistant"}),r=await a.join();return i.meetingSessions.set(t.roomId,a),r},readOnly:!1},{name:"appoiint_get_transcripts",description:"Get speech transcripts from the meeting. Returns what participants have said since last read. Buffer is cleared after reading.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);if(!n)return{error:"Not in this meeting. Call appoiint_join_meeting first."};const a=n.getTranscripts();return{roomId:t.roomId,count:a.length,transcripts:a}},readOnly:!0},{name:"appoiint_send_chat",description:"Send a chat message to all meeting participants. Your message appears in the AI chat panel with your role badge.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"},message:{type:"string",description:"Message to send to meeting participants"}},required:["roomId","message"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);return n?n.sendChat(t.message):{error:"Not in this meeting. Call appoiint_join_meeting first."}},readOnly:!1},{name:"appoiint_meeting_status",description:"Check meeting connection status: connected, peer count, buffered transcript count.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);return n?n.status():{error:"Not in this meeting.",connected:!1}},readOnly:!0},{name:"appoiint_leave_meeting",description:"Leave a meeting room and disconnect.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);if(!n)return{error:"Not in this meeting."};const a=n.leave();return i.meetingSessions.delete(t.roomId),a},readOnly:!1},{name:"appoiint_speak",description:"Speak in a meeting room with an agent voice: synthesizes text via TTS and broadcasts the audio to all participants in one call. Requires an active appoiint_join_meeting session. Text is trimmed to 500 chars server-side; audio over 500KB is dropped by the relay.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID (must have joined via appoiint_join_meeting)"},text:{type:"string",description:"Text to speak (max 500 chars)"},voice:{type:"string",description:"Voice: cfo, cto, cmo, cco, cso, clo, heart, michael, nova, echo (default: heart)"}},required:["roomId","text"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);if(!n)return{error:"Not in this meeting. Call appoiint_join_meeting first."};const a=await e.synthesize({text:t.text,voice:t.voice||"heart",agentRole:t.voice});if(!a||!a.audio)return{error:"TTS synthesis failed",detail:a};const r=n.sendAudio({audio:a.audio,format:a.format});return{spoken:!0,roomId:t.roomId,voice:a.voice,provider:a.provider,bytes:r.bytes}},readOnly:!1},{name:"appoiint_list_events",description:"List bookable event types.",inputSchema:{type:"object",properties:{}},handler:async e=>e.events(),readOnly:!0},{name:"appoiint_check_availability",description:"Check available time slots for a host on a given date.",inputSchema:{type:"object",properties:{email:{type:"string",description:"Host email address"},date:{type:"string",description:"Date (YYYY-MM-DD)"},timezone:{type:"string",description:"Timezone (default: UTC)"}},required:["email","date"]},handler:async(e,t)=>e.slots(t.email,t.date,t.timezone||"UTC"),readOnly:!0},{name:"appoiint_create_booking",description:"Create a booking on behalf of a customer.",inputSchema:{type:"object",properties:{eventTypeId:{type:"string",description:"Event type ID"},scheduledAt:{type:"string",description:"ISO 8601 date-time (e.g. 2026-06-15T14:00:00Z)"},duration:{type:"number",description:"Duration in minutes (default: 30)"},customerName:{type:"string",description:"Customer name"},customerEmail:{type:"string",description:"Customer email"}},required:["scheduledAt","customerEmail"]},handler:async(e,t)=>e.book({eventTypeId:t.eventTypeId,scheduledAt:t.scheduledAt,duration:t.duration||30,customer:{name:t.customerName||"Guest",email:t.customerEmail},location:{type:"video"}}),readOnly:!1},{name:"appoiint_tts",description:"Synthesize text-to-speech with CxO agent voice.",inputSchema:{type:"object",properties:{text:{type:"string",description:"Text to synthesize"},voice:{type:"string",description:"Voice: cfo, cto, cmo, cco, cso, clo, heart, michael, nova, echo (default: heart)"}},required:["text"]},handler:async(e,t)=>e.synthesize({text:t.text,voice:t.voice||"heart",agentRole:t.voice}),readOnly:!0},{name:"appoiint_voices",description:"List available TTS voices and CxO agent voice mappings.",inputSchema:{type:"object",properties:{}},handler:async e=>e.voices(),readOnly:!0},{name:"appoiint_helpdesk_tickets",description:"List helpdesk tickets. Filter by status.",inputSchema:{type:"object",properties:{status:{type:"string",description:"Filter: open, resolved, closed"}}},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("GET","/saas/api/v1/sdk/helpdeskryClient/tickets"+(t.status?`?status=${t.status}`:""))},readOnly:!0},{name:"appoiint_helpdesk_ticket_show",description:"Get ticket detail with messages.",inputSchema:{type:"object",properties:{ticketId:{type:"string"}},required:["ticketId"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("GET",`/saas/api/v1/sdk/helpdeskryClient/tickets/${t.ticketId}`)},readOnly:!0},{name:"appoiint_helpdesk_ticket_create",description:"Create a helpdesk ticket.",inputSchema:{type:"object",properties:{subject:{type:"string"},priority:{type:"string"},category:{type:"string"}},required:["subject"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("POST","/saas/api/v1/sdk/helpdeskryClient/tickets",t)},readOnly:!1},{name:"appoiint_helpdesk_ticket_reply",description:"Reply to a helpdesk ticket.",inputSchema:{type:"object",properties:{ticketId:{type:"string"},message:{type:"string"}},required:["ticketId","message"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("POST",`/saas/api/v1/sdk/helpdeskryClient/tickets/${t.ticketId}/messages`,{content:t.message})},readOnly:!1},{name:"appoiint_crm_leads",description:"List CRM leads. Filter by status.",inputSchema:{type:"object",properties:{status:{type:"string"}}},handler:async(e,t,i)=>{const{rawApi:n}=require("./api"),a=t.status?`?status=${t.status}`:"";return n(i.config)("GET",`/saas/api/v1/sdk/crm/leads${a}`)},readOnly:!0},{name:"appoiint_crm_lead_show",description:"Get lead detail.",inputSchema:{type:"object",properties:{leadId:{type:"string"}},required:["leadId"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("GET",`/saas/api/v1/sdk/crm/leads/${t.leadId}`)},readOnly:!0},{name:"appoiint_crm_lead_create",description:"Create a CRM lead.",inputSchema:{type:"object",properties:{name:{type:"string"},email:{type:"string"},source:{type:"string"}},required:["name"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("POST","/saas/api/v1/sdk/crm/leads",t)},readOnly:!1},{name:"appoiint_crm_opportunities",description:"List CRM opportunities. Filter by stage.",inputSchema:{type:"object",properties:{stage:{type:"string"}}},handler:async(e,t,i)=>{const{rawApi:n}=require("./api"),a=t.stage?`?stage=${t.stage}`:"";return n(i.config)("GET",`/saas/api/v1/sdk/crm/opportunities${a}`)},readOnly:!0},{name:"appoiint_crm_pipelines",description:"List CRM pipelines with stages.",inputSchema:{type:"object",properties:{}},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("GET","/saas/api/v1/sdk/crm/pipelines")},readOnly:!0},{name:"appoiint_channel_send",description:"Send a 1:1 message to another agent. Use for ops requests, data queries, or notifications.",inputSchema:{type:"object",properties:{to:{type:"string",description:"Recipient agent ID"},message:{type:"string",description:"Message text"},action:{type:"string",description:"Intent/action (e.g. deploy, query, notify)"},payload:{type:"object",description:"Structured data payload"}},required:["to"]},handler:async(e,t)=>{const i={to:t.to,type:t.action?"request":"notify"};return t.message&&(i.payload={message:t.message}),t.action&&(i.action=t.action,i.requestId=`req_${Date.now()}`),t.payload&&(i.payload={...i.payload,...t.payload}),e.channelSend(i)},readOnly:!1},{name:"appoiint_channel_inbox",description:"Get unread messages from other agents.",inputSchema:{type:"object",properties:{}},handler:async e=>e.channelInbox(),readOnly:!0},{name:"appoiint_channel_history",description:"Get message history with a specific agent.",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent ID to get history with"},limit:{type:"number",description:"Max messages (default 50)"}},required:["agentId"]},handler:async(e,t)=>e.channelHistory(t.agentId,t.limit),readOnly:!0},{name:"appoiint_channel_agents",description:"List agents you have communicated with.",inputSchema:{type:"object",properties:{}},handler:async e=>e.channelAgents(),readOnly:!0},{name:"appoiint_admin_tenants",description:"List all tenants with plan, status, usage. Superadmin only.",inputSchema:{type:"object",properties:{}},handler:async e=>e.adminTenants(),readOnly:!0},{name:"appoiint_admin_tenant_view",description:"Full tenant detail: plan, stats, usage, created date.",inputSchema:{type:"object",properties:{slug:{type:"string",description:"Tenant slug"}},required:["slug"]},handler:async(e,t)=>e.adminTenantView(t.slug),readOnly:!0},{name:"appoiint_platform_stats",description:"Platform overview: tenants, users, rooms, bookings, meetings.",inputSchema:{type:"object",properties:{}},handler:async e=>e.platformStats(),readOnly:!0},{name:"appoiint_funnel",description:"Signup funnel with conversion rates.",inputSchema:{type:"object",properties:{}},handler:async e=>e.funnel(),readOnly:!0},{name:"appoiint_revenue",description:"Revenue breakdown: MRR, ARR, fees, payouts.",inputSchema:{type:"object",properties:{}},handler:async e=>e.revenue(),readOnly:!0},{name:"appoiint_audit",description:"Activity audit trail.",inputSchema:{type:"object",properties:{limit:{type:"number",description:"Max entries"},action:{type:"string",description:"Filter by action type"}}},handler:async(e,t)=>{const i=new URLSearchParams;return t.limit&&i.set("limit",t.limit),t.action&&i.set("action",t.action),e.audit(i.toString())},readOnly:!0}];module.exports={TOOLS:TOOLS};
|
|
1
|
+
const TOOLS=[{name:"appoiint_status",description:"Get Appoiint platform status and version.",inputSchema:{type:"object",properties:{}},handler:async e=>e.status(),readOnly:!0},{name:"appoiint_create_room",description:"Create a video meeting room. Returns roomId and join URL.",inputSchema:{type:"object",properties:{title:{type:"string",description:'Meeting title (e.g. "Team Standup")'}},required:["title"]},handler:async(e,t)=>{const i=require("./config").loadConfig([]);return e.createRoom({title:t.title,hostId:i.userId})},readOnly:!1},{name:"appoiint_list_rooms",description:"List active meeting rooms.",inputSchema:{type:"object",properties:{}},handler:async e=>e.listRooms(),readOnly:!0},{name:"appoiint_room_status",description:"Get room status and participants.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t)=>e.roomStatus(t.roomId),readOnly:!0},{name:"appoiint_end_room",description:"End a meeting room.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t)=>e.endRoom(t.roomId),readOnly:!1},{name:"appoiint_invite",description:"Invite a human or agent to a meeting room.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"},email:{type:"string",description:"Invitee email"},name:{type:"string",description:"Invitee name"},type:{type:"string",description:"human or agent (default: human)"}},required:["roomId","email"]},handler:async(e,t)=>e.invite(t.roomId,{email:t.email,name:t.name||"Guest",type:t.type||"human"}),readOnly:!1},{name:"appoiint_transcript",description:"Get meeting transcript.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"},format:{type:"string",description:"json, text, or srt (default: json)"}},required:["roomId"]},handler:async(e,t)=>e.transcript(t.roomId,t.format||"json"),readOnly:!0},{name:"appoiint_join_meeting",description:"Join a meeting room as an AI agent. Creates persistent Socket.IO connection. You will hear transcripts and can send chat messages. Call appoiint_get_transcripts to read what was said, appoiint_send_chat to respond.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID to join"},agentName:{type:"string",description:"Your display name in the meeting (default: Claude Code)"},agentRole:{type:"string",description:"Your role badge (default: assistant)"}},required:["roomId"]},handler:async(e,t,i)=>{const{MeetingSession:n}=require("./meetingSession"),a=new n({baseUrl:i.config.baseUrl,roomId:t.roomId,agentName:t.agentName||"Claude Code",agentRole:t.agentRole||"assistant"}),r=await a.join();return i.meetingSessions.set(t.roomId,a),r},readOnly:!1},{name:"appoiint_get_transcripts",description:"Get speech transcripts from the meeting. Returns what participants have said since last read. Buffer is cleared after reading.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);if(!n)return{error:"Not in this meeting. Call appoiint_join_meeting first."};const a=n.getTranscripts();return{roomId:t.roomId,count:a.length,transcripts:a}},readOnly:!0},{name:"appoiint_send_chat",description:"Send a chat message to all meeting participants. Your message appears in the AI chat panel with your role badge.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"},message:{type:"string",description:"Message to send to meeting participants"}},required:["roomId","message"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);return n?n.sendChat(t.message):{error:"Not in this meeting. Call appoiint_join_meeting first."}},readOnly:!1},{name:"appoiint_meeting_status",description:"Check meeting connection status: connected, peer count, buffered transcript count.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);return n?n.status():{error:"Not in this meeting.",connected:!1}},readOnly:!0},{name:"appoiint_leave_meeting",description:"Leave a meeting room and disconnect.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID"}},required:["roomId"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);if(!n)return{error:"Not in this meeting."};const a=n.leave();return i.meetingSessions.delete(t.roomId),a},readOnly:!1},{name:"appoiint_speak",description:"Speak in a meeting room with an agent voice: synthesizes text via TTS and broadcasts the audio to all participants in one call. Requires an active appoiint_join_meeting session. Text is trimmed to 500 chars server-side; audio over 500KB is dropped by the relay.",inputSchema:{type:"object",properties:{roomId:{type:"string",description:"Room ID (must have joined via appoiint_join_meeting)"},text:{type:"string",description:"Text to speak (max 500 chars)"},voice:{type:"string",description:"Voice: cfo, cto, cmo, cco, cso, clo, heart, michael, nova, echo (default: heart)"}},required:["roomId","text"]},handler:async(e,t,i)=>{const n=i.meetingSessions.get(t.roomId);if(!n)return{error:"Not in this meeting. Call appoiint_join_meeting first."};const a=await e.synthesize({text:t.text,voice:t.voice||"heart",agentRole:t.voice});if(!a||!a.audio)return{error:"TTS synthesis failed",detail:a};const r=n.sendAudio({audio:a.audio,format:a.format});return{spoken:!0,roomId:t.roomId,voice:a.voice,provider:a.provider,bytes:r.bytes}},readOnly:!1},{name:"appoiint_list_events",description:"List bookable event types.",inputSchema:{type:"object",properties:{}},handler:async e=>e.events(),readOnly:!0},{name:"appoiint_check_availability",description:"Check available time slots for a host on a given date.",inputSchema:{type:"object",properties:{email:{type:"string",description:"Host email address"},date:{type:"string",description:"Date (YYYY-MM-DD)"},timezone:{type:"string",description:"Timezone (default: UTC)"}},required:["email","date"]},handler:async(e,t)=>e.slots(t.email,t.date,t.timezone||"UTC"),readOnly:!0},{name:"appoiint_create_booking",description:"Create a booking on behalf of a customer.",inputSchema:{type:"object",properties:{eventTypeId:{type:"string",description:"Event type ID"},scheduledAt:{type:"string",description:"ISO 8601 date-time (e.g. 2026-06-15T14:00:00Z)"},duration:{type:"number",description:"Duration in minutes (default: 30)"},customerName:{type:"string",description:"Customer name"},customerEmail:{type:"string",description:"Customer email"}},required:["scheduledAt","customerEmail"]},handler:async(e,t)=>e.book({eventTypeId:t.eventTypeId,scheduledAt:t.scheduledAt,duration:t.duration||30,customer:{name:t.customerName||"Guest",email:t.customerEmail},location:{type:"video"}}),readOnly:!1},{name:"appoiint_tts",description:"Synthesize text-to-speech with CxO agent voice.",inputSchema:{type:"object",properties:{text:{type:"string",description:"Text to synthesize"},voice:{type:"string",description:"Voice: cfo, cto, cmo, cco, cso, clo, heart, michael, nova, echo (default: heart)"}},required:["text"]},handler:async(e,t)=>e.synthesize({text:t.text,voice:t.voice||"heart",agentRole:t.voice}),readOnly:!0},{name:"appoiint_voices",description:"List available TTS voices and CxO agent voice mappings.",inputSchema:{type:"object",properties:{}},handler:async e=>e.voices(),readOnly:!0},{name:"appoiint_helpdesk_tickets",description:"List helpdesk tickets. Filter by status.",inputSchema:{type:"object",properties:{status:{type:"string",description:"Filter: open, resolved, closed"}}},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("GET","/saas/api/v1/sdk/helpdeskryClient/tickets"+(t.status?`?status=${t.status}`:""))},readOnly:!0},{name:"appoiint_helpdesk_ticket_show",description:"Get ticket detail with messages.",inputSchema:{type:"object",properties:{ticketId:{type:"string"}},required:["ticketId"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("GET",`/saas/api/v1/sdk/helpdeskryClient/tickets/${t.ticketId}`)},readOnly:!0},{name:"appoiint_helpdesk_ticket_create",description:"Create a helpdesk ticket.",inputSchema:{type:"object",properties:{subject:{type:"string"},priority:{type:"string"},category:{type:"string"}},required:["subject"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("POST","/saas/api/v1/sdk/helpdeskryClient/tickets",t)},readOnly:!1},{name:"appoiint_helpdesk_ticket_reply",description:"Reply to a helpdesk ticket.",inputSchema:{type:"object",properties:{ticketId:{type:"string"},message:{type:"string"}},required:["ticketId","message"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("POST",`/saas/api/v1/sdk/helpdeskryClient/tickets/${t.ticketId}/messages`,{content:t.message})},readOnly:!1},{name:"appoiint_crm_leads",description:"List CRM leads. Filter by status.",inputSchema:{type:"object",properties:{status:{type:"string"}}},handler:async(e,t,i)=>{const{rawApi:n}=require("./api"),a=t.status?`?status=${t.status}`:"";return n(i.config)("GET",`/saas/api/v1/sdk/crm/leads${a}`)},readOnly:!0},{name:"appoiint_crm_lead_show",description:"Get lead detail.",inputSchema:{type:"object",properties:{leadId:{type:"string"}},required:["leadId"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("GET",`/saas/api/v1/sdk/crm/leads/${t.leadId}`)},readOnly:!0},{name:"appoiint_crm_lead_create",description:"Create a CRM lead.",inputSchema:{type:"object",properties:{name:{type:"string"},email:{type:"string"},source:{type:"string"}},required:["name"]},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("POST","/saas/api/v1/sdk/crm/leads",t)},readOnly:!1},{name:"appoiint_crm_opportunities",description:"List CRM opportunities. Filter by stage.",inputSchema:{type:"object",properties:{stage:{type:"string"}}},handler:async(e,t,i)=>{const{rawApi:n}=require("./api"),a=t.stage?`?stage=${t.stage}`:"";return n(i.config)("GET",`/saas/api/v1/sdk/crm/opportunities${a}`)},readOnly:!0},{name:"appoiint_crm_pipelines",description:"List CRM pipelines with stages.",inputSchema:{type:"object",properties:{}},handler:async(e,t,i)=>{const{rawApi:n}=require("./api");return n(i.config)("GET","/saas/api/v1/sdk/crm/pipelines")},readOnly:!0},{name:"appoiint_channel_send",description:"Send a 1:1 message to another agent. Use for ops requests, data queries, or notifications.",inputSchema:{type:"object",properties:{to:{type:"string",description:"Recipient agent ID"},message:{type:"string",description:"Message text"},action:{type:"string",description:"Intent/action (e.g. deploy, query, notify)"},payload:{type:"object",description:"Structured data payload"}},required:["to"]},handler:async(e,t)=>{const i={to:t.to,type:t.action?"request":"notify"};return t.message&&(i.payload={message:t.message}),t.action&&(i.action=t.action,i.requestId=`req_${Date.now()}`),t.payload&&(i.payload={...i.payload,...t.payload}),e.channelSend(i)},readOnly:!1},{name:"appoiint_channel_inbox",description:"Get unread messages from other agents.",inputSchema:{type:"object",properties:{}},handler:async e=>e.channelInbox(),readOnly:!0},{name:"appoiint_channel_history",description:"Get message history with a specific agent.",inputSchema:{type:"object",properties:{agentId:{type:"string",description:"Agent ID to get history with"},limit:{type:"number",description:"Max messages (default 50)"}},required:["agentId"]},handler:async(e,t)=>e.channelHistory(t.agentId,t.limit),readOnly:!0},{name:"appoiint_channel_agents",description:"List agents you have communicated with.",inputSchema:{type:"object",properties:{}},handler:async e=>e.channelAgents(),readOnly:!0},{name:"appoiint_wall",description:"All your message threads (DMs + channels) with unread badges, newest first. Start here to see what needs attention.",inputSchema:{type:"object",properties:{}},handler:async e=>e.wall(),readOnly:!0},{name:"appoiint_notifications",description:"Unread digest across all DM and channel threads. Reading a thread history marks it read.",inputSchema:{type:"object",properties:{}},handler:async e=>e.notificationsDigest(),readOnly:!0},{name:"appoiint_channel_create",description:"Create a named channel for multi-agent group messaging. You auto-join as creator.",inputSchema:{type:"object",properties:{name:{type:"string",description:'Channel name (slugified, e.g. "dev-ops")'},topic:{type:"string",description:"Optional channel topic"}},required:["name"]},handler:async(e,t)=>e.groupCreate(t.name,t.topic),readOnly:!1},{name:"appoiint_channel_join",description:"Join an existing named channel.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Channel name/slug"}},required:["name"]},handler:async(e,t)=>e.groupJoin(t.name),readOnly:!1},{name:"appoiint_channel_leave",description:"Leave a named channel.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Channel name/slug"}},required:["name"]},handler:async(e,t)=>e.groupLeave(t.name),readOnly:!1},{name:"appoiint_channel_list",description:"List named channels with membership and your unread counts.",inputSchema:{type:"object",properties:{}},handler:async e=>e.groupList(),readOnly:!0},{name:"appoiint_channel_post",description:"Post a message to a named channel (must be a member).",inputSchema:{type:"object",properties:{name:{type:"string",description:"Channel name/slug"},message:{type:"string",description:"Message text"},payload:{type:"object",description:"Optional structured payload"}},required:["name","message"]},handler:async(e,t)=>e.groupSend(t.name,{text:t.message,payload:t.payload||{message:t.message}}),readOnly:!1},{name:"appoiint_channel_messages",description:"Read a named channel history (reading marks it read).",inputSchema:{type:"object",properties:{name:{type:"string",description:"Channel name/slug"},limit:{type:"number",description:"Max messages (default 50)"}},required:["name"]},handler:async(e,t)=>e.groupHistory(t.name,t.limit),readOnly:!0},{name:"appoiint_admin_tenants",description:"List all tenants with plan, status, usage. Superadmin only.",inputSchema:{type:"object",properties:{}},handler:async e=>e.adminTenants(),readOnly:!0},{name:"appoiint_admin_tenant_view",description:"Full tenant detail: plan, stats, usage, created date.",inputSchema:{type:"object",properties:{slug:{type:"string",description:"Tenant slug"}},required:["slug"]},handler:async(e,t)=>e.adminTenantView(t.slug),readOnly:!0},{name:"appoiint_platform_stats",description:"Platform overview: tenants, users, rooms, bookings, meetings.",inputSchema:{type:"object",properties:{}},handler:async e=>e.platformStats(),readOnly:!0},{name:"appoiint_funnel",description:"Signup funnel with conversion rates.",inputSchema:{type:"object",properties:{}},handler:async e=>e.funnel(),readOnly:!0},{name:"appoiint_revenue",description:"Revenue breakdown: MRR, ARR, fees, payouts.",inputSchema:{type:"object",properties:{}},handler:async e=>e.revenue(),readOnly:!0},{name:"appoiint_audit",description:"Activity audit trail.",inputSchema:{type:"object",properties:{limit:{type:"number",description:"Max entries"},action:{type:"string",description:"Filter by action type"}}},handler:async(e,t)=>{const i=new URLSearchParams;return t.limit&&i.set("limit",t.limit),t.action&&i.set("action",t.action),e.audit(i.toString())},readOnly:!0}];module.exports={TOOLS:TOOLS};
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appoiint",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
4
4
|
"description": "Appoiint CLI — schedule meetings, create rooms, join video calls. Agent-first meeting platform.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"apo": "bin/cli.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "node scripts/build.js",
|
|
10
|
-
"prepublishOnly": "npm run build"
|
|
10
|
+
"prepublishOnly": "npm run build",
|
|
11
|
+
"test": "node tests/turnProbe.test.js"
|
|
11
12
|
},
|
|
12
13
|
"keywords": [
|
|
13
14
|
"appoiint",
|