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

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
+75
View File
@@ -0,0 +1,75 @@
import { TABLE_LIMITS } from '@/lib/table/constants'
import { enrichTableToolSchema } from '@/tools/schema-enrichers'
import type { TableBatchInsertParams, TableBatchInsertResponse } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableBatchInsertRowsTool: ToolConfig<
TableBatchInsertParams,
TableBatchInsertResponse
> = {
id: 'table_batch_insert_rows',
name: 'Batch Insert Rows',
description: `Insert multiple rows into a table at once (up to ${TABLE_LIMITS.MAX_BATCH_INSERT_SIZE} rows)`,
version: '1.0.0',
toolEnrichment: {
dependsOn: 'tableId',
enrichTool: (tableId, schema, desc) =>
enrichTableToolSchema(tableId, 'table_batch_insert_rows', schema, desc),
},
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
rows: {
type: 'array',
required: true,
description: `Array of row data objects (max ${TABLE_LIMITS.MAX_BATCH_INSERT_SIZE} rows)`,
visibility: 'user-or-llm',
},
},
request: {
url: (params: TableBatchInsertParams) => `/api/table/${params.tableId}/rows`,
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: TableBatchInsertParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return {
rows: params.rows,
workspaceId,
}
},
},
transformResponse: async (response): Promise<TableBatchInsertResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
rows: data.rows,
insertedCount: data.insertedCount,
message: data.message || 'Rows inserted successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether rows were inserted' },
rows: { type: 'array', description: 'Inserted rows data' },
insertedCount: { type: 'number', description: 'Number of rows inserted' },
message: { type: 'string', description: 'Status message' },
},
}
+70
View File
@@ -0,0 +1,70 @@
import type { TableCreateParams, TableCreateResponse } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableCreateTool: ToolConfig<TableCreateParams, TableCreateResponse> = {
id: 'table_create',
name: 'Create Table',
description: 'Create a new user-defined table with schema',
version: '1.0.0',
params: {
name: {
type: 'string',
required: true,
description: 'Table name (alphanumeric, underscores, 1-50 chars)',
visibility: 'user-or-llm',
},
description: {
type: 'string',
required: false,
description: 'Optional table description',
visibility: 'user-or-llm',
},
schema: {
type: 'object',
required: true,
description: 'Table schema with column definitions',
visibility: 'user-or-llm',
},
},
request: {
url: '/api/table',
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return {
name: params.name,
description: params.description,
schema: params.schema,
workspaceId,
}
},
},
transformResponse: async (response): Promise<TableCreateResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
table: data.table,
message: data.message || 'Table created successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether table was created' },
table: { type: 'json', description: 'Created table metadata' },
message: { type: 'string', description: 'Status message' },
},
}
+61
View File
@@ -0,0 +1,61 @@
import type { TableDeleteResponse, TableRowDeleteParams } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableDeleteRowTool: ToolConfig<TableRowDeleteParams, TableDeleteResponse> = {
id: 'table_delete_row',
name: 'Delete Row',
description: 'Delete a row from a table',
version: '1.0.0',
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
rowId: {
type: 'string',
required: true,
description: 'Row ID to delete',
visibility: 'user-or-llm',
},
},
request: {
url: (params: TableRowDeleteParams) => `/api/table/${params.tableId}/rows/${params.rowId}`,
method: 'DELETE',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: TableRowDeleteParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return {
workspaceId,
}
},
},
transformResponse: async (response): Promise<TableDeleteResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
deletedCount: data.deletedCount,
message: data.message || 'Row deleted successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether row was deleted' },
deletedCount: { type: 'number', description: 'Number of rows deleted' },
message: { type: 'string', description: 'Status message' },
},
}
@@ -0,0 +1,84 @@
import { TABLE_LIMITS } from '@/lib/table/constants'
import { enrichTableToolSchema } from '@/tools/schema-enrichers'
import type { TableBulkOperationResponse, TableDeleteByFilterParams } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableDeleteRowsByFilterTool: ToolConfig<
TableDeleteByFilterParams,
TableBulkOperationResponse
> = {
id: 'table_delete_rows_by_filter',
name: 'Delete Rows by Filter',
description:
'Delete multiple rows that match filter criteria. Use with caution - supports optional limit for safety.',
version: '1.0.0',
toolEnrichment: {
dependsOn: 'tableId',
enrichTool: (tableId, schema, desc) =>
enrichTableToolSchema(tableId, 'table_delete_rows_by_filter', schema, desc),
},
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
filter: {
type: 'object',
required: true,
description:
'Filter criteria using operators like $eq, $ne, $gt, $lt, $contains, $ncontains, $startsWith, $endsWith, $in, $nin, $empty, etc.',
visibility: 'user-or-llm',
},
limit: {
type: 'number',
required: false,
description: `Maximum number of rows to delete (default: no limit, max: ${TABLE_LIMITS.MAX_BULK_OPERATION_SIZE})`,
visibility: 'user-or-llm',
},
},
request: {
url: (params: TableDeleteByFilterParams) => `/api/table/${params.tableId}/rows`,
method: 'DELETE',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: TableDeleteByFilterParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return {
filter: params.filter,
limit: params.limit,
workspaceId,
}
},
},
transformResponse: async (response): Promise<TableBulkOperationResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
deletedCount: data.deletedCount || 0,
deletedRowIds: data.deletedRowIds || [],
message: data.message || 'Rows deleted successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether rows were deleted' },
deletedCount: { type: 'number', description: 'Number of rows deleted' },
deletedRowIds: { type: 'array', description: 'IDs of deleted rows' },
message: { type: 'string', description: 'Status message' },
},
}
+58
View File
@@ -0,0 +1,58 @@
import type { TableRowGetParams, TableRowResponse } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableGetRowTool: ToolConfig<TableRowGetParams, TableRowResponse> = {
id: 'table_get_row',
name: 'Get Row',
description: 'Get a single row by ID',
version: '1.0.0',
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
rowId: {
type: 'string',
required: true,
description: 'Row ID to retrieve',
visibility: 'user-or-llm',
},
},
request: {
url: (params: TableRowGetParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return `/api/table/${params.tableId}/rows/${params.rowId}?workspaceId=${encodeURIComponent(workspaceId)}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response): Promise<TableRowResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
row: data.row,
message: data.message || 'Row retrieved successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether row was retrieved' },
row: { type: 'json', description: 'Row data' },
message: { type: 'string', description: 'Status message' },
},
}
+72
View File
@@ -0,0 +1,72 @@
import { getColumnId } from '@/lib/table/column-keys'
import type { ColumnDefinition } from '@/lib/table/types'
import type { TableGetSchemaParams, TableGetSchemaResponse } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableGetSchemaTool: ToolConfig<TableGetSchemaParams, TableGetSchemaResponse> = {
id: 'table_get_schema',
name: 'Get Schema',
description: 'Get the schema configuration of a table',
version: '1.0.0',
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
},
request: {
url: (params: TableGetSchemaParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return `/api/table/${params.tableId}?workspaceId=${encodeURIComponent(workspaceId)}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response): Promise<TableGetSchemaResponse> => {
const result = await response.json()
const data = result.data || result
// Always surface a usable `id` per column. Legacy columns predating the id
// backfill have no stored id; their storage key is the name, so project that
// as the id rather than leaving it undefined.
const columns: ColumnDefinition[] = (
(data.table.schema.columns ?? []) as ColumnDefinition[]
).map((col) => ({ ...col, id: getColumnId(col) }))
return {
success: true,
output: {
name: data.table.name,
columns,
columnCount: columns.length,
rowCount: data.table.rowCount ?? 0,
maxRows: data.table.maxRows ?? 0,
message: data.message || 'Schema retrieved successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether schema was retrieved' },
name: { type: 'string', description: 'Table name' },
columns: { type: 'array', description: 'Column definitions (each includes its stable id)' },
columnCount: { type: 'number', description: 'Number of columns' },
rowCount: { type: 'number', description: 'Number of rows in the table' },
maxRows: {
type: 'number',
description: "Max rows per table for the workspace's plan",
},
message: { type: 'string', description: 'Status message' },
},
}
+13
View File
@@ -0,0 +1,13 @@
export * from './batch_insert_rows'
export * from './create'
export * from './delete_row'
export * from './delete_rows_by_filter'
export * from './get_row'
export * from './get_schema'
export * from './insert_row'
export * from './list'
export * from './query_rows'
export * from './types'
export * from './update_row'
export * from './update_rows_by_filter'
export * from './upsert_row'
+70
View File
@@ -0,0 +1,70 @@
import { enrichTableToolSchema } from '@/tools/schema-enrichers'
import type { TableRowInsertParams, TableRowResponse } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableInsertRowTool: ToolConfig<TableRowInsertParams, TableRowResponse> = {
id: 'table_insert_row',
name: 'Insert Row',
description:
'Insert a new row into a table. IMPORTANT: You must use the "data" parameter (not "values", "row", "fields", or other variations) to specify the row contents.',
version: '1.0.0',
toolEnrichment: {
dependsOn: 'tableId',
enrichTool: (tableId, schema, desc) =>
enrichTableToolSchema(tableId, 'table_insert_row', schema, desc),
},
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
data: {
type: 'object',
required: true,
description: 'Row data as JSON object',
visibility: 'user-or-llm',
},
},
request: {
url: (params: TableRowInsertParams) => `/api/table/${params.tableId}/rows`,
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: TableRowInsertParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return {
data: params.data,
workspaceId,
}
},
},
transformResponse: async (response): Promise<TableRowResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
row: data.row,
message: data.message || 'Row inserted successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether row was inserted' },
row: { type: 'json', description: 'Inserted row data' },
message: { type: 'string', description: 'Status message' },
},
}
+44
View File
@@ -0,0 +1,44 @@
import type { TableListParams, TableListResponse } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableListTool: ToolConfig<TableListParams, TableListResponse> = {
id: 'table_list',
name: 'List Tables',
description: 'List all tables in the workspace',
version: '1.0.0',
params: {},
request: {
url: (params: TableListParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return `/api/table?workspaceId=${encodeURIComponent(workspaceId)}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response): Promise<TableListResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
tables: data.tables,
totalCount: data.totalCount,
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether operation succeeded' },
tables: { type: 'array', description: 'List of tables' },
totalCount: { type: 'number', description: 'Total number of tables' },
},
}
+108
View File
@@ -0,0 +1,108 @@
import { TABLE_LIMITS } from '@/lib/table/constants'
import { enrichTableToolSchema } from '@/tools/schema-enrichers'
import type { TableQueryResponse, TableRowQueryParams } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableQueryRowsTool: ToolConfig<TableRowQueryParams, TableQueryResponse> = {
id: 'table_query_rows',
name: 'Query Rows',
description: 'Query rows from a table with filtering, sorting, and pagination',
version: '1.0.0',
toolEnrichment: {
dependsOn: 'tableId',
enrichTool: (tableId, schema, desc) =>
enrichTableToolSchema(tableId, 'table_query_rows', schema, desc),
},
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
filter: {
type: 'object',
required: false,
description:
'Filter conditions (MongoDB-style operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $contains, $ncontains, $startsWith, $endsWith, $empty)',
visibility: 'user-or-llm',
},
sort: {
type: 'object',
required: false,
description: 'Sort order as {field: "asc"|"desc"}',
visibility: 'user-or-llm',
},
limit: {
type: 'number',
required: false,
description: `Maximum rows to return (default: ${TABLE_LIMITS.DEFAULT_QUERY_LIMIT}, max: ${TABLE_LIMITS.MAX_QUERY_LIMIT})`,
visibility: 'user-or-llm',
},
offset: {
type: 'number',
required: false,
description: 'Number of rows to skip (default: 0)',
visibility: 'user-or-llm',
},
},
request: {
url: (params: TableRowQueryParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
const searchParams = new URLSearchParams({
workspaceId,
})
if (params.filter) {
searchParams.append('filter', JSON.stringify(params.filter))
}
if (params.sort) {
searchParams.append('sort', JSON.stringify(params.sort))
}
if (params.limit !== undefined) {
searchParams.append('limit', String(params.limit))
}
if (params.offset !== undefined) {
searchParams.append('offset', String(params.offset))
}
return `/api/table/${params.tableId}/rows?${searchParams.toString()}`
},
method: 'GET',
headers: () => ({
'Content-Type': 'application/json',
}),
},
transformResponse: async (response): Promise<TableQueryResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
rows: data.rows,
rowCount: data.rowCount,
totalCount: data.totalCount,
limit: data.limit,
offset: data.offset,
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether query succeeded' },
rows: { type: 'array', description: 'Query result rows' },
rowCount: { type: 'number', description: 'Number of rows returned' },
totalCount: { type: 'number', description: 'Total rows matching filter' },
limit: { type: 'number', description: 'Limit used in query' },
offset: { type: 'number', description: 'Offset used in query' },
},
}
+158
View File
@@ -0,0 +1,158 @@
import type {
ColumnDefinition,
Filter,
RowData,
Sort,
TableDefinition,
TableRow,
TableSchema,
} from '@/lib/table/types'
import type { ToolResponse, WorkflowToolExecutionContext } from '@/tools/types'
export interface TableCreateParams {
name: string
description?: string
schema: TableSchema
_context?: WorkflowToolExecutionContext
}
export interface TableListParams {
_context?: WorkflowToolExecutionContext
}
export interface TableRowInsertParams {
tableId: string
data: RowData
/** Unique column to match on for upsert; ignored by plain insert */
conflictTarget?: string
_context?: WorkflowToolExecutionContext
}
export interface TableRowUpdateParams {
tableId: string
rowId: string
data: RowData
_context?: WorkflowToolExecutionContext
}
export interface TableRowDeleteParams {
tableId: string
rowId: string
_context?: WorkflowToolExecutionContext
}
export interface TableRowQueryParams {
tableId: string
filter?: Filter
sort?: Sort
limit?: number
offset?: number
_context?: WorkflowToolExecutionContext
}
export interface TableRowGetParams {
tableId: string
rowId: string
_context?: WorkflowToolExecutionContext
}
export interface TableCreateResponse extends ToolResponse {
output: {
table: TableDefinition
message: string
}
}
export interface TableListResponse extends ToolResponse {
output: {
tables: TableDefinition[]
totalCount: number
}
}
export interface TableRowResponse extends ToolResponse {
output: {
row: TableRow
message: string
}
}
export interface TableQueryResponse extends ToolResponse {
output: {
rows: TableRow[]
rowCount: number
totalCount: number
limit: number
offset: number
}
}
export interface TableDeleteResponse extends ToolResponse {
output: {
deletedCount: number
message: string
}
}
export interface TableBatchInsertParams {
tableId: string
rows: RowData[]
_context?: WorkflowToolExecutionContext
}
export interface TableBatchInsertResponse extends ToolResponse {
output: {
rows: TableRow[]
insertedCount: number
message: string
}
}
export interface TableUpdateByFilterParams {
tableId: string
filter: Filter
data: RowData
limit?: number
_context?: WorkflowToolExecutionContext
}
export interface TableDeleteByFilterParams {
tableId: string
filter: Filter
limit?: number
_context?: WorkflowToolExecutionContext
}
export interface TableBulkOperationResponse extends ToolResponse {
output: {
updatedCount?: number
deletedCount?: number
updatedRowIds?: string[]
deletedRowIds?: string[]
message: string
}
}
export interface TableGetSchemaParams {
tableId: string
_context?: WorkflowToolExecutionContext
}
export interface TableGetSchemaResponse extends ToolResponse {
output: {
name: string
columns: ColumnDefinition[]
columnCount: number
rowCount: number
maxRows: number
message: string
}
}
export interface TableUpsertResponse extends ToolResponse {
output: {
row: TableRow
operation: 'insert' | 'update'
message: string
}
}
+76
View File
@@ -0,0 +1,76 @@
import { enrichTableToolSchema } from '@/tools/schema-enrichers'
import type { TableRowResponse, TableRowUpdateParams } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableUpdateRowTool: ToolConfig<TableRowUpdateParams, TableRowResponse> = {
id: 'table_update_row',
name: 'Update Row',
description:
'Update an existing row in a table. Supports partial updates - only include the fields you want to change. IMPORTANT: You must use the "data" parameter (not "values", "row", "fields", or other variations) to specify the fields to update.',
version: '1.0.0',
toolEnrichment: {
dependsOn: 'tableId',
enrichTool: (tableId, schema, desc) =>
enrichTableToolSchema(tableId, 'table_update_row', schema, desc),
},
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
rowId: {
type: 'string',
required: true,
description: 'Row ID to update',
visibility: 'user-or-llm',
},
data: {
type: 'object',
required: true,
description: 'Updated row data',
visibility: 'user-or-llm',
},
},
request: {
url: (params: TableRowUpdateParams) => `/api/table/${params.tableId}/rows/${params.rowId}`,
method: 'PATCH',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: TableRowUpdateParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return {
data: params.data,
workspaceId,
}
},
},
transformResponse: async (response): Promise<TableRowResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
row: data.row,
message: data.message || 'Row updated successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether row was updated' },
row: { type: 'json', description: 'Updated row data' },
message: { type: 'string', description: 'Status message' },
},
}
@@ -0,0 +1,91 @@
import { TABLE_LIMITS } from '@/lib/table/constants'
import { enrichTableToolSchema } from '@/tools/schema-enrichers'
import type { TableBulkOperationResponse, TableUpdateByFilterParams } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableUpdateRowsByFilterTool: ToolConfig<
TableUpdateByFilterParams,
TableBulkOperationResponse
> = {
id: 'table_update_rows_by_filter',
name: 'Update Rows by Filter',
description:
'Update multiple rows that match filter criteria. Data is merged with existing row data.',
version: '1.0.0',
toolEnrichment: {
dependsOn: 'tableId',
enrichTool: (tableId, schema, desc) =>
enrichTableToolSchema(tableId, 'table_update_rows_by_filter', schema, desc),
},
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
filter: {
type: 'object',
required: true,
description:
'Filter criteria using operators like $eq, $ne, $gt, $lt, $contains, $ncontains, $startsWith, $endsWith, $in, $nin, $empty, etc.',
visibility: 'user-or-llm',
},
data: {
type: 'object',
required: true,
description: 'Fields to update (merged with existing data)',
visibility: 'user-or-llm',
},
limit: {
type: 'number',
required: false,
description: `Maximum number of rows to update (default: no limit, max: ${TABLE_LIMITS.MAX_BULK_OPERATION_SIZE})`,
visibility: 'user-or-llm',
},
},
request: {
url: (params: TableUpdateByFilterParams) => `/api/table/${params.tableId}/rows`,
method: 'PUT',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: TableUpdateByFilterParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return {
filter: params.filter,
data: params.data,
limit: params.limit,
workspaceId,
}
},
},
transformResponse: async (response): Promise<TableBulkOperationResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
updatedCount: data.updatedCount || 0,
updatedRowIds: data.updatedRowIds || [],
message: data.message || 'Rows updated successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether rows were updated' },
updatedCount: { type: 'number', description: 'Number of rows updated' },
updatedRowIds: { type: 'array', description: 'IDs of updated rows' },
message: { type: 'string', description: 'Status message' },
},
}
+80
View File
@@ -0,0 +1,80 @@
import { enrichTableToolSchema } from '@/tools/schema-enrichers'
import type { TableRowInsertParams, TableUpsertResponse } from '@/tools/table/types'
import type { ToolConfig } from '@/tools/types'
export const tableUpsertRowTool: ToolConfig<TableRowInsertParams, TableUpsertResponse> = {
id: 'table_upsert_row',
name: 'Upsert Row',
description:
'Insert or update a row based on unique column constraints. If a row with matching unique field exists, update it; otherwise insert a new row. IMPORTANT: You must use the "data" parameter (not "values", "row", "fields", or other variations) to specify the row contents.',
version: '1.0.0',
toolEnrichment: {
dependsOn: 'tableId',
enrichTool: (tableId, schema, desc) =>
enrichTableToolSchema(tableId, 'table_upsert_row', schema, desc),
},
params: {
tableId: {
type: 'string',
required: true,
description: 'Table ID',
visibility: 'user-only',
},
data: {
type: 'object',
required: true,
description: 'Row data to insert or update',
visibility: 'user-or-llm',
},
conflictTarget: {
type: 'string',
required: false,
description:
'Unique column to match on. Required only when the table has more than one unique column.',
visibility: 'user-only',
},
},
request: {
url: (params: TableRowInsertParams) => `/api/table/${params.tableId}/rows/upsert`,
method: 'POST',
headers: () => ({
'Content-Type': 'application/json',
}),
body: (params: TableRowInsertParams) => {
const workspaceId = params._context?.workspaceId
if (!workspaceId) {
throw new Error('Workspace ID is required in execution context')
}
return {
data: params.data,
workspaceId,
...(params.conflictTarget ? { conflictTarget: params.conflictTarget } : {}),
}
},
},
transformResponse: async (response): Promise<TableUpsertResponse> => {
const result = await response.json()
const data = result.data || result
return {
success: true,
output: {
row: data.row,
operation: data.operation,
message: data.message || 'Row upserted successfully',
},
}
},
outputs: {
success: { type: 'boolean', description: 'Whether row was upserted' },
row: { type: 'json', description: 'Upserted row data' },
operation: { type: 'string', description: 'Operation performed: insert or update' },
message: { type: 'string', description: 'Status message' },
},
}