chore: import upstream snapshot with attribution
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
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
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
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import { db } from '@sim/db'
|
||||
import { workflowSchedule } from '@sim/db/schema'
|
||||
import { and, eq, isNull } from 'drizzle-orm'
|
||||
import type { ExecutionContext, ToolCallResult } from '@/lib/copilot/request/types'
|
||||
import { type MothershipResource, MothershipResourceType } from '@/lib/copilot/resources/types'
|
||||
import { canonicalWorkspaceFilePath } from '@/lib/copilot/vfs/path-utils'
|
||||
import { getKnowledgeBaseById } from '@/lib/knowledge/service'
|
||||
import { getLogById } from '@/lib/logs/service'
|
||||
import { getTableById } from '@/lib/table/service'
|
||||
import {
|
||||
getWorkspaceFile,
|
||||
resolveWorkspaceFileReference,
|
||||
} from '@/lib/uploads/contexts/workspace/workspace-file-manager'
|
||||
import { getWorkflowById } from '@/lib/workflows/utils'
|
||||
import type { OpenResourceItem, OpenResourceParams, ValidOpenResourceParams } from './param-types'
|
||||
|
||||
const VALID_OPEN_RESOURCE_TYPES = new Set(Object.values(MothershipResourceType))
|
||||
|
||||
async function resolveResource(
|
||||
item: ValidOpenResourceParams,
|
||||
context: ExecutionContext
|
||||
): Promise<MothershipResource | { error: string }> {
|
||||
const resourceType = item.type
|
||||
let resourceId = item.id ?? ''
|
||||
let title: string = resourceType
|
||||
|
||||
if (resourceType === 'file') {
|
||||
if (!context.workspaceId)
|
||||
return { error: 'Opening a workspace file requires workspace context.' }
|
||||
const fileRef = item.path || item.id || ''
|
||||
const record = item.path
|
||||
? await resolveWorkspaceFileReference(context.workspaceId, item.path)
|
||||
: item.id
|
||||
? await getWorkspaceFile(context.workspaceId, item.id)
|
||||
: null
|
||||
if (!record) return { error: `No workspace file found for "${fileRef}".` }
|
||||
resourceId = record.id
|
||||
title = record.name
|
||||
return {
|
||||
type: resourceType,
|
||||
id: resourceId,
|
||||
title,
|
||||
path: canonicalWorkspaceFilePath({ folderPath: record.folderPath, name: record.name }),
|
||||
}
|
||||
}
|
||||
if (resourceType === 'workflow') {
|
||||
if (!item.id) return { error: 'workflow resources require `id`.' }
|
||||
const wf = await getWorkflowById(item.id)
|
||||
if (!wf) return { error: `No workflow with id "${item.id}".` }
|
||||
if (context.workspaceId && wf.workspaceId !== context.workspaceId)
|
||||
return { error: `Workflow not found in the current workspace.` }
|
||||
resourceId = wf.id
|
||||
title = wf.name
|
||||
}
|
||||
if (resourceType === 'table') {
|
||||
if (!item.id) return { error: 'table resources require `id`.' }
|
||||
const tbl = await getTableById(item.id)
|
||||
if (!tbl) return { error: `No table with id "${item.id}".` }
|
||||
if (context.workspaceId && tbl.workspaceId !== context.workspaceId)
|
||||
return { error: `Table not found in the current workspace.` }
|
||||
resourceId = tbl.id
|
||||
title = tbl.name
|
||||
}
|
||||
if (resourceType === 'knowledgebase') {
|
||||
if (!item.id) return { error: 'knowledgebase resources require `id`.' }
|
||||
const kb = await getKnowledgeBaseById(item.id)
|
||||
if (!kb) return { error: `No knowledge base with id "${item.id}".` }
|
||||
if (context.workspaceId && kb.workspaceId !== context.workspaceId)
|
||||
return { error: `Knowledge base not found in the current workspace.` }
|
||||
resourceId = kb.id
|
||||
title = kb.name
|
||||
}
|
||||
if (resourceType === 'log') {
|
||||
if (!item.id) return { error: 'log resources require `id`.' }
|
||||
const logRecord = await getLogById(item.id)
|
||||
if (!logRecord) return { error: `No log with id "${item.id}".` }
|
||||
if (context.workspaceId && logRecord.workspaceId !== context.workspaceId)
|
||||
return { error: `Log not found in the current workspace.` }
|
||||
resourceId = logRecord.id
|
||||
const workflowName = logRecord.workflowName ?? 'Unknown Workflow'
|
||||
const timestamp = logRecord.startedAt.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
title = `${workflowName} — ${timestamp}`
|
||||
}
|
||||
if (resourceType === 'scheduledtask') {
|
||||
if (!item.id) return { error: 'scheduledtask resources require `id`.' }
|
||||
if (!context.workspaceId)
|
||||
return { error: 'Opening a scheduled task requires workspace context.' }
|
||||
const [schedule] = await db
|
||||
.select({ id: workflowSchedule.id, jobTitle: workflowSchedule.jobTitle })
|
||||
.from(workflowSchedule)
|
||||
.where(
|
||||
and(
|
||||
eq(workflowSchedule.id, item.id),
|
||||
eq(workflowSchedule.sourceWorkspaceId, context.workspaceId),
|
||||
eq(workflowSchedule.sourceType, 'job'),
|
||||
isNull(workflowSchedule.archivedAt)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
if (!schedule) return { error: `No scheduled task with id "${item.id}".` }
|
||||
resourceId = schedule.id
|
||||
title = schedule.jobTitle || 'Scheduled Task'
|
||||
}
|
||||
|
||||
return { type: resourceType, id: resourceId, title }
|
||||
}
|
||||
|
||||
export async function executeOpenResource(
|
||||
rawParams: Record<string, unknown>,
|
||||
context: ExecutionContext
|
||||
): Promise<ToolCallResult> {
|
||||
const params = rawParams as OpenResourceParams
|
||||
|
||||
const items: OpenResourceItem[] =
|
||||
params.resources ??
|
||||
(params.type && (params.id || params.path)
|
||||
? [{ type: params.type, id: params.id, path: params.path }]
|
||||
: [])
|
||||
|
||||
if (items.length === 0) {
|
||||
return { success: false, error: 'resources array is required' }
|
||||
}
|
||||
|
||||
const resources: MothershipResource[] = []
|
||||
const errors: string[] = []
|
||||
|
||||
for (const item of items) {
|
||||
const validated = validateOpenResourceItem(item)
|
||||
if (!validated.success) {
|
||||
errors.push(validated.error)
|
||||
continue
|
||||
}
|
||||
const result = await resolveResource(validated.params, context)
|
||||
if ('error' in result) {
|
||||
errors.push(result.error)
|
||||
} else {
|
||||
resources.push(result)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: resources.length > 0,
|
||||
output: { opened: resources.length, errors },
|
||||
resources,
|
||||
}
|
||||
}
|
||||
|
||||
function validateOpenResourceItem(
|
||||
item: OpenResourceItem
|
||||
): { success: true; params: ValidOpenResourceParams } | { success: false; error: string } {
|
||||
if (!item.type) {
|
||||
return { success: false, error: 'type is required' }
|
||||
}
|
||||
if (!VALID_OPEN_RESOURCE_TYPES.has(item.type)) {
|
||||
return { success: false, error: `Invalid resource type: ${item.type}` }
|
||||
}
|
||||
if (!item.id && !(item.type === 'file' && item.path)) {
|
||||
return { success: false, error: `${item.type} resources require \`id\`` }
|
||||
}
|
||||
return { success: true, params: { type: item.type, id: item.id, path: item.path } }
|
||||
}
|
||||
Reference in New Issue
Block a user