mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-10 06:35:01 -05:00
improvement(jsm): destructured outputs for jsm, jira, and added 1password integration
This commit is contained in:
@@ -33,21 +33,21 @@ export function AgentIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M15.6667 9.25H4.66667C2.64162 9.25 1 10.8916 1 12.9167V18.4167C1 20.4417 2.64162 22.0833 4.66667 22.0833H15.6667C17.6917 22.0833 19.3333 20.4417 19.3333 18.4167V12.9167C19.3333 10.8916 17.6917 9.25 15.6667 9.25Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M10.1663 5.58464C11.1789 5.58464 11.9997 4.76382 11.9997 3.7513C11.9997 2.73878 11.1789 1.91797 10.1663 1.91797C9.15382 1.91797 8.33301 2.73878 8.33301 3.7513C8.33301 4.76382 9.15382 5.58464 10.1663 5.58464Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M10.167 5.58594V9.2526M7.41699 16.5859V14.7526M12.917 14.7526V16.5859'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -68,7 +68,7 @@ export function ApiIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M5.61111 24.3889C8.5 27.2778 12.4722 25.4722 13.5556 24.3889L15.7222 22.2222L7.77778 14.2778L5.61111 16.4444C4.52778 17.5278 2.72222 21.5 5.61111 24.3889ZM5.61111 24.3889L2 28M24.3889 5.61111C21.5 2.72222 17.5278 4.52778 16.4444 5.61111L14.2778 7.77778L22.2222 15.7222L24.3889 13.5556C25.4722 12.4722 27.2778 8.5 24.3889 5.61111ZM24.3889 5.61111L28 2M15.7222 9.22222L12.8333 12.1111M20.7778 14.2778L17.8889 17.1667'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -90,7 +90,7 @@ export function ConditionalIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<path
|
||||
d='M23.1015 1.01616C22.1825 1.01442 21.2926 1.33795 20.5894 1.92946C19.8861 2.52098 19.4149 3.34229 19.2592 4.24794C19.1035 5.15358 19.2733 6.08512 19.7386 6.87755C20.2039 7.66998 20.9346 8.27217 21.8014 8.57745V12.7169H6.20035V8.57745C7.06779 8.27077 7.79888 7.6673 8.26441 6.87372C8.72994 6.08013 8.89994 5.14752 8.74435 4.24072C8.58877 3.33391 8.11762 2.5113 7.41419 1.91828C6.71075 1.32526 5.82032 1 4.90027 1C3.98021 1 3.08978 1.32526 2.38634 1.91828C1.68291 2.5113 1.21176 3.33391 1.05618 4.24072C0.900594 5.14752 1.07059 6.08013 1.53612 6.87372C2.00165 7.6673 2.73274 8.27077 3.60018 8.57745V12.7169C3.60018 13.4065 3.87413 14.0679 4.36175 14.5555C4.84938 15.0432 5.51074 15.3171 6.20035 15.3171H12.7008V20.7567C11.8333 21.0633 11.1023 21.6668 10.6367 22.4604C10.1712 23.254 10.0012 24.1866 10.1568 25.0934C10.3124 26.0002 10.7835 26.8228 11.4869 27.4158C12.1904 28.0089 13.0808 28.3341 14.0009 28.3341C14.9209 28.3341 15.8114 28.0089 16.5148 27.4158C17.2182 26.8228 17.6894 26.0002 17.845 25.0934C18.0005 24.1866 17.8305 23.254 17.365 22.4604C16.8995 21.6668 16.1684 21.0633 15.301 20.7567V15.3171H21.8014C22.491 15.3171 23.1524 15.0432 23.64 14.5555C24.1276 14.0679 24.4015 13.4065 24.4015 12.7169V8.57745C25.2683 8.27217 25.999 7.66998 26.4643 6.87755C26.9296 6.08512 27.0994 5.15358 26.9437 4.24794C26.788 3.34229 26.3168 2.52098 25.6135 1.92946C24.9103 1.33795 24.0204 1.01442 23.1015 1.01616ZM4.90027 6.2165C4.64313 6.2165 4.39177 6.14025 4.17798 5.99739C3.96418 5.85454 3.79754 5.65149 3.69914 5.41393C3.60074 5.17637 3.575 4.91497 3.62516 4.66278C3.67532 4.41059 3.79915 4.17893 3.98097 3.99711C4.16279 3.81529 4.39444 3.69147 4.64663 3.64131C4.89882 3.59114 5.16023 3.61689 5.39779 3.71529C5.63535 3.81369 5.83839 3.98033 5.98125 4.19412C6.1241 4.40792 6.20035 4.65928 6.20035 4.91641C6.20035 5.26122 6.06338 5.5919 5.81956 5.83571C5.57575 6.07953 5.24507 6.2165 4.90027 6.2165ZM14.0009 25.7178C13.7437 25.7178 13.4924 25.6415 13.2786 25.4987C13.0648 25.3558 12.8981 25.1528 12.7997 24.9152C12.7013 24.6777 12.6756 24.4163 12.7258 24.1641C12.7759 23.9119 12.8997 23.6802 13.0816 23.4984C13.2634 23.3166 13.495 23.1928 13.7472 23.1426C13.9994 23.0924 14.2608 23.1182 14.4984 23.2166C14.7359 23.315 14.939 23.4816 15.0818 23.6954C15.2247 23.9092 15.301 24.1606 15.301 24.4177C15.301 24.7625 15.164 25.0932 14.9202 25.337C14.6764 25.5808 14.3457 25.7178 14.0009 25.7178ZM23.1015 6.2165C22.8443 6.2165 22.593 6.14025 22.3792 5.99739C22.1654 5.85454 21.9987 5.65149 21.9003 5.41393C21.8019 5.17637 21.7762 4.91497 21.8264 4.66278C21.8765 4.41059 22.0003 4.17893 22.1822 3.99711C22.364 3.81529 22.5956 3.69147 22.8478 3.64131C23.1 3.59114 23.3614 3.61689 23.599 3.71529C23.8365 3.81369 24.0396 3.98033 24.1824 4.19412C24.3253 4.40792 24.4015 4.65928 24.4015 4.91641C24.4015 5.26122 24.2646 5.5919 24.0208 5.83571C23.777 6.07953 23.4463 6.2165 23.1015 6.2165Z'
|
||||
fill='currentColor'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='0.25'
|
||||
/>
|
||||
</svg>
|
||||
@@ -113,7 +113,7 @@ export function NoteIcon(props: SVGProps<SVGSVGElement>) {
|
||||
width='16'
|
||||
height='18'
|
||||
rx='2.5'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
fill='none'
|
||||
/>
|
||||
@@ -139,7 +139,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
cx='12'
|
||||
cy='6'
|
||||
r='3'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -151,7 +151,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
width='8'
|
||||
x='2'
|
||||
y='16'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -163,7 +163,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
width='8'
|
||||
x='14'
|
||||
y='16'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -171,7 +171,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<path
|
||||
className='a'
|
||||
d='M6,16V14a2,2,0,0,1,2-2h8a2,2,0,0,1,2,2v2'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -182,7 +182,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
x2='12'
|
||||
y1='9'
|
||||
y2='12'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -203,7 +203,7 @@ export function SignalIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M23.964 33.2C26.252 28.416 27.5 23.216 27.5 17.6C27.5 11.984 26.252 6.576 23.964 2M16.476 29.664C18.14 25.92 19.18 21.76 19.18 17.6C19.18 13.44 18.14 9.072 16.476 5.328M8.988 26.128C10.236 23.424 10.86 20.512 10.86 17.6C10.86 14.688 10.236 11.568 8.988 9.072M1.5 22.384C2.124 20.928 2.54 19.264 2.54 17.6C2.54 15.936 2.124 14.064 1.5 12.608'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -224,7 +224,7 @@ export function CalendarIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M21.5 4.88889V2M8.5 4.88889V2M2.36111 9.22222H27.6389M2 12.1747C2 9.11967 2 7.59144 2.62978 6.42433C3.19924 5.38333 4.08288 4.54873 5.15467 4.03956C6.39111 3.44444 8.00889 3.44444 11.2444 3.44444H18.7556C21.9911 3.44444 23.6089 3.44444 24.8453 4.03956C25.933 4.56244 26.8156 5.39733 27.3702 6.42289C28 7.59289 28 9.12111 28 12.1761V19.2712C28 22.3262 28 23.8544 27.3702 25.0216C26.8008 26.0626 25.9171 26.8972 24.8453 27.4063C23.6089 28 21.9911 28 18.7556 28H11.2444C8.00889 28 6.39111 28 5.15467 27.4049C4.0831 26.8961 3.19948 26.062 2.62978 25.0216C2 23.8516 2 22.3233 2 19.2683V12.1747Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -245,7 +245,7 @@ export function MessagesIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M4.88867 14.999C6.27936 13.956 7.99961 13.4496 9.7336 13.5728C11.4676 13.6961 13.0989 14.4406 14.3281 15.6698C15.5574 16.899 16.3019 18.5304 16.4252 20.2644C16.5484 21.9984 16.042 23.7186 14.999 25.1093M4.88867 14.999C3.99176 15.6717 3.2638 16.5439 2.76241 17.5467C2.26103 18.5495 2 19.6552 2 20.7763C1.99954 21.5025 2.10861 22.2246 2.32353 22.9183C2.71639 24.1893 2.4333 25.6047 2.18054 26.9393C2.15813 27.0534 2.16551 27.1713 2.20198 27.2817C2.23845 27.392 2.30278 27.4912 2.38874 27.5694C2.4747 27.6477 2.57939 27.7025 2.6927 27.7285C2.80601 27.7544 2.92411 27.7508 3.03559 27.7178C4.26038 27.3827 5.47795 27.0967 6.72875 27.556C7.52721 27.849 8.37115 27.9986 9.22166 27.998C10.343 27.9991 11.4491 27.7386 12.4521 27.2371C13.455 26.7356 14.3271 26.0071 14.999 25.1093M4.88867 14.999C4.88867 8.23229 9.04112 2 16.4433 2C18.2963 1.99968 20.1222 2.44502 21.767 3.29845C23.4118 4.15189 24.8272 5.38838 25.8938 6.90363C26.9605 8.41888 27.647 10.1684 27.8956 12.0047C28.1441 13.841 27.9474 15.7101 27.322 17.4544C26.6345 19.3695 27.3755 21.9347 27.8088 24.0521C27.834 24.1648 27.8288 24.2822 27.7937 24.3923C27.7587 24.5024 27.6949 24.6011 27.6091 24.6785C27.5232 24.7558 27.4184 24.8089 27.3052 24.8323C27.1921 24.8557 27.0748 24.8486 26.9653 24.8118C25.0703 24.2196 22.846 23.3877 21.0652 24.1387C19.1384 24.9504 17.1135 25.1093 14.999 25.1093'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -266,7 +266,7 @@ export function NotificationsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M19.9056 24.7496H10.0908M19.9056 24.7496H25.7783C28.8267 24.7496 28.3116 21.7272 26.7695 20.1932C21.2153 14.6764 29.1046 2 14.9982 2C0.891783 2 8.78265 14.6748 3.22849 20.1932C1.74489 21.6687 1.11278 24.7496 4.21973 24.7496H10.0908M19.9056 24.7496C19.9056 27.8777 18.8526 31.2495 14.9982 31.2495C11.1437 31.2495 10.0908 27.8777 10.0908 24.7496'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -287,7 +287,7 @@ export function MailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M2.35742 5.83288L11.7674 12.1071C13.0656 12.9712 13.7141 13.404 14.4151 13.5725C15.0352 13.7208 15.681 13.7208 16.2998 13.5725C17.0008 13.404 17.6492 12.9712 18.9475 12.1071L28.3574 5.83288M8.82844 21.7219H21.8864C24.1513 21.7219 25.2837 21.7219 26.1492 21.2811C26.9097 20.8931 27.5278 20.2744 27.9152 19.5137C28.3574 18.6482 28.3574 17.5158 28.3574 15.2509V7.97102C28.3574 5.70616 28.3574 4.57373 27.9166 3.70823C27.5288 2.94727 26.9102 2.32858 26.1492 1.94084C25.2837 1.5 24.1513 1.5 21.8864 1.5H8.82844C6.56358 1.5 5.43115 1.5 4.56566 1.94084C3.80519 2.32881 3.187 2.94747 2.79961 3.70823C2.35742 4.57373 2.35742 5.70616 2.35742 7.97102V15.2509C2.35742 17.5158 2.35742 18.6482 2.79826 19.5137C3.186 20.2747 3.80469 20.8933 4.56566 21.2811C5.43115 21.7219 6.56358 21.7219 8.82844 21.7219Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -312,33 +312,25 @@ export function MailServerIcon(props: SVGProps<SVGSVGElement>) {
|
||||
width='18'
|
||||
height='16'
|
||||
rx='2'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M3 8L10.89 13.26C11.2187 13.4793 11.6049 13.5963 12 13.5963C12.3951 13.5963 12.7813 13.4793 13.11 13.26L21 8'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<line
|
||||
x1='7'
|
||||
y1='16'
|
||||
x2='7'
|
||||
y2='16'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<line x1='7' y1='16' x2='7' y2='16' stroke='#000000' strokeWidth='2' strokeLinecap='round' />
|
||||
<line
|
||||
x1='10'
|
||||
y1='16'
|
||||
x2='10'
|
||||
y2='16'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
@@ -358,7 +350,7 @@ export function CodeIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M23.2639 6.83064L23.6375 7.20422C26.5433 10.1117 27.9971 11.5638 27.9971 13.37C27.9971 15.1762 26.5433 16.63 23.6375 19.5358L23.2639 19.9094M18.0434 2L11.9507 24.7401M6.72863 6.83064L6.35504 7.20422C3.45081 10.1117 1.99707 11.5638 1.99707 13.37C1.99707 15.1762 3.45081 16.63 6.35829 19.5358L6.73187 19.9094'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.6'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -845,7 +837,7 @@ export function xIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<path
|
||||
d='M 5.9199219 6 L 20.582031 27.375 L 6.2304688 44 L 9.4101562 44 L 21.986328 29.421875 L 31.986328 44 L 44 44 L 28.681641 21.669922 L 42.199219 6 L 39.029297 6 L 27.275391 19.617188 L 17.933594 6 L 5.9199219 6 z M 9.7167969 8 L 16.880859 8 L 40.203125 42 L 33.039062 42 L 9.7167969 8 z'
|
||||
fill='currentColor'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='0.5'
|
||||
/>
|
||||
</svg>
|
||||
@@ -1499,14 +1491,14 @@ export function DocumentIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M8 15.2H15.2M8 20H11.6M2 4.4V23.6C2 24.2365 2.25286 24.847 2.70294 25.2971C3.15303 25.7471 3.76348 26 4.4 26H18.8C19.4365 26 20.047 25.7471 20.4971 25.2971C20.9471 24.847 21.2 24.2365 21.2 23.6V9.6104C21.2 9.29067 21.136 8.97417 21.012 8.67949C20.8879 8.38481 20.7062 8.11789 20.4776 7.8944L15.1496 2.684C14.7012 2.24559 14.0991 2.00008 13.472 2H4.4C3.76348 2 3.15303 2.25286 2.70294 2.70294C2.25286 3.15303 2 3.76348 2 4.4Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.25'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M14 2V6.8C14 7.43652 14.2529 8.04697 14.7029 8.49706C15.153 8.94714 15.7635 9.2 16.4 9.2H21.2'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.25'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
@@ -4001,7 +3993,7 @@ export function SmtpIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M2.35742 5.83288L11.7674 12.1071C13.0656 12.9712 13.7141 13.404 14.4151 13.5725C15.0352 13.7208 15.681 13.7208 16.2998 13.5725C17.0008 13.404 17.6492 12.9712 18.9475 12.1071L28.3574 5.83288M8.82844 21.7219H21.8864C24.1513 21.7219 25.2837 21.7219 26.1492 21.2811C26.9097 20.8931 27.5278 20.2744 27.9152 19.5137C28.3574 18.6482 28.3574 17.5158 28.3574 15.2509V7.97102C28.3574 5.70616 28.3574 4.57373 27.9166 3.70823C27.5288 2.94727 26.9102 2.32858 26.1492 1.94084C25.2837 1.5 24.1513 1.5 21.8864 1.5H8.82844C6.56358 1.5 5.43115 1.5 4.56566 1.94084C3.80519 2.32881 3.187 2.94747 2.79961 3.70823C2.35742 4.57373 2.35742 5.70616 2.35742 7.97102V15.2509C2.35742 17.5158 2.35742 18.6482 2.79826 19.5137C3.186 20.2747 3.80469 20.8933 4.56566 21.2811C5.43115 21.7219 6.56358 21.7219 8.82844 21.7219Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -4227,7 +4219,7 @@ export function A2AIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<circle cx='316' cy='307' r='27' fill='currentColor' />
|
||||
<path
|
||||
d='M336.5 191.003H162C97.6588 191.003 45.5 243.162 45.5 307.503C45.5 371.844 97.6442 424.003 161.985 424.003C206.551 424.003 256.288 424.003 296.5 424.003C487.5 424.003 374 191.005 569 191.001C613.886 191 658.966 191 698.025 191C762.366 191.001 814.5 243.16 814.5 307.501C814.5 371.843 762.34 424.003 697.998 424.003H523.5'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='48'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
@@ -4497,14 +4489,14 @@ export function RssIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M4 11C6.38695 11 8.67613 11.9482 10.364 13.636C12.0518 15.3239 13 17.6131 13 20'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M4 4C8.24346 4 12.3131 5.68571 15.3137 8.68629C18.3143 11.6869 20 15.7565 20 20'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -5483,3 +5475,37 @@ export function AgentSkillsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function OnePasswordIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg' fill='none'>
|
||||
<circle
|
||||
cx='24'
|
||||
cy='24'
|
||||
r='21.5'
|
||||
stroke='#000000'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M28.083,17.28a7.8633,7.8633,0,0,1,0,13.44'
|
||||
stroke='#000000'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M19.917,30.72a7.8633,7.8633,0,0,1,0-13.44'
|
||||
stroke='#000000'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M26.067,10.43H21.933a2.0172,2.0172,0,0,0-2.016,2.016v6.36c2.358,1.281,2.736,2.562,0,3.843V35.574a2.0169,2.0169,0,0,0,2.016,2.015h4.134a2.0169,2.0169,0,0,0,2.016-2.015V29.213c-2.358-1.281-2.736-2.562,0-3.842V12.446A2.0172,2.0172,0,0,0,26.067,10.43Z'
|
||||
fill='#000000'
|
||||
stroke='#000000'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ import {
|
||||
MySQLIcon,
|
||||
Neo4jIcon,
|
||||
NotionIcon,
|
||||
OnePasswordIcon,
|
||||
OpenAIIcon,
|
||||
OutlookIcon,
|
||||
PackageSearchIcon,
|
||||
@@ -214,6 +215,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
neo4j: Neo4jIcon,
|
||||
notion_v2: NotionIcon,
|
||||
onedrive: MicrosoftOneDriveIcon,
|
||||
onepassword: OnePasswordIcon,
|
||||
openai: OpenAIIcon,
|
||||
outlook: OutlookIcon,
|
||||
parallel_ai: ParallelIcon,
|
||||
|
||||
@@ -25,6 +25,7 @@ With Airweave, you can:
|
||||
In Sim, the Airweave integration empowers your agents to search, summarize, and extract insights from all your organization’s data via a single tool. Use Airweave to drive rich, contextual knowledge retrieval within your workflows—whether answering questions, generating summaries, or supporting dynamic decision-making.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Search across your synced data sources using Airweave. Supports semantic search with hybrid, neural, or keyword retrieval strategies. Optionally generate AI-powered answers from search results.
|
||||
|
||||
@@ -43,7 +43,6 @@ Retrieve detailed information about a specific Jira issue
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | No | Jira project key \(e.g., PROJ\). Optional when retrieving a single issue. |
|
||||
| `issueKey` | string | Yes | Jira issue key to retrieve \(e.g., PROJ-123\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
@@ -51,13 +50,182 @@ Retrieve detailed information about a specific Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueKey` | string | Issue key \(e.g., PROJ-123\) |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `id` | string | Issue ID |
|
||||
| `key` | string | Issue key \(e.g., PROJ-123\) |
|
||||
| `self` | string | REST API URL for this issue |
|
||||
| `summary` | string | Issue summary |
|
||||
| `description` | json | Issue description content |
|
||||
| `created` | string | Issue creation timestamp |
|
||||
| `updated` | string | Issue last updated timestamp |
|
||||
| `issue` | json | Complete issue object with all fields |
|
||||
| `description` | string | Issue description text \(extracted from ADF\) |
|
||||
| `status` | object | Issue status |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `name` | string | Status name \(e.g., Open, In Progress, Done\) |
|
||||
| ↳ `description` | string | Status description |
|
||||
| ↳ `statusCategory` | object | Status category grouping |
|
||||
| ↳ `id` | number | Status category ID |
|
||||
| ↳ `key` | string | Status category key \(e.g., new, indeterminate, done\) |
|
||||
| ↳ `name` | string | Status category name \(e.g., To Do, In Progress, Done\) |
|
||||
| ↳ `colorName` | string | Status category color \(e.g., blue-gray, yellow, green\) |
|
||||
| `issuetype` | object | Issue type |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `name` | string | Issue type name \(e.g., Task, Bug, Story, Epic\) |
|
||||
| ↳ `description` | string | Issue type description |
|
||||
| ↳ `subtask` | boolean | Whether this is a subtask type |
|
||||
| ↳ `iconUrl` | string | URL to the issue type icon |
|
||||
| `project` | object | Project the issue belongs to |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `key` | string | Project key \(e.g., PROJ\) |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `projectTypeKey` | string | Project type key \(e.g., software, business\) |
|
||||
| `priority` | object | Issue priority |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `name` | string | Priority name \(e.g., Highest, High, Medium, Low, Lowest\) |
|
||||
| ↳ `iconUrl` | string | URL to the priority icon |
|
||||
| `assignee` | object | Assigned user |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `reporter` | object | Reporter user |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `creator` | object | Issue creator |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `labels` | array | Issue labels |
|
||||
| `components` | array | Issue components |
|
||||
| ↳ `id` | string | Component ID |
|
||||
| ↳ `name` | string | Component name |
|
||||
| ↳ `description` | string | Component description |
|
||||
| `fixVersions` | array | Fix versions |
|
||||
| ↳ `id` | string | Version ID |
|
||||
| ↳ `name` | string | Version name |
|
||||
| ↳ `released` | boolean | Whether the version is released |
|
||||
| ↳ `releaseDate` | string | Release date \(YYYY-MM-DD\) |
|
||||
| `resolution` | object | Issue resolution |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Fixed, Duplicate, Won't Fix\) |
|
||||
| ↳ `description` | string | Resolution description |
|
||||
| `duedate` | string | Due date \(YYYY-MM-DD\) |
|
||||
| `created` | string | ISO 8601 timestamp when the issue was created |
|
||||
| `updated` | string | ISO 8601 timestamp when the issue was last updated |
|
||||
| `resolutiondate` | string | ISO 8601 timestamp when the issue was resolved |
|
||||
| `timetracking` | object | Time tracking information |
|
||||
| ↳ `originalEstimate` | string | Original estimate in human-readable format \(e.g., 1w 2d\) |
|
||||
| ↳ `remainingEstimate` | string | Remaining estimate in human-readable format |
|
||||
| ↳ `timeSpent` | string | Time spent in human-readable format |
|
||||
| ↳ `originalEstimateSeconds` | number | Original estimate in seconds |
|
||||
| ↳ `remainingEstimateSeconds` | number | Remaining estimate in seconds |
|
||||
| ↳ `timeSpentSeconds` | number | Time spent in seconds |
|
||||
| `parent` | object | Parent issue \(for subtasks\) |
|
||||
| ↳ `id` | string | Parent issue ID |
|
||||
| ↳ `key` | string | Parent issue key |
|
||||
| ↳ `summary` | string | Parent issue summary |
|
||||
| `issuelinks` | array | Linked issues |
|
||||
| ↳ `id` | string | Issue link ID |
|
||||
| ↳ `type` | object | Link type information |
|
||||
| ↳ `id` | string | Link type ID |
|
||||
| ↳ `name` | string | Link type name \(e.g., Blocks, Relates\) |
|
||||
| ↳ `inward` | string | Inward description \(e.g., is blocked by\) |
|
||||
| ↳ `outward` | string | Outward description \(e.g., blocks\) |
|
||||
| ↳ `inwardIssue` | object | Inward linked issue |
|
||||
| ↳ `id` | string | Issue ID |
|
||||
| ↳ `key` | string | Issue key |
|
||||
| ↳ `statusName` | string | Issue status name |
|
||||
| ↳ `summary` | string | Issue summary |
|
||||
| ↳ `outwardIssue` | object | Outward linked issue |
|
||||
| ↳ `id` | string | Issue ID |
|
||||
| ↳ `key` | string | Issue key |
|
||||
| ↳ `statusName` | string | Issue status name |
|
||||
| ↳ `summary` | string | Issue summary |
|
||||
| `subtasks` | array | Subtask issues |
|
||||
| ↳ `id` | string | Subtask issue ID |
|
||||
| ↳ `key` | string | Subtask issue key |
|
||||
| ↳ `summary` | string | Subtask summary |
|
||||
| ↳ `statusName` | string | Subtask status name |
|
||||
| ↳ `issueTypeName` | string | Subtask issue type name |
|
||||
| `votes` | object | Vote information |
|
||||
| ↳ `votes` | number | Number of votes |
|
||||
| ↳ `hasVoted` | boolean | Whether the current user has voted |
|
||||
| `watches` | object | Watch information |
|
||||
| ↳ `watchCount` | number | Number of watchers |
|
||||
| ↳ `isWatching` | boolean | Whether the current user is watching |
|
||||
| `comments` | array | Issue comments \(fetched separately\) |
|
||||
| ↳ `id` | string | Comment ID |
|
||||
| ↳ `body` | string | Comment body text \(extracted from ADF\) |
|
||||
| ↳ `author` | object | Comment author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `updateAuthor` | object | User who last updated the comment |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `created` | string | ISO 8601 timestamp when the comment was created |
|
||||
| ↳ `updated` | string | ISO 8601 timestamp when the comment was last updated |
|
||||
| ↳ `visibility` | object | Comment visibility restriction |
|
||||
| ↳ `type` | string | Restriction type \(e.g., role, group\) |
|
||||
| ↳ `value` | string | Restriction value \(e.g., Administrators\) |
|
||||
| `worklogs` | array | Issue worklogs \(fetched separately\) |
|
||||
| ↳ `id` | string | Worklog ID |
|
||||
| ↳ `author` | object | Worklog author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `updateAuthor` | object | User who last updated the worklog |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `comment` | string | Worklog comment text |
|
||||
| ↳ `started` | string | ISO 8601 timestamp when the work started |
|
||||
| ↳ `timeSpent` | string | Time spent in human-readable format \(e.g., 3h 20m\) |
|
||||
| ↳ `timeSpentSeconds` | number | Time spent in seconds |
|
||||
| ↳ `created` | string | ISO 8601 timestamp when the worklog was created |
|
||||
| ↳ `updated` | string | ISO 8601 timestamp when the worklog was last updated |
|
||||
| `attachments` | array | Issue attachments |
|
||||
| ↳ `id` | string | Attachment ID |
|
||||
| ↳ `filename` | string | Attachment file name |
|
||||
| ↳ `mimeType` | string | MIME type of the attachment |
|
||||
| ↳ `size` | number | File size in bytes |
|
||||
| ↳ `content` | string | URL to download the attachment content |
|
||||
| ↳ `thumbnail` | string | URL to the attachment thumbnail |
|
||||
| ↳ `author` | object | Attachment author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `created` | string | ISO 8601 timestamp when the attachment was created |
|
||||
|
||||
### `jira_update`
|
||||
|
||||
@@ -68,26 +236,32 @@ Update a Jira issue
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | No | Jira project key \(e.g., PROJ\). Optional when updating a single issue. |
|
||||
| `issueKey` | string | Yes | Jira issue key to update \(e.g., PROJ-123\) |
|
||||
| `summary` | string | No | New summary for the issue |
|
||||
| `description` | string | No | New description for the issue |
|
||||
| `status` | string | No | New status for the issue |
|
||||
| `priority` | string | No | New priority for the issue |
|
||||
| `assignee` | string | No | New assignee for the issue |
|
||||
| `priority` | string | No | New priority ID or name for the issue \(e.g., "High"\) |
|
||||
| `assignee` | string | No | New assignee account ID for the issue |
|
||||
| `labels` | json | No | Labels to set on the issue \(array of label name strings\) |
|
||||
| `components` | json | No | Components to set on the issue \(array of component name strings\) |
|
||||
| `duedate` | string | No | Due date for the issue \(format: YYYY-MM-DD\) |
|
||||
| `fixVersions` | json | No | Fix versions to set \(array of version name strings\) |
|
||||
| `environment` | string | No | Environment information for the issue |
|
||||
| `customFieldId` | string | No | Custom field ID to update \(e.g., customfield_10001\) |
|
||||
| `customFieldValue` | string | No | Value for the custom field |
|
||||
| `notifyUsers` | boolean | No | Whether to send email notifications about this update \(default: true\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Updated issue key \(e.g., PROJ-123\) |
|
||||
| `summary` | string | Issue summary after update |
|
||||
|
||||
### `jira_write`
|
||||
|
||||
Write a Jira issue
|
||||
Create a new Jira issue
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -100,9 +274,12 @@ Write a Jira issue
|
||||
| `priority` | string | No | Priority ID or name for the issue \(e.g., "10000" or "High"\) |
|
||||
| `assignee` | string | No | Assignee account ID for the issue |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
| `issueType` | string | Yes | Type of issue to create \(e.g., Task, Story\) |
|
||||
| `issueType` | string | Yes | Type of issue to create \(e.g., Task, Story, Bug, Epic, Sub-task\) |
|
||||
| `parent` | json | No | Parent issue key for creating subtasks \(e.g., \{ "key": "PROJ-123" \}\) |
|
||||
| `labels` | array | No | Labels for the issue \(array of label names\) |
|
||||
| `components` | array | No | Components for the issue \(array of component names\) |
|
||||
| `duedate` | string | No | Due date for the issue \(format: YYYY-MM-DD\) |
|
||||
| `fixVersions` | array | No | Fix versions for the issue \(array of version names\) |
|
||||
| `reporter` | string | No | Reporter account ID for the issue |
|
||||
| `environment` | string | No | Environment information for the issue |
|
||||
| `customFieldId` | string | No | Custom field ID \(e.g., customfield_10001\) |
|
||||
@@ -112,15 +289,16 @@ Write a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `id` | string | Created issue ID |
|
||||
| `issueKey` | string | Created issue key \(e.g., PROJ-123\) |
|
||||
| `self` | string | REST API URL for the created issue |
|
||||
| `summary` | string | Issue summary |
|
||||
| `url` | string | URL to the created issue |
|
||||
| `assigneeId` | string | Account ID of the assigned user \(if assigned\) |
|
||||
| `url` | string | URL to the created issue in Jira |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
Retrieve multiple Jira issues in bulk
|
||||
Retrieve multiple Jira issues from a project in bulk
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -134,7 +312,69 @@ Retrieve multiple Jira issues in bulk
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `issues` | array | Array of Jira issues with ts, summary, description, created, and updated timestamps |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `total` | number | Total number of issues in the project |
|
||||
| `issues` | array | Array of Jira issues |
|
||||
| ↳ `id` | string | Issue ID |
|
||||
| ↳ `key` | string | Issue key \(e.g., PROJ-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `summary` | string | Issue summary |
|
||||
| ↳ `description` | string | Issue description text |
|
||||
| ↳ `status` | object | Issue status |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `name` | string | Status name |
|
||||
| ↳ `issuetype` | object | Issue type |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `priority` | object | Issue priority |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `assignee` | object | Assigned user |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | Display name |
|
||||
| ↳ `created` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updated` | string | ISO 8601 last updated timestamp |
|
||||
|
||||
### `jira_bulk_read`
|
||||
|
||||
Retrieve multiple Jira issues from a project in bulk with cursor-based pagination (V2 - uses nextPageToken)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `projectId` | string | Yes | Jira project key \(e.g., PROJ\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issues` | array | Array of Jira issues |
|
||||
| ↳ `id` | string | Issue ID |
|
||||
| ↳ `key` | string | Issue key \(e.g., PROJ-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `summary` | string | Issue summary |
|
||||
| ↳ `description` | string | Issue description text |
|
||||
| ↳ `status` | object | Issue status |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `name` | string | Status name |
|
||||
| ↳ `issuetype` | object | Issue type |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `name` | string | Issue type name |
|
||||
| ↳ `priority` | object | Issue priority |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `name` | string | Priority name |
|
||||
| ↳ `assignee` | object | Assigned user |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | Display name |
|
||||
| ↳ `created` | string | ISO 8601 creation timestamp |
|
||||
| ↳ `updated` | string | ISO 8601 last updated timestamp |
|
||||
| `nextPageToken` | string | Cursor token for the next page. Null when no more results. |
|
||||
| `isLast` | boolean | Whether this is the last page of results |
|
||||
| `total` | number | Total number of issues in the project \(may not always be available\) |
|
||||
|
||||
### `jira_delete_issue`
|
||||
|
||||
@@ -153,7 +393,7 @@ Delete a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Deleted issue key |
|
||||
|
||||
### `jira_assign_issue`
|
||||
@@ -173,9 +413,9 @@ Assign a Jira issue to a user
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key that was assigned |
|
||||
| `assigneeId` | string | Account ID of the assignee |
|
||||
| `assigneeId` | string | Account ID of the assignee \(use "-1" for auto-assign, null to unassign\) |
|
||||
|
||||
### `jira_transition_issue`
|
||||
|
||||
@@ -189,15 +429,20 @@ Move a Jira issue between workflow statuses (e.g., To Do -> In Progress)
|
||||
| `issueKey` | string | Yes | Jira issue key to transition \(e.g., PROJ-123\) |
|
||||
| `transitionId` | string | Yes | ID of the transition to execute \(e.g., "11" for "To Do", "21" for "In Progress"\) |
|
||||
| `comment` | string | No | Optional comment to add when transitioning the issue |
|
||||
| `resolution` | string | No | Resolution name to set during transition \(e.g., "Fixed", "Won\'t Fix"\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key that was transitioned |
|
||||
| `transitionId` | string | Applied transition ID |
|
||||
| `transitionName` | string | Applied transition name |
|
||||
| `toStatus` | object | Target status after transition |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `name` | string | Status name |
|
||||
|
||||
### `jira_search_issues`
|
||||
|
||||
@@ -211,18 +456,157 @@ Search for Jira issues using JQL (Jira Query Language)
|
||||
| `jql` | string | Yes | JQL query string to search for issues \(e.g., "project = PROJ AND status = Open"\) |
|
||||
| `startAt` | number | No | The index of the first result to return \(for pagination\) |
|
||||
| `maxResults` | number | No | Maximum number of results to return \(default: 50\) |
|
||||
| `fields` | array | No | Array of field names to return \(default: \['summary', 'status', 'assignee', 'created', 'updated'\]\) |
|
||||
| `fields` | array | No | Array of field names to return \(default: all navigable\). Use "*all" for every field. |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `total` | number | Total number of matching issues |
|
||||
| `startAt` | number | Pagination start index |
|
||||
| `maxResults` | number | Maximum results per page |
|
||||
| `issues` | array | Array of matching issues with key, summary, status, assignee, created, updated |
|
||||
| `issues` | array | Array of matching issues |
|
||||
| ↳ `id` | string | Issue ID |
|
||||
| ↳ `key` | string | Issue key \(e.g., PROJ-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `summary` | string | Issue summary |
|
||||
| ↳ `description` | string | Issue description text \(extracted from ADF\) |
|
||||
| ↳ `status` | object | Issue status |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `name` | string | Status name \(e.g., Open, In Progress, Done\) |
|
||||
| ↳ `description` | string | Status description |
|
||||
| ↳ `statusCategory` | object | Status category grouping |
|
||||
| ↳ `id` | number | Status category ID |
|
||||
| ↳ `key` | string | Status category key \(e.g., new, indeterminate, done\) |
|
||||
| ↳ `name` | string | Status category name \(e.g., To Do, In Progress, Done\) |
|
||||
| ↳ `colorName` | string | Status category color \(e.g., blue-gray, yellow, green\) |
|
||||
| ↳ `issuetype` | object | Issue type |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `name` | string | Issue type name \(e.g., Task, Bug, Story, Epic\) |
|
||||
| ↳ `description` | string | Issue type description |
|
||||
| ↳ `subtask` | boolean | Whether this is a subtask type |
|
||||
| ↳ `iconUrl` | string | URL to the issue type icon |
|
||||
| ↳ `project` | object | Project the issue belongs to |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `key` | string | Project key \(e.g., PROJ\) |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `projectTypeKey` | string | Project type key \(e.g., software, business\) |
|
||||
| ↳ `priority` | object | Issue priority |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `name` | string | Priority name \(e.g., Highest, High, Medium, Low, Lowest\) |
|
||||
| ↳ `iconUrl` | string | URL to the priority icon |
|
||||
| ↳ `assignee` | object | Assigned user |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `reporter` | object | Reporter user |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `labels` | array | Issue labels |
|
||||
| ↳ `components` | array | Issue components |
|
||||
| ↳ `id` | string | Component ID |
|
||||
| ↳ `name` | string | Component name |
|
||||
| ↳ `description` | string | Component description |
|
||||
| ↳ `resolution` | object | Issue resolution |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Fixed, Duplicate, Won't Fix\) |
|
||||
| ↳ `description` | string | Resolution description |
|
||||
| ↳ `duedate` | string | Due date \(YYYY-MM-DD\) |
|
||||
| ↳ `created` | string | ISO 8601 timestamp when the issue was created |
|
||||
| ↳ `updated` | string | ISO 8601 timestamp when the issue was last updated |
|
||||
|
||||
### `jira_search_issues`
|
||||
|
||||
Search for Jira issues using JQL with cursor-based pagination (V2 - uses nextPageToken)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `jql` | string | Yes | JQL query string to search for issues \(e.g., "project = PROJ AND status = Open"\) |
|
||||
| `startAt` | number | No | The index of the first result to return \(for pagination\) |
|
||||
| `maxResults` | number | No | Maximum number of results to return \(default: 50\) |
|
||||
| `fields` | array | No | Array of field names to return \(default: all navigable\). Use "*all" for every field. |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issues` | array | Array of matching issues |
|
||||
| ↳ `id` | string | Issue ID |
|
||||
| ↳ `key` | string | Issue key \(e.g., PROJ-123\) |
|
||||
| ↳ `self` | string | REST API URL for this issue |
|
||||
| ↳ `summary` | string | Issue summary |
|
||||
| ↳ `description` | string | Issue description text \(extracted from ADF\) |
|
||||
| ↳ `status` | object | Issue status |
|
||||
| ↳ `id` | string | Status ID |
|
||||
| ↳ `name` | string | Status name \(e.g., Open, In Progress, Done\) |
|
||||
| ↳ `description` | string | Status description |
|
||||
| ↳ `statusCategory` | object | Status category grouping |
|
||||
| ↳ `id` | number | Status category ID |
|
||||
| ↳ `key` | string | Status category key \(e.g., new, indeterminate, done\) |
|
||||
| ↳ `name` | string | Status category name \(e.g., To Do, In Progress, Done\) |
|
||||
| ↳ `colorName` | string | Status category color \(e.g., blue-gray, yellow, green\) |
|
||||
| ↳ `issuetype` | object | Issue type |
|
||||
| ↳ `id` | string | Issue type ID |
|
||||
| ↳ `name` | string | Issue type name \(e.g., Task, Bug, Story, Epic\) |
|
||||
| ↳ `description` | string | Issue type description |
|
||||
| ↳ `subtask` | boolean | Whether this is a subtask type |
|
||||
| ↳ `iconUrl` | string | URL to the issue type icon |
|
||||
| ↳ `project` | object | Project the issue belongs to |
|
||||
| ↳ `id` | string | Project ID |
|
||||
| ↳ `key` | string | Project key \(e.g., PROJ\) |
|
||||
| ↳ `name` | string | Project name |
|
||||
| ↳ `projectTypeKey` | string | Project type key \(e.g., software, business\) |
|
||||
| ↳ `priority` | object | Issue priority |
|
||||
| ↳ `id` | string | Priority ID |
|
||||
| ↳ `name` | string | Priority name \(e.g., Highest, High, Medium, Low, Lowest\) |
|
||||
| ↳ `iconUrl` | string | URL to the priority icon |
|
||||
| ↳ `assignee` | object | Assigned user |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `reporter` | object | Reporter user |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `labels` | array | Issue labels |
|
||||
| ↳ `components` | array | Issue components |
|
||||
| ↳ `id` | string | Component ID |
|
||||
| ↳ `name` | string | Component name |
|
||||
| ↳ `description` | string | Component description |
|
||||
| ↳ `resolution` | object | Issue resolution |
|
||||
| ↳ `id` | string | Resolution ID |
|
||||
| ↳ `name` | string | Resolution name \(e.g., Fixed, Duplicate, Won't Fix\) |
|
||||
| ↳ `description` | string | Resolution description |
|
||||
| ↳ `duedate` | string | Due date \(YYYY-MM-DD\) |
|
||||
| ↳ `created` | string | ISO 8601 timestamp when the issue was created |
|
||||
| ↳ `updated` | string | ISO 8601 timestamp when the issue was last updated |
|
||||
| `nextPageToken` | string | Cursor token for the next page. Null when no more results. |
|
||||
| `isLast` | boolean | Whether this is the last page of results |
|
||||
| `total` | number | Total number of matching issues \(may not always be available\) |
|
||||
|
||||
### `jira_add_comment`
|
||||
|
||||
@@ -235,16 +619,27 @@ Add a comment to a Jira issue
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `issueKey` | string | Yes | Jira issue key to add comment to \(e.g., PROJ-123\) |
|
||||
| `body` | string | Yes | Comment body text |
|
||||
| `visibility` | json | No | Restrict comment visibility. Object with "type" \("role" or "group"\) and "value" \(role/group name\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key the comment was added to |
|
||||
| `commentId` | string | Created comment ID |
|
||||
| `body` | string | Comment text content |
|
||||
| `author` | object | Comment author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `created` | string | ISO 8601 timestamp when the comment was created |
|
||||
| `updated` | string | ISO 8601 timestamp when the comment was last updated |
|
||||
|
||||
### `jira_get_comments`
|
||||
|
||||
@@ -258,16 +653,42 @@ Get all comments from a Jira issue
|
||||
| `issueKey` | string | Yes | Jira issue key to get comments from \(e.g., PROJ-123\) |
|
||||
| `startAt` | number | No | Index of the first comment to return \(default: 0\) |
|
||||
| `maxResults` | number | No | Maximum number of comments to return \(default: 50\) |
|
||||
| `orderBy` | string | No | Sort order for comments: "-created" for newest first, "created" for oldest first |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `total` | number | Total number of comments |
|
||||
| `comments` | array | Array of comments with id, author, body, created, updated |
|
||||
| `startAt` | number | Pagination start index |
|
||||
| `maxResults` | number | Maximum results per page |
|
||||
| `comments` | array | Array of comments |
|
||||
| ↳ `id` | string | Comment ID |
|
||||
| ↳ `body` | string | Comment body text \(extracted from ADF\) |
|
||||
| ↳ `author` | object | Comment author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `updateAuthor` | object | User who last updated the comment |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `created` | string | ISO 8601 timestamp when the comment was created |
|
||||
| ↳ `updated` | string | ISO 8601 timestamp when the comment was last updated |
|
||||
| ↳ `visibility` | object | Comment visibility restriction |
|
||||
| ↳ `type` | string | Restriction type \(e.g., role, group\) |
|
||||
| ↳ `value` | string | Restriction value \(e.g., Administrators\) |
|
||||
|
||||
### `jira_update_comment`
|
||||
|
||||
@@ -281,16 +702,27 @@ Update an existing comment on a Jira issue
|
||||
| `issueKey` | string | Yes | Jira issue key containing the comment \(e.g., PROJ-123\) |
|
||||
| `commentId` | string | Yes | ID of the comment to update |
|
||||
| `body` | string | Yes | Updated comment text |
|
||||
| `visibility` | json | No | Restrict comment visibility. Object with "type" \("role" or "group"\) and "value" \(role/group name\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `commentId` | string | Updated comment ID |
|
||||
| `body` | string | Updated comment text |
|
||||
| `author` | object | Comment author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `created` | string | ISO 8601 timestamp when the comment was created |
|
||||
| `updated` | string | ISO 8601 timestamp when the comment was last updated |
|
||||
|
||||
### `jira_delete_comment`
|
||||
|
||||
@@ -309,7 +741,7 @@ Delete a comment from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `commentId` | string | Deleted comment ID |
|
||||
|
||||
@@ -329,9 +761,24 @@ Get all attachments from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `attachments` | array | Array of attachments with id, filename, size, mimeType, created, author |
|
||||
| `attachments` | array | Array of attachments |
|
||||
| ↳ `id` | string | Attachment ID |
|
||||
| ↳ `filename` | string | Attachment file name |
|
||||
| ↳ `mimeType` | string | MIME type of the attachment |
|
||||
| ↳ `size` | number | File size in bytes |
|
||||
| ↳ `content` | string | URL to download the attachment content |
|
||||
| ↳ `thumbnail` | string | URL to the attachment thumbnail |
|
||||
| ↳ `author` | object | Attachment author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `created` | string | ISO 8601 timestamp when the attachment was created |
|
||||
|
||||
### `jira_add_attachment`
|
||||
|
||||
@@ -350,10 +797,14 @@ Add attachments to a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `attachmentIds` | json | IDs of uploaded attachments |
|
||||
| `files` | file[] | Uploaded attachment files |
|
||||
| `attachments` | array | Uploaded attachments |
|
||||
| ↳ `id` | string | Attachment ID |
|
||||
| ↳ `filename` | string | Attachment file name |
|
||||
| ↳ `mimeType` | string | MIME type |
|
||||
| ↳ `size` | number | File size in bytes |
|
||||
| ↳ `content` | string | URL to download the attachment |
|
||||
|
||||
### `jira_delete_attachment`
|
||||
|
||||
@@ -371,7 +822,7 @@ Delete an attachment from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `attachmentId` | string | Deleted attachment ID |
|
||||
|
||||
### `jira_add_worklog`
|
||||
@@ -387,16 +838,28 @@ Add a time tracking worklog entry to a Jira issue
|
||||
| `timeSpentSeconds` | number | Yes | Time spent in seconds |
|
||||
| `comment` | string | No | Optional comment for the worklog entry |
|
||||
| `started` | string | No | Optional start time in ISO format \(defaults to current time\) |
|
||||
| `visibility` | json | No | Restrict worklog visibility. Object with "type" \("role" or "group"\) and "value" \(role/group name\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key the worklog was added to |
|
||||
| `worklogId` | string | Created worklog ID |
|
||||
| `timeSpent` | string | Time spent in human-readable format \(e.g., 3h 20m\) |
|
||||
| `timeSpentSeconds` | number | Time spent in seconds |
|
||||
| `author` | object | Worklog author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `started` | string | ISO 8601 timestamp when the work started |
|
||||
| `created` | string | ISO 8601 timestamp when the worklog was created |
|
||||
|
||||
### `jira_get_worklogs`
|
||||
|
||||
@@ -416,10 +879,35 @@ Get all worklog entries from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `total` | number | Total number of worklogs |
|
||||
| `worklogs` | array | Array of worklogs with id, author, timeSpentSeconds, timeSpent, comment, created, updated, started |
|
||||
| `startAt` | number | Pagination start index |
|
||||
| `maxResults` | number | Maximum results per page |
|
||||
| `worklogs` | array | Array of worklogs |
|
||||
| ↳ `id` | string | Worklog ID |
|
||||
| ↳ `author` | object | Worklog author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `updateAuthor` | object | User who last updated the worklog |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| ↳ `comment` | string | Worklog comment text |
|
||||
| ↳ `started` | string | ISO 8601 timestamp when the work started |
|
||||
| ↳ `timeSpent` | string | Time spent in human-readable format \(e.g., 3h 20m\) |
|
||||
| ↳ `timeSpentSeconds` | number | Time spent in seconds |
|
||||
| ↳ `created` | string | ISO 8601 timestamp when the worklog was created |
|
||||
| ↳ `updated` | string | ISO 8601 timestamp when the worklog was last updated |
|
||||
|
||||
### `jira_update_worklog`
|
||||
|
||||
@@ -435,15 +923,38 @@ Update an existing worklog entry on a Jira issue
|
||||
| `timeSpentSeconds` | number | No | Time spent in seconds |
|
||||
| `comment` | string | No | Optional comment for the worklog entry |
|
||||
| `started` | string | No | Optional start time in ISO format |
|
||||
| `visibility` | json | No | Restrict worklog visibility. Object with "type" \("role" or "group"\) and "value" \(role/group name\). |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance. If not provided, it will be fetched using the domain. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `worklogId` | string | Updated worklog ID |
|
||||
| `timeSpent` | string | Human-readable time spent \(e.g., "3h 20m"\) |
|
||||
| `timeSpentSeconds` | number | Time spent in seconds |
|
||||
| `comment` | string | Worklog comment text |
|
||||
| `author` | object | Worklog author |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `updateAuthor` | object | User who last updated the worklog |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `started` | string | Worklog start time in ISO format |
|
||||
| `created` | string | Worklog creation time |
|
||||
| `updated` | string | Worklog last update time |
|
||||
|
||||
### `jira_delete_worklog`
|
||||
|
||||
@@ -462,7 +973,7 @@ Delete a worklog entry from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `worklogId` | string | Deleted worklog ID |
|
||||
|
||||
@@ -485,7 +996,7 @@ Create a link relationship between two Jira issues
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `inwardIssue` | string | Inward issue key |
|
||||
| `outwardIssue` | string | Outward issue key |
|
||||
| `linkType` | string | Type of issue link |
|
||||
@@ -507,7 +1018,7 @@ Delete a link between two Jira issues
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `linkId` | string | Deleted link ID |
|
||||
|
||||
### `jira_add_watcher`
|
||||
@@ -527,7 +1038,7 @@ Add a watcher to a Jira issue to receive notifications about updates
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `watcherAccountId` | string | Added watcher account ID |
|
||||
|
||||
@@ -548,7 +1059,7 @@ Remove a watcher from a Jira issue
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `issueKey` | string | Issue key |
|
||||
| `watcherAccountId` | string | Removed watcher account ID |
|
||||
|
||||
@@ -570,8 +1081,15 @@ Get Jira users. If an account ID is provided, returns a single user. Otherwise,
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `users` | json | Array of users with accountId, displayName, emailAddress, active status, and avatarUrls |
|
||||
| `ts` | string | ISO 8601 timestamp of the operation |
|
||||
| `users` | array | Array of Jira users |
|
||||
| ↳ `accountId` | string | Atlassian account ID of the user |
|
||||
| ↳ `displayName` | string | Display name of the user |
|
||||
| ↳ `active` | boolean | Whether the user account is active |
|
||||
| ↳ `emailAddress` | string | Email address of the user |
|
||||
| ↳ `accountType` | string | Type of account \(e.g., atlassian, app, customer\) |
|
||||
| ↳ `avatarUrl` | string | URL to the user avatar \(48x48\) |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `total` | number | Total number of users returned |
|
||||
| `startAt` | number | Pagination start index |
|
||||
| `maxResults` | number | Maximum results per page |
|
||||
|
||||
@@ -46,6 +46,7 @@ Get all service desks from Jira Service Management
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `expand` | string | No | Comma-separated fields to expand in the response |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
|
||||
@@ -54,7 +55,14 @@ Get all service desks from Jira Service Management
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `serviceDesks` | json | Array of service desks |
|
||||
| `serviceDesks` | array | List of service desks |
|
||||
| ↳ `id` | string | Service desk ID |
|
||||
| ↳ `projectId` | string | Associated Jira project ID |
|
||||
| ↳ `projectName` | string | Associated project name |
|
||||
| ↳ `projectKey` | string | Associated project key |
|
||||
| ↳ `name` | string | Service desk name |
|
||||
| ↳ `description` | string | Service desk description |
|
||||
| ↳ `leadDisplayName` | string | Project lead display name |
|
||||
| `total` | number | Total number of service desks |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -69,6 +77,9 @@ Get request types for a service desk in Jira Service Management
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `searchQuery` | string | No | Filter request types by name |
|
||||
| `groupId` | string | No | Filter by request type group ID |
|
||||
| `expand` | string | No | Comma-separated fields to expand in the response |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
|
||||
@@ -77,7 +88,16 @@ Get request types for a service desk in Jira Service Management
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `requestTypes` | json | Array of request types |
|
||||
| `requestTypes` | array | List of request types |
|
||||
| ↳ `id` | string | Request type ID |
|
||||
| ↳ `name` | string | Request type name |
|
||||
| ↳ `description` | string | Request type description |
|
||||
| ↳ `helpText` | string | Help text for customers |
|
||||
| ↳ `issueTypeId` | string | Associated Jira issue type ID |
|
||||
| ↳ `serviceDeskId` | string | Parent service desk ID |
|
||||
| ↳ `groupIds` | json | Groups this request type belongs to |
|
||||
| ↳ `icon` | json | Request type icon with id and links |
|
||||
| ↳ `restrictionStatus` | string | OPEN or RESTRICTED |
|
||||
| `total` | number | Total number of request types |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -96,6 +116,9 @@ Create a new service request in Jira Service Management
|
||||
| `summary` | string | Yes | Summary/title for the service request |
|
||||
| `description` | string | No | Description for the service request |
|
||||
| `raiseOnBehalfOf` | string | No | Account ID of customer to raise request on behalf of |
|
||||
| `requestFieldValues` | json | No | Custom field values as key-value pairs \(overrides summary/description if provided\) |
|
||||
| `requestParticipants` | string | No | Comma-separated account IDs to add as request participants |
|
||||
| `channel` | string | No | Channel the request originates from \(e.g., portal, email\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -106,6 +129,9 @@ Create a new service request in Jira Service Management
|
||||
| `issueKey` | string | Created request issue key \(e.g., SD-123\) |
|
||||
| `requestTypeId` | string | Request type ID |
|
||||
| `serviceDeskId` | string | Service desk ID |
|
||||
| `createdDate` | json | Creation date with iso8601, friendly, epochMillis |
|
||||
| `currentStatus` | json | Current status with status name and category |
|
||||
| `reporter` | json | Reporter user with accountId, displayName, emailAddress |
|
||||
| `success` | boolean | Whether the request was created successfully |
|
||||
| `url` | string | URL to the created request |
|
||||
|
||||
@@ -120,13 +146,33 @@ Get a single service request from Jira Service Management
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `expand` | string | No | Comma-separated fields to expand: participant, status, sla, requestType, serviceDesk, attachment, comment, action |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `request` | json | The service request object |
|
||||
| `issueId` | string | Jira issue ID |
|
||||
| `issueKey` | string | Issue key \(e.g., SD-123\) |
|
||||
| `requestTypeId` | string | Request type ID |
|
||||
| `serviceDeskId` | string | Service desk ID |
|
||||
| `createdDate` | json | Creation date with iso8601, friendly, epochMillis |
|
||||
| `currentStatus` | object | Current request status |
|
||||
| ↳ `status` | string | Status name |
|
||||
| ↳ `statusCategory` | string | Status category \(NEW, INDETERMINATE, DONE\) |
|
||||
| ↳ `statusDate` | json | Status change date with iso8601, friendly, epochMillis |
|
||||
| `reporter` | object | Reporter user details |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | User display name |
|
||||
| ↳ `emailAddress` | string | User email address |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| `requestFieldValues` | array | Request field values |
|
||||
| ↳ `fieldId` | string | Field identifier |
|
||||
| ↳ `label` | string | Human-readable field label |
|
||||
| ↳ `value` | json | Field value |
|
||||
| ↳ `renderedValue` | json | HTML-rendered field value |
|
||||
| `url` | string | URL to the request |
|
||||
|
||||
### `jsm_get_requests`
|
||||
|
||||
@@ -139,9 +185,11 @@ Get multiple service requests from Jira Service Management
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | No | Filter by service desk ID \(e.g., "1", "2"\) |
|
||||
| `requestOwnership` | string | No | Filter by ownership: OWNED_REQUESTS, PARTICIPATED_REQUESTS, ORGANIZATION, ALL_REQUESTS |
|
||||
| `requestStatus` | string | No | Filter by status: OPEN, CLOSED, ALL |
|
||||
| `requestOwnership` | string | No | Filter by ownership: OWNED_REQUESTS, PARTICIPATED_REQUESTS, APPROVER, ALL_REQUESTS |
|
||||
| `requestStatus` | string | No | Filter by status: OPEN_REQUESTS, CLOSED_REQUESTS, ALL_REQUESTS |
|
||||
| `requestTypeId` | string | No | Filter by request type ID |
|
||||
| `searchTerm` | string | No | Search term to filter requests \(e.g., "password reset", "laptop"\) |
|
||||
| `expand` | string | No | Comma-separated fields to expand: participant, status, sla, requestType, serviceDesk, attachment, comment, action |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
|
||||
@@ -150,8 +198,27 @@ Get multiple service requests from Jira Service Management
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `requests` | json | Array of service requests |
|
||||
| `total` | number | Total number of requests |
|
||||
| `requests` | array | List of service requests |
|
||||
| ↳ `issueId` | string | Jira issue ID |
|
||||
| ↳ `issueKey` | string | Issue key \(e.g., SD-123\) |
|
||||
| ↳ `requestTypeId` | string | Request type ID |
|
||||
| ↳ `serviceDeskId` | string | Service desk ID |
|
||||
| ↳ `createdDate` | json | Creation date with iso8601, friendly, epochMillis |
|
||||
| ↳ `currentStatus` | object | Current request status |
|
||||
| ↳ `status` | string | Status name |
|
||||
| ↳ `statusCategory` | string | Status category \(NEW, INDETERMINATE, DONE\) |
|
||||
| ↳ `statusDate` | json | Status change date with iso8601, friendly, epochMillis |
|
||||
| ↳ `reporter` | object | Reporter user details |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | User display name |
|
||||
| ↳ `emailAddress` | string | User email address |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| ↳ `requestFieldValues` | array | Request field values |
|
||||
| ↳ `fieldId` | string | Field identifier |
|
||||
| ↳ `label` | string | Human-readable field label |
|
||||
| ↳ `value` | json | Field value |
|
||||
| ↳ `renderedValue` | json | HTML-rendered field value |
|
||||
| `total` | number | Total number of requests in current page |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
### `jsm_add_comment`
|
||||
@@ -177,6 +244,12 @@ Add a comment (public or internal) to a service request in Jira Service Manageme
|
||||
| `commentId` | string | Created comment ID |
|
||||
| `body` | string | Comment body text |
|
||||
| `isPublic` | boolean | Whether the comment is public |
|
||||
| `author` | object | Comment author |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | User display name |
|
||||
| ↳ `emailAddress` | string | User email address |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| `createdDate` | json | Comment creation date with iso8601, friendly, epochMillis |
|
||||
| `success` | boolean | Whether the comment was added successfully |
|
||||
|
||||
### `jsm_get_comments`
|
||||
@@ -192,6 +265,7 @@ Get comments for a service request in Jira Service Management
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `isPublic` | boolean | No | Filter to only public comments \(true/false\) |
|
||||
| `internal` | boolean | No | Filter to only internal comments \(true/false\) |
|
||||
| `expand` | string | No | Comma-separated fields to expand: renderedBody, attachment |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
|
||||
@@ -201,7 +275,17 @@ Get comments for a service request in Jira Service Management
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueIdOrKey` | string | Issue ID or key |
|
||||
| `comments` | json | Array of comments |
|
||||
| `comments` | array | List of comments |
|
||||
| ↳ `id` | string | Comment ID |
|
||||
| ↳ `body` | string | Comment body text |
|
||||
| ↳ `public` | boolean | Whether the comment is public |
|
||||
| ↳ `author` | object | Comment author |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | User display name |
|
||||
| ↳ `emailAddress` | string | User email address |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| ↳ `created` | json | Creation date with iso8601, friendly, epochMillis |
|
||||
| ↳ `renderedBody` | json | HTML-rendered comment body \(when expand=renderedBody\) |
|
||||
| `total` | number | Total number of comments |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -225,7 +309,12 @@ Get customers for a service desk in Jira Service Management
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `customers` | json | Array of customers |
|
||||
| `customers` | array | List of customers |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | Display name |
|
||||
| ↳ `emailAddress` | string | Email address |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| ↳ `timeZone` | string | User timezone |
|
||||
| `total` | number | Total number of customers |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -240,7 +329,7 @@ Add customers to a service desk in Jira Service Management
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `emails` | string | Yes | Comma-separated email addresses to add as customers |
|
||||
| `accountIds` | string | Yes | Comma-separated Atlassian account IDs to add as customers |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -269,7 +358,9 @@ Get organizations for a service desk in Jira Service Management
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `organizations` | json | Array of organizations |
|
||||
| `organizations` | array | List of organizations |
|
||||
| ↳ `id` | string | Organization ID |
|
||||
| ↳ `name` | string | Organization name |
|
||||
| `total` | number | Total number of organizations |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -336,7 +427,12 @@ Get queues for a service desk in Jira Service Management
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `queues` | json | Array of queues |
|
||||
| `queues` | array | List of queues |
|
||||
| ↳ `id` | string | Queue ID |
|
||||
| ↳ `name` | string | Queue name |
|
||||
| ↳ `jql` | string | JQL filter for the queue |
|
||||
| ↳ `fields` | json | Fields displayed in the queue |
|
||||
| ↳ `issueCount` | number | Number of issues in the queue |
|
||||
| `total` | number | Total number of queues |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -360,7 +456,11 @@ Get SLA information for a service request in Jira Service Management
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueIdOrKey` | string | Issue ID or key |
|
||||
| `slas` | json | Array of SLA information |
|
||||
| `slas` | array | List of SLA metrics |
|
||||
| ↳ `id` | string | SLA metric ID |
|
||||
| ↳ `name` | string | SLA metric name |
|
||||
| ↳ `completedCycles` | json | Completed SLA cycles with startTime, stopTime, breachTime, breached, goalDuration, elapsedTime, remainingTime \(each time as DateDTO, durations as DurationDTO\) |
|
||||
| ↳ `ongoingCycle` | json | Ongoing SLA cycle with startTime, breachTime, breached, paused, withinCalendarHours, goalDuration, elapsedTime, remainingTime |
|
||||
| `total` | number | Total number of SLAs |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -375,6 +475,8 @@ Get available transitions for a service request in Jira Service Management
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `issueIdOrKey` | string | Yes | Issue ID or key \(e.g., SD-123\) |
|
||||
| `start` | number | No | Start index for pagination \(e.g., 0, 50, 100\) |
|
||||
| `limit` | number | No | Maximum results to return \(e.g., 10, 25, 50\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -382,7 +484,11 @@ Get available transitions for a service request in Jira Service Management
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueIdOrKey` | string | Issue ID or key |
|
||||
| `transitions` | json | Array of available transitions |
|
||||
| `transitions` | array | List of available transitions |
|
||||
| ↳ `id` | string | Transition ID |
|
||||
| ↳ `name` | string | Transition name |
|
||||
| `total` | number | Total number of transitions |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
### `jsm_transition_request`
|
||||
|
||||
@@ -427,7 +533,11 @@ Get participants for a request in Jira Service Management
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueIdOrKey` | string | Issue ID or key |
|
||||
| `participants` | json | Array of participants |
|
||||
| `participants` | array | List of participants |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | Display name |
|
||||
| ↳ `emailAddress` | string | Email address |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| `total` | number | Total number of participants |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -450,7 +560,11 @@ Add participants to a request in Jira Service Management
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueIdOrKey` | string | Issue ID or key |
|
||||
| `participants` | json | Array of added participants |
|
||||
| `participants` | array | List of added participants |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | Display name |
|
||||
| ↳ `emailAddress` | string | Email address |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| `success` | boolean | Whether the operation succeeded |
|
||||
|
||||
### `jsm_get_approvals`
|
||||
@@ -473,7 +587,20 @@ Get approvals for a request in Jira Service Management
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `issueIdOrKey` | string | Issue ID or key |
|
||||
| `approvals` | json | Array of approvals |
|
||||
| `approvals` | array | List of approvals |
|
||||
| ↳ `id` | string | Approval ID |
|
||||
| ↳ `name` | string | Approval description |
|
||||
| ↳ `finalDecision` | string | Final decision: pending, approved, or declined |
|
||||
| ↳ `canAnswerApproval` | boolean | Whether current user can respond |
|
||||
| ↳ `approvers` | array | List of approvers with their decisions |
|
||||
| ↳ `approver` | object | Approver user details |
|
||||
| ↳ `accountId` | string | Atlassian account ID |
|
||||
| ↳ `displayName` | string | User display name |
|
||||
| ↳ `emailAddress` | string | User email address |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| ↳ `approverDecision` | string | Decision: pending, approved, or declined |
|
||||
| ↳ `createdDate` | json | Creation date |
|
||||
| ↳ `completedDate` | json | Completion date |
|
||||
| `total` | number | Total number of approvals |
|
||||
| `isLastPage` | boolean | Whether this is the last page |
|
||||
|
||||
@@ -499,6 +626,52 @@ Approve or decline an approval request in Jira Service Management
|
||||
| `issueIdOrKey` | string | Issue ID or key |
|
||||
| `approvalId` | string | Approval ID |
|
||||
| `decision` | string | Decision made \(approve/decline\) |
|
||||
| `id` | string | Approval ID from response |
|
||||
| `name` | string | Approval description |
|
||||
| `finalDecision` | string | Final approval decision: pending, approved, or declined |
|
||||
| `canAnswerApproval` | boolean | Whether the current user can still respond |
|
||||
| `approvers` | array | Updated list of approvers with decisions |
|
||||
| ↳ `approver` | object | Approver user details |
|
||||
| ↳ `accountId` | string | Approver account ID |
|
||||
| ↳ `displayName` | string | Approver display name |
|
||||
| ↳ `emailAddress` | string | Approver email |
|
||||
| ↳ `active` | boolean | Whether the account is active |
|
||||
| ↳ `approverDecision` | string | Individual approver decision |
|
||||
| `createdDate` | json | Approval creation date |
|
||||
| `completedDate` | json | Approval completion date |
|
||||
| `success` | boolean | Whether the operation succeeded |
|
||||
|
||||
### `jsm_get_request_type_fields`
|
||||
|
||||
Get the fields required to create a request of a specific type in Jira Service Management
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `domain` | string | Yes | Your Jira domain \(e.g., yourcompany.atlassian.net\) |
|
||||
| `cloudId` | string | No | Jira Cloud ID for the instance |
|
||||
| `serviceDeskId` | string | Yes | Service Desk ID \(e.g., "1", "2"\) |
|
||||
| `requestTypeId` | string | Yes | Request Type ID \(e.g., "10", "15"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ts` | string | Timestamp of the operation |
|
||||
| `serviceDeskId` | string | Service desk ID |
|
||||
| `requestTypeId` | string | Request type ID |
|
||||
| `canAddRequestParticipants` | boolean | Whether participants can be added to requests of this type |
|
||||
| `canRaiseOnBehalfOf` | boolean | Whether requests can be raised on behalf of another user |
|
||||
| `requestTypeFields` | array | List of fields for this request type |
|
||||
| ↳ `fieldId` | string | Field identifier \(e.g., summary, description, customfield_10010\) |
|
||||
| ↳ `name` | string | Human-readable field name |
|
||||
| ↳ `description` | string | Help text for the field |
|
||||
| ↳ `required` | boolean | Whether the field is required |
|
||||
| ↳ `visible` | boolean | Whether the field is visible |
|
||||
| ↳ `validValues` | json | Allowed values for select fields |
|
||||
| ↳ `presetValues` | json | Pre-populated values |
|
||||
| ↳ `defaultValues` | json | Default values for the field |
|
||||
| ↳ `jiraSchema` | json | Jira field schema with type, system, custom, customId |
|
||||
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
"neo4j",
|
||||
"notion",
|
||||
"onedrive",
|
||||
"onepassword",
|
||||
"openai",
|
||||
"outlook",
|
||||
"parallel_ai",
|
||||
|
||||
225
apps/docs/content/docs/en/tools/onepassword.mdx
Normal file
225
apps/docs/content/docs/en/tools/onepassword.mdx
Normal file
@@ -0,0 +1,225 @@
|
||||
---
|
||||
title: 1Password
|
||||
description: Manage secrets and items in 1Password vaults
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="onepassword"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[1Password](https://1password.com) is a widely trusted password manager and secrets vault solution, allowing individuals and teams to securely store, access, and share passwords, API credentials, and sensitive information. With robust encryption, granular access controls, and seamless syncing across devices, 1Password supports teams and organizations in managing secrets efficiently and securely.
|
||||
|
||||
The [1Password Connect API](https://developer.1password.com/docs/connect/) allows programmatic access to vaults and items within an organization's 1Password account. This integration in Sim lets you automate secret retrieval, onboarding workflows, secret rotation, vault audits, and more, all in a secure and auditable manner.
|
||||
|
||||
With 1Password in your Sim workflow, you can:
|
||||
|
||||
- **List, search, and retrieve vaults**: Access metadata or browse available vaults for organizing secrets by project or purpose
|
||||
- **Fetch items and secrets**: Get credentials, API keys, or custom secrets in real time to power your workflows securely
|
||||
- **Create, update, or delete secrets**: Automate secret management, provisioning, and rotation for enhanced security practices
|
||||
- **Integrate with CI/CD and automation**: Fetch credentials or tokens only when needed, reducing manual work and reducing risk
|
||||
- **Ensure access controls**: Leverage role-based access and fine-grained permissions to control which agents or users can access specific secrets
|
||||
|
||||
By connecting Sim with 1Password, you empower your agents to securely manage secrets, reduce manual overhead, and maintain best practices for security automation, incident response, and DevOps workflows—all while ensuring secrets never leave a controlled environment.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Access and manage secrets stored in 1Password vaults using the Connect API. List vaults, retrieve items with their fields and secrets, create new items, update existing ones, and delete items.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `onepassword_list_vaults`
|
||||
|
||||
List all vaults accessible by the Connect token
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | 1Password Connect API token |
|
||||
| `serverUrl` | string | Yes | 1Password Connect server URL \(e.g., http://localhost:8080\) |
|
||||
| `filter` | string | No | SCIM filter expression \(e.g., name eq "My Vault"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `vaults` | array | List of accessible vaults |
|
||||
| ↳ `id` | string | Vault ID |
|
||||
| ↳ `name` | string | Vault name |
|
||||
| ↳ `description` | string | Vault description |
|
||||
| ↳ `attributeVersion` | number | Vault attribute version |
|
||||
| ↳ `contentVersion` | number | Vault content version |
|
||||
| ↳ `type` | string | Vault type \(USER_CREATED, PERSONAL, EVERYONE, TRANSFER\) |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last update timestamp |
|
||||
|
||||
### `onepassword_get_vault`
|
||||
|
||||
Get details of a specific vault by ID
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | 1Password Connect API token |
|
||||
| `serverUrl` | string | Yes | 1Password Connect server URL \(e.g., http://localhost:8080\) |
|
||||
| `vaultId` | string | Yes | The vault UUID |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Vault ID |
|
||||
| `name` | string | Vault name |
|
||||
| `description` | string | Vault description |
|
||||
| `attributeVersion` | number | Vault attribute version |
|
||||
| `contentVersion` | number | Vault content version |
|
||||
| `items` | number | Number of items in the vault |
|
||||
| `type` | string | Vault type \(USER_CREATED, PERSONAL, EVERYONE, TRANSFER\) |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
| `updatedAt` | string | Last update timestamp |
|
||||
|
||||
### `onepassword_list_items`
|
||||
|
||||
List items in a vault. Returns summaries without field values.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | 1Password Connect API token |
|
||||
| `serverUrl` | string | Yes | 1Password Connect server URL \(e.g., http://localhost:8080\) |
|
||||
| `vaultId` | string | Yes | The vault UUID to list items from |
|
||||
| `filter` | string | No | SCIM filter expression \(e.g., title eq "API Key" or tag eq "production"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | List of items in the vault \(summaries without field values\) |
|
||||
| ↳ `id` | string | Item ID |
|
||||
| ↳ `title` | string | Item title |
|
||||
| ↳ `vault` | object | Vault reference |
|
||||
| ↳ `id` | string | Vault ID |
|
||||
| ↳ `category` | string | Item category \(e.g., LOGIN, API_CREDENTIAL\) |
|
||||
| ↳ `urls` | array | URLs associated with the item |
|
||||
| ↳ `href` | string | URL |
|
||||
| ↳ `label` | string | URL label |
|
||||
| ↳ `primary` | boolean | Whether this is the primary URL |
|
||||
| ↳ `favorite` | boolean | Whether the item is favorited |
|
||||
| ↳ `tags` | array | Item tags |
|
||||
| ↳ `version` | number | Item version number |
|
||||
| ↳ `state` | string | Item state \(ARCHIVED or DELETED\) |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| ↳ `updatedAt` | string | Last update timestamp |
|
||||
| ↳ `lastEditedBy` | string | ID of the last editor |
|
||||
|
||||
### `onepassword_get_item`
|
||||
|
||||
Get full details of an item including all fields and secrets
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | 1Password Connect API token |
|
||||
| `serverUrl` | string | Yes | 1Password Connect server URL \(e.g., http://localhost:8080\) |
|
||||
| `vaultId` | string | Yes | The vault UUID |
|
||||
| `itemId` | string | Yes | The item UUID to retrieve |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `response` | json | Operation response data |
|
||||
|
||||
### `onepassword_create_item`
|
||||
|
||||
Create a new item in a vault
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | 1Password Connect API token |
|
||||
| `serverUrl` | string | Yes | 1Password Connect server URL \(e.g., http://localhost:8080\) |
|
||||
| `vaultId` | string | Yes | The vault UUID to create the item in |
|
||||
| `category` | string | Yes | Item category \(e.g., LOGIN, PASSWORD, API_CREDENTIAL, SECURE_NOTE, SERVER, DATABASE\) |
|
||||
| `title` | string | No | Item title |
|
||||
| `tags` | string | No | Comma-separated list of tags |
|
||||
| `fields` | string | No | JSON array of field objects \(e.g., \[\{"label":"username","value":"admin","type":"STRING","purpose":"USERNAME"\}\]\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `response` | json | Operation response data |
|
||||
|
||||
### `onepassword_replace_item`
|
||||
|
||||
Replace an entire item with new data (full update)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | 1Password Connect API token |
|
||||
| `serverUrl` | string | Yes | 1Password Connect server URL \(e.g., http://localhost:8080\) |
|
||||
| `vaultId` | string | Yes | The vault UUID |
|
||||
| `itemId` | string | Yes | The item UUID to replace |
|
||||
| `item` | string | Yes | JSON object representing the full item \(e.g., \{"vault":\{"id":"..."\},"category":"LOGIN","title":"My Item","fields":\[...\]\}\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `response` | json | Operation response data |
|
||||
|
||||
### `onepassword_update_item`
|
||||
|
||||
Update an existing item using JSON Patch operations (RFC6902)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | 1Password Connect API token |
|
||||
| `serverUrl` | string | Yes | 1Password Connect server URL \(e.g., http://localhost:8080\) |
|
||||
| `vaultId` | string | Yes | The vault UUID |
|
||||
| `itemId` | string | Yes | The item UUID to update |
|
||||
| `operations` | string | Yes | JSON array of RFC6902 patch operations \(e.g., \[\{"op":"replace","path":"/title","value":"New Title"\}\]\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `response` | json | Operation response data |
|
||||
|
||||
### `onepassword_delete_item`
|
||||
|
||||
Delete an item from a vault
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | 1Password Connect API token |
|
||||
| `serverUrl` | string | Yes | 1Password Connect server URL \(e.g., http://localhost:8080\) |
|
||||
| `vaultId` | string | Yes | The vault UUID |
|
||||
| `itemId` | string | Yes | The item UUID to delete |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether the item was successfully deleted |
|
||||
|
||||
|
||||
@@ -16,9 +16,16 @@ const jiraUpdateSchema = z.object({
|
||||
summary: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
status: z.string().optional(),
|
||||
priority: z.string().optional(),
|
||||
assignee: z.string().optional(),
|
||||
labels: z.array(z.string()).optional(),
|
||||
components: z.array(z.string()).optional(),
|
||||
duedate: z.string().optional(),
|
||||
fixVersions: z.array(z.string()).optional(),
|
||||
environment: z.string().optional(),
|
||||
customFieldId: z.string().optional(),
|
||||
customFieldValue: z.string().optional(),
|
||||
notifyUsers: z.boolean().optional(),
|
||||
cloudId: z.string().optional(),
|
||||
})
|
||||
|
||||
@@ -45,9 +52,16 @@ export async function PUT(request: NextRequest) {
|
||||
summary,
|
||||
title,
|
||||
description,
|
||||
status,
|
||||
priority,
|
||||
assignee,
|
||||
labels,
|
||||
components,
|
||||
duedate,
|
||||
fixVersions,
|
||||
environment,
|
||||
customFieldId,
|
||||
customFieldValue,
|
||||
notifyUsers,
|
||||
cloudId: providedCloudId,
|
||||
} = validation.data
|
||||
|
||||
@@ -64,7 +78,8 @@ export async function PUT(request: NextRequest) {
|
||||
return NextResponse.json({ error: issueKeyValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${issueKey}`
|
||||
const notifyParam = notifyUsers === false ? '?notifyUsers=false' : ''
|
||||
const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${issueKey}${notifyParam}`
|
||||
|
||||
logger.info('Updating Jira issue at:', url)
|
||||
|
||||
@@ -93,24 +108,65 @@ export async function PUT(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
if (status !== undefined && status !== null && status !== '') {
|
||||
fields.status = {
|
||||
name: status,
|
||||
}
|
||||
}
|
||||
|
||||
if (priority !== undefined && priority !== null && priority !== '') {
|
||||
fields.priority = {
|
||||
name: priority,
|
||||
}
|
||||
const isNumericId = /^\d+$/.test(priority)
|
||||
fields.priority = isNumericId ? { id: priority } : { name: priority }
|
||||
}
|
||||
|
||||
if (assignee !== undefined && assignee !== null && assignee !== '') {
|
||||
fields.assignee = {
|
||||
id: assignee,
|
||||
accountId: assignee,
|
||||
}
|
||||
}
|
||||
|
||||
if (labels !== undefined && labels !== null && labels.length > 0) {
|
||||
fields.labels = labels
|
||||
}
|
||||
|
||||
if (components !== undefined && components !== null && components.length > 0) {
|
||||
fields.components = components.map((name) => ({ name }))
|
||||
}
|
||||
|
||||
if (duedate !== undefined && duedate !== null && duedate !== '') {
|
||||
fields.duedate = duedate
|
||||
}
|
||||
|
||||
if (fixVersions !== undefined && fixVersions !== null && fixVersions.length > 0) {
|
||||
fields.fixVersions = fixVersions.map((name) => ({ name }))
|
||||
}
|
||||
|
||||
if (environment !== undefined && environment !== null && environment !== '') {
|
||||
fields.environment = {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: environment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
customFieldId !== undefined &&
|
||||
customFieldId !== null &&
|
||||
customFieldId !== '' &&
|
||||
customFieldValue !== undefined &&
|
||||
customFieldValue !== null &&
|
||||
customFieldValue !== ''
|
||||
) {
|
||||
const fieldId = customFieldId.startsWith('customfield_')
|
||||
? customFieldId
|
||||
: `customfield_${customFieldId}`
|
||||
fields[fieldId] = customFieldValue
|
||||
}
|
||||
|
||||
const requestBody = { fields }
|
||||
|
||||
const response = await fetch(url, {
|
||||
|
||||
@@ -32,6 +32,8 @@ export async function POST(request: NextRequest) {
|
||||
environment,
|
||||
customFieldId,
|
||||
customFieldValue,
|
||||
components,
|
||||
fixVersions,
|
||||
} = await request.json()
|
||||
|
||||
if (!domain) {
|
||||
@@ -73,10 +75,9 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
logger.info('Creating Jira issue at:', url)
|
||||
|
||||
const isNumericProjectId = /^\d+$/.test(projectId)
|
||||
const fields: Record<string, any> = {
|
||||
project: {
|
||||
id: projectId,
|
||||
},
|
||||
project: isNumericProjectId ? { id: projectId } : { key: projectId },
|
||||
issuetype: {
|
||||
name: normalizedIssueType,
|
||||
},
|
||||
@@ -114,13 +115,31 @@ export async function POST(request: NextRequest) {
|
||||
fields.labels = labels
|
||||
}
|
||||
|
||||
if (
|
||||
components !== undefined &&
|
||||
components !== null &&
|
||||
Array.isArray(components) &&
|
||||
components.length > 0
|
||||
) {
|
||||
fields.components = components.map((name: string) => ({ name }))
|
||||
}
|
||||
|
||||
if (duedate !== undefined && duedate !== null && duedate !== '') {
|
||||
fields.duedate = duedate
|
||||
}
|
||||
|
||||
if (
|
||||
fixVersions !== undefined &&
|
||||
fixVersions !== null &&
|
||||
Array.isArray(fixVersions) &&
|
||||
fixVersions.length > 0
|
||||
) {
|
||||
fields.fixVersions = fixVersions.map((name: string) => ({ name }))
|
||||
}
|
||||
|
||||
if (reporter !== undefined && reporter !== null && reporter !== '') {
|
||||
fields.reporter = {
|
||||
id: reporter,
|
||||
accountId: reporter,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,8 +239,10 @@ export async function POST(request: NextRequest) {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
id: responseData.id || '',
|
||||
issueKey: issueKey,
|
||||
summary: responseData.fields?.summary || 'Issue created',
|
||||
self: responseData.self || '',
|
||||
summary: responseData.fields?.summary || summary || 'Issue created',
|
||||
success: true,
|
||||
url: `https://${domain}/browse/${issueKey}`,
|
||||
...(assigneeId && { assigneeId }),
|
||||
|
||||
@@ -165,8 +165,25 @@ export async function POST(request: NextRequest) {
|
||||
issueIdOrKey,
|
||||
approvalId,
|
||||
decision,
|
||||
id: data.id ?? null,
|
||||
name: data.name ?? null,
|
||||
finalDecision: data.finalDecision ?? null,
|
||||
canAnswerApproval: data.canAnswerApproval ?? null,
|
||||
approvers: (data.approvers ?? []).map((a: Record<string, unknown>) => {
|
||||
const approver = a.approver as Record<string, unknown> | undefined
|
||||
return {
|
||||
approver: {
|
||||
accountId: approver?.accountId ?? null,
|
||||
displayName: approver?.displayName ?? null,
|
||||
emailAddress: approver?.emailAddress ?? null,
|
||||
active: approver?.active ?? null,
|
||||
},
|
||||
approverDecision: a.approverDecision ?? null,
|
||||
}
|
||||
}),
|
||||
createdDate: data.createdDate ?? null,
|
||||
completedDate: data.completedDate ?? null,
|
||||
success: true,
|
||||
approval: data,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -95,6 +95,14 @@ export async function POST(request: NextRequest) {
|
||||
commentId: data.id,
|
||||
body: data.body,
|
||||
isPublic: data.public,
|
||||
author: data.author
|
||||
? {
|
||||
accountId: data.author.accountId ?? null,
|
||||
displayName: data.author.displayName ?? null,
|
||||
emailAddress: data.author.emailAddress ?? null,
|
||||
}
|
||||
: null,
|
||||
createdDate: data.created ?? null,
|
||||
success: true,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -23,6 +23,7 @@ export async function POST(request: NextRequest) {
|
||||
issueIdOrKey,
|
||||
isPublic,
|
||||
internal,
|
||||
expand,
|
||||
start,
|
||||
limit,
|
||||
} = body
|
||||
@@ -57,8 +58,9 @@ export async function POST(request: NextRequest) {
|
||||
const baseUrl = getJsmApiBaseUrl(cloudId)
|
||||
|
||||
const params = new URLSearchParams()
|
||||
if (isPublic) params.append('public', isPublic)
|
||||
if (internal) params.append('internal', internal)
|
||||
if (isPublic !== undefined) params.append('public', String(isPublic))
|
||||
if (internal !== undefined) params.append('internal', String(internal))
|
||||
if (expand) params.append('expand', expand)
|
||||
if (start) params.append('start', start)
|
||||
if (limit) params.append('limit', limit)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function POST(request: NextRequest) {
|
||||
query,
|
||||
start,
|
||||
limit,
|
||||
emails,
|
||||
accountIds,
|
||||
} = body
|
||||
|
||||
if (!domain) {
|
||||
@@ -56,24 +56,26 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const baseUrl = getJsmApiBaseUrl(cloudId)
|
||||
|
||||
const parsedEmails = emails
|
||||
? typeof emails === 'string'
|
||||
? emails
|
||||
const parsedAccountIds = accountIds
|
||||
? typeof accountIds === 'string'
|
||||
? accountIds
|
||||
.split(',')
|
||||
.map((email: string) => email.trim())
|
||||
.filter((email: string) => email)
|
||||
: emails
|
||||
.map((id: string) => id.trim())
|
||||
.filter((id: string) => id)
|
||||
: Array.isArray(accountIds)
|
||||
? accountIds
|
||||
: []
|
||||
: []
|
||||
|
||||
const isAddOperation = parsedEmails.length > 0
|
||||
const isAddOperation = parsedAccountIds.length > 0
|
||||
|
||||
if (isAddOperation) {
|
||||
const url = `${baseUrl}/servicedesk/${serviceDeskId}/customer`
|
||||
|
||||
logger.info('Adding customers to:', url, { emails: parsedEmails })
|
||||
logger.info('Adding customers to:', url, { accountIds: parsedAccountIds })
|
||||
|
||||
const requestBody: Record<string, unknown> = {
|
||||
usernames: parsedEmails,
|
||||
accountIds: parsedAccountIds,
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
|
||||
@@ -31,6 +31,9 @@ export async function POST(request: NextRequest) {
|
||||
description,
|
||||
raiseOnBehalfOf,
|
||||
requestFieldValues,
|
||||
requestParticipants,
|
||||
channel,
|
||||
expand,
|
||||
} = body
|
||||
|
||||
if (!domain) {
|
||||
@@ -80,6 +83,19 @@ export async function POST(request: NextRequest) {
|
||||
if (raiseOnBehalfOf) {
|
||||
requestBody.raiseOnBehalfOf = raiseOnBehalfOf
|
||||
}
|
||||
if (requestParticipants) {
|
||||
requestBody.requestParticipants = Array.isArray(requestParticipants)
|
||||
? requestParticipants
|
||||
: typeof requestParticipants === 'string'
|
||||
? requestParticipants
|
||||
.split(',')
|
||||
.map((id: string) => id.trim())
|
||||
.filter(Boolean)
|
||||
: []
|
||||
}
|
||||
if (channel) {
|
||||
requestBody.channel = channel
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
@@ -111,6 +127,21 @@ export async function POST(request: NextRequest) {
|
||||
issueKey: data.issueKey,
|
||||
requestTypeId: data.requestTypeId,
|
||||
serviceDeskId: data.serviceDeskId,
|
||||
createdDate: data.createdDate ?? null,
|
||||
currentStatus: data.currentStatus
|
||||
? {
|
||||
status: data.currentStatus.status ?? null,
|
||||
statusCategory: data.currentStatus.statusCategory ?? null,
|
||||
statusDate: data.currentStatus.statusDate ?? null,
|
||||
}
|
||||
: null,
|
||||
reporter: data.reporter
|
||||
? {
|
||||
accountId: data.reporter.accountId ?? null,
|
||||
displayName: data.reporter.displayName ?? null,
|
||||
emailAddress: data.reporter.emailAddress ?? null,
|
||||
}
|
||||
: null,
|
||||
success: true,
|
||||
url: `https://${domain}/browse/${data.issueKey}`,
|
||||
},
|
||||
@@ -126,7 +157,10 @@ export async function POST(request: NextRequest) {
|
||||
return NextResponse.json({ error: issueIdOrKeyValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const url = `${baseUrl}/request/${issueIdOrKey}`
|
||||
const params = new URLSearchParams()
|
||||
if (expand) params.append('expand', expand)
|
||||
|
||||
const url = `${baseUrl}/request/${issueIdOrKey}${params.toString() ? `?${params.toString()}` : ''}`
|
||||
|
||||
logger.info('Fetching request from:', url)
|
||||
|
||||
@@ -155,7 +189,32 @@ export async function POST(request: NextRequest) {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
request: data,
|
||||
issueId: data.issueId ?? null,
|
||||
issueKey: data.issueKey ?? null,
|
||||
requestTypeId: data.requestTypeId ?? null,
|
||||
serviceDeskId: data.serviceDeskId ?? null,
|
||||
createdDate: data.createdDate ?? null,
|
||||
currentStatus: data.currentStatus
|
||||
? {
|
||||
status: data.currentStatus.status ?? null,
|
||||
statusCategory: data.currentStatus.statusCategory ?? null,
|
||||
statusDate: data.currentStatus.statusDate ?? null,
|
||||
}
|
||||
: null,
|
||||
reporter: data.reporter
|
||||
? {
|
||||
accountId: data.reporter.accountId ?? null,
|
||||
displayName: data.reporter.displayName ?? null,
|
||||
emailAddress: data.reporter.emailAddress ?? null,
|
||||
active: data.reporter.active ?? true,
|
||||
}
|
||||
: null,
|
||||
requestFieldValues: (data.requestFieldValues ?? []).map((fv: Record<string, unknown>) => ({
|
||||
fieldId: fv.fieldId ?? null,
|
||||
label: fv.label ?? null,
|
||||
value: fv.value ?? null,
|
||||
})),
|
||||
url: `https://${domain}/browse/${data.issueKey}`,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
|
||||
import {
|
||||
validateAlphanumericId,
|
||||
validateEnum,
|
||||
validateJiraCloudId,
|
||||
} from '@/lib/core/security/input-validation'
|
||||
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
@@ -23,7 +27,9 @@ export async function POST(request: NextRequest) {
|
||||
serviceDeskId,
|
||||
requestOwnership,
|
||||
requestStatus,
|
||||
requestTypeId,
|
||||
searchTerm,
|
||||
expand,
|
||||
start,
|
||||
limit,
|
||||
} = body
|
||||
@@ -52,17 +58,45 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
const VALID_REQUEST_OWNERSHIP = [
|
||||
'OWNED_REQUESTS',
|
||||
'PARTICIPATED_REQUESTS',
|
||||
'APPROVER',
|
||||
'ALL_REQUESTS',
|
||||
] as const
|
||||
const VALID_REQUEST_STATUS = ['OPEN_REQUESTS', 'CLOSED_REQUESTS', 'ALL_REQUESTS'] as const
|
||||
|
||||
if (requestOwnership) {
|
||||
const ownershipValidation = validateEnum(
|
||||
requestOwnership,
|
||||
VALID_REQUEST_OWNERSHIP,
|
||||
'requestOwnership'
|
||||
)
|
||||
if (!ownershipValidation.isValid) {
|
||||
return NextResponse.json({ error: ownershipValidation.error }, { status: 400 })
|
||||
}
|
||||
}
|
||||
|
||||
if (requestStatus) {
|
||||
const statusValidation = validateEnum(requestStatus, VALID_REQUEST_STATUS, 'requestStatus')
|
||||
if (!statusValidation.isValid) {
|
||||
return NextResponse.json({ error: statusValidation.error }, { status: 400 })
|
||||
}
|
||||
}
|
||||
|
||||
const baseUrl = getJsmApiBaseUrl(cloudId)
|
||||
|
||||
const params = new URLSearchParams()
|
||||
if (serviceDeskId) params.append('serviceDeskId', serviceDeskId)
|
||||
if (requestOwnership && requestOwnership !== 'ALL_REQUESTS') {
|
||||
if (requestOwnership) {
|
||||
params.append('requestOwnership', requestOwnership)
|
||||
}
|
||||
if (requestStatus && requestStatus !== 'ALL') {
|
||||
if (requestStatus) {
|
||||
params.append('requestStatus', requestStatus)
|
||||
}
|
||||
if (requestTypeId) params.append('requestTypeId', requestTypeId)
|
||||
if (searchTerm) params.append('searchTerm', searchTerm)
|
||||
if (expand) params.append('expand', expand)
|
||||
if (start) params.append('start', start)
|
||||
if (limit) params.append('limit', limit)
|
||||
|
||||
|
||||
119
apps/sim/app/api/tools/jsm/requesttypefields/route.ts
Normal file
119
apps/sim/app/api/tools/jsm/requesttypefields/route.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
|
||||
import { getJiraCloudId, getJsmApiBaseUrl, getJsmHeaders } from '@/tools/jsm/utils'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('JsmRequestTypeFieldsAPI')
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const auth = await checkInternalAuth(request)
|
||||
if (!auth.success || !auth.userId) {
|
||||
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, cloudId: cloudIdParam, serviceDeskId, requestTypeId } = body
|
||||
|
||||
if (!domain) {
|
||||
logger.error('Missing domain in request')
|
||||
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
logger.error('Missing access token in request')
|
||||
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!serviceDeskId) {
|
||||
logger.error('Missing serviceDeskId in request')
|
||||
return NextResponse.json({ error: 'Service Desk ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
if (!requestTypeId) {
|
||||
logger.error('Missing requestTypeId in request')
|
||||
return NextResponse.json({ error: 'Request Type ID is required' }, { status: 400 })
|
||||
}
|
||||
|
||||
const cloudId = cloudIdParam || (await getJiraCloudId(domain, accessToken))
|
||||
|
||||
const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
|
||||
if (!cloudIdValidation.isValid) {
|
||||
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const serviceDeskIdValidation = validateAlphanumericId(serviceDeskId, 'serviceDeskId')
|
||||
if (!serviceDeskIdValidation.isValid) {
|
||||
return NextResponse.json({ error: serviceDeskIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const requestTypeIdValidation = validateAlphanumericId(requestTypeId, 'requestTypeId')
|
||||
if (!requestTypeIdValidation.isValid) {
|
||||
return NextResponse.json({ error: requestTypeIdValidation.error }, { status: 400 })
|
||||
}
|
||||
|
||||
const baseUrl = getJsmApiBaseUrl(cloudId)
|
||||
const url = `${baseUrl}/servicedesk/${serviceDeskId}/requesttype/${requestTypeId}/field`
|
||||
|
||||
logger.info('Fetching request type fields from:', url)
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: getJsmHeaders(accessToken),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
logger.error('JSM API error:', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
error: errorText,
|
||||
})
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: `JSM API error: ${response.status} ${response.statusText}`, details: errorText },
|
||||
{ status: response.status }
|
||||
)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
serviceDeskId,
|
||||
requestTypeId,
|
||||
canAddRequestParticipants: data.canAddRequestParticipants ?? false,
|
||||
canRaiseOnBehalfOf: data.canRaiseOnBehalfOf ?? false,
|
||||
requestTypeFields: (data.requestTypeFields ?? []).map((field: Record<string, unknown>) => ({
|
||||
fieldId: field.fieldId ?? null,
|
||||
name: field.name ?? null,
|
||||
description: field.description ?? null,
|
||||
required: field.required ?? false,
|
||||
visible: field.visible ?? true,
|
||||
validValues: field.validValues ?? [],
|
||||
presetValues: field.presetValues ?? [],
|
||||
defaultValues: field.defaultValues ?? [],
|
||||
jiraSchema: field.jiraSchema ?? null,
|
||||
})),
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('Error fetching request type fields:', {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
stack: error instanceof Error ? error.stack : undefined,
|
||||
})
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: error instanceof Error ? error.message : 'Internal server error',
|
||||
success: false,
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,17 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, cloudId: cloudIdParam, serviceDeskId, start, limit } = body
|
||||
const {
|
||||
domain,
|
||||
accessToken,
|
||||
cloudId: cloudIdParam,
|
||||
serviceDeskId,
|
||||
searchQuery,
|
||||
groupId,
|
||||
expand,
|
||||
start,
|
||||
limit,
|
||||
} = body
|
||||
|
||||
if (!domain) {
|
||||
logger.error('Missing domain in request')
|
||||
@@ -48,6 +58,9 @@ export async function POST(request: NextRequest) {
|
||||
const baseUrl = getJsmApiBaseUrl(cloudId)
|
||||
|
||||
const params = new URLSearchParams()
|
||||
if (searchQuery) params.append('searchQuery', searchQuery)
|
||||
if (groupId) params.append('groupId', groupId)
|
||||
if (expand) params.append('expand', expand)
|
||||
if (start) params.append('start', start)
|
||||
if (limit) params.append('limit', limit)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, cloudId: cloudIdParam, start, limit } = body
|
||||
const { domain, accessToken, cloudId: cloudIdParam, expand, start, limit } = body
|
||||
|
||||
if (!domain) {
|
||||
logger.error('Missing domain in request')
|
||||
@@ -38,6 +38,7 @@ export async function POST(request: NextRequest) {
|
||||
const baseUrl = getJsmApiBaseUrl(cloudId)
|
||||
|
||||
const params = new URLSearchParams()
|
||||
if (expand) params.append('expand', expand)
|
||||
if (start) params.append('start', start)
|
||||
if (limit) params.append('limit', limit)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { domain, accessToken, cloudId: cloudIdParam, issueIdOrKey } = body
|
||||
const { domain, accessToken, cloudId: cloudIdParam, issueIdOrKey, start, limit } = body
|
||||
|
||||
if (!domain) {
|
||||
logger.error('Missing domain in request')
|
||||
@@ -47,7 +47,11 @@ export async function POST(request: NextRequest) {
|
||||
|
||||
const baseUrl = getJsmApiBaseUrl(cloudId)
|
||||
|
||||
const url = `${baseUrl}/request/${issueIdOrKey}/transition`
|
||||
const params = new URLSearchParams()
|
||||
if (start) params.append('start', start)
|
||||
if (limit) params.append('limit', limit)
|
||||
|
||||
const url = `${baseUrl}/request/${issueIdOrKey}/transition${params.toString() ? `?${params.toString()}` : ''}`
|
||||
|
||||
logger.info('Fetching transitions from:', url)
|
||||
|
||||
@@ -78,6 +82,8 @@ export async function POST(request: NextRequest) {
|
||||
ts: new Date().toISOString(),
|
||||
issueIdOrKey,
|
||||
transitions: data.values || [],
|
||||
total: data.size || 0,
|
||||
isLastPage: data.isLastPage ?? true,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
@@ -269,14 +269,32 @@ Return ONLY the description text - no explanations.`,
|
||||
'Describe the issue details (e.g., "users seeing 500 error when clicking submit")...',
|
||||
},
|
||||
},
|
||||
// Write Issue additional fields
|
||||
// Write Issue type and parent
|
||||
{
|
||||
id: 'issueType',
|
||||
title: 'Issue Type',
|
||||
type: 'short-input',
|
||||
placeholder: 'Issue type (e.g., Task, Story, Bug, Epic)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
value: () => 'Task',
|
||||
},
|
||||
{
|
||||
id: 'parentIssue',
|
||||
title: 'Parent Issue Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Parent issue key for subtasks (e.g., PROJ-123)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
},
|
||||
// Write/Update Issue additional fields
|
||||
{
|
||||
id: 'assignee',
|
||||
title: 'Assignee Account ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Assignee account ID (e.g., 5b109f2e9729b51b54dc274d)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'priority',
|
||||
@@ -284,7 +302,7 @@ Return ONLY the description text - no explanations.`,
|
||||
type: 'short-input',
|
||||
placeholder: 'Priority ID or name (e.g., "10000" or "High")',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'labels',
|
||||
@@ -292,7 +310,7 @@ Return ONLY the description text - no explanations.`,
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated labels (e.g., bug, urgent)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'duedate',
|
||||
@@ -300,7 +318,7 @@ Return ONLY the description text - no explanations.`,
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD (e.g., 2024-12-31)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a date in YYYY-MM-DD format based on the user's description.
|
||||
@@ -329,7 +347,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
type: 'long-input',
|
||||
placeholder: 'Environment information (e.g., Production, Staging)',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'customFieldId',
|
||||
@@ -337,7 +355,7 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., customfield_10001 or 10001',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'customFieldValue',
|
||||
@@ -345,7 +363,34 @@ Return ONLY the date string in YYYY-MM-DD format - no explanations, no quotes, n
|
||||
type: 'short-input',
|
||||
placeholder: 'Value for the custom field',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: 'write' },
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'components',
|
||||
title: 'Components',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated component names',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'fixVersions',
|
||||
title: 'Fix Versions',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated fix version names',
|
||||
dependsOn: ['projectId'],
|
||||
condition: { field: 'operation', value: ['write', 'update'] },
|
||||
},
|
||||
{
|
||||
id: 'notifyUsers',
|
||||
title: 'Notify Users',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Yes', id: 'true' },
|
||||
{ label: 'No', id: 'false' },
|
||||
],
|
||||
value: () => 'true',
|
||||
condition: { field: 'operation', value: 'update' },
|
||||
},
|
||||
// Delete Issue fields
|
||||
{
|
||||
@@ -395,6 +440,13 @@ Return ONLY the comment text - no explanations.`,
|
||||
placeholder: 'Describe the transition reason (e.g., "fixed bug", "ready for QA review")...',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'resolution',
|
||||
title: 'Resolution',
|
||||
type: 'short-input',
|
||||
placeholder: 'Resolution name (e.g., "Fixed", "Won\'t Fix")',
|
||||
condition: { field: 'operation', value: 'transition' },
|
||||
},
|
||||
// Search Issues fields
|
||||
{
|
||||
id: 'jql',
|
||||
@@ -420,6 +472,20 @@ Return ONLY the JQL query - no explanations or markdown formatting.`,
|
||||
generationType: 'sql-query',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'nextPageToken',
|
||||
title: 'Next Page Token',
|
||||
type: 'short-input',
|
||||
placeholder: 'Cursor token for next page (omit for first page)',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
},
|
||||
{
|
||||
id: 'startAt',
|
||||
title: 'Start At',
|
||||
type: 'short-input',
|
||||
placeholder: 'Pagination start index (default: 0)',
|
||||
condition: { field: 'operation', value: ['get_comments', 'get_worklogs'] },
|
||||
},
|
||||
{
|
||||
id: 'maxResults',
|
||||
title: 'Max Results',
|
||||
@@ -638,10 +704,12 @@ Return ONLY the comment text - no explanations.`,
|
||||
'jira_update',
|
||||
'jira_write',
|
||||
'jira_bulk_read',
|
||||
'jira_bulk_read_v2',
|
||||
'jira_delete_issue',
|
||||
'jira_assign_issue',
|
||||
'jira_transition_issue',
|
||||
'jira_search_issues',
|
||||
'jira_search_issues_v2',
|
||||
'jira_add_comment',
|
||||
'jira_get_comments',
|
||||
'jira_update_comment',
|
||||
@@ -669,7 +737,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
case 'read':
|
||||
// If a project is selected but no issue is chosen, route to bulk read
|
||||
if (effectiveProjectId && !effectiveIssueKey) {
|
||||
return 'jira_bulk_read'
|
||||
return 'jira_bulk_read_v2'
|
||||
}
|
||||
return 'jira_retrieve'
|
||||
case 'update':
|
||||
@@ -677,7 +745,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
case 'write':
|
||||
return 'jira_write'
|
||||
case 'read-bulk':
|
||||
return 'jira_bulk_read'
|
||||
return 'jira_bulk_read_v2'
|
||||
case 'delete':
|
||||
return 'jira_delete_issue'
|
||||
case 'assign':
|
||||
@@ -685,7 +753,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
case 'transition':
|
||||
return 'jira_transition_issue'
|
||||
case 'search':
|
||||
return 'jira_search_issues'
|
||||
return 'jira_search_issues_v2'
|
||||
case 'add_comment':
|
||||
return 'jira_add_comment'
|
||||
case 'get_comments':
|
||||
@@ -756,7 +824,9 @@ Return ONLY the comment text - no explanations.`,
|
||||
assignee: params.assignee || undefined,
|
||||
priority: params.priority || undefined,
|
||||
labels: parseCommaSeparated(params.labels),
|
||||
components: parseCommaSeparated(params.components),
|
||||
duedate: params.duedate || undefined,
|
||||
fixVersions: parseCommaSeparated(params.fixVersions),
|
||||
reporter: params.reporter || undefined,
|
||||
environment: params.environment || undefined,
|
||||
customFieldId: params.customFieldId || undefined,
|
||||
@@ -768,11 +838,29 @@ Return ONLY the comment text - no explanations.`,
|
||||
}
|
||||
}
|
||||
case 'update': {
|
||||
const parseCommaSeparated = (value: string | undefined): string[] | undefined => {
|
||||
if (!value || value.trim() === '') return undefined
|
||||
return value
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter((item) => item !== '')
|
||||
}
|
||||
|
||||
const updateParams = {
|
||||
projectId: effectiveProjectId,
|
||||
issueKey: effectiveIssueKey,
|
||||
summary: params.summary || '',
|
||||
description: params.description || '',
|
||||
summary: params.summary || undefined,
|
||||
description: params.description || undefined,
|
||||
assignee: params.assignee || undefined,
|
||||
priority: params.priority || undefined,
|
||||
labels: parseCommaSeparated(params.labels),
|
||||
components: parseCommaSeparated(params.components),
|
||||
duedate: params.duedate || undefined,
|
||||
fixVersions: parseCommaSeparated(params.fixVersions),
|
||||
environment: params.environment || undefined,
|
||||
customFieldId: params.customFieldId || undefined,
|
||||
customFieldValue: params.customFieldValue || undefined,
|
||||
notifyUsers: params.notifyUsers === 'false' ? false : undefined,
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
@@ -813,12 +901,14 @@ Return ONLY the comment text - no explanations.`,
|
||||
issueKey: effectiveIssueKey,
|
||||
transitionId: params.transitionId,
|
||||
comment: params.transitionComment,
|
||||
resolution: params.resolution || undefined,
|
||||
}
|
||||
}
|
||||
case 'search': {
|
||||
return {
|
||||
...baseParams,
|
||||
jql: params.jql,
|
||||
nextPageToken: params.nextPageToken || undefined,
|
||||
maxResults: params.maxResults ? Number.parseInt(params.maxResults) : undefined,
|
||||
}
|
||||
}
|
||||
@@ -833,6 +923,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
return {
|
||||
...baseParams,
|
||||
issueKey: effectiveIssueKey,
|
||||
startAt: params.startAt ? Number.parseInt(params.startAt) : undefined,
|
||||
maxResults: params.maxResults ? Number.parseInt(params.maxResults) : undefined,
|
||||
}
|
||||
}
|
||||
@@ -889,6 +980,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
return {
|
||||
...baseParams,
|
||||
issueKey: effectiveIssueKey,
|
||||
startAt: params.startAt ? Number.parseInt(params.startAt) : undefined,
|
||||
maxResults: params.maxResults ? Number.parseInt(params.maxResults) : undefined,
|
||||
}
|
||||
}
|
||||
@@ -966,15 +1058,19 @@ Return ONLY the comment text - no explanations.`,
|
||||
summary: { type: 'string', description: 'Issue summary' },
|
||||
description: { type: 'string', description: 'Issue description' },
|
||||
issueType: { type: 'string', description: 'Issue type' },
|
||||
// Write operation additional inputs
|
||||
// Write/Update operation additional inputs
|
||||
parentIssue: { type: 'string', description: 'Parent issue key for subtasks' },
|
||||
assignee: { type: 'string', description: 'Assignee account ID' },
|
||||
priority: { type: 'string', description: 'Priority ID or name' },
|
||||
labels: { type: 'string', description: 'Comma-separated labels for the issue' },
|
||||
components: { type: 'string', description: 'Comma-separated component names' },
|
||||
duedate: { type: 'string', description: 'Due date in YYYY-MM-DD format' },
|
||||
fixVersions: { type: 'string', description: 'Comma-separated fix version names' },
|
||||
reporter: { type: 'string', description: 'Reporter account ID' },
|
||||
environment: { type: 'string', description: 'Environment information' },
|
||||
customFieldId: { type: 'string', description: 'Custom field ID (e.g., customfield_10001)' },
|
||||
customFieldValue: { type: 'string', description: 'Value for the custom field' },
|
||||
notifyUsers: { type: 'string', description: 'Whether to send notifications on update' },
|
||||
// Delete operation inputs
|
||||
deleteSubtasks: { type: 'string', description: 'Whether to delete subtasks (true/false)' },
|
||||
// Assign/Watcher operation inputs
|
||||
@@ -985,7 +1081,13 @@ Return ONLY the comment text - no explanations.`,
|
||||
// Transition operation inputs
|
||||
transitionId: { type: 'string', description: 'Transition ID for workflow status changes' },
|
||||
transitionComment: { type: 'string', description: 'Optional comment for transition' },
|
||||
resolution: { type: 'string', description: 'Resolution name for transition (e.g., "Fixed")' },
|
||||
// Search operation inputs
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Cursor token for the next page of search results',
|
||||
},
|
||||
startAt: { type: 'string', description: 'Pagination start index' },
|
||||
jql: { type: 'string', description: 'JQL (Jira Query Language) search query' },
|
||||
maxResults: { type: 'string', description: 'Maximum number of results to return' },
|
||||
// Comment operation inputs
|
||||
@@ -1038,8 +1140,11 @@ Return ONLY the comment text - no explanations.`,
|
||||
id: { type: 'string', description: 'Jira issue ID' },
|
||||
key: { type: 'string', description: 'Jira issue key' },
|
||||
|
||||
// jira_search_issues outputs
|
||||
// jira_search_issues_v2 / jira_bulk_read_v2 outputs
|
||||
total: { type: 'number', description: 'Total number of matching issues' },
|
||||
nextPageToken: { type: 'string', description: 'Cursor token for the next page of results' },
|
||||
isLast: { type: 'boolean', description: 'Whether this is the last page of results' },
|
||||
// Shared pagination outputs (get_comments, get_worklogs, get_users)
|
||||
startAt: { type: 'number', description: 'Pagination start index' },
|
||||
maxResults: { type: 'number', description: 'Maximum results per page' },
|
||||
issues: {
|
||||
|
||||
@@ -40,6 +40,7 @@ export const JiraServiceManagementBlock: BlockConfig<JsmResponse> = {
|
||||
{ label: 'Add Participants', id: 'add_participants' },
|
||||
{ label: 'Get Approvals', id: 'get_approvals' },
|
||||
{ label: 'Answer Approval', id: 'answer_approval' },
|
||||
{ label: 'Get Request Type Fields', id: 'get_request_type_fields' },
|
||||
],
|
||||
value: () => 'get_service_desks',
|
||||
},
|
||||
@@ -109,6 +110,8 @@ export const JiraServiceManagementBlock: BlockConfig<JsmResponse> = {
|
||||
'get_organizations',
|
||||
'add_organization',
|
||||
'get_queues',
|
||||
'get_requests',
|
||||
'get_request_type_fields',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -118,7 +121,7 @@ export const JiraServiceManagementBlock: BlockConfig<JsmResponse> = {
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Enter request type ID',
|
||||
condition: { field: 'operation', value: 'create_request' },
|
||||
condition: { field: 'operation', value: ['create_request', 'get_request_type_fields'] },
|
||||
},
|
||||
{
|
||||
id: 'issueIdOrKey',
|
||||
@@ -188,6 +191,51 @@ Return ONLY the description text - no explanations.`,
|
||||
placeholder: 'Account ID to raise request on behalf of',
|
||||
condition: { field: 'operation', value: 'create_request' },
|
||||
},
|
||||
{
|
||||
id: 'requestParticipants',
|
||||
title: 'Request Participants',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated account IDs to add as participants',
|
||||
condition: { field: 'operation', value: 'create_request' },
|
||||
},
|
||||
{
|
||||
id: 'channel',
|
||||
title: 'Channel',
|
||||
type: 'short-input',
|
||||
placeholder: 'Channel (e.g., portal, email)',
|
||||
condition: { field: 'operation', value: 'create_request' },
|
||||
},
|
||||
{
|
||||
id: 'requestFieldValues',
|
||||
title: 'Custom Field Values',
|
||||
type: 'long-input',
|
||||
placeholder: 'JSON object of custom field values (e.g., {"customfield_10010": "value"})',
|
||||
condition: { field: 'operation', value: 'create_request' },
|
||||
},
|
||||
{
|
||||
id: 'searchQuery',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter request types by name',
|
||||
condition: { field: 'operation', value: 'get_request_types' },
|
||||
},
|
||||
{
|
||||
id: 'groupId',
|
||||
title: 'Group ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by request type group',
|
||||
condition: { field: 'operation', value: 'get_request_types' },
|
||||
},
|
||||
{
|
||||
id: 'expand',
|
||||
title: 'Expand',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated fields to expand',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_request', 'get_requests', 'get_comments'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'commentBody',
|
||||
title: 'Comment',
|
||||
@@ -220,11 +268,11 @@ Return ONLY the comment text - no explanations.`,
|
||||
condition: { field: 'operation', value: 'add_comment' },
|
||||
},
|
||||
{
|
||||
id: 'emails',
|
||||
title: 'Email Addresses',
|
||||
id: 'accountIds',
|
||||
title: 'Account IDs',
|
||||
type: 'short-input',
|
||||
required: true,
|
||||
placeholder: 'Comma-separated email addresses',
|
||||
placeholder: 'Comma-separated Atlassian account IDs',
|
||||
condition: { field: 'operation', value: 'add_customer' },
|
||||
},
|
||||
{
|
||||
@@ -269,7 +317,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
{ label: 'All Requests', id: 'ALL_REQUESTS' },
|
||||
{ label: 'My Requests', id: 'OWNED_REQUESTS' },
|
||||
{ label: 'Participated', id: 'PARTICIPATED_REQUESTS' },
|
||||
{ label: 'Organization', id: 'ORGANIZATION' },
|
||||
{ label: 'Approver', id: 'APPROVER' },
|
||||
],
|
||||
value: () => 'ALL_REQUESTS',
|
||||
condition: { field: 'operation', value: 'get_requests' },
|
||||
@@ -279,9 +327,9 @@ Return ONLY the comment text - no explanations.`,
|
||||
title: 'Request Status',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: 'ALL' },
|
||||
{ label: 'Open', id: 'OPEN' },
|
||||
{ label: 'Closed', id: 'CLOSED' },
|
||||
{ label: 'All', id: 'ALL_REQUESTS' },
|
||||
{ label: 'Open', id: 'OPEN_REQUESTS' },
|
||||
{ label: 'Closed', id: 'CLOSED_REQUESTS' },
|
||||
],
|
||||
value: () => 'ALL',
|
||||
condition: { field: 'operation', value: 'get_requests' },
|
||||
@@ -363,6 +411,9 @@ Return ONLY the comment text - no explanations.`,
|
||||
'get_organizations',
|
||||
'get_queues',
|
||||
'get_sla',
|
||||
'get_transitions',
|
||||
'get_participants',
|
||||
'get_approvals',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -389,6 +440,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
'jsm_add_participants',
|
||||
'jsm_get_approvals',
|
||||
'jsm_answer_approval',
|
||||
'jsm_get_request_type_fields',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
@@ -433,6 +485,8 @@ Return ONLY the comment text - no explanations.`,
|
||||
return 'jsm_get_approvals'
|
||||
case 'answer_approval':
|
||||
return 'jsm_answer_approval'
|
||||
case 'get_request_type_fields':
|
||||
return 'jsm_get_request_type_fields'
|
||||
default:
|
||||
return 'jsm_get_service_desks'
|
||||
}
|
||||
@@ -456,6 +510,8 @@ Return ONLY the comment text - no explanations.`,
|
||||
return {
|
||||
...baseParams,
|
||||
serviceDeskId: params.serviceDeskId,
|
||||
searchQuery: params.searchQuery,
|
||||
groupId: params.groupId,
|
||||
limit: params.maxResults ? Number.parseInt(params.maxResults) : undefined,
|
||||
}
|
||||
case 'create_request':
|
||||
@@ -475,6 +531,11 @@ Return ONLY the comment text - no explanations.`,
|
||||
summary: params.summary,
|
||||
description: params.description,
|
||||
raiseOnBehalfOf: params.raiseOnBehalfOf,
|
||||
requestParticipants: params.requestParticipants,
|
||||
channel: params.channel,
|
||||
requestFieldValues: params.requestFieldValues
|
||||
? JSON.parse(params.requestFieldValues)
|
||||
: undefined,
|
||||
}
|
||||
case 'get_request':
|
||||
if (!params.issueIdOrKey) {
|
||||
@@ -483,6 +544,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
return {
|
||||
...baseParams,
|
||||
issueIdOrKey: params.issueIdOrKey,
|
||||
expand: params.expand,
|
||||
}
|
||||
case 'get_requests':
|
||||
return {
|
||||
@@ -491,6 +553,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
requestOwnership: params.requestOwnership,
|
||||
requestStatus: params.requestStatus,
|
||||
searchTerm: params.searchTerm,
|
||||
expand: params.expand,
|
||||
limit: params.maxResults ? Number.parseInt(params.maxResults) : undefined,
|
||||
}
|
||||
case 'add_comment':
|
||||
@@ -513,6 +576,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
return {
|
||||
...baseParams,
|
||||
issueIdOrKey: params.issueIdOrKey,
|
||||
expand: params.expand,
|
||||
limit: params.maxResults ? Number.parseInt(params.maxResults) : undefined,
|
||||
}
|
||||
case 'get_customers':
|
||||
@@ -529,26 +593,13 @@ Return ONLY the comment text - no explanations.`,
|
||||
if (!params.serviceDeskId) {
|
||||
throw new Error('Service Desk ID is required')
|
||||
}
|
||||
const accountIds = params.accountIds
|
||||
? params.accountIds
|
||||
.split(',')
|
||||
.map((id: string) => id.trim())
|
||||
.filter((id: string) => id)
|
||||
: undefined
|
||||
const emails = params.emails
|
||||
? params.emails
|
||||
.split(',')
|
||||
.map((email: string) => email.trim())
|
||||
.filter((email: string) => email)
|
||||
: undefined
|
||||
if ((!accountIds || accountIds.length === 0) && (!emails || emails.length === 0)) {
|
||||
throw new Error('At least one account ID or email is required')
|
||||
if (!params.accountIds) {
|
||||
throw new Error('At least one account ID is required')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
serviceDeskId: params.serviceDeskId,
|
||||
accountIds,
|
||||
emails,
|
||||
accountIds: params.accountIds,
|
||||
}
|
||||
}
|
||||
case 'get_organizations':
|
||||
@@ -586,6 +637,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
return {
|
||||
...baseParams,
|
||||
issueIdOrKey: params.issueIdOrKey,
|
||||
limit: params.maxResults ? Number.parseInt(params.maxResults) : undefined,
|
||||
}
|
||||
case 'transition_request':
|
||||
if (!params.issueIdOrKey) {
|
||||
@@ -666,6 +718,18 @@ Return ONLY the comment text - no explanations.`,
|
||||
approvalId: params.approvalId,
|
||||
decision: params.approvalDecision,
|
||||
}
|
||||
case 'get_request_type_fields':
|
||||
if (!params.serviceDeskId) {
|
||||
throw new Error('Service Desk ID is required')
|
||||
}
|
||||
if (!params.requestTypeId) {
|
||||
throw new Error('Request Type ID is required')
|
||||
}
|
||||
return {
|
||||
...baseParams,
|
||||
serviceDeskId: params.serviceDeskId,
|
||||
requestTypeId: params.requestTypeId,
|
||||
}
|
||||
default:
|
||||
return baseParams
|
||||
}
|
||||
@@ -684,8 +748,7 @@ Return ONLY the comment text - no explanations.`,
|
||||
raiseOnBehalfOf: { type: 'string', description: 'Account ID to raise request on behalf of' },
|
||||
commentBody: { type: 'string', description: 'Comment text' },
|
||||
isPublic: { type: 'string', description: 'Whether comment is public or internal' },
|
||||
accountIds: { type: 'string', description: 'Comma-separated account IDs' },
|
||||
emails: { type: 'string', description: 'Comma-separated email addresses' },
|
||||
accountIds: { type: 'string', description: 'Comma-separated Atlassian account IDs' },
|
||||
customerQuery: { type: 'string', description: 'Customer search query' },
|
||||
transitionId: { type: 'string', description: 'Transition ID' },
|
||||
transitionComment: { type: 'string', description: 'Transition comment' },
|
||||
@@ -702,6 +765,15 @@ Return ONLY the comment text - no explanations.`,
|
||||
},
|
||||
approvalId: { type: 'string', description: 'Approval ID' },
|
||||
approvalDecision: { type: 'string', description: 'Approval decision (approve/decline)' },
|
||||
requestParticipants: {
|
||||
type: 'string',
|
||||
description: 'Comma-separated account IDs for request participants',
|
||||
},
|
||||
channel: { type: 'string', description: 'Channel (e.g., portal, email)' },
|
||||
requestFieldValues: { type: 'string', description: 'JSON object of custom field values' },
|
||||
searchQuery: { type: 'string', description: 'Filter request types by name' },
|
||||
groupId: { type: 'string', description: 'Filter by request type group ID' },
|
||||
expand: { type: 'string', description: 'Comma-separated fields to expand' },
|
||||
},
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
@@ -731,5 +803,14 @@ Return ONLY the comment text - no explanations.`,
|
||||
decision: { type: 'string', description: 'Approval decision' },
|
||||
total: { type: 'number', description: 'Total count' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
requestTypeFields: { type: 'json', description: 'Array of request type fields' },
|
||||
canAddRequestParticipants: {
|
||||
type: 'boolean',
|
||||
description: 'Whether participants can be added to this request type',
|
||||
},
|
||||
canRaiseOnBehalfOf: {
|
||||
type: 'boolean',
|
||||
description: 'Whether requests can be raised on behalf of another user',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
217
apps/sim/blocks/blocks/onepassword.ts
Normal file
217
apps/sim/blocks/blocks/onepassword.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
import { OnePasswordIcon } from '@/components/icons'
|
||||
import { AuthMode, type BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const OnePasswordBlock: BlockConfig = {
|
||||
type: 'onepassword',
|
||||
name: '1Password',
|
||||
description: 'Manage secrets and items in 1Password vaults',
|
||||
longDescription:
|
||||
'Access and manage secrets stored in 1Password vaults using the Connect API. List vaults, retrieve items with their fields and secrets, create new items, update existing ones, and delete items.',
|
||||
docsLink: 'https://docs.sim.ai/tools/onepassword',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
icon: OnePasswordIcon,
|
||||
authMode: AuthMode.ApiKey,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'List Vaults', id: 'list_vaults' },
|
||||
{ label: 'Get Vault', id: 'get_vault' },
|
||||
{ label: 'List Items', id: 'list_items' },
|
||||
{ label: 'Get Item', id: 'get_item' },
|
||||
{ label: 'Create Item', id: 'create_item' },
|
||||
{ label: 'Replace Item', id: 'replace_item' },
|
||||
{ label: 'Update Item', id: 'update_item' },
|
||||
{ label: 'Delete Item', id: 'delete_item' },
|
||||
],
|
||||
value: () => 'get_item',
|
||||
},
|
||||
{
|
||||
id: 'serverUrl',
|
||||
title: 'Server URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'http://localhost:8080',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'Connect Token',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your 1Password Connect token',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'vaultId',
|
||||
title: 'Vault ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter vault UUID',
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: [
|
||||
'get_vault',
|
||||
'list_items',
|
||||
'get_item',
|
||||
'create_item',
|
||||
'replace_item',
|
||||
'update_item',
|
||||
'delete_item',
|
||||
],
|
||||
},
|
||||
condition: { field: 'operation', value: 'list_vaults', not: true },
|
||||
},
|
||||
{
|
||||
id: 'itemId',
|
||||
title: 'Item ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter item UUID',
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: ['get_item', 'replace_item', 'update_item', 'delete_item'],
|
||||
},
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['get_item', 'replace_item', 'update_item', 'delete_item'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'filter',
|
||||
title: 'Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'SCIM filter (e.g., name eq "My Vault")',
|
||||
condition: { field: 'operation', value: ['list_vaults', 'list_items'] },
|
||||
},
|
||||
{
|
||||
id: 'category',
|
||||
title: 'Category',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Login', id: 'LOGIN' },
|
||||
{ label: 'Password', id: 'PASSWORD' },
|
||||
{ label: 'API Credential', id: 'API_CREDENTIAL' },
|
||||
{ label: 'Secure Note', id: 'SECURE_NOTE' },
|
||||
{ label: 'Server', id: 'SERVER' },
|
||||
{ label: 'Database', id: 'DATABASE' },
|
||||
{ label: 'Credit Card', id: 'CREDIT_CARD' },
|
||||
{ label: 'Identity', id: 'IDENTITY' },
|
||||
{ label: 'SSH Key', id: 'SSH_KEY' },
|
||||
],
|
||||
value: () => 'LOGIN',
|
||||
required: { field: 'operation', value: 'create_item' },
|
||||
condition: { field: 'operation', value: 'create_item' },
|
||||
},
|
||||
{
|
||||
id: 'title',
|
||||
title: 'Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'Item title',
|
||||
condition: { field: 'operation', value: 'create_item' },
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated tags (e.g., production, api)',
|
||||
condition: { field: 'operation', value: 'create_item' },
|
||||
},
|
||||
{
|
||||
id: 'fields',
|
||||
title: 'Fields (JSON)',
|
||||
type: 'code',
|
||||
placeholder:
|
||||
'[\n {\n "label": "username",\n "value": "admin",\n "type": "STRING",\n "purpose": "USERNAME"\n }\n]',
|
||||
condition: { field: 'operation', value: 'create_item' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a 1Password item fields JSON array based on the user's description.
|
||||
Each field object can have: label, value, type (STRING, CONCEALED, EMAIL, URL, TOTP, DATE), purpose (USERNAME, PASSWORD, NOTES, or empty).
|
||||
Examples:
|
||||
- [{"label":"username","value":"admin","type":"STRING","purpose":"USERNAME"},{"label":"password","value":"secret123","type":"CONCEALED","purpose":"PASSWORD"}]
|
||||
- [{"label":"API Key","value":"sk-abc123","type":"CONCEALED"}]
|
||||
|
||||
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'item',
|
||||
title: 'Item (JSON)',
|
||||
type: 'code',
|
||||
placeholder:
|
||||
'{\n "vault": {"id": "..."},\n "category": "LOGIN",\n "title": "My Item",\n "fields": []\n}',
|
||||
required: { field: 'operation', value: 'replace_item' },
|
||||
condition: { field: 'operation', value: 'replace_item' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a full 1Password item JSON object based on the user's description.
|
||||
The object must include vault.id, category, and optionally title, tags, fields, and sections.
|
||||
Categories: LOGIN, PASSWORD, API_CREDENTIAL, SECURE_NOTE, SERVER, DATABASE, CREDIT_CARD, IDENTITY, SSH_KEY.
|
||||
Field types: STRING, CONCEALED, EMAIL, URL, TOTP, DATE. Purposes: USERNAME, PASSWORD, NOTES, or empty.
|
||||
Example: {"vault":{"id":"abc123"},"category":"LOGIN","title":"My Login","fields":[{"label":"username","value":"admin","type":"STRING","purpose":"USERNAME"}]}
|
||||
|
||||
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'operations',
|
||||
title: 'Patch Operations (JSON)',
|
||||
type: 'code',
|
||||
placeholder:
|
||||
'[\n {\n "op": "replace",\n "path": "/title",\n "value": "New Title"\n }\n]',
|
||||
required: { field: 'operation', value: 'update_item' },
|
||||
condition: { field: 'operation', value: 'update_item' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a JSON array of RFC6902 patch operations for a 1Password item based on the user's description.
|
||||
Each operation has: op (add, remove, replace), path (JSON pointer), and value.
|
||||
Examples:
|
||||
- [{"op":"replace","path":"/title","value":"New Title"}]
|
||||
- [{"op":"replace","path":"/fields/username/value","value":"newuser"}]
|
||||
- [{"op":"add","path":"/tags/-","value":"production"}]
|
||||
|
||||
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: [
|
||||
'onepassword_list_vaults',
|
||||
'onepassword_get_vault',
|
||||
'onepassword_list_items',
|
||||
'onepassword_get_item',
|
||||
'onepassword_create_item',
|
||||
'onepassword_replace_item',
|
||||
'onepassword_update_item',
|
||||
'onepassword_delete_item',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `onepassword_${params.operation}`,
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
serverUrl: { type: 'string', description: '1Password Connect server URL' },
|
||||
apiKey: { type: 'string', description: '1Password Connect token' },
|
||||
vaultId: { type: 'string', description: 'Vault UUID' },
|
||||
itemId: { type: 'string', description: 'Item UUID' },
|
||||
filter: { type: 'string', description: 'SCIM filter expression' },
|
||||
category: { type: 'string', description: 'Item category' },
|
||||
title: { type: 'string', description: 'Item title' },
|
||||
tags: { type: 'string', description: 'Comma-separated tags' },
|
||||
fields: { type: 'string', description: 'JSON array of field objects' },
|
||||
item: { type: 'string', description: 'Full item JSON for replacement' },
|
||||
operations: { type: 'string', description: 'JSON array of patch operations' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
response: {
|
||||
type: 'json',
|
||||
description: 'Operation response data',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -91,6 +91,7 @@ import { Neo4jBlock } from '@/blocks/blocks/neo4j'
|
||||
import { NoteBlock } from '@/blocks/blocks/note'
|
||||
import { NotionBlock, NotionV2Block } from '@/blocks/blocks/notion'
|
||||
import { OneDriveBlock } from '@/blocks/blocks/onedrive'
|
||||
import { OnePasswordBlock } from '@/blocks/blocks/onepassword'
|
||||
import { OpenAIBlock } from '@/blocks/blocks/openai'
|
||||
import { OutlookBlock } from '@/blocks/blocks/outlook'
|
||||
import { ParallelBlock } from '@/blocks/blocks/parallel'
|
||||
@@ -268,6 +269,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
note: NoteBlock,
|
||||
notion: NotionBlock,
|
||||
notion_v2: NotionV2Block,
|
||||
onepassword: OnePasswordBlock,
|
||||
onedrive: OneDriveBlock,
|
||||
openai: OpenAIBlock,
|
||||
outlook: OutlookBlock,
|
||||
|
||||
@@ -33,21 +33,21 @@ export function AgentIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M15.6667 9.25H4.66667C2.64162 9.25 1 10.8916 1 12.9167V18.4167C1 20.4417 2.64162 22.0833 4.66667 22.0833H15.6667C17.6917 22.0833 19.3333 20.4417 19.3333 18.4167V12.9167C19.3333 10.8916 17.6917 9.25 15.6667 9.25Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M10.1663 5.58464C11.1789 5.58464 11.9997 4.76382 11.9997 3.7513C11.9997 2.73878 11.1789 1.91797 10.1663 1.91797C9.15382 1.91797 8.33301 2.73878 8.33301 3.7513C8.33301 4.76382 9.15382 5.58464 10.1663 5.58464Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M10.167 5.58594V9.2526M7.41699 16.5859V14.7526M12.917 14.7526V16.5859'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -68,7 +68,7 @@ export function ApiIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M5.61111 24.3889C8.5 27.2778 12.4722 25.4722 13.5556 24.3889L15.7222 22.2222L7.77778 14.2778L5.61111 16.4444C4.52778 17.5278 2.72222 21.5 5.61111 24.3889ZM5.61111 24.3889L2 28M24.3889 5.61111C21.5 2.72222 17.5278 4.52778 16.4444 5.61111L14.2778 7.77778L22.2222 15.7222L24.3889 13.5556C25.4722 12.4722 27.2778 8.5 24.3889 5.61111ZM24.3889 5.61111L28 2M15.7222 9.22222L12.8333 12.1111M20.7778 14.2778L17.8889 17.1667'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -90,7 +90,7 @@ export function ConditionalIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<path
|
||||
d='M23.1015 1.01616C22.1825 1.01442 21.2926 1.33795 20.5894 1.92946C19.8861 2.52098 19.4149 3.34229 19.2592 4.24794C19.1035 5.15358 19.2733 6.08512 19.7386 6.87755C20.2039 7.66998 20.9346 8.27217 21.8014 8.57745V12.7169H6.20035V8.57745C7.06779 8.27077 7.79888 7.6673 8.26441 6.87372C8.72994 6.08013 8.89994 5.14752 8.74435 4.24072C8.58877 3.33391 8.11762 2.5113 7.41419 1.91828C6.71075 1.32526 5.82032 1 4.90027 1C3.98021 1 3.08978 1.32526 2.38634 1.91828C1.68291 2.5113 1.21176 3.33391 1.05618 4.24072C0.900594 5.14752 1.07059 6.08013 1.53612 6.87372C2.00165 7.6673 2.73274 8.27077 3.60018 8.57745V12.7169C3.60018 13.4065 3.87413 14.0679 4.36175 14.5555C4.84938 15.0432 5.51074 15.3171 6.20035 15.3171H12.7008V20.7567C11.8333 21.0633 11.1023 21.6668 10.6367 22.4604C10.1712 23.254 10.0012 24.1866 10.1568 25.0934C10.3124 26.0002 10.7835 26.8228 11.4869 27.4158C12.1904 28.0089 13.0808 28.3341 14.0009 28.3341C14.9209 28.3341 15.8114 28.0089 16.5148 27.4158C17.2182 26.8228 17.6894 26.0002 17.845 25.0934C18.0005 24.1866 17.8305 23.254 17.365 22.4604C16.8995 21.6668 16.1684 21.0633 15.301 20.7567V15.3171H21.8014C22.491 15.3171 23.1524 15.0432 23.64 14.5555C24.1276 14.0679 24.4015 13.4065 24.4015 12.7169V8.57745C25.2683 8.27217 25.999 7.66998 26.4643 6.87755C26.9296 6.08512 27.0994 5.15358 26.9437 4.24794C26.788 3.34229 26.3168 2.52098 25.6135 1.92946C24.9103 1.33795 24.0204 1.01442 23.1015 1.01616ZM4.90027 6.2165C4.64313 6.2165 4.39177 6.14025 4.17798 5.99739C3.96418 5.85454 3.79754 5.65149 3.69914 5.41393C3.60074 5.17637 3.575 4.91497 3.62516 4.66278C3.67532 4.41059 3.79915 4.17893 3.98097 3.99711C4.16279 3.81529 4.39444 3.69147 4.64663 3.64131C4.89882 3.59114 5.16023 3.61689 5.39779 3.71529C5.63535 3.81369 5.83839 3.98033 5.98125 4.19412C6.1241 4.40792 6.20035 4.65928 6.20035 4.91641C6.20035 5.26122 6.06338 5.5919 5.81956 5.83571C5.57575 6.07953 5.24507 6.2165 4.90027 6.2165ZM14.0009 25.7178C13.7437 25.7178 13.4924 25.6415 13.2786 25.4987C13.0648 25.3558 12.8981 25.1528 12.7997 24.9152C12.7013 24.6777 12.6756 24.4163 12.7258 24.1641C12.7759 23.9119 12.8997 23.6802 13.0816 23.4984C13.2634 23.3166 13.495 23.1928 13.7472 23.1426C13.9994 23.0924 14.2608 23.1182 14.4984 23.2166C14.7359 23.315 14.939 23.4816 15.0818 23.6954C15.2247 23.9092 15.301 24.1606 15.301 24.4177C15.301 24.7625 15.164 25.0932 14.9202 25.337C14.6764 25.5808 14.3457 25.7178 14.0009 25.7178ZM23.1015 6.2165C22.8443 6.2165 22.593 6.14025 22.3792 5.99739C22.1654 5.85454 21.9987 5.65149 21.9003 5.41393C21.8019 5.17637 21.7762 4.91497 21.8264 4.66278C21.8765 4.41059 22.0003 4.17893 22.1822 3.99711C22.364 3.81529 22.5956 3.69147 22.8478 3.64131C23.1 3.59114 23.3614 3.61689 23.599 3.71529C23.8365 3.81369 24.0396 3.98033 24.1824 4.19412C24.3253 4.40792 24.4015 4.65928 24.4015 4.91641C24.4015 5.26122 24.2646 5.5919 24.0208 5.83571C23.777 6.07953 23.4463 6.2165 23.1015 6.2165Z'
|
||||
fill='currentColor'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='0.25'
|
||||
/>
|
||||
</svg>
|
||||
@@ -113,7 +113,7 @@ export function NoteIcon(props: SVGProps<SVGSVGElement>) {
|
||||
width='16'
|
||||
height='18'
|
||||
rx='2.5'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
fill='none'
|
||||
/>
|
||||
@@ -139,7 +139,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
cx='12'
|
||||
cy='6'
|
||||
r='3'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -151,7 +151,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
width='8'
|
||||
x='2'
|
||||
y='16'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -163,7 +163,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
width='8'
|
||||
x='14'
|
||||
y='16'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -171,7 +171,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<path
|
||||
className='a'
|
||||
d='M6,16V14a2,2,0,0,1,2-2h8a2,2,0,0,1,2,2v2'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -182,7 +182,7 @@ export function WorkflowIcon(props: SVGProps<SVGSVGElement>) {
|
||||
x2='12'
|
||||
y1='9'
|
||||
y2='12'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='1.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -203,7 +203,7 @@ export function SignalIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M23.964 33.2C26.252 28.416 27.5 23.216 27.5 17.6C27.5 11.984 26.252 6.576 23.964 2M16.476 29.664C18.14 25.92 19.18 21.76 19.18 17.6C19.18 13.44 18.14 9.072 16.476 5.328M8.988 26.128C10.236 23.424 10.86 20.512 10.86 17.6C10.86 14.688 10.236 11.568 8.988 9.072M1.5 22.384C2.124 20.928 2.54 19.264 2.54 17.6C2.54 15.936 2.124 14.064 1.5 12.608'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -224,7 +224,7 @@ export function CalendarIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M21.5 4.88889V2M8.5 4.88889V2M2.36111 9.22222H27.6389M2 12.1747C2 9.11967 2 7.59144 2.62978 6.42433C3.19924 5.38333 4.08288 4.54873 5.15467 4.03956C6.39111 3.44444 8.00889 3.44444 11.2444 3.44444H18.7556C21.9911 3.44444 23.6089 3.44444 24.8453 4.03956C25.933 4.56244 26.8156 5.39733 27.3702 6.42289C28 7.59289 28 9.12111 28 12.1761V19.2712C28 22.3262 28 23.8544 27.3702 25.0216C26.8008 26.0626 25.9171 26.8972 24.8453 27.4063C23.6089 28 21.9911 28 18.7556 28H11.2444C8.00889 28 6.39111 28 5.15467 27.4049C4.0831 26.8961 3.19948 26.062 2.62978 25.0216C2 23.8516 2 22.3233 2 19.2683V12.1747Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -245,7 +245,7 @@ export function MessagesIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M4.88867 14.999C6.27936 13.956 7.99961 13.4496 9.7336 13.5728C11.4676 13.6961 13.0989 14.4406 14.3281 15.6698C15.5574 16.899 16.3019 18.5304 16.4252 20.2644C16.5484 21.9984 16.042 23.7186 14.999 25.1093M4.88867 14.999C3.99176 15.6717 3.2638 16.5439 2.76241 17.5467C2.26103 18.5495 2 19.6552 2 20.7763C1.99954 21.5025 2.10861 22.2246 2.32353 22.9183C2.71639 24.1893 2.4333 25.6047 2.18054 26.9393C2.15813 27.0534 2.16551 27.1713 2.20198 27.2817C2.23845 27.392 2.30278 27.4912 2.38874 27.5694C2.4747 27.6477 2.57939 27.7025 2.6927 27.7285C2.80601 27.7544 2.92411 27.7508 3.03559 27.7178C4.26038 27.3827 5.47795 27.0967 6.72875 27.556C7.52721 27.849 8.37115 27.9986 9.22166 27.998C10.343 27.9991 11.4491 27.7386 12.4521 27.2371C13.455 26.7356 14.3271 26.0071 14.999 25.1093M4.88867 14.999C4.88867 8.23229 9.04112 2 16.4433 2C18.2963 1.99968 20.1222 2.44502 21.767 3.29845C23.4118 4.15189 24.8272 5.38838 25.8938 6.90363C26.9605 8.41888 27.647 10.1684 27.8956 12.0047C28.1441 13.841 27.9474 15.7101 27.322 17.4544C26.6345 19.3695 27.3755 21.9347 27.8088 24.0521C27.834 24.1648 27.8288 24.2822 27.7937 24.3923C27.7587 24.5024 27.6949 24.6011 27.6091 24.6785C27.5232 24.7558 27.4184 24.8089 27.3052 24.8323C27.1921 24.8557 27.0748 24.8486 26.9653 24.8118C25.0703 24.2196 22.846 23.3877 21.0652 24.1387C19.1384 24.9504 17.1135 25.1093 14.999 25.1093'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -266,7 +266,7 @@ export function NotificationsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M19.9056 24.7496H10.0908M19.9056 24.7496H25.7783C28.8267 24.7496 28.3116 21.7272 26.7695 20.1932C21.2153 14.6764 29.1046 2 14.9982 2C0.891783 2 8.78265 14.6748 3.22849 20.1932C1.74489 21.6687 1.11278 24.7496 4.21973 24.7496H10.0908M19.9056 24.7496C19.9056 27.8777 18.8526 31.2495 14.9982 31.2495C11.1437 31.2495 10.0908 27.8777 10.0908 24.7496'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -287,7 +287,7 @@ export function MailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M2.35742 5.83288L11.7674 12.1071C13.0656 12.9712 13.7141 13.404 14.4151 13.5725C15.0352 13.7208 15.681 13.7208 16.2998 13.5725C17.0008 13.404 17.6492 12.9712 18.9475 12.1071L28.3574 5.83288M8.82844 21.7219H21.8864C24.1513 21.7219 25.2837 21.7219 26.1492 21.2811C26.9097 20.8931 27.5278 20.2744 27.9152 19.5137C28.3574 18.6482 28.3574 17.5158 28.3574 15.2509V7.97102C28.3574 5.70616 28.3574 4.57373 27.9166 3.70823C27.5288 2.94727 26.9102 2.32858 26.1492 1.94084C25.2837 1.5 24.1513 1.5 21.8864 1.5H8.82844C6.56358 1.5 5.43115 1.5 4.56566 1.94084C3.80519 2.32881 3.187 2.94747 2.79961 3.70823C2.35742 4.57373 2.35742 5.70616 2.35742 7.97102V15.2509C2.35742 17.5158 2.35742 18.6482 2.79826 19.5137C3.186 20.2747 3.80469 20.8933 4.56566 21.2811C5.43115 21.7219 6.56358 21.7219 8.82844 21.7219Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -312,33 +312,25 @@ export function MailServerIcon(props: SVGProps<SVGSVGElement>) {
|
||||
width='18'
|
||||
height='16'
|
||||
rx='2'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M3 8L10.89 13.26C11.2187 13.4793 11.6049 13.5963 12 13.5963C12.3951 13.5963 12.7813 13.4793 13.11 13.26L21 8'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<line
|
||||
x1='7'
|
||||
y1='16'
|
||||
x2='7'
|
||||
y2='16'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
<line x1='7' y1='16' x2='7' y2='16' stroke='#000000' strokeWidth='2' strokeLinecap='round' />
|
||||
<line
|
||||
x1='10'
|
||||
y1='16'
|
||||
x2='10'
|
||||
y2='16'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
@@ -358,7 +350,7 @@ export function CodeIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M23.2639 6.83064L23.6375 7.20422C26.5433 10.1117 27.9971 11.5638 27.9971 13.37C27.9971 15.1762 26.5433 16.63 23.6375 19.5358L23.2639 19.9094M18.0434 2L11.9507 24.7401M6.72863 6.83064L6.35504 7.20422C3.45081 10.1117 1.99707 11.5638 1.99707 13.37C1.99707 15.1762 3.45081 16.63 6.35829 19.5358L6.73187 19.9094'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.6'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -845,7 +837,7 @@ export function xIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<path
|
||||
d='M 5.9199219 6 L 20.582031 27.375 L 6.2304688 44 L 9.4101562 44 L 21.986328 29.421875 L 31.986328 44 L 44 44 L 28.681641 21.669922 L 42.199219 6 L 39.029297 6 L 27.275391 19.617188 L 17.933594 6 L 5.9199219 6 z M 9.7167969 8 L 16.880859 8 L 40.203125 42 L 33.039062 42 L 9.7167969 8 z'
|
||||
fill='currentColor'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='0.5'
|
||||
/>
|
||||
</svg>
|
||||
@@ -1499,14 +1491,14 @@ export function DocumentIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M8 15.2H15.2M8 20H11.6M2 4.4V23.6C2 24.2365 2.25286 24.847 2.70294 25.2971C3.15303 25.7471 3.76348 26 4.4 26H18.8C19.4365 26 20.047 25.7471 20.4971 25.2971C20.9471 24.847 21.2 24.2365 21.2 23.6V9.6104C21.2 9.29067 21.136 8.97417 21.012 8.67949C20.8879 8.38481 20.7062 8.11789 20.4776 7.8944L15.1496 2.684C14.7012 2.24559 14.0991 2.00008 13.472 2H4.4C3.76348 2 3.15303 2.25286 2.70294 2.70294C2.25286 3.15303 2 3.76348 2 4.4Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.25'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M14 2V6.8C14 7.43652 14.2529 8.04697 14.7029 8.49706C15.153 8.94714 15.7635 9.2 16.4 9.2H21.2'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.25'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
@@ -4001,7 +3993,7 @@ export function SmtpIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M2.35742 5.83288L11.7674 12.1071C13.0656 12.9712 13.7141 13.404 14.4151 13.5725C15.0352 13.7208 15.681 13.7208 16.2998 13.5725C17.0008 13.404 17.6492 12.9712 18.9475 12.1071L28.3574 5.83288M8.82844 21.7219H21.8864C24.1513 21.7219 25.2837 21.7219 26.1492 21.2811C26.9097 20.8931 27.5278 20.2744 27.9152 19.5137C28.3574 18.6482 28.3574 17.5158 28.3574 15.2509V7.97102C28.3574 5.70616 28.3574 4.57373 27.9166 3.70823C27.5288 2.94727 26.9102 2.32858 26.1492 1.94084C25.2837 1.5 24.1513 1.5 21.8864 1.5H8.82844C6.56358 1.5 5.43115 1.5 4.56566 1.94084C3.80519 2.32881 3.187 2.94747 2.79961 3.70823C2.35742 4.57373 2.35742 5.70616 2.35742 7.97102V15.2509C2.35742 17.5158 2.35742 18.6482 2.79826 19.5137C3.186 20.2747 3.80469 20.8933 4.56566 21.2811C5.43115 21.7219 6.56358 21.7219 8.82844 21.7219Z'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2.5'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -4227,7 +4219,7 @@ export function A2AIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<circle cx='316' cy='307' r='27' fill='currentColor' />
|
||||
<path
|
||||
d='M336.5 191.003H162C97.6588 191.003 45.5 243.162 45.5 307.503C45.5 371.844 97.6442 424.003 161.985 424.003C206.551 424.003 256.288 424.003 296.5 424.003C487.5 424.003 374 191.005 569 191.001C613.886 191 658.966 191 698.025 191C762.366 191.001 814.5 243.16 814.5 307.501C814.5 371.843 762.34 424.003 697.998 424.003H523.5'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='48'
|
||||
strokeLinecap='round'
|
||||
/>
|
||||
@@ -4497,14 +4489,14 @@ export function RssIcon(props: SVGProps<SVGSVGElement>) {
|
||||
>
|
||||
<path
|
||||
d='M4 11C6.38695 11 8.67613 11.9482 10.364 13.636C12.0518 15.3239 13 17.6131 13 20'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M4 4C8.24346 4 12.3131 5.68571 15.3137 8.68629C18.3143 11.6869 20 15.7565 20 20'
|
||||
stroke='currentColor'
|
||||
stroke='#000000'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
@@ -5483,3 +5475,37 @@ export function AgentSkillsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function OnePasswordIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg' fill='none'>
|
||||
<circle
|
||||
cx='24'
|
||||
cy='24'
|
||||
r='21.5'
|
||||
stroke='#000000'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M28.083,17.28a7.8633,7.8633,0,0,1,0,13.44'
|
||||
stroke='#000000'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M19.917,30.72a7.8633,7.8633,0,0,1,0-13.44'
|
||||
stroke='#000000'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
<path
|
||||
d='M26.067,10.43H21.933a2.0172,2.0172,0,0,0-2.016,2.016v6.36c2.358,1.281,2.736,2.562,0,3.843V35.574a2.0169,2.0169,0,0,0,2.016,2.015h4.134a2.0169,2.0169,0,0,0,2.016-2.015V29.213c-2.358-1.281-2.736-2.562,0-3.842V12.446A2.0172,2.0172,0,0,0,26.067,10.43Z'
|
||||
fill='#000000'
|
||||
stroke='#000000'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JiraAddAttachmentParams, JiraAddAttachmentResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraAddAttachmentTool: ToolConfig<JiraAddAttachmentParams, JiraAddAttachmentResponse> =
|
||||
@@ -75,9 +76,21 @@ export const jiraAddAttachmentTool: ToolConfig<JiraAddAttachmentParams, JiraAddA
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
attachmentIds: { type: 'json', description: 'IDs of uploaded attachments' },
|
||||
files: { type: 'file[]', description: 'Uploaded attachment files' },
|
||||
attachments: {
|
||||
type: 'array',
|
||||
description: 'Uploaded attachments',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Attachment ID' },
|
||||
filename: { type: 'string', description: 'Attachment file name' },
|
||||
mimeType: { type: 'string', description: 'MIME type' },
|
||||
size: { type: 'number', description: 'File size in bytes' },
|
||||
content: { type: 'string', description: 'URL to download the attachment' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
import type { JiraAddCommentParams, JiraAddCommentResponse } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import { TIMESTAMP_OUTPUT, USER_OUTPUT_PROPERTIES } from '@/tools/jira/types'
|
||||
import { extractAdfText, getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Transforms an add comment API response into typed output.
|
||||
*/
|
||||
function transformCommentResponse(data: any, params: JiraAddCommentParams) {
|
||||
return {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params.issueKey ?? 'unknown',
|
||||
commentId: data?.id ?? 'unknown',
|
||||
body: data?.body ? (extractAdfText(data.body) ?? params.body ?? '') : (params.body ?? ''),
|
||||
author: transformUser(data?.author) ?? { accountId: '', displayName: '' },
|
||||
created: data?.created ?? '',
|
||||
updated: data?.updated ?? '',
|
||||
success: true,
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraAddCommentTool: ToolConfig<JiraAddCommentParams, JiraAddCommentResponse> = {
|
||||
id: 'jira_add_comment',
|
||||
name: 'Jira Add Comment',
|
||||
@@ -38,6 +55,13 @@ export const jiraAddCommentTool: ToolConfig<JiraAddCommentParams, JiraAddComment
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment body text',
|
||||
},
|
||||
visibility: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Restrict comment visibility. Object with "type" ("role" or "group") and "value" (role/group name).',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -64,55 +88,48 @@ export const jiraAddCommentTool: ToolConfig<JiraAddCommentParams, JiraAddComment
|
||||
},
|
||||
body: (params: JiraAddCommentParams) => {
|
||||
if (!params.cloudId) return undefined as any
|
||||
return {
|
||||
const payload: Record<string, any> = {
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params?.body || '',
|
||||
},
|
||||
],
|
||||
content: [{ type: 'text', text: params.body ?? '' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
if (params.visibility) payload.visibility = params.visibility
|
||||
return payload
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraAddCommentParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const commentUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/comment`
|
||||
const payload: Record<string, any> = {
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: params?.body ?? '' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
if (params?.visibility) payload.visibility = params.visibility
|
||||
|
||||
const makeRequest = async (cloudId: string) => {
|
||||
const commentUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/comment`
|
||||
const commentResponse = await fetch(commentUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params?.body || '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
if (!commentResponse.ok) {
|
||||
@@ -124,48 +141,46 @@ export const jiraAddCommentTool: ToolConfig<JiraAddCommentParams, JiraAddComment
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await commentResponse.json()
|
||||
return commentResponse.json()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
commentId: data?.id || 'unknown',
|
||||
body: params?.body || '',
|
||||
success: true,
|
||||
},
|
||||
let data: any
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
data = await makeRequest(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to add comment to Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to add comment to Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
commentId: data?.id || 'unknown',
|
||||
body: params?.body || '',
|
||||
success: true,
|
||||
},
|
||||
output: transformCommentResponse(data, params!),
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key the comment was added to' },
|
||||
commentId: { type: 'string', description: 'Created comment ID' },
|
||||
body: { type: 'string', description: 'Comment text content' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Comment author',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
created: { type: 'string', description: 'ISO 8601 timestamp when the comment was created' },
|
||||
updated: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the comment was last updated',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
import type { JiraAddWatcherParams, JiraAddWatcherResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraAddWatcherParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
issueKey: string
|
||||
accountId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraAddWatcherResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueKey: string
|
||||
watcherAccountId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraAddWatcherTool: ToolConfig<JiraAddWatcherParams, JiraAddWatcherResponse> = {
|
||||
id: 'jira_add_watcher',
|
||||
@@ -87,16 +72,15 @@ export const jiraAddWatcherTool: ToolConfig<JiraAddWatcherParams, JiraAddWatcher
|
||||
transformResponse: async (response: Response, params?: JiraAddWatcherParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const watcherUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/watchers`
|
||||
const watcherUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/watchers`
|
||||
const watcherResponse = await fetch(watcherUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
body: JSON.stringify(params?.accountId),
|
||||
body: JSON.stringify(params!.accountId),
|
||||
})
|
||||
|
||||
if (!watcherResponse.ok) {
|
||||
@@ -112,14 +96,13 @@ export const jiraAddWatcherTool: ToolConfig<JiraAddWatcherParams, JiraAddWatcher
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
watcherAccountId: params?.accountId || 'unknown',
|
||||
issueKey: params!.issueKey || 'unknown',
|
||||
watcherAccountId: params!.accountId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to add watcher to Jira issue (${response.status})`
|
||||
try {
|
||||
@@ -133,15 +116,15 @@ export const jiraAddWatcherTool: ToolConfig<JiraAddWatcherParams, JiraAddWatcher
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
watcherAccountId: params?.accountId || 'unknown',
|
||||
issueKey: params!.issueKey || 'unknown',
|
||||
watcherAccountId: params!.accountId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
watcherAccountId: { type: 'string', description: 'Added watcher account ID' },
|
||||
},
|
||||
|
||||
@@ -1,7 +1,51 @@
|
||||
import type { JiraAddWorklogParams, JiraAddWorklogResponse } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import { TIMESTAMP_OUTPUT, USER_OUTPUT_PROPERTIES } from '@/tools/jira/types'
|
||||
import { getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Builds the worklog request body per Jira API v3.
|
||||
*/
|
||||
function buildWorklogBody(params: JiraAddWorklogParams) {
|
||||
const body: Record<string, any> = {
|
||||
timeSpentSeconds: Number(params.timeSpentSeconds),
|
||||
comment: params.comment
|
||||
? {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: params.comment }],
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
started:
|
||||
(params.started ? params.started.replace(/Z$/, '+0000') : undefined) ||
|
||||
new Date().toISOString().replace(/Z$/, '+0000'),
|
||||
}
|
||||
if (params.visibility) body.visibility = params.visibility
|
||||
return body
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a worklog API response into typed output.
|
||||
*/
|
||||
function transformWorklogResponse(data: any, params: JiraAddWorklogParams) {
|
||||
return {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params.issueKey ?? 'unknown',
|
||||
worklogId: data?.id ?? 'unknown',
|
||||
timeSpent: data?.timeSpent ?? '',
|
||||
timeSpentSeconds: data?.timeSpentSeconds ?? Number(params.timeSpentSeconds) ?? 0,
|
||||
author: transformUser(data?.author) ?? { accountId: '', displayName: '' },
|
||||
started: data?.started ?? '',
|
||||
created: data?.created ?? '',
|
||||
success: true,
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraAddWorklogTool: ToolConfig<JiraAddWorklogParams, JiraAddWorklogResponse> = {
|
||||
id: 'jira_add_worklog',
|
||||
name: 'Jira Add Worklog',
|
||||
@@ -50,6 +94,13 @@ export const jiraAddWorklogTool: ToolConfig<JiraAddWorklogParams, JiraAddWorklog
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional start time in ISO format (defaults to current time)',
|
||||
},
|
||||
visibility: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Restrict worklog visibility. Object with "type" ("role" or "group") and "value" (role/group name).',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -76,71 +127,25 @@ export const jiraAddWorklogTool: ToolConfig<JiraAddWorklogParams, JiraAddWorklog
|
||||
},
|
||||
body: (params: JiraAddWorklogParams) => {
|
||||
if (!params.cloudId) return undefined as any
|
||||
return {
|
||||
timeSpentSeconds: Number(params.timeSpentSeconds),
|
||||
comment: params.comment
|
||||
? {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params.comment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
started:
|
||||
(params.started ? params.started.replace(/Z$/, '+0000') : undefined) ||
|
||||
new Date().toISOString().replace(/Z$/, '+0000'),
|
||||
}
|
||||
return buildWorklogBody(params)
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraAddWorklogParams) => {
|
||||
if (!params?.cloudId) {
|
||||
if (!params?.timeSpentSeconds || params.timeSpentSeconds <= 0) {
|
||||
throw new Error('timeSpentSeconds is required and must be greater than 0')
|
||||
}
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const worklogUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/worklog`
|
||||
if (!params?.timeSpentSeconds || params.timeSpentSeconds <= 0) {
|
||||
throw new Error('timeSpentSeconds is required and must be greater than 0')
|
||||
}
|
||||
|
||||
const makeRequest = async (cloudId: string) => {
|
||||
const worklogUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/worklog`
|
||||
const worklogResponse = await fetch(worklogUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
timeSpentSeconds: params?.timeSpentSeconds ? Number(params.timeSpentSeconds) : 0,
|
||||
comment: params?.comment
|
||||
? {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params.comment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
// Preserve milliseconds and convert trailing Z to +0000 as required by Jira examples
|
||||
started:
|
||||
(params?.started ? params.started.replace(/Z$/, '+0000') : undefined) ||
|
||||
new Date().toISOString().replace(/Z$/, '+0000'),
|
||||
}),
|
||||
body: JSON.stringify(buildWorklogBody(params!)),
|
||||
})
|
||||
|
||||
if (!worklogResponse.ok) {
|
||||
@@ -152,48 +157,47 @@ export const jiraAddWorklogTool: ToolConfig<JiraAddWorklogParams, JiraAddWorklog
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await worklogResponse.json()
|
||||
return worklogResponse.json()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
worklogId: data?.id || 'unknown',
|
||||
timeSpentSeconds: params?.timeSpentSeconds ? Number(params.timeSpentSeconds) : 0 || 0,
|
||||
success: true,
|
||||
},
|
||||
let data: any
|
||||
|
||||
if (!params.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params.domain, params.accessToken)
|
||||
data = await makeRequest(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to add worklog to Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to add worklog to Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
worklogId: data?.id || 'unknown',
|
||||
timeSpentSeconds: params?.timeSpentSeconds ? Number(params.timeSpentSeconds) : 0 || 0,
|
||||
success: true,
|
||||
},
|
||||
output: transformWorklogResponse(data, params),
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key the worklog was added to' },
|
||||
worklogId: { type: 'string', description: 'Created worklog ID' },
|
||||
timeSpent: {
|
||||
type: 'string',
|
||||
description: 'Time spent in human-readable format (e.g., 3h 20m)',
|
||||
},
|
||||
timeSpentSeconds: { type: 'number', description: 'Time spent in seconds' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Worklog author',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
started: { type: 'string', description: 'ISO 8601 timestamp when the work started' },
|
||||
created: { type: 'string', description: 'ISO 8601 timestamp when the worklog was created' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
import type { JiraAssignIssueParams, JiraAssignIssueResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraAssignIssueParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
issueKey: string
|
||||
accountId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraAssignIssueResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueKey: string
|
||||
assigneeId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraAssignIssueTool: ToolConfig<JiraAssignIssueParams, JiraAssignIssueResponse> = {
|
||||
id: 'jira_assign_issue',
|
||||
@@ -144,8 +129,11 @@ export const jiraAssignIssueTool: ToolConfig<JiraAssignIssueParams, JiraAssignIs
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key that was assigned' },
|
||||
assigneeId: { type: 'string', description: 'Account ID of the assignee' },
|
||||
assigneeId: {
|
||||
type: 'string',
|
||||
description: 'Account ID of the assignee (use "-1" for auto-assign, null to unassign)',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import type { JiraRetrieveBulkParams, JiraRetrieveResponseBulk } from '@/tools/jira/types'
|
||||
import type {
|
||||
JiraRetrieveBulkParams,
|
||||
JiraRetrieveBulkV2Params,
|
||||
JiraRetrieveResponseBulk,
|
||||
JiraRetrieveResponseBulkV2,
|
||||
} from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { extractAdfText } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraBulkRetrieveTool: ToolConfig<JiraRetrieveBulkParams, JiraRetrieveResponseBulk> = {
|
||||
id: 'jira_bulk_read',
|
||||
name: 'Jira Bulk Read',
|
||||
description: 'Retrieve multiple Jira issues in bulk',
|
||||
description: 'Retrieve multiple Jira issues from a project in bulk',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
@@ -41,44 +48,18 @@ export const jiraBulkRetrieveTool: ToolConfig<JiraRetrieveBulkParams, JiraRetrie
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: JiraRetrieveBulkParams) => {
|
||||
// Always return accessible resources endpoint; transformResponse will build search URLs
|
||||
return 'https://api.atlassian.com/oauth/token/accessible-resources'
|
||||
},
|
||||
url: () => 'https://api.atlassian.com/oauth/token/accessible-resources',
|
||||
method: 'GET',
|
||||
headers: (params: JiraRetrieveBulkParams) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params: JiraRetrieveBulkParams) =>
|
||||
params.cloudId
|
||||
? {
|
||||
jql: '', // Will be set in transformResponse when we know the resolved project key
|
||||
startAt: 0,
|
||||
maxResults: 100,
|
||||
fields: ['summary', 'description', 'created', 'updated'],
|
||||
}
|
||||
: {},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraRetrieveBulkParams) => {
|
||||
const MAX_TOTAL = 1000
|
||||
const PAGE_SIZE = 100
|
||||
|
||||
// Helper to extract description text safely (ADF can be nested)
|
||||
const extractDescription = (desc: any): string => {
|
||||
try {
|
||||
return (
|
||||
desc?.content?.[0]?.content?.[0]?.text ||
|
||||
desc?.content?.flatMap((c: any) => c?.content || [])?.find((c: any) => c?.text)?.text ||
|
||||
''
|
||||
)
|
||||
} catch (_e) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to resolve a project reference (id or key) to its canonical key
|
||||
const resolveProjectKey = async (cloudId: string, accessToken: string, ref: string) => {
|
||||
const refTrimmed = (ref || '').trim()
|
||||
if (!refTrimmed) return refTrimmed
|
||||
@@ -87,128 +68,387 @@ export const jiraBulkRetrieveTool: ToolConfig<JiraRetrieveBulkParams, JiraRetrie
|
||||
method: 'GET',
|
||||
headers: { Authorization: `Bearer ${accessToken}`, Accept: 'application/json' },
|
||||
})
|
||||
if (!resp.ok) {
|
||||
// If can't resolve, fall back to original ref (JQL can still work with id or key)
|
||||
return refTrimmed
|
||||
}
|
||||
if (!resp.ok) return refTrimmed
|
||||
const project = await resp.json()
|
||||
return project?.key || refTrimmed
|
||||
}
|
||||
|
||||
// If we don't have a cloudId, look it up first
|
||||
if (!params?.cloudId) {
|
||||
const resolveCloudId = async () => {
|
||||
if (params?.cloudId) return params.cloudId
|
||||
const accessibleResources = await response.json()
|
||||
const normalizedInput = `https://${params?.domain}`.toLowerCase()
|
||||
const matchedResource = accessibleResources.find(
|
||||
(r: any) => r.url.toLowerCase() === normalizedInput
|
||||
)
|
||||
|
||||
const projectKey = await resolveProjectKey(
|
||||
matchedResource.id,
|
||||
params!.accessToken,
|
||||
params!.projectId
|
||||
)
|
||||
const jql = `project = ${projectKey} ORDER BY updated DESC`
|
||||
|
||||
let startAt = 0
|
||||
let collected: any[] = []
|
||||
let total = 0
|
||||
|
||||
while (startAt < MAX_TOTAL) {
|
||||
const queryParams = new URLSearchParams({
|
||||
jql,
|
||||
fields: 'summary,description,created,updated',
|
||||
maxResults: String(PAGE_SIZE),
|
||||
})
|
||||
if (startAt > 0) {
|
||||
queryParams.set('startAt', String(startAt))
|
||||
}
|
||||
const url = `https://api.atlassian.com/ex/jira/${matchedResource.id}/rest/api/3/search/jql?${queryParams.toString()}`
|
||||
const pageResponse = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
const pageData = await pageResponse.json()
|
||||
const issues = pageData.issues || []
|
||||
total = pageData.total || issues.length
|
||||
collected = collected.concat(issues)
|
||||
|
||||
if (collected.length >= Math.min(total, MAX_TOTAL) || issues.length === 0) break
|
||||
startAt += PAGE_SIZE
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: collected.slice(0, MAX_TOTAL).map((issue: any) => ({
|
||||
ts: new Date().toISOString(),
|
||||
summary: issue.fields?.summary,
|
||||
description: extractDescription(issue.fields?.description),
|
||||
created: issue.fields?.created,
|
||||
updated: issue.fields?.updated,
|
||||
})),
|
||||
}
|
||||
if (matchedResource) return matchedResource.id
|
||||
if (Array.isArray(accessibleResources) && accessibleResources.length > 0)
|
||||
return accessibleResources[0].id
|
||||
throw new Error('No Jira resources found')
|
||||
}
|
||||
|
||||
// cloudId present: resolve project and paginate using the Search API
|
||||
// Resolve to canonical project key for consistent JQL
|
||||
const projectKey = await resolveProjectKey(
|
||||
params!.cloudId!,
|
||||
params!.accessToken,
|
||||
params!.projectId
|
||||
)
|
||||
|
||||
const cloudId = await resolveCloudId()
|
||||
const projectKey = await resolveProjectKey(cloudId, params!.accessToken, params!.projectId)
|
||||
const jql = `project = ${projectKey} ORDER BY updated DESC`
|
||||
|
||||
// Always do full pagination with resolved key
|
||||
let startAt = 0
|
||||
let collected: any[] = []
|
||||
let total = 0
|
||||
let startAt = 0
|
||||
|
||||
while (startAt < MAX_TOTAL) {
|
||||
const queryParams = new URLSearchParams({
|
||||
jql,
|
||||
fields: 'summary,description,created,updated',
|
||||
fields: 'summary,description,status,issuetype,priority,assignee,created,updated',
|
||||
maxResults: String(PAGE_SIZE),
|
||||
})
|
||||
if (startAt > 0) {
|
||||
queryParams.set('startAt', String(startAt))
|
||||
}
|
||||
const url = `https://api.atlassian.com/ex/jira/${params?.cloudId}/rest/api/3/search/jql?${queryParams.toString()}`
|
||||
if (startAt > 0) queryParams.set('startAt', String(startAt))
|
||||
|
||||
const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/search/jql?${queryParams.toString()}`
|
||||
const pageResponse = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
const pageData = await pageResponse.json()
|
||||
const issues = pageData.issues || []
|
||||
total = pageData.total || issues.length
|
||||
collected = collected.concat(issues)
|
||||
if (issues.length === 0 || collected.length >= Math.min(total, MAX_TOTAL)) break
|
||||
|
||||
if (collected.length >= Math.min(total, MAX_TOTAL) || issues.length === 0) break
|
||||
startAt += PAGE_SIZE
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: collected.slice(0, MAX_TOTAL).map((issue: any) => ({
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
summary: issue.fields?.summary,
|
||||
description: extractDescription(issue.fields?.description),
|
||||
created: issue.fields?.created,
|
||||
updated: issue.fields?.updated,
|
||||
})),
|
||||
total,
|
||||
issues: collected.slice(0, MAX_TOTAL).map((issue: any) => ({
|
||||
id: issue.id ?? '',
|
||||
key: issue.key ?? '',
|
||||
self: issue.self ?? '',
|
||||
summary: issue.fields?.summary ?? '',
|
||||
description: extractAdfText(issue.fields?.description),
|
||||
status: {
|
||||
id: issue.fields?.status?.id ?? '',
|
||||
name: issue.fields?.status?.name ?? '',
|
||||
},
|
||||
issuetype: {
|
||||
id: issue.fields?.issuetype?.id ?? '',
|
||||
name: issue.fields?.issuetype?.name ?? '',
|
||||
},
|
||||
priority: issue.fields?.priority
|
||||
? { id: issue.fields.priority.id ?? '', name: issue.fields.priority.name ?? '' }
|
||||
: null,
|
||||
assignee: issue.fields?.assignee
|
||||
? {
|
||||
accountId: issue.fields.assignee.accountId ?? '',
|
||||
displayName: issue.fields.assignee.displayName ?? '',
|
||||
}
|
||||
: null,
|
||||
created: issue.fields?.created ?? '',
|
||||
updated: issue.fields?.updated ?? '',
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
total: { type: 'number', description: 'Total number of issues in the project' },
|
||||
issues: {
|
||||
type: 'array',
|
||||
description:
|
||||
'Array of Jira issues with ts, summary, description, created, and updated timestamps',
|
||||
description: 'Array of Jira issues',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Issue ID' },
|
||||
key: { type: 'string', description: 'Issue key (e.g., PROJ-123)' },
|
||||
self: { type: 'string', description: 'REST API URL for this issue' },
|
||||
summary: { type: 'string', description: 'Issue summary' },
|
||||
description: { type: 'string', description: 'Issue description text', optional: true },
|
||||
status: {
|
||||
type: 'object',
|
||||
description: 'Issue status',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Status ID' },
|
||||
name: { type: 'string', description: 'Status name' },
|
||||
},
|
||||
},
|
||||
issuetype: {
|
||||
type: 'object',
|
||||
description: 'Issue type',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Issue type ID' },
|
||||
name: { type: 'string', description: 'Issue type name' },
|
||||
},
|
||||
},
|
||||
priority: {
|
||||
type: 'object',
|
||||
description: 'Issue priority',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Priority ID' },
|
||||
name: { type: 'string', description: 'Priority name' },
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
assignee: {
|
||||
type: 'object',
|
||||
description: 'Assigned user',
|
||||
properties: {
|
||||
accountId: { type: 'string', description: 'Atlassian account ID' },
|
||||
displayName: { type: 'string', description: 'Display name' },
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
created: { type: 'string', description: 'ISO 8601 creation timestamp' },
|
||||
updated: { type: 'string', description: 'ISO 8601 last updated timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* V2 Bulk Read Tool - Uses cursor-based pagination (nextPageToken) on /rest/api/3/search/jql.
|
||||
* The startAt parameter was deprecated on this endpoint as of Sept 2025.
|
||||
*/
|
||||
export const jiraBulkRetrieveV2Tool: ToolConfig<
|
||||
JiraRetrieveBulkV2Params,
|
||||
JiraRetrieveResponseBulkV2
|
||||
> = {
|
||||
id: 'jira_bulk_read_v2',
|
||||
name: 'Jira Bulk Read V2',
|
||||
description:
|
||||
'Retrieve multiple Jira issues from a project in bulk with cursor-based pagination (V2 - uses nextPageToken)',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'jira',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Jira',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Your Jira domain (e.g., yourcompany.atlassian.net)',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Jira project key (e.g., PROJ)',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description:
|
||||
'Jira Cloud ID for the instance. If not provided, it will be fetched using the domain.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => 'https://api.atlassian.com/oauth/token/accessible-resources',
|
||||
method: 'GET',
|
||||
headers: (params: JiraRetrieveBulkV2Params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraRetrieveBulkV2Params) => {
|
||||
const MAX_TOTAL = 1000
|
||||
const PAGE_SIZE = 100
|
||||
|
||||
const resolveProjectKey = async (cloudId: string, accessToken: string, ref: string) => {
|
||||
const refTrimmed = (ref || '').trim()
|
||||
if (!refTrimmed) return refTrimmed
|
||||
const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/project/${encodeURIComponent(refTrimmed)}`
|
||||
const resp = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: { Authorization: `Bearer ${accessToken}`, Accept: 'application/json' },
|
||||
})
|
||||
if (!resp.ok) return refTrimmed
|
||||
const project = await resp.json()
|
||||
return project?.key || refTrimmed
|
||||
}
|
||||
|
||||
const resolveCloudId = async () => {
|
||||
if (params?.cloudId) return params.cloudId
|
||||
const accessibleResources = await response.json()
|
||||
const normalizedInput = `https://${params?.domain}`.toLowerCase()
|
||||
const matchedResource = accessibleResources.find(
|
||||
(r: { url: string }) => r.url.toLowerCase() === normalizedInput
|
||||
)
|
||||
if (matchedResource) return matchedResource.id
|
||||
if (Array.isArray(accessibleResources) && accessibleResources.length > 0)
|
||||
return accessibleResources[0].id
|
||||
throw new Error('No Jira resources found')
|
||||
}
|
||||
|
||||
const cloudId = await resolveCloudId()
|
||||
const projectKey = await resolveProjectKey(cloudId, params!.accessToken, params!.projectId)
|
||||
const jql = `project = ${projectKey} ORDER BY updated DESC`
|
||||
|
||||
let collected: Array<Record<string, unknown>> = []
|
||||
let nextPageToken: string | undefined
|
||||
let total: number | null = null
|
||||
|
||||
while (collected.length < MAX_TOTAL) {
|
||||
const queryParams = new URLSearchParams({
|
||||
jql,
|
||||
fields: 'summary,description,status,issuetype,priority,assignee,created,updated',
|
||||
maxResults: String(PAGE_SIZE),
|
||||
})
|
||||
if (nextPageToken) queryParams.set('nextPageToken', nextPageToken)
|
||||
|
||||
const url = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/search/jql?${queryParams.toString()}`
|
||||
const pageResponse = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!pageResponse.ok) {
|
||||
let message = `Failed to bulk read Jira issues (${pageResponse.status})`
|
||||
try {
|
||||
const err = await pageResponse.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const pageData = await pageResponse.json()
|
||||
const issues = pageData.issues || []
|
||||
if (pageData.total != null) total = pageData.total
|
||||
collected = collected.concat(issues)
|
||||
|
||||
if (pageData.isLast || !pageData.nextPageToken || issues.length === 0) break
|
||||
nextPageToken = pageData.nextPageToken
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issues: collected.slice(0, MAX_TOTAL).map((issue: Record<string, unknown>) => {
|
||||
const fields = (issue.fields as Record<string, unknown>) ?? {}
|
||||
const status = fields.status as Record<string, unknown> | undefined
|
||||
const issuetype = fields.issuetype as Record<string, unknown> | undefined
|
||||
const priority = fields.priority as Record<string, unknown> | undefined
|
||||
const assignee = fields.assignee as Record<string, unknown> | undefined
|
||||
return {
|
||||
id: (issue.id as string) ?? '',
|
||||
key: (issue.key as string) ?? '',
|
||||
self: (issue.self as string) ?? '',
|
||||
summary: (fields.summary as string) ?? '',
|
||||
description: extractAdfText(fields.description),
|
||||
status: {
|
||||
id: (status?.id as string) ?? '',
|
||||
name: (status?.name as string) ?? '',
|
||||
},
|
||||
issuetype: {
|
||||
id: (issuetype?.id as string) ?? '',
|
||||
name: (issuetype?.name as string) ?? '',
|
||||
},
|
||||
priority: priority
|
||||
? {
|
||||
id: (priority.id as string) ?? '',
|
||||
name: (priority.name as string) ?? '',
|
||||
}
|
||||
: null,
|
||||
assignee: assignee
|
||||
? {
|
||||
accountId: (assignee.accountId as string) ?? '',
|
||||
displayName: (assignee.displayName as string) ?? '',
|
||||
}
|
||||
: null,
|
||||
created: (fields.created as string) ?? '',
|
||||
updated: (fields.updated as string) ?? '',
|
||||
}
|
||||
}),
|
||||
nextPageToken: nextPageToken ?? null,
|
||||
isLast: !nextPageToken || collected.length >= MAX_TOTAL,
|
||||
total,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issues: {
|
||||
type: 'array',
|
||||
description: 'Array of Jira issues',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Issue ID' },
|
||||
key: { type: 'string', description: 'Issue key (e.g., PROJ-123)' },
|
||||
self: { type: 'string', description: 'REST API URL for this issue' },
|
||||
summary: { type: 'string', description: 'Issue summary' },
|
||||
description: { type: 'string', description: 'Issue description text', optional: true },
|
||||
status: {
|
||||
type: 'object',
|
||||
description: 'Issue status',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Status ID' },
|
||||
name: { type: 'string', description: 'Status name' },
|
||||
},
|
||||
},
|
||||
issuetype: {
|
||||
type: 'object',
|
||||
description: 'Issue type',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Issue type ID' },
|
||||
name: { type: 'string', description: 'Issue type name' },
|
||||
},
|
||||
},
|
||||
priority: {
|
||||
type: 'object',
|
||||
description: 'Issue priority',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Priority ID' },
|
||||
name: { type: 'string', description: 'Priority name' },
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
assignee: {
|
||||
type: 'object',
|
||||
description: 'Assigned user',
|
||||
properties: {
|
||||
accountId: { type: 'string', description: 'Atlassian account ID' },
|
||||
displayName: { type: 'string', description: 'Display name' },
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
created: { type: 'string', description: 'ISO 8601 creation timestamp' },
|
||||
updated: { type: 'string', description: 'ISO 8601 last updated timestamp' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Cursor token for the next page. Null when no more results.',
|
||||
optional: true,
|
||||
},
|
||||
isLast: { type: 'boolean', description: 'Whether this is the last page of results' },
|
||||
total: {
|
||||
type: 'number',
|
||||
description: 'Total number of issues in the project (may not always be available)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,26 +1,7 @@
|
||||
import type { JiraCreateIssueLinkParams, JiraCreateIssueLinkResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraCreateIssueLinkParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
inwardIssueKey: string
|
||||
outwardIssueKey: string
|
||||
linkType: string
|
||||
comment?: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraCreateIssueLinkResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
inwardIssue: string
|
||||
outwardIssue: string
|
||||
linkType: string
|
||||
linkId?: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraCreateIssueLinkTool: ToolConfig<
|
||||
JiraCreateIssueLinkParams,
|
||||
@@ -84,7 +65,6 @@ export const jiraCreateIssueLinkTool: ToolConfig<
|
||||
|
||||
request: {
|
||||
url: (_params: JiraCreateIssueLinkParams) => {
|
||||
// Always discover first; actual POST happens in transformResponse
|
||||
return 'https://api.atlassian.com/oauth/token/accessible-resources'
|
||||
},
|
||||
method: () => 'GET',
|
||||
@@ -99,10 +79,8 @@ export const jiraCreateIssueLinkTool: ToolConfig<
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraCreateIssueLinkParams) => {
|
||||
// Resolve cloudId
|
||||
const cloudId = params?.cloudId || (await getJiraCloudId(params!.domain, params!.accessToken))
|
||||
|
||||
// Fetch and resolve link type by id/name/inward/outward (case-insensitive)
|
||||
const typesResp = await fetch(
|
||||
`https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issueLinkType`,
|
||||
{
|
||||
@@ -136,7 +114,6 @@ export const jiraCreateIssueLinkTool: ToolConfig<
|
||||
throw new Error(`Unknown issue link type "${params!.linkType}". Available: ${available}`)
|
||||
}
|
||||
|
||||
// Create issue link
|
||||
const linkUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issueLink`
|
||||
const linkResponse = await fetch(linkUrl, {
|
||||
method: 'POST',
|
||||
@@ -179,21 +156,26 @@ export const jiraCreateIssueLinkTool: ToolConfig<
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
// Try to extract the newly created link ID from the Location header
|
||||
const location = linkResponse.headers.get('location') || linkResponse.headers.get('Location')
|
||||
let linkId: string | undefined
|
||||
if (location) {
|
||||
const match = location.match(/\/issueLink\/(\d+)/)
|
||||
if (match) linkId = match[1]
|
||||
let linkId: string | null = null
|
||||
|
||||
try {
|
||||
const linkData = await linkResponse.json()
|
||||
if (linkData?.id) linkId = String(linkData.id)
|
||||
} catch {
|
||||
const location = linkResponse.headers.get('location') || linkResponse.headers.get('Location')
|
||||
if (location) {
|
||||
const match = location.match(/\/issueLink\/(\d+)/)
|
||||
if (match) linkId = match[1]
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
inwardIssue: params?.inwardIssueKey || 'unknown',
|
||||
outwardIssue: params?.outwardIssueKey || 'unknown',
|
||||
linkType: params?.linkType || 'unknown',
|
||||
inwardIssue: params!.inwardIssueKey || 'unknown',
|
||||
outwardIssue: params!.outwardIssueKey || 'unknown',
|
||||
linkType: params!.linkType || 'unknown',
|
||||
linkId,
|
||||
success: true,
|
||||
},
|
||||
@@ -201,7 +183,7 @@ export const jiraCreateIssueLinkTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
inwardIssue: { type: 'string', description: 'Inward issue key' },
|
||||
outwardIssue: { type: 'string', description: 'Outward issue key' },
|
||||
linkType: { type: 'string', description: 'Type of issue link' },
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
import type { JiraDeleteAttachmentParams, JiraDeleteAttachmentResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraDeleteAttachmentParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
attachmentId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraDeleteAttachmentResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
attachmentId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraDeleteAttachmentTool: ToolConfig<
|
||||
JiraDeleteAttachmentParams,
|
||||
@@ -127,7 +114,7 @@ export const jiraDeleteAttachmentTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
attachmentId: { type: 'string', description: 'Deleted attachment ID' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
import type { JiraDeleteCommentParams, JiraDeleteCommentResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraDeleteCommentParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
issueKey: string
|
||||
commentId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraDeleteCommentResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueKey: string
|
||||
commentId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraDeleteCommentTool: ToolConfig<JiraDeleteCommentParams, JiraDeleteCommentResponse> =
|
||||
{
|
||||
@@ -135,7 +120,7 @@ export const jiraDeleteCommentTool: ToolConfig<JiraDeleteCommentParams, JiraDele
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
commentId: { type: 'string', description: 'Deleted comment ID' },
|
||||
},
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
import type { JiraDeleteIssueParams, JiraDeleteIssueResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraDeleteIssueParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
issueKey: string
|
||||
cloudId?: string
|
||||
deleteSubtasks?: boolean
|
||||
}
|
||||
|
||||
export interface JiraDeleteIssueResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueKey: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraDeleteIssueTool: ToolConfig<JiraDeleteIssueParams, JiraDeleteIssueResponse> = {
|
||||
id: 'jira_delete_issue',
|
||||
@@ -170,7 +156,7 @@ export const jiraDeleteIssueTool: ToolConfig<JiraDeleteIssueParams, JiraDeleteIs
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Deleted issue key' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
import type { JiraDeleteIssueLinkParams, JiraDeleteIssueLinkResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraDeleteIssueLinkParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
linkId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraDeleteIssueLinkResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
linkId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraDeleteIssueLinkTool: ToolConfig<
|
||||
JiraDeleteIssueLinkParams,
|
||||
@@ -77,13 +64,12 @@ export const jiraDeleteIssueLinkTool: ToolConfig<
|
||||
transformResponse: async (response: Response, params?: JiraDeleteIssueLinkParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const issueLinkUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issueLink/${params?.linkId}`
|
||||
const issueLinkUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issueLink/${params!.linkId}`
|
||||
const issueLinkResponse = await fetch(issueLinkUrl, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -100,13 +86,12 @@ export const jiraDeleteIssueLinkTool: ToolConfig<
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
linkId: params?.linkId || 'unknown',
|
||||
linkId: params!.linkId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to delete issue link (${response.status})`
|
||||
try {
|
||||
@@ -120,14 +105,14 @@ export const jiraDeleteIssueLinkTool: ToolConfig<
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
linkId: params?.linkId || 'unknown',
|
||||
linkId: params!.linkId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
linkId: { type: 'string', description: 'Deleted link ID' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
import type { JiraDeleteWorklogParams, JiraDeleteWorklogResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraDeleteWorklogParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
issueKey: string
|
||||
worklogId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraDeleteWorklogResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueKey: string
|
||||
worklogId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraDeleteWorklogTool: ToolConfig<JiraDeleteWorklogParams, JiraDeleteWorklogResponse> =
|
||||
{
|
||||
@@ -83,13 +68,12 @@ export const jiraDeleteWorklogTool: ToolConfig<JiraDeleteWorklogParams, JiraDele
|
||||
transformResponse: async (response: Response, params?: JiraDeleteWorklogParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const worklogUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/worklog/${params?.worklogId}`
|
||||
const worklogUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/worklog/${params!.worklogId}`
|
||||
const worklogResponse = await fetch(worklogUrl, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -106,14 +90,13 @@ export const jiraDeleteWorklogTool: ToolConfig<JiraDeleteWorklogParams, JiraDele
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
worklogId: params?.worklogId || 'unknown',
|
||||
issueKey: params!.issueKey || 'unknown',
|
||||
worklogId: params!.worklogId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to delete worklog from Jira issue (${response.status})`
|
||||
try {
|
||||
@@ -127,15 +110,15 @@ export const jiraDeleteWorklogTool: ToolConfig<JiraDeleteWorklogParams, JiraDele
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
worklogId: params?.worklogId || 'unknown',
|
||||
issueKey: params!.issueKey || 'unknown',
|
||||
worklogId: params!.worklogId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
worklogId: { type: 'string', description: 'Deleted worklog ID' },
|
||||
},
|
||||
|
||||
@@ -1,7 +1,25 @@
|
||||
import type { JiraGetAttachmentsParams, JiraGetAttachmentsResponse } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import { ATTACHMENT_ITEM_PROPERTIES, TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Transforms a raw Jira attachment object into typed output.
|
||||
*/
|
||||
function transformAttachment(att: any) {
|
||||
return {
|
||||
id: att.id ?? '',
|
||||
filename: att.filename ?? '',
|
||||
mimeType: att.mimeType ?? '',
|
||||
size: att.size ?? 0,
|
||||
content: att.content ?? '',
|
||||
thumbnail: att.thumbnail ?? null,
|
||||
author: transformUser(att.author),
|
||||
created:
|
||||
typeof att.created === 'number' ? new Date(att.created).toISOString() : (att.created ?? ''),
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraGetAttachmentsTool: ToolConfig<
|
||||
JiraGetAttachmentsParams,
|
||||
JiraGetAttachmentsResponse
|
||||
@@ -61,15 +79,13 @@ export const jiraGetAttachmentsTool: ToolConfig<
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraGetAttachmentsParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const attachmentsUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}?fields=attachment`
|
||||
const fetchAttachments = async (cloudId: string) => {
|
||||
const attachmentsUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}?fields=attachment`
|
||||
const attachmentsResponse = await fetch(attachmentsUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -82,60 +98,46 @@ export const jiraGetAttachmentsTool: ToolConfig<
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await attachmentsResponse.json()
|
||||
return attachmentsResponse.json()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
attachments: (data?.fields?.attachment || []).map((att: any) => ({
|
||||
id: att.id,
|
||||
filename: att.filename,
|
||||
size: att.size,
|
||||
mimeType: att.mimeType,
|
||||
created: att.created,
|
||||
author: att.author?.displayName || att.author?.accountId || 'Unknown',
|
||||
})),
|
||||
},
|
||||
let data: any
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
data = await fetchAttachments(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get attachments from Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get attachments from Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
attachments: (data?.fields?.attachment || []).map((att: any) => ({
|
||||
id: att.id,
|
||||
filename: att.filename,
|
||||
size: att.size,
|
||||
mimeType: att.mimeType,
|
||||
created: att.created,
|
||||
author: att.author?.displayName || att.author?.accountId || 'Unknown',
|
||||
})),
|
||||
issueKey: params?.issueKey ?? 'unknown',
|
||||
attachments: (data?.fields?.attachment ?? []).map(transformAttachment),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
attachments: {
|
||||
type: 'array',
|
||||
description: 'Array of attachments with id, filename, size, mimeType, created, author',
|
||||
description: 'Array of attachments',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: ATTACHMENT_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import type { JiraGetCommentsParams, JiraGetCommentsResponse } from '@/tools/jira/types'
|
||||
import { COMMENT_ITEM_PROPERTIES, TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { extractAdfText, getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface JiraGetCommentsParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
issueKey: string
|
||||
startAt?: number
|
||||
maxResults?: number
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraGetCommentsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueKey: string
|
||||
total: number
|
||||
comments: Array<{
|
||||
id: string
|
||||
author: string
|
||||
body: string
|
||||
created: string
|
||||
updated: string
|
||||
}>
|
||||
/**
|
||||
* Transforms a raw Jira comment object into typed output.
|
||||
*/
|
||||
function transformComment(comment: any) {
|
||||
return {
|
||||
id: comment.id ?? '',
|
||||
body: extractAdfText(comment.body) ?? '',
|
||||
author: transformUser(comment.author) ?? { accountId: '', displayName: '' },
|
||||
updateAuthor: transformUser(comment.updateAuthor),
|
||||
created: comment.created ?? '',
|
||||
updated: comment.updated ?? '',
|
||||
visibility: comment.visibility
|
||||
? { type: comment.visibility.type ?? '', value: comment.visibility.value ?? '' }
|
||||
: null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +62,13 @@ export const jiraGetCommentsTool: ToolConfig<JiraGetCommentsParams, JiraGetComme
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of comments to return (default: 50)',
|
||||
},
|
||||
orderBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Sort order for comments: "-created" for newest first, "created" for oldest first',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -79,9 +81,10 @@ export const jiraGetCommentsTool: ToolConfig<JiraGetCommentsParams, JiraGetComme
|
||||
request: {
|
||||
url: (params: JiraGetCommentsParams) => {
|
||||
if (params.cloudId) {
|
||||
const startAt = params.startAt || 0
|
||||
const maxResults = params.maxResults || 50
|
||||
return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/issue/${params.issueKey}/comment?startAt=${startAt}&maxResults=${maxResults}`
|
||||
const startAt = params.startAt ?? 0
|
||||
const maxResults = params.maxResults ?? 50
|
||||
const orderBy = params.orderBy ?? '-created'
|
||||
return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/issue/${params.issueKey}/comment?startAt=${startAt}&maxResults=${maxResults}&orderBy=${orderBy}`
|
||||
}
|
||||
return 'https://api.atlassian.com/oauth/token/accessible-resources'
|
||||
},
|
||||
@@ -95,29 +98,16 @@ export const jiraGetCommentsTool: ToolConfig<JiraGetCommentsParams, JiraGetComme
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraGetCommentsParams) => {
|
||||
// Extract text from Atlassian Document Format
|
||||
const extractText = (content: any): string => {
|
||||
if (!content) return ''
|
||||
if (typeof content === 'string') return content
|
||||
if (Array.isArray(content)) {
|
||||
return content.map(extractText).join(' ')
|
||||
}
|
||||
if (content.type === 'text') return content.text || ''
|
||||
if (content.content) return extractText(content.content)
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const startAt = params?.startAt || 0
|
||||
const maxResults = params?.maxResults || 50
|
||||
const commentsUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/comment?startAt=${startAt}&maxResults=${maxResults}`
|
||||
const fetchComments = async (cloudId: string) => {
|
||||
const startAt = params?.startAt ?? 0
|
||||
const maxResults = params?.maxResults ?? 50
|
||||
const orderBy = params?.orderBy ?? '-created'
|
||||
const commentsUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/comment?startAt=${startAt}&maxResults=${maxResults}&orderBy=${orderBy}`
|
||||
const commentsResponse = await fetch(commentsUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -130,61 +120,52 @@ export const jiraGetCommentsTool: ToolConfig<JiraGetCommentsParams, JiraGetComme
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await commentsResponse.json()
|
||||
return commentsResponse.json()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
total: data.total || 0,
|
||||
comments: (data.comments || []).map((comment: any) => ({
|
||||
id: comment.id,
|
||||
author: comment.author?.displayName || comment.author?.accountId || 'Unknown',
|
||||
body: extractText(comment.body),
|
||||
created: comment.created,
|
||||
updated: comment.updated,
|
||||
})),
|
||||
},
|
||||
let data: any
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
data = await fetchComments(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get comments from Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get comments from Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
total: data.total || 0,
|
||||
comments: (data.comments || []).map((comment: any) => ({
|
||||
id: comment.id,
|
||||
author: comment.author?.displayName || comment.author?.accountId || 'Unknown',
|
||||
body: extractText(comment.body),
|
||||
created: comment.created,
|
||||
updated: comment.updated,
|
||||
})),
|
||||
issueKey: params?.issueKey ?? 'unknown',
|
||||
total: data.total ?? 0,
|
||||
startAt: data.startAt ?? 0,
|
||||
maxResults: data.maxResults ?? 0,
|
||||
comments: (data.comments ?? []).map(transformComment),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
total: { type: 'number', description: 'Total number of comments' },
|
||||
startAt: { type: 'number', description: 'Pagination start index' },
|
||||
maxResults: { type: 'number', description: 'Maximum results per page' },
|
||||
comments: {
|
||||
type: 'array',
|
||||
description: 'Array of comments with id, author, body, created, updated',
|
||||
description: 'Array of comments',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: COMMENT_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,38 +1,20 @@
|
||||
import type { JiraGetUsersParams, JiraGetUsersResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT, USER_OUTPUT_PROPERTIES } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface JiraGetUsersParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
accountId?: string
|
||||
startAt?: number
|
||||
maxResults?: number
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraUser {
|
||||
accountId: string
|
||||
accountType?: string
|
||||
active: boolean
|
||||
displayName: string
|
||||
emailAddress?: string
|
||||
avatarUrls?: {
|
||||
'16x16'?: string
|
||||
'24x24'?: string
|
||||
'32x32'?: string
|
||||
'48x48'?: string
|
||||
}
|
||||
timeZone?: string
|
||||
self?: string
|
||||
}
|
||||
|
||||
export interface JiraGetUsersResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
users: JiraUser[]
|
||||
total?: number
|
||||
startAt?: number
|
||||
maxResults?: number
|
||||
/**
|
||||
* Transforms a raw Jira user API object into typed output.
|
||||
*/
|
||||
function transformUserOutput(user: any) {
|
||||
return {
|
||||
accountId: user.accountId ?? '',
|
||||
accountType: user.accountType ?? null,
|
||||
active: user.active ?? false,
|
||||
displayName: user.displayName ?? '',
|
||||
emailAddress: user.emailAddress ?? null,
|
||||
avatarUrl: user.avatarUrls?.['48x48'] ?? null,
|
||||
timeZone: user.timeZone ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,9 +94,7 @@ export const jiraGetUsersTool: ToolConfig<JiraGetUsersParams, JiraGetUsersRespon
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraGetUsersParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
|
||||
const fetchUsers = async (cloudId: string) => {
|
||||
let usersUrl: string
|
||||
if (params!.accountId) {
|
||||
usersUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/user?accountId=${encodeURIComponent(params!.accountId)}`
|
||||
@@ -144,71 +124,49 @@ export const jiraGetUsersTool: ToolConfig<JiraGetUsersParams, JiraGetUsersRespon
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await usersResponse.json()
|
||||
return usersResponse.json()
|
||||
}
|
||||
|
||||
const users = params!.accountId ? [data] : data
|
||||
let data: any
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
users: users.map((user: any) => ({
|
||||
accountId: user.accountId,
|
||||
accountType: user.accountType,
|
||||
active: user.active,
|
||||
displayName: user.displayName,
|
||||
emailAddress: user.emailAddress,
|
||||
avatarUrls: user.avatarUrls,
|
||||
timeZone: user.timeZone,
|
||||
self: user.self,
|
||||
})),
|
||||
total: params!.accountId ? 1 : users.length,
|
||||
startAt: params!.startAt || 0,
|
||||
maxResults: params!.maxResults || 50,
|
||||
},
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
data = await fetchUsers(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get Jira users (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get Jira users (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
const users = params?.accountId ? [data] : data
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
users: users.map((user: any) => ({
|
||||
accountId: user.accountId,
|
||||
accountType: user.accountType,
|
||||
active: user.active,
|
||||
displayName: user.displayName,
|
||||
emailAddress: user.emailAddress,
|
||||
avatarUrls: user.avatarUrls,
|
||||
timeZone: user.timeZone,
|
||||
self: user.self,
|
||||
})),
|
||||
users: users.map(transformUserOutput),
|
||||
total: params?.accountId ? 1 : users.length,
|
||||
startAt: params?.startAt || 0,
|
||||
maxResults: params?.maxResults || 50,
|
||||
startAt: params?.startAt ?? 0,
|
||||
maxResults: params?.maxResults ?? 50,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
users: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of users with accountId, displayName, emailAddress, active status, and avatarUrls',
|
||||
type: 'array',
|
||||
description: 'Array of Jira users',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of users returned' },
|
||||
startAt: { type: 'number', description: 'Pagination start index' },
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
import type { JiraGetWorklogsParams, JiraGetWorklogsResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT, WORKLOG_ITEM_PROPERTIES } from '@/tools/jira/types'
|
||||
import { extractAdfText, getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export interface JiraGetWorklogsParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
issueKey: string
|
||||
startAt?: number
|
||||
maxResults?: number
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraGetWorklogsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueKey: string
|
||||
total: number
|
||||
worklogs: Array<{
|
||||
id: string
|
||||
author: string
|
||||
timeSpentSeconds: number
|
||||
timeSpent: string
|
||||
comment?: string
|
||||
created: string
|
||||
updated: string
|
||||
started: string
|
||||
}>
|
||||
/**
|
||||
* Transforms a raw Jira worklog object into typed output.
|
||||
*/
|
||||
function transformWorklog(worklog: any) {
|
||||
return {
|
||||
id: worklog.id ?? '',
|
||||
author: transformUser(worklog.author) ?? { accountId: '', displayName: '' },
|
||||
updateAuthor: transformUser(worklog.updateAuthor),
|
||||
comment: worklog.comment ? (extractAdfText(worklog.comment) ?? null) : null,
|
||||
started: worklog.started ?? '',
|
||||
timeSpent: worklog.timeSpent ?? '',
|
||||
timeSpentSeconds: worklog.timeSpentSeconds ?? 0,
|
||||
created: worklog.created ?? '',
|
||||
updated: worklog.updated ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +74,8 @@ export const jiraGetWorklogsTool: ToolConfig<JiraGetWorklogsParams, JiraGetWorkl
|
||||
request: {
|
||||
url: (params: JiraGetWorklogsParams) => {
|
||||
if (params.cloudId) {
|
||||
const startAt = params.startAt || 0
|
||||
const maxResults = params.maxResults || 50
|
||||
const startAt = params.startAt ?? 0
|
||||
const maxResults = params.maxResults ?? 50
|
||||
return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/issue/${params.issueKey}/worklog?startAt=${startAt}&maxResults=${maxResults}`
|
||||
}
|
||||
return 'https://api.atlassian.com/oauth/token/accessible-resources'
|
||||
@@ -98,29 +90,15 @@ export const jiraGetWorklogsTool: ToolConfig<JiraGetWorklogsParams, JiraGetWorkl
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraGetWorklogsParams) => {
|
||||
// Extract text from Atlassian Document Format
|
||||
const extractText = (content: any): string => {
|
||||
if (!content) return ''
|
||||
if (typeof content === 'string') return content
|
||||
if (Array.isArray(content)) {
|
||||
return content.map(extractText).join(' ')
|
||||
}
|
||||
if (content.type === 'text') return content.text || ''
|
||||
if (content.content) return extractText(content.content)
|
||||
return ''
|
||||
}
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const startAt = params?.startAt || 0
|
||||
const maxResults = params?.maxResults || 50
|
||||
const worklogsUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/worklog?startAt=${startAt}&maxResults=${maxResults}`
|
||||
const fetchWorklogs = async (cloudId: string) => {
|
||||
const startAt = params?.startAt ?? 0
|
||||
const maxResults = params?.maxResults ?? 50
|
||||
const worklogsUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/worklog?startAt=${startAt}&maxResults=${maxResults}`
|
||||
const worklogsResponse = await fetch(worklogsUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -133,68 +111,52 @@ export const jiraGetWorklogsTool: ToolConfig<JiraGetWorklogsParams, JiraGetWorkl
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await worklogsResponse.json()
|
||||
return worklogsResponse.json()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
total: data.total || 0,
|
||||
worklogs: (data.worklogs || []).map((worklog: any) => ({
|
||||
id: worklog.id,
|
||||
author: worklog.author?.displayName || worklog.author?.accountId || 'Unknown',
|
||||
timeSpentSeconds: worklog.timeSpentSeconds,
|
||||
timeSpent: worklog.timeSpent,
|
||||
comment: worklog.comment ? extractText(worklog.comment) : undefined,
|
||||
created: worklog.created,
|
||||
updated: worklog.updated,
|
||||
started: worklog.started,
|
||||
})),
|
||||
},
|
||||
let data: any
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
data = await fetchWorklogs(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get worklogs from Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to get worklogs from Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
total: data.total || 0,
|
||||
worklogs: (data.worklogs || []).map((worklog: any) => ({
|
||||
id: worklog.id,
|
||||
author: worklog.author?.displayName || worklog.author?.accountId || 'Unknown',
|
||||
timeSpentSeconds: worklog.timeSpentSeconds,
|
||||
timeSpent: worklog.timeSpent,
|
||||
comment: worklog.comment ? extractText(worklog.comment) : undefined,
|
||||
created: worklog.created,
|
||||
updated: worklog.updated,
|
||||
started: worklog.started,
|
||||
})),
|
||||
issueKey: params?.issueKey ?? 'unknown',
|
||||
total: data.total ?? 0,
|
||||
startAt: data.startAt ?? 0,
|
||||
maxResults: data.maxResults ?? 0,
|
||||
worklogs: (data.worklogs ?? []).map(transformWorklog),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
total: { type: 'number', description: 'Total number of worklogs' },
|
||||
startAt: { type: 'number', description: 'Pagination start index' },
|
||||
maxResults: { type: 'number', description: 'Maximum results per page' },
|
||||
worklogs: {
|
||||
type: 'array',
|
||||
description:
|
||||
'Array of worklogs with id, author, timeSpentSeconds, timeSpent, comment, created, updated, started',
|
||||
description: 'Array of worklogs',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: WORKLOG_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { jiraAddCommentTool } from '@/tools/jira/add_comment'
|
||||
import { jiraAddWatcherTool } from '@/tools/jira/add_watcher'
|
||||
import { jiraAddWorklogTool } from '@/tools/jira/add_worklog'
|
||||
import { jiraAssignIssueTool } from '@/tools/jira/assign_issue'
|
||||
import { jiraBulkRetrieveTool } from '@/tools/jira/bulk_read'
|
||||
import { jiraBulkRetrieveTool, jiraBulkRetrieveV2Tool } from '@/tools/jira/bulk_read'
|
||||
import { jiraCreateIssueLinkTool } from '@/tools/jira/create_issue_link'
|
||||
import { jiraDeleteAttachmentTool } from '@/tools/jira/delete_attachment'
|
||||
import { jiraDeleteCommentTool } from '@/tools/jira/delete_comment'
|
||||
@@ -16,7 +16,7 @@ import { jiraGetUsersTool } from '@/tools/jira/get_users'
|
||||
import { jiraGetWorklogsTool } from '@/tools/jira/get_worklogs'
|
||||
import { jiraRemoveWatcherTool } from '@/tools/jira/remove_watcher'
|
||||
import { jiraRetrieveTool } from '@/tools/jira/retrieve'
|
||||
import { jiraSearchIssuesTool } from '@/tools/jira/search_issues'
|
||||
import { jiraSearchIssuesTool, jiraSearchIssuesV2Tool } from '@/tools/jira/search_issues'
|
||||
import { jiraTransitionIssueTool } from '@/tools/jira/transition_issue'
|
||||
import { jiraUpdateTool } from '@/tools/jira/update'
|
||||
import { jiraUpdateCommentTool } from '@/tools/jira/update_comment'
|
||||
@@ -28,10 +28,12 @@ export {
|
||||
jiraUpdateTool,
|
||||
jiraWriteTool,
|
||||
jiraBulkRetrieveTool,
|
||||
jiraBulkRetrieveV2Tool,
|
||||
jiraDeleteIssueTool,
|
||||
jiraAssignIssueTool,
|
||||
jiraTransitionIssueTool,
|
||||
jiraSearchIssuesTool,
|
||||
jiraSearchIssuesV2Tool,
|
||||
jiraAddCommentTool,
|
||||
jiraAddAttachmentTool,
|
||||
jiraGetCommentsTool,
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
import type { JiraRemoveWatcherParams, JiraRemoveWatcherResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface JiraRemoveWatcherParams {
|
||||
accessToken: string
|
||||
domain: string
|
||||
issueKey: string
|
||||
accountId: string
|
||||
cloudId?: string
|
||||
}
|
||||
|
||||
export interface JiraRemoveWatcherResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueKey: string
|
||||
watcherAccountId: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraRemoveWatcherTool: ToolConfig<JiraRemoveWatcherParams, JiraRemoveWatcherResponse> =
|
||||
{
|
||||
@@ -83,13 +68,12 @@ export const jiraRemoveWatcherTool: ToolConfig<JiraRemoveWatcherParams, JiraRemo
|
||||
transformResponse: async (response: Response, params?: JiraRemoveWatcherParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const watcherUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/watchers?accountId=${params?.accountId}`
|
||||
const watcherUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/watchers?accountId=${params!.accountId}`
|
||||
const watcherResponse = await fetch(watcherUrl, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -106,14 +90,13 @@ export const jiraRemoveWatcherTool: ToolConfig<JiraRemoveWatcherParams, JiraRemo
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
watcherAccountId: params?.accountId || 'unknown',
|
||||
issueKey: params!.issueKey || 'unknown',
|
||||
watcherAccountId: params!.accountId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to remove watcher from Jira issue (${response.status})`
|
||||
try {
|
||||
@@ -127,15 +110,15 @@ export const jiraRemoveWatcherTool: ToolConfig<JiraRemoveWatcherParams, JiraRemo
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
watcherAccountId: params?.accountId || 'unknown',
|
||||
issueKey: params!.issueKey || 'unknown',
|
||||
watcherAccountId: params!.accountId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
watcherAccountId: { type: 'string', description: 'Removed watcher account ID' },
|
||||
},
|
||||
|
||||
@@ -1,10 +1,175 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { JiraRetrieveParams, JiraRetrieveResponse } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import { ISSUE_ITEM_PROPERTIES, TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { extractAdfText, getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('JiraRetrieveTool')
|
||||
|
||||
/**
|
||||
* Transforms a raw Jira API issue response into a fully typed output.
|
||||
*/
|
||||
function transformIssueData(data: any) {
|
||||
const fields = data?.fields ?? {}
|
||||
return {
|
||||
id: data?.id ?? '',
|
||||
key: data?.key ?? '',
|
||||
self: data?.self ?? '',
|
||||
summary: fields.summary ?? '',
|
||||
description: extractAdfText(fields.description),
|
||||
status: {
|
||||
id: fields.status?.id ?? '',
|
||||
name: fields.status?.name ?? '',
|
||||
description: fields.status?.description ?? null,
|
||||
statusCategory: fields.status?.statusCategory
|
||||
? {
|
||||
id: fields.status.statusCategory.id,
|
||||
key: fields.status.statusCategory.key ?? '',
|
||||
name: fields.status.statusCategory.name ?? '',
|
||||
colorName: fields.status.statusCategory.colorName ?? '',
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
issuetype: {
|
||||
id: fields.issuetype?.id ?? '',
|
||||
name: fields.issuetype?.name ?? '',
|
||||
description: fields.issuetype?.description ?? null,
|
||||
subtask: fields.issuetype?.subtask ?? false,
|
||||
iconUrl: fields.issuetype?.iconUrl ?? null,
|
||||
},
|
||||
project: {
|
||||
id: fields.project?.id ?? '',
|
||||
key: fields.project?.key ?? '',
|
||||
name: fields.project?.name ?? '',
|
||||
projectTypeKey: fields.project?.projectTypeKey ?? null,
|
||||
},
|
||||
priority: fields.priority
|
||||
? {
|
||||
id: fields.priority.id ?? '',
|
||||
name: fields.priority.name ?? '',
|
||||
iconUrl: fields.priority.iconUrl ?? null,
|
||||
}
|
||||
: null,
|
||||
assignee: transformUser(fields.assignee),
|
||||
reporter: transformUser(fields.reporter),
|
||||
creator: transformUser(fields.creator),
|
||||
labels: fields.labels ?? [],
|
||||
components: (fields.components ?? []).map((c: any) => ({
|
||||
id: c.id ?? '',
|
||||
name: c.name ?? '',
|
||||
description: c.description ?? null,
|
||||
})),
|
||||
fixVersions: (fields.fixVersions ?? []).map((v: any) => ({
|
||||
id: v.id ?? '',
|
||||
name: v.name ?? '',
|
||||
released: v.released ?? null,
|
||||
releaseDate: v.releaseDate ?? null,
|
||||
})),
|
||||
resolution: fields.resolution
|
||||
? {
|
||||
id: fields.resolution.id ?? '',
|
||||
name: fields.resolution.name ?? '',
|
||||
description: fields.resolution.description ?? null,
|
||||
}
|
||||
: null,
|
||||
duedate: fields.duedate ?? null,
|
||||
created: fields.created ?? '',
|
||||
updated: fields.updated ?? '',
|
||||
resolutiondate: fields.resolutiondate ?? null,
|
||||
timetracking: fields.timetracking
|
||||
? {
|
||||
originalEstimate: fields.timetracking.originalEstimate ?? null,
|
||||
remainingEstimate: fields.timetracking.remainingEstimate ?? null,
|
||||
timeSpent: fields.timetracking.timeSpent ?? null,
|
||||
originalEstimateSeconds: fields.timetracking.originalEstimateSeconds ?? null,
|
||||
remainingEstimateSeconds: fields.timetracking.remainingEstimateSeconds ?? null,
|
||||
timeSpentSeconds: fields.timetracking.timeSpentSeconds ?? null,
|
||||
}
|
||||
: null,
|
||||
parent: fields.parent
|
||||
? {
|
||||
id: fields.parent.id ?? '',
|
||||
key: fields.parent.key ?? '',
|
||||
summary: fields.parent.fields?.summary ?? null,
|
||||
}
|
||||
: null,
|
||||
issuelinks: (fields.issuelinks ?? []).map((link: any) => ({
|
||||
id: link.id ?? '',
|
||||
type: {
|
||||
id: link.type?.id ?? '',
|
||||
name: link.type?.name ?? '',
|
||||
inward: link.type?.inward ?? '',
|
||||
outward: link.type?.outward ?? '',
|
||||
},
|
||||
inwardIssue: link.inwardIssue
|
||||
? {
|
||||
id: link.inwardIssue.id ?? '',
|
||||
key: link.inwardIssue.key ?? '',
|
||||
statusName: link.inwardIssue.fields?.status?.name ?? null,
|
||||
summary: link.inwardIssue.fields?.summary ?? null,
|
||||
}
|
||||
: null,
|
||||
outwardIssue: link.outwardIssue
|
||||
? {
|
||||
id: link.outwardIssue.id ?? '',
|
||||
key: link.outwardIssue.key ?? '',
|
||||
statusName: link.outwardIssue.fields?.status?.name ?? null,
|
||||
summary: link.outwardIssue.fields?.summary ?? null,
|
||||
}
|
||||
: null,
|
||||
})),
|
||||
subtasks: (fields.subtasks ?? []).map((sub: any) => ({
|
||||
id: sub.id ?? '',
|
||||
key: sub.key ?? '',
|
||||
summary: sub.fields?.summary ?? '',
|
||||
statusName: sub.fields?.status?.name ?? '',
|
||||
issueTypeName: sub.fields?.issuetype?.name ?? null,
|
||||
})),
|
||||
votes: fields.votes
|
||||
? {
|
||||
votes: fields.votes.votes ?? 0,
|
||||
hasVoted: fields.votes.hasVoted ?? false,
|
||||
}
|
||||
: null,
|
||||
watches:
|
||||
(fields.watches ?? fields.watcher)
|
||||
? {
|
||||
watchCount: (fields.watches ?? fields.watcher)?.watchCount ?? 0,
|
||||
isWatching: (fields.watches ?? fields.watcher)?.isWatching ?? false,
|
||||
}
|
||||
: null,
|
||||
comments: ((fields.comment?.comments ?? fields.comment) || []).map((c: any) => ({
|
||||
id: c.id ?? '',
|
||||
body: extractAdfText(c.body) ?? '',
|
||||
author: transformUser(c.author),
|
||||
updateAuthor: transformUser(c.updateAuthor),
|
||||
created: c.created ?? '',
|
||||
updated: c.updated ?? '',
|
||||
})),
|
||||
worklogs: ((fields.worklog?.worklogs ?? fields.worklog) || []).map((w: any) => ({
|
||||
id: w.id ?? '',
|
||||
author: transformUser(w.author),
|
||||
updateAuthor: transformUser(w.updateAuthor),
|
||||
comment: w.comment ? (extractAdfText(w.comment) ?? null) : null,
|
||||
started: w.started ?? '',
|
||||
timeSpent: w.timeSpent ?? '',
|
||||
timeSpentSeconds: w.timeSpentSeconds ?? 0,
|
||||
created: w.created ?? '',
|
||||
updated: w.updated ?? '',
|
||||
})),
|
||||
attachments: (fields.attachment ?? []).map((att: any) => ({
|
||||
id: att.id ?? '',
|
||||
filename: att.filename ?? '',
|
||||
mimeType: att.mimeType ?? '',
|
||||
size: att.size ?? 0,
|
||||
content: att.content ?? '',
|
||||
thumbnail: att.thumbnail ?? null,
|
||||
author: transformUser(att.author),
|
||||
created: att.created ?? '',
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraRetrieveTool: ToolConfig<JiraRetrieveParams, JiraRetrieveResponse> = {
|
||||
id: 'jira_retrieve',
|
||||
name: 'Jira Retrieve',
|
||||
@@ -29,12 +194,6 @@ export const jiraRetrieveTool: ToolConfig<JiraRetrieveParams, JiraRetrieveRespon
|
||||
visibility: 'user-only',
|
||||
description: 'Your Jira domain (e.g., yourcompany.atlassian.net)',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Jira project key (e.g., PROJ). Optional when retrieving a single issue.',
|
||||
},
|
||||
issueKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -53,10 +212,8 @@ export const jiraRetrieveTool: ToolConfig<JiraRetrieveParams, JiraRetrieveRespon
|
||||
request: {
|
||||
url: (params: JiraRetrieveParams) => {
|
||||
if (params.cloudId) {
|
||||
// Request with broad expands; additional endpoints fetched in transform for completeness
|
||||
return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/issue/${params.issueKey}?expand=renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations`
|
||||
}
|
||||
// If no cloudId, use the accessible resources endpoint
|
||||
return 'https://api.atlassian.com/oauth/token/accessible-resources'
|
||||
},
|
||||
method: 'GET',
|
||||
@@ -70,21 +227,16 @@ export const jiraRetrieveTool: ToolConfig<JiraRetrieveParams, JiraRetrieveRespon
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraRetrieveParams) => {
|
||||
if (!params?.issueKey) {
|
||||
throw new Error(
|
||||
'Select a project to read issues, or provide an issue key to read a single issue.'
|
||||
)
|
||||
throw new Error('Provide an issue key to retrieve a single issue.')
|
||||
}
|
||||
|
||||
// If we don't have a cloudId, resolve it robustly using the Jira utils helper
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Now fetch the actual issue with the found cloudId
|
||||
const issueUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}?expand=renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations`
|
||||
const fetchIssue = async (cloudId: string) => {
|
||||
const issueUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params.issueKey}?expand=renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations`
|
||||
const issueResponse = await fetch(issueUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -97,19 +249,20 @@ export const jiraRetrieveTool: ToolConfig<JiraRetrieveParams, JiraRetrieveRespon
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await issueResponse.json()
|
||||
return issueResponse.json()
|
||||
}
|
||||
|
||||
// Fetch additional resources for a comprehensive view
|
||||
const fetchSupplementary = async (cloudId: string, data: any) => {
|
||||
const base = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params.issueKey}`
|
||||
const [commentsResp, worklogResp, watchersResp] = await Promise.all([
|
||||
fetch(`${base}/comment?maxResults=100&orderBy=-created`, {
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params!.accessToken}` },
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params.accessToken}` },
|
||||
}),
|
||||
fetch(`${base}/worklog?maxResults=100`, {
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params!.accessToken}` },
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params.accessToken}` },
|
||||
}),
|
||||
fetch(`${base}/watchers`, {
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params!.accessToken}` },
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params.accessToken}` },
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -117,124 +270,62 @@ export const jiraRetrieveTool: ToolConfig<JiraRetrieveParams, JiraRetrieveRespon
|
||||
if (commentsResp.ok) {
|
||||
const commentsData = await commentsResp.json()
|
||||
if (data?.fields) data.fields.comment = commentsData?.comments || data.fields.comment
|
||||
} else {
|
||||
logger.debug?.('Failed to fetch comments', { status: commentsResp.status })
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
logger.debug?.('Failed to fetch comments')
|
||||
}
|
||||
|
||||
try {
|
||||
if (worklogResp.ok) {
|
||||
const worklogData = await worklogResp.json()
|
||||
if (data?.fields) data.fields.worklog = worklogData || data.fields.worklog
|
||||
} else {
|
||||
logger.debug?.('Failed to fetch worklog', { status: worklogResp.status })
|
||||
}
|
||||
} catch {}
|
||||
} catch {
|
||||
logger.debug?.('Failed to fetch worklog')
|
||||
}
|
||||
|
||||
try {
|
||||
if (watchersResp.ok) {
|
||||
const watchersData = await watchersResp.json()
|
||||
if (data?.fields) {
|
||||
// Provide both common keys for compatibility
|
||||
;(data.fields as any).watcher = watchersData
|
||||
;(data.fields as any).watches = watchersData
|
||||
data.fields.watches = watchersData
|
||||
}
|
||||
} else {
|
||||
logger.debug?.('Failed to fetch watchers', { status: watchersResp.status })
|
||||
}
|
||||
} catch {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: data?.key,
|
||||
summary: data?.fields?.summary,
|
||||
description: data?.fields?.description,
|
||||
created: data?.fields?.created,
|
||||
updated: data?.fields?.updated,
|
||||
issue: data,
|
||||
},
|
||||
} catch {
|
||||
logger.debug?.('Failed to fetch watchers')
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a cloudId, this response is the issue data
|
||||
if (!response.ok) {
|
||||
let message = `Failed to fetch Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.message || err?.errorMessages?.[0] || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
const data = await response.json()
|
||||
|
||||
// When cloudId was provided up-front, fetch additional data too
|
||||
try {
|
||||
const url = new URL(response.url)
|
||||
const match = url.pathname.match(/\/ex\/jira\/([^/]+)/)
|
||||
const cloudId = match?.[1]
|
||||
if (cloudId) {
|
||||
const base = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params.issueKey}`
|
||||
const [commentsResp, worklogResp, watchersResp] = await Promise.all([
|
||||
fetch(`${base}/comment?maxResults=100&orderBy=-created`, {
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params!.accessToken}` },
|
||||
}),
|
||||
fetch(`${base}/worklog?maxResults=100`, {
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params!.accessToken}` },
|
||||
}),
|
||||
fetch(`${base}/watchers`, {
|
||||
headers: { Accept: 'application/json', Authorization: `Bearer ${params!.accessToken}` },
|
||||
}),
|
||||
])
|
||||
let data: any
|
||||
|
||||
if (!params.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params.domain, params.accessToken)
|
||||
data = await fetchIssue(cloudId)
|
||||
await fetchSupplementary(cloudId, data)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to fetch Jira issue (${response.status})`
|
||||
try {
|
||||
if (commentsResp.ok) {
|
||||
const commentsData = await commentsResp.json()
|
||||
if (data?.fields) data.fields.comment = commentsData?.comments || data.fields.comment
|
||||
}
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
if (worklogResp.ok) {
|
||||
const worklogData = await worklogResp.json()
|
||||
if (data?.fields) data.fields.worklog = worklogData || data.fields.worklog
|
||||
}
|
||||
} catch {}
|
||||
|
||||
try {
|
||||
if (watchersResp.ok) {
|
||||
const watchersData = await watchersResp.json()
|
||||
if (data?.fields) {
|
||||
;(data.fields as any).watcher = watchersData
|
||||
;(data.fields as any).watches = watchersData
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
const err = await response.json()
|
||||
message = err?.message || err?.errorMessages?.[0] || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
} catch {}
|
||||
data = await response.json()
|
||||
await fetchSupplementary(params.cloudId, data)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: data?.key,
|
||||
summary: data?.fields?.summary,
|
||||
description: data?.fields?.description,
|
||||
created: data?.fields?.created,
|
||||
updated: data?.fields?.updated,
|
||||
issue: data,
|
||||
...transformIssueData(data),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
issueKey: { type: 'string', description: 'Issue key (e.g., PROJ-123)' },
|
||||
summary: { type: 'string', description: 'Issue summary' },
|
||||
description: { type: 'json', description: 'Issue description content' },
|
||||
created: { type: 'string', description: 'Issue creation timestamp' },
|
||||
updated: { type: 'string', description: 'Issue last updated timestamp' },
|
||||
issue: { type: 'json', description: 'Complete issue object with all fields' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
...ISSUE_ITEM_PROPERTIES,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,65 @@
|
||||
import type { JiraSearchIssuesParams, JiraSearchIssuesResponse } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type {
|
||||
JiraSearchIssuesParams,
|
||||
JiraSearchIssuesResponse,
|
||||
JiraSearchIssuesV2Params,
|
||||
JiraSearchIssuesV2Response,
|
||||
} from '@/tools/jira/types'
|
||||
import { SEARCH_ISSUE_ITEM_PROPERTIES, TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { extractAdfText, getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Transforms a raw Jira search result issue into typed output.
|
||||
*/
|
||||
function transformSearchIssue(issue: any) {
|
||||
const fields = issue?.fields ?? {}
|
||||
return {
|
||||
id: issue.id ?? '',
|
||||
key: issue.key ?? '',
|
||||
self: issue.self ?? '',
|
||||
summary: fields.summary ?? '',
|
||||
description: extractAdfText(fields.description),
|
||||
status: {
|
||||
id: fields.status?.id ?? '',
|
||||
name: fields.status?.name ?? '',
|
||||
statusCategory: fields.status?.statusCategory
|
||||
? {
|
||||
id: fields.status.statusCategory.id,
|
||||
key: fields.status.statusCategory.key ?? '',
|
||||
name: fields.status.statusCategory.name ?? '',
|
||||
colorName: fields.status.statusCategory.colorName ?? '',
|
||||
}
|
||||
: null,
|
||||
},
|
||||
issuetype: {
|
||||
id: fields.issuetype?.id ?? '',
|
||||
name: fields.issuetype?.name ?? '',
|
||||
subtask: fields.issuetype?.subtask ?? false,
|
||||
},
|
||||
project: {
|
||||
id: fields.project?.id ?? '',
|
||||
key: fields.project?.key ?? '',
|
||||
name: fields.project?.name ?? '',
|
||||
},
|
||||
priority: fields.priority
|
||||
? { id: fields.priority.id ?? '', name: fields.priority.name ?? '' }
|
||||
: null,
|
||||
assignee: transformUser(fields.assignee),
|
||||
reporter: transformUser(fields.reporter),
|
||||
labels: fields.labels ?? [],
|
||||
components: (fields.components ?? []).map((c: any) => ({
|
||||
id: c.id ?? '',
|
||||
name: c.name ?? '',
|
||||
})),
|
||||
resolution: fields.resolution
|
||||
? { id: fields.resolution.id ?? '', name: fields.resolution.name ?? '' }
|
||||
: null,
|
||||
duedate: fields.duedate ?? null,
|
||||
created: fields.created ?? '',
|
||||
updated: fields.updated ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraSearchIssuesTool: ToolConfig<JiraSearchIssuesParams, JiraSearchIssuesResponse> = {
|
||||
id: 'jira_search_issues',
|
||||
name: 'Jira Search Issues',
|
||||
@@ -50,7 +108,7 @@ export const jiraSearchIssuesTool: ToolConfig<JiraSearchIssuesParams, JiraSearch
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
"Array of field names to return (default: ['summary', 'status', 'assignee', 'created', 'updated'])",
|
||||
'Array of field names to return (default: all navigable). Use "*all" for every field.',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
@@ -88,8 +146,7 @@ export const jiraSearchIssuesTool: ToolConfig<JiraSearchIssuesParams, JiraSearch
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraSearchIssuesParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
const performSearch = async (cloudId: string) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params?.jql) query.set('jql', params.jql)
|
||||
if (typeof params?.startAt === 'number') query.set('startAt', String(params.startAt))
|
||||
@@ -103,12 +160,6 @@ export const jiraSearchIssuesTool: ToolConfig<JiraSearchIssuesParams, JiraSearch
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
jql: params?.jql,
|
||||
startAt: params?.startAt ? Number(params.startAt) : 0,
|
||||
maxResults: params?.maxResults ? Number(params.maxResults) : 50,
|
||||
fields: params?.fields || ['summary', 'status', 'assignee', 'created', 'updated'],
|
||||
}),
|
||||
})
|
||||
|
||||
if (!searchResponse.ok) {
|
||||
@@ -120,65 +171,223 @@ export const jiraSearchIssuesTool: ToolConfig<JiraSearchIssuesParams, JiraSearch
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await searchResponse.json()
|
||||
return searchResponse.json()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
total: data?.total || 0,
|
||||
startAt: data?.startAt || 0,
|
||||
maxResults: data?.maxResults || 0,
|
||||
issues: (data?.issues || []).map((issue: any) => ({
|
||||
key: issue.key,
|
||||
summary: issue.fields?.summary,
|
||||
status: issue.fields?.status?.name,
|
||||
assignee: issue.fields?.assignee?.displayName || issue.fields?.assignee?.accountId,
|
||||
created: issue.fields?.created,
|
||||
updated: issue.fields?.updated,
|
||||
})),
|
||||
},
|
||||
let data: any
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
data = await performSearch(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to search Jira issues (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
let message = `Failed to search Jira issues (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
total: data?.total || 0,
|
||||
startAt: data?.startAt || 0,
|
||||
maxResults: data?.maxResults || 0,
|
||||
issues: (data?.issues || []).map((issue: any) => ({
|
||||
key: issue.key,
|
||||
summary: issue.fields?.summary,
|
||||
status: issue.fields?.status?.name,
|
||||
assignee: issue.fields?.assignee?.displayName || issue.fields?.assignee?.accountId,
|
||||
created: issue.fields?.created,
|
||||
updated: issue.fields?.updated,
|
||||
})),
|
||||
total: data?.total ?? 0,
|
||||
startAt: data?.startAt ?? 0,
|
||||
maxResults: data?.maxResults ?? 0,
|
||||
issues: (data?.issues ?? []).map(transformSearchIssue),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
total: { type: 'number', description: 'Total number of matching issues' },
|
||||
startAt: { type: 'number', description: 'Pagination start index' },
|
||||
maxResults: { type: 'number', description: 'Maximum results per page' },
|
||||
issues: {
|
||||
type: 'array',
|
||||
description: 'Array of matching issues with key, summary, status, assignee, created, updated',
|
||||
description: 'Array of matching issues',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: SEARCH_ISSUE_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* V2 Search Issues Tool - Uses cursor-based pagination (nextPageToken) on /rest/api/3/search/jql.
|
||||
* The startAt parameter was deprecated on this endpoint as of Sept 2025.
|
||||
*/
|
||||
export const jiraSearchIssuesV2Tool: ToolConfig<
|
||||
JiraSearchIssuesV2Params,
|
||||
JiraSearchIssuesV2Response
|
||||
> = {
|
||||
id: 'jira_search_issues_v2',
|
||||
name: 'Jira Search Issues V2',
|
||||
description:
|
||||
'Search for Jira issues using JQL with cursor-based pagination (V2 - uses nextPageToken)',
|
||||
version: '2.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'jira',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Jira',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Your Jira domain (e.g., yourcompany.atlassian.net)',
|
||||
},
|
||||
jql: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JQL query string to search for issues (e.g., "project = PROJ AND status = Open")',
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Cursor token for the next page of results. Omit for the first page.',
|
||||
},
|
||||
maxResults: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results to return per page (default: 50)',
|
||||
},
|
||||
fields: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Array of field names to return (default: all navigable). Use "*all" for every field.',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description:
|
||||
'Jira Cloud ID for the instance. If not provided, it will be fetched using the domain.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: JiraSearchIssuesV2Params) => {
|
||||
if (params.cloudId) {
|
||||
const query = new URLSearchParams()
|
||||
if (params.jql) query.set('jql', params.jql)
|
||||
if (params.nextPageToken) query.set('nextPageToken', params.nextPageToken)
|
||||
if (typeof params.maxResults === 'number')
|
||||
query.set('maxResults', String(params.maxResults))
|
||||
if (Array.isArray(params.fields) && params.fields.length > 0)
|
||||
query.set('fields', params.fields.join(','))
|
||||
const qs = query.toString()
|
||||
return `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/search/jql${qs ? `?${qs}` : ''}`
|
||||
}
|
||||
return 'https://api.atlassian.com/oauth/token/accessible-resources'
|
||||
},
|
||||
method: () => 'GET',
|
||||
headers: (params: JiraSearchIssuesV2Params) => ({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
}),
|
||||
body: () => undefined as any,
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraSearchIssuesV2Params) => {
|
||||
const performSearch = async (cloudId: string) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params?.jql) query.set('jql', params.jql)
|
||||
if (params?.nextPageToken) query.set('nextPageToken', params.nextPageToken)
|
||||
if (typeof params?.maxResults === 'number') query.set('maxResults', String(params.maxResults))
|
||||
if (Array.isArray(params?.fields) && params.fields.length > 0)
|
||||
query.set('fields', params.fields.join(','))
|
||||
const searchUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/search/jql?${query.toString()}`
|
||||
const searchResponse = await fetch(searchUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
if (!searchResponse.ok) {
|
||||
let message = `Failed to search Jira issues (${searchResponse.status})`
|
||||
try {
|
||||
const err = await searchResponse.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
return searchResponse.json()
|
||||
}
|
||||
|
||||
let data: any
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
data = await performSearch(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to search Jira issues (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issues: (data?.issues ?? []).map(transformSearchIssue),
|
||||
nextPageToken: data?.nextPageToken ?? null,
|
||||
isLast: data?.isLast ?? true,
|
||||
total: data?.total ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issues: {
|
||||
type: 'array',
|
||||
description: 'Array of matching issues',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: SEARCH_ISSUE_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Cursor token for the next page. Null when no more results.',
|
||||
optional: true,
|
||||
},
|
||||
isLast: { type: 'boolean', description: 'Whether this is the last page of results' },
|
||||
total: {
|
||||
type: 'number',
|
||||
description: 'Total number of matching issues (may not always be available)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JiraTransitionIssueParams, JiraTransitionIssueResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
@@ -48,6 +49,12 @@ export const jiraTransitionIssueTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional comment to add when transitioning the issue',
|
||||
},
|
||||
resolution: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Resolution name to set during transition (e.g., "Fixed", "Won\'t Fix")',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -74,87 +81,47 @@ export const jiraTransitionIssueTool: ToolConfig<
|
||||
},
|
||||
body: (params: JiraTransitionIssueParams) => {
|
||||
if (!params.cloudId) return undefined as any
|
||||
const body: any = {
|
||||
transition: {
|
||||
id: params.transitionId,
|
||||
},
|
||||
}
|
||||
|
||||
if (params.comment) {
|
||||
body.update = {
|
||||
comment: [
|
||||
{
|
||||
add: {
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params.comment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
return buildTransitionBody(params)
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraTransitionIssueParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
const transitionUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/transitions`
|
||||
|
||||
const body: any = {
|
||||
transition: {
|
||||
id: params!.transitionId,
|
||||
const performTransition = async (cloudId: string) => {
|
||||
// First, fetch available transitions to get the name and target status
|
||||
const transitionsUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/transitions`
|
||||
const transitionsResp = await fetch(transitionsUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
if (params!.comment) {
|
||||
body.update = {
|
||||
comment: [
|
||||
{
|
||||
add: {
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params!.comment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
let transitionName: string | null = null
|
||||
let toStatus: { id: string; name: string } | null = null
|
||||
|
||||
if (transitionsResp.ok) {
|
||||
const transitionsData = await transitionsResp.json()
|
||||
const transition = (transitionsData?.transitions ?? []).find(
|
||||
(t: any) => String(t.id) === String(params!.transitionId)
|
||||
)
|
||||
if (transition) {
|
||||
transitionName = transition.name ?? null
|
||||
toStatus = transition.to
|
||||
? { id: transition.to.id ?? '', name: transition.to.name ?? '' }
|
||||
: null
|
||||
}
|
||||
}
|
||||
|
||||
const transitionResponse = await fetch(transitionUrl, {
|
||||
// Perform the transition
|
||||
const transitionResponse = await fetch(transitionsUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
body: JSON.stringify(buildTransitionBody(params!)),
|
||||
})
|
||||
|
||||
if (!transitionResponse.ok) {
|
||||
@@ -166,42 +133,119 @@ export const jiraTransitionIssueTool: ToolConfig<
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
// Transition endpoint returns 204 No Content on success
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params!.issueKey,
|
||||
transitionId: params!.transitionId,
|
||||
success: true,
|
||||
},
|
||||
return { transitionName, toStatus }
|
||||
}
|
||||
|
||||
let transitionName: string | null = null
|
||||
let toStatus: { id: string; name: string } | null = null
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
const result = await performTransition(cloudId)
|
||||
transitionName = result.transitionName
|
||||
toStatus = result.toStatus
|
||||
} else {
|
||||
// When cloudId was provided, the initial request was the POST transition.
|
||||
// We need to fetch transition metadata separately.
|
||||
if (!response.ok) {
|
||||
let message = `Failed to transition Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
let message = `Failed to transition Jira issue (${response.status})`
|
||||
// Fetch transition metadata for the response
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
const transitionsUrl = `https://api.atlassian.com/ex/jira/${params.cloudId}/rest/api/3/issue/${params.issueKey}/transitions`
|
||||
const transitionsResp = await fetch(transitionsUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
},
|
||||
})
|
||||
if (transitionsResp.ok) {
|
||||
const transitionsData = await transitionsResp.json()
|
||||
const transition = (transitionsData?.transitions ?? []).find(
|
||||
(t: any) => String(t.id) === String(params.transitionId)
|
||||
)
|
||||
if (transition) {
|
||||
transitionName = transition.name ?? null
|
||||
toStatus = transition.to
|
||||
? { id: transition.to.id ?? '', name: transition.to.name ?? '' }
|
||||
: null
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Transition endpoint returns 204 No Content on success
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
transitionId: params?.transitionId || 'unknown',
|
||||
issueKey: params?.issueKey ?? 'unknown',
|
||||
transitionId: params?.transitionId ?? 'unknown',
|
||||
transitionName,
|
||||
toStatus,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key that was transitioned' },
|
||||
transitionId: { type: 'string', description: 'Applied transition ID' },
|
||||
transitionName: { type: 'string', description: 'Applied transition name', optional: true },
|
||||
toStatus: {
|
||||
type: 'object',
|
||||
description: 'Target status after transition',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Status ID' },
|
||||
name: { type: 'string', description: 'Status name' },
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the transition request body per Jira API v3.
|
||||
*/
|
||||
function buildTransitionBody(params: JiraTransitionIssueParams) {
|
||||
const body: any = {
|
||||
transition: { id: params.transitionId },
|
||||
}
|
||||
|
||||
if (params.resolution) {
|
||||
body.fields = {
|
||||
...body.fields,
|
||||
resolution: { name: params.resolution },
|
||||
}
|
||||
}
|
||||
|
||||
if (params.comment) {
|
||||
body.update = {
|
||||
comment: [
|
||||
{
|
||||
add: {
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: params.comment }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
return body
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
import type { JiraUpdateParams, JiraUpdateResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraUpdateTool: ToolConfig<JiraUpdateParams, JiraUpdateResponse> = {
|
||||
@@ -25,12 +26,6 @@ export const jiraUpdateTool: ToolConfig<JiraUpdateParams, JiraUpdateResponse> =
|
||||
visibility: 'user-only',
|
||||
description: 'Your Jira domain (e.g., yourcompany.atlassian.net)',
|
||||
},
|
||||
projectId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Jira project key (e.g., PROJ). Optional when updating a single issue.',
|
||||
},
|
||||
issueKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -49,23 +44,65 @@ export const jiraUpdateTool: ToolConfig<JiraUpdateParams, JiraUpdateResponse> =
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New description for the issue',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New status for the issue',
|
||||
},
|
||||
priority: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New priority for the issue',
|
||||
description: 'New priority ID or name for the issue (e.g., "High")',
|
||||
},
|
||||
assignee: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New assignee for the issue',
|
||||
description: 'New assignee account ID for the issue',
|
||||
},
|
||||
labels: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Labels to set on the issue (array of label name strings)',
|
||||
},
|
||||
components: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Components to set on the issue (array of component name strings)',
|
||||
},
|
||||
duedate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date for the issue (format: YYYY-MM-DD)',
|
||||
},
|
||||
fixVersions: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Fix versions to set (array of version name strings)',
|
||||
},
|
||||
environment: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Environment information for the issue',
|
||||
},
|
||||
customFieldId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Custom field ID to update (e.g., customfield_10001)',
|
||||
},
|
||||
customFieldValue: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Value for the custom field',
|
||||
},
|
||||
notifyUsers: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to send email notifications about this update (default: true)',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
@@ -83,17 +120,22 @@ export const jiraUpdateTool: ToolConfig<JiraUpdateParams, JiraUpdateResponse> =
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
// Pass all parameters to the internal API route
|
||||
return {
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
issueKey: params.issueKey,
|
||||
summary: params.summary,
|
||||
title: params.title, // Support both for backwards compatibility
|
||||
description: params.description,
|
||||
status: params.status,
|
||||
priority: params.priority,
|
||||
assignee: params.assignee,
|
||||
labels: params.labels,
|
||||
components: params.components,
|
||||
duedate: params.duedate,
|
||||
fixVersions: params.fixVersions,
|
||||
environment: params.environment,
|
||||
customFieldId: params.customFieldId,
|
||||
customFieldValue: params.customFieldValue,
|
||||
notifyUsers: params.notifyUsers,
|
||||
cloudId: params.cloudId,
|
||||
}
|
||||
},
|
||||
@@ -116,12 +158,10 @@ export const jiraUpdateTool: ToolConfig<JiraUpdateParams, JiraUpdateResponse> =
|
||||
|
||||
const data = JSON.parse(responseText)
|
||||
|
||||
// The internal API route already returns the correct format
|
||||
if (data.success && data.output) {
|
||||
return data
|
||||
}
|
||||
|
||||
// Fallback for unexpected response format
|
||||
return {
|
||||
success: data.success || false,
|
||||
output: data.output || {
|
||||
@@ -135,7 +175,7 @@ export const jiraUpdateTool: ToolConfig<JiraUpdateParams, JiraUpdateResponse> =
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Updated issue key (e.g., PROJ-123)' },
|
||||
summary: { type: 'string', description: 'Issue summary after update' },
|
||||
},
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
import type { JiraUpdateCommentParams, JiraUpdateCommentResponse } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import { TIMESTAMP_OUTPUT, USER_OUTPUT_PROPERTIES } from '@/tools/jira/types'
|
||||
import { extractAdfText, getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Transforms an update comment API response into typed output.
|
||||
*/
|
||||
function transformUpdateCommentResponse(data: any, params: JiraUpdateCommentParams) {
|
||||
return {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params.issueKey ?? 'unknown',
|
||||
commentId: data?.id ?? params.commentId ?? 'unknown',
|
||||
body: data?.body ? (extractAdfText(data.body) ?? params.body ?? '') : (params.body ?? ''),
|
||||
author: transformUser(data?.author) ?? { accountId: '', displayName: '' },
|
||||
created: data?.created ?? '',
|
||||
updated: data?.updated ?? '',
|
||||
success: true,
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraUpdateCommentTool: ToolConfig<JiraUpdateCommentParams, JiraUpdateCommentResponse> =
|
||||
{
|
||||
id: 'jira_update_comment',
|
||||
@@ -45,6 +62,13 @@ export const jiraUpdateCommentTool: ToolConfig<JiraUpdateCommentParams, JiraUpda
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Updated comment text',
|
||||
},
|
||||
visibility: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Restrict comment visibility. Object with "type" ("role" or "group") and "value" (role/group name).',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -71,55 +95,48 @@ export const jiraUpdateCommentTool: ToolConfig<JiraUpdateCommentParams, JiraUpda
|
||||
},
|
||||
body: (params: JiraUpdateCommentParams) => {
|
||||
if (!params.cloudId) return undefined as any
|
||||
return {
|
||||
const payload: Record<string, any> = {
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params.body,
|
||||
},
|
||||
],
|
||||
content: [{ type: 'text', text: params.body }],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
if (params.visibility) payload.visibility = params.visibility
|
||||
return payload
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraUpdateCommentParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const commentUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/comment/${params?.commentId}`
|
||||
const payload: Record<string, any> = {
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [{ type: 'text', text: params?.body ?? '' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
if (params?.visibility) payload.visibility = params.visibility
|
||||
|
||||
const makeRequest = async (cloudId: string) => {
|
||||
const commentUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/comment/${params!.commentId}`
|
||||
const commentResponse = await fetch(commentUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params?.body,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
if (!commentResponse.ok) {
|
||||
@@ -131,48 +148,46 @@ export const jiraUpdateCommentTool: ToolConfig<JiraUpdateCommentParams, JiraUpda
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await commentResponse.json()
|
||||
return commentResponse.json()
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
commentId: data?.id || params?.commentId || 'unknown',
|
||||
body: params?.body || '',
|
||||
success: true,
|
||||
},
|
||||
let data: any
|
||||
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
data = await makeRequest(cloudId)
|
||||
} else {
|
||||
if (!response.ok) {
|
||||
let message = `Failed to update comment on Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
data = await response.json()
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to update comment on Jira issue (${response.status})`
|
||||
try {
|
||||
const err = await response.json()
|
||||
message = err?.errorMessages?.join(', ') || err?.message || message
|
||||
} catch (_e) {}
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
commentId: data?.id || params?.commentId || 'unknown',
|
||||
body: params?.body || '',
|
||||
success: true,
|
||||
},
|
||||
output: transformUpdateCommentResponse(data, params!),
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
commentId: { type: 'string', description: 'Updated comment ID' },
|
||||
body: { type: 'string', description: 'Updated comment text' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Comment author',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
created: { type: 'string', description: 'ISO 8601 timestamp when the comment was created' },
|
||||
updated: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the comment was last updated',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,51 @@
|
||||
import type { JiraUpdateWorklogParams, JiraUpdateWorklogResponse } from '@/tools/jira/types'
|
||||
import { getJiraCloudId } from '@/tools/jira/utils'
|
||||
import { TIMESTAMP_OUTPUT, USER_OUTPUT_PROPERTIES } from '@/tools/jira/types'
|
||||
import { extractAdfText, getJiraCloudId, transformUser } from '@/tools/jira/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
function buildWorklogBody(params: JiraUpdateWorklogParams) {
|
||||
const body: Record<string, any> = {
|
||||
timeSpentSeconds: params.timeSpentSeconds ? Number(params.timeSpentSeconds) : undefined,
|
||||
comment: params.comment
|
||||
? {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params.comment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
started: params.started ? params.started.replace(/Z$/, '+0000') : undefined,
|
||||
}
|
||||
if (params.visibility) body.visibility = params.visibility
|
||||
return body
|
||||
}
|
||||
|
||||
function transformWorklogResponse(data: any, params: JiraUpdateWorklogParams) {
|
||||
return {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params.issueKey || 'unknown',
|
||||
worklogId: data?.id || params.worklogId || 'unknown',
|
||||
timeSpent: data?.timeSpent ?? null,
|
||||
timeSpentSeconds: data?.timeSpentSeconds ?? null,
|
||||
comment: data?.comment ? extractAdfText(data.comment) : null,
|
||||
author: data?.author ? transformUser(data.author) : null,
|
||||
updateAuthor: data?.updateAuthor ? transformUser(data.updateAuthor) : null,
|
||||
started: data?.started || null,
|
||||
created: data?.created || null,
|
||||
updated: data?.updated || null,
|
||||
success: true,
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraUpdateWorklogTool: ToolConfig<JiraUpdateWorklogParams, JiraUpdateWorklogResponse> =
|
||||
{
|
||||
id: 'jira_update_worklog',
|
||||
@@ -57,6 +101,13 @@ export const jiraUpdateWorklogTool: ToolConfig<JiraUpdateWorklogParams, JiraUpda
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Optional start time in ISO format',
|
||||
},
|
||||
visibility: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Restrict worklog visibility. Object with "type" ("role" or "group") and "value" (role/group name).',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -83,63 +134,22 @@ export const jiraUpdateWorklogTool: ToolConfig<JiraUpdateWorklogParams, JiraUpda
|
||||
},
|
||||
body: (params: JiraUpdateWorklogParams) => {
|
||||
if (!params.cloudId) return undefined as any
|
||||
return {
|
||||
timeSpentSeconds: Number(params.timeSpentSeconds),
|
||||
comment: params.comment
|
||||
? {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params.comment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
started: params.started,
|
||||
}
|
||||
return buildWorklogBody(params)
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: JiraUpdateWorklogParams) => {
|
||||
if (!params?.cloudId) {
|
||||
const cloudId = await getJiraCloudId(params!.domain, params!.accessToken)
|
||||
// Make the actual request with the resolved cloudId
|
||||
const worklogUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params?.issueKey}/worklog/${params?.worklogId}`
|
||||
const worklogUrl = `https://api.atlassian.com/ex/jira/${cloudId}/rest/api/3/issue/${params!.issueKey}/worklog/${params!.worklogId}`
|
||||
const worklogResponse = await fetch(worklogUrl, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params?.accessToken}`,
|
||||
Authorization: `Bearer ${params!.accessToken}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
timeSpentSeconds: params?.timeSpentSeconds ? Number(params.timeSpentSeconds) : 0,
|
||||
comment: params?.comment
|
||||
? {
|
||||
type: 'doc',
|
||||
version: 1,
|
||||
content: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: params.comment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
started: params?.started,
|
||||
}),
|
||||
body: JSON.stringify(buildWorklogBody(params!)),
|
||||
})
|
||||
|
||||
if (!worklogResponse.ok) {
|
||||
@@ -152,19 +162,12 @@ export const jiraUpdateWorklogTool: ToolConfig<JiraUpdateWorklogParams, JiraUpda
|
||||
}
|
||||
|
||||
const data = await worklogResponse.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
worklogId: data?.id || params?.worklogId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
output: transformWorklogResponse(data, params!),
|
||||
}
|
||||
}
|
||||
|
||||
// If cloudId was provided, process the response
|
||||
if (!response.ok) {
|
||||
let message = `Failed to update worklog on Jira issue (${response.status})`
|
||||
try {
|
||||
@@ -175,21 +178,31 @@ export const jiraUpdateWorklogTool: ToolConfig<JiraUpdateWorklogParams, JiraUpda
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: params?.issueKey || 'unknown',
|
||||
worklogId: data?.id || params?.worklogId || 'unknown',
|
||||
success: true,
|
||||
},
|
||||
output: transformWorklogResponse(data, params!),
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
issueKey: { type: 'string', description: 'Issue key' },
|
||||
worklogId: { type: 'string', description: 'Updated worklog ID' },
|
||||
timeSpent: { type: 'string', description: 'Human-readable time spent (e.g., "3h 20m")' },
|
||||
timeSpentSeconds: { type: 'number', description: 'Time spent in seconds' },
|
||||
comment: { type: 'string', description: 'Worklog comment text' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Worklog author',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
updateAuthor: {
|
||||
type: 'object',
|
||||
description: 'User who last updated the worklog',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
started: { type: 'string', description: 'Worklog start time in ISO format' },
|
||||
created: { type: 'string', description: 'Worklog creation time' },
|
||||
updated: { type: 'string', description: 'Worklog last update time' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,43 @@
|
||||
/**
|
||||
* Extracts plain text from Atlassian Document Format (ADF) content.
|
||||
* Returns null if content is falsy.
|
||||
*/
|
||||
export function extractAdfText(content: any): string | null {
|
||||
if (!content) return null
|
||||
if (typeof content === 'string') return content
|
||||
if (Array.isArray(content)) {
|
||||
return content.map(extractAdfText).filter(Boolean).join(' ')
|
||||
}
|
||||
if (content.type === 'text') return content.text || ''
|
||||
if (content.content) return extractAdfText(content.content)
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a raw Jira API user object into a typed user output.
|
||||
* Returns null if user data is falsy.
|
||||
*/
|
||||
export function transformUser(user: any): {
|
||||
accountId: string
|
||||
displayName: string
|
||||
active?: boolean
|
||||
emailAddress?: string
|
||||
avatarUrl?: string
|
||||
accountType?: string
|
||||
timeZone?: string
|
||||
} | null {
|
||||
if (!user) return null
|
||||
return {
|
||||
accountId: user.accountId ?? '',
|
||||
displayName: user.displayName ?? '',
|
||||
active: user.active ?? null,
|
||||
emailAddress: user.emailAddress ?? null,
|
||||
avatarUrl: user.avatarUrls?.['48x48'] ?? null,
|
||||
accountType: user.accountType ?? null,
|
||||
timeZone: user.timeZone ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getJiraCloudId(domain: string, accessToken: string): Promise<string> {
|
||||
const response = await fetch('https://api.atlassian.com/oauth/token/accessible-resources', {
|
||||
method: 'GET',
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { JiraWriteParams, JiraWriteResponse } from '@/tools/jira/types'
|
||||
import { TIMESTAMP_OUTPUT } from '@/tools/jira/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
id: 'jira_write',
|
||||
name: 'Jira Write',
|
||||
description: 'Write a Jira issue',
|
||||
description: 'Create a new Jira issue',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
@@ -65,8 +66,14 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
issueType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'Type of issue to create (e.g., Task, Story)',
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Type of issue to create (e.g., Task, Story, Bug, Epic, Sub-task)',
|
||||
},
|
||||
parent: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Parent issue key for creating subtasks (e.g., { "key": "PROJ-123" })',
|
||||
},
|
||||
labels: {
|
||||
type: 'array',
|
||||
@@ -74,12 +81,24 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Labels for the issue (array of label names)',
|
||||
},
|
||||
components: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Components for the issue (array of component names)',
|
||||
},
|
||||
duedate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Due date for the issue (format: YYYY-MM-DD)',
|
||||
},
|
||||
fixVersions: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Fix versions for the issue (array of version names)',
|
||||
},
|
||||
reporter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -113,7 +132,6 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
// Pass all parameters to the internal API route
|
||||
return {
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
@@ -126,7 +144,9 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
issueType: params.issueType,
|
||||
parent: params.parent,
|
||||
labels: params.labels,
|
||||
components: params.components,
|
||||
duedate: params.duedate,
|
||||
fixVersions: params.fixVersions,
|
||||
reporter: params.reporter,
|
||||
environment: params.environment,
|
||||
customFieldId: params.customFieldId,
|
||||
@@ -143,7 +163,9 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
success: true,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
id: '',
|
||||
issueKey: 'unknown',
|
||||
self: '',
|
||||
summary: 'Issue created successfully',
|
||||
success: true,
|
||||
url: '',
|
||||
@@ -153,29 +175,42 @@ export const jiraWriteTool: ToolConfig<JiraWriteParams, JiraWriteResponse> = {
|
||||
|
||||
const data = JSON.parse(responseText)
|
||||
|
||||
// The internal API route already returns the correct format
|
||||
if (data.success && data.output) {
|
||||
return data
|
||||
return {
|
||||
success: data.success,
|
||||
output: {
|
||||
ts: data.output.ts ?? new Date().toISOString(),
|
||||
id: data.output.id ?? '',
|
||||
issueKey: data.output.issueKey ?? 'unknown',
|
||||
self: data.output.self ?? '',
|
||||
summary: data.output.summary ?? '',
|
||||
success: data.output.success ?? true,
|
||||
url: data.output.url ?? '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback for unexpected response format
|
||||
return {
|
||||
success: data.success || false,
|
||||
output: data.output || {
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
issueKey: 'unknown',
|
||||
summary: 'Issue created',
|
||||
id: data.output?.id ?? '',
|
||||
issueKey: data.output?.issueKey ?? 'unknown',
|
||||
self: data.output?.self ?? '',
|
||||
summary: data.output?.summary ?? 'Issue created',
|
||||
success: false,
|
||||
url: data.output?.url ?? '',
|
||||
},
|
||||
error: data.error,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
ts: TIMESTAMP_OUTPUT,
|
||||
id: { type: 'string', description: 'Created issue ID' },
|
||||
issueKey: { type: 'string', description: 'Created issue key (e.g., PROJ-123)' },
|
||||
self: { type: 'string', description: 'REST API URL for the created issue' },
|
||||
summary: { type: 'string', description: 'Issue summary' },
|
||||
url: { type: 'string', description: 'URL to the created issue' },
|
||||
assigneeId: { type: 'string', description: 'Account ID of the assigned user (if assigned)' },
|
||||
url: { type: 'string', description: 'URL to the created issue in Jira' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmAddCommentParams, JsmAddCommentResponse } from '@/tools/jsm/types'
|
||||
import { USER_OUTPUT_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmAddCommentTool: ToolConfig<JsmAddCommentParams, JsmAddCommentResponse> = {
|
||||
@@ -79,6 +80,8 @@ export const jsmAddCommentTool: ToolConfig<JsmAddCommentParams, JsmAddCommentRes
|
||||
commentId: '',
|
||||
body: '',
|
||||
isPublic: false,
|
||||
author: null,
|
||||
createdDate: null,
|
||||
success: false,
|
||||
},
|
||||
error: 'Empty response from API',
|
||||
@@ -99,6 +102,8 @@ export const jsmAddCommentTool: ToolConfig<JsmAddCommentParams, JsmAddCommentRes
|
||||
commentId: '',
|
||||
body: '',
|
||||
isPublic: false,
|
||||
author: null,
|
||||
createdDate: null,
|
||||
success: false,
|
||||
},
|
||||
error: data.error,
|
||||
@@ -111,6 +116,17 @@ export const jsmAddCommentTool: ToolConfig<JsmAddCommentParams, JsmAddCommentRes
|
||||
commentId: { type: 'string', description: 'Created comment ID' },
|
||||
body: { type: 'string', description: 'Comment body text' },
|
||||
isPublic: { type: 'boolean', description: 'Whether the comment is public' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Comment author',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
optional: true,
|
||||
},
|
||||
createdDate: {
|
||||
type: 'json',
|
||||
description: 'Comment creation date with iso8601, friendly, epochMillis',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Whether the comment was added successfully' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@ export const jsmAddCustomerTool: ToolConfig<JsmAddCustomerParams, JsmAddCustomer
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Service Desk ID (e.g., "1", "2")',
|
||||
},
|
||||
emails: {
|
||||
accountIds: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated email addresses to add as customers',
|
||||
description: 'Comma-separated Atlassian account IDs to add as customers',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -56,7 +56,7 @@ export const jsmAddCustomerTool: ToolConfig<JsmAddCustomerParams, JsmAddCustomer
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
serviceDeskId: params.serviceDeskId,
|
||||
emails: params.emails,
|
||||
accountIds: params.accountIds,
|
||||
}),
|
||||
},
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmAddParticipantsParams, JsmAddParticipantsResponse } from '@/tools/jsm/types'
|
||||
import { PARTICIPANT_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmAddParticipantsTool: ToolConfig<
|
||||
@@ -101,7 +102,14 @@ export const jsmAddParticipantsTool: ToolConfig<
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
issueIdOrKey: { type: 'string', description: 'Issue ID or key' },
|
||||
participants: { type: 'json', description: 'Array of added participants' },
|
||||
participants: {
|
||||
type: 'array',
|
||||
description: 'List of added participants',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: PARTICIPANT_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
success: { type: 'boolean', description: 'Whether the operation succeeded' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ export const jsmAnswerApprovalTool: ToolConfig<JsmAnswerApprovalParams, JsmAnswe
|
||||
issueIdOrKey: '',
|
||||
approvalId: '',
|
||||
decision: '',
|
||||
finalDecision: null,
|
||||
approvers: null,
|
||||
success: false,
|
||||
},
|
||||
error: 'Empty response from API',
|
||||
@@ -99,6 +101,8 @@ export const jsmAnswerApprovalTool: ToolConfig<JsmAnswerApprovalParams, JsmAnswe
|
||||
issueIdOrKey: '',
|
||||
approvalId: '',
|
||||
decision: '',
|
||||
finalDecision: null,
|
||||
approvers: null,
|
||||
success: false,
|
||||
},
|
||||
error: data.error,
|
||||
@@ -110,6 +114,45 @@ export const jsmAnswerApprovalTool: ToolConfig<JsmAnswerApprovalParams, JsmAnswe
|
||||
issueIdOrKey: { type: 'string', description: 'Issue ID or key' },
|
||||
approvalId: { type: 'string', description: 'Approval ID' },
|
||||
decision: { type: 'string', description: 'Decision made (approve/decline)' },
|
||||
id: { type: 'string', description: 'Approval ID from response', optional: true },
|
||||
name: { type: 'string', description: 'Approval description', optional: true },
|
||||
finalDecision: {
|
||||
type: 'string',
|
||||
description: 'Final approval decision: pending, approved, or declined',
|
||||
optional: true,
|
||||
},
|
||||
canAnswerApproval: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the current user can still respond',
|
||||
optional: true,
|
||||
},
|
||||
approvers: {
|
||||
type: 'array',
|
||||
description: 'Updated list of approvers with decisions',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
approver: {
|
||||
type: 'object',
|
||||
description: 'Approver user details',
|
||||
properties: {
|
||||
accountId: { type: 'string', description: 'Approver account ID' },
|
||||
displayName: { type: 'string', description: 'Approver display name' },
|
||||
emailAddress: { type: 'string', description: 'Approver email', optional: true },
|
||||
active: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the account is active',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
approverDecision: { type: 'string', description: 'Individual approver decision' },
|
||||
},
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
createdDate: { type: 'json', description: 'Approval creation date', optional: true },
|
||||
completedDate: { type: 'json', description: 'Approval completion date', optional: true },
|
||||
success: { type: 'boolean', description: 'Whether the operation succeeded' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -61,6 +61,25 @@ export const jsmCreateRequestTool: ToolConfig<JsmCreateRequestParams, JsmCreateR
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Account ID of customer to raise request on behalf of',
|
||||
},
|
||||
requestFieldValues: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Custom field values as key-value pairs (overrides summary/description if provided)',
|
||||
},
|
||||
requestParticipants: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated account IDs to add as request participants',
|
||||
},
|
||||
channel: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Channel the request originates from (e.g., portal, email)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -79,6 +98,8 @@ export const jsmCreateRequestTool: ToolConfig<JsmCreateRequestParams, JsmCreateR
|
||||
description: params.description,
|
||||
raiseOnBehalfOf: params.raiseOnBehalfOf,
|
||||
requestFieldValues: params.requestFieldValues,
|
||||
requestParticipants: params.requestParticipants,
|
||||
channel: params.channel,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -94,6 +115,9 @@ export const jsmCreateRequestTool: ToolConfig<JsmCreateRequestParams, JsmCreateR
|
||||
issueKey: '',
|
||||
requestTypeId: '',
|
||||
serviceDeskId: '',
|
||||
createdDate: null,
|
||||
currentStatus: null,
|
||||
reporter: null,
|
||||
success: false,
|
||||
url: '',
|
||||
},
|
||||
@@ -115,6 +139,9 @@ export const jsmCreateRequestTool: ToolConfig<JsmCreateRequestParams, JsmCreateR
|
||||
issueKey: '',
|
||||
requestTypeId: '',
|
||||
serviceDeskId: '',
|
||||
createdDate: null,
|
||||
currentStatus: null,
|
||||
reporter: null,
|
||||
success: false,
|
||||
url: '',
|
||||
},
|
||||
@@ -128,6 +155,21 @@ export const jsmCreateRequestTool: ToolConfig<JsmCreateRequestParams, JsmCreateR
|
||||
issueKey: { type: 'string', description: 'Created request issue key (e.g., SD-123)' },
|
||||
requestTypeId: { type: 'string', description: 'Request type ID' },
|
||||
serviceDeskId: { type: 'string', description: 'Service desk ID' },
|
||||
createdDate: {
|
||||
type: 'json',
|
||||
description: 'Creation date with iso8601, friendly, epochMillis',
|
||||
optional: true,
|
||||
},
|
||||
currentStatus: {
|
||||
type: 'json',
|
||||
description: 'Current status with status name and category',
|
||||
optional: true,
|
||||
},
|
||||
reporter: {
|
||||
type: 'json',
|
||||
description: 'Reporter user with accountId, displayName, emailAddress',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Whether the request was created successfully' },
|
||||
url: { type: 'string', description: 'URL to the created request' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetApprovalsParams, JsmGetApprovalsResponse } from '@/tools/jsm/types'
|
||||
import { APPROVAL_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetApprovalsTool: ToolConfig<JsmGetApprovalsParams, JsmGetApprovalsResponse> = {
|
||||
@@ -107,7 +108,14 @@ export const jsmGetApprovalsTool: ToolConfig<JsmGetApprovalsParams, JsmGetApprov
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
issueIdOrKey: { type: 'string', description: 'Issue ID or key' },
|
||||
approvals: { type: 'json', description: 'Array of approvals' },
|
||||
approvals: {
|
||||
type: 'array',
|
||||
description: 'List of approvals',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: APPROVAL_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of approvals' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetCommentsParams, JsmGetCommentsResponse } from '@/tools/jsm/types'
|
||||
import { COMMENT_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetCommentsTool: ToolConfig<JsmGetCommentsParams, JsmGetCommentsResponse> = {
|
||||
@@ -49,6 +50,12 @@ export const jsmGetCommentsTool: ToolConfig<JsmGetCommentsParams, JsmGetComments
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter to only internal comments (true/false)',
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated fields to expand: renderedBody, attachment',
|
||||
},
|
||||
start: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
@@ -76,6 +83,7 @@ export const jsmGetCommentsTool: ToolConfig<JsmGetCommentsParams, JsmGetComments
|
||||
issueIdOrKey: params.issueIdOrKey,
|
||||
isPublic: params.isPublic,
|
||||
internal: params.internal,
|
||||
expand: params.expand,
|
||||
start: params.start,
|
||||
limit: params.limit,
|
||||
}),
|
||||
@@ -120,7 +128,14 @@ export const jsmGetCommentsTool: ToolConfig<JsmGetCommentsParams, JsmGetComments
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
issueIdOrKey: { type: 'string', description: 'Issue ID or key' },
|
||||
comments: { type: 'json', description: 'Array of comments' },
|
||||
comments: {
|
||||
type: 'array',
|
||||
description: 'List of comments',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: COMMENT_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of comments' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetCustomersParams, JsmGetCustomersResponse } from '@/tools/jsm/types'
|
||||
import { CUSTOMER_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetCustomersTool: ToolConfig<JsmGetCustomersParams, JsmGetCustomersResponse> = {
|
||||
@@ -110,7 +111,14 @@ export const jsmGetCustomersTool: ToolConfig<JsmGetCustomersParams, JsmGetCustom
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
customers: { type: 'json', description: 'Array of customers' },
|
||||
customers: {
|
||||
type: 'array',
|
||||
description: 'List of customers',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: CUSTOMER_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of customers' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetOrganizationsParams, JsmGetOrganizationsResponse } from '@/tools/jsm/types'
|
||||
import { ORGANIZATION_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetOrganizationsTool: ToolConfig<
|
||||
@@ -106,7 +107,14 @@ export const jsmGetOrganizationsTool: ToolConfig<
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
organizations: { type: 'json', description: 'Array of organizations' },
|
||||
organizations: {
|
||||
type: 'array',
|
||||
description: 'List of organizations',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: ORGANIZATION_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of organizations' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetParticipantsParams, JsmGetParticipantsResponse } from '@/tools/jsm/types'
|
||||
import { PARTICIPANT_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetParticipantsTool: ToolConfig<
|
||||
@@ -110,7 +111,14 @@ export const jsmGetParticipantsTool: ToolConfig<
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
issueIdOrKey: { type: 'string', description: 'Issue ID or key' },
|
||||
participants: { type: 'json', description: 'Array of participants' },
|
||||
participants: {
|
||||
type: 'array',
|
||||
description: 'List of participants',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: PARTICIPANT_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of participants' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetQueuesParams, JsmGetQueuesResponse } from '@/tools/jsm/types'
|
||||
import { QUEUE_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetQueuesTool: ToolConfig<JsmGetQueuesParams, JsmGetQueuesResponse> = {
|
||||
@@ -110,7 +111,14 @@ export const jsmGetQueuesTool: ToolConfig<JsmGetQueuesParams, JsmGetQueuesRespon
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
queues: { type: 'json', description: 'Array of queues' },
|
||||
queues: {
|
||||
type: 'array',
|
||||
description: 'List of queues',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: QUEUE_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of queues' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import type { JsmGetRequestParams, JsmGetRequestResponse } from '@/tools/jsm/types'
|
||||
import {
|
||||
REQUEST_FIELD_VALUE_PROPERTIES,
|
||||
REQUEST_STATUS_PROPERTIES,
|
||||
USER_OUTPUT_PROPERTIES,
|
||||
} from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetRequestTool: ToolConfig<JsmGetRequestParams, JsmGetRequestResponse> = {
|
||||
@@ -37,6 +42,13 @@ export const jsmGetRequestTool: ToolConfig<JsmGetRequestParams, JsmGetRequestRes
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue ID or key (e.g., SD-123)',
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated fields to expand: participant, status, sla, requestType, serviceDesk, attachment, comment, action',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -50,6 +62,7 @@ export const jsmGetRequestTool: ToolConfig<JsmGetRequestParams, JsmGetRequestRes
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
issueIdOrKey: params.issueIdOrKey,
|
||||
expand: params.expand,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -61,7 +74,15 @@ export const jsmGetRequestTool: ToolConfig<JsmGetRequestParams, JsmGetRequestRes
|
||||
success: false,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
request: null,
|
||||
issueId: '',
|
||||
issueKey: '',
|
||||
requestTypeId: '',
|
||||
serviceDeskId: '',
|
||||
createdDate: null,
|
||||
currentStatus: null,
|
||||
reporter: null,
|
||||
requestFieldValues: [],
|
||||
url: '',
|
||||
},
|
||||
error: 'Empty response from API',
|
||||
}
|
||||
@@ -77,7 +98,15 @@ export const jsmGetRequestTool: ToolConfig<JsmGetRequestParams, JsmGetRequestRes
|
||||
success: data.success || false,
|
||||
output: data.output || {
|
||||
ts: new Date().toISOString(),
|
||||
request: null,
|
||||
issueId: '',
|
||||
issueKey: '',
|
||||
requestTypeId: '',
|
||||
serviceDeskId: '',
|
||||
createdDate: null,
|
||||
currentStatus: null,
|
||||
reporter: null,
|
||||
requestFieldValues: [],
|
||||
url: '',
|
||||
},
|
||||
error: data.error,
|
||||
}
|
||||
@@ -85,6 +114,35 @@ export const jsmGetRequestTool: ToolConfig<JsmGetRequestParams, JsmGetRequestRes
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
request: { type: 'json', description: 'The service request object' },
|
||||
issueId: { type: 'string', description: 'Jira issue ID' },
|
||||
issueKey: { type: 'string', description: 'Issue key (e.g., SD-123)' },
|
||||
requestTypeId: { type: 'string', description: 'Request type ID' },
|
||||
serviceDeskId: { type: 'string', description: 'Service desk ID' },
|
||||
createdDate: {
|
||||
type: 'json',
|
||||
description: 'Creation date with iso8601, friendly, epochMillis',
|
||||
optional: true,
|
||||
},
|
||||
currentStatus: {
|
||||
type: 'object',
|
||||
description: 'Current request status',
|
||||
properties: REQUEST_STATUS_PROPERTIES,
|
||||
optional: true,
|
||||
},
|
||||
reporter: {
|
||||
type: 'object',
|
||||
description: 'Reporter user details',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
optional: true,
|
||||
},
|
||||
requestFieldValues: {
|
||||
type: 'array',
|
||||
description: 'Request field values',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: REQUEST_FIELD_VALUE_PROPERTIES,
|
||||
},
|
||||
},
|
||||
url: { type: 'string', description: 'URL to the request' },
|
||||
},
|
||||
}
|
||||
|
||||
130
apps/sim/tools/jsm/get_request_type_fields.ts
Normal file
130
apps/sim/tools/jsm/get_request_type_fields.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type {
|
||||
JsmGetRequestTypeFieldsParams,
|
||||
JsmGetRequestTypeFieldsResponse,
|
||||
} from '@/tools/jsm/types'
|
||||
import { REQUEST_TYPE_FIELD_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetRequestTypeFieldsTool: ToolConfig<
|
||||
JsmGetRequestTypeFieldsParams,
|
||||
JsmGetRequestTypeFieldsResponse
|
||||
> = {
|
||||
id: 'jsm_get_request_type_fields',
|
||||
name: 'JSM Get Request Type Fields',
|
||||
description:
|
||||
'Get the fields required to create a request of a specific type in Jira Service Management',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: {
|
||||
required: true,
|
||||
provider: 'jira',
|
||||
},
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Jira Service Management',
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Your Jira domain (e.g., yourcompany.atlassian.net)',
|
||||
},
|
||||
cloudId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Jira Cloud ID for the instance',
|
||||
},
|
||||
serviceDeskId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Service Desk ID (e.g., "1", "2")',
|
||||
},
|
||||
requestTypeId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Request Type ID (e.g., "10", "15")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/jsm/requesttypefields',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
serviceDeskId: params.serviceDeskId,
|
||||
requestTypeId: params.requestTypeId,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const responseText = await response.text()
|
||||
|
||||
if (!responseText) {
|
||||
return {
|
||||
success: false,
|
||||
output: {
|
||||
ts: new Date().toISOString(),
|
||||
serviceDeskId: '',
|
||||
requestTypeId: '',
|
||||
canAddRequestParticipants: false,
|
||||
canRaiseOnBehalfOf: false,
|
||||
requestTypeFields: [],
|
||||
},
|
||||
error: 'Empty response from API',
|
||||
}
|
||||
}
|
||||
|
||||
const data = JSON.parse(responseText)
|
||||
|
||||
if (data.success && data.output) {
|
||||
return data
|
||||
}
|
||||
|
||||
return {
|
||||
success: data.success || false,
|
||||
output: data.output || {
|
||||
ts: new Date().toISOString(),
|
||||
serviceDeskId: '',
|
||||
requestTypeId: '',
|
||||
canAddRequestParticipants: false,
|
||||
canRaiseOnBehalfOf: false,
|
||||
requestTypeFields: [],
|
||||
},
|
||||
error: data.error,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
serviceDeskId: { type: 'string', description: 'Service desk ID' },
|
||||
requestTypeId: { type: 'string', description: 'Request type ID' },
|
||||
canAddRequestParticipants: {
|
||||
type: 'boolean',
|
||||
description: 'Whether participants can be added to requests of this type',
|
||||
},
|
||||
canRaiseOnBehalfOf: {
|
||||
type: 'boolean',
|
||||
description: 'Whether requests can be raised on behalf of another user',
|
||||
},
|
||||
requestTypeFields: {
|
||||
type: 'array',
|
||||
description: 'List of fields for this request type',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: REQUEST_TYPE_FIELD_PROPERTIES,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetRequestTypesParams, JsmGetRequestTypesResponse } from '@/tools/jsm/types'
|
||||
import { REQUEST_TYPE_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetRequestTypesTool: ToolConfig<
|
||||
@@ -40,6 +41,24 @@ export const jsmGetRequestTypesTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Service Desk ID (e.g., "1", "2")',
|
||||
},
|
||||
searchQuery: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter request types by name',
|
||||
},
|
||||
groupId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by request type group ID',
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated fields to expand in the response',
|
||||
},
|
||||
start: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
@@ -65,6 +84,9 @@ export const jsmGetRequestTypesTool: ToolConfig<
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
serviceDeskId: params.serviceDeskId,
|
||||
searchQuery: params.searchQuery,
|
||||
groupId: params.groupId,
|
||||
expand: params.expand,
|
||||
start: params.start,
|
||||
limit: params.limit,
|
||||
}),
|
||||
@@ -106,7 +128,14 @@ export const jsmGetRequestTypesTool: ToolConfig<
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
requestTypes: { type: 'json', description: 'Array of request types' },
|
||||
requestTypes: {
|
||||
type: 'array',
|
||||
description: 'List of request types',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: REQUEST_TYPE_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of request types' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetRequestsParams, JsmGetRequestsResponse } from '@/tools/jsm/types'
|
||||
import { REQUEST_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetRequestsTool: ToolConfig<JsmGetRequestsParams, JsmGetRequestsResponse> = {
|
||||
@@ -42,13 +43,19 @@ export const jsmGetRequestsTool: ToolConfig<JsmGetRequestsParams, JsmGetRequests
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Filter by ownership: OWNED_REQUESTS, PARTICIPATED_REQUESTS, ORGANIZATION, ALL_REQUESTS',
|
||||
'Filter by ownership: OWNED_REQUESTS, PARTICIPATED_REQUESTS, APPROVER, ALL_REQUESTS',
|
||||
},
|
||||
requestStatus: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by status: OPEN, CLOSED, ALL',
|
||||
description: 'Filter by status: OPEN_REQUESTS, CLOSED_REQUESTS, ALL_REQUESTS',
|
||||
},
|
||||
requestTypeId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by request type ID',
|
||||
},
|
||||
searchTerm: {
|
||||
type: 'string',
|
||||
@@ -56,6 +63,13 @@ export const jsmGetRequestsTool: ToolConfig<JsmGetRequestsParams, JsmGetRequests
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search term to filter requests (e.g., "password reset", "laptop")',
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated fields to expand: participant, status, sla, requestType, serviceDesk, attachment, comment, action',
|
||||
},
|
||||
start: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
@@ -83,7 +97,9 @@ export const jsmGetRequestsTool: ToolConfig<JsmGetRequestsParams, JsmGetRequests
|
||||
serviceDeskId: params.serviceDeskId,
|
||||
requestOwnership: params.requestOwnership,
|
||||
requestStatus: params.requestStatus,
|
||||
requestTypeId: params.requestTypeId,
|
||||
searchTerm: params.searchTerm,
|
||||
expand: params.expand,
|
||||
start: params.start,
|
||||
limit: params.limit,
|
||||
}),
|
||||
@@ -125,8 +141,15 @@ export const jsmGetRequestsTool: ToolConfig<JsmGetRequestsParams, JsmGetRequests
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
requests: { type: 'json', description: 'Array of service requests' },
|
||||
total: { type: 'number', description: 'Total number of requests' },
|
||||
requests: {
|
||||
type: 'array',
|
||||
description: 'List of service requests',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: REQUEST_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of requests in current page' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetServiceDesksParams, JsmGetServiceDesksResponse } from '@/tools/jsm/types'
|
||||
import { SERVICE_DESK_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetServiceDesksTool: ToolConfig<
|
||||
@@ -34,6 +35,12 @@ export const jsmGetServiceDesksTool: ToolConfig<
|
||||
visibility: 'hidden',
|
||||
description: 'Jira Cloud ID for the instance',
|
||||
},
|
||||
expand: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated fields to expand in the response',
|
||||
},
|
||||
start: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
@@ -58,6 +65,7 @@ export const jsmGetServiceDesksTool: ToolConfig<
|
||||
domain: params.domain,
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
expand: params.expand,
|
||||
start: params.start,
|
||||
limit: params.limit,
|
||||
}),
|
||||
@@ -99,7 +107,14 @@ export const jsmGetServiceDesksTool: ToolConfig<
|
||||
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
serviceDesks: { type: 'json', description: 'Array of service desks' },
|
||||
serviceDesks: {
|
||||
type: 'array',
|
||||
description: 'List of service desks',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: SERVICE_DESK_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of service desks' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetSlaParams, JsmGetSlaResponse } from '@/tools/jsm/types'
|
||||
import { SLA_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetSlaTool: ToolConfig<JsmGetSlaParams, JsmGetSlaResponse> = {
|
||||
@@ -106,7 +107,14 @@ export const jsmGetSlaTool: ToolConfig<JsmGetSlaParams, JsmGetSlaResponse> = {
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
issueIdOrKey: { type: 'string', description: 'Issue ID or key' },
|
||||
slas: { type: 'json', description: 'Array of SLA information' },
|
||||
slas: {
|
||||
type: 'array',
|
||||
description: 'List of SLA metrics',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: SLA_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of SLAs' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { JsmGetTransitionsParams, JsmGetTransitionsResponse } from '@/tools/jsm/types'
|
||||
import { TRANSITION_ITEM_PROPERTIES } from '@/tools/jsm/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const jsmGetTransitionsTool: ToolConfig<JsmGetTransitionsParams, JsmGetTransitionsResponse> =
|
||||
@@ -38,6 +39,18 @@ export const jsmGetTransitionsTool: ToolConfig<JsmGetTransitionsParams, JsmGetTr
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Issue ID or key (e.g., SD-123)',
|
||||
},
|
||||
start: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Start index for pagination (e.g., 0, 50, 100)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum results to return (e.g., 10, 25, 50)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
@@ -51,6 +64,8 @@ export const jsmGetTransitionsTool: ToolConfig<JsmGetTransitionsParams, JsmGetTr
|
||||
accessToken: params.accessToken,
|
||||
cloudId: params.cloudId,
|
||||
issueIdOrKey: params.issueIdOrKey,
|
||||
start: params.start,
|
||||
limit: params.limit,
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -64,6 +79,8 @@ export const jsmGetTransitionsTool: ToolConfig<JsmGetTransitionsParams, JsmGetTr
|
||||
ts: new Date().toISOString(),
|
||||
issueIdOrKey: '',
|
||||
transitions: [],
|
||||
total: 0,
|
||||
isLastPage: true,
|
||||
},
|
||||
error: 'Empty response from API',
|
||||
}
|
||||
@@ -81,6 +98,8 @@ export const jsmGetTransitionsTool: ToolConfig<JsmGetTransitionsParams, JsmGetTr
|
||||
ts: new Date().toISOString(),
|
||||
issueIdOrKey: '',
|
||||
transitions: [],
|
||||
total: 0,
|
||||
isLastPage: true,
|
||||
},
|
||||
error: data.error,
|
||||
}
|
||||
@@ -89,6 +108,15 @@ export const jsmGetTransitionsTool: ToolConfig<JsmGetTransitionsParams, JsmGetTr
|
||||
outputs: {
|
||||
ts: { type: 'string', description: 'Timestamp of the operation' },
|
||||
issueIdOrKey: { type: 'string', description: 'Issue ID or key' },
|
||||
transitions: { type: 'json', description: 'Array of available transitions' },
|
||||
transitions: {
|
||||
type: 'array',
|
||||
description: 'List of available transitions',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: TRANSITION_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
total: { type: 'number', description: 'Total number of transitions' },
|
||||
isLastPage: { type: 'boolean', description: 'Whether this is the last page' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { jsmGetOrganizationsTool } from '@/tools/jsm/get_organizations'
|
||||
import { jsmGetParticipantsTool } from '@/tools/jsm/get_participants'
|
||||
import { jsmGetQueuesTool } from '@/tools/jsm/get_queues'
|
||||
import { jsmGetRequestTool } from '@/tools/jsm/get_request'
|
||||
import { jsmGetRequestTypeFieldsTool } from '@/tools/jsm/get_request_type_fields'
|
||||
import { jsmGetRequestTypesTool } from '@/tools/jsm/get_request_types'
|
||||
import { jsmGetRequestsTool } from '@/tools/jsm/get_requests'
|
||||
import { jsmGetServiceDesksTool } from '@/tools/jsm/get_service_desks'
|
||||
@@ -34,6 +35,7 @@ export {
|
||||
jsmGetParticipantsTool,
|
||||
jsmGetQueuesTool,
|
||||
jsmGetRequestTool,
|
||||
jsmGetRequestTypeFieldsTool,
|
||||
jsmGetRequestsTool,
|
||||
jsmGetRequestTypesTool,
|
||||
jsmGetServiceDesksTool,
|
||||
|
||||
@@ -1,5 +1,231 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared output property constants for JSM tools (following Confluence pattern)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Reusable date output properties with ISO 8601, friendly, and epoch formats */
|
||||
export const DATE_OUTPUT_PROPERTIES = {
|
||||
iso8601: { type: 'string', description: 'ISO 8601 formatted date' },
|
||||
friendly: { type: 'string', description: 'Human-readable date' },
|
||||
epochMillis: { type: 'number', description: 'Unix epoch milliseconds' },
|
||||
} as const
|
||||
|
||||
/** Reusable user output properties */
|
||||
export const USER_OUTPUT_PROPERTIES = {
|
||||
accountId: { type: 'string', description: 'Atlassian account ID' },
|
||||
displayName: { type: 'string', description: 'User display name' },
|
||||
emailAddress: { type: 'string', description: 'User email address', optional: true },
|
||||
active: { type: 'boolean', description: 'Whether the account is active' },
|
||||
} as const
|
||||
|
||||
/** Output properties for a service desk item */
|
||||
export const SERVICE_DESK_ITEM_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Service desk ID' },
|
||||
projectId: { type: 'string', description: 'Associated Jira project ID' },
|
||||
projectName: { type: 'string', description: 'Associated project name' },
|
||||
projectKey: { type: 'string', description: 'Associated project key' },
|
||||
name: { type: 'string', description: 'Service desk name' },
|
||||
description: { type: 'string', description: 'Service desk description', optional: true },
|
||||
leadDisplayName: { type: 'string', description: 'Project lead display name', optional: true },
|
||||
} as const
|
||||
|
||||
/** Output properties for a request type item */
|
||||
export const REQUEST_TYPE_ITEM_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Request type ID' },
|
||||
name: { type: 'string', description: 'Request type name' },
|
||||
description: { type: 'string', description: 'Request type description' },
|
||||
helpText: { type: 'string', description: 'Help text for customers', optional: true },
|
||||
issueTypeId: { type: 'string', description: 'Associated Jira issue type ID' },
|
||||
serviceDeskId: { type: 'string', description: 'Parent service desk ID' },
|
||||
groupIds: { type: 'json', description: 'Groups this request type belongs to' },
|
||||
icon: { type: 'json', description: 'Request type icon with id and links', optional: true },
|
||||
restrictionStatus: { type: 'string', description: 'OPEN or RESTRICTED', optional: true },
|
||||
} as const
|
||||
|
||||
/** Output properties for a request field value */
|
||||
export const REQUEST_FIELD_VALUE_PROPERTIES = {
|
||||
fieldId: { type: 'string', description: 'Field identifier' },
|
||||
label: { type: 'string', description: 'Human-readable field label' },
|
||||
value: { type: 'json', description: 'Field value' },
|
||||
renderedValue: { type: 'json', description: 'HTML-rendered field value', optional: true },
|
||||
} as const
|
||||
|
||||
/** Output properties for a request status */
|
||||
export const REQUEST_STATUS_PROPERTIES = {
|
||||
status: { type: 'string', description: 'Status name' },
|
||||
statusCategory: { type: 'string', description: 'Status category (NEW, INDETERMINATE, DONE)' },
|
||||
statusDate: {
|
||||
type: 'json',
|
||||
description: 'Status change date with iso8601, friendly, epochMillis',
|
||||
},
|
||||
} as const
|
||||
|
||||
/** Output properties for a request (ticket) item */
|
||||
export const REQUEST_ITEM_PROPERTIES = {
|
||||
issueId: { type: 'string', description: 'Jira issue ID' },
|
||||
issueKey: { type: 'string', description: 'Issue key (e.g., SD-123)' },
|
||||
requestTypeId: { type: 'string', description: 'Request type ID' },
|
||||
serviceDeskId: { type: 'string', description: 'Service desk ID' },
|
||||
createdDate: {
|
||||
type: 'json',
|
||||
description: 'Creation date with iso8601, friendly, epochMillis',
|
||||
},
|
||||
currentStatus: {
|
||||
type: 'object',
|
||||
description: 'Current request status',
|
||||
properties: REQUEST_STATUS_PROPERTIES,
|
||||
},
|
||||
reporter: {
|
||||
type: 'object',
|
||||
description: 'Reporter user details',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
requestFieldValues: {
|
||||
type: 'array',
|
||||
description: 'Request field values',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: REQUEST_FIELD_VALUE_PROPERTIES,
|
||||
},
|
||||
},
|
||||
} as const
|
||||
|
||||
/** Output properties for a comment item */
|
||||
export const COMMENT_ITEM_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Comment ID' },
|
||||
body: { type: 'string', description: 'Comment body text' },
|
||||
public: { type: 'boolean', description: 'Whether the comment is public' },
|
||||
author: {
|
||||
type: 'object',
|
||||
description: 'Comment author',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
created: {
|
||||
type: 'json',
|
||||
description: 'Creation date with iso8601, friendly, epochMillis',
|
||||
},
|
||||
renderedBody: {
|
||||
type: 'json',
|
||||
description: 'HTML-rendered comment body (when expand=renderedBody)',
|
||||
optional: true,
|
||||
},
|
||||
} as const
|
||||
|
||||
/** Output properties for a queue item */
|
||||
export const QUEUE_ITEM_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Queue ID' },
|
||||
name: { type: 'string', description: 'Queue name' },
|
||||
jql: { type: 'string', description: 'JQL filter for the queue' },
|
||||
fields: { type: 'json', description: 'Fields displayed in the queue' },
|
||||
issueCount: { type: 'number', description: 'Number of issues in the queue' },
|
||||
} as const
|
||||
|
||||
/** Output properties for an SLA item */
|
||||
export const SLA_ITEM_PROPERTIES = {
|
||||
id: { type: 'string', description: 'SLA metric ID' },
|
||||
name: { type: 'string', description: 'SLA metric name' },
|
||||
completedCycles: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Completed SLA cycles with startTime, stopTime, breachTime, breached, goalDuration, elapsedTime, remainingTime (each time as DateDTO, durations as DurationDTO)',
|
||||
},
|
||||
ongoingCycle: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Ongoing SLA cycle with startTime, breachTime, breached, paused, withinCalendarHours, goalDuration, elapsedTime, remainingTime',
|
||||
optional: true,
|
||||
},
|
||||
} as const
|
||||
|
||||
/** Output properties for a transition item */
|
||||
export const TRANSITION_ITEM_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Transition ID' },
|
||||
name: { type: 'string', description: 'Transition name' },
|
||||
} as const
|
||||
|
||||
/** Output properties for a participant item */
|
||||
export const PARTICIPANT_ITEM_PROPERTIES = {
|
||||
accountId: { type: 'string', description: 'Atlassian account ID' },
|
||||
displayName: { type: 'string', description: 'Display name' },
|
||||
emailAddress: { type: 'string', description: 'Email address', optional: true },
|
||||
active: { type: 'boolean', description: 'Whether the account is active' },
|
||||
} as const
|
||||
|
||||
/** Output properties for an organization item */
|
||||
export const ORGANIZATION_ITEM_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Organization ID' },
|
||||
name: { type: 'string', description: 'Organization name' },
|
||||
} as const
|
||||
|
||||
/** Output properties for a customer item */
|
||||
export const CUSTOMER_ITEM_PROPERTIES = {
|
||||
accountId: { type: 'string', description: 'Atlassian account ID' },
|
||||
displayName: { type: 'string', description: 'Display name' },
|
||||
emailAddress: { type: 'string', description: 'Email address' },
|
||||
active: { type: 'boolean', description: 'Whether the account is active' },
|
||||
timeZone: { type: 'string', description: 'User timezone', optional: true },
|
||||
} as const
|
||||
|
||||
/** Output properties for an approver item */
|
||||
export const APPROVER_ITEM_PROPERTIES = {
|
||||
approver: {
|
||||
type: 'object',
|
||||
description: 'Approver user details',
|
||||
properties: USER_OUTPUT_PROPERTIES,
|
||||
},
|
||||
approverDecision: { type: 'string', description: 'Decision: pending, approved, or declined' },
|
||||
} as const
|
||||
|
||||
/** Output properties for an approval item */
|
||||
export const APPROVAL_ITEM_PROPERTIES = {
|
||||
id: { type: 'string', description: 'Approval ID' },
|
||||
name: { type: 'string', description: 'Approval description' },
|
||||
finalDecision: { type: 'string', description: 'Final decision: pending, approved, or declined' },
|
||||
canAnswerApproval: { type: 'boolean', description: 'Whether current user can respond' },
|
||||
approvers: {
|
||||
type: 'array',
|
||||
description: 'List of approvers with their decisions',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: APPROVER_ITEM_PROPERTIES,
|
||||
},
|
||||
},
|
||||
createdDate: {
|
||||
type: 'json',
|
||||
description: 'Creation date',
|
||||
optional: true,
|
||||
},
|
||||
completedDate: {
|
||||
type: 'json',
|
||||
description: 'Completion date',
|
||||
optional: true,
|
||||
},
|
||||
} as const
|
||||
|
||||
/** Output properties for a request type field */
|
||||
export const REQUEST_TYPE_FIELD_PROPERTIES = {
|
||||
fieldId: {
|
||||
type: 'string',
|
||||
description: 'Field identifier (e.g., summary, description, customfield_10010)',
|
||||
},
|
||||
name: { type: 'string', description: 'Human-readable field name' },
|
||||
description: { type: 'string', description: 'Help text for the field', optional: true },
|
||||
required: { type: 'boolean', description: 'Whether the field is required' },
|
||||
visible: { type: 'boolean', description: 'Whether the field is visible' },
|
||||
validValues: { type: 'json', description: 'Allowed values for select fields' },
|
||||
presetValues: { type: 'json', description: 'Pre-populated values', optional: true },
|
||||
defaultValues: { type: 'json', description: 'Default values for the field', optional: true },
|
||||
jiraSchema: {
|
||||
type: 'json',
|
||||
description: 'Jira field schema with type, system, custom, customId',
|
||||
},
|
||||
} as const
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Data model interfaces
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Common parameters for all JSM API calls */
|
||||
export interface JsmBaseParams {
|
||||
accessToken: string
|
||||
@@ -13,6 +239,9 @@ export interface JsmServiceDesk {
|
||||
projectId: string
|
||||
projectName: string
|
||||
projectKey: string
|
||||
name: string
|
||||
description?: string
|
||||
leadDisplayName?: string
|
||||
}
|
||||
|
||||
/** Request Type representation */
|
||||
@@ -21,6 +250,7 @@ export interface JsmRequestType {
|
||||
name: string
|
||||
description: string
|
||||
helpText?: string
|
||||
issueTypeId?: string
|
||||
serviceDeskId: string
|
||||
groupIds: string[]
|
||||
icon: {
|
||||
@@ -60,9 +290,13 @@ export interface JsmSla {
|
||||
id: string
|
||||
name: string
|
||||
completedCycles: Array<{
|
||||
startTime: { iso8601: string }
|
||||
stopTime: { iso8601: string }
|
||||
startTime: { iso8601: string; friendly: string; epochMillis: number }
|
||||
stopTime: { iso8601: string; friendly: string; epochMillis: number }
|
||||
breachTime?: { iso8601: string; friendly: string; epochMillis: number }
|
||||
breached: boolean
|
||||
goalDuration?: { millis: number; friendly: string }
|
||||
elapsedTime?: { millis: number; friendly: string }
|
||||
remainingTime?: { millis: number; friendly: string }
|
||||
}>
|
||||
ongoingCycle?: {
|
||||
startTime: { iso8601: string }
|
||||
@@ -115,11 +349,191 @@ export interface JsmTransition {
|
||||
name: string
|
||||
}
|
||||
|
||||
/** Participant representation */
|
||||
export interface JsmParticipant {
|
||||
accountId: string
|
||||
displayName: string
|
||||
emailAddress?: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
/** Approver representation */
|
||||
export interface JsmApprover {
|
||||
accountId: string
|
||||
displayName: string
|
||||
emailAddress?: string
|
||||
approverDecision: 'pending' | 'approved' | 'declined'
|
||||
}
|
||||
|
||||
/** Approval representation */
|
||||
export interface JsmApproval {
|
||||
id: string
|
||||
name: string
|
||||
finalDecision: 'pending' | 'approved' | 'declined'
|
||||
canAnswerApproval: boolean
|
||||
approvers: JsmApprover[]
|
||||
createdDate?: { iso8601: string; friendly: string }
|
||||
completedDate?: { iso8601: string; friendly: string }
|
||||
}
|
||||
|
||||
/** Request type field representation */
|
||||
export interface JsmRequestTypeField {
|
||||
fieldId: string
|
||||
name: string
|
||||
description?: string
|
||||
required: boolean
|
||||
visible?: boolean
|
||||
validValues: Array<{ value: string; label: string; children?: unknown[] }>
|
||||
presetValues?: unknown[]
|
||||
defaultValues?: unknown[]
|
||||
jiraSchema: { type: string; system?: string; custom?: string; customId?: number }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Params interfaces
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface JsmGetServiceDesksParams extends JsmBaseParams {
|
||||
expand?: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetRequestTypesParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
searchQuery?: string
|
||||
groupId?: string
|
||||
expand?: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmCreateRequestParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
requestTypeId: string
|
||||
summary: string
|
||||
description?: string
|
||||
requestFieldValues?: Record<string, unknown>
|
||||
raiseOnBehalfOf?: string
|
||||
requestParticipants?: string[]
|
||||
channel?: string
|
||||
}
|
||||
|
||||
export interface JsmGetRequestParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
expand?: string
|
||||
}
|
||||
|
||||
export interface JsmGetRequestsParams extends JsmBaseParams {
|
||||
serviceDeskId?: string
|
||||
requestOwnership?: 'OWNED_REQUESTS' | 'PARTICIPATED_REQUESTS' | 'APPROVER' | 'ALL_REQUESTS'
|
||||
requestStatus?: 'OPEN_REQUESTS' | 'CLOSED_REQUESTS' | 'ALL_REQUESTS'
|
||||
requestTypeId?: string
|
||||
searchTerm?: string
|
||||
expand?: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmAddCommentParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
body: string
|
||||
isPublic: boolean
|
||||
}
|
||||
|
||||
export interface JsmGetCommentsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
isPublic?: boolean
|
||||
internal?: boolean
|
||||
expand?: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetCustomersParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
query?: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmAddCustomerParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
accountIds: string
|
||||
}
|
||||
|
||||
export interface JsmGetOrganizationsParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetQueuesParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
includeCount?: boolean
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetSlaParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmTransitionRequestParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
transitionId: string
|
||||
comment?: string
|
||||
}
|
||||
|
||||
export interface JsmGetTransitionsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmCreateOrganizationParams extends JsmBaseParams {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface JsmAddOrganizationParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
organizationId: string
|
||||
}
|
||||
|
||||
export interface JsmGetParticipantsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmAddParticipantsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
accountIds: string
|
||||
}
|
||||
|
||||
export interface JsmGetApprovalsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmAnswerApprovalParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
approvalId: string
|
||||
decision: 'approve' | 'decline'
|
||||
}
|
||||
|
||||
export interface JsmGetRequestTypeFieldsParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
requestTypeId: string
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Response interfaces
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface JsmGetServiceDesksResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -129,12 +543,6 @@ export interface JsmGetServiceDesksResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetRequestTypesParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetRequestTypesResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -144,15 +552,6 @@ export interface JsmGetRequestTypesResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmCreateRequestParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
requestTypeId: string
|
||||
summary: string
|
||||
description?: string
|
||||
requestFieldValues?: Record<string, unknown>
|
||||
raiseOnBehalfOf?: string
|
||||
}
|
||||
|
||||
export interface JsmCreateRequestResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -160,31 +559,42 @@ export interface JsmCreateRequestResponse extends ToolResponse {
|
||||
issueKey: string
|
||||
requestTypeId: string
|
||||
serviceDeskId: string
|
||||
createdDate: { iso8601: string; friendly: string; epochMillis: number } | null
|
||||
currentStatus: {
|
||||
status: string
|
||||
statusCategory: string
|
||||
statusDate?: { iso8601: string; friendly: string }
|
||||
} | null
|
||||
reporter: { accountId: string; displayName: string; emailAddress?: string } | null
|
||||
success: boolean
|
||||
url: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetRequestParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
}
|
||||
|
||||
export interface JsmGetRequestResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
request: JsmRequest
|
||||
issueId: string
|
||||
issueKey: string
|
||||
requestTypeId: string
|
||||
serviceDeskId: string
|
||||
createdDate: { iso8601: string; friendly: string; epochMillis: number } | null
|
||||
currentStatus: {
|
||||
status: string
|
||||
statusCategory: string
|
||||
statusDate: { iso8601: string; friendly: string }
|
||||
} | null
|
||||
reporter: {
|
||||
accountId: string
|
||||
displayName: string
|
||||
emailAddress?: string
|
||||
active: boolean
|
||||
} | null
|
||||
requestFieldValues: Array<{ fieldId: string; label: string; value: unknown }>
|
||||
url: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetRequestsParams extends JsmBaseParams {
|
||||
serviceDeskId?: string
|
||||
requestOwnership?: 'OWNED_REQUESTS' | 'PARTICIPATED_REQUESTS' | 'ORGANIZATION' | 'ALL_REQUESTS'
|
||||
requestStatus?: 'OPEN' | 'CLOSED' | 'ALL'
|
||||
searchTerm?: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetRequestsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -194,12 +604,6 @@ export interface JsmGetRequestsResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmAddCommentParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
body: string
|
||||
isPublic: boolean
|
||||
}
|
||||
|
||||
export interface JsmAddCommentResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -207,18 +611,12 @@ export interface JsmAddCommentResponse extends ToolResponse {
|
||||
commentId: string
|
||||
body: string
|
||||
isPublic: boolean
|
||||
author: { accountId: string; displayName: string; emailAddress?: string } | null
|
||||
createdDate: { iso8601: string; friendly: string } | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetCommentsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
isPublic?: boolean
|
||||
internal?: boolean
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetCommentsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -229,13 +627,6 @@ export interface JsmGetCommentsResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetCustomersParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
query?: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetCustomersResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -245,11 +636,6 @@ export interface JsmGetCustomersResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmAddCustomerParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
emails: string
|
||||
}
|
||||
|
||||
export interface JsmAddCustomerResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -258,12 +644,6 @@ export interface JsmAddCustomerResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetOrganizationsParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetOrganizationsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -273,13 +653,6 @@ export interface JsmGetOrganizationsResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetQueuesParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
includeCount?: boolean
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetQueuesResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -289,12 +662,6 @@ export interface JsmGetQueuesResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetSlaParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetSlaResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -305,12 +672,6 @@ export interface JsmGetSlaResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmTransitionRequestParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
transitionId: string
|
||||
comment?: string
|
||||
}
|
||||
|
||||
export interface JsmTransitionRequestResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -320,22 +681,16 @@ export interface JsmTransitionRequestResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetTransitionsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
}
|
||||
|
||||
export interface JsmGetTransitionsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueIdOrKey: string
|
||||
transitions: JsmTransition[]
|
||||
total: number
|
||||
isLastPage: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmCreateOrganizationParams extends JsmBaseParams {
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface JsmCreateOrganizationResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -345,11 +700,6 @@ export interface JsmCreateOrganizationResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmAddOrganizationParams extends JsmBaseParams {
|
||||
serviceDeskId: string
|
||||
organizationId: string
|
||||
}
|
||||
|
||||
export interface JsmAddOrganizationResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -359,19 +709,6 @@ export interface JsmAddOrganizationResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmParticipant {
|
||||
accountId: string
|
||||
displayName: string
|
||||
emailAddress?: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export interface JsmGetParticipantsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetParticipantsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -382,11 +719,6 @@ export interface JsmGetParticipantsResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmAddParticipantsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
accountIds: string
|
||||
}
|
||||
|
||||
export interface JsmAddParticipantsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -396,29 +728,6 @@ export interface JsmAddParticipantsResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmApprover {
|
||||
accountId: string
|
||||
displayName: string
|
||||
emailAddress?: string
|
||||
approverDecision: 'pending' | 'approved' | 'declined'
|
||||
}
|
||||
|
||||
export interface JsmApproval {
|
||||
id: string
|
||||
name: string
|
||||
finalDecision: 'pending' | 'approved' | 'declined'
|
||||
canAnswerApproval: boolean
|
||||
approvers: JsmApprover[]
|
||||
createdDate?: { iso8601: string; friendly: string }
|
||||
completedDate?: { iso8601: string; friendly: string }
|
||||
}
|
||||
|
||||
export interface JsmGetApprovalsParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
start?: number
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface JsmGetApprovalsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
@@ -429,22 +738,46 @@ export interface JsmGetApprovalsResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmAnswerApprovalParams extends JsmBaseParams {
|
||||
issueIdOrKey: string
|
||||
approvalId: string
|
||||
decision: 'approve' | 'decline'
|
||||
}
|
||||
|
||||
export interface JsmAnswerApprovalResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
issueIdOrKey: string
|
||||
approvalId: string
|
||||
decision: string
|
||||
id: string | null
|
||||
name: string | null
|
||||
finalDecision: string | null
|
||||
canAnswerApproval: boolean | null
|
||||
approvers: Array<{
|
||||
approver: {
|
||||
accountId: string
|
||||
displayName: string
|
||||
emailAddress?: string
|
||||
active?: boolean
|
||||
}
|
||||
approverDecision: string
|
||||
}> | null
|
||||
createdDate: { iso8601: string; friendly: string; epochMillis: number } | null
|
||||
completedDate: { iso8601: string; friendly: string; epochMillis: number } | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface JsmGetRequestTypeFieldsResponse extends ToolResponse {
|
||||
output: {
|
||||
ts: string
|
||||
serviceDeskId: string
|
||||
requestTypeId: string
|
||||
canAddRequestParticipants: boolean
|
||||
canRaiseOnBehalfOf: boolean
|
||||
requestTypeFields: JsmRequestTypeField[]
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Union type for all JSM responses
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** Union type for all JSM responses */
|
||||
export type JsmResponse =
|
||||
| JsmGetServiceDesksResponse
|
||||
@@ -467,3 +800,4 @@ export type JsmResponse =
|
||||
| JsmAddParticipantsResponse
|
||||
| JsmGetApprovalsResponse
|
||||
| JsmAnswerApprovalResponse
|
||||
| JsmGetRequestTypeFieldsResponse
|
||||
|
||||
105
apps/sim/tools/onepassword/create_item.ts
Normal file
105
apps/sim/tools/onepassword/create_item.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import type {
|
||||
OnePasswordCreateItemParams,
|
||||
OnePasswordCreateItemResponse,
|
||||
} from '@/tools/onepassword/types'
|
||||
import { FULL_ITEM_OUTPUTS, transformFullItem } from '@/tools/onepassword/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const createItemTool: ToolConfig<
|
||||
OnePasswordCreateItemParams,
|
||||
OnePasswordCreateItemResponse
|
||||
> = {
|
||||
id: 'onepassword_create_item',
|
||||
name: '1Password Create Item',
|
||||
description: 'Create a new item in a vault',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect API token',
|
||||
},
|
||||
serverUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect server URL (e.g., http://localhost:8080)',
|
||||
},
|
||||
vaultId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The vault UUID to create the item in',
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Item category (e.g., LOGIN, PASSWORD, API_CREDENTIAL, SECURE_NOTE, SERVER, DATABASE)',
|
||||
},
|
||||
title: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Item title',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of tags',
|
||||
},
|
||||
fields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of field objects (e.g., [{"label":"username","value":"admin","type":"STRING","purpose":"USERNAME"}])',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = params.serverUrl.replace(/\/$/, '')
|
||||
return `${base}/v1/vaults/${params.vaultId}/items`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
vault: { id: params.vaultId },
|
||||
category: params.category,
|
||||
}
|
||||
|
||||
if (params.title) {
|
||||
body.title = params.title
|
||||
}
|
||||
|
||||
if (params.tags) {
|
||||
body.tags = params.tags.split(',').map((t) => t.trim())
|
||||
}
|
||||
|
||||
if (params.fields) {
|
||||
body.fields = JSON.parse(params.fields)
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: transformFullItem(data),
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FULL_ITEM_OUTPUTS,
|
||||
}
|
||||
66
apps/sim/tools/onepassword/delete_item.ts
Normal file
66
apps/sim/tools/onepassword/delete_item.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type {
|
||||
OnePasswordDeleteItemParams,
|
||||
OnePasswordDeleteItemResponse,
|
||||
} from '@/tools/onepassword/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const deleteItemTool: ToolConfig<
|
||||
OnePasswordDeleteItemParams,
|
||||
OnePasswordDeleteItemResponse
|
||||
> = {
|
||||
id: 'onepassword_delete_item',
|
||||
name: '1Password Delete Item',
|
||||
description: 'Delete an item from a vault',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect API token',
|
||||
},
|
||||
serverUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect server URL (e.g., http://localhost:8080)',
|
||||
},
|
||||
vaultId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The vault UUID',
|
||||
},
|
||||
itemId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The item UUID to delete',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = params.serverUrl.replace(/\/$/, '')
|
||||
return `${base}/v1/vaults/${params.vaultId}/items/${params.itemId}`
|
||||
},
|
||||
method: 'DELETE',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async () => {
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the item was successfully deleted' },
|
||||
},
|
||||
}
|
||||
61
apps/sim/tools/onepassword/get_item.ts
Normal file
61
apps/sim/tools/onepassword/get_item.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type {
|
||||
OnePasswordGetItemParams,
|
||||
OnePasswordGetItemResponse,
|
||||
} from '@/tools/onepassword/types'
|
||||
import { FULL_ITEM_OUTPUTS, transformFullItem } from '@/tools/onepassword/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getItemTool: ToolConfig<OnePasswordGetItemParams, OnePasswordGetItemResponse> = {
|
||||
id: 'onepassword_get_item',
|
||||
name: '1Password Get Item',
|
||||
description: 'Get full details of an item including all fields and secrets',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect API token',
|
||||
},
|
||||
serverUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect server URL (e.g., http://localhost:8080)',
|
||||
},
|
||||
vaultId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The vault UUID',
|
||||
},
|
||||
itemId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The item UUID to retrieve',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = params.serverUrl.replace(/\/$/, '')
|
||||
return `${base}/v1/vaults/${params.vaultId}/items/${params.itemId}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: transformFullItem(data),
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FULL_ITEM_OUTPUTS,
|
||||
}
|
||||
77
apps/sim/tools/onepassword/get_vault.ts
Normal file
77
apps/sim/tools/onepassword/get_vault.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import type {
|
||||
OnePasswordGetVaultParams,
|
||||
OnePasswordGetVaultResponse,
|
||||
} from '@/tools/onepassword/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const getVaultTool: ToolConfig<OnePasswordGetVaultParams, OnePasswordGetVaultResponse> = {
|
||||
id: 'onepassword_get_vault',
|
||||
name: '1Password Get Vault',
|
||||
description: 'Get details of a specific vault by ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect API token',
|
||||
},
|
||||
serverUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect server URL (e.g., http://localhost:8080)',
|
||||
},
|
||||
vaultId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The vault UUID',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = params.serverUrl.replace(/\/$/, '')
|
||||
return `${base}/v1/vaults/${params.vaultId}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? null,
|
||||
name: data.name ?? null,
|
||||
description: data.description ?? null,
|
||||
attributeVersion: data.attributeVersion ?? 0,
|
||||
contentVersion: data.contentVersion ?? 0,
|
||||
items: data.items ?? 0,
|
||||
type: data.type ?? null,
|
||||
createdAt: data.createdAt ?? null,
|
||||
updatedAt: data.updatedAt ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Vault ID' },
|
||||
name: { type: 'string', description: 'Vault name' },
|
||||
description: { type: 'string', description: 'Vault description', optional: true },
|
||||
attributeVersion: { type: 'number', description: 'Vault attribute version' },
|
||||
contentVersion: { type: 'number', description: 'Vault content version' },
|
||||
items: { type: 'number', description: 'Number of items in the vault' },
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Vault type (USER_CREATED, PERSONAL, EVERYONE, TRANSFER)',
|
||||
},
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
},
|
||||
}
|
||||
17
apps/sim/tools/onepassword/index.ts
Normal file
17
apps/sim/tools/onepassword/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createItemTool } from '@/tools/onepassword/create_item'
|
||||
import { deleteItemTool } from '@/tools/onepassword/delete_item'
|
||||
import { getItemTool } from '@/tools/onepassword/get_item'
|
||||
import { getVaultTool } from '@/tools/onepassword/get_vault'
|
||||
import { listItemsTool } from '@/tools/onepassword/list_items'
|
||||
import { listVaultsTool } from '@/tools/onepassword/list_vaults'
|
||||
import { replaceItemTool } from '@/tools/onepassword/replace_item'
|
||||
import { updateItemTool } from '@/tools/onepassword/update_item'
|
||||
|
||||
export const onepasswordCreateItemTool = createItemTool
|
||||
export const onepasswordDeleteItemTool = deleteItemTool
|
||||
export const onepasswordGetItemTool = getItemTool
|
||||
export const onepasswordGetVaultTool = getVaultTool
|
||||
export const onepasswordListItemsTool = listItemsTool
|
||||
export const onepasswordListVaultsTool = listVaultsTool
|
||||
export const onepasswordReplaceItemTool = replaceItemTool
|
||||
export const onepasswordUpdateItemTool = updateItemTool
|
||||
124
apps/sim/tools/onepassword/list_items.ts
Normal file
124
apps/sim/tools/onepassword/list_items.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type {
|
||||
OnePasswordListItemsParams,
|
||||
OnePasswordListItemsResponse,
|
||||
} from '@/tools/onepassword/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listItemsTool: ToolConfig<OnePasswordListItemsParams, OnePasswordListItemsResponse> = {
|
||||
id: 'onepassword_list_items',
|
||||
name: '1Password List Items',
|
||||
description: 'List items in a vault. Returns summaries without field values.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect API token',
|
||||
},
|
||||
serverUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect server URL (e.g., http://localhost:8080)',
|
||||
},
|
||||
vaultId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The vault UUID to list items from',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'SCIM filter expression (e.g., title eq "API Key" or tag eq "production")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = params.serverUrl.replace(/\/$/, '')
|
||||
const query = params.filter ? `?filter=${encodeURIComponent(params.filter)}` : ''
|
||||
return `${base}/v1/vaults/${params.vaultId}/items${query}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: (data ?? []).map((item: any) => ({
|
||||
id: item.id ?? null,
|
||||
title: item.title ?? null,
|
||||
vault: item.vault ?? null,
|
||||
category: item.category ?? null,
|
||||
urls: (item.urls ?? []).map((url: any) => ({
|
||||
href: url.href ?? null,
|
||||
label: url.label ?? null,
|
||||
primary: url.primary ?? false,
|
||||
})),
|
||||
favorite: item.favorite ?? false,
|
||||
tags: item.tags ?? [],
|
||||
version: item.version ?? 0,
|
||||
state: item.state ?? null,
|
||||
createdAt: item.createdAt ?? null,
|
||||
updatedAt: item.updatedAt ?? null,
|
||||
lastEditedBy: item.lastEditedBy ?? null,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'List of items in the vault (summaries without field values)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Item ID' },
|
||||
title: { type: 'string', description: 'Item title' },
|
||||
vault: {
|
||||
type: 'object',
|
||||
description: 'Vault reference',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Vault ID' },
|
||||
},
|
||||
},
|
||||
category: { type: 'string', description: 'Item category (e.g., LOGIN, API_CREDENTIAL)' },
|
||||
urls: {
|
||||
type: 'array',
|
||||
description: 'URLs associated with the item',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
href: { type: 'string', description: 'URL' },
|
||||
label: { type: 'string', description: 'URL label', optional: true },
|
||||
primary: { type: 'boolean', description: 'Whether this is the primary URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
favorite: { type: 'boolean', description: 'Whether the item is favorited' },
|
||||
tags: { type: 'array', description: 'Item tags' },
|
||||
version: { type: 'number', description: 'Item version number' },
|
||||
state: {
|
||||
type: 'string',
|
||||
description: 'Item state (ARCHIVED or DELETED)',
|
||||
optional: true,
|
||||
},
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
lastEditedBy: { type: 'string', description: 'ID of the last editor', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
92
apps/sim/tools/onepassword/list_vaults.ts
Normal file
92
apps/sim/tools/onepassword/list_vaults.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import type {
|
||||
OnePasswordListVaultsParams,
|
||||
OnePasswordListVaultsResponse,
|
||||
} from '@/tools/onepassword/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const listVaultsTool: ToolConfig<
|
||||
OnePasswordListVaultsParams,
|
||||
OnePasswordListVaultsResponse
|
||||
> = {
|
||||
id: 'onepassword_list_vaults',
|
||||
name: '1Password List Vaults',
|
||||
description: 'List all vaults accessible by the Connect token',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect API token',
|
||||
},
|
||||
serverUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect server URL (e.g., http://localhost:8080)',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'SCIM filter expression (e.g., name eq "My Vault")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = params.serverUrl.replace(/\/$/, '')
|
||||
const query = params.filter ? `?filter=${encodeURIComponent(params.filter)}` : ''
|
||||
return `${base}/v1/vaults${query}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
vaults: (data ?? []).map((vault: any) => ({
|
||||
id: vault.id ?? null,
|
||||
name: vault.name ?? null,
|
||||
description: vault.description ?? null,
|
||||
attributeVersion: vault.attributeVersion ?? 0,
|
||||
contentVersion: vault.contentVersion ?? 0,
|
||||
items: vault.items ?? 0,
|
||||
type: vault.type ?? null,
|
||||
createdAt: vault.createdAt ?? null,
|
||||
updatedAt: vault.updatedAt ?? null,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
vaults: {
|
||||
type: 'array',
|
||||
description: 'List of accessible vaults',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Vault ID' },
|
||||
name: { type: 'string', description: 'Vault name' },
|
||||
description: { type: 'string', description: 'Vault description', optional: true },
|
||||
attributeVersion: { type: 'number', description: 'Vault attribute version' },
|
||||
contentVersion: { type: 'number', description: 'Vault content version' },
|
||||
items: { type: 'number', description: 'Number of items in the vault' },
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Vault type (USER_CREATED, PERSONAL, EVERYONE, TRANSFER)',
|
||||
},
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
73
apps/sim/tools/onepassword/replace_item.ts
Normal file
73
apps/sim/tools/onepassword/replace_item.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import type {
|
||||
OnePasswordReplaceItemParams,
|
||||
OnePasswordReplaceItemResponse,
|
||||
} from '@/tools/onepassword/types'
|
||||
import { FULL_ITEM_OUTPUTS, transformFullItem } from '@/tools/onepassword/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const replaceItemTool: ToolConfig<
|
||||
OnePasswordReplaceItemParams,
|
||||
OnePasswordReplaceItemResponse
|
||||
> = {
|
||||
id: 'onepassword_replace_item',
|
||||
name: '1Password Replace Item',
|
||||
description: 'Replace an entire item with new data (full update)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect API token',
|
||||
},
|
||||
serverUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect server URL (e.g., http://localhost:8080)',
|
||||
},
|
||||
vaultId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The vault UUID',
|
||||
},
|
||||
itemId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The item UUID to replace',
|
||||
},
|
||||
item: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON object representing the full item (e.g., {"vault":{"id":"..."},"category":"LOGIN","title":"My Item","fields":[...]})',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = params.serverUrl.replace(/\/$/, '')
|
||||
return `${base}/v1/vaults/${params.vaultId}/items/${params.itemId}`
|
||||
},
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => JSON.parse(params.item),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: transformFullItem(data),
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FULL_ITEM_OUTPUTS,
|
||||
}
|
||||
156
apps/sim/tools/onepassword/types.ts
Normal file
156
apps/sim/tools/onepassword/types.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface OnePasswordListVaultsParams {
|
||||
apiKey: string
|
||||
serverUrl: string
|
||||
filter?: string
|
||||
}
|
||||
|
||||
export interface OnePasswordGetVaultParams {
|
||||
apiKey: string
|
||||
serverUrl: string
|
||||
vaultId: string
|
||||
}
|
||||
|
||||
export interface OnePasswordListItemsParams {
|
||||
apiKey: string
|
||||
serverUrl: string
|
||||
vaultId: string
|
||||
filter?: string
|
||||
}
|
||||
|
||||
export interface OnePasswordGetItemParams {
|
||||
apiKey: string
|
||||
serverUrl: string
|
||||
vaultId: string
|
||||
itemId: string
|
||||
}
|
||||
|
||||
export interface OnePasswordCreateItemParams {
|
||||
apiKey: string
|
||||
serverUrl: string
|
||||
vaultId: string
|
||||
category: string
|
||||
title?: string
|
||||
tags?: string
|
||||
fields?: string
|
||||
}
|
||||
|
||||
export interface OnePasswordUpdateItemParams {
|
||||
apiKey: string
|
||||
serverUrl: string
|
||||
vaultId: string
|
||||
itemId: string
|
||||
operations: string
|
||||
}
|
||||
|
||||
export interface OnePasswordReplaceItemParams {
|
||||
apiKey: string
|
||||
serverUrl: string
|
||||
vaultId: string
|
||||
itemId: string
|
||||
item: string
|
||||
}
|
||||
|
||||
export interface OnePasswordDeleteItemParams {
|
||||
apiKey: string
|
||||
serverUrl: string
|
||||
vaultId: string
|
||||
itemId: string
|
||||
}
|
||||
|
||||
export interface OnePasswordListVaultsResponse extends ToolResponse {
|
||||
output: {
|
||||
vaults: Array<{
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
attributeVersion: number
|
||||
contentVersion: number
|
||||
items: number
|
||||
type: string
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnePasswordGetVaultResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
attributeVersion: number
|
||||
contentVersion: number
|
||||
items: number
|
||||
type: string
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnePasswordListItemsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: Array<{
|
||||
id: string
|
||||
title: string
|
||||
vault: { id: string }
|
||||
category: string
|
||||
urls: Array<{ href: string; label: string | null; primary: boolean }>
|
||||
favorite: boolean
|
||||
tags: string[]
|
||||
version: number
|
||||
state: string | null
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
lastEditedBy: string | null
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnePasswordFullItemResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
title: string
|
||||
vault: { id: string }
|
||||
category: string
|
||||
urls: Array<{ href: string; label: string | null; primary: boolean }>
|
||||
favorite: boolean
|
||||
tags: string[]
|
||||
version: number
|
||||
state: string | null
|
||||
fields: Array<{
|
||||
id: string
|
||||
label: string | null
|
||||
type: string
|
||||
purpose: string
|
||||
value: string | null
|
||||
section: { id: string } | null
|
||||
generate: boolean
|
||||
recipe: {
|
||||
length: number | null
|
||||
characterSets: string[]
|
||||
excludeCharacters: string | null
|
||||
} | null
|
||||
entropy: number | null
|
||||
}>
|
||||
sections: Array<{
|
||||
id: string
|
||||
label: string | null
|
||||
}>
|
||||
createdAt: string | null
|
||||
updatedAt: string | null
|
||||
lastEditedBy: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export type OnePasswordGetItemResponse = OnePasswordFullItemResponse
|
||||
export type OnePasswordCreateItemResponse = OnePasswordFullItemResponse
|
||||
export type OnePasswordUpdateItemResponse = OnePasswordFullItemResponse
|
||||
export type OnePasswordReplaceItemResponse = OnePasswordFullItemResponse
|
||||
|
||||
export interface OnePasswordDeleteItemResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
73
apps/sim/tools/onepassword/update_item.ts
Normal file
73
apps/sim/tools/onepassword/update_item.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import type {
|
||||
OnePasswordUpdateItemParams,
|
||||
OnePasswordUpdateItemResponse,
|
||||
} from '@/tools/onepassword/types'
|
||||
import { FULL_ITEM_OUTPUTS, transformFullItem } from '@/tools/onepassword/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const updateItemTool: ToolConfig<
|
||||
OnePasswordUpdateItemParams,
|
||||
OnePasswordUpdateItemResponse
|
||||
> = {
|
||||
id: 'onepassword_update_item',
|
||||
name: '1Password Update Item',
|
||||
description: 'Update an existing item using JSON Patch operations (RFC6902)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect API token',
|
||||
},
|
||||
serverUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: '1Password Connect server URL (e.g., http://localhost:8080)',
|
||||
},
|
||||
vaultId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The vault UUID',
|
||||
},
|
||||
itemId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The item UUID to update',
|
||||
},
|
||||
operations: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of RFC6902 patch operations (e.g., [{"op":"replace","path":"/title","value":"New Title"}])',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const base = params.serverUrl.replace(/\/$/, '')
|
||||
return `${base}/v1/vaults/${params.vaultId}/items/${params.itemId}`
|
||||
},
|
||||
method: 'PATCH',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => JSON.parse(params.operations),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
return {
|
||||
success: true,
|
||||
output: transformFullItem(data),
|
||||
}
|
||||
},
|
||||
|
||||
outputs: FULL_ITEM_OUTPUTS,
|
||||
}
|
||||
148
apps/sim/tools/onepassword/utils.ts
Normal file
148
apps/sim/tools/onepassword/utils.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import type { OutputType } from '@/tools/types'
|
||||
|
||||
/** Transforms a raw FullItem API response into our standardized output. */
|
||||
export function transformFullItem(data: any) {
|
||||
return {
|
||||
id: data.id ?? null,
|
||||
title: data.title ?? null,
|
||||
vault: data.vault ?? null,
|
||||
category: data.category ?? null,
|
||||
urls: (data.urls ?? []).map((url: any) => ({
|
||||
href: url.href ?? null,
|
||||
label: url.label ?? null,
|
||||
primary: url.primary ?? false,
|
||||
})),
|
||||
favorite: data.favorite ?? false,
|
||||
tags: data.tags ?? [],
|
||||
version: data.version ?? 0,
|
||||
state: data.state ?? null,
|
||||
fields: (data.fields ?? []).map((field: any) => ({
|
||||
id: field.id ?? null,
|
||||
label: field.label ?? null,
|
||||
type: field.type ?? 'STRING',
|
||||
purpose: field.purpose ?? '',
|
||||
value: field.value ?? null,
|
||||
section: field.section ?? null,
|
||||
generate: field.generate ?? false,
|
||||
recipe: field.recipe
|
||||
? {
|
||||
length: field.recipe.length ?? null,
|
||||
characterSets: field.recipe.characterSets ?? [],
|
||||
excludeCharacters: field.recipe.excludeCharacters ?? null,
|
||||
}
|
||||
: null,
|
||||
entropy: field.entropy ?? null,
|
||||
})),
|
||||
sections: (data.sections ?? []).map((section: any) => ({
|
||||
id: section.id ?? null,
|
||||
label: section.label ?? null,
|
||||
})),
|
||||
createdAt: data.createdAt ?? null,
|
||||
updatedAt: data.updatedAt ?? null,
|
||||
lastEditedBy: data.lastEditedBy ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
/** Shared output schema for FullItem responses (get_item, create_item, update_item). */
|
||||
export const FULL_ITEM_OUTPUTS: Record<
|
||||
string,
|
||||
{
|
||||
type: OutputType
|
||||
description: string
|
||||
optional?: boolean
|
||||
properties?: Record<string, any>
|
||||
items?: { type: OutputType; description?: string; properties?: Record<string, any> }
|
||||
}
|
||||
> = {
|
||||
id: { type: 'string', description: 'Item ID' },
|
||||
title: { type: 'string', description: 'Item title' },
|
||||
vault: {
|
||||
type: 'object',
|
||||
description: 'Vault reference',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Vault ID' },
|
||||
},
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
description: 'Item category (e.g., LOGIN, API_CREDENTIAL, SECURE_NOTE)',
|
||||
},
|
||||
urls: {
|
||||
type: 'array',
|
||||
description: 'URLs associated with the item',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
href: { type: 'string', description: 'URL' },
|
||||
label: { type: 'string', description: 'URL label', optional: true },
|
||||
primary: { type: 'boolean', description: 'Whether this is the primary URL' },
|
||||
},
|
||||
},
|
||||
},
|
||||
favorite: { type: 'boolean', description: 'Whether the item is favorited' },
|
||||
tags: { type: 'array', description: 'Item tags' },
|
||||
version: { type: 'number', description: 'Item version number' },
|
||||
state: { type: 'string', description: 'Item state (ARCHIVED or DELETED)', optional: true },
|
||||
fields: {
|
||||
type: 'array',
|
||||
description: 'Item fields including secrets',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Field ID' },
|
||||
label: { type: 'string', description: 'Field label', optional: true },
|
||||
type: {
|
||||
type: 'string',
|
||||
description: 'Field type (STRING, EMAIL, CONCEALED, URL, TOTP, DATE, MONTH_YEAR, MENU)',
|
||||
},
|
||||
purpose: {
|
||||
type: 'string',
|
||||
description: 'Field purpose (USERNAME, PASSWORD, NOTES, or empty)',
|
||||
},
|
||||
value: { type: 'string', description: 'Field value', optional: true },
|
||||
section: {
|
||||
type: 'object',
|
||||
description: 'Section reference this field belongs to',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Section ID' },
|
||||
},
|
||||
},
|
||||
generate: { type: 'boolean', description: 'Whether the field value should be generated' },
|
||||
recipe: {
|
||||
type: 'object',
|
||||
description: 'Password generation recipe',
|
||||
optional: true,
|
||||
properties: {
|
||||
length: { type: 'number', description: 'Generated password length', optional: true },
|
||||
characterSets: {
|
||||
type: 'array',
|
||||
description: 'Character sets (LETTERS, DIGITS, SYMBOLS)',
|
||||
},
|
||||
excludeCharacters: {
|
||||
type: 'string',
|
||||
description: 'Characters to exclude',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
entropy: { type: 'number', description: 'Password entropy score', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
sections: {
|
||||
type: 'array',
|
||||
description: 'Item sections',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Section ID' },
|
||||
label: { type: 'string', description: 'Section label', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp', optional: true },
|
||||
lastEditedBy: { type: 'string', description: 'ID of the last editor', optional: true },
|
||||
}
|
||||
@@ -814,6 +814,7 @@ import {
|
||||
jiraAddWorklogTool,
|
||||
jiraAssignIssueTool,
|
||||
jiraBulkRetrieveTool,
|
||||
jiraBulkRetrieveV2Tool,
|
||||
jiraCreateIssueLinkTool,
|
||||
jiraDeleteAttachmentTool,
|
||||
jiraDeleteCommentTool,
|
||||
@@ -827,6 +828,7 @@ import {
|
||||
jiraRemoveWatcherTool,
|
||||
jiraRetrieveTool,
|
||||
jiraSearchIssuesTool,
|
||||
jiraSearchIssuesV2Tool,
|
||||
jiraTransitionIssueTool,
|
||||
jiraUpdateCommentTool,
|
||||
jiraUpdateTool,
|
||||
@@ -849,6 +851,7 @@ import {
|
||||
jsmGetQueuesTool,
|
||||
jsmGetRequestsTool,
|
||||
jsmGetRequestTool,
|
||||
jsmGetRequestTypeFieldsTool,
|
||||
jsmGetRequestTypesTool,
|
||||
jsmGetServiceDesksTool,
|
||||
jsmGetSlaTool,
|
||||
@@ -1158,6 +1161,16 @@ import {
|
||||
onedriveListTool,
|
||||
onedriveUploadTool,
|
||||
} from '@/tools/onedrive'
|
||||
import {
|
||||
onepasswordCreateItemTool,
|
||||
onepasswordDeleteItemTool,
|
||||
onepasswordGetItemTool,
|
||||
onepasswordGetVaultTool,
|
||||
onepasswordListItemsTool,
|
||||
onepasswordListVaultsTool,
|
||||
onepasswordReplaceItemTool,
|
||||
onepasswordUpdateItemTool,
|
||||
} from '@/tools/onepassword'
|
||||
import { openAIEmbeddingsTool, openAIImageTool } from '@/tools/openai'
|
||||
import {
|
||||
outlookCopyTool,
|
||||
@@ -1923,10 +1936,12 @@ export const tools: Record<string, ToolConfig> = {
|
||||
jira_update: jiraUpdateTool,
|
||||
jira_write: jiraWriteTool,
|
||||
jira_bulk_read: jiraBulkRetrieveTool,
|
||||
jira_bulk_read_v2: jiraBulkRetrieveV2Tool,
|
||||
jira_delete_issue: jiraDeleteIssueTool,
|
||||
jira_assign_issue: jiraAssignIssueTool,
|
||||
jira_transition_issue: jiraTransitionIssueTool,
|
||||
jira_search_issues: jiraSearchIssuesTool,
|
||||
jira_search_issues_v2: jiraSearchIssuesV2Tool,
|
||||
jira_add_comment: jiraAddCommentTool,
|
||||
jira_get_comments: jiraGetCommentsTool,
|
||||
jira_update_comment: jiraUpdateCommentTool,
|
||||
@@ -1945,6 +1960,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
jira_get_users: jiraGetUsersTool,
|
||||
jsm_get_service_desks: jsmGetServiceDesksTool,
|
||||
jsm_get_request_types: jsmGetRequestTypesTool,
|
||||
jsm_get_request_type_fields: jsmGetRequestTypeFieldsTool,
|
||||
jsm_create_request: jsmCreateRequestTool,
|
||||
jsm_get_request: jsmGetRequestTool,
|
||||
jsm_get_requests: jsmGetRequestsTool,
|
||||
@@ -2133,6 +2149,14 @@ export const tools: Record<string, ToolConfig> = {
|
||||
notion_create_database_v2: notionCreateDatabaseV2Tool,
|
||||
notion_update_page_v2: notionUpdatePageV2Tool,
|
||||
notion_add_database_row_v2: notionAddDatabaseRowTool,
|
||||
onepassword_list_vaults: onepasswordListVaultsTool,
|
||||
onepassword_get_vault: onepasswordGetVaultTool,
|
||||
onepassword_list_items: onepasswordListItemsTool,
|
||||
onepassword_get_item: onepasswordGetItemTool,
|
||||
onepassword_create_item: onepasswordCreateItemTool,
|
||||
onepassword_replace_item: onepasswordReplaceItemTool,
|
||||
onepassword_update_item: onepasswordUpdateItemTool,
|
||||
onepassword_delete_item: onepasswordDeleteItemTool,
|
||||
gmail_send: gmailSendTool,
|
||||
gmail_send_v2: gmailSendV2Tool,
|
||||
gmail_read: gmailReadTool,
|
||||
|
||||
Reference in New Issue
Block a user