@taurgis/bonsai 1.0.5 → 3.0.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/README.md +59 -12
- package/agents/agents/official-docs-researcher.agent.md +15 -26
- package/agents/agents/salesforce-docs-researcher.agent.md +17 -35
- package/agents/hooks/README.md +2 -9
- package/agents/hooks/claude-code/hooks/bonsai-web-fetch.sh +1 -1
- package/agents/hooks/codex/hooks/bonsai-user-prompt-context.sh +1 -1
- package/agents/hooks/codex/hooks/bonsai-web-fetch.sh +1 -1
- package/agents/hooks/cursor/hooks/bonsai-web-fetch.sh +1 -1
- package/agents/hooks/github-copilot/bonsai-web-fetch.sh +1 -1
- package/agents/instructions/salesforce-research.instructions.md +7 -6
- package/agents/instructions/web-research.instructions.md +3 -2
- package/agents/skills/web-research/SKILL.md +24 -32
- package/bin/cli.mjs +44 -14
- package/dist/base-command.d.ts +17 -1
- package/dist/base-command.js +70 -9
- package/dist/commands/config/base.d.ts +2 -0
- package/dist/commands/config/base.js +15 -1
- package/dist/commands/config/get.d.ts +1 -1
- package/dist/commands/config/get.js +4 -5
- package/dist/commands/config/index.d.ts +12 -0
- package/dist/commands/config/index.js +34 -0
- package/dist/commands/config/list.js +2 -2
- package/dist/commands/config/set.js +18 -12
- package/dist/commands/config/unset.d.ts +1 -1
- package/dist/commands/config/unset.js +9 -6
- package/dist/commands/fetch.d.ts +2 -0
- package/dist/commands/fetch.js +69 -45
- package/dist/commands/import.d.ts +3 -0
- package/dist/commands/import.js +91 -53
- package/dist/commands/inspect.d.ts +8 -0
- package/dist/commands/inspect.js +59 -30
- package/dist/commands/list.d.ts +5 -3
- package/dist/commands/list.js +60 -35
- package/dist/commands/prune.d.ts +2 -1
- package/dist/commands/prune.js +62 -19
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.js +84 -26
- package/dist/commands.d.ts +2 -2
- package/dist/commands.js +2 -2
- package/dist/hooks/command-not-found/suggest.d.ts +7 -1
- package/dist/hooks/command-not-found/suggest.js +100 -46
- package/dist/lib/argv.d.ts +10 -0
- package/dist/lib/argv.js +97 -0
- package/dist/lib/color.d.ts +20 -0
- package/dist/lib/color.js +36 -0
- package/dist/lib/envelope.d.ts +36 -0
- package/dist/lib/envelope.js +82 -0
- package/dist/lib/help-preflight.d.ts +15 -0
- package/dist/lib/help-preflight.js +81 -0
- package/dist/lib/json-meta.d.ts +10 -0
- package/dist/lib/json-meta.js +98 -0
- package/dist/lib/limit-flag.d.ts +1 -0
- package/dist/lib/limit-flag.js +24 -0
- package/dist/lib/research/{search-index.d.ts → artifact-index.d.ts} +5 -5
- package/dist/lib/research/{search-index.js → artifact-index.js} +10 -10
- package/dist/lib/research/browser.d.ts +0 -8
- package/dist/lib/research/browser.js +20 -43
- package/dist/lib/research/regression.d.ts +18 -0
- package/dist/lib/research/regression.js +47 -0
- package/dist/lib/research/revalidate.js +3 -1
- package/dist/lib/research/schema.d.ts +5 -2
- package/dist/lib/research/schema.js +12 -1
- package/dist/lib/research/storage.d.ts +0 -1
- package/dist/lib/research/storage.js +15 -13
- package/dist/lib/research/url.d.ts +22 -0
- package/dist/lib/research/url.js +63 -1
- package/dist/lib/text.d.ts +5 -1
- package/dist/lib/text.js +11 -1
- package/dist/sites/coveo.d.ts +7 -0
- package/dist/sites/coveo.js +33 -0
- package/dist/sites/index.js +1 -2
- package/dist/sites/salesforce/fetch-page.js +1 -1
- package/dist/sites/salesforce/index.js +3 -8
- package/dist/sites/salesforce-developer/fetch-page.js +3 -1
- package/dist/sites/salesforce-developer/index.js +1 -2
- package/dist/sites/salesforce-doc-fetch.d.ts +11 -0
- package/dist/sites/salesforce-doc-fetch.js +117 -76
- package/dist/sites/salesforce-dom-probe.d.ts +16 -0
- package/dist/sites/salesforce-dom-probe.js +114 -0
- package/dist/sites/types.d.ts +0 -6
- package/oclif.manifest.json +153 -155
- package/package.json +38 -25
- package/dist/commands/index.d.ts +0 -35
- package/dist/commands/index.js +0 -306
- package/dist/commands/research/config/base.d.ts +0 -23
- package/dist/commands/research/config/base.js +0 -46
- package/dist/commands/research/config/get.d.ts +0 -20
- package/dist/commands/research/config/get.js +0 -45
- package/dist/commands/research/config/list.d.ts +0 -17
- package/dist/commands/research/config/list.js +0 -42
- package/dist/commands/research/config/set.d.ts +0 -31
- package/dist/commands/research/config/set.js +0 -94
- package/dist/commands/research/config/unset.d.ts +0 -21
- package/dist/commands/research/config/unset.js +0 -59
- package/dist/commands/research/import.d.ts +0 -38
- package/dist/commands/research/import.js +0 -313
- package/dist/commands/research/inspect.d.ts +0 -13
- package/dist/commands/research/inspect.js +0 -86
- package/dist/commands/research/list.d.ts +0 -25
- package/dist/commands/research/list.js +0 -143
- package/dist/commands/research/prune.d.ts +0 -24
- package/dist/commands/research/prune.js +0 -141
- package/dist/commands/research/search.d.ts +0 -32
- package/dist/commands/research/search.js +0 -334
- package/dist/commands/research/status.d.ts +0 -17
- package/dist/commands/research/status.js +0 -108
- package/dist/commands/research.d.ts +0 -36
- package/dist/commands/research.js +0 -304
- package/dist/commands/search.d.ts +0 -31
- package/dist/commands/search.js +0 -329
- package/dist/lib/research/docs/__fixtures__/sphinx-searchindex.d.ts +0 -1
- package/dist/lib/research/docs/__fixtures__/sphinx-searchindex.js +0 -2
- package/dist/lib/research/docs/discovery-index.d.ts +0 -14
- package/dist/lib/research/docs/discovery-index.js +0 -112
- package/dist/lib/research/docs/remote-search-runner.d.ts +0 -21
- package/dist/lib/research/docs/remote-search-runner.js +0 -114
- package/dist/lib/research/docs/remote-search.d.ts +0 -14
- package/dist/lib/research/docs/remote-search.js +0 -101
- package/dist/lib/research/docs/search-index.d.ts +0 -13
- package/dist/lib/research/docs/search-index.js +0 -176
- package/dist/sites/salesforce/browser-search.d.ts +0 -7
- package/dist/sites/salesforce/browser-search.js +0 -82
- package/dist/sites/salesforce/coveo.d.ts +0 -23
- package/dist/sites/salesforce/coveo.js +0 -161
- package/dist/sites/salesforce/token.d.ts +0 -26
- package/dist/sites/salesforce/token.js +0 -154
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { SiteSearchResult } from '../types.js';
|
|
2
|
-
import { type TokenInfo } from './token.js';
|
|
3
|
-
export declare const ALLOWED_DOC_HOSTS: Set<string>;
|
|
4
|
-
export declare const SEARCH_PAGE_URL = "https://help.salesforce.com/s/search-result";
|
|
5
|
-
export declare const COVEO_SEARCH_PATH = "/services/apexrest/coveo/analytics/rest/search/v2";
|
|
6
|
-
export declare const DEFAULT_LANGUAGE = "en_US";
|
|
7
|
-
export declare const AURA_PATH = "/s/sfsites/aura";
|
|
8
|
-
export declare function isAllowedDocHost(hostname: string): boolean;
|
|
9
|
-
/** The search page reads its query from the URL hash, so this URL is browser-only (never normalized). */
|
|
10
|
-
export declare function buildSearchPageUrl(query: string, language?: string): string;
|
|
11
|
-
/**
|
|
12
|
-
* Coveo indexes Help articles under an internal `/help_doccontent` URL; rewrite it to the
|
|
13
|
-
* canonical `/s/articleView` page a human (or our fetcher) can actually open.
|
|
14
|
-
*/
|
|
15
|
-
export declare function normalizeHelpDocContentUrl(rawUrl: string): string;
|
|
16
|
-
/** Maps a raw Coveo response into deduped, host-validated search results. */
|
|
17
|
-
export declare function extractCoveoResults(data: unknown, limit: number): SiteSearchResult[];
|
|
18
|
-
/**
|
|
19
|
-
* Direct Coveo Search API call using a cached token — the no-browser fast path.
|
|
20
|
-
* Tries a bearer header first, then falls back to a query-string token on auth rejection.
|
|
21
|
-
* Returns the parsed response body (with a `results` array) or null on failure.
|
|
22
|
-
*/
|
|
23
|
-
export declare function searchCoveoDirect(tokenInfo: TokenInfo, query: string, limit: number, language?: string, timeoutMs?: number): Promise<unknown | null>;
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { COVEO_DEFAULTS } from './token.js';
|
|
2
|
-
export const ALLOWED_DOC_HOSTS = new Set(['help.salesforce.com', 'developer.salesforce.com']);
|
|
3
|
-
export const SEARCH_PAGE_URL = 'https://help.salesforce.com/s/search-result';
|
|
4
|
-
// Path of the XHR the search page fires; we intercept its response as a fallback.
|
|
5
|
-
export const COVEO_SEARCH_PATH = '/services/apexrest/coveo/analytics/rest/search/v2';
|
|
6
|
-
export const DEFAULT_LANGUAGE = 'en_US';
|
|
7
|
-
// The page mints its Coveo token via an Aura action on this path; we intercept the response
|
|
8
|
-
// (identified by whether its body parses to a token) to cache it for the no-browser fast path.
|
|
9
|
-
export const AURA_PATH = '/s/sfsites/aura';
|
|
10
|
-
export function isAllowedDocHost(hostname) {
|
|
11
|
-
return ALLOWED_DOC_HOSTS.has(hostname.toLowerCase());
|
|
12
|
-
}
|
|
13
|
-
/** The search page reads its query from the URL hash, so this URL is browser-only (never normalized). */
|
|
14
|
-
export function buildSearchPageUrl(query, language = DEFAULT_LANGUAGE) {
|
|
15
|
-
const url = new URL(SEARCH_PAGE_URL);
|
|
16
|
-
if (language)
|
|
17
|
-
url.searchParams.set('language', language);
|
|
18
|
-
if (query) {
|
|
19
|
-
url.hash = new URLSearchParams({ q: query, t: 'allResultsTab', sort: 'relevancy' }).toString();
|
|
20
|
-
}
|
|
21
|
-
return url.toString();
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Coveo indexes Help articles under an internal `/help_doccontent` URL; rewrite it to the
|
|
25
|
-
* canonical `/s/articleView` page a human (or our fetcher) can actually open.
|
|
26
|
-
*/
|
|
27
|
-
export function normalizeHelpDocContentUrl(rawUrl) {
|
|
28
|
-
let url;
|
|
29
|
-
try {
|
|
30
|
-
url = new URL(rawUrl);
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
return rawUrl;
|
|
34
|
-
}
|
|
35
|
-
if (url.hostname.toLowerCase() !== 'help.salesforce.com')
|
|
36
|
-
return rawUrl;
|
|
37
|
-
if (!url.pathname.toLowerCase().startsWith('/help_doccontent'))
|
|
38
|
-
return rawUrl;
|
|
39
|
-
const id = url.searchParams.get('id');
|
|
40
|
-
if (!id)
|
|
41
|
-
return rawUrl;
|
|
42
|
-
const articleUrl = new URL('https://help.salesforce.com/s/articleView');
|
|
43
|
-
articleUrl.searchParams.set('id', id.endsWith('.htm') ? id : `${id}.htm`);
|
|
44
|
-
articleUrl.searchParams.set('type', '5');
|
|
45
|
-
for (const key of ['release', 'language']) {
|
|
46
|
-
const value = url.searchParams.get(key);
|
|
47
|
-
if (value)
|
|
48
|
-
articleUrl.searchParams.set(key, value);
|
|
49
|
-
}
|
|
50
|
-
return articleUrl.toString();
|
|
51
|
-
}
|
|
52
|
-
const SKIP_URLS = new Set([
|
|
53
|
-
'https://help.salesforce.com/',
|
|
54
|
-
'https://help.salesforce.com/s',
|
|
55
|
-
'https://help.salesforce.com/s/',
|
|
56
|
-
'https://help.salesforce.com/s/login',
|
|
57
|
-
]);
|
|
58
|
-
function resultUrl(result) {
|
|
59
|
-
const raw = result.raw || {};
|
|
60
|
-
return (result.clickUri ||
|
|
61
|
-
result.uri ||
|
|
62
|
-
raw.clickuri ||
|
|
63
|
-
raw.uri ||
|
|
64
|
-
raw.sourceurl ||
|
|
65
|
-
raw.document_uri ||
|
|
66
|
-
raw.sfurl ||
|
|
67
|
-
raw.sfdcurl ||
|
|
68
|
-
null);
|
|
69
|
-
}
|
|
70
|
-
/** Maps a raw Coveo response into deduped, host-validated search results. */
|
|
71
|
-
export function extractCoveoResults(data, limit) {
|
|
72
|
-
const results = Array.isArray(data?.results)
|
|
73
|
-
? data.results
|
|
74
|
-
: [];
|
|
75
|
-
const items = [];
|
|
76
|
-
const seen = new Set();
|
|
77
|
-
for (const result of results) {
|
|
78
|
-
const candidate = resultUrl(result);
|
|
79
|
-
if (!candidate)
|
|
80
|
-
continue;
|
|
81
|
-
const url = normalizeHelpDocContentUrl(candidate);
|
|
82
|
-
let hostname;
|
|
83
|
-
try {
|
|
84
|
-
hostname = new URL(url).hostname;
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
if (!isAllowedDocHost(hostname) || SKIP_URLS.has(url) || seen.has(url))
|
|
90
|
-
continue;
|
|
91
|
-
seen.add(url);
|
|
92
|
-
items.push({
|
|
93
|
-
url,
|
|
94
|
-
title: result.title || result.raw?.title || url,
|
|
95
|
-
...(result.excerpt ? { snippet: result.excerpt } : {}),
|
|
96
|
-
});
|
|
97
|
-
if (items.length >= limit)
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
return items;
|
|
101
|
-
}
|
|
102
|
-
function buildCoveoPayload(tokenInfo, query, language, limit) {
|
|
103
|
-
const payload = {
|
|
104
|
-
q: query,
|
|
105
|
-
numberOfResults: limit,
|
|
106
|
-
firstResult: 0,
|
|
107
|
-
searchHub: tokenInfo.searchHub,
|
|
108
|
-
locale: language,
|
|
109
|
-
tab: 'allResultsTab',
|
|
110
|
-
sortCriteria: 'relevancy',
|
|
111
|
-
};
|
|
112
|
-
if (tokenInfo.filterer)
|
|
113
|
-
payload.aq = tokenInfo.filterer;
|
|
114
|
-
return payload;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Direct Coveo Search API call using a cached token — the no-browser fast path.
|
|
118
|
-
* Tries a bearer header first, then falls back to a query-string token on auth rejection.
|
|
119
|
-
* Returns the parsed response body (with a `results` array) or null on failure.
|
|
120
|
-
*/
|
|
121
|
-
export async function searchCoveoDirect(tokenInfo, query, limit, language = DEFAULT_LANGUAGE, timeoutMs = 15_000) {
|
|
122
|
-
const clientBase = (tokenInfo.clientUri || COVEO_DEFAULTS.clientUri).replace(/\/$/, '');
|
|
123
|
-
const payload = JSON.stringify(buildCoveoPayload(tokenInfo, query, language, limit));
|
|
124
|
-
const attempt = async (mode) => {
|
|
125
|
-
const url = new URL(`${clientBase}/rest/search/v2`);
|
|
126
|
-
// ponytail: query-string token is a documented Coveo fallback for clients that strip the
|
|
127
|
-
// Authorization header. It can land in proxy logs, but this is an anonymous public search
|
|
128
|
-
// token (no user identity), so the exposure is acceptable; revisit if the token ever binds a user.
|
|
129
|
-
if (mode === 'query')
|
|
130
|
-
url.searchParams.set('access_token', tokenInfo.accessToken);
|
|
131
|
-
const headers = { 'content-type': 'application/json' };
|
|
132
|
-
if (mode === 'header')
|
|
133
|
-
headers.authorization = `Bearer ${tokenInfo.accessToken}`;
|
|
134
|
-
const controller = new AbortController();
|
|
135
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
136
|
-
try {
|
|
137
|
-
const response = await fetch(url, {
|
|
138
|
-
method: 'POST',
|
|
139
|
-
headers,
|
|
140
|
-
body: payload,
|
|
141
|
-
signal: controller.signal,
|
|
142
|
-
});
|
|
143
|
-
const data = response.ok ? await response.json().catch(() => null) : null;
|
|
144
|
-
return { status: response.status, data };
|
|
145
|
-
}
|
|
146
|
-
finally {
|
|
147
|
-
clearTimeout(timer);
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
try {
|
|
151
|
-
const primary = await attempt('header');
|
|
152
|
-
if (primary.data)
|
|
153
|
-
return primary.data;
|
|
154
|
-
if (![400, 401, 403].includes(primary.status))
|
|
155
|
-
return null;
|
|
156
|
-
return (await attempt('query')).data;
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export declare const COVEO_DEFAULTS: {
|
|
2
|
-
readonly organizationId: "org62salesforce";
|
|
3
|
-
readonly searchHub: "HTCommunity";
|
|
4
|
-
readonly endpointBase: "https://help.salesforce.com/services/apexrest/coveo";
|
|
5
|
-
readonly clientUri: "https://platform.cloud.coveo.com";
|
|
6
|
-
};
|
|
7
|
-
export interface TokenInfo {
|
|
8
|
-
accessToken: string;
|
|
9
|
-
organizationId: string;
|
|
10
|
-
searchHub: string;
|
|
11
|
-
endpointBase: string;
|
|
12
|
-
clientUri: string;
|
|
13
|
-
filterer: string | null;
|
|
14
|
-
expiresAtMs: number;
|
|
15
|
-
}
|
|
16
|
-
export declare function isTokenValid(expiresAtMs: number | null | undefined): boolean;
|
|
17
|
-
export declare function decodeJwtPayload(token: string): Record<string, unknown> | null;
|
|
18
|
-
export declare function getTokenExpiryMs(accessToken: string): number | null;
|
|
19
|
-
export declare function applyTokenExpiry(tokenInfo: TokenInfo): TokenInfo;
|
|
20
|
-
/**
|
|
21
|
-
* Parses the access token out of a Salesforce Aura `getToken` response body.
|
|
22
|
-
* Returns null when the payload is missing, errored, or lacks a token.
|
|
23
|
-
*/
|
|
24
|
-
export declare function parseAuraToken(rawResponseBody: string): TokenInfo | null;
|
|
25
|
-
export declare function loadToken(): TokenInfo | null;
|
|
26
|
-
export declare function storeToken(tokenInfo: TokenInfo): void;
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { dirname, join } from 'node:path';
|
|
4
|
-
// Salesforce Help routes anonymous doc search through a public Coveo org. These are
|
|
5
|
-
// the published defaults for that org; they are not user credentials.
|
|
6
|
-
export const COVEO_DEFAULTS = {
|
|
7
|
-
organizationId: 'org62salesforce',
|
|
8
|
-
searchHub: 'HTCommunity',
|
|
9
|
-
endpointBase: 'https://help.salesforce.com/services/apexrest/coveo',
|
|
10
|
-
clientUri: 'https://platform.cloud.coveo.com',
|
|
11
|
-
};
|
|
12
|
-
const DEFAULT_TOKEN_TTL_MS = 24 * 60 * 60 * 1000;
|
|
13
|
-
const TOKEN_EXPIRY_SKEW_MS = 2 * 60 * 1000;
|
|
14
|
-
export function isTokenValid(expiresAtMs) {
|
|
15
|
-
if (!Number.isFinite(expiresAtMs))
|
|
16
|
-
return false;
|
|
17
|
-
return Date.now() + TOKEN_EXPIRY_SKEW_MS < expiresAtMs;
|
|
18
|
-
}
|
|
19
|
-
export function decodeJwtPayload(token) {
|
|
20
|
-
const parts = token.split('.');
|
|
21
|
-
if (parts.length < 2 || !parts[1])
|
|
22
|
-
return null;
|
|
23
|
-
try {
|
|
24
|
-
return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
export function getTokenExpiryMs(accessToken) {
|
|
31
|
-
const payload = decodeJwtPayload(accessToken);
|
|
32
|
-
const rawExp = payload?.exp ?? payload?.expires_at ?? payload?.expiresAt;
|
|
33
|
-
const expNumber = Number(rawExp);
|
|
34
|
-
if (rawExp === undefined || rawExp === null || !Number.isFinite(expNumber))
|
|
35
|
-
return null;
|
|
36
|
-
// JWT `exp` is seconds; values past year ~33658 are already milliseconds.
|
|
37
|
-
return expNumber > 1_000_000_000_000 ? expNumber : expNumber * 1000;
|
|
38
|
-
}
|
|
39
|
-
export function applyTokenExpiry(tokenInfo) {
|
|
40
|
-
if (!tokenInfo?.accessToken)
|
|
41
|
-
return tokenInfo;
|
|
42
|
-
const expiresAtMs = tokenInfo.expiresAtMs ||
|
|
43
|
-
getTokenExpiryMs(tokenInfo.accessToken) ||
|
|
44
|
-
Date.now() + DEFAULT_TOKEN_TTL_MS;
|
|
45
|
-
return { ...tokenInfo, expiresAtMs };
|
|
46
|
-
}
|
|
47
|
-
function stripAuraResponsePrefix(text) {
|
|
48
|
-
return text
|
|
49
|
-
.trim()
|
|
50
|
-
.replace(/^for\(;;\);/, '')
|
|
51
|
-
.replace(/^\)\]\}',?/, '')
|
|
52
|
-
.trim();
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Parses the access token out of a Salesforce Aura `getToken` response body.
|
|
56
|
-
* Returns null when the payload is missing, errored, or lacks a token.
|
|
57
|
-
*/
|
|
58
|
-
export function parseAuraToken(rawResponseBody) {
|
|
59
|
-
let payload;
|
|
60
|
-
try {
|
|
61
|
-
payload = JSON.parse(stripAuraResponsePrefix(rawResponseBody));
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
const action = Array.isArray(payload?.actions) ? payload.actions[0] : null;
|
|
67
|
-
if (!action || (action.state && action.state !== 'SUCCESS'))
|
|
68
|
-
return null;
|
|
69
|
-
let returnValue = action.returnValue ?? null;
|
|
70
|
-
if (typeof returnValue?.returnValue === 'string')
|
|
71
|
-
returnValue = returnValue.returnValue;
|
|
72
|
-
const fields = readTokenFields(returnValue);
|
|
73
|
-
const accessToken = fields.accessToken;
|
|
74
|
-
if (!accessToken)
|
|
75
|
-
return null;
|
|
76
|
-
let filterer = fields.filterer;
|
|
77
|
-
const jwtFilterer = decodeJwtPayload(accessToken)?.filterer;
|
|
78
|
-
if (!filterer && typeof jwtFilterer === 'string')
|
|
79
|
-
filterer = jwtFilterer;
|
|
80
|
-
return applyTokenExpiry({
|
|
81
|
-
accessToken,
|
|
82
|
-
organizationId: fields.organizationId || COVEO_DEFAULTS.organizationId,
|
|
83
|
-
searchHub: fields.searchHub || COVEO_DEFAULTS.searchHub,
|
|
84
|
-
endpointBase: fields.endpointBase || COVEO_DEFAULTS.endpointBase,
|
|
85
|
-
clientUri: fields.clientUri || COVEO_DEFAULTS.clientUri,
|
|
86
|
-
filterer,
|
|
87
|
-
expiresAtMs: 0,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
function readTokenFields(returnValue) {
|
|
91
|
-
if (typeof returnValue === 'string') {
|
|
92
|
-
const trimmed = returnValue.trim();
|
|
93
|
-
if (/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+$/.test(trimmed)) {
|
|
94
|
-
return { accessToken: trimmed, filterer: null };
|
|
95
|
-
}
|
|
96
|
-
if (trimmed.startsWith('{')) {
|
|
97
|
-
try {
|
|
98
|
-
return readTokenFields(JSON.parse(trimmed));
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
return { accessToken: null, filterer: null };
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return { accessToken: null, filterer: null };
|
|
105
|
-
}
|
|
106
|
-
if (returnValue && typeof returnValue === 'object') {
|
|
107
|
-
const v = returnValue;
|
|
108
|
-
return {
|
|
109
|
-
accessToken: v.accessToken || v.token || v.authToken || v.access_token || null,
|
|
110
|
-
organizationId: v.organizationId || v.organization || v.sfOrganizationId,
|
|
111
|
-
searchHub: v.searchHub,
|
|
112
|
-
endpointBase: v.platformUri || v.clientUri,
|
|
113
|
-
clientUri: v.clientUri,
|
|
114
|
-
filterer: v.filterer || v.filter || null,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
return { accessToken: null, filterer: null };
|
|
118
|
-
}
|
|
119
|
-
function tokenCachePath() {
|
|
120
|
-
const base = process.env.XDG_CACHE_HOME || join(homedir(), '.cache');
|
|
121
|
-
return join(base, 'bonsai', 'salesforce-coveo-token.json');
|
|
122
|
-
}
|
|
123
|
-
export function loadToken() {
|
|
124
|
-
try {
|
|
125
|
-
const parsed = JSON.parse(readFileSync(tokenCachePath(), 'utf8'));
|
|
126
|
-
if (!parsed.accessToken)
|
|
127
|
-
return null;
|
|
128
|
-
return applyTokenExpiry({
|
|
129
|
-
...COVEO_DEFAULTS,
|
|
130
|
-
filterer: null,
|
|
131
|
-
expiresAtMs: 0,
|
|
132
|
-
...parsed,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// ponytail: token persisted as plaintext on disk. It is a public anonymous Coveo
|
|
140
|
-
// search token (no user data, ~24h TTL), so the OS keychain isn't warranted. Move to
|
|
141
|
-
// keytar here if these modules ever hold a privileged token.
|
|
142
|
-
export function storeToken(tokenInfo) {
|
|
143
|
-
try {
|
|
144
|
-
const path = tokenCachePath();
|
|
145
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
146
|
-
writeFileSync(path, JSON.stringify(applyTokenExpiry(tokenInfo)), {
|
|
147
|
-
encoding: 'utf8',
|
|
148
|
-
mode: 0o600,
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
// A non-writable cache dir just means we re-mint via the browser next time.
|
|
153
|
-
}
|
|
154
|
-
}
|