chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
import type { GrainCreateHookParams, GrainCreateHookResponse } from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainCreateHookTool: ToolConfig<GrainCreateHookParams, GrainCreateHookResponse> = {
id: 'grain_create_hook',
name: 'Grain Create Webhook',
description: 'Create a webhook to receive recording events',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
hookUrl: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Webhook endpoint URL (e.g., "https://example.com/webhooks/grain")',
},
viewId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Grain view ID from GET /_/public-api/views',
},
actions: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Optional list of actions to subscribe to: added, updated, removed',
items: {
type: 'string',
},
},
},
request: {
url: 'https://api.grain.com/_/public-api/hooks',
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
body: (params) => {
const body: Record<string, any> = {
version: 2,
hook_url: params.hookUrl,
view_id: params.viewId,
}
if (params.actions && params.actions.length > 0) {
body.actions = params.actions
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.message || 'Failed to create webhook')
}
if (!data?.id) {
throw new Error('Grain webhook created but response did not include a webhook id')
}
return {
success: true,
output: data,
}
},
outputs: {
id: {
type: 'string',
description: 'Hook UUID',
},
enabled: {
type: 'boolean',
description: 'Whether hook is active',
},
hook_url: {
type: 'string',
description: 'The webhook URL',
},
view_id: {
type: 'string',
description: 'Grain view ID for the webhook',
},
actions: {
type: 'array',
description: 'Configured actions for the webhook',
},
inserted_at: {
type: 'string',
description: 'ISO8601 creation timestamp',
},
},
}
+54
View File
@@ -0,0 +1,54 @@
import type { GrainDeleteHookParams, GrainDeleteHookResponse } from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainDeleteHookTool: ToolConfig<GrainDeleteHookParams, GrainDeleteHookResponse> = {
id: 'grain_delete_hook',
name: 'Grain Delete Webhook',
description: 'Delete a webhook by ID',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
hookId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The hook UUID to delete (e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890")',
},
},
request: {
url: (params) => `https://api.grain.com/_/public-api/hooks/${params.hookId}`,
method: 'DELETE',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const data = await response.json()
throw new Error(data.error || data.message || 'Failed to delete webhook')
}
return {
success: true,
output: {
success: true,
},
}
},
outputs: {
success: {
type: 'boolean',
description: 'True when webhook was successfully deleted',
},
},
}
+180
View File
@@ -0,0 +1,180 @@
import type { GrainGetRecordingParams, GrainGetRecordingResponse } from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainGetRecordingTool: ToolConfig<GrainGetRecordingParams, GrainGetRecordingResponse> =
{
id: 'grain_get_recording',
name: 'Grain Get Recording',
description: 'Get details of a single recording by ID',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
recordingId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The recording UUID (e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890")',
},
includeHighlights: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include highlights/clips',
},
includeParticipants: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include participant list',
},
includeAiSummary: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include AI summary',
},
includeCalendarEvent: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include calendar event data',
},
includeHubspot: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include HubSpot associations',
},
},
request: {
url: (params) => `https://api.grain.com/_/public-api/v2/recordings/${params.recordingId}`,
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
'Public-Api-Version': '2025-10-31',
}),
body: (params) => {
const include: Record<string, any> = {}
if (params.includeHighlights) {
include.highlights = true
}
if (params.includeParticipants) {
include.participants = true
}
if (params.includeAiSummary) {
include.ai_summary = true
}
if (params.includeCalendarEvent) {
include.calendar_event = true
}
if (params.includeHubspot) {
include.hubspot = true
}
if (Object.keys(include).length > 0) {
return { include }
}
return {}
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.message || 'Failed to get recording')
}
return {
success: true,
output: data,
}
},
outputs: {
id: {
type: 'string',
description: 'Recording UUID',
},
title: {
type: 'string',
description: 'Recording title',
},
start_datetime: {
type: 'string',
description: 'ISO8601 start timestamp',
},
end_datetime: {
type: 'string',
description: 'ISO8601 end timestamp',
},
duration_ms: {
type: 'number',
description: 'Duration in milliseconds',
},
media_type: {
type: 'string',
description: 'audio, transcript, or video',
},
source: {
type: 'string',
description: 'Recording source (zoom, meet, teams, etc.)',
},
url: {
type: 'string',
description: 'URL to view in Grain',
},
thumbnail_url: {
type: 'string',
description: 'Thumbnail image URL',
optional: true,
},
tags: {
type: 'array',
description: 'Array of tag strings',
},
teams: {
type: 'array',
description: 'Teams the recording belongs to',
},
meeting_type: {
type: 'object',
description: 'Meeting type info (id, name, scope)',
optional: true,
},
highlights: {
type: 'array',
description: 'Highlights (if included)',
optional: true,
},
participants: {
type: 'array',
description: 'Participants (if included)',
optional: true,
},
ai_summary: {
type: 'object',
description: 'AI summary text (if included)',
optional: true,
},
calendar_event: {
type: 'object',
description: 'Calendar event data (if included)',
optional: true,
},
hubspot: {
type: 'object',
description: 'HubSpot associations (if included)',
optional: true,
},
},
}
+71
View File
@@ -0,0 +1,71 @@
import type { GrainGetTranscriptParams, GrainGetTranscriptResponse } from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainGetTranscriptTool: ToolConfig<
GrainGetTranscriptParams,
GrainGetTranscriptResponse
> = {
id: 'grain_get_transcript',
name: 'Grain Get Transcript',
description: 'Get the full transcript of a recording',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
recordingId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'The recording UUID (e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890")',
},
},
request: {
url: (params) =>
`https://api.grain.com/_/public-api/v2/recordings/${params.recordingId}/transcript`,
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
'Public-Api-Version': '2025-10-31',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.message || 'Failed to get transcript')
}
// API returns array directly
return {
success: true,
output: {
transcript: Array.isArray(data) ? data : [],
},
}
},
outputs: {
transcript: {
type: 'array',
description: 'Array of transcript sections',
items: {
type: 'object',
properties: {
participant_id: { type: 'string', description: 'Participant UUID (nullable)' },
speaker: { type: 'string', description: 'Speaker name' },
start: { type: 'number', description: 'Start timestamp in ms' },
end: { type: 'number', description: 'End timestamp in ms' },
text: { type: 'string', description: 'Transcript text' },
},
},
},
},
}
+9
View File
@@ -0,0 +1,9 @@
export { grainCreateHookTool } from './create_hook'
export { grainDeleteHookTool } from './delete_hook'
export { grainGetRecordingTool } from './get_recording'
export { grainGetTranscriptTool } from './get_transcript'
export { grainListHooksTool } from './list_hooks'
export { grainListMeetingTypesTool } from './list_meeting_types'
export { grainListRecordingsTool } from './list_recordings'
export { grainListTeamsTool } from './list_teams'
export { grainListViewsTool } from './list_views'
+60
View File
@@ -0,0 +1,60 @@
import type { GrainListHooksParams, GrainListHooksResponse } from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainListHooksTool: ToolConfig<GrainListHooksParams, GrainListHooksResponse> = {
id: 'grain_list_hooks',
name: 'Grain List Webhooks',
description: 'List all webhooks for the account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
},
request: {
url: 'https://api.grain.com/_/public-api/hooks',
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.message || 'Failed to list webhooks')
}
return {
success: true,
output: {
hooks: data.hooks || data || [],
},
}
},
outputs: {
hooks: {
type: 'array',
description: 'Array of hook objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Hook UUID' },
enabled: { type: 'boolean', description: 'Whether hook is active' },
hook_url: { type: 'string', description: 'Webhook URL' },
view_id: { type: 'string', description: 'Grain view ID' },
actions: { type: 'array', description: 'Configured actions' },
inserted_at: { type: 'string', description: 'Creation timestamp' },
},
},
},
},
}
@@ -0,0 +1,64 @@
import type {
GrainListMeetingTypesParams,
GrainListMeetingTypesResponse,
} from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainListMeetingTypesTool: ToolConfig<
GrainListMeetingTypesParams,
GrainListMeetingTypesResponse
> = {
id: 'grain_list_meeting_types',
name: 'Grain List Meeting Types',
description: 'List all meeting types in the workspace',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
},
request: {
url: 'https://api.grain.com/_/public-api/v2/meeting_types',
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
'Public-Api-Version': '2025-10-31',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.message || 'Failed to list meeting types')
}
return {
success: true,
output: {
meeting_types: data.meeting_types || data || [],
},
}
},
outputs: {
meeting_types: {
type: 'array',
description: 'Array of meeting type objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Meeting type UUID' },
name: { type: 'string', description: 'Meeting type name' },
scope: { type: 'string', description: 'internal or external' },
},
},
},
},
}
+182
View File
@@ -0,0 +1,182 @@
import type { GrainListRecordingsParams, GrainListRecordingsResponse } from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainListRecordingsTool: ToolConfig<
GrainListRecordingsParams,
GrainListRecordingsResponse
> = {
id: 'grain_list_recordings',
name: 'Grain List Recordings',
description: 'List recordings from Grain with optional filters and pagination',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
cursor: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Pagination cursor for next page (returned from previous response)',
},
beforeDatetime: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only recordings before this ISO8601 timestamp (e.g., "2024-01-15T00:00:00Z")',
},
afterDatetime: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only recordings after this ISO8601 timestamp (e.g., "2024-01-01T00:00:00Z")',
},
participantScope: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter: "internal" or "external"',
},
titleSearch: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search term to filter by recording title (e.g., "weekly standup")',
},
teamId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by team UUID (e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890")',
},
meetingTypeId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by meeting type UUID (e.g., "a1b2c3d4-e5f6-7890-abcd-ef1234567890")',
},
includeHighlights: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include highlights/clips in response',
},
includeParticipants: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include participant list in response',
},
includeAiSummary: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Include AI-generated summary',
},
},
request: {
url: 'https://api.grain.com/_/public-api/v2/recordings',
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
'Public-Api-Version': '2025-10-31',
}),
body: (params) => {
const body: Record<string, any> = {}
if (params.cursor) {
body.cursor = params.cursor
}
const filter: Record<string, any> = {}
if (params.beforeDatetime) {
filter.before_datetime = params.beforeDatetime
}
if (params.afterDatetime) {
filter.after_datetime = params.afterDatetime
}
if (params.participantScope) {
filter.participant_scope = params.participantScope
}
if (params.titleSearch) {
filter.title_search = params.titleSearch
}
if (params.teamId) {
filter.team = params.teamId
}
if (params.meetingTypeId) {
filter.meeting_type = params.meetingTypeId
}
if (Object.keys(filter).length > 0) {
body.filter = filter
}
const include: Record<string, any> = {}
if (params.includeHighlights) {
include.highlights = true
}
if (params.includeParticipants) {
include.participants = true
}
if (params.includeAiSummary) {
include.ai_summary = true
}
if (Object.keys(include).length > 0) {
body.include = include
}
return body
},
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.message || 'Failed to list recordings')
}
return {
success: true,
output: {
recordings: data.recordings || [],
cursor: data.cursor || null,
},
}
},
outputs: {
recordings: {
type: 'array',
description: 'Array of recording objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Recording UUID' },
title: { type: 'string', description: 'Recording title' },
start_datetime: { type: 'string', description: 'ISO8601 start timestamp' },
end_datetime: { type: 'string', description: 'ISO8601 end timestamp' },
duration_ms: { type: 'number', description: 'Duration in milliseconds' },
media_type: { type: 'string', description: 'audio, transcript, or video' },
source: { type: 'string', description: 'Recording source' },
url: { type: 'string', description: 'URL to view in Grain' },
thumbnail_url: { type: 'string', description: 'Thumbnail URL' },
tags: { type: 'array', description: 'Array of tags' },
teams: { type: 'array', description: 'Teams the recording belongs to' },
meeting_type: { type: 'object', description: 'Meeting type info' },
},
},
},
cursor: {
type: 'string',
description: 'Cursor for next page (null if no more)',
optional: true,
},
},
}
+57
View File
@@ -0,0 +1,57 @@
import type { GrainListTeamsParams, GrainListTeamsResponse } from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainListTeamsTool: ToolConfig<GrainListTeamsParams, GrainListTeamsResponse> = {
id: 'grain_list_teams',
name: 'Grain List Teams',
description: 'List all teams in the workspace',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
},
request: {
url: 'https://api.grain.com/_/public-api/v2/teams',
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
'Public-Api-Version': '2025-10-31',
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.message || 'Failed to list teams')
}
return {
success: true,
output: {
teams: data.teams || data || [],
},
}
},
outputs: {
teams: {
type: 'array',
description: 'Array of team objects',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Team UUID' },
name: { type: 'string', description: 'Team name' },
},
},
},
},
}
+66
View File
@@ -0,0 +1,66 @@
import type { GrainListViewsParams, GrainListViewsResponse } from '@/tools/grain/types'
import type { ToolConfig } from '@/tools/types'
export const grainListViewsTool: ToolConfig<GrainListViewsParams, GrainListViewsResponse> = {
id: 'grain_list_views',
name: 'Grain List Views',
description: 'List available Grain views for webhook subscriptions',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Grain API key (Personal Access Token)',
},
typeFilter: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional view type filter: recordings, highlights, or stories',
},
},
request: {
url: (params) =>
params.typeFilter
? `https://api.grain.com/_/public-api/views?type_filter=${encodeURIComponent(params.typeFilter)}`
: 'https://api.grain.com/_/public-api/views',
method: 'GET',
headers: (params) => ({
'Content-Type': 'application/json',
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response) => {
const data = await response.json()
if (!response.ok) {
throw new Error(data.error || data.message || 'Failed to list views')
}
return {
success: true,
output: {
views: data.views || data || [],
},
}
},
outputs: {
views: {
type: 'array',
description: 'Array of Grain views',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'View UUID' },
name: { type: 'string', description: 'View name' },
type: { type: 'string', description: 'View type: recordings, highlights, or stories' },
},
},
},
},
}
+218
View File
@@ -0,0 +1,218 @@
/**
* Grain API Types
* Base URL: https://api.grain.com/_/public-api
* API Version: 2025-10-31
*/
import type { ToolResponse } from '@/tools/types'
interface GrainTeam {
id: string
name: string
}
interface GrainMeetingType {
id: string
name: string
scope: 'internal' | 'external'
}
interface GrainParticipant {
id: string
name: string
email: string | null
scope: 'internal' | 'external' | 'unknown'
confirmed_attendee: boolean
hs_contact_id?: string | null
}
interface GrainHighlight {
id: string
recording_id: string
text: string
transcript: string
speakers: string[]
timestamp: number
duration: number
tags: string[]
url: string
thumbnail_url: string
created_datetime: string
}
interface GrainAiSummary {
text: string
}
interface GrainCalendarEvent {
ical_uid: string
}
interface GrainHubspotData {
hubspot_company_ids: string[]
hubspot_deal_ids: string[]
}
interface GrainAiTemplateSection {
title: string
data: Record<string, unknown>
}
interface GrainPrivateNotes {
text: string
}
interface GrainTranscriptSection {
participant_id: string | null
speaker: string
start: number
end: number
text: string
}
interface GrainRecording {
id: string
title: string
start_datetime: string
end_datetime: string
duration_ms: number
media_type: 'audio' | 'transcript' | 'video'
source: 'aircall' | 'local_capture' | 'meet' | 'teams' | 'upload' | 'webex' | 'zoom' | 'other'
url: string
thumbnail_url: string | null
tags: string[]
teams: GrainTeam[]
meeting_type: GrainMeetingType | null
highlights?: GrainHighlight[]
participants?: GrainParticipant[]
ai_summary?: GrainAiSummary
calendar_event?: GrainCalendarEvent | null
hubspot?: GrainHubspotData
private_notes?: GrainPrivateNotes | null
ai_template_sections?: GrainAiTemplateSection[]
}
interface GrainHook {
id: string
enabled: boolean
version?: number
hook_url: string
view_id?: string
actions?: Array<'added' | 'updated' | 'removed'>
inserted_at: string
}
interface GrainView {
id: string
name?: string
type?: 'recordings' | 'highlights' | 'stories'
}
export interface GrainListViewsParams {
apiKey: string
typeFilter?: 'recordings' | 'highlights' | 'stories'
}
export interface GrainListViewsResponse extends ToolResponse {
output: {
views: GrainView[]
}
}
export interface GrainListRecordingsParams {
apiKey: string
cursor?: string
beforeDatetime?: string
afterDatetime?: string
participantScope?: 'internal' | 'external'
titleSearch?: string
teamId?: string
meetingTypeId?: string
includeHighlights?: boolean
includeParticipants?: boolean
includeAiSummary?: boolean
}
export interface GrainListRecordingsResponse extends ToolResponse {
output: {
recordings: GrainRecording[]
cursor: string | null
}
}
export interface GrainGetRecordingParams {
apiKey: string
recordingId: string
includeHighlights?: boolean
includeParticipants?: boolean
includeAiSummary?: boolean
includeCalendarEvent?: boolean
includeHubspot?: boolean
}
export interface GrainGetRecordingResponse extends ToolResponse {
output: GrainRecording
}
export interface GrainGetTranscriptParams {
apiKey: string
recordingId: string
}
export interface GrainGetTranscriptResponse extends ToolResponse {
output: {
transcript: GrainTranscriptSection[]
}
}
export interface GrainListTeamsParams {
apiKey: string
}
export interface GrainListTeamsResponse extends ToolResponse {
output: {
teams: GrainTeam[]
}
}
export interface GrainListMeetingTypesParams {
apiKey: string
}
export interface GrainListMeetingTypesResponse extends ToolResponse {
output: {
meeting_types: GrainMeetingType[]
}
}
export interface GrainCreateHookParams {
apiKey: string
hookUrl: string
viewId: string
actions?: Array<'added' | 'updated' | 'removed'>
}
export interface GrainCreateHookResponse extends ToolResponse {
output: GrainHook
}
export interface GrainListHooksParams {
apiKey: string
}
export interface GrainListHooksResponse extends ToolResponse {
output: {
hooks: GrainHook[]
}
}
export interface GrainDeleteHookParams {
apiKey: string
hookId: string
}
export interface GrainDeleteHookResponse extends ToolResponse {
output: {
success: true
}
}