@egain/egain-mcp-server 1.0.6 → 1.0.11
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/README.md +22 -0
- package/bin/mcp-server.js +191 -171
- package/bin/mcp-server.js.map +9 -9
- package/esm/src/funcs/getPortals.d.ts +48 -7
- package/esm/src/funcs/getPortals.d.ts.map +1 -1
- package/esm/src/funcs/getPortals.js +48 -7
- package/esm/src/funcs/getPortals.js.map +1 -1
- package/esm/src/hooks/auth-hook.d.ts +6 -1
- package/esm/src/hooks/auth-hook.d.ts.map +1 -1
- package/esm/src/hooks/auth-hook.js +174 -197
- package/esm/src/hooks/auth-hook.js.map +1 -1
- package/esm/src/hooks/tooltip-images.d.ts +10 -0
- package/esm/src/hooks/tooltip-images.d.ts.map +1 -0
- package/esm/src/hooks/tooltip-images.js +12 -0
- package/esm/src/hooks/tooltip-images.js.map +1 -0
- package/esm/src/lib/config.d.ts +2 -2
- package/esm/src/lib/config.js +2 -2
- package/esm/src/lib/config.js.map +1 -1
- package/esm/src/mcp-server/mcp-server.js +1 -1
- package/esm/src/mcp-server/mcp-server.js.map +1 -1
- package/esm/src/mcp-server/server.js +1 -1
- package/esm/src/mcp-server/server.js.map +1 -1
- package/esm/src/mcp-server/tools/getPortals.d.ts.map +1 -1
- package/esm/src/mcp-server/tools/getPortals.js +48 -7
- package/esm/src/mcp-server/tools/getPortals.js.map +1 -1
- package/esm/src/models/getmyportalsop.d.ts +1 -1
- package/esm/src/models/getmyportalsop.d.ts.map +1 -1
- package/manifest.json +2 -2
- package/package.json +1 -1
- package/src/funcs/getPortals.ts +48 -7
- package/src/hooks/auth-hook.ts +185 -224
- package/src/lib/config.ts +2 -2
- package/src/mcp-server/mcp-server.ts +1 -1
- package/src/mcp-server/server.ts +1 -1
- package/src/mcp-server/tools/getPortals.ts +48 -7
- package/src/models/getmyportalsop.ts +1 -1
|
@@ -10,14 +10,55 @@ import { Result } from "../types/fp.js";
|
|
|
10
10
|
* Get All Portals Accessible To User
|
|
11
11
|
*
|
|
12
12
|
* @remarks
|
|
13
|
+
* Get All Portals Accessible to User
|
|
14
|
+
*
|
|
13
15
|
* ## Overview
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* The Get All Portals Accessible to User API allows a user to fetch all portals accessible to the user across all departments.
|
|
17
|
+
* - If no access tags are specified for a portal, any user can access the portal.
|
|
18
|
+
* - If access tags are specified for a portal, users with a user profile that allows access can access the portal. For users with multiple user profiles, the user profile that allows access does not need to be the active user profile.
|
|
19
|
+
* - Global users (partition) cannot be assigned user profiles; their access is limited to portals without access restrictions.
|
|
20
|
+
* - The only articles returned are associated to an Article type when the parameter “Include in browse on portals” is set to "Yes".
|
|
21
|
+
* - When the `shortUrlTemplate` query parameter is provided, the API filters accessible portals according to the specified language and template name. A portal short URL specific to the `shortUrlTemplate` value is returned in the response when available. If there is no short URL for a language, the portal object returns an empty `shortURL` field.
|
|
22
|
+
*
|
|
23
|
+
* ## Pagination behavior (CRITICAL for AI assistants)
|
|
24
|
+
*
|
|
25
|
+
* **IMPORTANT**: This endpoint is paginated. When searching for a portal by name or listing portals, you MUST automatically fetch ALL pages before concluding that a portal doesn't exist.
|
|
26
|
+
*
|
|
27
|
+
* ### Automatic pagination is REQUIRED when:
|
|
28
|
+
* - User asks to find a portal by name (e.g., "business portal", "Master portal")
|
|
29
|
+
* - User requests to list or see all portals
|
|
30
|
+
* - You need to resolve a natural portal name to its ID
|
|
31
|
+
*
|
|
32
|
+
* ### How to detect more pages exist:
|
|
33
|
+
* The response includes `paginationInfo` with:
|
|
34
|
+
* - `count`: Total number of items across all pages
|
|
35
|
+
* - `pagenum`: Current page number
|
|
36
|
+
* - `pagesize`: Items per page (default: 25)
|
|
37
|
+
*
|
|
38
|
+
* **Check for more pages if ANY of these are true:**
|
|
39
|
+
* 1. The number of portals returned equals `pagesize` (e.g., exactly 25 portals returned)
|
|
40
|
+
* 2. `paginationInfo.count > (pagenum * pagesize)` - there are more items beyond this page
|
|
41
|
+
* 3. The response includes a `link` array with a `next` relation
|
|
42
|
+
*
|
|
43
|
+
* ### Required pagination workflow:
|
|
44
|
+
* 1. Start with `$pagenum=1` and `$pagesize=25` (default)
|
|
45
|
+
* 2. After receiving the response, check `paginationInfo`
|
|
46
|
+
* 3. **If more pages exist** (using the checks above), automatically call this endpoint again with `$pagenum=2`, then `$pagenum=3`, etc.
|
|
47
|
+
* 4. Continue incrementing `$pagenum` until:
|
|
48
|
+
* - A page returns fewer portals than `pagesize` (indicating the last page)
|
|
49
|
+
* - A page returns zero portals
|
|
50
|
+
* - `pagenum * pagesize >= paginationInfo.count` (if count represents total items)
|
|
51
|
+
* 5. Merge all portals from all pages by unique portal ID
|
|
52
|
+
* 6. Only then search through the complete merged list or report results to the user
|
|
53
|
+
*
|
|
54
|
+
* ### Example scenario:
|
|
55
|
+
* If you search for "business portal" and the first page returns 25 portals but none match:
|
|
56
|
+
* - DO NOT immediately tell the user the portal doesn't exist
|
|
57
|
+
* - Check `paginationInfo.count` - if it's > 25, automatically fetch page 2
|
|
58
|
+
* - Continue fetching until all pages are retrieved
|
|
59
|
+
* - Search the complete merged list before concluding the portal doesn't exist
|
|
60
|
+
*
|
|
61
|
+
* This ensures reliable portal name-to-ID resolution and prevents false "not found" errors.
|
|
21
62
|
*/
|
|
22
63
|
export declare function getPortals(client$: EgainMcpCore, request: GetMyPortalsRequest, options?: RequestOptions): APIPromise<Result<GetMyPortalsResponse, APIError | SDKValidationError | UnexpectedClientError | InvalidRequestError | RequestAbortedError | RequestTimeoutError | ConnectionError>>;
|
|
23
64
|
//# sourceMappingURL=getPortals.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getPortals.d.ts","sourceRoot":"","sources":["../../../src/funcs/getPortals.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK1C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EACL,mBAAmB,EAEnB,oBAAoB,EAErB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAW,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC
|
|
1
|
+
{"version":3,"file":"getPortals.d.ts","sourceRoot":"","sources":["../../../src/funcs/getPortals.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK1C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAC5E,OAAO,EACL,mBAAmB,EAEnB,oBAAoB,EAErB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAW,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,cAAc,GACvB,UAAU,CACX,MAAM,CACJ,oBAAoB,EAClB,QAAQ,GACR,kBAAkB,GAClB,qBAAqB,GACrB,mBAAmB,GACnB,mBAAmB,GACnB,mBAAmB,GACnB,eAAe,CAClB,CACF,CAMA"}
|
|
@@ -13,14 +13,55 @@ import { APIPromise } from "../types/async.js";
|
|
|
13
13
|
* Get All Portals Accessible To User
|
|
14
14
|
*
|
|
15
15
|
* @remarks
|
|
16
|
+
* Get All Portals Accessible to User
|
|
17
|
+
*
|
|
16
18
|
* ## Overview
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
19
|
+
* The Get All Portals Accessible to User API allows a user to fetch all portals accessible to the user across all departments.
|
|
20
|
+
* - If no access tags are specified for a portal, any user can access the portal.
|
|
21
|
+
* - If access tags are specified for a portal, users with a user profile that allows access can access the portal. For users with multiple user profiles, the user profile that allows access does not need to be the active user profile.
|
|
22
|
+
* - Global users (partition) cannot be assigned user profiles; their access is limited to portals without access restrictions.
|
|
23
|
+
* - The only articles returned are associated to an Article type when the parameter “Include in browse on portals” is set to "Yes".
|
|
24
|
+
* - When the `shortUrlTemplate` query parameter is provided, the API filters accessible portals according to the specified language and template name. A portal short URL specific to the `shortUrlTemplate` value is returned in the response when available. If there is no short URL for a language, the portal object returns an empty `shortURL` field.
|
|
25
|
+
*
|
|
26
|
+
* ## Pagination behavior (CRITICAL for AI assistants)
|
|
27
|
+
*
|
|
28
|
+
* **IMPORTANT**: This endpoint is paginated. When searching for a portal by name or listing portals, you MUST automatically fetch ALL pages before concluding that a portal doesn't exist.
|
|
29
|
+
*
|
|
30
|
+
* ### Automatic pagination is REQUIRED when:
|
|
31
|
+
* - User asks to find a portal by name (e.g., "business portal", "Master portal")
|
|
32
|
+
* - User requests to list or see all portals
|
|
33
|
+
* - You need to resolve a natural portal name to its ID
|
|
34
|
+
*
|
|
35
|
+
* ### How to detect more pages exist:
|
|
36
|
+
* The response includes `paginationInfo` with:
|
|
37
|
+
* - `count`: Total number of items across all pages
|
|
38
|
+
* - `pagenum`: Current page number
|
|
39
|
+
* - `pagesize`: Items per page (default: 25)
|
|
40
|
+
*
|
|
41
|
+
* **Check for more pages if ANY of these are true:**
|
|
42
|
+
* 1. The number of portals returned equals `pagesize` (e.g., exactly 25 portals returned)
|
|
43
|
+
* 2. `paginationInfo.count > (pagenum * pagesize)` - there are more items beyond this page
|
|
44
|
+
* 3. The response includes a `link` array with a `next` relation
|
|
45
|
+
*
|
|
46
|
+
* ### Required pagination workflow:
|
|
47
|
+
* 1. Start with `$pagenum=1` and `$pagesize=25` (default)
|
|
48
|
+
* 2. After receiving the response, check `paginationInfo`
|
|
49
|
+
* 3. **If more pages exist** (using the checks above), automatically call this endpoint again with `$pagenum=2`, then `$pagenum=3`, etc.
|
|
50
|
+
* 4. Continue incrementing `$pagenum` until:
|
|
51
|
+
* - A page returns fewer portals than `pagesize` (indicating the last page)
|
|
52
|
+
* - A page returns zero portals
|
|
53
|
+
* - `pagenum * pagesize >= paginationInfo.count` (if count represents total items)
|
|
54
|
+
* 5. Merge all portals from all pages by unique portal ID
|
|
55
|
+
* 6. Only then search through the complete merged list or report results to the user
|
|
56
|
+
*
|
|
57
|
+
* ### Example scenario:
|
|
58
|
+
* If you search for "business portal" and the first page returns 25 portals but none match:
|
|
59
|
+
* - DO NOT immediately tell the user the portal doesn't exist
|
|
60
|
+
* - Check `paginationInfo.count` - if it's > 25, automatically fetch page 2
|
|
61
|
+
* - Continue fetching until all pages are retrieved
|
|
62
|
+
* - Search the complete merged list before concluding the portal doesn't exist
|
|
63
|
+
*
|
|
64
|
+
* This ensures reliable portal name-to-ID resolution and prevents false "not found" errors.
|
|
24
65
|
*/
|
|
25
66
|
export function getPortals(client$, request, options) {
|
|
26
67
|
return new APIPromise($do(client$, request, options));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getPortals.js","sourceRoot":"","sources":["../../../src/funcs/getPortals.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,KAAK,CAAC,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAU3C,OAAO,EAEL,6BAA6B,EAE7B,8BAA8B,GAC/B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAW,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAGxD
|
|
1
|
+
{"version":3,"file":"getPortals.js","sourceRoot":"","sources":["../../../src/funcs/getPortals.ts"],"names":[],"mappings":"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"}
|
|
@@ -61,7 +61,6 @@ export declare class AuthenticationHook implements SDKInitHook, BeforeRequestHoo
|
|
|
61
61
|
* Monitor browser window for authorization code in URL
|
|
62
62
|
* Works with ANY redirect URL - detects when URL contains code= parameter
|
|
63
63
|
*/
|
|
64
|
-
private monitorBrowserForAuthCode;
|
|
65
64
|
private getUserAccessToken;
|
|
66
65
|
private saveToken;
|
|
67
66
|
/**
|
|
@@ -84,6 +83,12 @@ export declare class AuthenticationHook implements SDKInitHook, BeforeRequestHoo
|
|
|
84
83
|
* Start the configuration HTTP server
|
|
85
84
|
*/
|
|
86
85
|
private startConfigServer;
|
|
86
|
+
/**
|
|
87
|
+
* Monitor browser for authorization code with retry on OAuth errors
|
|
88
|
+
* This method will continue monitoring even after OAuth errors (like wrong password)
|
|
89
|
+
* to allow users to retry authentication
|
|
90
|
+
*/
|
|
91
|
+
private monitorBrowserWithRetry;
|
|
87
92
|
/**
|
|
88
93
|
* Stop the configuration HTTP server
|
|
89
94
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-hook.d.ts","sourceRoot":"","sources":["../../../src/hooks/auth-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAgrC9C,qBAAa,kBAAmB,YAAW,WAAW,EAAE,iBAAiB;IACvE,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAC,CAAM;IAC9B,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,oBAAoB,CAAkB;IAG9C;;;OAGG;IACH,OAAO,CAAC,kBAAkB;gBAuBd,eAAe,CAAC,EAAE,GAAG;IAUjC;;OAEG;YACW,oBAAoB;IA2BlC;;OAEG;YACW,kBAAkB;IA+EhC;;OAEG;YACW,oBAAoB;IAkFlC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAyBxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;OAEG;IACH,OAAO,CAAC,cAAc;IA2GtB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,YAAY;IAgEpB;;;OAGG;YACW,
|
|
1
|
+
{"version":3,"file":"auth-hook.d.ts","sourceRoot":"","sources":["../../../src/hooks/auth-hook.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAgrC9C,qBAAa,kBAAmB,YAAW,WAAW,EAAE,iBAAiB;IACvE,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAC,CAAM;IAC9B,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,aAAa,CAAkB;IACvC,OAAO,CAAC,oBAAoB,CAAkB;IAG9C;;;OAGG;IACH,OAAO,CAAC,kBAAkB;gBAuBd,eAAe,CAAC,EAAE,GAAG;IAUjC;;OAEG;YACW,oBAAoB;IA2BlC;;OAEG;YACW,kBAAkB;IA+EhC;;OAEG;YACW,oBAAoB;IAkFlC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAyBxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;OAEG;IACH,OAAO,CAAC,cAAc;IA2GtB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,YAAY;IAgEpB;;;OAGG;YACW,kBAAkB;YA2GlB,SAAS;IAmBvB;;;;OAIG;YACW,uBAAuB;IA2BrC;;;OAGG;IACH,OAAO,CAAC,YAAY;IA2CpB,OAAO,CAAC,iBAAiB;IAmBzB;;OAEG;IACI,UAAU,IAAI,IAAI;IAwBzB;;OAEG;YACW,iBAAiB;IAoX/B;;;;OAIG;YACW,uBAAuB;IAqJrC;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;OAEG;YACW,iBAAiB;IA0IlB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IA2FtC,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IA8B9E,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU;IAoCrC,OAAO,CAAC,eAAe;CAiCxB"}
|
|
@@ -1657,149 +1657,6 @@ export class AuthenticationHook {
|
|
|
1657
1657
|
* Monitor browser window for authorization code in URL
|
|
1658
1658
|
* Works with ANY redirect URL - detects when URL contains code= parameter
|
|
1659
1659
|
*/
|
|
1660
|
-
async monitorBrowserForAuthCode() {
|
|
1661
|
-
const platform = process.platform;
|
|
1662
|
-
const timeout = 120; // 2 minutes
|
|
1663
|
-
const startTime = Date.now();
|
|
1664
|
-
if (platform === 'darwin') {
|
|
1665
|
-
// macOS - Monitor using AppleScript
|
|
1666
|
-
console.error(`🔍 Monitoring ${this.detectedBrowser} for authorization code...`);
|
|
1667
|
-
let lastUrl = '';
|
|
1668
|
-
while ((Date.now() - startTime) < timeout * 1000) {
|
|
1669
|
-
try {
|
|
1670
|
-
// Get URL from browser using AppleScript
|
|
1671
|
-
const script = `
|
|
1672
|
-
tell application "${this.detectedBrowser}"
|
|
1673
|
-
try
|
|
1674
|
-
set currentURL to URL of active tab of front window
|
|
1675
|
-
return currentURL
|
|
1676
|
-
on error
|
|
1677
|
-
return ""
|
|
1678
|
-
end try
|
|
1679
|
-
end tell
|
|
1680
|
-
`;
|
|
1681
|
-
const { stdout } = await execAsync(`osascript -e '${script}'`);
|
|
1682
|
-
const currentUrl = stdout.trim();
|
|
1683
|
-
if (currentUrl && currentUrl !== lastUrl) {
|
|
1684
|
-
lastUrl = currentUrl;
|
|
1685
|
-
console.error(`🔍 Current URL: ${currentUrl}`);
|
|
1686
|
-
}
|
|
1687
|
-
// Check if URL contains code= parameter (regardless of domain)
|
|
1688
|
-
if (currentUrl && currentUrl.includes('code=')) {
|
|
1689
|
-
console.error('✅ Found authorization code in URL!');
|
|
1690
|
-
// Extract the code from the URL
|
|
1691
|
-
const codeMatch = currentUrl.match(/[?&]code=([^&]+)/);
|
|
1692
|
-
if (codeMatch && codeMatch[1]) {
|
|
1693
|
-
const code = decodeURIComponent(codeMatch[1]);
|
|
1694
|
-
console.error(`🔑 Extracted authorization code (first 20 chars): ${code.substring(0, 20)}...`);
|
|
1695
|
-
console.error(` Code length: ${code.length} characters`);
|
|
1696
|
-
// Close the browser window immediately (non-blocking - fire and forget)
|
|
1697
|
-
setImmediate(async () => {
|
|
1698
|
-
try {
|
|
1699
|
-
await execAsync(`osascript -e 'tell application "${this.detectedBrowser}" to close front window'`);
|
|
1700
|
-
console.error('✅ Browser window closed');
|
|
1701
|
-
}
|
|
1702
|
-
catch (closeError) {
|
|
1703
|
-
console.error('⚠️ Could not close browser window:', closeError);
|
|
1704
|
-
}
|
|
1705
|
-
});
|
|
1706
|
-
// Return code immediately without waiting for window close
|
|
1707
|
-
return code;
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
// Also check for error parameters
|
|
1711
|
-
if (currentUrl && currentUrl.includes('error=')) {
|
|
1712
|
-
const errorMatch = currentUrl.match(/[?&]error=([^&]+)/);
|
|
1713
|
-
const errorDescMatch = currentUrl.match(/error_description=([^&]+)/);
|
|
1714
|
-
const error = errorMatch && errorMatch[1] ? decodeURIComponent(errorMatch[1]) : 'unknown_error';
|
|
1715
|
-
const errorDesc = errorDescMatch && errorDescMatch[1] ? decodeURIComponent(errorDescMatch[1]) : 'No description';
|
|
1716
|
-
// Throw OAuth error - this will stop monitoring but window stays open so user can see the error
|
|
1717
|
-
throw new Error(`OAuth error: ${error} - ${errorDesc}`);
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
catch (error) {
|
|
1721
|
-
// Re-throw OAuth errors (they should stop monitoring but window stays open)
|
|
1722
|
-
if (error instanceof Error && error.message.includes('OAuth error:')) {
|
|
1723
|
-
throw error;
|
|
1724
|
-
}
|
|
1725
|
-
// Ignore AppleScript errors and continue monitoring
|
|
1726
|
-
}
|
|
1727
|
-
// Wait 500ms before checking again
|
|
1728
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
1729
|
-
}
|
|
1730
|
-
throw new Error('Authentication timeout. Please try again.');
|
|
1731
|
-
}
|
|
1732
|
-
else if (platform === 'win32') {
|
|
1733
|
-
// Windows - Monitor browser window title (contains URL in most browsers)
|
|
1734
|
-
console.error(`🔍 Monitoring ${this.detectedBrowser} for authorization code...`);
|
|
1735
|
-
let lastTitle = '';
|
|
1736
|
-
while ((Date.now() - startTime) < timeout * 1000) {
|
|
1737
|
-
try {
|
|
1738
|
-
// PowerShell script to get browser window title
|
|
1739
|
-
// Window titles often contain the URL or page title
|
|
1740
|
-
const browserProcessName = this.detectedBrowser.replace('.exe', '');
|
|
1741
|
-
const psScript = `
|
|
1742
|
-
$process = Get-Process -Name "${browserProcessName}" -ErrorAction SilentlyContinue |
|
|
1743
|
-
Where-Object { $_.MainWindowHandle -ne 0 } |
|
|
1744
|
-
Select-Object -First 1
|
|
1745
|
-
if ($process) {
|
|
1746
|
-
$process.MainWindowTitle
|
|
1747
|
-
}
|
|
1748
|
-
`.replace(/\n\s+/g, ' ');
|
|
1749
|
-
const { stdout } = await execAsync(`powershell -Command "${psScript}"`);
|
|
1750
|
-
const windowTitle = stdout.trim();
|
|
1751
|
-
if (windowTitle && windowTitle !== lastTitle) {
|
|
1752
|
-
lastTitle = windowTitle;
|
|
1753
|
-
console.error(`🔍 Browser window: ${windowTitle.substring(0, 100)}...`);
|
|
1754
|
-
// Check if title or URL contains the code parameter
|
|
1755
|
-
// Most browsers show URL in the title or we can detect redirect completion
|
|
1756
|
-
if (windowTitle.includes('code=') || windowTitle.includes('localhost:3333')) {
|
|
1757
|
-
console.error('✅ Detected OAuth callback!');
|
|
1758
|
-
// Try to extract code from title if visible
|
|
1759
|
-
const codeMatch = windowTitle.match(/code=([^&\s]+)/);
|
|
1760
|
-
if (codeMatch && codeMatch[1]) {
|
|
1761
|
-
const code = decodeURIComponent(codeMatch[1]);
|
|
1762
|
-
console.error(`🔑 Extracted authorization code (first 20 chars): ${code.substring(0, 20)}...`);
|
|
1763
|
-
console.error(` Code length: ${code.length} characters`);
|
|
1764
|
-
// Close browser window immediately (non-blocking - fire and forget)
|
|
1765
|
-
setImmediate(async () => {
|
|
1766
|
-
try {
|
|
1767
|
-
await execAsync(`powershell -Command "Stop-Process -Name '${browserProcessName}' -Force"`);
|
|
1768
|
-
console.error('✅ Browser window closed');
|
|
1769
|
-
}
|
|
1770
|
-
catch (closeError) {
|
|
1771
|
-
console.error('⚠️ Could not close browser window:', closeError);
|
|
1772
|
-
}
|
|
1773
|
-
});
|
|
1774
|
-
// Return code immediately without waiting for window close
|
|
1775
|
-
return code;
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
// Check for OAuth error in title
|
|
1779
|
-
if (windowTitle.includes('error=')) {
|
|
1780
|
-
const errorMatch = windowTitle.match(/error=([^&\s]+)/);
|
|
1781
|
-
const error = errorMatch && errorMatch[1] ? decodeURIComponent(errorMatch[1]) : 'unknown_error';
|
|
1782
|
-
// Throw OAuth error - this will stop monitoring but window stays open so user can see the error
|
|
1783
|
-
throw new Error(`OAuth error: ${error}`);
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
}
|
|
1787
|
-
catch (error) {
|
|
1788
|
-
// Re-throw OAuth errors (they should stop monitoring but window stays open)
|
|
1789
|
-
if (error instanceof Error && error.message.includes('OAuth error:')) {
|
|
1790
|
-
throw error;
|
|
1791
|
-
}
|
|
1792
|
-
// Ignore other errors and continue monitoring
|
|
1793
|
-
}
|
|
1794
|
-
// Wait 500ms before checking again
|
|
1795
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
1796
|
-
}
|
|
1797
|
-
throw new Error('Authentication timeout. The browser window title did not show the authorization code. Please ensure your redirect URL is http://localhost:3333/callback for automatic detection on Windows.');
|
|
1798
|
-
}
|
|
1799
|
-
else {
|
|
1800
|
-
throw new Error('Linux is not supported. Use macOS or Windows for automatic authentication.');
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
1660
|
async getUserAccessToken(code) {
|
|
1804
1661
|
const { clientId, clientSecret, redirectUri, accessUrl } = this.authConfig;
|
|
1805
1662
|
console.error('🔄 Starting token exchange...');
|
|
@@ -1963,6 +1820,22 @@ export class AuthenticationHook {
|
|
|
1963
1820
|
}
|
|
1964
1821
|
else {
|
|
1965
1822
|
console.error(`⏰ AUTH: Token expires in ${Math.round(timeUntilExpiry / 1000)} seconds - treating as expired`);
|
|
1823
|
+
// Delete expired token files to prevent reuse
|
|
1824
|
+
const projectRoot = getProjectRoot();
|
|
1825
|
+
const tokenPath = path.join(projectRoot, '.bearer_token');
|
|
1826
|
+
try {
|
|
1827
|
+
if (fs.existsSync(tokenPath)) {
|
|
1828
|
+
fs.unlinkSync(tokenPath);
|
|
1829
|
+
console.error('🗑️ Deleted expired bearer token file');
|
|
1830
|
+
}
|
|
1831
|
+
if (fs.existsSync(metadataPath)) {
|
|
1832
|
+
fs.unlinkSync(metadataPath);
|
|
1833
|
+
console.error('🗑️ Deleted expired bearer token metadata file');
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
catch (error) {
|
|
1837
|
+
console.error('⚠️ Failed to delete expired token files:', error);
|
|
1838
|
+
}
|
|
1966
1839
|
return false;
|
|
1967
1840
|
}
|
|
1968
1841
|
}
|
|
@@ -2117,31 +1990,7 @@ export class AuthenticationHook {
|
|
|
2117
1990
|
// Start monitoring browser in background (for ANY redirect URL)
|
|
2118
1991
|
console.error('🔍 Starting browser URL monitoring for authorization code...');
|
|
2119
1992
|
setImmediate(async () => {
|
|
2120
|
-
|
|
2121
|
-
const code = await this.monitorBrowserForAuthCode();
|
|
2122
|
-
console.error('✅ Authorization code detected:', code.substring(0, 10) + '...');
|
|
2123
|
-
const accessToken = await this.getUserAccessToken(code);
|
|
2124
|
-
console.error('✅ Access token received');
|
|
2125
|
-
this.token = accessToken;
|
|
2126
|
-
// Trigger cache initialization if available
|
|
2127
|
-
if (this.portalCacheHook) {
|
|
2128
|
-
try {
|
|
2129
|
-
const fakeRequest = new Request(this.authConfig.environmentUrl, {
|
|
2130
|
-
headers: { 'Authorization': `Bearer ${accessToken}` }
|
|
2131
|
-
});
|
|
2132
|
-
await this.portalCacheHook.ensureCacheInitialized(fakeRequest);
|
|
2133
|
-
}
|
|
2134
|
-
catch (error) {
|
|
2135
|
-
// Cache init failure is non-fatal
|
|
2136
|
-
}
|
|
2137
|
-
}
|
|
2138
|
-
console.error('🎉 Authentication complete! Stopping config server...');
|
|
2139
|
-
this.stopConfigServer();
|
|
2140
|
-
}
|
|
2141
|
-
catch (authError) {
|
|
2142
|
-
console.error('❌ Authentication monitoring error:', authError);
|
|
2143
|
-
this.stopConfigServer();
|
|
2144
|
-
}
|
|
1993
|
+
await this.monitorBrowserWithRetry();
|
|
2145
1994
|
});
|
|
2146
1995
|
}
|
|
2147
1996
|
catch (error) {
|
|
@@ -2230,33 +2079,7 @@ export class AuthenticationHook {
|
|
|
2230
2079
|
// Start monitoring browser in background (for ANY redirect URL)
|
|
2231
2080
|
console.error('🔍 Starting browser URL monitoring for authorization code...');
|
|
2232
2081
|
setImmediate(async () => {
|
|
2233
|
-
|
|
2234
|
-
const code = await this.monitorBrowserForAuthCode();
|
|
2235
|
-
console.error('✅ Authorization code detected:', code.substring(0, 10) + '...');
|
|
2236
|
-
const accessToken = await this.getUserAccessToken(code);
|
|
2237
|
-
console.error('✅ Access token received');
|
|
2238
|
-
this.token = accessToken;
|
|
2239
|
-
// Trigger cache initialization if available
|
|
2240
|
-
if (this.portalCacheHook) {
|
|
2241
|
-
console.error('🔄 Triggering cache initialization...');
|
|
2242
|
-
try {
|
|
2243
|
-
const fakeRequest = new Request(this.authConfig.environmentUrl, {
|
|
2244
|
-
headers: { 'Authorization': `Bearer ${accessToken}` }
|
|
2245
|
-
});
|
|
2246
|
-
await this.portalCacheHook.ensureCacheInitialized(fakeRequest);
|
|
2247
|
-
console.error('✅ Cache initialization completed');
|
|
2248
|
-
}
|
|
2249
|
-
catch (error) {
|
|
2250
|
-
console.error('⚠️ Cache initialization failed:', error);
|
|
2251
|
-
}
|
|
2252
|
-
}
|
|
2253
|
-
console.error('🎉 Authentication complete! Stopping config server...');
|
|
2254
|
-
this.stopConfigServer();
|
|
2255
|
-
}
|
|
2256
|
-
catch (authError) {
|
|
2257
|
-
console.error('❌ Authentication monitoring error:', authError);
|
|
2258
|
-
this.stopConfigServer();
|
|
2259
|
-
}
|
|
2082
|
+
await this.monitorBrowserWithRetry();
|
|
2260
2083
|
});
|
|
2261
2084
|
}
|
|
2262
2085
|
catch (error) {
|
|
@@ -2322,8 +2145,18 @@ export class AuthenticationHook {
|
|
|
2322
2145
|
this.stopConfigServer();
|
|
2323
2146
|
}
|
|
2324
2147
|
catch (authError) {
|
|
2325
|
-
|
|
2326
|
-
|
|
2148
|
+
// Check if this is an OAuth error (like wrong username/password)
|
|
2149
|
+
const isOAuthError = authError instanceof Error && authError.message.includes('OAuth error:');
|
|
2150
|
+
if (isOAuthError) {
|
|
2151
|
+
// For OAuth errors, don't stop the server - allow user to try again
|
|
2152
|
+
console.error('❌ OAuth authentication error:', authError.message);
|
|
2153
|
+
console.error('💡 The configuration server will remain running. Please try again with correct credentials.');
|
|
2154
|
+
}
|
|
2155
|
+
else {
|
|
2156
|
+
// For other token exchange errors, stop the server
|
|
2157
|
+
console.error('❌ Token exchange error:', authError);
|
|
2158
|
+
this.stopConfigServer();
|
|
2159
|
+
}
|
|
2327
2160
|
}
|
|
2328
2161
|
});
|
|
2329
2162
|
// Send success page to browser
|
|
@@ -2397,6 +2230,150 @@ export class AuthenticationHook {
|
|
|
2397
2230
|
});
|
|
2398
2231
|
});
|
|
2399
2232
|
}
|
|
2233
|
+
/**
|
|
2234
|
+
* Monitor browser for authorization code with retry on OAuth errors
|
|
2235
|
+
* This method will continue monitoring even after OAuth errors (like wrong password)
|
|
2236
|
+
* to allow users to retry authentication
|
|
2237
|
+
*/
|
|
2238
|
+
async monitorBrowserWithRetry() {
|
|
2239
|
+
const platform = process.platform;
|
|
2240
|
+
const timeout = 120; // 2 minutes
|
|
2241
|
+
const startTime = Date.now();
|
|
2242
|
+
let oAuthErrorLogged = false;
|
|
2243
|
+
let lastUrl = '';
|
|
2244
|
+
let lastErrorUrl = null;
|
|
2245
|
+
// Log monitoring start only once
|
|
2246
|
+
if (platform === 'darwin') {
|
|
2247
|
+
console.error(`🔍 Monitoring ${this.detectedBrowser} for authorization code...`);
|
|
2248
|
+
}
|
|
2249
|
+
else if (platform === 'win32') {
|
|
2250
|
+
console.error(`🔍 Monitoring ${this.detectedBrowser} for authorization code...`);
|
|
2251
|
+
}
|
|
2252
|
+
while (true) {
|
|
2253
|
+
try {
|
|
2254
|
+
// Check timeout
|
|
2255
|
+
if ((Date.now() - startTime) >= timeout * 1000) {
|
|
2256
|
+
throw new Error('Authentication timeout. Please try again.');
|
|
2257
|
+
}
|
|
2258
|
+
let currentUrl = '';
|
|
2259
|
+
if (platform === 'darwin') {
|
|
2260
|
+
// macOS - Get URL from browser using AppleScript
|
|
2261
|
+
const script = `
|
|
2262
|
+
tell application "${this.detectedBrowser}"
|
|
2263
|
+
try
|
|
2264
|
+
set currentURL to URL of active tab of front window
|
|
2265
|
+
return currentURL
|
|
2266
|
+
on error
|
|
2267
|
+
return ""
|
|
2268
|
+
end try
|
|
2269
|
+
end tell
|
|
2270
|
+
`;
|
|
2271
|
+
const { stdout } = await execAsync(`osascript -e '${script}'`);
|
|
2272
|
+
currentUrl = stdout.trim();
|
|
2273
|
+
}
|
|
2274
|
+
else if (platform === 'win32') {
|
|
2275
|
+
// Windows - Get browser window title
|
|
2276
|
+
const browserProcessName = this.detectedBrowser.replace('.exe', '');
|
|
2277
|
+
const psScript = `
|
|
2278
|
+
$process = Get-Process -Name "${browserProcessName}" -ErrorAction SilentlyContinue |
|
|
2279
|
+
Where-Object { $_.MainWindowHandle -ne 0 } |
|
|
2280
|
+
Select-Object -First 1
|
|
2281
|
+
if ($process) {
|
|
2282
|
+
$process.MainWindowTitle
|
|
2283
|
+
}
|
|
2284
|
+
`.replace(/\n\s+/g, ' ');
|
|
2285
|
+
const { stdout } = await execAsync(`powershell -Command "${psScript}"`);
|
|
2286
|
+
currentUrl = stdout.trim();
|
|
2287
|
+
}
|
|
2288
|
+
// Only log URL when it changes
|
|
2289
|
+
if (currentUrl && currentUrl !== lastUrl) {
|
|
2290
|
+
lastUrl = currentUrl;
|
|
2291
|
+
console.error(`🔍 Current URL: ${currentUrl}`);
|
|
2292
|
+
}
|
|
2293
|
+
// Check if URL contains code= parameter
|
|
2294
|
+
if (currentUrl && currentUrl.includes('code=')) {
|
|
2295
|
+
const codeMatch = currentUrl.match(/[?&]code=([^&]+)/);
|
|
2296
|
+
if (codeMatch && codeMatch[1]) {
|
|
2297
|
+
const code = decodeURIComponent(codeMatch[1]);
|
|
2298
|
+
console.error('✅ Found authorization code in URL!');
|
|
2299
|
+
console.error(`🔑 Extracted authorization code (first 20 chars): ${code.substring(0, 20)}...`);
|
|
2300
|
+
// Close browser window (non-blocking)
|
|
2301
|
+
setImmediate(async () => {
|
|
2302
|
+
try {
|
|
2303
|
+
if (platform === 'darwin') {
|
|
2304
|
+
await execAsync(`osascript -e 'tell application "${this.detectedBrowser}" to close front window'`);
|
|
2305
|
+
}
|
|
2306
|
+
else if (platform === 'win32') {
|
|
2307
|
+
const browserProcessName = this.detectedBrowser.replace('.exe', '');
|
|
2308
|
+
await execAsync(`powershell -Command "Stop-Process -Name '${browserProcessName}' -Force"`);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
catch (closeError) {
|
|
2312
|
+
// Ignore close errors
|
|
2313
|
+
}
|
|
2314
|
+
});
|
|
2315
|
+
console.error('✅ Authorization code detected:', code.substring(0, 10) + '...');
|
|
2316
|
+
const accessToken = await this.getUserAccessToken(code);
|
|
2317
|
+
console.error('✅ Access token received');
|
|
2318
|
+
this.token = accessToken;
|
|
2319
|
+
// Trigger cache initialization if available
|
|
2320
|
+
if (this.portalCacheHook) {
|
|
2321
|
+
try {
|
|
2322
|
+
const fakeRequest = new Request(this.authConfig.environmentUrl, {
|
|
2323
|
+
headers: { 'Authorization': `Bearer ${accessToken}` }
|
|
2324
|
+
});
|
|
2325
|
+
await this.portalCacheHook.ensureCacheInitialized(fakeRequest);
|
|
2326
|
+
console.error('✅ Cache initialization completed');
|
|
2327
|
+
}
|
|
2328
|
+
catch (error) {
|
|
2329
|
+
console.error('⚠️ Cache initialization failed:', error);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
console.error('🎉 Authentication complete! Stopping config server...');
|
|
2333
|
+
this.stopConfigServer();
|
|
2334
|
+
return; // Success - exit the loop
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
// Check for error parameters - only throw if this is a new error URL
|
|
2338
|
+
if (currentUrl && currentUrl.includes('error=')) {
|
|
2339
|
+
if (currentUrl !== lastErrorUrl) {
|
|
2340
|
+
lastErrorUrl = currentUrl;
|
|
2341
|
+
const errorMatch = currentUrl.match(/[?&]error=([^&]+)/);
|
|
2342
|
+
const errorDescMatch = currentUrl.match(/error_description=([^&]+)/);
|
|
2343
|
+
const error = errorMatch && errorMatch[1] ? decodeURIComponent(errorMatch[1]) : 'unknown_error';
|
|
2344
|
+
const errorDesc = errorDescMatch && errorDescMatch[1] ? decodeURIComponent(errorDescMatch[1]) : 'No description';
|
|
2345
|
+
// Log error only once
|
|
2346
|
+
if (!oAuthErrorLogged) {
|
|
2347
|
+
console.error('❌ OAuth authentication error:', `${error} - ${errorDesc}`);
|
|
2348
|
+
console.error('💡 The configuration server will remain running. Please try again with correct credentials.');
|
|
2349
|
+
console.error('🔍 Continuing to monitor browser for authorization code...');
|
|
2350
|
+
oAuthErrorLogged = true;
|
|
2351
|
+
}
|
|
2352
|
+
// Continue monitoring silently - don't throw, just keep checking
|
|
2353
|
+
}
|
|
2354
|
+
// If it's the same error URL, continue monitoring silently
|
|
2355
|
+
}
|
|
2356
|
+
else {
|
|
2357
|
+
// Reset error tracking if URL no longer contains error
|
|
2358
|
+
if (lastErrorUrl !== null) {
|
|
2359
|
+
lastErrorUrl = null;
|
|
2360
|
+
oAuthErrorLogged = false; // Reset so we can log new errors
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
// Wait before checking again
|
|
2364
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
2365
|
+
}
|
|
2366
|
+
catch (error) {
|
|
2367
|
+
// Only handle non-OAuth errors here (OAuth errors are handled above)
|
|
2368
|
+
if (!(error instanceof Error && error.message.includes('OAuth error:'))) {
|
|
2369
|
+
console.error('❌ Authentication monitoring error:', error);
|
|
2370
|
+
this.stopConfigServer();
|
|
2371
|
+
return; // Exit the loop
|
|
2372
|
+
}
|
|
2373
|
+
// OAuth errors are handled in the main loop above
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2400
2377
|
/**
|
|
2401
2378
|
* Stop the configuration HTTP server
|
|
2402
2379
|
*/
|