@bevel-software/platform-mcp-core 0.23.0 → 0.25.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/dist/chain-example.d.ts +58 -0
- package/dist/chain-example.d.ts.map +1 -0
- package/dist/chain-example.js +259 -0
- package/dist/chain-example.js.map +1 -0
- package/dist/chain-runtime.d.ts +122 -0
- package/dist/chain-runtime.d.ts.map +1 -0
- package/dist/chain-runtime.js +352 -0
- package/dist/chain-runtime.js.map +1 -0
- package/dist/google-service-account/google-auth-http.protocol.d.ts +24 -0
- package/dist/google-service-account/google-auth-http.protocol.d.ts.map +1 -0
- package/dist/google-service-account/google-auth-http.protocol.js +47 -0
- package/dist/google-service-account/google-auth-http.protocol.js.map +1 -0
- package/dist/google-service-account/google-service-account.auth.d.ts +45 -0
- package/dist/google-service-account/google-service-account.auth.d.ts.map +1 -0
- package/dist/google-service-account/google-service-account.auth.js +153 -0
- package/dist/google-service-account/google-service-account.auth.js.map +1 -0
- package/dist/google-service-account/google-service-account.token-source.d.ts +28 -0
- package/dist/google-service-account/google-service-account.token-source.d.ts.map +1 -0
- package/dist/google-service-account/google-service-account.token-source.js +206 -0
- package/dist/google-service-account/google-service-account.token-source.js.map +1 -0
- package/dist/google-service-account/index.d.ts +5 -0
- package/dist/google-service-account/index.d.ts.map +1 -0
- package/dist/google-service-account/index.js +7 -0
- package/dist/google-service-account/index.js.map +1 -0
- package/dist/google-service-account/service-account-token.contract.d.ts +33 -0
- package/dist/google-service-account/service-account-token.contract.d.ts.map +1 -0
- package/dist/google-service-account/service-account-token.contract.js +14 -0
- package/dist/google-service-account/service-account-token.contract.js.map +1 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -3
- package/dist/index.js.map +1 -1
- package/dist/meta-tools.d.ts +100 -1
- package/dist/meta-tools.d.ts.map +1 -1
- package/dist/meta-tools.js +181 -53
- package/dist/meta-tools.js.map +1 -1
- package/dist/results.d.ts +20 -0
- package/dist/results.d.ts.map +1 -1
- package/dist/results.js +111 -1
- package/dist/results.js.map +1 -1
- package/dist/retired-tools.d.ts +0 -12
- package/dist/retired-tools.d.ts.map +1 -1
- package/dist/retired-tools.js +0 -22
- package/dist/retired-tools.js.map +1 -1
- package/package.json +3 -2
- package/src/chain-example.ts +315 -0
- package/src/chain-runtime.ts +382 -0
- package/src/google-service-account/google-auth-http.protocol.ts +62 -0
- package/src/google-service-account/google-service-account.auth.ts +161 -0
- package/src/google-service-account/google-service-account.token-source.ts +239 -0
- package/src/google-service-account/index.ts +15 -0
- package/src/google-service-account/service-account-token.contract.ts +38 -0
- package/src/index.ts +47 -2
- package/src/meta-tools.ts +212 -58
- package/src/results.ts +106 -1
- package/src/retired-tools.ts +0 -22
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { createHash, createSign } from 'node:crypto';
|
|
2
|
+
import {
|
|
3
|
+
ServiceAccountAuthError,
|
|
4
|
+
type GoogleServiceAccountAuth,
|
|
5
|
+
type IServiceAccountTokenSource,
|
|
6
|
+
} from './service-account-token.contract.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Google's token endpoint. Fixed rather than read from the key's own
|
|
10
|
+
* `token_uri`, so a key cannot point the server at another host with a signed
|
|
11
|
+
* assertion in hand.
|
|
12
|
+
*/
|
|
13
|
+
export const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
|
14
|
+
const JWT_BEARER_GRANT = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
|
|
15
|
+
/** The longest assertion lifetime Google accepts. */
|
|
16
|
+
const ASSERTION_LIFETIME_S = 3600;
|
|
17
|
+
/** A token this close to expiry is replaced, so a call never leaves with one that dies on the way. */
|
|
18
|
+
const REFRESH_SKEW_MS = 60_000;
|
|
19
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
20
|
+
/** Tokens (and failures) kept at once. Each distinct key, scope set and subject is one entry. */
|
|
21
|
+
const MAX_CACHED_TOKENS = 256;
|
|
22
|
+
/** How long a failed exchange is answered from memory when Google names no `Retry-After`. */
|
|
23
|
+
const FAILURE_BACKOFF_MS = 5_000;
|
|
24
|
+
/** The longest a `Retry-After` from Google holds calls back, so a far-off value cannot park a tool for good. */
|
|
25
|
+
const MAX_FAILURE_BACKOFF_MS = 60_000;
|
|
26
|
+
|
|
27
|
+
interface ServiceAccountKey {
|
|
28
|
+
clientEmail: string;
|
|
29
|
+
privateKey: string;
|
|
30
|
+
privateKeyId?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface CachedToken {
|
|
34
|
+
accessToken: string;
|
|
35
|
+
expiresAt: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface CachedFailure {
|
|
39
|
+
error: ServiceAccountAuthError;
|
|
40
|
+
retryAt: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Mints and caches access tokens for Google service accounts: signs an RS256
|
|
45
|
+
* assertion with the key's private key, exchanges it at Google's token
|
|
46
|
+
* endpoint, and keeps the token until shortly before it expires. Concurrent
|
|
47
|
+
* calls for the same token share one exchange, and a failed exchange is
|
|
48
|
+
* answered from memory for a few seconds (or for Google's `Retry-After`), so
|
|
49
|
+
* an outage or a rate limit is not met with a fresh exchange on every call.
|
|
50
|
+
* Injected with `fetch` and a clock so suites answer for Google.
|
|
51
|
+
*/
|
|
52
|
+
export class GoogleServiceAccountTokenSource implements IServiceAccountTokenSource {
|
|
53
|
+
private readonly tokens = new Map<string, CachedToken>();
|
|
54
|
+
private readonly failures = new Map<string, CachedFailure>();
|
|
55
|
+
private readonly inFlight = new Map<string, Promise<string>>();
|
|
56
|
+
|
|
57
|
+
constructor(
|
|
58
|
+
private readonly fetchImpl: typeof fetch = fetch,
|
|
59
|
+
private readonly now: () => number = Date.now,
|
|
60
|
+
private readonly timeoutMs: number = REQUEST_TIMEOUT_MS,
|
|
61
|
+
) {}
|
|
62
|
+
|
|
63
|
+
async accessToken(auth: GoogleServiceAccountAuth): Promise<string> {
|
|
64
|
+
const key = parseServiceAccountKey(auth.credentials);
|
|
65
|
+
const scope = scopeString(auth.scopes);
|
|
66
|
+
const cacheKey = tokenCacheKey(key, scope, auth.subject);
|
|
67
|
+
|
|
68
|
+
const cached = this.tokens.get(cacheKey);
|
|
69
|
+
if (cached && cached.expiresAt - REFRESH_SKEW_MS > this.now()) return cached.accessToken;
|
|
70
|
+
|
|
71
|
+
const failed = this.failures.get(cacheKey);
|
|
72
|
+
if (failed && failed.retryAt > this.now()) throw failed.error;
|
|
73
|
+
|
|
74
|
+
const pending = this.inFlight.get(cacheKey);
|
|
75
|
+
if (pending) return pending;
|
|
76
|
+
|
|
77
|
+
const exchange = this.exchange(key, scope, auth.subject, cacheKey)
|
|
78
|
+
.catch((err: unknown) => {
|
|
79
|
+
// Remembered per key, scope set and subject: a corrected key hashes to
|
|
80
|
+
// another entry, so fixing it is never held back by this.
|
|
81
|
+
if (err instanceof ServiceAccountAuthError) {
|
|
82
|
+
remember(this.failures, cacheKey, { error: err, retryAt: this.now() + err.retryAfterMs });
|
|
83
|
+
}
|
|
84
|
+
throw err;
|
|
85
|
+
})
|
|
86
|
+
.finally(() => this.inFlight.delete(cacheKey));
|
|
87
|
+
this.inFlight.set(cacheKey, exchange);
|
|
88
|
+
return exchange;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private async exchange(key: ServiceAccountKey, scope: string, subject: string | undefined, cacheKey: string): Promise<string> {
|
|
92
|
+
const assertion = signAssertion(key, scope, subject, this.now());
|
|
93
|
+
let res: Response;
|
|
94
|
+
try {
|
|
95
|
+
res = await this.fetchImpl(GOOGLE_TOKEN_URL, {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
|
|
98
|
+
body: new URLSearchParams({ grant_type: JWT_BEARER_GRANT, assertion }).toString(),
|
|
99
|
+
// Don't follow redirects: the assertion goes to Google's endpoint and nowhere else.
|
|
100
|
+
redirect: 'error',
|
|
101
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
102
|
+
});
|
|
103
|
+
} catch (err) {
|
|
104
|
+
const reason =
|
|
105
|
+
err instanceof Error && err.name === 'TimeoutError'
|
|
106
|
+
? `timed out after ${this.timeoutMs}ms`
|
|
107
|
+
: err instanceof Error
|
|
108
|
+
? err.message
|
|
109
|
+
: String(err);
|
|
110
|
+
throw new ServiceAccountAuthError(`Google's token endpoint could not be reached: ${reason}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Whatever came back is read as an object of fields or as none. A body
|
|
114
|
+
// that is not JSON, and one that is JSON but no object (`null`, a number,
|
|
115
|
+
// a list), both carry no field to read: reading one off `null` would
|
|
116
|
+
// throw a TypeError instead of the refusal below, past the code that
|
|
117
|
+
// remembers a failure and says who was refused.
|
|
118
|
+
const parsed: unknown = await res.json().catch(() => null);
|
|
119
|
+
const body = (parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}) as Record<string, unknown>;
|
|
120
|
+
if (!res.ok) {
|
|
121
|
+
// Google says why in `error` and `error_description` (a revoked key, a
|
|
122
|
+
// scope the account may not have, a subject without delegation); neither
|
|
123
|
+
// carries the key, so both are passed on.
|
|
124
|
+
const detail = [body.error, body.error_description].filter((v) => typeof v === 'string').join(': ');
|
|
125
|
+
throw new ServiceAccountAuthError(
|
|
126
|
+
`Google refused the service account ${key.clientEmail} (HTTP ${res.status})${detail ? `: ${detail}` : ''}`,
|
|
127
|
+
retryAfterMs(res.headers.get('retry-after'), this.now()),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const accessToken = typeof body.access_token === 'string' ? body.access_token : '';
|
|
131
|
+
if (!accessToken) throw new ServiceAccountAuthError("Google's token response had no access_token");
|
|
132
|
+
const expiresIn = typeof body.expires_in === 'number' ? body.expires_in : ASSERTION_LIFETIME_S;
|
|
133
|
+
|
|
134
|
+
this.failures.delete(cacheKey);
|
|
135
|
+
remember(this.tokens, cacheKey, { accessToken, expiresAt: this.now() + expiresIn * 1000 });
|
|
136
|
+
return accessToken;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Keep `value` under `cacheKey`, dropping the oldest entry first when the map
|
|
142
|
+
* is full: a Map iterates in insertion order, and re-inserting moves an entry
|
|
143
|
+
* to the back.
|
|
144
|
+
*/
|
|
145
|
+
function remember<T>(map: Map<string, T>, cacheKey: string, value: T): void {
|
|
146
|
+
map.delete(cacheKey);
|
|
147
|
+
if (map.size >= MAX_CACHED_TOKENS) {
|
|
148
|
+
const oldest = map.keys().next().value;
|
|
149
|
+
if (oldest !== undefined) map.delete(oldest);
|
|
150
|
+
}
|
|
151
|
+
map.set(cacheKey, value);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* How long to hold calls back after a refusal: Google's `Retry-After` (seconds
|
|
156
|
+
* or an HTTP date) when it sent one, capped, else the default backoff.
|
|
157
|
+
*/
|
|
158
|
+
function retryAfterMs(header: string | null, now: number): number {
|
|
159
|
+
if (!header) return FAILURE_BACKOFF_MS;
|
|
160
|
+
const seconds = Number(header);
|
|
161
|
+
const ms = Number.isFinite(seconds) ? seconds * 1000 : Date.parse(header) - now;
|
|
162
|
+
if (!Number.isFinite(ms) || ms <= 0) return FAILURE_BACKOFF_MS;
|
|
163
|
+
return Math.min(ms, MAX_FAILURE_BACKOFF_MS);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The key as an admin is likely to have stored it: the JSON file Google
|
|
168
|
+
* issued, or that JSON in base64. Only the fields the assertion needs are
|
|
169
|
+
* kept, and no error quotes what was stored.
|
|
170
|
+
*/
|
|
171
|
+
function parseServiceAccountKey(raw: string): ServiceAccountKey {
|
|
172
|
+
const value = raw.trim();
|
|
173
|
+
let parsed: unknown;
|
|
174
|
+
try {
|
|
175
|
+
parsed = JSON.parse(value.startsWith('{') ? value : Buffer.from(value, 'base64').toString('utf8'));
|
|
176
|
+
} catch {
|
|
177
|
+
throw new ServiceAccountAuthError(
|
|
178
|
+
'The service-account credentials are not a key JSON. Store the JSON file Google issued for the service account.',
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const obj = (parsed ?? {}) as Record<string, unknown>;
|
|
182
|
+
const clientEmail = typeof obj.client_email === 'string' ? obj.client_email : '';
|
|
183
|
+
const privateKey = typeof obj.private_key === 'string' ? obj.private_key : '';
|
|
184
|
+
if (!clientEmail || !privateKey) {
|
|
185
|
+
throw new ServiceAccountAuthError(
|
|
186
|
+
'The service-account key JSON has no client_email or private_key. Store the JSON file Google issued for the service account.',
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
clientEmail,
|
|
191
|
+
privateKey,
|
|
192
|
+
privateKeyId: typeof obj.private_key_id === 'string' ? obj.private_key_id : undefined,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The scopes as Google wants them: space-separated, each trimmed. UTCP keeps
|
|
198
|
+
* the template as written once it validates, so stray whitespace is cleaned
|
|
199
|
+
* here, whether it sits in a list entry or in a space-separated string.
|
|
200
|
+
*/
|
|
201
|
+
function scopeString(scopes: string | string[]): string {
|
|
202
|
+
const list = Array.isArray(scopes) ? scopes : [scopes];
|
|
203
|
+
return list
|
|
204
|
+
.flatMap((s) => s.split(/\s+/))
|
|
205
|
+
.filter(Boolean)
|
|
206
|
+
.join(' ');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** One entry per key, scope set and subject. The private key is hashed so the cache holds no copy of it. */
|
|
210
|
+
function tokenCacheKey(key: ServiceAccountKey, scope: string, subject: string | undefined): string {
|
|
211
|
+
const keyHash = createHash('sha256').update(key.privateKey).digest('hex');
|
|
212
|
+
return JSON.stringify([key.clientEmail, key.privateKeyId ?? '', keyHash, scope.split(' ').sort().join(' '), subject ?? '']);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** The RS256 assertion Google exchanges for an access token. */
|
|
216
|
+
function signAssertion(key: ServiceAccountKey, scope: string, subject: string | undefined, now: number): string {
|
|
217
|
+
const seconds = Math.floor(now / 1000);
|
|
218
|
+
const part = (value: unknown) => Buffer.from(JSON.stringify(value), 'utf8').toString('base64url');
|
|
219
|
+
const header = { alg: 'RS256', typ: 'JWT', ...(key.privateKeyId ? { kid: key.privateKeyId } : {}) };
|
|
220
|
+
const claims = {
|
|
221
|
+
iss: key.clientEmail,
|
|
222
|
+
scope,
|
|
223
|
+
aud: GOOGLE_TOKEN_URL,
|
|
224
|
+
iat: seconds,
|
|
225
|
+
exp: seconds + ASSERTION_LIFETIME_S,
|
|
226
|
+
...(subject ? { sub: subject } : {}),
|
|
227
|
+
};
|
|
228
|
+
const body = `${part(header)}.${part(claims)}`;
|
|
229
|
+
let signature: string;
|
|
230
|
+
try {
|
|
231
|
+
signature = createSign('RSA-SHA256').update(body).end().sign(key.privateKey, 'base64url');
|
|
232
|
+
} catch {
|
|
233
|
+
// What the key was is never quoted.
|
|
234
|
+
throw new ServiceAccountAuthError(
|
|
235
|
+
`The private key of service account ${key.clientEmail} could not be read. It must be the PEM in the key JSON Google issued.`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return `${body}.${signature}`;
|
|
239
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Importing this module registers the `google_service_account` auth type with
|
|
2
|
+
// UTCP and puts the `http` protocol that acts on it in place of the stock one.
|
|
3
|
+
export {
|
|
4
|
+
isGoogleServiceAccountAuth,
|
|
5
|
+
findUnservedGoogleServiceAccountAuth,
|
|
6
|
+
holdsLiteralGoogleServiceAccountKey,
|
|
7
|
+
} from './google-service-account.auth.js';
|
|
8
|
+
export { GoogleAuthHttpProtocol, installGoogleServiceAccountAuth } from './google-auth-http.protocol.js';
|
|
9
|
+
export { GoogleServiceAccountTokenSource, GOOGLE_TOKEN_URL } from './google-service-account.token-source.js';
|
|
10
|
+
export {
|
|
11
|
+
GOOGLE_SERVICE_ACCOUNT_AUTH_TYPE,
|
|
12
|
+
ServiceAccountAuthError,
|
|
13
|
+
type GoogleServiceAccountAuth,
|
|
14
|
+
type IServiceAccountTokenSource,
|
|
15
|
+
} from './service-account-token.contract.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** The `auth_type` a `.tool` call template names to call a Google API as a service account. */
|
|
2
|
+
export const GOOGLE_SERVICE_ACCOUNT_AUTH_TYPE = 'google_service_account';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A call template's `auth` block for a Google service account, as it reaches
|
|
6
|
+
* the protocol: variables already substituted, so `credentials` holds the key
|
|
7
|
+
* itself rather than the `${VAR}` that named it.
|
|
8
|
+
*/
|
|
9
|
+
export interface GoogleServiceAccountAuth {
|
|
10
|
+
auth_type: typeof GOOGLE_SERVICE_ACCOUNT_AUTH_TYPE;
|
|
11
|
+
/** The service-account key JSON Google issued, or that JSON in base64. Normally `${VAR}`, filled from the Secrets Vault. */
|
|
12
|
+
credentials: string;
|
|
13
|
+
/** The OAuth scopes the token is for: one scope, a space-separated list, or an array. */
|
|
14
|
+
scopes: string | string[];
|
|
15
|
+
/** The user to act as under domain-wide delegation. Absent, the token is the service account's own. */
|
|
16
|
+
subject?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Answers the one question a tool call has of a service account: which bearer
|
|
21
|
+
* token to send right now. How the token is minted and how long it is kept is
|
|
22
|
+
* the implementation's business.
|
|
23
|
+
*/
|
|
24
|
+
export interface IServiceAccountTokenSource {
|
|
25
|
+
accessToken(auth: GoogleServiceAccountAuth): Promise<string>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A service-account token could not be had. The message never carries the key. */
|
|
29
|
+
export class ServiceAccountAuthError extends Error {
|
|
30
|
+
constructor(
|
|
31
|
+
message: string,
|
|
32
|
+
/** How long the same token should not be asked for again. */
|
|
33
|
+
readonly retryAfterMs: number = 5_000,
|
|
34
|
+
) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'ServiceAccountAuthError';
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ export {
|
|
|
36
36
|
|
|
37
37
|
export {
|
|
38
38
|
describeToolFailure,
|
|
39
|
+
withTransportDetail,
|
|
39
40
|
toCallToolResult,
|
|
40
41
|
renderProgress,
|
|
41
42
|
toolError,
|
|
@@ -48,13 +49,40 @@ export {
|
|
|
48
49
|
} from './results.js';
|
|
49
50
|
|
|
50
51
|
export {
|
|
51
|
-
|
|
52
|
+
codeModeMetaTools,
|
|
53
|
+
chainNamespaceExample,
|
|
52
54
|
META_TOOL_NAMES,
|
|
53
55
|
CALL_TOOL_CHAIN_MAX_OUTPUT,
|
|
56
|
+
CALL_TOOL_CHAIN_NAME,
|
|
57
|
+
CHAIN_FAILURES_RULE,
|
|
58
|
+
CHAIN_LARGE_RESULTS_RULE,
|
|
59
|
+
type CodeModeMetaToolsOptions,
|
|
54
60
|
type SpillPort,
|
|
55
61
|
dispatchMetaTool,
|
|
56
62
|
} from './meta-tools.js';
|
|
57
63
|
|
|
64
|
+
export {
|
|
65
|
+
CHAIN_RUNTIME_PRELUDE,
|
|
66
|
+
CHAIN_TIMEOUT_DEFAULT_MS,
|
|
67
|
+
CHAIN_TIMEOUT_MAX_MS,
|
|
68
|
+
CHAIN_TIMEOUT_MIN_MS,
|
|
69
|
+
type ChainNamespaces,
|
|
70
|
+
type ToolChainOutcome,
|
|
71
|
+
chainNamespaces,
|
|
72
|
+
chainOutOfMemoryMessage,
|
|
73
|
+
chainTimeoutMessage,
|
|
74
|
+
describeChainFailure,
|
|
75
|
+
runToolChain,
|
|
76
|
+
unknownNamespaceMessage,
|
|
77
|
+
withChainRuntime,
|
|
78
|
+
} from './chain-runtime.js';
|
|
79
|
+
|
|
80
|
+
export {
|
|
81
|
+
type ChainExample,
|
|
82
|
+
type ChainExampleTool,
|
|
83
|
+
chainExample,
|
|
84
|
+
} from './chain-example.js';
|
|
85
|
+
|
|
58
86
|
export { registerManual, dispatchToolCall } from './dispatch.js';
|
|
59
87
|
|
|
60
88
|
export {
|
|
@@ -62,7 +90,6 @@ export {
|
|
|
62
90
|
RETIRED_TOOL_NAMES,
|
|
63
91
|
retiredToolMessage,
|
|
64
92
|
retiredToolInFailure,
|
|
65
|
-
retiredToolChainFailure,
|
|
66
93
|
} from './retired-tools.js';
|
|
67
94
|
|
|
68
95
|
export {
|
|
@@ -93,3 +120,21 @@ export {
|
|
|
93
120
|
} from './code-mode-names.js';
|
|
94
121
|
|
|
95
122
|
export { printable } from './printable.js';
|
|
123
|
+
|
|
124
|
+
// Loading this package teaches UTCP `auth_type: google_service_account`, on
|
|
125
|
+
// both surfaces alike: the auth type validates and the `http` protocol mints
|
|
126
|
+
// the token. A side effect of the import, like the `@utcp/http` registration
|
|
127
|
+
// it builds on, so neither surface has a step to forget.
|
|
128
|
+
export {
|
|
129
|
+
GOOGLE_SERVICE_ACCOUNT_AUTH_TYPE,
|
|
130
|
+
GOOGLE_TOKEN_URL,
|
|
131
|
+
GoogleAuthHttpProtocol,
|
|
132
|
+
GoogleServiceAccountTokenSource,
|
|
133
|
+
ServiceAccountAuthError,
|
|
134
|
+
findUnservedGoogleServiceAccountAuth,
|
|
135
|
+
holdsLiteralGoogleServiceAccountKey,
|
|
136
|
+
installGoogleServiceAccountAuth,
|
|
137
|
+
isGoogleServiceAccountAuth,
|
|
138
|
+
type GoogleServiceAccountAuth,
|
|
139
|
+
type IServiceAccountTokenSource,
|
|
140
|
+
} from './google-service-account/index.js';
|