mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
feat(tools): Rocketlane integration — 64 tools across projects, tasks, phases, fields, time tracking, spaces, and invoices (#5709)
* feat(rocketlane): Rocketlane integration — 64 tools across projects, tasks, phases, fields, time tracking, spaces, and invoices * fix(rocketlane): allow clearing optional fields on update, require a user reference for time-off and placeholder assignment * chore(rocketlane): trigger fresh review round * fix(rocketlane): require an owner reference when creating a project, matching the API contract
This commit is contained in:
@@ -8710,3 +8710,24 @@ export function JupyterIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function RocketlaneIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 19.001 16.514' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<g transform='translate(0 -6.486)'>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M15.446 6.94198L19.0007 14.7744L15.446 22.6068C14.652 22.862 13.8054 22.9998 12.9266 22.9998C11.714 22.9998 10.5627 22.7374 9.52637 22.2664L12.9266 14.7744L9.52637 7.28242C10.5627 6.81133 11.714 6.54895 12.9266 6.54895C13.8054 6.54895 14.652 6.68677 15.446 6.94198Z'
|
||||
fill='#0F62FE'
|
||||
/>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M6.03112 7.01001L9.52638 14.7114L6.03112 22.4129C5.13054 22.7516 4.15483 22.9369 3.13585 22.9369C2.02543 22.9369 0.96641 22.7168 0 22.318L3.45221 14.7114L2.49902e-08 7.10487C0.96641 6.70602 2.02543 6.48599 3.13585 6.48599C4.15483 6.48599 5.13054 6.67128 6.03112 7.01001Z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ import {
|
||||
ResendIcon,
|
||||
RevenueCatIcon,
|
||||
RipplingIcon,
|
||||
RocketlaneIcon,
|
||||
RootlyIcon,
|
||||
S3Icon,
|
||||
SalesforceIcon,
|
||||
@@ -444,6 +445,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
resend: ResendIcon,
|
||||
revenuecat: RevenueCatIcon,
|
||||
rippling: RipplingIcon,
|
||||
rocketlane: RocketlaneIcon,
|
||||
rootly: RootlyIcon,
|
||||
s3: S3Icon,
|
||||
salesforce: SalesforceIcon,
|
||||
|
||||
@@ -203,6 +203,7 @@
|
||||
"resend",
|
||||
"revenuecat",
|
||||
"rippling",
|
||||
"rocketlane",
|
||||
"rootly",
|
||||
"s3",
|
||||
"salesforce",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -254,6 +254,7 @@ import { ResendBlock, ResendBlockMeta } from '@/blocks/blocks/resend'
|
||||
import { ResponseBlock } from '@/blocks/blocks/response'
|
||||
import { RevenueCatBlock, RevenueCatBlockMeta } from '@/blocks/blocks/revenuecat'
|
||||
import { RipplingBlock, RipplingBlockMeta } from '@/blocks/blocks/rippling'
|
||||
import { RocketlaneBlock, RocketlaneBlockMeta } from '@/blocks/blocks/rocketlane'
|
||||
import { RootlyBlock, RootlyBlockMeta } from '@/blocks/blocks/rootly'
|
||||
import { RouterBlock, RouterV2Block } from '@/blocks/blocks/router'
|
||||
import { RssBlock, RssBlockMeta } from '@/blocks/blocks/rss'
|
||||
@@ -566,6 +567,7 @@ export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
|
||||
response: ResponseBlock,
|
||||
revenuecat: RevenueCatBlock,
|
||||
rippling: RipplingBlock,
|
||||
rocketlane: RocketlaneBlock,
|
||||
rootly: RootlyBlock,
|
||||
router: RouterBlock,
|
||||
router_v2: RouterV2Block,
|
||||
@@ -853,6 +855,7 @@ export const BLOCK_META_REGISTRY: Record<string, BlockMeta> = {
|
||||
resend: ResendBlockMeta,
|
||||
revenuecat: RevenueCatBlockMeta,
|
||||
rippling: RipplingBlockMeta,
|
||||
rocketlane: RocketlaneBlockMeta,
|
||||
rootly: RootlyBlockMeta,
|
||||
rss: RssBlockMeta,
|
||||
s3: S3BlockMeta,
|
||||
|
||||
@@ -8710,3 +8710,24 @@ export function JupyterIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function RocketlaneIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 19.001 16.514' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<g transform='translate(0 -6.486)'>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M15.446 6.94198L19.0007 14.7744L15.446 22.6068C14.652 22.862 13.8054 22.9998 12.9266 22.9998C11.714 22.9998 10.5627 22.7374 9.52637 22.2664L12.9266 14.7744L9.52637 7.28242C10.5627 6.81133 11.714 6.54895 12.9266 6.54895C13.8054 6.54895 14.652 6.68677 15.446 6.94198Z'
|
||||
fill='#0F62FE'
|
||||
/>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M6.03112 7.01001L9.52638 14.7114L6.03112 22.4129C5.13054 22.7516 4.15483 22.9369 3.13585 22.9369C2.02543 22.9369 0.96641 22.7168 0 22.318L3.45221 14.7114L2.49902e-08 7.10487C0.96641 6.70602 2.02543 6.48599 3.13585 6.48599C4.15483 6.48599 5.13054 6.67128 6.03112 7.01001Z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -181,6 +181,7 @@ import {
|
||||
ResendIcon,
|
||||
RevenueCatIcon,
|
||||
RipplingIcon,
|
||||
RocketlaneIcon,
|
||||
RootlyIcon,
|
||||
S3Icon,
|
||||
SalesforceIcon,
|
||||
@@ -421,6 +422,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
resend: ResendIcon,
|
||||
revenuecat: RevenueCatIcon,
|
||||
rippling: RipplingIcon,
|
||||
rocketlane: RocketlaneIcon,
|
||||
rootly: RootlyIcon,
|
||||
s3: S3Icon,
|
||||
salesforce: SalesforceIcon,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"updatedAt": "2026-07-13",
|
||||
"updatedAt": "2026-07-16",
|
||||
"integrations": [
|
||||
{
|
||||
"type": "onepassword",
|
||||
@@ -15089,6 +15089,281 @@
|
||||
"integrationType": "hr",
|
||||
"tags": ["hiring"]
|
||||
},
|
||||
{
|
||||
"type": "rocketlane",
|
||||
"slug": "rocketlane",
|
||||
"name": "Rocketlane",
|
||||
"description": "Manage client onboarding projects, tasks, time tracking, and invoices",
|
||||
"longDescription": "Integrate Rocketlane into your workflow. Rocketlane is a professional-services automation platform for client onboarding and project delivery. Create and manage projects, tasks, phases, custom fields, time entries, time-offs, spaces, documents, resource allocations, and invoices.",
|
||||
"bgColor": "#000000",
|
||||
"iconName": "RocketlaneIcon",
|
||||
"docsUrl": "https://docs.sim.ai/integrations/rocketlane",
|
||||
"operations": [
|
||||
{
|
||||
"name": "Create Project",
|
||||
"description": "Create a new Rocketlane project with a customer, owner, dates, team members, templates, financials, and custom fields"
|
||||
},
|
||||
{
|
||||
"name": "Get Project",
|
||||
"description": "Retrieve a Rocketlane project by its unique identifier"
|
||||
},
|
||||
{
|
||||
"name": "List Projects",
|
||||
"description": "List Rocketlane projects with optional filters, sorting, and token-based pagination"
|
||||
},
|
||||
{
|
||||
"name": "Update Project",
|
||||
"description": "Update a Rocketlane project by ID, including name, dates, visibility, owner, status, and custom fields"
|
||||
},
|
||||
{
|
||||
"name": "Archive Project",
|
||||
"description": "Archive a Rocketlane project by ID, making it dormant while preserving its details and history"
|
||||
},
|
||||
{
|
||||
"name": "Delete Project",
|
||||
"description": "Permanently delete a Rocketlane project by ID (irreversible; only Admins, Super Users, and Project Owners can delete)"
|
||||
},
|
||||
{
|
||||
"name": "Add Project Members",
|
||||
"description": "Add team members and customer stakeholders to a Rocketlane project"
|
||||
},
|
||||
{
|
||||
"name": "Remove Project Members",
|
||||
"description": "Remove team members from a Rocketlane project"
|
||||
},
|
||||
{
|
||||
"name": "Import Template",
|
||||
"description": "Import a project template into an existing Rocketlane project"
|
||||
},
|
||||
{
|
||||
"name": "List Placeholders",
|
||||
"description": "List the placeholders of a Rocketlane project"
|
||||
},
|
||||
{
|
||||
"name": "Assign Placeholder",
|
||||
"description": "Assign a placeholder in a Rocketlane project to a user"
|
||||
},
|
||||
{
|
||||
"name": "Unassign Placeholder",
|
||||
"description": "Unassign a placeholder from its user in a Rocketlane project"
|
||||
},
|
||||
{
|
||||
"name": "Create Task",
|
||||
"description": "Create a new task in a Rocketlane project"
|
||||
},
|
||||
{
|
||||
"name": "Get Task",
|
||||
"description": "Retrieve detailed information about a Rocketlane task by its unique identifier"
|
||||
},
|
||||
{
|
||||
"name": "List Tasks",
|
||||
"description": "Retrieve all Rocketlane tasks with optional filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Update Task",
|
||||
"description": "Update the properties of an existing Rocketlane task by its unique identifier"
|
||||
},
|
||||
{
|
||||
"name": "Delete Task",
|
||||
"description": "Permanently delete a Rocketlane task by its unique identifier"
|
||||
},
|
||||
{
|
||||
"name": "Move Task to Phase",
|
||||
"description": "Move a Rocketlane task to a given phase, associating the task with that phase"
|
||||
},
|
||||
{
|
||||
"name": "Add Task Assignees",
|
||||
"description": "Add members as assignees to a Rocketlane task"
|
||||
},
|
||||
{
|
||||
"name": "Remove Task Assignees",
|
||||
"description": "Remove assignees from a Rocketlane task"
|
||||
},
|
||||
{
|
||||
"name": "Add Task Followers",
|
||||
"description": "Add members as followers to a Rocketlane task"
|
||||
},
|
||||
{
|
||||
"name": "Remove Task Followers",
|
||||
"description": "Remove followers from a Rocketlane task"
|
||||
},
|
||||
{
|
||||
"name": "Add Task Dependencies",
|
||||
"description": "Add finish-to-start dependencies between a Rocketlane task and other tasks"
|
||||
},
|
||||
{
|
||||
"name": "Remove Task Dependencies",
|
||||
"description": "Remove dependencies from a Rocketlane task"
|
||||
},
|
||||
{
|
||||
"name": "Create Phase",
|
||||
"description": "Create a new phase in a Rocketlane project"
|
||||
},
|
||||
{
|
||||
"name": "Get Phase",
|
||||
"description": "Retrieve a single Rocketlane phase by ID"
|
||||
},
|
||||
{
|
||||
"name": "List Phases",
|
||||
"description": "List phases of a Rocketlane project, with optional filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Update Phase",
|
||||
"description": "Update the name, dates, status, or privacy of an existing Rocketlane phase"
|
||||
},
|
||||
{
|
||||
"name": "Delete Phase",
|
||||
"description": "Permanently delete a Rocketlane phase by ID"
|
||||
},
|
||||
{
|
||||
"name": "Create Field",
|
||||
"description": "Create a custom field in your Rocketlane account, with options for SINGLE_CHOICE and MULTIPLE_CHOICE field types"
|
||||
},
|
||||
{
|
||||
"name": "Get Field",
|
||||
"description": "Retrieve a single Rocketlane field by ID"
|
||||
},
|
||||
{
|
||||
"name": "List Fields",
|
||||
"description": "List fields in your Rocketlane account, with optional filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Update Field",
|
||||
"description": "Update the label, description, enabled state, or privacy of an existing Rocketlane field"
|
||||
},
|
||||
{
|
||||
"name": "Delete Field",
|
||||
"description": "Permanently delete a Rocketlane field by ID"
|
||||
},
|
||||
{
|
||||
"name": "Add Field Option",
|
||||
"description": "Add a new option to an existing SINGLE_CHOICE or MULTIPLE_CHOICE Rocketlane field"
|
||||
},
|
||||
{
|
||||
"name": "Update Field Option",
|
||||
"description": "Update the label or color of an existing option on a SINGLE_CHOICE or MULTIPLE_CHOICE Rocketlane field"
|
||||
},
|
||||
{
|
||||
"name": "Create Time Entry",
|
||||
"description": "Create a time entry in Rocketlane against an adhoc activity, task, project phase, or project"
|
||||
},
|
||||
{
|
||||
"name": "Get Time Entry",
|
||||
"description": "Get a single Rocketlane time entry by ID"
|
||||
},
|
||||
{
|
||||
"name": "List Time Entries",
|
||||
"description": "List Rocketlane time entries with optional filters, sorting, and cursor-based pagination"
|
||||
},
|
||||
{
|
||||
"name": "Search Time Entries",
|
||||
"description": "Search Rocketlane time entries with filters, sorting, and pagination (deprecated by Rocketlane in favor of listing time entries with filters)"
|
||||
},
|
||||
{
|
||||
"name": "Update Time Entry",
|
||||
"description": "Update a Rocketlane time entry by ID. The activityName, notes, billable, and minutes properties can be updated"
|
||||
},
|
||||
{
|
||||
"name": "Delete Time Entry",
|
||||
"description": "Permanently delete a Rocketlane time entry by ID"
|
||||
},
|
||||
{
|
||||
"name": "List Time Entry Categories",
|
||||
"description": "List the time entry categories configured in Rocketlane"
|
||||
},
|
||||
{
|
||||
"name": "Create Time-Off",
|
||||
"description": "Create a time-off for a team member in Rocketlane. Holidays and weekends within the date range are automatically excluded from the duration."
|
||||
},
|
||||
{
|
||||
"name": "Get Time-Off",
|
||||
"description": "Retrieve a Rocketlane time-off by its ID"
|
||||
},
|
||||
{
|
||||
"name": "List Time-Offs",
|
||||
"description": "List time-offs in Rocketlane with optional date, type, and user filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Delete Time-Off",
|
||||
"description": "Permanently delete a Rocketlane time-off by its ID"
|
||||
},
|
||||
{
|
||||
"name": "Get User",
|
||||
"description": "Retrieve a Rocketlane user by their ID"
|
||||
},
|
||||
{
|
||||
"name": "List Users",
|
||||
"description": "List users in your Rocketlane account, with optional filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Create Space",
|
||||
"description": "Create a new space in a Rocketlane project"
|
||||
},
|
||||
{
|
||||
"name": "Get Space",
|
||||
"description": "Retrieve a Rocketlane space by its ID"
|
||||
},
|
||||
{
|
||||
"name": "List Spaces",
|
||||
"description": "List spaces in a Rocketlane project, with optional filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Update Space",
|
||||
"description": "Update a Rocketlane space by its ID"
|
||||
},
|
||||
{
|
||||
"name": "Delete Space",
|
||||
"description": "Permanently delete a Rocketlane space by its ID"
|
||||
},
|
||||
{
|
||||
"name": "Create Space Document",
|
||||
"description": "Create a new space document in a Rocketlane space"
|
||||
},
|
||||
{
|
||||
"name": "Get Space Document",
|
||||
"description": "Retrieve a Rocketlane space document by its ID"
|
||||
},
|
||||
{
|
||||
"name": "List Space Documents",
|
||||
"description": "List space documents in a Rocketlane project, with optional filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Update Space Document",
|
||||
"description": "Update a Rocketlane space document by its ID"
|
||||
},
|
||||
{
|
||||
"name": "Delete Space Document",
|
||||
"description": "Permanently delete a Rocketlane space document by its ID"
|
||||
},
|
||||
{
|
||||
"name": "List Resource Allocations",
|
||||
"description": "List resource allocations in Rocketlane within a date range, with optional member, project, and placeholder filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Get Invoice",
|
||||
"description": "Retrieve a Rocketlane invoice by its ID"
|
||||
},
|
||||
{
|
||||
"name": "List Invoices",
|
||||
"description": "Search invoices in Rocketlane with date, amount, company, invoice-number, and status filters, sorting, and pagination"
|
||||
},
|
||||
{
|
||||
"name": "Get Invoice Line Items",
|
||||
"description": "List line items of a Rocketlane invoice"
|
||||
},
|
||||
{
|
||||
"name": "Get Invoice Payments",
|
||||
"description": "List payments recorded against a Rocketlane invoice"
|
||||
}
|
||||
],
|
||||
"operationCount": 64,
|
||||
"triggers": [],
|
||||
"triggerCount": 0,
|
||||
"authType": "api-key",
|
||||
"category": "tools",
|
||||
"integrationType": "productivity",
|
||||
"tags": ["project-management", "automation"]
|
||||
},
|
||||
{
|
||||
"type": "rootly",
|
||||
"slug": "rootly",
|
||||
|
||||
@@ -3164,6 +3164,72 @@ import {
|
||||
ripplingUpdateTitleTool,
|
||||
ripplingUpdateWorkLocationTool,
|
||||
} from '@/tools/rippling'
|
||||
import {
|
||||
rocketlaneAddFieldOptionTool,
|
||||
rocketlaneAddProjectMembersTool,
|
||||
rocketlaneAddTaskAssigneesTool,
|
||||
rocketlaneAddTaskDependenciesTool,
|
||||
rocketlaneAddTaskFollowersTool,
|
||||
rocketlaneArchiveProjectTool,
|
||||
rocketlaneAssignPlaceholdersTool,
|
||||
rocketlaneCreateFieldTool,
|
||||
rocketlaneCreatePhaseTool,
|
||||
rocketlaneCreateProjectTool,
|
||||
rocketlaneCreateSpaceDocumentTool,
|
||||
rocketlaneCreateSpaceTool,
|
||||
rocketlaneCreateTaskTool,
|
||||
rocketlaneCreateTimeEntryTool,
|
||||
rocketlaneCreateTimeOffTool,
|
||||
rocketlaneDeleteFieldTool,
|
||||
rocketlaneDeletePhaseTool,
|
||||
rocketlaneDeleteProjectTool,
|
||||
rocketlaneDeleteSpaceDocumentTool,
|
||||
rocketlaneDeleteSpaceTool,
|
||||
rocketlaneDeleteTaskTool,
|
||||
rocketlaneDeleteTimeEntryTool,
|
||||
rocketlaneDeleteTimeOffTool,
|
||||
rocketlaneGetFieldTool,
|
||||
rocketlaneGetInvoiceLineItemsTool,
|
||||
rocketlaneGetInvoicePaymentsTool,
|
||||
rocketlaneGetInvoiceTool,
|
||||
rocketlaneGetPhaseTool,
|
||||
rocketlaneGetProjectTool,
|
||||
rocketlaneGetSpaceDocumentTool,
|
||||
rocketlaneGetSpaceTool,
|
||||
rocketlaneGetTaskTool,
|
||||
rocketlaneGetTimeEntryTool,
|
||||
rocketlaneGetTimeOffTool,
|
||||
rocketlaneGetUserTool,
|
||||
rocketlaneImportTemplateTool,
|
||||
rocketlaneListFieldsTool,
|
||||
rocketlaneListInvoicesTool,
|
||||
rocketlaneListPhasesTool,
|
||||
rocketlaneListPlaceholdersTool,
|
||||
rocketlaneListProjectsTool,
|
||||
rocketlaneListResourceAllocationsTool,
|
||||
rocketlaneListSpaceDocumentsTool,
|
||||
rocketlaneListSpacesTool,
|
||||
rocketlaneListTasksTool,
|
||||
rocketlaneListTimeEntriesTool,
|
||||
rocketlaneListTimeEntryCategoriesTool,
|
||||
rocketlaneListTimeOffsTool,
|
||||
rocketlaneListUsersTool,
|
||||
rocketlaneMoveTaskToPhaseTool,
|
||||
rocketlaneRemoveProjectMembersTool,
|
||||
rocketlaneRemoveTaskAssigneesTool,
|
||||
rocketlaneRemoveTaskDependenciesTool,
|
||||
rocketlaneRemoveTaskFollowersTool,
|
||||
rocketlaneSearchTimeEntriesTool,
|
||||
rocketlaneUnassignPlaceholdersTool,
|
||||
rocketlaneUpdateFieldOptionTool,
|
||||
rocketlaneUpdateFieldTool,
|
||||
rocketlaneUpdatePhaseTool,
|
||||
rocketlaneUpdateProjectTool,
|
||||
rocketlaneUpdateSpaceDocumentTool,
|
||||
rocketlaneUpdateSpaceTool,
|
||||
rocketlaneUpdateTaskTool,
|
||||
rocketlaneUpdateTimeEntryTool,
|
||||
} from '@/tools/rocketlane'
|
||||
import {
|
||||
rootlyAcknowledgeAlertTool,
|
||||
rootlyAddIncidentEventTool,
|
||||
@@ -6569,6 +6635,70 @@ export const tools: Record<string, ToolConfig> = {
|
||||
rippling_update_supergroup_inclusion_members: ripplingUpdateSupergroupInclusionMembersTool,
|
||||
rippling_update_title: ripplingUpdateTitleTool,
|
||||
rippling_update_work_location: ripplingUpdateWorkLocationTool,
|
||||
rocketlane_add_field_option: rocketlaneAddFieldOptionTool,
|
||||
rocketlane_add_project_members: rocketlaneAddProjectMembersTool,
|
||||
rocketlane_add_task_assignees: rocketlaneAddTaskAssigneesTool,
|
||||
rocketlane_add_task_dependencies: rocketlaneAddTaskDependenciesTool,
|
||||
rocketlane_add_task_followers: rocketlaneAddTaskFollowersTool,
|
||||
rocketlane_archive_project: rocketlaneArchiveProjectTool,
|
||||
rocketlane_assign_placeholders: rocketlaneAssignPlaceholdersTool,
|
||||
rocketlane_create_field: rocketlaneCreateFieldTool,
|
||||
rocketlane_create_phase: rocketlaneCreatePhaseTool,
|
||||
rocketlane_create_project: rocketlaneCreateProjectTool,
|
||||
rocketlane_create_space: rocketlaneCreateSpaceTool,
|
||||
rocketlane_create_space_document: rocketlaneCreateSpaceDocumentTool,
|
||||
rocketlane_create_task: rocketlaneCreateTaskTool,
|
||||
rocketlane_create_time_entry: rocketlaneCreateTimeEntryTool,
|
||||
rocketlane_create_time_off: rocketlaneCreateTimeOffTool,
|
||||
rocketlane_delete_field: rocketlaneDeleteFieldTool,
|
||||
rocketlane_delete_phase: rocketlaneDeletePhaseTool,
|
||||
rocketlane_delete_project: rocketlaneDeleteProjectTool,
|
||||
rocketlane_delete_space: rocketlaneDeleteSpaceTool,
|
||||
rocketlane_delete_space_document: rocketlaneDeleteSpaceDocumentTool,
|
||||
rocketlane_delete_task: rocketlaneDeleteTaskTool,
|
||||
rocketlane_delete_time_entry: rocketlaneDeleteTimeEntryTool,
|
||||
rocketlane_delete_time_off: rocketlaneDeleteTimeOffTool,
|
||||
rocketlane_get_field: rocketlaneGetFieldTool,
|
||||
rocketlane_get_invoice: rocketlaneGetInvoiceTool,
|
||||
rocketlane_get_invoice_line_items: rocketlaneGetInvoiceLineItemsTool,
|
||||
rocketlane_get_invoice_payments: rocketlaneGetInvoicePaymentsTool,
|
||||
rocketlane_get_phase: rocketlaneGetPhaseTool,
|
||||
rocketlane_get_project: rocketlaneGetProjectTool,
|
||||
rocketlane_get_space: rocketlaneGetSpaceTool,
|
||||
rocketlane_get_space_document: rocketlaneGetSpaceDocumentTool,
|
||||
rocketlane_get_task: rocketlaneGetTaskTool,
|
||||
rocketlane_get_time_entry: rocketlaneGetTimeEntryTool,
|
||||
rocketlane_get_time_off: rocketlaneGetTimeOffTool,
|
||||
rocketlane_get_user: rocketlaneGetUserTool,
|
||||
rocketlane_import_template: rocketlaneImportTemplateTool,
|
||||
rocketlane_list_fields: rocketlaneListFieldsTool,
|
||||
rocketlane_list_invoices: rocketlaneListInvoicesTool,
|
||||
rocketlane_list_phases: rocketlaneListPhasesTool,
|
||||
rocketlane_list_placeholders: rocketlaneListPlaceholdersTool,
|
||||
rocketlane_list_projects: rocketlaneListProjectsTool,
|
||||
rocketlane_list_resource_allocations: rocketlaneListResourceAllocationsTool,
|
||||
rocketlane_list_space_documents: rocketlaneListSpaceDocumentsTool,
|
||||
rocketlane_list_spaces: rocketlaneListSpacesTool,
|
||||
rocketlane_list_tasks: rocketlaneListTasksTool,
|
||||
rocketlane_list_time_entries: rocketlaneListTimeEntriesTool,
|
||||
rocketlane_list_time_entry_categories: rocketlaneListTimeEntryCategoriesTool,
|
||||
rocketlane_list_time_offs: rocketlaneListTimeOffsTool,
|
||||
rocketlane_list_users: rocketlaneListUsersTool,
|
||||
rocketlane_move_task_to_phase: rocketlaneMoveTaskToPhaseTool,
|
||||
rocketlane_remove_project_members: rocketlaneRemoveProjectMembersTool,
|
||||
rocketlane_remove_task_assignees: rocketlaneRemoveTaskAssigneesTool,
|
||||
rocketlane_remove_task_dependencies: rocketlaneRemoveTaskDependenciesTool,
|
||||
rocketlane_remove_task_followers: rocketlaneRemoveTaskFollowersTool,
|
||||
rocketlane_search_time_entries: rocketlaneSearchTimeEntriesTool,
|
||||
rocketlane_unassign_placeholders: rocketlaneUnassignPlaceholdersTool,
|
||||
rocketlane_update_field: rocketlaneUpdateFieldTool,
|
||||
rocketlane_update_field_option: rocketlaneUpdateFieldOptionTool,
|
||||
rocketlane_update_phase: rocketlaneUpdatePhaseTool,
|
||||
rocketlane_update_project: rocketlaneUpdateProjectTool,
|
||||
rocketlane_update_space: rocketlaneUpdateSpaceTool,
|
||||
rocketlane_update_space_document: rocketlaneUpdateSpaceDocumentTool,
|
||||
rocketlane_update_task: rocketlaneUpdateTaskTool,
|
||||
rocketlane_update_time_entry: rocketlaneUpdateTimeEntryTool,
|
||||
rootly_acknowledge_alert: rootlyAcknowledgeAlertTool,
|
||||
rootly_add_incident_event: rootlyAddIncidentEventTool,
|
||||
rootly_add_subscribers: rootlyAddSubscribersTool,
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import {
|
||||
FIELD_OPTION_OUTPUT_PROPERTIES,
|
||||
mapFieldOption,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneAddFieldOptionParams,
|
||||
type RocketlaneFieldOptionResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneAddFieldOptionTool: ToolConfig<
|
||||
RocketlaneAddFieldOptionParams,
|
||||
RocketlaneFieldOptionResponse
|
||||
> = {
|
||||
id: 'rocketlane_add_field_option',
|
||||
name: 'Rocketlane Add Field Option',
|
||||
description: 'Add a new option to an existing SINGLE_CHOICE or MULTIPLE_CHOICE Rocketlane field',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
fieldId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the field to add the option to',
|
||||
},
|
||||
optionLabel: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Display label of the new option',
|
||||
},
|
||||
optionColor: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Color of the new option (RED, YELLOW, GREEN, TEAL, CYAN, BLUE, PURPLE, MAGENTA, GRAY, COOL_GRAY)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/fields/${encodeURIComponent(String(params.fieldId))}/add-option`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
optionLabel: params.optionLabel,
|
||||
optionColor: params.optionColor,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { option: mapFieldOption(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
option: {
|
||||
type: 'object',
|
||||
description: 'The created field option',
|
||||
properties: FIELD_OPTION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import {
|
||||
mapProject,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneAddProjectMembersParams,
|
||||
type RocketlaneProjectResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneAddProjectMembersTool: ToolConfig<
|
||||
RocketlaneAddProjectMembersParams,
|
||||
RocketlaneProjectResponse
|
||||
> = {
|
||||
id: 'rocketlane_add_project_members',
|
||||
name: 'Rocketlane Add Project Members',
|
||||
description: 'Add team members and customer stakeholders to a Rocketlane project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of team members from your organization to add',
|
||||
items: { type: 'number', description: 'User ID of a team member' },
|
||||
},
|
||||
memberEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Emails of team members from your organization to add',
|
||||
items: { type: 'string', description: 'Email of a team member' },
|
||||
},
|
||||
customerUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of customer stakeholders to add',
|
||||
items: { type: 'number', description: 'User ID of a customer' },
|
||||
},
|
||||
customerEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Emails of customer stakeholders to add',
|
||||
items: { type: 'string', description: 'Email of a customer' },
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}/add-members`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
const members = [
|
||||
...(params.memberUserIds ?? []).map((userId) => ({ userId })),
|
||||
...(params.memberEmailIds ?? []).map((emailId) => ({ emailId })),
|
||||
]
|
||||
if (members.length > 0) body.members = members
|
||||
const customers = [
|
||||
...(params.customerUserIds ?? []).map((userId) => ({ userId })),
|
||||
...(params.customerEmailIds ?? []).map((emailId) => ({ emailId })),
|
||||
]
|
||||
if (customers.length > 0) body.customers = customers
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { project: mapProject(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The project with its updated team members',
|
||||
properties: PROJECT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
buildTaskMembers,
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTaskAssigneesParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneAddTaskAssigneesTool: ToolConfig<
|
||||
RocketlaneTaskAssigneesParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_add_task_assignees',
|
||||
name: 'Rocketlane Add Task Assignees',
|
||||
description: 'Add members as assignees to a Rocketlane task',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to add assignees to',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of members to add as assignees',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
memberEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email addresses of members to add as assignees',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}/add-assignees`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
members: buildTaskMembers(params.memberUserIds, params.memberEmailIds),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The task with its updated assignees',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTaskDependenciesParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneAddTaskDependenciesTool: ToolConfig<
|
||||
RocketlaneTaskDependenciesParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_add_task_dependencies',
|
||||
name: 'Rocketlane Add Task Dependencies',
|
||||
description: 'Add finish-to-start dependencies between a Rocketlane task and other tasks',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to add dependencies to',
|
||||
},
|
||||
dependencyTaskIds: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task IDs the task should depend on',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}/add-dependencies`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
dependencies: params.dependencyTaskIds.map((taskId) => ({ taskId })),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The task with its updated dependencies',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
buildTaskMembers,
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTaskFollowersParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneAddTaskFollowersTool: ToolConfig<
|
||||
RocketlaneTaskFollowersParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_add_task_followers',
|
||||
name: 'Rocketlane Add Task Followers',
|
||||
description: 'Add members as followers to a Rocketlane task',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to add followers to',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of members to add as followers',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
memberEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email addresses of members to add as followers',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}/add-followers`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
members: buildTaskMembers(params.memberUserIds, params.memberEmailIds),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The task with its updated followers',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneArchiveProjectParams,
|
||||
type RocketlaneProjectArchiveResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneArchiveProjectTool: ToolConfig<
|
||||
RocketlaneArchiveProjectParams,
|
||||
RocketlaneProjectArchiveResponse
|
||||
> = {
|
||||
id: 'rocketlane_archive_project',
|
||||
name: 'Rocketlane Archive Project',
|
||||
description:
|
||||
'Archive a Rocketlane project by ID, making it dormant while preserving its details and history',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project to archive',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}/archive`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneArchiveProjectParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { archived: true, projectId: params?.projectId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
archived: { type: 'boolean', description: 'Whether the project was archived' },
|
||||
projectId: {
|
||||
type: 'number',
|
||||
description: 'Unique identifier of the archived project',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import {
|
||||
mapPlaceholderMapping,
|
||||
mapProject,
|
||||
PLACEHOLDER_MAPPING_OUTPUT_PROPERTIES,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneAssignPlaceholdersParams,
|
||||
type RocketlaneProjectPlaceholdersResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneAssignPlaceholdersTool: ToolConfig<
|
||||
RocketlaneAssignPlaceholdersParams,
|
||||
RocketlaneProjectPlaceholdersResponse
|
||||
> = {
|
||||
id: 'rocketlane_assign_placeholders',
|
||||
name: 'Rocketlane Assign Placeholders',
|
||||
description: 'Assign a placeholder in a Rocketlane project to a user',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project',
|
||||
},
|
||||
placeholderId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the placeholder to assign',
|
||||
},
|
||||
userId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'User ID of the project member to assign (either userId or userEmailId must be provided; must be a customer user for CUSTOMER placeholders)',
|
||||
},
|
||||
userEmailId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Email of the project member to assign (either userId or userEmailId must be provided)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}/assign-placeholders`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
if (params.userId == null && !params.userEmailId) {
|
||||
throw new Error('Either a user ID or a user email is required to assign a placeholder')
|
||||
}
|
||||
const user: Record<string, unknown> = {}
|
||||
if (params.userId != null) user.userId = params.userId
|
||||
if (params.userEmailId) user.emailId = params.userEmailId
|
||||
return [{ placeholderId: params.placeholderId, user }]
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
project: mapProject(data),
|
||||
placeholders: Array.isArray(data?.placeholders)
|
||||
? data.placeholders.map(mapPlaceholderMapping)
|
||||
: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The project after the placeholder assignment',
|
||||
properties: PROJECT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
placeholders: {
|
||||
type: 'array',
|
||||
description: 'Placeholder-to-user mappings on the project',
|
||||
items: { type: 'object', properties: PLACEHOLDER_MAPPING_OUTPUT_PROPERTIES },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
import {
|
||||
FIELD_OUTPUT_PROPERTIES,
|
||||
mapField,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneCreateFieldParams,
|
||||
type RocketlaneFieldResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneCreateFieldTool: ToolConfig<
|
||||
RocketlaneCreateFieldParams,
|
||||
RocketlaneFieldResponse
|
||||
> = {
|
||||
id: 'rocketlane_create_field',
|
||||
name: 'Rocketlane Create Field',
|
||||
description:
|
||||
'Create a custom field in your Rocketlane account, with options for SINGLE_CHOICE and MULTIPLE_CHOICE field types',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
fieldLabel: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the field',
|
||||
},
|
||||
fieldType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Type of the field (TEXT, MULTI_LINE_TEXT, YES_OR_NO, DATE, SINGLE_CHOICE, MULTIPLE_CHOICE, SINGLE_USER, MULTIPLE_USER, NUMBER, NOTE, RATING)',
|
||||
},
|
||||
objectType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Object the field is associated with (PROJECT, TASK, or USER)',
|
||||
},
|
||||
fieldDescription: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of the field',
|
||||
},
|
||||
fieldOptions: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Options for SINGLE_CHOICE and MULTIPLE_CHOICE fields; the order provided is preserved',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
optionLabel: { type: 'string', description: 'Display label of the option' },
|
||||
optionColor: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Color of the option (RED, YELLOW, GREEN, TEAL, CYAN, BLUE, PURPLE, MAGENTA, GRAY, COOL_GRAY)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ratingScale: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of stars for RATING fields (THREE, FIVE, SEVEN, TEN)',
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the field is enabled (defaults to true)',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the field is private',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra field properties to include in the response (supported: options)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return all field properties in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/fields`)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
fieldLabel: params.fieldLabel,
|
||||
fieldType: params.fieldType,
|
||||
objectType: params.objectType,
|
||||
...(params.fieldDescription != null && { fieldDescription: params.fieldDescription }),
|
||||
...(params.fieldOptions != null && { fieldOptions: params.fieldOptions }),
|
||||
...(params.ratingScale != null && { ratingScale: params.ratingScale }),
|
||||
...(params.enabled != null && { enabled: params.enabled }),
|
||||
...(params.private != null && { private: params.private }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { field: mapField(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
field: {
|
||||
type: 'object',
|
||||
description: 'The created field',
|
||||
properties: FIELD_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import {
|
||||
mapPhase,
|
||||
PHASE_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneCreatePhaseParams,
|
||||
type RocketlanePhaseResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneCreatePhaseTool: ToolConfig<
|
||||
RocketlaneCreatePhaseParams,
|
||||
RocketlanePhaseResponse
|
||||
> = {
|
||||
id: 'rocketlane_create_phase',
|
||||
name: 'Rocketlane Create Phase',
|
||||
description: 'Create a new phase in a Rocketlane project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
phaseName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the phase',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the project to create the phase in',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Planned start date of the phase (YYYY-MM-DD)',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Planned due date of the phase (YYYY-MM-DD)',
|
||||
},
|
||||
statusValue: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Numeric status value to set on the phase',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the phase is private',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra phase properties to include in the response (supported: startDateActual, dueDateActual)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return all phase properties in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/phases`)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
phaseName: params.phaseName,
|
||||
project: { projectId: params.projectId },
|
||||
startDate: params.startDate,
|
||||
dueDate: params.dueDate,
|
||||
...(params.statusValue != null && { status: { value: params.statusValue } }),
|
||||
...(params.private != null && { private: params.private }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { phase: mapPhase(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
phase: {
|
||||
type: 'object',
|
||||
description: 'The created phase',
|
||||
properties: PHASE_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
import {
|
||||
mapProject,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneCreateProjectParams,
|
||||
type RocketlaneProjectResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneCreateProjectTool: ToolConfig<
|
||||
RocketlaneCreateProjectParams,
|
||||
RocketlaneProjectResponse
|
||||
> = {
|
||||
id: 'rocketlane_create_project',
|
||||
name: 'Rocketlane Create Project',
|
||||
description:
|
||||
'Create a new Rocketlane project with a customer, owner, dates, team members, templates, financials, and custom fields',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the project',
|
||||
},
|
||||
customerCompanyName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Name of the customer company (case-sensitive exact match; cannot be changed after creation)',
|
||||
},
|
||||
ownerUserId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'User ID of the project owner (either ownerUserId or ownerEmailId must be provided)',
|
||||
},
|
||||
ownerEmailId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Email of the project owner (either ownerUserId or ownerEmailId must be provided)',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Date on which the project begins (YYYY-MM-DD); required when sources are provided',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Date on which the project is planned to complete (YYYY-MM-DD, on or after startDate)',
|
||||
},
|
||||
visibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Who can see the project: EVERYONE or MEMBERS',
|
||||
},
|
||||
statusValue: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Value (identifier) of the project status',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of team members from your organization to add to the project',
|
||||
items: { type: 'number', description: 'User ID of a team member' },
|
||||
},
|
||||
customerUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of customer stakeholders to add to the project',
|
||||
items: { type: 'number', description: 'User ID of a customer' },
|
||||
},
|
||||
customerChampionUserId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User ID of the customer champion',
|
||||
},
|
||||
fields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Custom field assignments, each with fieldId and fieldValue (string, number, or number array matching the field type)',
|
||||
items: {
|
||||
type: 'object',
|
||||
description: 'Custom field assignment',
|
||||
properties: {
|
||||
fieldId: { type: 'number', description: 'Unique identifier of the field' },
|
||||
fieldValue: {
|
||||
type: 'string',
|
||||
description: 'Value of the field (string, number, or number array)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sources: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Project templates to import at creation, each with templateId, startDate (YYYY-MM-DD), and optional prefix',
|
||||
items: {
|
||||
type: 'object',
|
||||
description: 'Template source',
|
||||
properties: {
|
||||
templateId: { type: 'number', description: 'Unique identifier of the template' },
|
||||
startDate: { type: 'string', description: 'Date the template takes effect (YYYY-MM-DD)' },
|
||||
prefix: { type: 'string', description: 'Prefix distinguishing this template' },
|
||||
},
|
||||
},
|
||||
},
|
||||
placeholders: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Placeholder-to-user mappings, each with placeholderId and user ({ userId } or { emailId }); ignored unless the project is built from sources',
|
||||
items: {
|
||||
type: 'object',
|
||||
description: 'Placeholder mapping',
|
||||
properties: {
|
||||
placeholderId: { type: 'number', description: 'Unique identifier of the placeholder' },
|
||||
user: { type: 'object', description: 'User to assign ({ userId } or { emailId })' },
|
||||
},
|
||||
},
|
||||
},
|
||||
assignProjectOwner: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Automatically assign unassigned tasks to the project owner (skipped when no sources are used)',
|
||||
},
|
||||
annualizedRecurringRevenue: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Recurring revenue of the customer subscriptions for a single calendar year',
|
||||
},
|
||||
projectFee: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Total fee charged for the project',
|
||||
},
|
||||
autoAllocation: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether auto allocation is enabled for the project',
|
||||
},
|
||||
autoCreateCompany: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Create the customer company if it does not already exist',
|
||||
},
|
||||
budgetedHours: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Total hours allocated for project execution (decimal, up to two places)',
|
||||
},
|
||||
contractType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Contract type for the project financials: FIXED_FEE, TIME_AND_MATERIAL, NON_BILLABLE, or SUBSCRIPTION',
|
||||
},
|
||||
fixedFee: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project fee for FIXED_FEE contract type projects',
|
||||
},
|
||||
projectBudget: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Project budget for TIME_AND_MATERIAL contract type projects',
|
||||
},
|
||||
rateCardId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Rate card ID for TIME_AND_MATERIAL contract type projects',
|
||||
},
|
||||
subscriptionFrequency: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Subscription renewal interval for SUBSCRIPTION contracts: MONTHLY, QUARTERLY, HALF_YEARLY, or YEARLY',
|
||||
},
|
||||
subscriptionStartDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date when the subscription interval begins (YYYY-MM-DD)',
|
||||
},
|
||||
periodMinutes: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Budgeted minutes for each subscription period',
|
||||
},
|
||||
periodBudget: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Fixed budget of every subscription period',
|
||||
},
|
||||
noOfPeriods: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of periods in the subscription',
|
||||
},
|
||||
currency: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Currency for the project financials (ISO code, e.g. USD); cannot be changed once set',
|
||||
},
|
||||
externalReferenceId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Identifier linking the project to an external system',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to return in the response (e.g. budgetedHours,progressPercentage)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response body',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/projects`)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null)
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
projectName: params.projectName,
|
||||
customer: { companyName: params.customerCompanyName },
|
||||
}
|
||||
if (params.ownerUserId == null && !params.ownerEmailId) {
|
||||
throw new Error('Either an owner user ID or an owner email is required to create a project')
|
||||
}
|
||||
const owner: Record<string, unknown> = {}
|
||||
if (params.ownerUserId != null) owner.userId = params.ownerUserId
|
||||
if (params.ownerEmailId) owner.emailId = params.ownerEmailId
|
||||
body.owner = owner
|
||||
if (params.startDate) body.startDate = params.startDate
|
||||
if (params.dueDate) body.dueDate = params.dueDate
|
||||
if (params.visibility) body.visibility = params.visibility
|
||||
if (params.statusValue != null) body.status = { value: params.statusValue }
|
||||
const teamMembers: Record<string, unknown> = {}
|
||||
if (params.memberUserIds && params.memberUserIds.length > 0) {
|
||||
teamMembers.members = params.memberUserIds.map((userId) => ({ userId }))
|
||||
}
|
||||
if (params.customerUserIds && params.customerUserIds.length > 0) {
|
||||
teamMembers.customers = params.customerUserIds.map((userId) => ({ userId }))
|
||||
}
|
||||
if (params.customerChampionUserId != null) {
|
||||
teamMembers.customerChampion = { userId: params.customerChampionUserId }
|
||||
}
|
||||
if (Object.keys(teamMembers).length > 0) body.teamMembers = teamMembers
|
||||
if (params.fields && params.fields.length > 0) body.fields = params.fields
|
||||
if (params.sources && params.sources.length > 0) body.sources = params.sources
|
||||
if (params.placeholders && params.placeholders.length > 0)
|
||||
body.placeholders = params.placeholders
|
||||
if (params.assignProjectOwner != null) body.assignProjectOwner = params.assignProjectOwner
|
||||
if (params.annualizedRecurringRevenue != null)
|
||||
body.annualizedRecurringRevenue = params.annualizedRecurringRevenue
|
||||
if (params.projectFee != null) body.projectFee = params.projectFee
|
||||
if (params.autoAllocation != null) body.autoAllocation = params.autoAllocation
|
||||
if (params.autoCreateCompany != null) body.autoCreateCompany = params.autoCreateCompany
|
||||
if (params.budgetedHours != null) body.budgetedHours = params.budgetedHours
|
||||
if (params.contractType) {
|
||||
const financials: Record<string, unknown> = { contractType: params.contractType }
|
||||
if (params.fixedFee != null) financials.fixedFeeContract = { fixedFee: params.fixedFee }
|
||||
const timeAndMaterialContract: Record<string, unknown> = {}
|
||||
if (params.rateCardId != null)
|
||||
timeAndMaterialContract.rateCard = { rateCardId: params.rateCardId }
|
||||
if (params.projectBudget != null)
|
||||
timeAndMaterialContract.projectBudget = params.projectBudget
|
||||
if (Object.keys(timeAndMaterialContract).length > 0)
|
||||
financials.timeAndMaterialContract = timeAndMaterialContract
|
||||
const subscriptionContract: Record<string, unknown> = {}
|
||||
if (params.subscriptionFrequency)
|
||||
subscriptionContract.subscriptionFrequency = params.subscriptionFrequency
|
||||
if (params.subscriptionStartDate)
|
||||
subscriptionContract.subscriptionStartDate = params.subscriptionStartDate
|
||||
if (params.periodMinutes != null) subscriptionContract.periodMinutes = params.periodMinutes
|
||||
if (params.periodBudget != null) subscriptionContract.periodBudget = params.periodBudget
|
||||
if (params.noOfPeriods != null) subscriptionContract.noOfPeriods = params.noOfPeriods
|
||||
if (Object.keys(subscriptionContract).length > 0)
|
||||
financials.subscriptionContract = subscriptionContract
|
||||
body.financials = financials
|
||||
}
|
||||
if (params.currency) body.currency = params.currency
|
||||
if (params.externalReferenceId) body.externalReferenceId = params.externalReferenceId
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { project: mapProject(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The created project',
|
||||
properties: PROJECT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
mapSpace,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneCreateSpaceParams,
|
||||
type RocketlaneSpaceResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
SPACE_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneCreateSpaceTool: ToolConfig<
|
||||
RocketlaneCreateSpaceParams,
|
||||
RocketlaneSpaceResponse
|
||||
> = {
|
||||
id: 'rocketlane_create_space',
|
||||
name: 'Rocketlane Create Space',
|
||||
description: 'Create a new space in a Rocketlane project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the project the space belongs to',
|
||||
},
|
||||
spaceName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the space',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the space is private or shared (defaults to false)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${ROCKETLANE_API_BASE}/spaces`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
spaceName: params.spaceName,
|
||||
project: { projectId: params.projectId },
|
||||
}
|
||||
if (params.private != null) body.private = params.private
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { space: mapSpace(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
space: {
|
||||
type: 'object',
|
||||
description: 'The created space',
|
||||
properties: SPACE_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import {
|
||||
mapSpaceDocument,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneCreateSpaceDocumentParams,
|
||||
type RocketlaneSpaceDocumentResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
SPACE_DOCUMENT_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneCreateSpaceDocumentTool: ToolConfig<
|
||||
RocketlaneCreateSpaceDocumentParams,
|
||||
RocketlaneSpaceDocumentResponse
|
||||
> = {
|
||||
id: 'rocketlane_create_space_document',
|
||||
name: 'Rocketlane Create Space Document',
|
||||
description: 'Create a new space document in a Rocketlane space',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space the document belongs to',
|
||||
},
|
||||
spaceDocumentType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Type of the space document: ROCKETLANE_DOCUMENT or EMBEDDED_DOCUMENT',
|
||||
},
|
||||
spaceDocumentName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the space document (defaults to Untitled)',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'URL to embed in the space document (for embedded documents)',
|
||||
},
|
||||
templateId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the document template to create the document from',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${ROCKETLANE_API_BASE}/space-documents`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
space: { spaceId: params.spaceId },
|
||||
spaceDocumentType: params.spaceDocumentType,
|
||||
}
|
||||
if (params.spaceDocumentName != null) body.spaceDocumentName = params.spaceDocumentName
|
||||
if (params.url != null) body.url = params.url
|
||||
if (params.templateId != null) body.source = { templateId: params.templateId }
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { spaceDocument: mapSpaceDocument(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
spaceDocument: {
|
||||
type: 'object',
|
||||
description: 'The created space document',
|
||||
properties: SPACE_DOCUMENT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
import {
|
||||
buildTaskMembers,
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneCreateTaskParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneCreateTaskTool: ToolConfig<
|
||||
RocketlaneCreateTaskParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_create_task',
|
||||
name: 'Rocketlane Create Task',
|
||||
description: 'Create a new task in a Rocketlane project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the task',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the project the task belongs to',
|
||||
},
|
||||
taskDescription: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of the task in HTML format',
|
||||
},
|
||||
taskPrivateNote: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Private note visible only to team members, in HTML format',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date when the task starts (YYYY-MM-DD)',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date when the task is due, on or after the start date (YYYY-MM-DD)',
|
||||
},
|
||||
effortInMinutes: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Expected effort to complete the task, in minutes',
|
||||
},
|
||||
progress: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Progress of the task (0-100)',
|
||||
},
|
||||
atRisk: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the task is marked as At Risk',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Type of the task: TASK or MILESTONE (defaults to TASK)',
|
||||
},
|
||||
phaseId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the phase to associate the task with (must belong to the project)',
|
||||
},
|
||||
statusValue: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Status value to set on the task',
|
||||
},
|
||||
assigneeUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of members to assign to the task',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
assigneeEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email addresses of members to assign to the task',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
followerUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of members to add as followers of the task',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
followerEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email addresses of members to add as followers of the task',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
parentTaskId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the parent task',
|
||||
},
|
||||
externalReferenceId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'External reference identifier linking the task to an external system',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the task is private',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Extra fields to include in the response (startDateActual, dueDateActual, type, phase, assignees, followers, dependencies, billable, csatEnabled, priority, timeEntryCategory, financialsBudget, taskPrivateNote, parent, externalReferenceId)',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether all fields should be returned in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/tasks`)
|
||||
if (params.includeFields && params.includeFields.length > 0) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields !== undefined) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
taskName: params.taskName,
|
||||
project: { projectId: params.projectId },
|
||||
}
|
||||
if (params.taskDescription !== undefined) body.taskDescription = params.taskDescription
|
||||
if (params.taskPrivateNote !== undefined) body.taskPrivateNote = params.taskPrivateNote
|
||||
if (params.startDate !== undefined) body.startDate = params.startDate
|
||||
if (params.dueDate !== undefined) body.dueDate = params.dueDate
|
||||
if (params.effortInMinutes !== undefined) body.effortInMinutes = params.effortInMinutes
|
||||
if (params.progress !== undefined) body.progress = params.progress
|
||||
if (params.atRisk !== undefined) body.atRisk = params.atRisk
|
||||
if (params.type !== undefined) body.type = params.type
|
||||
if (params.phaseId !== undefined) body.phase = { phaseId: params.phaseId }
|
||||
if (params.statusValue !== undefined) body.status = { value: params.statusValue }
|
||||
const assignees = buildTaskMembers(params.assigneeUserIds, params.assigneeEmailIds)
|
||||
if (assignees.length > 0) body.assignees = { members: assignees }
|
||||
const followers = buildTaskMembers(params.followerUserIds, params.followerEmailIds)
|
||||
if (followers.length > 0) body.followers = { members: followers }
|
||||
if (params.parentTaskId !== undefined) body.parent = { taskId: params.parentTaskId }
|
||||
if (params.externalReferenceId !== undefined) {
|
||||
body.externalReferenceId = params.externalReferenceId
|
||||
}
|
||||
if (params.private !== undefined) body.private = params.private
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The created task',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import {
|
||||
mapTimeEntry,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneCreateTimeEntryParams,
|
||||
type RocketlaneTimeEntryResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_ENTRY_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneCreateTimeEntryTool: ToolConfig<
|
||||
RocketlaneCreateTimeEntryParams,
|
||||
RocketlaneTimeEntryResponse
|
||||
> = {
|
||||
id: 'rocketlane_create_time_entry',
|
||||
name: 'Rocketlane Create Time Entry',
|
||||
description:
|
||||
'Create a time entry in Rocketlane against an adhoc activity, task, project phase, or project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date of the time entry in YYYY-MM-DD format',
|
||||
},
|
||||
minutes: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Duration of the time entry in minutes (1-1440)',
|
||||
},
|
||||
activityName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Name of the adhoc activity to track time against. Exactly one source is required among activityName, taskId, projectPhaseId, and projectId — providing more than one results in an error',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'ID of the task to track time against. Exactly one source is required among activityName, taskId, projectPhaseId, and projectId — providing more than one results in an error',
|
||||
},
|
||||
projectPhaseId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'ID of the project phase to track time against. Exactly one source is required among activityName, taskId, projectPhaseId, and projectId — providing more than one results in an error',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'ID of the project to track time against. Exactly one source is required among activityName, taskId, projectPhaseId, and projectId — providing more than one results in an error',
|
||||
},
|
||||
billable: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the time entry is billable (defaults to true)',
|
||||
},
|
||||
userId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the user the time entry belongs to',
|
||||
},
|
||||
userEmail: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email of the user the time entry belongs to (alternative to userId)',
|
||||
},
|
||||
notes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Notes for the time entry',
|
||||
},
|
||||
categoryId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the time entry category',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to include in the response (notes, sourceType, deleted, status, submittedBy, submittedAt, approvedBy, approvedAt, rejectedBy, rejectedAt, costRate, billRate)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether all fields should be returned in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/time-entries`)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
date: params.date,
|
||||
minutes: params.minutes,
|
||||
}
|
||||
if (params.activityName) body.activityName = params.activityName
|
||||
if (params.taskId != null) body.task = { taskId: params.taskId }
|
||||
if (params.projectPhaseId != null) body.projectPhase = { phaseId: params.projectPhaseId }
|
||||
if (params.projectId != null) body.project = { projectId: params.projectId }
|
||||
if (params.billable != null) body.billable = params.billable
|
||||
if (params.userId != null || params.userEmail) {
|
||||
const user: Record<string, unknown> = {}
|
||||
if (params.userId != null) user.userId = params.userId
|
||||
if (params.userEmail) user.emailId = params.userEmail
|
||||
body.user = user
|
||||
}
|
||||
if (params.notes) body.notes = params.notes
|
||||
if (params.categoryId != null) body.category = { categoryId: params.categoryId }
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { timeEntry: mapTimeEntry(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeEntry: {
|
||||
type: 'object',
|
||||
description: 'The created time entry',
|
||||
properties: TIME_ENTRY_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import {
|
||||
mapTimeOff,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTimeOffCreateParams,
|
||||
type RocketlaneTimeOffResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_OFF_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneCreateTimeOffTool: ToolConfig<
|
||||
RocketlaneTimeOffCreateParams,
|
||||
RocketlaneTimeOffResponse
|
||||
> = {
|
||||
id: 'rocketlane_create_time_off',
|
||||
name: 'Rocketlane Create Time-Off',
|
||||
description:
|
||||
'Create a time-off for a team member in Rocketlane. Holidays and weekends within the date range are automatically excluded from the duration.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
userId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'User ID of the team member taking the time-off (provide this or the user email)',
|
||||
},
|
||||
userEmail: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email of the team member taking the time-off (provide this or the user ID)',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Time-off start date in YYYY-MM-DD format',
|
||||
},
|
||||
endDate: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Time-off end date in YYYY-MM-DD format (on or after the start date)',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Type of the time-off: FULL_DAY, HALF_DAY, or CUSTOM',
|
||||
},
|
||||
durationInMinutes: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Duration in minutes per day; required when type is CUSTOM',
|
||||
},
|
||||
note: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Note or comment about the time-off',
|
||||
},
|
||||
notifyProjectOwners: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Notify project owners of projects the user is part of',
|
||||
},
|
||||
notifyUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of additional users to notify about the time-off',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
notifyUserEmails: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Emails of additional users to notify about the time-off',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional fields to include in the response: note, notifyUsers',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/time-offs`)
|
||||
if (params.includeFields?.length) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
if (params.userId == null && !params.userEmail) {
|
||||
throw new Error('Either a user ID or a user email is required to create a time-off')
|
||||
}
|
||||
const user: Record<string, unknown> = {}
|
||||
if (params.userId != null) user.userId = params.userId
|
||||
if (params.userEmail) user.emailId = params.userEmail
|
||||
const body: Record<string, unknown> = {
|
||||
user,
|
||||
startDate: params.startDate,
|
||||
endDate: params.endDate,
|
||||
type: params.type,
|
||||
}
|
||||
if (params.note) body.note = params.note
|
||||
if (params.durationInMinutes != null) body.durationInMinutes = params.durationInMinutes
|
||||
const others = [
|
||||
...(params.notifyUserIds ?? []).map((userId) => ({ userId })),
|
||||
...(params.notifyUserEmails ?? []).map((emailId) => ({ emailId })),
|
||||
]
|
||||
if (params.notifyProjectOwners != null || others.length > 0) {
|
||||
const notifyUsers: Record<string, unknown> = {}
|
||||
if (params.notifyProjectOwners != null) {
|
||||
notifyUsers.projectOwners = params.notifyProjectOwners
|
||||
}
|
||||
if (others.length > 0) notifyUsers.others = others
|
||||
body.notifyUsers = notifyUsers
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { timeOff: mapTimeOff(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeOff: {
|
||||
type: 'object',
|
||||
description: 'The created time-off',
|
||||
properties: TIME_OFF_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneDeleteFieldParams,
|
||||
type RocketlaneFieldDeleteResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneDeleteFieldTool: ToolConfig<
|
||||
RocketlaneDeleteFieldParams,
|
||||
RocketlaneFieldDeleteResponse
|
||||
> = {
|
||||
id: 'rocketlane_delete_field',
|
||||
name: 'Rocketlane Delete Field',
|
||||
description: 'Permanently delete a Rocketlane field by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
fieldId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the field to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${ROCKETLANE_API_BASE}/fields/${encodeURIComponent(String(params.fieldId))}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneDeleteFieldParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { deleted: true, fieldId: params?.fieldId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the field was deleted' },
|
||||
fieldId: { type: 'number', description: 'ID of the deleted field', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneDeletePhaseParams,
|
||||
type RocketlanePhaseDeleteResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneDeletePhaseTool: ToolConfig<
|
||||
RocketlaneDeletePhaseParams,
|
||||
RocketlanePhaseDeleteResponse
|
||||
> = {
|
||||
id: 'rocketlane_delete_phase',
|
||||
name: 'Rocketlane Delete Phase',
|
||||
description: 'Permanently delete a Rocketlane phase by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
phaseId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the phase to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${ROCKETLANE_API_BASE}/phases/${encodeURIComponent(String(params.phaseId))}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneDeletePhaseParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { deleted: true, phaseId: params?.phaseId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the phase was deleted' },
|
||||
phaseId: { type: 'number', description: 'ID of the deleted phase', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneDeleteProjectParams,
|
||||
type RocketlaneProjectDeleteResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneDeleteProjectTool: ToolConfig<
|
||||
RocketlaneDeleteProjectParams,
|
||||
RocketlaneProjectDeleteResponse
|
||||
> = {
|
||||
id: 'rocketlane_delete_project',
|
||||
name: 'Rocketlane Delete Project',
|
||||
description:
|
||||
'Permanently delete a Rocketlane project by ID (irreversible; only Admins, Super Users, and Project Owners can delete)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneDeleteProjectParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { deleted: true, projectId: params?.projectId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the project was deleted' },
|
||||
projectId: {
|
||||
type: 'number',
|
||||
description: 'Unique identifier of the deleted project',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneDeleteSpaceParams,
|
||||
type RocketlaneDeleteSpaceResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneDeleteSpaceTool: ToolConfig<
|
||||
RocketlaneDeleteSpaceParams,
|
||||
RocketlaneDeleteSpaceResponse
|
||||
> = {
|
||||
id: 'rocketlane_delete_space',
|
||||
name: 'Rocketlane Delete Space',
|
||||
description: 'Permanently delete a Rocketlane space by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${ROCKETLANE_API_BASE}/spaces/${encodeURIComponent(params.spaceId)}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneDeleteSpaceParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { deleted: true, spaceId: params?.spaceId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the space was deleted' },
|
||||
spaceId: { type: 'number', description: 'ID of the deleted space', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneDeleteSpaceDocumentParams,
|
||||
type RocketlaneDeleteSpaceDocumentResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneDeleteSpaceDocumentTool: ToolConfig<
|
||||
RocketlaneDeleteSpaceDocumentParams,
|
||||
RocketlaneDeleteSpaceDocumentResponse
|
||||
> = {
|
||||
id: 'rocketlane_delete_space_document',
|
||||
name: 'Rocketlane Delete Space Document',
|
||||
description: 'Permanently delete a Rocketlane space document by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
spaceDocumentId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space document to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/space-documents/${encodeURIComponent(params.spaceDocumentId)}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneDeleteSpaceDocumentParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { deleted: true, spaceDocumentId: params?.spaceDocumentId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the space document was deleted' },
|
||||
spaceDocumentId: {
|
||||
type: 'number',
|
||||
description: 'ID of the deleted space document',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneDeleteTaskParams,
|
||||
type RocketlaneTaskDeleteResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneDeleteTaskTool: ToolConfig<
|
||||
RocketlaneDeleteTaskParams,
|
||||
RocketlaneTaskDeleteResponse
|
||||
> = {
|
||||
id: 'rocketlane_delete_task',
|
||||
name: 'Rocketlane Delete Task',
|
||||
description: 'Permanently delete a Rocketlane task by its unique identifier',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneDeleteTaskParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { deleted: true, taskId: params?.taskId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the task was deleted' },
|
||||
taskId: { type: 'number', description: 'ID of the deleted task', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneDeleteTimeEntryParams,
|
||||
type RocketlaneDeleteTimeEntryResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneDeleteTimeEntryTool: ToolConfig<
|
||||
RocketlaneDeleteTimeEntryParams,
|
||||
RocketlaneDeleteTimeEntryResponse
|
||||
> = {
|
||||
id: 'rocketlane_delete_time_entry',
|
||||
name: 'Rocketlane Delete Time Entry',
|
||||
description: 'Permanently delete a Rocketlane time entry by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
timeEntryId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the time entry to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/time-entries/${encodeURIComponent(params.timeEntryId)}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneDeleteTimeEntryParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { deleted: true, timeEntryId: params?.timeEntryId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the time entry was deleted' },
|
||||
timeEntryId: {
|
||||
type: 'number',
|
||||
description: 'ID of the deleted time entry',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTimeOffDeleteParams,
|
||||
type RocketlaneTimeOffDeleteResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneDeleteTimeOffTool: ToolConfig<
|
||||
RocketlaneTimeOffDeleteParams,
|
||||
RocketlaneTimeOffDeleteResponse
|
||||
> = {
|
||||
id: 'rocketlane_delete_time_off',
|
||||
name: 'Rocketlane Delete Time-Off',
|
||||
description: 'Permanently delete a Rocketlane time-off by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
timeOffId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the time-off to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${ROCKETLANE_API_BASE}/time-offs/${encodeURIComponent(params.timeOffId)}`,
|
||||
method: 'DELETE',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: RocketlaneTimeOffDeleteParams) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: { deleted: true, timeOffId: params?.timeOffId ?? null },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
deleted: { type: 'boolean', description: 'Whether the time-off was deleted' },
|
||||
timeOffId: {
|
||||
type: 'number',
|
||||
description: 'ID of the deleted time-off',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
FIELD_OUTPUT_PROPERTIES,
|
||||
mapField,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneFieldResponse,
|
||||
type RocketlaneGetFieldParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetFieldTool: ToolConfig<RocketlaneGetFieldParams, RocketlaneFieldResponse> =
|
||||
{
|
||||
id: 'rocketlane_get_field',
|
||||
name: 'Rocketlane Get Field',
|
||||
description: 'Retrieve a single Rocketlane field by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
fieldId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the field to retrieve',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra field properties to include in the response (supported: options)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return all field properties in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/fields/${encodeURIComponent(String(params.fieldId))}`
|
||||
)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { field: mapField(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
field: {
|
||||
type: 'object',
|
||||
description: 'The requested field',
|
||||
properties: FIELD_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
INVOICE_OUTPUT_PROPERTIES,
|
||||
mapInvoice,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneInvoiceGetParams,
|
||||
type RocketlaneInvoiceResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetInvoiceTool: ToolConfig<
|
||||
RocketlaneInvoiceGetParams,
|
||||
RocketlaneInvoiceResponse
|
||||
> = {
|
||||
id: 'rocketlane_get_invoice',
|
||||
name: 'Rocketlane Get Invoice',
|
||||
description: 'Retrieve a Rocketlane invoice by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
invoiceId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the invoice',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional fields to include in the response: notes, attachments',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/invoices/${encodeURIComponent(params.invoiceId)}`)
|
||||
if (params.includeFields?.length) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { invoice: mapInvoice(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
invoice: {
|
||||
type: 'object',
|
||||
description: 'The requested invoice',
|
||||
properties: INVOICE_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import {
|
||||
INVOICE_LINE_ITEM_OUTPUT_PROPERTIES,
|
||||
mapInvoiceLineItem,
|
||||
mapPagination,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneInvoiceLineItemListResponse,
|
||||
type RocketlaneInvoiceLineItemsParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetInvoiceLineItemsTool: ToolConfig<
|
||||
RocketlaneInvoiceLineItemsParams,
|
||||
RocketlaneInvoiceLineItemListResponse
|
||||
> = {
|
||||
id: 'rocketlane_get_invoice_line_items',
|
||||
name: 'Rocketlane Get Invoice Line Items',
|
||||
description: 'List line items of a Rocketlane invoice',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
invoiceId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the invoice',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of line items per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response (valid for 15 minutes)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/invoices/${encodeURIComponent(params.invoiceId)}/lines`
|
||||
)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const lineItems = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
lineItems: lineItems.map(mapInvoiceLineItem),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
lineItems: {
|
||||
type: 'array',
|
||||
description: 'List of invoice line items',
|
||||
items: { type: 'object', properties: INVOICE_LINE_ITEM_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import {
|
||||
INVOICE_PAYMENT_OUTPUT_PROPERTIES,
|
||||
mapInvoicePayment,
|
||||
mapPagination,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneInvoicePaymentListResponse,
|
||||
type RocketlaneInvoicePaymentsParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetInvoicePaymentsTool: ToolConfig<
|
||||
RocketlaneInvoicePaymentsParams,
|
||||
RocketlaneInvoicePaymentListResponse
|
||||
> = {
|
||||
id: 'rocketlane_get_invoice_payments',
|
||||
name: 'Rocketlane Get Invoice Payments',
|
||||
description: 'List payments recorded against a Rocketlane invoice',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
invoiceId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the invoice',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of payments per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response (valid for 15 minutes)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/invoices/${encodeURIComponent(params.invoiceId)}/payments`
|
||||
)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const payments = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
payments: payments.map(mapInvoicePayment),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
payments: {
|
||||
type: 'array',
|
||||
description: 'List of payments recorded against the invoice',
|
||||
items: { type: 'object', properties: INVOICE_PAYMENT_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
mapPhase,
|
||||
PHASE_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneGetPhaseParams,
|
||||
type RocketlanePhaseResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetPhaseTool: ToolConfig<RocketlaneGetPhaseParams, RocketlanePhaseResponse> =
|
||||
{
|
||||
id: 'rocketlane_get_phase',
|
||||
name: 'Rocketlane Get Phase',
|
||||
description: 'Retrieve a single Rocketlane phase by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
phaseId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the phase to retrieve',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra phase properties to include in the response (supported: startDateActual, dueDateActual)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return all phase properties in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/phases/${encodeURIComponent(String(params.phaseId))}`
|
||||
)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { phase: mapPhase(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
phase: {
|
||||
type: 'object',
|
||||
description: 'The requested phase',
|
||||
properties: PHASE_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import {
|
||||
mapProject,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneGetProjectParams,
|
||||
type RocketlaneProjectResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetProjectTool: ToolConfig<
|
||||
RocketlaneGetProjectParams,
|
||||
RocketlaneProjectResponse
|
||||
> = {
|
||||
id: 'rocketlane_get_project',
|
||||
name: 'Rocketlane Get Project',
|
||||
description: 'Retrieve a Rocketlane project by its unique identifier',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to return in the response (e.g. budgetedHours,progressPercentage)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response body',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}`
|
||||
)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null)
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { project: mapProject(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The requested project',
|
||||
properties: PROJECT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
mapSpace,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneGetSpaceParams,
|
||||
type RocketlaneSpaceResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
SPACE_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetSpaceTool: ToolConfig<RocketlaneGetSpaceParams, RocketlaneSpaceResponse> =
|
||||
{
|
||||
id: 'rocketlane_get_space',
|
||||
name: 'Rocketlane Get Space',
|
||||
description: 'Retrieve a Rocketlane space by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${ROCKETLANE_API_BASE}/spaces/${encodeURIComponent(params.spaceId)}`,
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { space: mapSpace(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
space: {
|
||||
type: 'object',
|
||||
description: 'The requested space',
|
||||
properties: SPACE_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
mapSpaceDocument,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneGetSpaceDocumentParams,
|
||||
type RocketlaneSpaceDocumentResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
SPACE_DOCUMENT_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetSpaceDocumentTool: ToolConfig<
|
||||
RocketlaneGetSpaceDocumentParams,
|
||||
RocketlaneSpaceDocumentResponse
|
||||
> = {
|
||||
id: 'rocketlane_get_space_document',
|
||||
name: 'Rocketlane Get Space Document',
|
||||
description: 'Retrieve a Rocketlane space document by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
spaceDocumentId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space document to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/space-documents/${encodeURIComponent(params.spaceDocumentId)}`,
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { spaceDocument: mapSpaceDocument(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
spaceDocument: {
|
||||
type: 'object',
|
||||
description: 'The requested space document',
|
||||
properties: SPACE_DOCUMENT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneGetTaskParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetTaskTool: ToolConfig<RocketlaneGetTaskParams, RocketlaneTaskResponse> = {
|
||||
id: 'rocketlane_get_task',
|
||||
name: 'Rocketlane Get Task',
|
||||
description: 'Retrieve detailed information about a Rocketlane task by its unique identifier',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to retrieve',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Extra fields to include in the response (startDateActual, dueDateActual, type, phase, assignees, followers, dependencies, billable, csatEnabled, priority, timeEntryCategory, financialsBudget, taskPrivateNote, parent, externalReferenceId)',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether all fields should be returned in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}`
|
||||
)
|
||||
if (params.includeFields && params.includeFields.length > 0) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields !== undefined) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The requested task',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
mapTimeEntry,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneGetTimeEntryParams,
|
||||
type RocketlaneTimeEntryResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_ENTRY_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetTimeEntryTool: ToolConfig<
|
||||
RocketlaneGetTimeEntryParams,
|
||||
RocketlaneTimeEntryResponse
|
||||
> = {
|
||||
id: 'rocketlane_get_time_entry',
|
||||
name: 'Rocketlane Get Time Entry',
|
||||
description: 'Get a single Rocketlane time entry by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
timeEntryId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the time entry to retrieve',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to include in the response (notes, sourceType, deleted, status, submittedBy, submittedAt, approvedBy, approvedAt, rejectedBy, rejectedAt, costRate, billRate)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether all fields should be returned in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/time-entries/${encodeURIComponent(params.timeEntryId)}`
|
||||
)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { timeEntry: mapTimeEntry(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeEntry: {
|
||||
type: 'object',
|
||||
description: 'The requested time entry',
|
||||
properties: TIME_ENTRY_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
mapTimeOff,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTimeOffGetParams,
|
||||
type RocketlaneTimeOffResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_OFF_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetTimeOffTool: ToolConfig<
|
||||
RocketlaneTimeOffGetParams,
|
||||
RocketlaneTimeOffResponse
|
||||
> = {
|
||||
id: 'rocketlane_get_time_off',
|
||||
name: 'Rocketlane Get Time-Off',
|
||||
description: 'Retrieve a Rocketlane time-off by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
timeOffId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the time-off',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional fields to include in the response: note, notifyUsers',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/time-offs/${encodeURIComponent(params.timeOffId)}`
|
||||
)
|
||||
if (params.includeFields?.length) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { timeOff: mapTimeOff(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeOff: {
|
||||
type: 'object',
|
||||
description: 'The requested time-off',
|
||||
properties: TIME_OFF_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
mapUser,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneGetUserParams,
|
||||
type RocketlaneUserResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
USER_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneGetUserTool: ToolConfig<RocketlaneGetUserParams, RocketlaneUserResponse> = {
|
||||
id: 'rocketlane_get_user',
|
||||
name: 'Rocketlane Get User',
|
||||
description: 'Retrieve a Rocketlane user by their ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
userId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the user to retrieve',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to include: role, company, permission, holidayCalendar, capacityInMinutes, profilePictureUrl',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to include all fields in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/users/${encodeURIComponent(params.userId)}`)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null)
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { user: mapUser(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
user: {
|
||||
type: 'object',
|
||||
description: 'The requested user',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
mapProject,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneImportTemplateParams,
|
||||
type RocketlaneProjectResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneImportTemplateTool: ToolConfig<
|
||||
RocketlaneImportTemplateParams,
|
||||
RocketlaneProjectResponse
|
||||
> = {
|
||||
id: 'rocketlane_import_template',
|
||||
name: 'Rocketlane Import Template',
|
||||
description: 'Import a project template into an existing Rocketlane project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project to import the template into',
|
||||
},
|
||||
templateId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the template to import',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date on which the template goes into effect for the project (YYYY-MM-DD)',
|
||||
},
|
||||
prefix: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Prefix distinguishing which phase or task corresponds to this template when importing multiple templates',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}/import-template`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const source: Record<string, unknown> = {
|
||||
templateId: params.templateId,
|
||||
startDate: params.startDate,
|
||||
}
|
||||
if (params.prefix) source.prefix = params.prefix
|
||||
return [source]
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { project: mapProject(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The project after the template import (including its sources)',
|
||||
properties: PROJECT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { rocketlaneAddFieldOptionTool } from '@/tools/rocketlane/add_field_option'
|
||||
import { rocketlaneAddProjectMembersTool } from '@/tools/rocketlane/add_project_members'
|
||||
import { rocketlaneAddTaskAssigneesTool } from '@/tools/rocketlane/add_task_assignees'
|
||||
import { rocketlaneAddTaskDependenciesTool } from '@/tools/rocketlane/add_task_dependencies'
|
||||
import { rocketlaneAddTaskFollowersTool } from '@/tools/rocketlane/add_task_followers'
|
||||
import { rocketlaneArchiveProjectTool } from '@/tools/rocketlane/archive_project'
|
||||
import { rocketlaneAssignPlaceholdersTool } from '@/tools/rocketlane/assign_placeholders'
|
||||
import { rocketlaneCreateFieldTool } from '@/tools/rocketlane/create_field'
|
||||
import { rocketlaneCreatePhaseTool } from '@/tools/rocketlane/create_phase'
|
||||
import { rocketlaneCreateProjectTool } from '@/tools/rocketlane/create_project'
|
||||
import { rocketlaneCreateSpaceTool } from '@/tools/rocketlane/create_space'
|
||||
import { rocketlaneCreateSpaceDocumentTool } from '@/tools/rocketlane/create_space_document'
|
||||
import { rocketlaneCreateTaskTool } from '@/tools/rocketlane/create_task'
|
||||
import { rocketlaneCreateTimeEntryTool } from '@/tools/rocketlane/create_time_entry'
|
||||
import { rocketlaneCreateTimeOffTool } from '@/tools/rocketlane/create_time_off'
|
||||
import { rocketlaneDeleteFieldTool } from '@/tools/rocketlane/delete_field'
|
||||
import { rocketlaneDeletePhaseTool } from '@/tools/rocketlane/delete_phase'
|
||||
import { rocketlaneDeleteProjectTool } from '@/tools/rocketlane/delete_project'
|
||||
import { rocketlaneDeleteSpaceTool } from '@/tools/rocketlane/delete_space'
|
||||
import { rocketlaneDeleteSpaceDocumentTool } from '@/tools/rocketlane/delete_space_document'
|
||||
import { rocketlaneDeleteTaskTool } from '@/tools/rocketlane/delete_task'
|
||||
import { rocketlaneDeleteTimeEntryTool } from '@/tools/rocketlane/delete_time_entry'
|
||||
import { rocketlaneDeleteTimeOffTool } from '@/tools/rocketlane/delete_time_off'
|
||||
import { rocketlaneGetFieldTool } from '@/tools/rocketlane/get_field'
|
||||
import { rocketlaneGetInvoiceTool } from '@/tools/rocketlane/get_invoice'
|
||||
import { rocketlaneGetInvoiceLineItemsTool } from '@/tools/rocketlane/get_invoice_line_items'
|
||||
import { rocketlaneGetInvoicePaymentsTool } from '@/tools/rocketlane/get_invoice_payments'
|
||||
import { rocketlaneGetPhaseTool } from '@/tools/rocketlane/get_phase'
|
||||
import { rocketlaneGetProjectTool } from '@/tools/rocketlane/get_project'
|
||||
import { rocketlaneGetSpaceTool } from '@/tools/rocketlane/get_space'
|
||||
import { rocketlaneGetSpaceDocumentTool } from '@/tools/rocketlane/get_space_document'
|
||||
import { rocketlaneGetTaskTool } from '@/tools/rocketlane/get_task'
|
||||
import { rocketlaneGetTimeEntryTool } from '@/tools/rocketlane/get_time_entry'
|
||||
import { rocketlaneGetTimeOffTool } from '@/tools/rocketlane/get_time_off'
|
||||
import { rocketlaneGetUserTool } from '@/tools/rocketlane/get_user'
|
||||
import { rocketlaneImportTemplateTool } from '@/tools/rocketlane/import_template'
|
||||
import { rocketlaneListFieldsTool } from '@/tools/rocketlane/list_fields'
|
||||
import { rocketlaneListInvoicesTool } from '@/tools/rocketlane/list_invoices'
|
||||
import { rocketlaneListPhasesTool } from '@/tools/rocketlane/list_phases'
|
||||
import { rocketlaneListPlaceholdersTool } from '@/tools/rocketlane/list_placeholders'
|
||||
import { rocketlaneListProjectsTool } from '@/tools/rocketlane/list_projects'
|
||||
import { rocketlaneListResourceAllocationsTool } from '@/tools/rocketlane/list_resource_allocations'
|
||||
import { rocketlaneListSpaceDocumentsTool } from '@/tools/rocketlane/list_space_documents'
|
||||
import { rocketlaneListSpacesTool } from '@/tools/rocketlane/list_spaces'
|
||||
import { rocketlaneListTasksTool } from '@/tools/rocketlane/list_tasks'
|
||||
import { rocketlaneListTimeEntriesTool } from '@/tools/rocketlane/list_time_entries'
|
||||
import { rocketlaneListTimeEntryCategoriesTool } from '@/tools/rocketlane/list_time_entry_categories'
|
||||
import { rocketlaneListTimeOffsTool } from '@/tools/rocketlane/list_time_offs'
|
||||
import { rocketlaneListUsersTool } from '@/tools/rocketlane/list_users'
|
||||
import { rocketlaneMoveTaskToPhaseTool } from '@/tools/rocketlane/move_task_to_phase'
|
||||
import { rocketlaneRemoveProjectMembersTool } from '@/tools/rocketlane/remove_project_members'
|
||||
import { rocketlaneRemoveTaskAssigneesTool } from '@/tools/rocketlane/remove_task_assignees'
|
||||
import { rocketlaneRemoveTaskDependenciesTool } from '@/tools/rocketlane/remove_task_dependencies'
|
||||
import { rocketlaneRemoveTaskFollowersTool } from '@/tools/rocketlane/remove_task_followers'
|
||||
import { rocketlaneSearchTimeEntriesTool } from '@/tools/rocketlane/search_time_entries'
|
||||
import { rocketlaneUnassignPlaceholdersTool } from '@/tools/rocketlane/unassign_placeholders'
|
||||
import { rocketlaneUpdateFieldTool } from '@/tools/rocketlane/update_field'
|
||||
import { rocketlaneUpdateFieldOptionTool } from '@/tools/rocketlane/update_field_option'
|
||||
import { rocketlaneUpdatePhaseTool } from '@/tools/rocketlane/update_phase'
|
||||
import { rocketlaneUpdateProjectTool } from '@/tools/rocketlane/update_project'
|
||||
import { rocketlaneUpdateSpaceTool } from '@/tools/rocketlane/update_space'
|
||||
import { rocketlaneUpdateSpaceDocumentTool } from '@/tools/rocketlane/update_space_document'
|
||||
import { rocketlaneUpdateTaskTool } from '@/tools/rocketlane/update_task'
|
||||
import { rocketlaneUpdateTimeEntryTool } from '@/tools/rocketlane/update_time_entry'
|
||||
|
||||
export {
|
||||
rocketlaneAddFieldOptionTool,
|
||||
rocketlaneAddProjectMembersTool,
|
||||
rocketlaneAddTaskAssigneesTool,
|
||||
rocketlaneAddTaskDependenciesTool,
|
||||
rocketlaneAddTaskFollowersTool,
|
||||
rocketlaneArchiveProjectTool,
|
||||
rocketlaneAssignPlaceholdersTool,
|
||||
rocketlaneCreateFieldTool,
|
||||
rocketlaneCreatePhaseTool,
|
||||
rocketlaneCreateProjectTool,
|
||||
rocketlaneCreateSpaceDocumentTool,
|
||||
rocketlaneCreateSpaceTool,
|
||||
rocketlaneCreateTaskTool,
|
||||
rocketlaneCreateTimeEntryTool,
|
||||
rocketlaneCreateTimeOffTool,
|
||||
rocketlaneDeleteFieldTool,
|
||||
rocketlaneDeletePhaseTool,
|
||||
rocketlaneDeleteProjectTool,
|
||||
rocketlaneDeleteSpaceDocumentTool,
|
||||
rocketlaneDeleteSpaceTool,
|
||||
rocketlaneDeleteTaskTool,
|
||||
rocketlaneDeleteTimeEntryTool,
|
||||
rocketlaneDeleteTimeOffTool,
|
||||
rocketlaneGetFieldTool,
|
||||
rocketlaneGetInvoiceLineItemsTool,
|
||||
rocketlaneGetInvoicePaymentsTool,
|
||||
rocketlaneGetInvoiceTool,
|
||||
rocketlaneGetPhaseTool,
|
||||
rocketlaneGetProjectTool,
|
||||
rocketlaneGetSpaceDocumentTool,
|
||||
rocketlaneGetSpaceTool,
|
||||
rocketlaneGetTaskTool,
|
||||
rocketlaneGetTimeEntryTool,
|
||||
rocketlaneGetTimeOffTool,
|
||||
rocketlaneGetUserTool,
|
||||
rocketlaneImportTemplateTool,
|
||||
rocketlaneListFieldsTool,
|
||||
rocketlaneListInvoicesTool,
|
||||
rocketlaneListPhasesTool,
|
||||
rocketlaneListPlaceholdersTool,
|
||||
rocketlaneListProjectsTool,
|
||||
rocketlaneListResourceAllocationsTool,
|
||||
rocketlaneListSpaceDocumentsTool,
|
||||
rocketlaneListSpacesTool,
|
||||
rocketlaneListTasksTool,
|
||||
rocketlaneListTimeEntriesTool,
|
||||
rocketlaneListTimeEntryCategoriesTool,
|
||||
rocketlaneListTimeOffsTool,
|
||||
rocketlaneListUsersTool,
|
||||
rocketlaneMoveTaskToPhaseTool,
|
||||
rocketlaneRemoveProjectMembersTool,
|
||||
rocketlaneRemoveTaskAssigneesTool,
|
||||
rocketlaneRemoveTaskDependenciesTool,
|
||||
rocketlaneRemoveTaskFollowersTool,
|
||||
rocketlaneSearchTimeEntriesTool,
|
||||
rocketlaneUnassignPlaceholdersTool,
|
||||
rocketlaneUpdateFieldOptionTool,
|
||||
rocketlaneUpdateFieldTool,
|
||||
rocketlaneUpdatePhaseTool,
|
||||
rocketlaneUpdateProjectTool,
|
||||
rocketlaneUpdateSpaceDocumentTool,
|
||||
rocketlaneUpdateSpaceTool,
|
||||
rocketlaneUpdateTaskTool,
|
||||
rocketlaneUpdateTimeEntryTool,
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import {
|
||||
FIELD_OUTPUT_PROPERTIES,
|
||||
mapField,
|
||||
mapPagination,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneFieldListResponse,
|
||||
type RocketlaneListFieldsParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListFieldsTool: ToolConfig<
|
||||
RocketlaneListFieldsParams,
|
||||
RocketlaneFieldListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_fields',
|
||||
name: 'Rocketlane List Fields',
|
||||
description:
|
||||
'List fields in your Rocketlane account, with optional filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of fields per page',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token returned by a previous request (valid for 15 minutes)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra field properties to include in the response (supported: options)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return all field properties in the response',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Property to sort by (supported: fieldLabel)',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order (ASC or DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether results must match all filters or any filter (all or any)',
|
||||
},
|
||||
objectType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by associated object type (PROJECT, TASK, or USER)',
|
||||
},
|
||||
fieldType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter by field type (TEXT, MULTI_LINE_TEXT, YES_OR_NO, DATE, SINGLE_CHOICE, MULTIPLE_CHOICE, SINGLE_USER, MULTIPLE_USER, NUMBER, NOTE, RATING)',
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by enabled state',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by privacy setting',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/fields`)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.objectType) url.searchParams.set('objectType.eq', params.objectType)
|
||||
if (params.fieldType) url.searchParams.set('fieldType.eq', params.fieldType)
|
||||
if (params.enabled != null) url.searchParams.set('enabled.eq', String(params.enabled))
|
||||
if (params.private != null) url.searchParams.set('private.eq', String(params.private))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const fields = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
fields: fields.map(mapField),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
fields: {
|
||||
type: 'array',
|
||||
description: 'List of fields',
|
||||
items: { type: 'object', properties: FIELD_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
import {
|
||||
INVOICE_OUTPUT_PROPERTIES,
|
||||
mapInvoice,
|
||||
mapPagination,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneInvoiceListParams,
|
||||
type RocketlaneInvoiceListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListInvoicesTool: ToolConfig<
|
||||
RocketlaneInvoiceListParams,
|
||||
RocketlaneInvoiceListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_invoices',
|
||||
name: 'Rocketlane List Invoices',
|
||||
description:
|
||||
'Search invoices in Rocketlane with date, amount, company, invoice-number, and status filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of invoices per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response (valid for 15 minutes)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional fields to include in the response: notes, attachments',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by: createdAt or invoiceNumber',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (defaults to DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Combine filters with AND (all) or OR (any); defaults to all',
|
||||
},
|
||||
dateOfIssueEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by date of issue equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateOfIssueGt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by date of issue greater than this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateOfIssueGe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by date of issue greater than or equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateOfIssueLt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by date of issue less than this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateOfIssueLe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by date of issue less than or equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by due date equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateGt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by due date greater than this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateGe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by due date greater than or equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateLt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by due date less than this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateLe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by due date less than or equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
amountEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by total amount equal to this value',
|
||||
},
|
||||
amountGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by total amount greater than this value',
|
||||
},
|
||||
amountGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by total amount greater than or equal to this value',
|
||||
},
|
||||
amountLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by total amount less than this value',
|
||||
},
|
||||
amountLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by total amount less than or equal to this value',
|
||||
},
|
||||
amountOutstandingEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount outstanding equal to this value',
|
||||
},
|
||||
amountOutstandingGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount outstanding greater than this value',
|
||||
},
|
||||
amountOutstandingGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount outstanding greater than or equal to this value',
|
||||
},
|
||||
amountOutstandingLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount outstanding less than this value',
|
||||
},
|
||||
amountOutstandingLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount outstanding less than or equal to this value',
|
||||
},
|
||||
amountPaidEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount paid equal to this value',
|
||||
},
|
||||
amountPaidGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount paid greater than this value',
|
||||
},
|
||||
amountPaidGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount paid greater than or equal to this value',
|
||||
},
|
||||
amountPaidLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount paid less than this value',
|
||||
},
|
||||
amountPaidLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount paid less than or equal to this value',
|
||||
},
|
||||
amountWrittenOffEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount written off equal to this value',
|
||||
},
|
||||
amountWrittenOffGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount written off greater than this value',
|
||||
},
|
||||
amountWrittenOffGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount written off greater than or equal to this value',
|
||||
},
|
||||
amountWrittenOffLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount written off less than this value',
|
||||
},
|
||||
amountWrittenOffLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by amount written off less than or equal to this value',
|
||||
},
|
||||
createdAtEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by created timestamp equal to this value (epoch milliseconds)',
|
||||
},
|
||||
createdAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by created timestamp greater than this value (epoch milliseconds)',
|
||||
},
|
||||
createdAtGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter by created timestamp greater than or equal to this value (epoch milliseconds)',
|
||||
},
|
||||
createdAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by created timestamp less than this value (epoch milliseconds)',
|
||||
},
|
||||
createdAtLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter by created timestamp less than or equal to this value (epoch milliseconds)',
|
||||
},
|
||||
companyIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return invoices that exactly match this customer company ID',
|
||||
},
|
||||
companyIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated customer company IDs to match any of',
|
||||
},
|
||||
companyIdNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated customer company IDs to match none of',
|
||||
},
|
||||
invoiceNumberEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return invoices whose invoice number equals this value',
|
||||
},
|
||||
invoiceNumberCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return invoices whose invoice number contains this text',
|
||||
},
|
||||
invoiceNumberNc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return invoices whose invoice number does not contain this text',
|
||||
},
|
||||
statusEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return invoices that equal this status (e.g. DRAFT)',
|
||||
},
|
||||
statusOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated statuses to match any of (e.g. DRAFT,PAID)',
|
||||
},
|
||||
statusNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated statuses to match none of (e.g. DRAFT,PAID)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/invoices`)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.includeFields?.length) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.dateOfIssueEq) url.searchParams.set('dateOfIssue.eq', params.dateOfIssueEq)
|
||||
if (params.dateOfIssueGt) url.searchParams.set('dateOfIssue.gt', params.dateOfIssueGt)
|
||||
if (params.dateOfIssueGe) url.searchParams.set('dateOfIssue.ge', params.dateOfIssueGe)
|
||||
if (params.dateOfIssueLt) url.searchParams.set('dateOfIssue.lt', params.dateOfIssueLt)
|
||||
if (params.dateOfIssueLe) url.searchParams.set('dateOfIssue.le', params.dateOfIssueLe)
|
||||
if (params.dueDateEq) url.searchParams.set('dueDate.eq', params.dueDateEq)
|
||||
if (params.dueDateGt) url.searchParams.set('dueDate.gt', params.dueDateGt)
|
||||
if (params.dueDateGe) url.searchParams.set('dueDate.ge', params.dueDateGe)
|
||||
if (params.dueDateLt) url.searchParams.set('dueDate.lt', params.dueDateLt)
|
||||
if (params.dueDateLe) url.searchParams.set('dueDate.le', params.dueDateLe)
|
||||
if (params.amountEq != null) url.searchParams.set('amount.eq', String(params.amountEq))
|
||||
if (params.amountGt != null) url.searchParams.set('amount.gt', String(params.amountGt))
|
||||
if (params.amountGe != null) url.searchParams.set('amount.ge', String(params.amountGe))
|
||||
if (params.amountLt != null) url.searchParams.set('amount.lt', String(params.amountLt))
|
||||
if (params.amountLe != null) url.searchParams.set('amount.le', String(params.amountLe))
|
||||
if (params.amountOutstandingEq != null) {
|
||||
url.searchParams.set('amountOutstanding.eq', String(params.amountOutstandingEq))
|
||||
}
|
||||
if (params.amountOutstandingGt != null) {
|
||||
url.searchParams.set('amountOutstanding.gt', String(params.amountOutstandingGt))
|
||||
}
|
||||
if (params.amountOutstandingGe != null) {
|
||||
url.searchParams.set('amountOutstanding.ge', String(params.amountOutstandingGe))
|
||||
}
|
||||
if (params.amountOutstandingLt != null) {
|
||||
url.searchParams.set('amountOutstanding.lt', String(params.amountOutstandingLt))
|
||||
}
|
||||
if (params.amountOutstandingLe != null) {
|
||||
url.searchParams.set('amountOutstanding.le', String(params.amountOutstandingLe))
|
||||
}
|
||||
if (params.amountPaidEq != null) {
|
||||
url.searchParams.set('amountPaid.eq', String(params.amountPaidEq))
|
||||
}
|
||||
if (params.amountPaidGt != null) {
|
||||
url.searchParams.set('amountPaid.gt', String(params.amountPaidGt))
|
||||
}
|
||||
if (params.amountPaidGe != null) {
|
||||
url.searchParams.set('amountPaid.ge', String(params.amountPaidGe))
|
||||
}
|
||||
if (params.amountPaidLt != null) {
|
||||
url.searchParams.set('amountPaid.lt', String(params.amountPaidLt))
|
||||
}
|
||||
if (params.amountPaidLe != null) {
|
||||
url.searchParams.set('amountPaid.le', String(params.amountPaidLe))
|
||||
}
|
||||
if (params.amountWrittenOffEq != null) {
|
||||
url.searchParams.set('amountWrittenOff.eq', String(params.amountWrittenOffEq))
|
||||
}
|
||||
if (params.amountWrittenOffGt != null) {
|
||||
url.searchParams.set('amountWrittenOff.gt', String(params.amountWrittenOffGt))
|
||||
}
|
||||
if (params.amountWrittenOffGe != null) {
|
||||
url.searchParams.set('amountWrittenOff.ge', String(params.amountWrittenOffGe))
|
||||
}
|
||||
if (params.amountWrittenOffLt != null) {
|
||||
url.searchParams.set('amountWrittenOff.lt', String(params.amountWrittenOffLt))
|
||||
}
|
||||
if (params.amountWrittenOffLe != null) {
|
||||
url.searchParams.set('amountWrittenOff.le', String(params.amountWrittenOffLe))
|
||||
}
|
||||
if (params.createdAtEq != null) {
|
||||
url.searchParams.set('createdAt.eq', String(params.createdAtEq))
|
||||
}
|
||||
if (params.createdAtGt != null) {
|
||||
url.searchParams.set('createdAt.gt', String(params.createdAtGt))
|
||||
}
|
||||
if (params.createdAtGe != null) {
|
||||
url.searchParams.set('createdAt.ge', String(params.createdAtGe))
|
||||
}
|
||||
if (params.createdAtLt != null) {
|
||||
url.searchParams.set('createdAt.lt', String(params.createdAtLt))
|
||||
}
|
||||
if (params.createdAtLe != null) {
|
||||
url.searchParams.set('createdAt.le', String(params.createdAtLe))
|
||||
}
|
||||
if (params.companyIdEq) url.searchParams.set('companyId.eq', params.companyIdEq)
|
||||
if (params.companyIdOneOf) url.searchParams.set('companyId.oneOf', params.companyIdOneOf)
|
||||
if (params.companyIdNoneOf) {
|
||||
url.searchParams.set('companyId.noneOf', params.companyIdNoneOf)
|
||||
}
|
||||
if (params.invoiceNumberEq) {
|
||||
url.searchParams.set('invoiceNumber.eq', params.invoiceNumberEq)
|
||||
}
|
||||
if (params.invoiceNumberCn) {
|
||||
url.searchParams.set('invoiceNumber.cn', params.invoiceNumberCn)
|
||||
}
|
||||
if (params.invoiceNumberNc) {
|
||||
url.searchParams.set('invoiceNumber.nc', params.invoiceNumberNc)
|
||||
}
|
||||
if (params.statusEq) url.searchParams.set('status.eq', params.statusEq)
|
||||
if (params.statusOneOf) url.searchParams.set('status.oneOf', params.statusOneOf)
|
||||
if (params.statusNoneOf) url.searchParams.set('status.noneOf', params.statusNoneOf)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const invoices = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
invoices: invoices.map(mapInvoice),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
invoices: {
|
||||
type: 'array',
|
||||
description: 'List of invoices',
|
||||
items: { type: 'object', properties: INVOICE_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapPhase,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
PHASE_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListPhasesParams,
|
||||
type RocketlanePhaseListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListPhasesTool: ToolConfig<
|
||||
RocketlaneListPhasesParams,
|
||||
RocketlanePhaseListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_phases',
|
||||
name: 'Rocketlane List Phases',
|
||||
description:
|
||||
'List phases of a Rocketlane project, with optional filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the project to list phases for',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of phases per page',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token returned by a previous request (valid for 15 minutes)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra phase properties to include in the response (supported: startDateActual, dueDateActual)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return all phase properties in the response',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Property to sort by (phaseName, startDate, dueDate, startDateActual, dueDateActual)',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order (ASC or DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether results must match all filters or any filter (all or any)',
|
||||
},
|
||||
phaseName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by exact phase name',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/phases`)
|
||||
url.searchParams.set('projectId', String(params.projectId))
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.phaseName) url.searchParams.set('phaseName.eq', params.phaseName)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const phases = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
phases: phases.map(mapPhase),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
phases: {
|
||||
type: 'array',
|
||||
description: 'List of phases',
|
||||
items: { type: 'object', properties: PHASE_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapPlaceholder,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
PLACEHOLDER_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListPlaceholdersParams,
|
||||
type RocketlanePlaceholderListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListPlaceholdersTool: ToolConfig<
|
||||
RocketlaneListPlaceholdersParams,
|
||||
RocketlanePlaceholderListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_placeholders',
|
||||
name: 'Rocketlane List Placeholders',
|
||||
description: 'List the placeholders of a Rocketlane project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}/get-placeholders`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
placeholders: Array.isArray(data?.data) ? data.data.map(mapPlaceholder) : [],
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
placeholders: {
|
||||
type: 'array',
|
||||
description: 'Placeholders of the project',
|
||||
items: { type: 'object', properties: PLACEHOLDER_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for fetching further pages',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapProject,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListProjectsParams,
|
||||
type RocketlaneProjectListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListProjectsTool: ToolConfig<
|
||||
RocketlaneListProjectsParams,
|
||||
RocketlaneProjectListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_projects',
|
||||
name: 'Rocketlane List Projects',
|
||||
description:
|
||||
'List Rocketlane projects with optional filters, sorting, and token-based pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of projects per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response (valid for 15 minutes)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to return in the response (e.g. budgetedHours,progressPercentage)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response body',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Field to sort by: projectName, startDate, dueDate, startDateActual, dueDateActual, annualizedRecurringRevenue, or projectFee',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (defaults to DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Combine filters with AND (all) or OR (any); defaults to all',
|
||||
},
|
||||
projectNameContains: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects whose name contains this value',
|
||||
},
|
||||
projectNameEquals: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects whose name exactly matches this value',
|
||||
},
|
||||
statusEquals: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects with this status value',
|
||||
},
|
||||
statusOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated status values; returns projects matching any of them',
|
||||
},
|
||||
customerIdEquals: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects for this customer company ID',
|
||||
},
|
||||
customerIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated customer company IDs; returns projects matching any of them',
|
||||
},
|
||||
teamMemberIdEquals: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects that include this team member ID',
|
||||
},
|
||||
contractTypeEquals: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only return projects with this contract type: FIXED_FEE, TIME_AND_MATERIAL, SUBSCRIPTION, or NON_BILLABLE',
|
||||
},
|
||||
includeArchived: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to include archived projects in the results',
|
||||
},
|
||||
externalReferenceIdEquals: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects with this external reference ID',
|
||||
},
|
||||
startDateAfter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects whose start date is after this date (YYYY-MM-DD)',
|
||||
},
|
||||
startDateBefore: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects whose start date is before this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateAfter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects whose due date is after this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateBefore: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only return projects whose due date is before this date (YYYY-MM-DD)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/projects`)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null)
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.projectNameContains)
|
||||
url.searchParams.set('projectName.cn', params.projectNameContains)
|
||||
if (params.projectNameEquals) url.searchParams.set('projectName.eq', params.projectNameEquals)
|
||||
if (params.statusEquals) url.searchParams.set('status.eq', params.statusEquals)
|
||||
if (params.statusOneOf) url.searchParams.set('status.oneOf', params.statusOneOf)
|
||||
if (params.customerIdEquals) url.searchParams.set('customerId.eq', params.customerIdEquals)
|
||||
if (params.customerIdOneOf) url.searchParams.set('customerId.oneOf', params.customerIdOneOf)
|
||||
if (params.teamMemberIdEquals)
|
||||
url.searchParams.set('teamMemberId.eq', params.teamMemberIdEquals)
|
||||
if (params.contractTypeEquals)
|
||||
url.searchParams.set('contractType.eq', params.contractTypeEquals)
|
||||
if (params.includeArchived != null)
|
||||
url.searchParams.set('includeArchive.eq', String(params.includeArchived))
|
||||
if (params.externalReferenceIdEquals)
|
||||
url.searchParams.set('externalReferenceId.eq', params.externalReferenceIdEquals)
|
||||
if (params.startDateAfter) url.searchParams.set('startDate.gt', params.startDateAfter)
|
||||
if (params.startDateBefore) url.searchParams.set('startDate.lt', params.startDateBefore)
|
||||
if (params.dueDateAfter) url.searchParams.set('dueDate.gt', params.dueDateAfter)
|
||||
if (params.dueDateBefore) url.searchParams.set('dueDate.lt', params.dueDateBefore)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
projects: Array.isArray(data?.data) ? data.data.map(mapProject) : [],
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
projects: {
|
||||
type: 'array',
|
||||
description: 'List of projects',
|
||||
items: { type: 'object', properties: PROJECT_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for fetching further pages',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapResourceAllocation,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
RESOURCE_ALLOCATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneResourceAllocationListParams,
|
||||
type RocketlaneResourceAllocationListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListResourceAllocationsTool: ToolConfig<
|
||||
RocketlaneResourceAllocationListParams,
|
||||
RocketlaneResourceAllocationListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_resource_allocations',
|
||||
name: 'Rocketlane List Resource Allocations',
|
||||
description:
|
||||
'List resource allocations in Rocketlane within a date range, with optional member, project, and placeholder filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return allocations that start on or after this date (YYYY-MM-DD)',
|
||||
},
|
||||
endDate: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return allocations that end on or before this date (YYYY-MM-DD)',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of allocations per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response (valid for 15 minutes)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Optional fields to include in the response: member, task, placeholder, duration',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by: startDate, endDate, allocationType, or allocationFor',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (defaults to DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Combine filters with AND (all) or OR (any); defaults to all',
|
||||
},
|
||||
memberIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return allocations that exactly match this member ID',
|
||||
},
|
||||
memberIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated member IDs to match any of',
|
||||
},
|
||||
memberIdNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated member IDs to exclude',
|
||||
},
|
||||
projectIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return allocations that exactly match this project ID',
|
||||
},
|
||||
projectIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated project IDs to match any of',
|
||||
},
|
||||
projectIdNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated project IDs to exclude',
|
||||
},
|
||||
placeholderIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return allocations that exactly match this placeholder ID',
|
||||
},
|
||||
placeholderIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated placeholder IDs to match any of',
|
||||
},
|
||||
placeholderIdNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated placeholder IDs to exclude',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/resource-allocations`)
|
||||
url.searchParams.set('startDate', params.startDate)
|
||||
url.searchParams.set('endDate', params.endDate)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.includeFields?.length) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.memberIdEq) url.searchParams.set('memberId.eq', params.memberIdEq)
|
||||
if (params.memberIdOneOf) url.searchParams.set('memberId.oneOf', params.memberIdOneOf)
|
||||
if (params.memberIdNoneOf) url.searchParams.set('memberId.noneOf', params.memberIdNoneOf)
|
||||
if (params.projectIdEq) url.searchParams.set('projectId.eq', params.projectIdEq)
|
||||
if (params.projectIdOneOf) url.searchParams.set('projectId.oneOf', params.projectIdOneOf)
|
||||
if (params.projectIdNoneOf) {
|
||||
url.searchParams.set('projectId.noneOf', params.projectIdNoneOf)
|
||||
}
|
||||
if (params.placeholderIdEq) {
|
||||
url.searchParams.set('placeholderId.eq', params.placeholderIdEq)
|
||||
}
|
||||
if (params.placeholderIdOneOf) {
|
||||
url.searchParams.set('placeholderId.oneOf', params.placeholderIdOneOf)
|
||||
}
|
||||
if (params.placeholderIdNoneOf) {
|
||||
url.searchParams.set('placeholderId.noneOf', params.placeholderIdNoneOf)
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const allocations = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
allocations: allocations.map(mapResourceAllocation),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
allocations: {
|
||||
type: 'array',
|
||||
description: 'List of resource allocations',
|
||||
items: { type: 'object', properties: RESOURCE_ALLOCATION_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapSpaceDocument,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListSpaceDocumentsParams,
|
||||
type RocketlaneListSpaceDocumentsResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
SPACE_DOCUMENT_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListSpaceDocumentsTool: ToolConfig<
|
||||
RocketlaneListSpaceDocumentsParams,
|
||||
RocketlaneListSpaceDocumentsResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_space_documents',
|
||||
name: 'Rocketlane List Space Documents',
|
||||
description:
|
||||
'List space documents in a Rocketlane project, with optional filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the project whose space documents to list',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of space documents per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous request (valid for 15 minutes)',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by (spaceTabName)',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (defaults to DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How to combine filters: all (AND) or any (OR); defaults to all',
|
||||
},
|
||||
spaceDocumentNameEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents whose name exactly matches this value',
|
||||
},
|
||||
spaceDocumentNameCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents whose name contains this value',
|
||||
},
|
||||
spaceDocumentNameNc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Exclude space documents whose name contains this value',
|
||||
},
|
||||
spaceIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents in the space with this ID',
|
||||
},
|
||||
createdAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents created after this time (epoch millis)',
|
||||
},
|
||||
createdAtEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents created at exactly this time (epoch millis)',
|
||||
},
|
||||
createdAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents created before this time (epoch millis)',
|
||||
},
|
||||
createdAtGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents created at or after this time (epoch millis)',
|
||||
},
|
||||
createdAtLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents created at or before this time (epoch millis)',
|
||||
},
|
||||
updatedAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents updated after this time (epoch millis)',
|
||||
},
|
||||
updatedAtEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents updated at exactly this time (epoch millis)',
|
||||
},
|
||||
updatedAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents updated before this time (epoch millis)',
|
||||
},
|
||||
updatedAtGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents updated at or after this time (epoch millis)',
|
||||
},
|
||||
updatedAtLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include space documents updated at or before this time (epoch millis)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/space-documents`)
|
||||
url.searchParams.set('projectId', String(params.projectId))
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.spaceDocumentNameEq)
|
||||
url.searchParams.set('spaceDocumentName.eq', params.spaceDocumentNameEq)
|
||||
if (params.spaceDocumentNameCn)
|
||||
url.searchParams.set('spaceDocumentName.cn', params.spaceDocumentNameCn)
|
||||
if (params.spaceDocumentNameNc)
|
||||
url.searchParams.set('spaceDocumentName.nc', params.spaceDocumentNameNc)
|
||||
if (params.spaceIdEq != null) url.searchParams.set('spaceId.eq', String(params.spaceIdEq))
|
||||
if (params.createdAtGt != null)
|
||||
url.searchParams.set('createdAt.gt', String(params.createdAtGt))
|
||||
if (params.createdAtEq != null)
|
||||
url.searchParams.set('createdAt.eq', String(params.createdAtEq))
|
||||
if (params.createdAtLt != null)
|
||||
url.searchParams.set('createdAt.lt', String(params.createdAtLt))
|
||||
if (params.createdAtGe != null)
|
||||
url.searchParams.set('createdAt.ge', String(params.createdAtGe))
|
||||
if (params.createdAtLe != null)
|
||||
url.searchParams.set('createdAt.le', String(params.createdAtLe))
|
||||
if (params.updatedAtGt != null)
|
||||
url.searchParams.set('updatedAt.gt', String(params.updatedAtGt))
|
||||
if (params.updatedAtEq != null)
|
||||
url.searchParams.set('updatedAt.eq', String(params.updatedAtEq))
|
||||
if (params.updatedAtLt != null)
|
||||
url.searchParams.set('updatedAt.lt', String(params.updatedAtLt))
|
||||
if (params.updatedAtGe != null)
|
||||
url.searchParams.set('updatedAt.ge', String(params.updatedAtGe))
|
||||
if (params.updatedAtLe != null)
|
||||
url.searchParams.set('updatedAt.le', String(params.updatedAtLe))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const spaceDocuments = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
spaceDocuments: spaceDocuments.map(mapSpaceDocument),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
spaceDocuments: {
|
||||
type: 'array',
|
||||
description: 'List of space documents',
|
||||
items: { type: 'object', properties: SPACE_DOCUMENT_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapSpace,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListSpacesParams,
|
||||
type RocketlaneListSpacesResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
SPACE_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListSpacesTool: ToolConfig<
|
||||
RocketlaneListSpacesParams,
|
||||
RocketlaneListSpacesResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_spaces',
|
||||
name: 'Rocketlane List Spaces',
|
||||
description:
|
||||
'List spaces in a Rocketlane project, with optional filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the project whose spaces to list',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of spaces per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous request (valid for 15 minutes)',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by (spaceName)',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (defaults to DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How to combine filters: all (AND) or any (OR); defaults to all',
|
||||
},
|
||||
spaceNameEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces whose name exactly matches this value',
|
||||
},
|
||||
spaceNameCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces whose name contains this value',
|
||||
},
|
||||
spaceNameNc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Exclude spaces whose name contains this value',
|
||||
},
|
||||
createdAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces created after this time (epoch millis)',
|
||||
},
|
||||
createdAtEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces created at exactly this time (epoch millis)',
|
||||
},
|
||||
createdAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces created before this time (epoch millis)',
|
||||
},
|
||||
createdAtGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces created at or after this time (epoch millis)',
|
||||
},
|
||||
createdAtLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces created at or before this time (epoch millis)',
|
||||
},
|
||||
updatedAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces updated after this time (epoch millis)',
|
||||
},
|
||||
updatedAtEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces updated at exactly this time (epoch millis)',
|
||||
},
|
||||
updatedAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces updated before this time (epoch millis)',
|
||||
},
|
||||
updatedAtGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces updated at or after this time (epoch millis)',
|
||||
},
|
||||
updatedAtLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include spaces updated at or before this time (epoch millis)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/spaces`)
|
||||
url.searchParams.set('projectId', String(params.projectId))
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.spaceNameEq) url.searchParams.set('spaceName.eq', params.spaceNameEq)
|
||||
if (params.spaceNameCn) url.searchParams.set('spaceName.cn', params.spaceNameCn)
|
||||
if (params.spaceNameNc) url.searchParams.set('spaceName.nc', params.spaceNameNc)
|
||||
if (params.createdAtGt != null)
|
||||
url.searchParams.set('createdAt.gt', String(params.createdAtGt))
|
||||
if (params.createdAtEq != null)
|
||||
url.searchParams.set('createdAt.eq', String(params.createdAtEq))
|
||||
if (params.createdAtLt != null)
|
||||
url.searchParams.set('createdAt.lt', String(params.createdAtLt))
|
||||
if (params.createdAtGe != null)
|
||||
url.searchParams.set('createdAt.ge', String(params.createdAtGe))
|
||||
if (params.createdAtLe != null)
|
||||
url.searchParams.set('createdAt.le', String(params.createdAtLe))
|
||||
if (params.updatedAtGt != null)
|
||||
url.searchParams.set('updatedAt.gt', String(params.updatedAtGt))
|
||||
if (params.updatedAtEq != null)
|
||||
url.searchParams.set('updatedAt.eq', String(params.updatedAtEq))
|
||||
if (params.updatedAtLt != null)
|
||||
url.searchParams.set('updatedAt.lt', String(params.updatedAtLt))
|
||||
if (params.updatedAtGe != null)
|
||||
url.searchParams.set('updatedAt.ge', String(params.updatedAtGe))
|
||||
if (params.updatedAtLe != null)
|
||||
url.searchParams.set('updatedAt.le', String(params.updatedAtLe))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const spaces = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
spaces: spaces.map(mapSpace),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
spaces: {
|
||||
type: 'array',
|
||||
description: 'List of spaces',
|
||||
items: { type: 'object', properties: SPACE_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapTask,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListTasksParams,
|
||||
type RocketlaneTaskListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListTasksTool: ToolConfig<
|
||||
RocketlaneListTasksParams,
|
||||
RocketlaneTaskListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_tasks',
|
||||
name: 'Rocketlane List Tasks',
|
||||
description: 'Retrieve all Rocketlane tasks with optional filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of tasks per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Token pointing to the next page of results, from a previous response',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Extra fields to include in the response (startDateActual, dueDateActual, type, phase, assignees, followers, dependencies, billable, csatEnabled, priority, timeEntryCategory, financialsBudget, taskPrivateNote, parent, externalReferenceId)',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether all fields should be returned in the response',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Field to sort by: taskName, startDate, dueDate, startDateActual, or dueDateActual',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How multiple filters combine: all (AND) or any (OR)',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks by project ID',
|
||||
},
|
||||
phaseId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks by phase ID',
|
||||
},
|
||||
taskName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks whose name exactly matches this value',
|
||||
},
|
||||
taskNameContains: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks whose name contains this value',
|
||||
},
|
||||
taskStatus: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks by task status value',
|
||||
},
|
||||
startDateFrom: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks with a start date on or after this date (YYYY-MM-DD)',
|
||||
},
|
||||
startDateTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks with a start date on or before this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateFrom: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks with a due date on or after this date (YYYY-MM-DD)',
|
||||
},
|
||||
dueDateTo: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks with a due date on or before this date (YYYY-MM-DD)',
|
||||
},
|
||||
includeArchive: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether archived tasks should be included in the results',
|
||||
},
|
||||
externalReferenceId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter tasks by external reference identifier',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/tasks`)
|
||||
if (params.pageSize !== undefined) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.includeFields && params.includeFields.length > 0) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields !== undefined) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.projectId !== undefined) {
|
||||
url.searchParams.set('projectId.eq', String(params.projectId))
|
||||
}
|
||||
if (params.phaseId !== undefined) {
|
||||
url.searchParams.set('phaseId.eq', String(params.phaseId))
|
||||
}
|
||||
if (params.taskName) url.searchParams.set('taskName.eq', params.taskName)
|
||||
if (params.taskNameContains) url.searchParams.set('taskName.cn', params.taskNameContains)
|
||||
if (params.taskStatus) url.searchParams.set('task.status.eq', params.taskStatus)
|
||||
if (params.startDateFrom) url.searchParams.set('startDate.ge', params.startDateFrom)
|
||||
if (params.startDateTo) url.searchParams.set('startDate.le', params.startDateTo)
|
||||
if (params.dueDateFrom) url.searchParams.set('dueDate.ge', params.dueDateFrom)
|
||||
if (params.dueDateTo) url.searchParams.set('dueDate.le', params.dueDateTo)
|
||||
if (params.includeArchive !== undefined) {
|
||||
url.searchParams.set('includeArchive.eq', String(params.includeArchive))
|
||||
}
|
||||
if (params.externalReferenceId) {
|
||||
url.searchParams.set('externalReferenceId.eq', params.externalReferenceId)
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const tasks = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
tasks: tasks.map(mapTask),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
tasks: {
|
||||
type: 'array',
|
||||
description: 'List of tasks matching the filters',
|
||||
items: { type: 'object', properties: TASK_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapTimeEntry,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListTimeEntriesParams,
|
||||
type RocketlaneTimeEntryListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_ENTRY_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListTimeEntriesTool: ToolConfig<
|
||||
RocketlaneListTimeEntriesParams,
|
||||
RocketlaneTimeEntryListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_time_entries',
|
||||
name: 'Rocketlane List Time Entries',
|
||||
description:
|
||||
'List Rocketlane time entries with optional filters, sorting, and cursor-based pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
dateEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries on this exact date (YYYY-MM-DD)',
|
||||
},
|
||||
dateGt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries after this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateGe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries on or after this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateLt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries before this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateLe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries on or before this date (YYYY-MM-DD)',
|
||||
},
|
||||
projectIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries for this project ID',
|
||||
},
|
||||
taskIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries for this task ID',
|
||||
},
|
||||
projectPhaseIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries for this project phase ID',
|
||||
},
|
||||
categoryIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries with this category ID',
|
||||
},
|
||||
userIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries belonging to this user ID',
|
||||
},
|
||||
emailIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries belonging to the user with this exact email',
|
||||
},
|
||||
emailIdCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries whose user email contains this text',
|
||||
},
|
||||
sourceTypeEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only entries with this source type (GOOGLE_CALENDAR, OUTLOOK_CALENDAR, TASK, PROJECT, PHASE, ADHOC, MILESTONE)',
|
||||
},
|
||||
activityNameEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries with this exact activity name',
|
||||
},
|
||||
activityNameCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries whose activity name contains this text',
|
||||
},
|
||||
approvalStatusEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only entries with this approval status (NOT_SUBMITTED, SUBMITTED, APPROVED, REJECTED)',
|
||||
},
|
||||
billableEq: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only billable (true) or non-billable (false) entries',
|
||||
},
|
||||
includeDeletedEq: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether deleted time entries are included in the response',
|
||||
},
|
||||
submittedByEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries submitted by this user ID',
|
||||
},
|
||||
approvedByEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries approved by this user ID',
|
||||
},
|
||||
rejectedByEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries rejected by this user ID',
|
||||
},
|
||||
createdAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries created after this epoch-millisecond timestamp',
|
||||
},
|
||||
createdAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries created before this epoch-millisecond timestamp',
|
||||
},
|
||||
updatedAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries updated after this epoch-millisecond timestamp',
|
||||
},
|
||||
updatedAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries updated before this epoch-millisecond timestamp',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How to combine filters: all (AND, default) or any (OR)',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by (minutes, date, id, billable)',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (default DESC)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to include in the response (notes, sourceType, deleted, status, submittedBy, submittedAt, approvedBy, approvedAt, rejectedBy, rejectedAt, costRate, billRate)',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of entries per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response for fetching the next page',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/time-entries`)
|
||||
if (params.dateEq) url.searchParams.set('date.eq', params.dateEq)
|
||||
if (params.dateGt) url.searchParams.set('date.gt', params.dateGt)
|
||||
if (params.dateGe) url.searchParams.set('date.ge', params.dateGe)
|
||||
if (params.dateLt) url.searchParams.set('date.lt', params.dateLt)
|
||||
if (params.dateLe) url.searchParams.set('date.le', params.dateLe)
|
||||
if (params.projectIdEq != null) {
|
||||
url.searchParams.set('projectId.eq', String(params.projectIdEq))
|
||||
}
|
||||
if (params.taskIdEq != null) url.searchParams.set('taskId.eq', String(params.taskIdEq))
|
||||
if (params.projectPhaseIdEq != null) {
|
||||
url.searchParams.set('projectPhase.eq', String(params.projectPhaseIdEq))
|
||||
}
|
||||
if (params.categoryIdEq != null) {
|
||||
url.searchParams.set('category.eq', String(params.categoryIdEq))
|
||||
}
|
||||
if (params.userIdEq != null) url.searchParams.set('user.eq', String(params.userIdEq))
|
||||
if (params.emailIdEq) url.searchParams.set('emailId.eq', params.emailIdEq)
|
||||
if (params.emailIdCn) url.searchParams.set('emailId.cn', params.emailIdCn)
|
||||
if (params.sourceTypeEq) url.searchParams.set('sourceType.eq', params.sourceTypeEq)
|
||||
if (params.activityNameEq) url.searchParams.set('activityName.eq', params.activityNameEq)
|
||||
if (params.activityNameCn) url.searchParams.set('activityName.cn', params.activityNameCn)
|
||||
if (params.approvalStatusEq) {
|
||||
url.searchParams.set('approvalStatus.eq', params.approvalStatusEq)
|
||||
}
|
||||
if (params.billableEq != null) {
|
||||
url.searchParams.set('billable.eq', String(params.billableEq))
|
||||
}
|
||||
if (params.includeDeletedEq != null) {
|
||||
url.searchParams.set('includeDeleted.eq', String(params.includeDeletedEq))
|
||||
}
|
||||
if (params.submittedByEq != null) {
|
||||
url.searchParams.set('submittedBy.eq', String(params.submittedByEq))
|
||||
}
|
||||
if (params.approvedByEq != null) {
|
||||
url.searchParams.set('approvedBy.eq', String(params.approvedByEq))
|
||||
}
|
||||
if (params.rejectedByEq != null) {
|
||||
url.searchParams.set('rejectedBy.eq', String(params.rejectedByEq))
|
||||
}
|
||||
if (params.createdAtGt != null) {
|
||||
url.searchParams.set('createdAt.gt', String(params.createdAtGt))
|
||||
}
|
||||
if (params.createdAtLt != null) {
|
||||
url.searchParams.set('createdAt.lt', String(params.createdAtLt))
|
||||
}
|
||||
if (params.updatedAtGt != null) {
|
||||
url.searchParams.set('updatedAt.gt', String(params.updatedAtGt))
|
||||
}
|
||||
if (params.updatedAtLt != null) {
|
||||
url.searchParams.set('updatedAt.lt', String(params.updatedAtLt))
|
||||
}
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const entries = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
timeEntries: entries.map(mapTimeEntry),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeEntries: {
|
||||
type: 'array',
|
||||
description: 'List of time entries matching the filters',
|
||||
items: { type: 'object', properties: TIME_ENTRY_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapTimeEntryCategory,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListTimeEntryCategoriesParams,
|
||||
type RocketlaneTimeEntryCategory,
|
||||
type RocketlaneTimeEntryCategoryListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_ENTRY_CATEGORY_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListTimeEntryCategoriesTool: ToolConfig<
|
||||
RocketlaneListTimeEntryCategoriesParams,
|
||||
RocketlaneTimeEntryCategoryListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_time_entry_categories',
|
||||
name: 'Rocketlane List Time Entry Categories',
|
||||
description: 'List the time entry categories configured in Rocketlane',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of categories per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response for fetching the next page',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/time-entries/categories`)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const rawCategories = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
categories: rawCategories
|
||||
.map(mapTimeEntryCategory)
|
||||
.filter(
|
||||
(
|
||||
category: RocketlaneTimeEntryCategory | null
|
||||
): category is RocketlaneTimeEntryCategory => category !== null
|
||||
),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
categories: {
|
||||
type: 'array',
|
||||
description: 'List of time entry categories',
|
||||
items: { type: 'object', properties: TIME_ENTRY_CATEGORY_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapTimeOff,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTimeOffListParams,
|
||||
type RocketlaneTimeOffListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_OFF_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListTimeOffsTool: ToolConfig<
|
||||
RocketlaneTimeOffListParams,
|
||||
RocketlaneTimeOffListResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_time_offs',
|
||||
name: 'Rocketlane List Time-Offs',
|
||||
description:
|
||||
'List time-offs in Rocketlane with optional date, type, and user filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of time-offs per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response (valid for 15 minutes)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional fields to include in the response: note, notifyUsers',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by: startDate, endDate, or createdAt',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (defaults to DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Combine filters with AND (all) or OR (any); defaults to all',
|
||||
},
|
||||
startDateGt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs with start dates greater than this date (YYYY-MM-DD)',
|
||||
},
|
||||
startDateEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs with start dates equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
startDateLt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs with start dates lesser than this date (YYYY-MM-DD)',
|
||||
},
|
||||
startDateGe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Return time-offs with start dates greater than or equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
startDateLe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Return time-offs with start dates lesser than or equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
endDateGt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs with end dates greater than this date (YYYY-MM-DD)',
|
||||
},
|
||||
endDateEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs with end dates equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
endDateLt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs with end dates lesser than this date (YYYY-MM-DD)',
|
||||
},
|
||||
endDateGe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Return time-offs with end dates greater than or equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
endDateLe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs with end dates lesser than or equal to this date (YYYY-MM-DD)',
|
||||
},
|
||||
typeEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs matching this type: FULL_DAY, HALF_DAY, or CUSTOM',
|
||||
},
|
||||
typeOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated time-off types to match any of (FULL_DAY, HALF_DAY, CUSTOM)',
|
||||
},
|
||||
typeNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated time-off types to match none of (FULL_DAY, HALF_DAY, CUSTOM)',
|
||||
},
|
||||
userIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs that exactly match this user ID',
|
||||
},
|
||||
userIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs to match any of',
|
||||
},
|
||||
userIdNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs to match none of',
|
||||
},
|
||||
emailIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return time-offs that exactly match this user email',
|
||||
},
|
||||
emailIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user emails to match any of',
|
||||
},
|
||||
emailIdNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user emails to match none of',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/time-offs`)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.includeFields?.length) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.startDateGt) url.searchParams.set('startDate.gt', params.startDateGt)
|
||||
if (params.startDateEq) url.searchParams.set('startDate.eq', params.startDateEq)
|
||||
if (params.startDateLt) url.searchParams.set('startDate.lt', params.startDateLt)
|
||||
if (params.startDateGe) url.searchParams.set('startDate.ge', params.startDateGe)
|
||||
if (params.startDateLe) url.searchParams.set('startDate.le', params.startDateLe)
|
||||
if (params.endDateGt) url.searchParams.set('endDate.gt', params.endDateGt)
|
||||
if (params.endDateEq) url.searchParams.set('endDate.eq', params.endDateEq)
|
||||
if (params.endDateLt) url.searchParams.set('endDate.lt', params.endDateLt)
|
||||
if (params.endDateGe) url.searchParams.set('endDate.ge', params.endDateGe)
|
||||
if (params.endDateLe) url.searchParams.set('endDate.le', params.endDateLe)
|
||||
if (params.typeEq) url.searchParams.set('type.eq', params.typeEq)
|
||||
if (params.typeOneOf) url.searchParams.set('type.oneOf', params.typeOneOf)
|
||||
if (params.typeNoneOf) url.searchParams.set('type.noneOf', params.typeNoneOf)
|
||||
if (params.userIdEq) url.searchParams.set('userId.eq', params.userIdEq)
|
||||
if (params.userIdOneOf) url.searchParams.set('userId.oneOf', params.userIdOneOf)
|
||||
if (params.userIdNoneOf) url.searchParams.set('userId.noneOf', params.userIdNoneOf)
|
||||
if (params.emailIdEq) url.searchParams.set('emailId.eq', params.emailIdEq)
|
||||
if (params.emailIdOneOf) url.searchParams.set('emailId.oneOf', params.emailIdOneOf)
|
||||
if (params.emailIdNoneOf) url.searchParams.set('emailId.noneOf', params.emailIdNoneOf)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const timeOffs = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
timeOffs: timeOffs.map(mapTimeOff),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeOffs: {
|
||||
type: 'array',
|
||||
description: 'List of time-offs',
|
||||
items: { type: 'object', properties: TIME_OFF_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,389 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapUser,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneListUsersParams,
|
||||
type RocketlaneListUsersResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
USER_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneListUsersTool: ToolConfig<
|
||||
RocketlaneListUsersParams,
|
||||
RocketlaneListUsersResponse
|
||||
> = {
|
||||
id: 'rocketlane_list_users',
|
||||
name: 'Rocketlane List Users',
|
||||
description:
|
||||
'List users in your Rocketlane account, with optional filters, sorting, and pagination',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of users per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous request (valid for 15 minutes)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to include: role, company, permission, holidayCalendar, capacityInMinutes, profilePictureUrl',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to include all fields in the response',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Field to sort by: email, firstName, lastName, type, status, or capacityInMinutes',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (defaults to DESC)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How to combine filters: all (AND) or any (OR); defaults to all',
|
||||
},
|
||||
firstNameEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose first name exactly matches this value',
|
||||
},
|
||||
firstNameCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose first name contains this value',
|
||||
},
|
||||
firstNameNc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Exclude users whose first name contains this value',
|
||||
},
|
||||
lastNameEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose last name exactly matches this value',
|
||||
},
|
||||
lastNameCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose last name contains this value',
|
||||
},
|
||||
lastNameNc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Exclude users whose last name contains this value',
|
||||
},
|
||||
emailEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose email exactly matches this value',
|
||||
},
|
||||
emailCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose email contains this value',
|
||||
},
|
||||
emailNc: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Exclude users whose email contains this value',
|
||||
},
|
||||
statusEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users with this status: INACTIVE, INVITED, ACTIVE, or PASSIVE',
|
||||
},
|
||||
statusOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated statuses; only include users matching one of them (INACTIVE, INVITED, ACTIVE, PASSIVE)',
|
||||
},
|
||||
statusNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated statuses; exclude users matching any of them (INACTIVE, INVITED, ACTIVE, PASSIVE)',
|
||||
},
|
||||
typeEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only include users of this type: TEAM_MEMBER, PARTNER, CUSTOMER, or EXTERNAL_PARTNER',
|
||||
},
|
||||
typeOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated types; only include users matching one of them (TEAM_MEMBER, PARTNER, CUSTOMER, EXTERNAL_PARTNER)',
|
||||
},
|
||||
roleIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users with this role ID',
|
||||
},
|
||||
roleIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated role IDs; only include users matching one of them',
|
||||
},
|
||||
roleIdNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated role IDs; exclude users matching any of them',
|
||||
},
|
||||
permissionIdEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users with this permission ID',
|
||||
},
|
||||
permissionIdOneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated permission IDs; only include users matching one of them',
|
||||
},
|
||||
permissionIdNoneOf: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated permission IDs; exclude users matching any of them',
|
||||
},
|
||||
capacityInMinutesEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose capacity in minutes equals this value',
|
||||
},
|
||||
capacityInMinutesGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose capacity in minutes is greater than this value',
|
||||
},
|
||||
capacityInMinutesGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only include users whose capacity in minutes is greater than or equal to this value',
|
||||
},
|
||||
capacityInMinutesLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users whose capacity in minutes is less than this value',
|
||||
},
|
||||
capacityInMinutesLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only include users whose capacity in minutes is less than or equal to this value',
|
||||
},
|
||||
createdAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users created after this time (epoch millis)',
|
||||
},
|
||||
createdAtEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users created at exactly this time (epoch millis)',
|
||||
},
|
||||
createdAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users created before this time (epoch millis)',
|
||||
},
|
||||
createdAtGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users created at or after this time (epoch millis)',
|
||||
},
|
||||
createdAtLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users created at or before this time (epoch millis)',
|
||||
},
|
||||
updatedAtGt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users updated after this time (epoch millis)',
|
||||
},
|
||||
updatedAtEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users updated at exactly this time (epoch millis)',
|
||||
},
|
||||
updatedAtLt: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users updated before this time (epoch millis)',
|
||||
},
|
||||
updatedAtGe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users updated at or after this time (epoch millis)',
|
||||
},
|
||||
updatedAtLe: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include users updated at or before this time (epoch millis)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/users`)
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null)
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.firstNameEq) url.searchParams.set('firstName.eq', params.firstNameEq)
|
||||
if (params.firstNameCn) url.searchParams.set('firstName.cn', params.firstNameCn)
|
||||
if (params.firstNameNc) url.searchParams.set('firstName.nc', params.firstNameNc)
|
||||
if (params.lastNameEq) url.searchParams.set('lastName.eq', params.lastNameEq)
|
||||
if (params.lastNameCn) url.searchParams.set('lastName.cn', params.lastNameCn)
|
||||
if (params.lastNameNc) url.searchParams.set('lastName.nc', params.lastNameNc)
|
||||
if (params.emailEq) url.searchParams.set('email.eq', params.emailEq)
|
||||
if (params.emailCn) url.searchParams.set('email.cn', params.emailCn)
|
||||
if (params.emailNc) url.searchParams.set('email.nc', params.emailNc)
|
||||
if (params.statusEq) url.searchParams.set('status.eq', params.statusEq)
|
||||
if (params.statusOneOf) url.searchParams.set('status.oneOf', params.statusOneOf)
|
||||
if (params.statusNoneOf) url.searchParams.set('status.noneOf', params.statusNoneOf)
|
||||
if (params.typeEq) url.searchParams.set('type.eq', params.typeEq)
|
||||
if (params.typeOneOf) url.searchParams.set('type.oneOf', params.typeOneOf)
|
||||
if (params.roleIdEq) url.searchParams.set('roleId.eq', params.roleIdEq)
|
||||
if (params.roleIdOneOf) url.searchParams.set('roleId.oneOf', params.roleIdOneOf)
|
||||
if (params.roleIdNoneOf) url.searchParams.set('roleId.noneOf', params.roleIdNoneOf)
|
||||
if (params.permissionIdEq) url.searchParams.set('permissionId.eq', params.permissionIdEq)
|
||||
if (params.permissionIdOneOf)
|
||||
url.searchParams.set('permissionId.oneOf', params.permissionIdOneOf)
|
||||
if (params.permissionIdNoneOf)
|
||||
url.searchParams.set('permissionId.noneOf', params.permissionIdNoneOf)
|
||||
if (params.capacityInMinutesEq != null)
|
||||
url.searchParams.set('capacityInMinutes.eq', String(params.capacityInMinutesEq))
|
||||
if (params.capacityInMinutesGt != null)
|
||||
url.searchParams.set('capacityInMinutes.gt', String(params.capacityInMinutesGt))
|
||||
if (params.capacityInMinutesGe != null)
|
||||
url.searchParams.set('capacityInMinutes.ge', String(params.capacityInMinutesGe))
|
||||
if (params.capacityInMinutesLt != null)
|
||||
url.searchParams.set('capacityInMinutes.lt', String(params.capacityInMinutesLt))
|
||||
if (params.capacityInMinutesLe != null)
|
||||
url.searchParams.set('capacityInMinutes.le', String(params.capacityInMinutesLe))
|
||||
if (params.createdAtGt != null)
|
||||
url.searchParams.set('createdAt.gt', String(params.createdAtGt))
|
||||
if (params.createdAtEq != null)
|
||||
url.searchParams.set('createdAt.eq', String(params.createdAtEq))
|
||||
if (params.createdAtLt != null)
|
||||
url.searchParams.set('createdAt.lt', String(params.createdAtLt))
|
||||
if (params.createdAtGe != null)
|
||||
url.searchParams.set('createdAt.ge', String(params.createdAtGe))
|
||||
if (params.createdAtLe != null)
|
||||
url.searchParams.set('createdAt.le', String(params.createdAtLe))
|
||||
if (params.updatedAtGt != null)
|
||||
url.searchParams.set('updatedAt.gt', String(params.updatedAtGt))
|
||||
if (params.updatedAtEq != null)
|
||||
url.searchParams.set('updatedAt.eq', String(params.updatedAtEq))
|
||||
if (params.updatedAtLt != null)
|
||||
url.searchParams.set('updatedAt.lt', String(params.updatedAtLt))
|
||||
if (params.updatedAtGe != null)
|
||||
url.searchParams.set('updatedAt.ge', String(params.updatedAtGe))
|
||||
if (params.updatedAtLe != null)
|
||||
url.searchParams.set('updatedAt.le', String(params.updatedAtLe))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const users = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
users: users.map(mapUser),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
users: {
|
||||
type: 'array',
|
||||
description: 'List of users',
|
||||
items: { type: 'object', properties: USER_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneMoveTaskToPhaseParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneMoveTaskToPhaseTool: ToolConfig<
|
||||
RocketlaneMoveTaskToPhaseParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_move_task_to_phase',
|
||||
name: 'Rocketlane Move Task To Phase',
|
||||
description: 'Move a Rocketlane task to a given phase, associating the task with that phase',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to move',
|
||||
},
|
||||
phaseId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the phase to move the task to',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}/move-phase`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
phase: { phaseId: params.phaseId },
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The task with its updated phase',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
mapProject,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneProjectResponse,
|
||||
type RocketlaneRemoveProjectMembersParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneRemoveProjectMembersTool: ToolConfig<
|
||||
RocketlaneRemoveProjectMembersParams,
|
||||
RocketlaneProjectResponse
|
||||
> = {
|
||||
id: 'rocketlane_remove_project_members',
|
||||
name: 'Rocketlane Remove Project Members',
|
||||
description: 'Remove team members from a Rocketlane project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'User IDs of team members to remove (at least one of memberUserIds or memberEmailIds is required)',
|
||||
items: { type: 'number', description: 'User ID of a team member' },
|
||||
},
|
||||
memberEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Emails of team members to remove (at least one of memberUserIds or memberEmailIds is required)',
|
||||
items: { type: 'string', description: 'Email of a team member' },
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}/remove-members`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const members = [
|
||||
...(params.memberUserIds ?? []).map((userId) => ({ userId })),
|
||||
...(params.memberEmailIds ?? []).map((emailId) => ({ emailId })),
|
||||
]
|
||||
return { members }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { project: mapProject(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The project with its updated team members',
|
||||
properties: PROJECT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
buildTaskMembers,
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTaskAssigneesParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneRemoveTaskAssigneesTool: ToolConfig<
|
||||
RocketlaneTaskAssigneesParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_remove_task_assignees',
|
||||
name: 'Rocketlane Remove Task Assignees',
|
||||
description: 'Remove assignees from a Rocketlane task',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to remove assignees from',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of members to remove from the assignees',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
memberEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email addresses of members to remove from the assignees',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}/remove-assignees`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
members: buildTaskMembers(params.memberUserIds, params.memberEmailIds),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The task with its updated assignees',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTaskDependenciesParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneRemoveTaskDependenciesTool: ToolConfig<
|
||||
RocketlaneTaskDependenciesParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_remove_task_dependencies',
|
||||
name: 'Rocketlane Remove Task Dependencies',
|
||||
description: 'Remove dependencies from a Rocketlane task',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to remove dependencies from',
|
||||
},
|
||||
dependencyTaskIds: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Task IDs to remove from the task dependencies',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}/remove-dependencies`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
dependencies: params.dependencyTaskIds.map((taskId) => ({ taskId })),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The task with its updated dependencies',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
buildTaskMembers,
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTaskFollowersParams,
|
||||
type RocketlaneTaskResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneRemoveTaskFollowersTool: ToolConfig<
|
||||
RocketlaneTaskFollowersParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_remove_task_followers',
|
||||
name: 'Rocketlane Remove Task Followers',
|
||||
description: 'Remove followers from a Rocketlane task',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to remove followers from',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'User IDs of members to remove from the followers',
|
||||
items: { type: 'number' },
|
||||
},
|
||||
memberEmailIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email addresses of members to remove from the followers',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}/remove-followers`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
members: buildTaskMembers(params.memberUserIds, params.memberEmailIds),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The task with its updated followers',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
import {
|
||||
mapPagination,
|
||||
mapTimeEntry,
|
||||
PAGINATION_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneSearchTimeEntriesParams,
|
||||
type RocketlaneTimeEntryListResponse,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_ENTRY_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneSearchTimeEntriesTool: ToolConfig<
|
||||
RocketlaneSearchTimeEntriesParams,
|
||||
RocketlaneTimeEntryListResponse
|
||||
> = {
|
||||
id: 'rocketlane_search_time_entries',
|
||||
name: 'Rocketlane Search Time Entries',
|
||||
description:
|
||||
'Search Rocketlane time entries with filters, sorting, and pagination (deprecated by Rocketlane in favor of listing time entries with filters)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
dateEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries on this exact date (YYYY-MM-DD)',
|
||||
},
|
||||
dateGt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries after this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateGe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries on or after this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateLt: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries before this date (YYYY-MM-DD)',
|
||||
},
|
||||
dateLe: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries on or before this date (YYYY-MM-DD)',
|
||||
},
|
||||
projectEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries for this project ID',
|
||||
},
|
||||
taskEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries for this task ID',
|
||||
},
|
||||
projectPhaseIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries for this project phase ID',
|
||||
},
|
||||
categoryIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries with this category ID',
|
||||
},
|
||||
userIdEq: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries belonging to this user ID',
|
||||
},
|
||||
sourceTypeEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only entries with this source type (GOOGLE_CALENDAR, OUTLOOK_CALENDAR, TASK, PROJECT, PHASE, ADHOC, MILESTONE)',
|
||||
},
|
||||
activityNameEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries with this exact activity name',
|
||||
},
|
||||
activityNameCn: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only entries whose activity name contains this text',
|
||||
},
|
||||
approvalStatusEq: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Only entries with this approval status (NOT_SUBMITTED, SUBMITTED, APPROVED, REJECTED)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How to combine filters: all (AND, default) or any (OR)',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by (minutes, date, id, billable)',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order: ASC or DESC (default DESC)',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to include in the response (notes, sourceType, deleted, status, submittedBy, submittedAt, approvedBy, approvedAt, rejectedBy, rejectedAt, costRate, billRate)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether all fields should be returned in the response',
|
||||
},
|
||||
pageSize: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of entries per page (defaults to 100)',
|
||||
},
|
||||
pageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page token from a previous response for fetching the next page',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(`${ROCKETLANE_API_BASE}/time-entries/search`)
|
||||
if (params.dateEq) url.searchParams.set('date.eq', params.dateEq)
|
||||
if (params.dateGt) url.searchParams.set('date.gt', params.dateGt)
|
||||
if (params.dateGe) url.searchParams.set('date.ge', params.dateGe)
|
||||
if (params.dateLt) url.searchParams.set('date.lt', params.dateLt)
|
||||
if (params.dateLe) url.searchParams.set('date.le', params.dateLe)
|
||||
if (params.projectEq != null) url.searchParams.set('project.eq', String(params.projectEq))
|
||||
if (params.taskEq != null) url.searchParams.set('task.eq', String(params.taskEq))
|
||||
if (params.projectPhaseIdEq != null) {
|
||||
url.searchParams.set('projectPhase.eq', String(params.projectPhaseIdEq))
|
||||
}
|
||||
if (params.categoryIdEq != null) {
|
||||
url.searchParams.set('category.eq', String(params.categoryIdEq))
|
||||
}
|
||||
if (params.userIdEq != null) url.searchParams.set('user.eq', String(params.userIdEq))
|
||||
if (params.sourceTypeEq) url.searchParams.set('sourceType.eq', params.sourceTypeEq)
|
||||
if (params.activityNameEq) url.searchParams.set('activityName.eq', params.activityNameEq)
|
||||
if (params.activityNameCn) url.searchParams.set('activityName.cn', params.activityNameCn)
|
||||
if (params.approvalStatusEq) {
|
||||
url.searchParams.set('approvalStatus.eq', params.approvalStatusEq)
|
||||
}
|
||||
if (params.match) url.searchParams.set('match', params.match)
|
||||
if (params.sortBy) url.searchParams.set('sortBy', params.sortBy)
|
||||
if (params.sortOrder) url.searchParams.set('sortOrder', params.sortOrder)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
if (params.pageSize != null) url.searchParams.set('pageSize', String(params.pageSize))
|
||||
if (params.pageToken) url.searchParams.set('pageToken', params.pageToken)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
const entries = Array.isArray(data?.data) ? data.data : []
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
timeEntries: entries.map(mapTimeEntry),
|
||||
pagination: mapPagination(data?.pagination),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeEntries: {
|
||||
type: 'array',
|
||||
description: 'List of time entries matching the search filters',
|
||||
items: { type: 'object', properties: TIME_ENTRY_OUTPUT_PROPERTIES },
|
||||
},
|
||||
pagination: {
|
||||
type: 'object',
|
||||
description: 'Pagination details for the result set',
|
||||
properties: PAGINATION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
import {
|
||||
mapPlaceholderMapping,
|
||||
mapProject,
|
||||
PLACEHOLDER_MAPPING_OUTPUT_PROPERTIES,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneProjectPlaceholdersResponse,
|
||||
type RocketlaneUnassignPlaceholdersParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUnassignPlaceholdersTool: ToolConfig<
|
||||
RocketlaneUnassignPlaceholdersParams,
|
||||
RocketlaneProjectPlaceholdersResponse
|
||||
> = {
|
||||
id: 'rocketlane_unassign_placeholders',
|
||||
name: 'Rocketlane Unassign Placeholders',
|
||||
description: 'Unassign a placeholder from its user in a Rocketlane project',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project',
|
||||
},
|
||||
placeholderId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the placeholder to unassign',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}/unassign-placeholders`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => [{ placeholderId: params.placeholderId }],
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
project: mapProject(data),
|
||||
placeholders: Array.isArray(data?.placeholders)
|
||||
? data.placeholders.map(mapPlaceholderMapping)
|
||||
: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The project after the placeholder was unassigned',
|
||||
properties: PROJECT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
placeholders: {
|
||||
type: 'array',
|
||||
description: 'Placeholder-to-user mappings on the project',
|
||||
items: { type: 'object', properties: PLACEHOLDER_MAPPING_OUTPUT_PROPERTIES },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import {
|
||||
FIELD_OUTPUT_PROPERTIES,
|
||||
mapField,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneFieldResponse,
|
||||
type RocketlaneUpdateFieldParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUpdateFieldTool: ToolConfig<
|
||||
RocketlaneUpdateFieldParams,
|
||||
RocketlaneFieldResponse
|
||||
> = {
|
||||
id: 'rocketlane_update_field',
|
||||
name: 'Rocketlane Update Field',
|
||||
description:
|
||||
'Update the label, description, enabled state, or privacy of an existing Rocketlane field',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
fieldId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the field to update',
|
||||
},
|
||||
fieldLabel: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name of the field',
|
||||
},
|
||||
fieldDescription: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description of the field',
|
||||
},
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the field is enabled',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the field is private',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra field properties to include in the response (supported: options)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return all field properties in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/fields/${encodeURIComponent(String(params.fieldId))}`
|
||||
)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'PUT',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
...(params.fieldLabel != null && { fieldLabel: params.fieldLabel }),
|
||||
...(params.fieldDescription != null && { fieldDescription: params.fieldDescription }),
|
||||
...(params.enabled != null && { enabled: params.enabled }),
|
||||
...(params.private != null && { private: params.private }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { field: mapField(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
field: {
|
||||
type: 'object',
|
||||
description: 'The updated field',
|
||||
properties: FIELD_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import {
|
||||
FIELD_OPTION_OUTPUT_PROPERTIES,
|
||||
mapFieldOption,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneFieldOptionResponse,
|
||||
type RocketlaneUpdateFieldOptionParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUpdateFieldOptionTool: ToolConfig<
|
||||
RocketlaneUpdateFieldOptionParams,
|
||||
RocketlaneFieldOptionResponse
|
||||
> = {
|
||||
id: 'rocketlane_update_field_option',
|
||||
name: 'Rocketlane Update Field Option',
|
||||
description:
|
||||
'Update the label or color of an existing option on a SINGLE_CHOICE or MULTIPLE_CHOICE Rocketlane field',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
fieldId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the field the option belongs to',
|
||||
},
|
||||
optionValue: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the option to update',
|
||||
},
|
||||
optionLabel: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New display label of the option',
|
||||
},
|
||||
optionColor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'New color of the option (RED, YELLOW, GREEN, TEAL, CYAN, BLUE, PURPLE, MAGENTA, GRAY, COOL_GRAY)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/fields/${encodeURIComponent(String(params.fieldId))}/update-option`,
|
||||
method: 'POST',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
optionValue: params.optionValue,
|
||||
...(params.optionLabel != null && { optionLabel: params.optionLabel }),
|
||||
...(params.optionColor != null && { optionColor: params.optionColor }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { option: mapFieldOption(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
option: {
|
||||
type: 'object',
|
||||
description: 'The updated field option',
|
||||
properties: FIELD_OPTION_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import {
|
||||
mapPhase,
|
||||
PHASE_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlanePhaseResponse,
|
||||
type RocketlaneUpdatePhaseParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUpdatePhaseTool: ToolConfig<
|
||||
RocketlaneUpdatePhaseParams,
|
||||
RocketlanePhaseResponse
|
||||
> = {
|
||||
id: 'rocketlane_update_phase',
|
||||
name: 'Rocketlane Update Phase',
|
||||
description: 'Update the name, dates, status, or privacy of an existing Rocketlane phase',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
phaseId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the phase to update',
|
||||
},
|
||||
phaseName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name of the phase',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New planned start date of the phase (YYYY-MM-DD)',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New planned due date of the phase (YYYY-MM-DD)',
|
||||
},
|
||||
statusValue: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New numeric status value for the phase',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the phase is private',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra phase properties to include in the response (supported: startDateActual, dueDateActual)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to return all phase properties in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/phases/${encodeURIComponent(String(params.phaseId))}`
|
||||
)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'PUT',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => ({
|
||||
...(params.phaseName != null && { phaseName: params.phaseName }),
|
||||
...(params.startDate != null && { startDate: params.startDate }),
|
||||
...(params.dueDate != null && { dueDate: params.dueDate }),
|
||||
...(params.statusValue != null && { status: { value: params.statusValue } }),
|
||||
...(params.private != null && { private: params.private }),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { phase: mapPhase(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
phase: {
|
||||
type: 'object',
|
||||
description: 'The updated phase',
|
||||
properties: PHASE_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
import {
|
||||
mapProject,
|
||||
PROJECT_OUTPUT_PROPERTIES,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneProjectResponse,
|
||||
type RocketlaneUpdateProjectParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUpdateProjectTool: ToolConfig<
|
||||
RocketlaneUpdateProjectParams,
|
||||
RocketlaneProjectResponse
|
||||
> = {
|
||||
id: 'rocketlane_update_project',
|
||||
name: 'Rocketlane Update Project',
|
||||
description:
|
||||
'Update a Rocketlane project by ID, including name, dates, visibility, owner, status, and custom fields',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
projectId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the project to update',
|
||||
},
|
||||
projectName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name of the project',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date on which the project begins (YYYY-MM-DD)',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Date on which the project is planned to complete (YYYY-MM-DD, on or after startDate)',
|
||||
},
|
||||
visibility: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Who can see the project: EVERYONE or MEMBERS',
|
||||
},
|
||||
ownerUserId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'User ID of the new project owner (transfers ownership and revokes access for the previous owner)',
|
||||
},
|
||||
ownerEmailId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email of the new project owner',
|
||||
},
|
||||
statusValue: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Value (identifier) of the project status',
|
||||
},
|
||||
fields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Custom field assignments, each with fieldId and fieldValue (string, number, or number array matching the field type)',
|
||||
items: {
|
||||
type: 'object',
|
||||
description: 'Custom field assignment',
|
||||
properties: {
|
||||
fieldId: { type: 'number', description: 'Unique identifier of the field' },
|
||||
fieldValue: {
|
||||
type: 'string',
|
||||
description: 'Value of the field (string, number, or number array)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
annualizedRecurringRevenue: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Recurring revenue of the customer subscriptions for a single calendar year',
|
||||
},
|
||||
projectFee: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Total fee charged for the project',
|
||||
},
|
||||
autoAllocation: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether auto allocation is enabled for the project',
|
||||
},
|
||||
budgetedHours: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Total hours allocated for project execution (decimal, up to two places)',
|
||||
},
|
||||
externalReferenceId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Identifier linking the project to an external system',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to return in the response (e.g. budgetedHours,progressPercentage)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Return all fields in the response body',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/projects/${encodeURIComponent(String(params.projectId))}`
|
||||
)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null)
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
return url.toString()
|
||||
},
|
||||
method: 'PUT',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.projectName !== undefined) body.projectName = params.projectName
|
||||
if (params.startDate !== undefined) body.startDate = params.startDate
|
||||
if (params.dueDate !== undefined) body.dueDate = params.dueDate
|
||||
if (params.visibility) body.visibility = params.visibility
|
||||
const owner: Record<string, unknown> = {}
|
||||
if (params.ownerUserId != null) owner.userId = params.ownerUserId
|
||||
if (params.ownerEmailId) owner.emailId = params.ownerEmailId
|
||||
if (Object.keys(owner).length > 0) body.owner = owner
|
||||
if (params.statusValue != null) body.status = { value: params.statusValue }
|
||||
if (params.fields && params.fields.length > 0) body.fields = params.fields
|
||||
if (params.annualizedRecurringRevenue != null)
|
||||
body.annualizedRecurringRevenue = params.annualizedRecurringRevenue
|
||||
if (params.projectFee != null) body.projectFee = params.projectFee
|
||||
if (params.autoAllocation != null) body.autoAllocation = params.autoAllocation
|
||||
if (params.budgetedHours != null) body.budgetedHours = params.budgetedHours
|
||||
if (params.externalReferenceId !== undefined)
|
||||
body.externalReferenceId = params.externalReferenceId
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { project: mapProject(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
project: {
|
||||
type: 'object',
|
||||
description: 'The updated project',
|
||||
properties: PROJECT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import {
|
||||
mapSpace,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneSpaceResponse,
|
||||
type RocketlaneUpdateSpaceParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
SPACE_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUpdateSpaceTool: ToolConfig<
|
||||
RocketlaneUpdateSpaceParams,
|
||||
RocketlaneSpaceResponse
|
||||
> = {
|
||||
id: 'rocketlane_update_space',
|
||||
name: 'Rocketlane Update Space',
|
||||
description: 'Update a Rocketlane space by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
spaceId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space to update',
|
||||
},
|
||||
spaceName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name of the space',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${ROCKETLANE_API_BASE}/spaces/${encodeURIComponent(params.spaceId)}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.spaceName != null) body.spaceName = params.spaceName
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { space: mapSpace(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
space: {
|
||||
type: 'object',
|
||||
description: 'The updated space',
|
||||
properties: SPACE_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
mapSpaceDocument,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneSpaceDocumentResponse,
|
||||
type RocketlaneUpdateSpaceDocumentParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
SPACE_DOCUMENT_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUpdateSpaceDocumentTool: ToolConfig<
|
||||
RocketlaneUpdateSpaceDocumentParams,
|
||||
RocketlaneSpaceDocumentResponse
|
||||
> = {
|
||||
id: 'rocketlane_update_space_document',
|
||||
name: 'Rocketlane Update Space Document',
|
||||
description: 'Update a Rocketlane space document by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
spaceDocumentId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the space document to update',
|
||||
},
|
||||
spaceDocumentName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name of the space document',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New URL to embed in the space document (for embedded documents)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${ROCKETLANE_API_BASE}/space-documents/${encodeURIComponent(params.spaceDocumentId)}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.spaceDocumentName != null) body.spaceDocumentName = params.spaceDocumentName
|
||||
if (params.url != null) body.url = params.url
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { spaceDocument: mapSpaceDocument(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
spaceDocument: {
|
||||
type: 'object',
|
||||
description: 'The updated space document',
|
||||
properties: SPACE_DOCUMENT_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
import {
|
||||
mapTask,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTaskResponse,
|
||||
type RocketlaneUpdateTaskParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TASK_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUpdateTaskTool: ToolConfig<
|
||||
RocketlaneUpdateTaskParams,
|
||||
RocketlaneTaskResponse
|
||||
> = {
|
||||
id: 'rocketlane_update_task',
|
||||
name: 'Rocketlane Update Task',
|
||||
description: 'Update the properties of an existing Rocketlane task by its unique identifier',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
taskId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unique identifier of the task to update',
|
||||
},
|
||||
taskName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name of the task',
|
||||
},
|
||||
taskDescription: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of the task in HTML format',
|
||||
},
|
||||
taskPrivateNote: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Private note visible only to team members, in HTML format',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date when the task starts (YYYY-MM-DD)',
|
||||
},
|
||||
dueDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date when the task is due, on or after the start date (YYYY-MM-DD)',
|
||||
},
|
||||
effortInMinutes: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Expected effort to complete the task, in minutes',
|
||||
},
|
||||
progress: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Progress of the task (0-100)',
|
||||
},
|
||||
atRisk: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the task is marked as At Risk',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Type of the task: TASK or MILESTONE',
|
||||
},
|
||||
statusValue: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Status value to set on the task',
|
||||
},
|
||||
externalReferenceId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'External reference identifier linking the task to an external system',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the task is private',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Extra fields to include in the response (startDateActual, dueDateActual, type, phase, assignees, followers, dependencies, billable, csatEnabled, priority, timeEntryCategory, financialsBudget, taskPrivateNote, parent, externalReferenceId)',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether all fields should be returned in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/tasks/${encodeURIComponent(String(params.taskId))}`
|
||||
)
|
||||
if (params.includeFields && params.includeFields.length > 0) {
|
||||
url.searchParams.set('includeFields', params.includeFields.join(','))
|
||||
}
|
||||
if (params.includeAllFields !== undefined) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'PUT',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.taskName !== undefined) body.taskName = params.taskName
|
||||
if (params.taskDescription !== undefined) body.taskDescription = params.taskDescription
|
||||
if (params.taskPrivateNote !== undefined) body.taskPrivateNote = params.taskPrivateNote
|
||||
if (params.startDate !== undefined) body.startDate = params.startDate
|
||||
if (params.dueDate !== undefined) body.dueDate = params.dueDate
|
||||
if (params.effortInMinutes !== undefined) body.effortInMinutes = params.effortInMinutes
|
||||
if (params.progress !== undefined) body.progress = params.progress
|
||||
if (params.atRisk !== undefined) body.atRisk = params.atRisk
|
||||
if (params.type !== undefined) body.type = params.type
|
||||
if (params.statusValue !== undefined) body.status = { value: params.statusValue }
|
||||
if (params.externalReferenceId !== undefined) {
|
||||
body.externalReferenceId = params.externalReferenceId
|
||||
}
|
||||
if (params.private !== undefined) body.private = params.private
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { task: mapTask(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
task: {
|
||||
type: 'object',
|
||||
description: 'The updated task',
|
||||
properties: TASK_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import {
|
||||
mapTimeEntry,
|
||||
ROCKETLANE_API_BASE,
|
||||
type RocketlaneTimeEntryResponse,
|
||||
type RocketlaneUpdateTimeEntryParams,
|
||||
rocketlaneError,
|
||||
rocketlaneHeaders,
|
||||
TIME_ENTRY_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/rocketlane/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const rocketlaneUpdateTimeEntryTool: ToolConfig<
|
||||
RocketlaneUpdateTimeEntryParams,
|
||||
RocketlaneTimeEntryResponse
|
||||
> = {
|
||||
id: 'rocketlane_update_time_entry',
|
||||
name: 'Rocketlane Update Time Entry',
|
||||
description:
|
||||
'Update a Rocketlane time entry by ID. The activityName, notes, billable, and minutes properties can be updated',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Rocketlane API key',
|
||||
},
|
||||
timeEntryId: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the time entry to update',
|
||||
},
|
||||
date: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Date of the time entry in YYYY-MM-DD format (mandatory so the total time for the date does not exceed 24 hours)',
|
||||
},
|
||||
minutes: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Duration of the time entry in minutes (1-1440)',
|
||||
},
|
||||
activityName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name for the adhoc activity',
|
||||
},
|
||||
notes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New notes for the time entry',
|
||||
},
|
||||
billable: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether the time entry is billable',
|
||||
},
|
||||
categoryId: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the time entry category',
|
||||
},
|
||||
includeFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated extra fields to include in the response (notes, sourceType, deleted, status, submittedBy, submittedAt, approvedBy, approvedAt, rejectedBy, rejectedAt, costRate, billRate)',
|
||||
},
|
||||
includeAllFields: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether all fields should be returned in the response',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL(
|
||||
`${ROCKETLANE_API_BASE}/time-entries/${encodeURIComponent(params.timeEntryId)}`
|
||||
)
|
||||
if (params.includeFields) url.searchParams.set('includeFields', params.includeFields)
|
||||
if (params.includeAllFields != null) {
|
||||
url.searchParams.set('includeAllFields', String(params.includeAllFields))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'PUT',
|
||||
headers: (params) => rocketlaneHeaders(params.apiKey),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
date: params.date,
|
||||
minutes: params.minutes,
|
||||
}
|
||||
if (params.activityName !== undefined) body.activityName = params.activityName
|
||||
if (params.notes !== undefined) body.notes = params.notes
|
||||
if (params.billable != null) body.billable = params.billable
|
||||
if (params.categoryId != null) body.category = { categoryId: params.categoryId }
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(await rocketlaneError(response))
|
||||
}
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: { timeEntry: mapTimeEntry(data) },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeEntry: {
|
||||
type: 'object',
|
||||
description: 'The updated time entry',
|
||||
properties: TIME_ENTRY_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user