문서
TeamOnly 문서
CLI, 앱이 쓰는 SDK, AI에게 무엇을 쓸지 알려 주는 스킬 파일. 기술적이고 짧아요. 코딩 에이전트는 이 페이지를 /docs.md에서 Markdown으로 읽을 수 있어요.
빠른 시작
Node.js 20 이상이 필요해요. 아직 요금제가 없나요? 처음 로그인할 때 카드 없이 14일 무료 체험을 시작하거나 구독할 수 있어요. 여기서 구독할 수도 있어요. 새 폴더나 이미 있는 프로젝트에서:
npx teamonly init는 시작용 앱과 AI 툴이 따를 안내를 추가해요.npx teamonly login은 컴퓨터마다 한 번. 회사 이메일과 메일로 받은 6자리 코드를 입력하세요. 회사에 아직 요금제가 없으면 바로 시작되는 14일 무료 체험이나, 결제 페이지로 가는 요금제를 제안해요. 이메일 대신 Enter를 누르면 브라우저로 로그인해요.npx teamonly deploy는 앱을https://<app>.<company>.teamonly.app에서 운영되게 해요.
이렇게 보여요:
npx teamonly initAdded the TeamOnly skill, Cursor rules and SDK경비 승인 툴 만들어 줘(AI가 앱을 만들어요)npx teamonly loginSigned in as dana@acme.comnpx teamonly deployLive at https://expenses.acme.teamonly.appOnly @acme.com accounts can sign in첫 배포 때 앱, 데이터베이스, 파일 저장소가 만들어져요. 그 뒤의 배포는 같은 주소의 같은 앱을 업데이트해요.
CLI 명령어
npx teamonly login- 이 컴퓨터에서 TeamOnly에 로그인해요. 컴퓨터마다 한 번. 회사 이메일과 메일로 받은 6자리 코드를 입력하거나, Enter를 눌러 브라우저로 로그인하세요.
--email과--code는 질문 없이 같은 일을 해요. AI 도구와 스크립트용이에요. npx teamonly trial --company "Acme"- 카드 없이 회사의 14일 무료 체험을 시작해요. 회사에 아직 요금제가 없을 때
login다음에 쓰세요. 무료 체험은 회사 이메일 도메인당 한 번이에요. npx teamonly subscribe --plan builder- 구독하거나, 무료 체험이 끝난 뒤 앱을 다시 켜요.
builder,team,business중에서 고르세요. 결제 페이지가 열리고, 결제가 끝나면 여기서 로그인돼요. npx teamonly init- 이 폴더를 설정해요: 시작용 앱을 만들거나, 기존 프로젝트에 AI 툴용 TeamOnly 안내(Claude Code 스킬, Cursor 규칙, AGENTS.md)와 SDK를 넣어요.
npx teamonly deploy- 앱을 빌드해서 온라인에 올려요. 첫 배포 때 데이터베이스와 파일 저장소를 만들어요. 앱 주소를 보여 줘요.
npx teamonly access- 앱을 열 수 있는 사람을 보여 줘요. 목록을 쓰려면 이메일 주소와 함께
only,add,remove를, 회사 전체에 열려면everyone을 붙이세요. npx teamonly roles- 앱 안에서 승인자나 재무 같은 역할을 사람들에게 줘요.
roles add approver dana@acme.com으로 주고,roles remove로 빼고,roles만 쓰면 누가 어떤 역할인지 보여 줘요. 회사 주소만 돼요. npx teamonly secrets set NAME- 이 앱의 시크릿을 저장해요. 값은 입력하라는 안내가 나올 때 넣기 때문에 셸 기록에 남지 않아요.
npx teamonly logs- 운영 중인 앱의 요청,
console.log출력, 오류를 실시간으로 봐요. 멈추려면 Ctrl+C를 누르세요. npx teamonly whoami- 누구로 로그인했는지, 요금제, 내 앱을 보여 줘요.
npx teamonly delete- 운영 중인 앱을 데이터베이스, 파일과 함께 삭제해요. 먼저 앱 이름을 입력하라고 해요.
npx teamonly logout- 이 컴퓨터에서 TeamOnly 로그아웃.
SDK
API는 Hono 앱이에요. SDK의 미들웨어가 요청마다 c.var.ctx에 컨텍스트를 줘요. 내용은 이래요:
ctx.user- 로그인한 사람:
email,name, 앱을 처음 배포한 사람에게는isOwner, 그리고 이 앱에서 그 사람의 역할인roles. 코드가 실행되기 전에 TeamOnly가 검증해요. 신원을 헤더에서 직접 읽지 마세요. ctx.db- 이 앱만의 SQL 데이터베이스(SQLite, Cloudflare D1 기반). 첫 배포 때 만들어져요.
ctx.files- 이 앱의 파일 저장소:
put,get,list,delete. ctx.secretsnpx teamonly secrets set으로 설정한 값.ctx.secrets.get(name)으로 읽어요.ctx.email- 동료에게 이메일을 보내요:
send({ to, subject, text, html?, replyTo? }). 이메일 참고. ctx.schedule- TeamOnly가 예약 작업을 실행할 때는 작업 이름, 그 외에는
undefined. 예약 작업 참고.
브라우저에서는 teamonly/react의 useUser()가 { email, name, roles }를 돌려줘요.
import { Hono } from 'hono'
import { teamonly, type TeamOnly } from 'teamonly/server'
const app = new Hono<TeamOnly>()
app.use('*', teamonly())
// Who is signed in. Verified before your code runs.
app.get('/api/me', (c) => {
return c.json(c.var.ctx.user) // { email: 'dana@acme.com', name: 'Dana Okafor', isOwner: false, roles: ['approver'] }
})
// Save to this app's own database.
app.post('/api/expenses', async (c) => {
const { db, user } = c.var.ctx
const { amount, reason } = await c.req.json()
await db
.prepare('INSERT INTO expenses (amount, reason, submitted_by) VALUES (?, ?, ?)')
.bind(amount, reason, user.email)
.run()
return c.json({ ok: true })
})
// Store an uploaded receipt in this app's file storage.
app.put('/api/receipts/:id', async (c) => {
await c.var.ctx.files.put(`receipts/${c.req.param('id')}`, c.req.raw.body)
return c.json({ ok: true })
})
// Read a secret set with: npx teamonly secrets set QUICKBOOKS_KEY
app.get('/api/sync', async (c) => {
const key = c.var.ctx.secrets.get('QUICKBOOKS_KEY')
// ...
})
export default app이메일
앱은 ctx.email.send()로 회사 사람들에게 이메일을 보낼 수 있어요. 예를 들어 승인자에게 경비가 기다리고 있다고 알릴 때요.
// Tell the approver an expense is waiting.
await c.var.ctx.email.send({
to: 'priya@acme.com', // one address or a list, up to 50
subject: 'An expense is waiting for you',
text: 'Dana sent a $42 expense. Review it: https://expenses.acme.teamonly.app',
})- 회사 자체 이메일 도메인의 주소로만 보내요. 로그인할 수 있는 사람과 같아요. 다른 주소는 거부되고 아무것도 발송되지 않아요.
notify@teamonly.app에서 회사의 그 앱 이름으로 보내요(예: “Expenses at Acme”). 답장은 앱 담당자에게 가요.replyTo를 설정하면 그 주소로 가요(이것도 회사 주소).- 모든 이메일에 어느 앱이 보냈는지 알려 주는 한 줄이 들어가요.
- 앱별 한도: 시간당 수신자 100명, 하루 500명, 이메일 한 통에 수신자 최대 50명.
- 대시보드의 앱 페이지에서 지난 30일 동안 보낸 이메일 수를 볼 수 있어요.
- 로컬(
npm run dev)에서는 이메일을 보내지 않고 터미널에 출력해요. - 회사 밖 사람에게 보내는 이메일은 지원하지 않아요.
예약 작업
매일 요약, 알림, 월말 보고서가 필요하면 teamonly.json에 작업을 적으세요. TeamOnly가 그 시간에 각 경로를 호출해요.
{
"name": "expenses",
"timezone": "America/New_York",
"schedules": [
{ "name": "daily-digest", "cron": "0 9 * * 1-5", "path": "/api/cron/daily-digest" }
]
}import { scheduled } from 'teamonly/server'
// Runs at 9am New York time, Monday to Friday.
app.post('/api/cron/daily-digest', scheduled(async (c) => {
const { db, email } = c.var.ctx
// ...find what is waiting, then email each approver
return c.json({ ok: true })
}))cron은 다섯 칸이에요: 분, 시, 일, 월, 요일. 작업은 5분 단위로 실행되니 분은 5의 배수여야 해요.timezone은 선택이고, 기본값은 UTC예요.- 경로는
teamonly/server의scheduled(...)로 감싸세요. URL을 여는 사람은 404를 받아요. - 대시보드의 앱 페이지에서 작업마다 마지막 실행과 성공 여부를 볼 수 있어요.
- 무료 체험이 끝나면 작업이 일시 중지돼요.
역할
승인자, 재무, 관리자 같은 역할은 코드가 아니라 TeamOnly에 저장돼요. 앱 담당자나 관리자가 대시보드의 앱 페이지에서, 또는 npx teamonly roles로 역할을 줘요. 회사 주소만 돼요.
npx teamonly roles add approver dana@acme.comGave dana@acme.com the role approverapprover: dana@acme.com// Only approvers can approve.
app.post('/api/expenses/:id/approve', async (c) => {
const { user } = c.var.ctx
if (!user.roles.includes('approver')) return c.json({ error: 'Approvers only' }, 403)
// ...
})앱은 ctx.user.roles로 읽고, 역할마다 무엇을 할 수 있는지 정해요. 로그인 코드는 여전히 짤 필요가 없어요.
스킬 파일
init은 Claude Code용 스킬 파일과, 같은 내용의 Cursor용 규칙 파일을 추가해요. AI 툴은 코드를 쓰기 전에 이 파일을 읽어요. 알려 주는 내용은 이래요:
- 지원하는 단 하나의 앱 형태(아래)와, 그 안에서 벗어나지 않기.
- 신원은
ctx.user로 확인하고, 로그인, 회원가입, 비밀번호 화면을 직접 만들지 않기. - 데이터베이스 서비스, Express, Supabase, Firebase를 추가하지 않고
ctx.db와ctx.files쓰기. - API 키는 코드가 아니라
ctx.secrets에 두기. - 이메일은
ctx.email로 보내고, 정해진 시간에 할 일은 예약 작업으로 돌리고,ctx.user.roles는 서버에서 확인하기. deploy를 실행하고 알려 주는 문제를 고치는 방법.
목표는 첫 배포가 한 번에 되는 거예요. AI가 계속 저희가 지원하지 않는 걸 쓰려고 하면 알려 주세요. 스킬 파일의 버그예요.
MCP 서버
teamonly 패키지는 MCP 서버도 실행해요. Claude Desktop이나 터미널 없는 Cursor 에이전트처럼 터미널 명령어보다 도구 호출을 선호하는 AI 툴을 위한 거예요. 배포할 때 프로젝트 파일을 읽어야 해서 내 컴퓨터에서 실행돼요. Claude Code에는 이렇게 추가해요:
claude mcp add teamonly -- npx -y teamonly mcp- Cursor, Claude Desktop, Windsurf: MCP 설정에
{ "mcpServers": { "teamonly": { "command": "npx", "args": ["-y", "teamonly", "mcp"] } } }를 추가하세요. - VS Code:
.vscode/mcp.json에서는 키가servers이고"type": "stdio"를 써요. - 도구 12개:
get_app_rules,init,deploy,status,logs,access,roles,set_secret,sign_in,start_trial,subscribe,delete_app. - 공식 MCP Registry에
dev.teamonly/teamonly로 올라가 있어요.
스킬 파일과 규칙이 여전히 AI가 스택을 배우는 주된 방법이고, CLI는 전과 똑같이 동작해요. 툴별 설정과 Cursor 원클릭 설치 버튼: TeamOnly MCP 서버.
지원하는 앱 형태
- 화면: Vite로 빌드한 React, 정적 파일로 제공.
- API: TypeScript나 JavaScript로 만든 Hono 앱, 같은 주소의
/api아래. - 런타임: Cloudflare Workers. 표준 웹 API, 대부분의 Node 내장 모듈은 호환 기능으로 지원하지만 네이티브 모듈이나 오래 실행되는 프로세스는 안 돼요.
- 지원하지 않음: Python, Ruby, PHP, Docker 컨테이너, WebSocket과 실시간 업데이트, 백그라운드 워커, 회사 밖으로 보내는 이메일, 회사 내부 네트워크의 서버 연결, 커스텀 도메인, 공개 앱.
제한
현재 제한:
- Builder: 제작자 1명, 앱 3개. Team: 제작자와 앱 무제한.
- 각 앱의 데이터베이스와 파일 저장소는 Cloudflare D1과 R2의 제한을 따라요.
- 요청은 Cloudflare Workers의 CPU 시간과 메모리 제한 안에서 실행돼요.
얼리 액세스
이번 주에 첫 툴을 올려 보세요.
TeamOnly는 얼리 액세스 중이고, 지금 모든 요금제에 가입할 수 있어요. 카드 없이 14일 동안 무료로 써 보거나, 구독하세요. 어느 쪽이든 회사 설정이 바로 끝나고, 명령어 세 개면 첫 앱이 온라인에 올라가요.
아직 준비가 안 됐나요? 소식 받기
질문은 hello@teamonly.dev로 보내 주세요. 사람이 직접 답해요.