gaunt-sloth-assistant 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gsloth.config_.json +2 -2
- package/.gsloth.config_.mjs +18 -0
- package/.gsloth.config_claude.mjs +18 -0
- package/.gsloth.config_vertex.json +6 -0
- package/README.md +8 -1
- package/assets/release-notes/v0_7_3.md +64 -0
- package/assets/release-notes/v0_8_0.md +13 -0
- package/dist/builtInToolsConfig.d.ts +6 -0
- package/dist/builtInToolsConfig.js +73 -0
- package/dist/builtInToolsConfig.js.map +1 -0
- package/dist/commands/prCommand.js +14 -1
- package/dist/commands/prCommand.js.map +1 -1
- package/dist/config.d.ts +18 -7
- package/dist/config.js +1 -41
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/Invocation.d.ts +1 -1
- package/dist/core/Invocation.js +27 -9
- package/dist/core/Invocation.js.map +1 -1
- package/dist/filePathUtils.js +1 -2
- package/dist/filePathUtils.js.map +1 -1
- package/dist/globalConfigUtils.d.ts +28 -0
- package/dist/globalConfigUtils.js +61 -0
- package/dist/globalConfigUtils.js.map +1 -0
- package/dist/helpers/jira/jiraClient.d.ts +4 -0
- package/dist/helpers/jira/jiraClient.js +75 -0
- package/dist/helpers/jira/jiraClient.js.map +1 -0
- package/dist/helpers/jira/jiraLogWork.d.ts +2 -0
- package/dist/helpers/jira/jiraLogWork.js +53 -0
- package/dist/helpers/jira/jiraLogWork.js.map +1 -0
- package/dist/mcp/OAuthClientProviderImpl.d.ts +36 -0
- package/dist/mcp/OAuthClientProviderImpl.js +197 -0
- package/dist/mcp/OAuthClientProviderImpl.js.map +1 -0
- package/dist/providers/jiraIssueProvider.js +9 -52
- package/dist/providers/jiraIssueProvider.js.map +1 -1
- package/dist/tools/gthJiraLogWorkTool.d.ts +3 -0
- package/dist/tools/gthJiraLogWorkTool.js +37 -0
- package/dist/tools/gthJiraLogWorkTool.js.map +1 -0
- package/dist/tools/gthStatusUpdateTool.d.ts +2 -0
- package/dist/tools/gthStatusUpdateTool.js +16 -0
- package/dist/tools/gthStatusUpdateTool.js.map +1 -0
- package/docs/CONFIGURATION.md +37 -2
- package/docs/DEVELOPMENT.md +26 -0
- package/eslint.config.js +7 -1
- package/package.json +3 -1
- package/src/builtInToolsConfig.ts +99 -0
- package/src/commands/prCommand.ts +28 -3
- package/src/config.ts +20 -62
- package/src/constants.ts +3 -0
- package/src/core/Invocation.ts +30 -11
- package/src/filePathUtils.ts +1 -3
- package/src/globalConfigUtils.ts +71 -0
- package/src/helpers/jira/jiraClient.ts +99 -0
- package/src/helpers/jira/jiraLogWork.ts +81 -0
- package/src/mcp/OAuthClientProviderImpl.ts +236 -0
- package/src/providers/jiraIssueProvider.ts +13 -70
- package/src/tools/gthJiraLogWorkTool.ts +51 -0
- package/src/tools/gthStatusUpdateTool.ts +18 -0
- package/vitest-it.config.ts +1 -1
- package/vitest.config.ts +1 -0
- package/dist/tools/statusUpdate.d.ts +0 -1
- package/dist/tools/statusUpdate.js +0 -12
- package/dist/tools/statusUpdate.js.map +0 -1
- package/src/tools/statusUpdate.ts +0 -15
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gets the global .gsloth directory path in the user's home directory
|
|
3
|
+
* @returns The resolved path to the global .gsloth directory
|
|
4
|
+
*/
|
|
5
|
+
export declare function getGlobalGslothDir(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Ensures the global .gsloth directory exists in the user's home directory
|
|
8
|
+
* Creates it if it doesn't exist
|
|
9
|
+
* @returns The resolved path to the global .gsloth directory
|
|
10
|
+
*/
|
|
11
|
+
export declare function ensureGlobalGslothDir(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Gets the global auth directory path
|
|
14
|
+
* @returns The resolved path to the global auth directory
|
|
15
|
+
*/
|
|
16
|
+
export declare function getGlobalAuthDir(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Ensures the global auth directory exists
|
|
19
|
+
* Creates it if it doesn't exist
|
|
20
|
+
* @returns The resolved path to the global auth directory
|
|
21
|
+
*/
|
|
22
|
+
export declare function ensureGlobalAuthDir(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Gets the path for a specific OAuth provider's storage file
|
|
25
|
+
* @param serverUrl The server URL or identifier for the OAuth provider
|
|
26
|
+
* @returns The resolved path where the OAuth data should be stored
|
|
27
|
+
*/
|
|
28
|
+
export declare function getOAuthStoragePath(serverUrl: string): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { GSLOTH_DIR, GSLOTH_AUTH } from '#src/constants.js';
|
|
5
|
+
/**
|
|
6
|
+
* Gets the global .gsloth directory path in the user's home directory
|
|
7
|
+
* @returns The resolved path to the global .gsloth directory
|
|
8
|
+
*/
|
|
9
|
+
export function getGlobalGslothDir() {
|
|
10
|
+
return resolve(homedir(), GSLOTH_DIR);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Ensures the global .gsloth directory exists in the user's home directory
|
|
14
|
+
* Creates it if it doesn't exist
|
|
15
|
+
* @returns The resolved path to the global .gsloth directory
|
|
16
|
+
*/
|
|
17
|
+
export function ensureGlobalGslothDir() {
|
|
18
|
+
const globalDir = getGlobalGslothDir();
|
|
19
|
+
if (!existsSync(globalDir)) {
|
|
20
|
+
mkdirSync(globalDir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
return globalDir;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Gets the global auth directory path
|
|
26
|
+
* @returns The resolved path to the global auth directory
|
|
27
|
+
*/
|
|
28
|
+
export function getGlobalAuthDir() {
|
|
29
|
+
const globalDir = getGlobalGslothDir();
|
|
30
|
+
return resolve(globalDir, GSLOTH_AUTH);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Ensures the global auth directory exists
|
|
34
|
+
* Creates it if it doesn't exist
|
|
35
|
+
* @returns The resolved path to the global auth directory
|
|
36
|
+
*/
|
|
37
|
+
export function ensureGlobalAuthDir() {
|
|
38
|
+
// First ensure parent directory exists
|
|
39
|
+
ensureGlobalGslothDir();
|
|
40
|
+
const authDir = getGlobalAuthDir();
|
|
41
|
+
if (!existsSync(authDir)) {
|
|
42
|
+
mkdirSync(authDir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
return authDir;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Gets the path for a specific OAuth provider's storage file
|
|
48
|
+
* @param serverUrl The server URL or identifier for the OAuth provider
|
|
49
|
+
* @returns The resolved path where the OAuth data should be stored
|
|
50
|
+
*/
|
|
51
|
+
export function getOAuthStoragePath(serverUrl) {
|
|
52
|
+
const authDir = ensureGlobalAuthDir();
|
|
53
|
+
// Create a safe filename from the server URL
|
|
54
|
+
const safeFilename = serverUrl
|
|
55
|
+
.replace(/https?:\/\//, '')
|
|
56
|
+
.replace(/[^a-zA-Z0-9.-]/g, '_')
|
|
57
|
+
.replace(/_+/g, '_')
|
|
58
|
+
.toLowerCase();
|
|
59
|
+
return resolve(authDir, `${safeFilename}.json`);
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=globalConfigUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"globalConfigUtils.js","sourceRoot":"","sources":["../src/globalConfigUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,OAAO,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;IAEvC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;IACvC,OAAO,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB;IACjC,uCAAuC;IACvC,qBAAqB,EAAE,CAAC;IAExB,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;IAEnC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,MAAM,OAAO,GAAG,mBAAmB,EAAE,CAAC;IACtC,6CAA6C;IAC7C,MAAM,YAAY,GAAG,SAAS;SAC3B,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC;SAC1B,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC;SAC/B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,WAAW,EAAE,CAAC;IAEjB,OAAO,OAAO,CAAC,OAAO,EAAE,GAAG,YAAY,OAAO,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { JiraConfig } from '#src/providers/types.js';
|
|
2
|
+
export declare function getJiraCredentials(config: Partial<JiraConfig> | null): JiraConfig;
|
|
3
|
+
export declare function getJiraHeaders(config: JiraConfig): Record<string, string>;
|
|
4
|
+
export declare function jiraRequest<T>(config: JiraConfig, endpoint: string, options?: RequestInit, showProgress?: boolean): Promise<T>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { env } from '#src/systemUtils.js';
|
|
2
|
+
import { ProgressIndicator } from '#src/utils.js';
|
|
3
|
+
export function getJiraCredentials(config) {
|
|
4
|
+
if (!config) {
|
|
5
|
+
throw new Error('No Jira config provided');
|
|
6
|
+
}
|
|
7
|
+
const username = env.JIRA_USERNAME || config.username;
|
|
8
|
+
if (!username) {
|
|
9
|
+
throw new Error('Missing JIRA username. The username can be defined as JIRA_USERNAME environment variable or as "username" in config.');
|
|
10
|
+
}
|
|
11
|
+
const token = env.JIRA_API_PAT_TOKEN || config.token;
|
|
12
|
+
if (!token) {
|
|
13
|
+
throw new Error('Missing JIRA PAT token. The token can be defined as JIRA_API_PAT_TOKEN environment variable or as "token" in config.');
|
|
14
|
+
}
|
|
15
|
+
const cloudId = env.JIRA_CLOUD_ID || config.cloudId;
|
|
16
|
+
if (!cloudId) {
|
|
17
|
+
throw new Error('Missing JIRA Cloud ID. The Cloud ID can be defined as JIRA_CLOUD_ID environment variable or as "cloudId" in config.');
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
username,
|
|
21
|
+
token,
|
|
22
|
+
cloudId,
|
|
23
|
+
displayUrl: config.displayUrl,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function getJiraHeaders(config) {
|
|
27
|
+
const credentials = `${config.username}:${config.token}`;
|
|
28
|
+
const encodedCredentials = Buffer.from(credentials).toString('base64');
|
|
29
|
+
const authHeader = `Basic ${encodedCredentials}`;
|
|
30
|
+
return {
|
|
31
|
+
Authorization: authHeader,
|
|
32
|
+
Accept: 'application/json; charset=utf-8',
|
|
33
|
+
'Accept-Language': 'en-US,en;q=0.9',
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function jiraRequest(config, endpoint, options = {}, showProgress = true) {
|
|
38
|
+
const apiUrl = `https://api.atlassian.com/ex/jira/${config.cloudId}${endpoint}`;
|
|
39
|
+
const headers = getJiraHeaders(config);
|
|
40
|
+
let progressIndicator;
|
|
41
|
+
if (showProgress) {
|
|
42
|
+
progressIndicator = new ProgressIndicator(`${options.method || 'GET'} ${apiUrl.replace(/^https?:\/\//, '')}`);
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const response = await fetch(apiUrl, {
|
|
46
|
+
...options,
|
|
47
|
+
headers: {
|
|
48
|
+
...headers,
|
|
49
|
+
...options.headers,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
if (progressIndicator) {
|
|
53
|
+
progressIndicator.stop();
|
|
54
|
+
}
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
let errorMessage = `Failed to fetch from Jira: ${response.statusText}`;
|
|
57
|
+
try {
|
|
58
|
+
const errorData = await response.json();
|
|
59
|
+
errorMessage += ` - ${JSON.stringify(errorData)}`;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// If we can't parse JSON error, use the basic message
|
|
63
|
+
}
|
|
64
|
+
throw new Error(errorMessage);
|
|
65
|
+
}
|
|
66
|
+
return response.json();
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (progressIndicator) {
|
|
70
|
+
progressIndicator.stop();
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=jiraClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jiraClient.js","sourceRoot":"","sources":["../../../src/helpers/jira/jiraClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getJiraCredentials, jiraRequest } from './jiraClient.js';
|
|
2
|
+
import { displayError, displaySuccess } from '#src/consoleUtils.js';
|
|
3
|
+
export default async function jiraLogWork(config, jiraId, timeInSeconds, comment = 'Work logged', startedAt = new Date()) {
|
|
4
|
+
try {
|
|
5
|
+
// Use provided config or empty config (will use environment variables)
|
|
6
|
+
const credentials = getJiraCredentials(config);
|
|
7
|
+
const bodyData = {
|
|
8
|
+
comment: {
|
|
9
|
+
content: [
|
|
10
|
+
{
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
text: comment,
|
|
14
|
+
type: 'text',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
type: 'paragraph',
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
type: 'doc',
|
|
21
|
+
version: 1,
|
|
22
|
+
},
|
|
23
|
+
started: startedAt.toISOString().replace('Z', '+0000'),
|
|
24
|
+
timeSpentSeconds: timeInSeconds,
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-worklogs/#api-rest-api-3-issue-issueidorkey-worklog-post
|
|
28
|
+
* Needs:
|
|
29
|
+
* Classic RECOMMENDED:write:jira-work
|
|
30
|
+
*
|
|
31
|
+
* OR
|
|
32
|
+
*
|
|
33
|
+
* write:issue-worklog:jira, write:issue-worklog.property:jira, read:avatar:jira, read:group:jira,
|
|
34
|
+
* read:issue-worklog:jira, read:project-role:jira, read:user:jira, read:issue-worklog.property:jira
|
|
35
|
+
*/
|
|
36
|
+
await jiraRequest(credentials, `/rest/api/3/issue/${jiraId}/worklog`, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
body: JSON.stringify(bodyData),
|
|
39
|
+
});
|
|
40
|
+
const hours = Math.floor(timeInSeconds / 3600);
|
|
41
|
+
const minutes = Math.floor((timeInSeconds % 3600) / 60);
|
|
42
|
+
const timeStr = hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
|
|
43
|
+
const successMessage = `✅ Logged ${timeStr} to ${jiraId}`;
|
|
44
|
+
displaySuccess(successMessage);
|
|
45
|
+
return successMessage;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const errorMessage = `Failed to log work to Jira: ${error instanceof Error ? error.message : String(error)}`;
|
|
49
|
+
displayError(errorMessage);
|
|
50
|
+
return errorMessage;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=jiraLogWork.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jiraLogWork.js","sourceRoot":"","sources":["../../../src/helpers/jira/jiraLogWork.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAkBpE,MAAM,CAAC,OAAO,CAAC,KAAK,UAAU,WAAW,CACvC,MAAkC,EAClC,MAAc,EACd,aAAqB,EACrB,UAAkB,aAAa,EAC/B,YAAkB,IAAI,IAAI,EAAE;IAE5B,IAAI,CAAC;QACH,uEAAuE;QACvE,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAE/C,MAAM,QAAQ,GAAuB;YACnC,OAAO,EAAE;gBACP,OAAO,EAAE;oBACP;wBACE,OAAO,EAAE;4BACP;gCACE,IAAI,EAAE,OAAO;gCACb,IAAI,EAAE,MAAM;6BACb;yBACF;wBACD,IAAI,EAAE,WAAW;qBAClB;iBACF;gBACD,IAAI,EAAE,KAAK;gBACX,OAAO,EAAE,CAAC;aACX;YACD,OAAO,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC;YACtD,gBAAgB,EAAE,aAAa;SAChC,CAAC;QAEF;;;;;;;;;WASG;QACH,MAAM,WAAW,CAAC,WAAW,EAAE,qBAAqB,MAAM,UAAU,EAAE;YACpE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;SAC/B,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC;QAEpE,MAAM,cAAc,GAAG,YAAY,OAAO,OAAO,MAAM,EAAE,CAAC;QAC1D,cAAc,CAAC,cAAc,CAAC,CAAC;QAC/B,OAAO,cAAc,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,+BACnB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE,CAAC;QACH,YAAY,CAAC,YAAY,CAAC,CAAC;QAC3B,OAAO,YAAY,CAAC;IACtB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
2
|
+
import type { OAuthClientInformationFull, OAuthClientMetadata, OAuthTokens } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
3
|
+
import { StreamableHTTPConnection } from '@langchain/mcp-adapters';
|
|
4
|
+
import http from 'http';
|
|
5
|
+
interface OAuthClientProviderConfig {
|
|
6
|
+
redirectUrl: string;
|
|
7
|
+
serverUrl: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Please note most of these "unused" methods are part of {@link OAuthClientProvider}
|
|
11
|
+
*/
|
|
12
|
+
export declare class OAuthClientProviderImpl implements OAuthClientProvider {
|
|
13
|
+
private config;
|
|
14
|
+
private innerState;
|
|
15
|
+
private storagePath;
|
|
16
|
+
constructor(config: OAuthClientProviderConfig);
|
|
17
|
+
private loadStorageData;
|
|
18
|
+
private saveStorageData;
|
|
19
|
+
state(): string | Promise<string>;
|
|
20
|
+
get redirectUrl(): string;
|
|
21
|
+
get clientMetadata(): OAuthClientMetadata;
|
|
22
|
+
saveClientInformation(clientInformation: OAuthClientInformationFull): Promise<void>;
|
|
23
|
+
clientInformation(): Promise<OAuthClientInformationFull | undefined>;
|
|
24
|
+
saveTokens(tokens: OAuthTokens): Promise<void>;
|
|
25
|
+
tokens(): OAuthTokens | undefined;
|
|
26
|
+
saveCodeVerifier(codeVerifier: string): Promise<void>;
|
|
27
|
+
codeVerifier(): Promise<string>;
|
|
28
|
+
redirectToAuthorization(authUrl: URL): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export declare function createAuthProviderAndAuthenticate(mcpServer: StreamableHTTPConnection): Promise<OAuthClientProviderImpl>;
|
|
31
|
+
export declare function createOAuthRedirectServer(path: string, portParam?: number): Promise<{
|
|
32
|
+
port: number;
|
|
33
|
+
server: http.Server;
|
|
34
|
+
codePromise: Promise<string>;
|
|
35
|
+
}>;
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { auth } from '@modelcontextprotocol/sdk/client/auth.js';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import * as crypto from 'crypto';
|
|
4
|
+
import { platform } from 'node:os';
|
|
5
|
+
import { execSync } from 'node:child_process';
|
|
6
|
+
import { displayInfo } from '#src/consoleUtils.js';
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
8
|
+
import { getOAuthStoragePath } from '#src/globalConfigUtils.js';
|
|
9
|
+
/**
|
|
10
|
+
* Please note most of these "unused" methods are part of {@link OAuthClientProvider}
|
|
11
|
+
*/
|
|
12
|
+
export class OAuthClientProviderImpl {
|
|
13
|
+
config;
|
|
14
|
+
innerState;
|
|
15
|
+
storagePath;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.innerState = crypto.randomUUID();
|
|
19
|
+
if (!this.config.redirectUrl) {
|
|
20
|
+
throw new Error('No redirect URL provided');
|
|
21
|
+
}
|
|
22
|
+
if (!this.config.serverUrl) {
|
|
23
|
+
throw new Error('No server URL provided');
|
|
24
|
+
}
|
|
25
|
+
this.storagePath = getOAuthStoragePath(this.config.serverUrl);
|
|
26
|
+
}
|
|
27
|
+
loadStorageData() {
|
|
28
|
+
if (existsSync(this.storagePath)) {
|
|
29
|
+
try {
|
|
30
|
+
const data = readFileSync(this.storagePath, 'utf-8');
|
|
31
|
+
return JSON.parse(data);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
displayInfo('Failed to load OAuth storage data:' + error);
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
saveStorageData(data) {
|
|
41
|
+
try {
|
|
42
|
+
writeFileSync(this.storagePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error('Failed to save OAuth storage data:', error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
state() {
|
|
49
|
+
return this.innerState;
|
|
50
|
+
}
|
|
51
|
+
// noinspection JSUnusedGlobalSymbols
|
|
52
|
+
get redirectUrl() {
|
|
53
|
+
return this.config.redirectUrl;
|
|
54
|
+
}
|
|
55
|
+
// noinspection JSUnusedGlobalSymbols
|
|
56
|
+
get clientMetadata() {
|
|
57
|
+
return {
|
|
58
|
+
redirect_uris: [this.config.redirectUrl],
|
|
59
|
+
client_name: 'Gaunt Sloth Assistant',
|
|
60
|
+
client_uri: 'https://github.com/andruhon/gaunt-sloth-assistant',
|
|
61
|
+
software_id: '1dd38b83-946b-4631-8855-66ee467bfd68',
|
|
62
|
+
scope: 'mcp:read mcp:write',
|
|
63
|
+
token_endpoint_auth_method: 'none',
|
|
64
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
65
|
+
response_types: ['code'],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// noinspection JSUnusedGlobalSymbols
|
|
69
|
+
saveClientInformation(clientInformation) {
|
|
70
|
+
const data = this.loadStorageData();
|
|
71
|
+
data.clientInformation = clientInformation;
|
|
72
|
+
this.saveStorageData(data);
|
|
73
|
+
return Promise.resolve();
|
|
74
|
+
}
|
|
75
|
+
// noinspection JSUnusedGlobalSymbols
|
|
76
|
+
async clientInformation() {
|
|
77
|
+
const data = this.loadStorageData();
|
|
78
|
+
return Promise.resolve(data.clientInformation);
|
|
79
|
+
}
|
|
80
|
+
// noinspection JSUnusedGlobalSymbols
|
|
81
|
+
async saveTokens(tokens) {
|
|
82
|
+
const data = this.loadStorageData();
|
|
83
|
+
data.tokens = tokens;
|
|
84
|
+
this.saveStorageData(data);
|
|
85
|
+
}
|
|
86
|
+
// noinspection JSUnusedGlobalSymbols
|
|
87
|
+
tokens() {
|
|
88
|
+
const data = this.loadStorageData();
|
|
89
|
+
return data.tokens;
|
|
90
|
+
}
|
|
91
|
+
// noinspection JSUnusedGlobalSymbols
|
|
92
|
+
saveCodeVerifier(codeVerifier) {
|
|
93
|
+
const data = this.loadStorageData();
|
|
94
|
+
data.codeVerifier = codeVerifier;
|
|
95
|
+
this.saveStorageData(data);
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
}
|
|
98
|
+
// noinspection JSUnusedGlobalSymbols
|
|
99
|
+
codeVerifier() {
|
|
100
|
+
const data = this.loadStorageData();
|
|
101
|
+
if (!data.codeVerifier) {
|
|
102
|
+
throw new Error('No code verifier stored');
|
|
103
|
+
}
|
|
104
|
+
return Promise.resolve(data.codeVerifier);
|
|
105
|
+
}
|
|
106
|
+
// noinspection JSUnusedGlobalSymbols
|
|
107
|
+
async redirectToAuthorization(authUrl) {
|
|
108
|
+
displayInfo('Auth url: ' + authUrl.toString());
|
|
109
|
+
try {
|
|
110
|
+
const url = authUrl.toString();
|
|
111
|
+
displayInfo('Trying to open browser');
|
|
112
|
+
// Handle different platforms
|
|
113
|
+
const platformName = platform();
|
|
114
|
+
if (platformName === 'win32') {
|
|
115
|
+
// Windows
|
|
116
|
+
execSync(`start "" "${url}"`);
|
|
117
|
+
}
|
|
118
|
+
else if (platformName === 'darwin') {
|
|
119
|
+
// macOS
|
|
120
|
+
execSync(`open "${url}"`);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Linux and others
|
|
124
|
+
execSync(`xdg-open "${url}"`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
displayInfo(`Failed to open browser: ${error}`);
|
|
129
|
+
displayInfo(`Please open ${authUrl.toString()} in your browser`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export async function createAuthProviderAndAuthenticate(mcpServer) {
|
|
134
|
+
const { port, server, codePromise } = await createOAuthRedirectServer('/oauth-callback');
|
|
135
|
+
const authProvider = new OAuthClientProviderImpl({
|
|
136
|
+
redirectUrl: `http://127.0.0.1:${port}/oauth-callback`,
|
|
137
|
+
serverUrl: mcpServer.url,
|
|
138
|
+
});
|
|
139
|
+
const outcome = await auth(authProvider, { serverUrl: mcpServer.url });
|
|
140
|
+
if (outcome == 'REDIRECT') {
|
|
141
|
+
const authorizationCode = await codePromise;
|
|
142
|
+
await auth(authProvider, { serverUrl: mcpServer.url, authorizationCode });
|
|
143
|
+
}
|
|
144
|
+
else if (outcome == 'AUTHORIZED') {
|
|
145
|
+
try {
|
|
146
|
+
server.close();
|
|
147
|
+
}
|
|
148
|
+
catch { }
|
|
149
|
+
displayInfo('Authorized');
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
throw new Error(`Unexpected Auth outcome: ${outcome}`);
|
|
153
|
+
}
|
|
154
|
+
return authProvider;
|
|
155
|
+
}
|
|
156
|
+
export function createOAuthRedirectServer(path, portParam = 0) {
|
|
157
|
+
const redirectApp = express();
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
const codePromise = new Promise((resolveCode, rejectCode) => {
|
|
160
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
161
|
+
redirectApp.get(path, (req, res) => {
|
|
162
|
+
const code = req.query.code;
|
|
163
|
+
if (!code) {
|
|
164
|
+
res.status(400).send('Error: No auth code received');
|
|
165
|
+
rejectCode('Error: No auth code received');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
res.send(`<div style="height: 80vh;text-align: center;display: flex;justify-content: center;align-items: center;">
|
|
169
|
+
<div>
|
|
170
|
+
<h1>Auth successful!</h1>
|
|
171
|
+
You may close this window and return to the Gaunt Sloth.
|
|
172
|
+
<div>
|
|
173
|
+
<img src="data:image/bmp;base64,Qk02AwAAAAAAADYAAAAoAAAAEAAAABAAAAABABgAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAnD98ZBtDTQ8sey9YOQobRA8nZx9OdyxahzZrMwERdCtShjZqLQUVLwgZKwUWKQYXsFCRq06KciJPnEh/n0GEmjuClDl9lzl/uFGglz56dChOm0h0SBInLgQVKwgZKgUVgzZchDRfqEeLsUmavFWjvV6joEGFizFvsU+Yw2CowWCiu1qdbCRNKgEQMAofMAcebSRIgC9bt1KdqEqNnD6BokGJqEaOvVmjqEWMu12ftFeYwGCjpUOMUxc8KQIRLAQWhjVmo0qGs1CYqEmOqEmPmjyArk2UrEyQr02TsU2WwV6pvF2eo0CKlDl+OgsiJwQSijZthS9qnDuAo0GFqESMmz2BijZtpUOLnT2CuVicyWmtsVCUjDh0p0KQbiVYJAQSkzx3kzZ7iS5rv2mn3Y3Gq0+Pgi1laCFOfyxmmTx/slCVmT55eihemzyGjTd6NwgfgSxokzl9qkuY4pLR+Lvg1H3EjTRzZB1IjTV3cCNWmjmAgC1diCxpjC1ulDd8RhAtdihbljl/vmK1v2WuuWGgtVWdjDVwgS5ldCVZcydckjl5kzh3ynGwxHOooT6DSA8sQQwkpUeTyGrBlTp8eCNZkzh1rEeSmDx9ZB9JcyNYkzh2tlek5pjT8q3bqU2PPQokJgEPhTVxy2nBkDd6kDV2qEaOwGOvvWGxuViqoUSNlDd6nj6FvmKq13zKnkiJMgUZKwUWPg4poUaOkzp+kTh7nkCExG60033Jym7C03zKsVOdiC9tjjR10W7IeThrKAAOMAUYJgUULgUZdCZdqkqYu1ersk+fvFyww2q4z3fDqUmXhy5ulTp/yWi/Txo6JgEPLgYZLQUXKQUVJwkaSxExq1GTxWW3t1SpsU+gv1mvnUOLjTJ3izh3dTRkJwEQKgQWLQYaLAgcLQUYLQYZLAERezZaiTtmXyFJgjp0ijt1iTlqOAsjNQ0pKQASJwUVKQYYLgUZLAQZLAgcLgUZMAUYZyVRfCthPQoiIQAJOgcgYyBLKAIRLAUZNwsoMQcfKAQV"
|
|
174
|
+
width="16" height="16">
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>`);
|
|
178
|
+
resolveCode(code);
|
|
179
|
+
if (server) {
|
|
180
|
+
displayInfo('Cleaning auth redirect server...');
|
|
181
|
+
server.close();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
const server = redirectApp.listen(portParam, () => {
|
|
186
|
+
const addressInfo = server.address();
|
|
187
|
+
const port = addressInfo.port;
|
|
188
|
+
displayInfo(`OAuth callback server listening at ${port}`);
|
|
189
|
+
resolve({ port, server, codePromise });
|
|
190
|
+
});
|
|
191
|
+
server.on('error', (err) => {
|
|
192
|
+
// If the server fails to start, reject the outer promise.
|
|
193
|
+
reject(err);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=OAuthClientProviderImpl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthClientProviderImpl.js","sourceRoot":"","sources":["../../src/mcp/OAuthClientProviderImpl.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { display, displayError, displayWarning } from '#src/consoleUtils.js';
|
|
2
|
-
import {
|
|
3
|
-
import { ProgressIndicator } from '#src/utils.js';
|
|
2
|
+
import { getJiraCredentials, jiraRequest } from '#src/helpers/jira/jiraClient.js';
|
|
4
3
|
/**
|
|
5
4
|
* Gets Jira issue using Atlassian REST API v3 with Personal Access Token
|
|
6
5
|
*
|
|
@@ -19,28 +18,9 @@ export async function get(config, issueId) {
|
|
|
19
18
|
displayWarning('No issue ID provided');
|
|
20
19
|
return null;
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
const username = env.JIRA_USERNAME || config.username;
|
|
24
|
-
if (!username) {
|
|
25
|
-
throw new Error('Missing JIRA username. The username can be defined as JIRA_USERNAME environment variable or as "username" in config.');
|
|
26
|
-
}
|
|
27
|
-
// Get token from environment variable or config
|
|
28
|
-
const token = env.JIRA_API_PAT_TOKEN || config.token;
|
|
29
|
-
if (!token) {
|
|
30
|
-
throw new Error('Missing JIRA PAT token. The token can be defined as JIRA_API_PAT_TOKEN environment variable or as "token" in config.');
|
|
31
|
-
}
|
|
32
|
-
// Get cloud ID from environment variable or config
|
|
33
|
-
const cloudId = env.JIRA_CLOUD_ID || config.cloudId;
|
|
34
|
-
if (!cloudId) {
|
|
35
|
-
throw new Error('Missing JIRA Cloud ID. The Cloud ID can be defined as JIRA_CLOUD_ID environment variable or as "cloudId" in config.');
|
|
36
|
-
}
|
|
21
|
+
const credentials = getJiraCredentials(config);
|
|
37
22
|
try {
|
|
38
|
-
const issue = await getJiraIssue(
|
|
39
|
-
...config,
|
|
40
|
-
username,
|
|
41
|
-
token,
|
|
42
|
-
cloudId,
|
|
43
|
-
}, issueId);
|
|
23
|
+
const issue = await getJiraIssue(credentials, issueId);
|
|
44
24
|
if (!issue) {
|
|
45
25
|
return null;
|
|
46
26
|
}
|
|
@@ -63,43 +43,20 @@ export async function get(config, issueId) {
|
|
|
63
43
|
* @param jiraKey Jira issue ID
|
|
64
44
|
* @returns Jira issue response
|
|
65
45
|
*/
|
|
66
|
-
async function getJiraIssue(
|
|
46
|
+
async function getJiraIssue(credentials, jiraKey) {
|
|
67
47
|
// Jira Cloud ID can be found by authenticated user at https://company.atlassian.net/_edge/tenant_info
|
|
68
48
|
// According to doc https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issues/#api-rest-api-3-issue-issueidorkey-get permissions to read this resource:
|
|
69
49
|
// https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-issueidorkey-get
|
|
70
50
|
// either Classic (RECOMMENDED) read:jira-work
|
|
71
|
-
// or Granular read:issue-meta:jira, read:issue-security-level:jira, read:issue.vote:jira, read:issue.changelog:jira,
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
display(`Loading Jira issue ${
|
|
51
|
+
// or Granular read:issue-meta:jira, read:issue-security-level:jira, read:issue.vote:jira, read:issue.changelog:jira,
|
|
52
|
+
// read:avatar:jira, read:issue:jira, read:status:jira, read:user:jira, read:field-configuration:jira
|
|
53
|
+
if (credentials.displayUrl) {
|
|
54
|
+
display(`Loading Jira issue ${credentials.displayUrl}${jiraKey}`);
|
|
75
55
|
}
|
|
76
56
|
// This filter will be necessary for V3: `&expand=renderedFields` to convert ADF to HTML
|
|
77
57
|
const filters = '?fields=summary,description'; // Limit JSON to summary and description
|
|
78
|
-
|
|
79
|
-
const credentials = `${config.username}:${config.token}`;
|
|
80
|
-
const encodedCredentials = Buffer.from(credentials).toString('base64');
|
|
81
|
-
const authHeader = `Basic ${encodedCredentials}`;
|
|
82
|
-
// Define request headers
|
|
83
|
-
const headers = {
|
|
84
|
-
Authorization: authHeader,
|
|
85
|
-
Accept: 'application/json; charset=utf-8',
|
|
86
|
-
'Accept-Language': 'en-US,en;q=0.9', // Prevents errors in other languages
|
|
87
|
-
};
|
|
88
|
-
const progressIndicator = new ProgressIndicator(`Retrieving jira from api ${apiUrl.replace(/^https?:\/\//, '')}`);
|
|
89
|
-
const response = await fetch(apiUrl + filters, {
|
|
58
|
+
return jiraRequest(credentials, `/rest/api/2/issue/${jiraKey}${filters}`, {
|
|
90
59
|
method: 'GET',
|
|
91
|
-
headers: headers,
|
|
92
60
|
});
|
|
93
|
-
progressIndicator.stop();
|
|
94
|
-
if (!response?.ok) {
|
|
95
|
-
try {
|
|
96
|
-
const errorData = await response.json();
|
|
97
|
-
throw new Error(`Failed to fetch Jira issue: ${response.statusText} - ${JSON.stringify(errorData)}`);
|
|
98
|
-
}
|
|
99
|
-
catch (_e) {
|
|
100
|
-
throw new Error(`Failed to fetch Jira issue: ${response?.statusText}`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return response.json();
|
|
104
61
|
}
|
|
105
62
|
//# sourceMappingURL=jiraIssueProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jiraIssueProvider.js","sourceRoot":"","sources":["../../src/providers/jiraIssueProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"jiraIssueProvider.js","sourceRoot":"","sources":["../../src/providers/jiraIssueProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE7E,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAYlF;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,MAAkC,EAClC,OAA2B;IAE3B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,cAAc,CAAC,yBAAyB,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,cAAc,CAAC,sBAAsB,CAAC,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAE/C,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC;QACrC,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC;QAE7C,OAAO,eAAe,OAAO,cAAc,OAAO,qBAAqB,WAAW,EAAE,CAAC;IACvF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,YAAY,CACV,6BAA6B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACtF,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,UAAU,YAAY,CACzB,WAAsF,EACtF,OAAe;IAEf,sGAAsG;IAEtG,0KAA0K;IAC1K,sHAAsH;IACtH,8CAA8C;IAC9C,qHAAqH;IACrH,qGAAqG;IAErG,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;QAC3B,OAAO,CAAC,sBAAsB,WAAW,CAAC,UAAU,GAAG,OAAO,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,wFAAwF;IACxF,MAAM,OAAO,GAAG,6BAA6B,CAAC,CAAC,wCAAwC;IAEvF,OAAO,WAAW,CAAoB,WAAW,EAAE,qBAAqB,OAAO,GAAG,OAAO,EAAE,EAAE;QAC3F,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { tool } from '@langchain/core/tools';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { displayWarning } from '#src/consoleUtils.js';
|
|
4
|
+
import jiraLogWork from '#src/helpers/jira/jiraLogWork.js';
|
|
5
|
+
// Define the input schema for the tool
|
|
6
|
+
const gthJiraLogWorkSchema = z.object({
|
|
7
|
+
jiraId: z.string().describe('The Jira issue ID (e.g., "PROJ-123")'),
|
|
8
|
+
timeInSeconds: z.number().describe('Time spent in seconds'),
|
|
9
|
+
comment: z.string().optional().describe('Work log comment'),
|
|
10
|
+
startedAt: z.string().optional().describe('ISO 8601 date string for when work started'),
|
|
11
|
+
});
|
|
12
|
+
const toolDefinition = {
|
|
13
|
+
name: 'gth_jira_log_work',
|
|
14
|
+
description: `Gaunt Sloth Jira Log Work Tool. Log work time to a Jira issue. Requires Jira configuration with credentials.
|
|
15
|
+
Example: gth_jira_log_work({ jiraId: "PROJ-123", timeInSeconds: 3600, comment: "Implemented feature X" })`,
|
|
16
|
+
schema: gthJiraLogWorkSchema,
|
|
17
|
+
};
|
|
18
|
+
function getToolImpl(config) {
|
|
19
|
+
let toolImpl = async ({ jiraId, timeInSeconds, comment = 'Work logged', startedAt, }) => {
|
|
20
|
+
const jiraConfig = config || {};
|
|
21
|
+
const startDate = startedAt ? new Date(startedAt) : new Date();
|
|
22
|
+
return await jiraLogWork(jiraConfig, jiraId, timeInSeconds, comment, startDate);
|
|
23
|
+
};
|
|
24
|
+
return tool(toolImpl, toolDefinition);
|
|
25
|
+
}
|
|
26
|
+
// Export a default instance that uses environment variables
|
|
27
|
+
export function get(config) {
|
|
28
|
+
if (!config.prebuiltToolsConfig?.jira && config.requirementsProviderConfig?.jira) {
|
|
29
|
+
displayWarning('config.prebuiltToolsConfig.jira is not defined. Using config.requirementsProviderConfig.jira.');
|
|
30
|
+
}
|
|
31
|
+
let jiraConfig = config.prebuiltToolsConfig?.jira || config.requirementsProviderConfig?.jira;
|
|
32
|
+
if (!jiraConfig) {
|
|
33
|
+
throw new Error('gth_jira_log_work is added to preBuiltTools, but no Jira config is provided.');
|
|
34
|
+
}
|
|
35
|
+
return getToolImpl(jiraConfig);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=gthJiraLogWorkTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gthJiraLogWorkTool.js","sourceRoot":"","sources":["../../src/tools/gthJiraLogWorkTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,WAAW,MAAM,kCAAkC,CAAC;AAI3D,uCAAuC;AACvC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;IACnE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;IAC3D,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IAC3D,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;CACxF,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG;IACrB,IAAI,EAAE,mBAAmB;IACzB,WAAW,EAAE;0GAC2F;IACxG,MAAM,EAAE,oBAAoB;CAC7B,CAAC;AAEF,SAAS,WAAW,CAAC,MAA4B;IAC/C,IAAI,QAAQ,GAAG,KAAK,EAAE,EACpB,MAAM,EACN,aAAa,EACb,OAAO,GAAG,aAAa,EACvB,SAAS,GAC4B,EAAmB,EAAE;QAC1D,MAAM,UAAU,GAAG,MAAM,IAAI,EAAE,CAAC;QAEhC,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;QAE/D,OAAO,MAAM,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;IAClF,CAAC,CAAC;IACF,OAAO,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;AACxC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,GAAG,CAAC,MAAmB;IACrC,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,IAAI,IAAI,MAAM,CAAC,0BAA0B,EAAE,IAAI,EAAE,CAAC;QACjF,cAAc,CACZ,+FAA+F,CAChG,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,GAAG,MAAM,CAAC,mBAAmB,EAAE,IAAI,IAAI,MAAM,CAAC,0BAA0B,EAAE,IAAI,CAAC;IAC7F,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;IAClG,CAAC;IACD,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC;AACjC,CAAC"}
|