@slashfi/agents-sdk 0.67.2 → 0.68.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/adk-tools.d.ts.map +1 -1
- package/dist/adk-tools.js +3 -0
- package/dist/adk-tools.js.map +1 -1
- package/dist/cjs/adk-tools.js +3 -0
- package/dist/cjs/adk-tools.js.map +1 -1
- package/dist/cjs/config-store.js +111 -14
- package/dist/cjs/config-store.js.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/config-store.d.ts +24 -1
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +111 -14
- package/dist/config-store.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +26 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/adk-tools.ts +3 -1
- package/src/config-store.ts +148 -14
- package/src/index.ts +1 -0
- package/src/types.ts +24 -6
package/src/config-store.ts
CHANGED
|
@@ -143,11 +143,29 @@ export interface OAuthResult {
|
|
|
143
143
|
clientId: string;
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/** A field the caller needs to collect from the user */
|
|
147
|
+
export interface AuthChallengeField {
|
|
148
|
+
/** Field key (e.g. "api_key", "token") */
|
|
149
|
+
name: string;
|
|
150
|
+
/** Human-readable label (e.g. "API Key", "DD-API-KEY") */
|
|
151
|
+
label: string;
|
|
152
|
+
/** Whether this is a secret value (should be masked in UI) */
|
|
153
|
+
secret: boolean;
|
|
154
|
+
/** Optional description / help text */
|
|
155
|
+
description?: string;
|
|
156
|
+
}
|
|
157
|
+
|
|
146
158
|
export interface AuthStartResult {
|
|
147
159
|
type: string;
|
|
148
160
|
complete: boolean;
|
|
149
161
|
/** For OAuth: the URL to open in the browser */
|
|
150
162
|
authorizeUrl?: string;
|
|
163
|
+
/**
|
|
164
|
+
* When complete=false and type is "apiKey" or "http",
|
|
165
|
+
* these are the fields the caller should collect from the user.
|
|
166
|
+
* The caller can render these as a form (Slack blocks, web modal, CLI prompts).
|
|
167
|
+
*/
|
|
168
|
+
fields?: AuthChallengeField[];
|
|
151
169
|
}
|
|
152
170
|
|
|
153
171
|
export interface AdkRefApi {
|
|
@@ -168,8 +186,14 @@ export interface AdkRefApi {
|
|
|
168
186
|
* adk.ref.authLocal() to spin up a local server and block.
|
|
169
187
|
*/
|
|
170
188
|
auth(name: string, opts?: {
|
|
171
|
-
/** For API key / bearer auth: the key/token value */
|
|
189
|
+
/** For API key / bearer auth: the key/token value (single-key shorthand) */
|
|
172
190
|
apiKey?: string;
|
|
191
|
+
/**
|
|
192
|
+
* Credentials map for multi-field auth. Keys match the `name` field
|
|
193
|
+
* from AuthChallengeField (e.g. { "api_key": "xxx", "app_key": "yyy" }).
|
|
194
|
+
* For single-key apiKey or http bearer, `apiKey` shorthand also works.
|
|
195
|
+
*/
|
|
196
|
+
credentials?: Record<string, string>;
|
|
173
197
|
/** Extra context to encode in the OAuth state (e.g., tenant/user IDs for multi-tenant callbacks) */
|
|
174
198
|
stateContext?: Record<string, unknown>;
|
|
175
199
|
/** Additional scopes to request (e.g., optional scopes declared by the agent) */
|
|
@@ -998,12 +1022,32 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
998
1022
|
resolvable: false,
|
|
999
1023
|
};
|
|
1000
1024
|
} else if (security.type === "apiKey") {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
automated: false,
|
|
1004
|
-
present: configKeys.includes("api_key"),
|
|
1005
|
-
resolvable: await canResolve("api_key"),
|
|
1025
|
+
const apiKeySec = security as {
|
|
1026
|
+
name?: string; headers?: Record<string, { description?: string }>;
|
|
1006
1027
|
};
|
|
1028
|
+
const toStorageKey = (headerName: string) =>
|
|
1029
|
+
headerName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
1030
|
+
|
|
1031
|
+
if (apiKeySec.headers && Object.keys(apiKeySec.headers).length > 0) {
|
|
1032
|
+
// Multi-key mode
|
|
1033
|
+
for (const headerName of Object.keys(apiKeySec.headers)) {
|
|
1034
|
+
const storageKey = toStorageKey(headerName);
|
|
1035
|
+
fields[storageKey] = {
|
|
1036
|
+
required: true,
|
|
1037
|
+
automated: false,
|
|
1038
|
+
present: configKeys.includes(storageKey),
|
|
1039
|
+
resolvable: await canResolve(storageKey),
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
} else {
|
|
1043
|
+
// Single-key mode (backwards compat)
|
|
1044
|
+
fields.api_key = {
|
|
1045
|
+
required: true,
|
|
1046
|
+
automated: false,
|
|
1047
|
+
present: configKeys.includes("api_key"),
|
|
1048
|
+
resolvable: await canResolve("api_key"),
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1007
1051
|
} else if (security.type === "http") {
|
|
1008
1052
|
fields.token = {
|
|
1009
1053
|
required: true,
|
|
@@ -1022,6 +1066,7 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
1022
1066
|
|
|
1023
1067
|
async auth(name: string, opts?: {
|
|
1024
1068
|
apiKey?: string;
|
|
1069
|
+
credentials?: Record<string, string>;
|
|
1025
1070
|
/** Extra context to encode in the OAuth state (e.g., tenant/user IDs for multi-tenant callbacks) */
|
|
1026
1071
|
stateContext?: Record<string, unknown>;
|
|
1027
1072
|
/** Additional scopes to request (e.g., optional scopes declared by the agent) */
|
|
@@ -1045,15 +1090,92 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
1045
1090
|
}
|
|
1046
1091
|
|
|
1047
1092
|
if (security.type === "apiKey") {
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1093
|
+
const apiKeySec = security as {
|
|
1094
|
+
name?: string; prefix?: string;
|
|
1095
|
+
headers?: Record<string, { description?: string }>;
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
const toStorageKey = (headerName: string) =>
|
|
1099
|
+
headerName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
|
|
1100
|
+
|
|
1101
|
+
if (apiKeySec.headers && Object.keys(apiKeySec.headers).length > 0) {
|
|
1102
|
+
// Multi-key mode: iterate all declared headers
|
|
1103
|
+
const missingFields: AuthChallengeField[] = [];
|
|
1104
|
+
const resolvedKeys: Array<{ storageKey: string; value: string }> = [];
|
|
1105
|
+
|
|
1106
|
+
for (const [headerName, meta] of Object.entries(apiKeySec.headers)) {
|
|
1107
|
+
const storageKey = toStorageKey(headerName);
|
|
1108
|
+
const value = opts?.credentials?.[storageKey] ?? await tryResolve(storageKey);
|
|
1109
|
+
|
|
1110
|
+
if (value) {
|
|
1111
|
+
resolvedKeys.push({ storageKey, value });
|
|
1112
|
+
} else {
|
|
1113
|
+
missingFields.push({
|
|
1114
|
+
name: storageKey,
|
|
1115
|
+
label: headerName,
|
|
1116
|
+
secret: true,
|
|
1117
|
+
description: meta.description,
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
if (missingFields.length > 0) {
|
|
1123
|
+
return { type: "apiKey", complete: false, fields: missingFields };
|
|
1124
|
+
}
|
|
1125
|
+
for (const { storageKey, value } of resolvedKeys) {
|
|
1126
|
+
await storeRefSecret(name, storageKey, value);
|
|
1127
|
+
}
|
|
1128
|
+
return { type: "apiKey", complete: true };
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// Single-key mode (backwards compat)
|
|
1132
|
+
const key = opts?.credentials?.["api_key"] ?? opts?.apiKey ?? await tryResolve("api_key");
|
|
1133
|
+
if (!key) {
|
|
1134
|
+
return {
|
|
1135
|
+
type: "apiKey",
|
|
1136
|
+
complete: false,
|
|
1137
|
+
fields: [{
|
|
1138
|
+
name: "api_key",
|
|
1139
|
+
label: apiKeySec.name ?? "API Key",
|
|
1140
|
+
secret: true,
|
|
1141
|
+
description: apiKeySec.prefix
|
|
1142
|
+
? `Value sent as "${apiKeySec.prefix} <key>"`
|
|
1143
|
+
: undefined,
|
|
1144
|
+
}],
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1050
1147
|
await storeRefSecret(name, "api_key", key);
|
|
1051
1148
|
return { type: "apiKey", complete: true };
|
|
1052
1149
|
}
|
|
1053
1150
|
|
|
1054
1151
|
if (security.type === "http") {
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1152
|
+
const httpSec = security as { scheme?: string };
|
|
1153
|
+
const isBasic = httpSec.scheme === "basic";
|
|
1154
|
+
|
|
1155
|
+
if (isBasic) {
|
|
1156
|
+
const username = opts?.credentials?.["username"] ?? await tryResolve("username");
|
|
1157
|
+
const password = opts?.credentials?.["password"] ?? await tryResolve("password");
|
|
1158
|
+
if (!username || !password) {
|
|
1159
|
+
const missingFields: AuthChallengeField[] = [];
|
|
1160
|
+
if (!username) missingFields.push({ name: "username", label: "Username", secret: false });
|
|
1161
|
+
if (!password) missingFields.push({ name: "password", label: "Password", secret: true });
|
|
1162
|
+
return { type: "http", complete: false, fields: missingFields };
|
|
1163
|
+
}
|
|
1164
|
+
// Store as base64 encoded basic auth token
|
|
1165
|
+
const token = btoa(`${username}:${password}`);
|
|
1166
|
+
await storeRefSecret(name, "token", token);
|
|
1167
|
+
return { type: "http", complete: true };
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// Bearer token
|
|
1171
|
+
const token = opts?.credentials?.["token"] ?? opts?.apiKey ?? await tryResolve("token");
|
|
1172
|
+
if (!token) {
|
|
1173
|
+
return {
|
|
1174
|
+
type: "http",
|
|
1175
|
+
complete: false,
|
|
1176
|
+
fields: [{ name: "token", label: "Bearer Token", secret: true }],
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1057
1179
|
await storeRefSecret(name, "token", token);
|
|
1058
1180
|
return { type: "http", complete: true };
|
|
1059
1181
|
}
|
|
@@ -1062,7 +1184,14 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
1062
1184
|
const flows = (security as { flows?: { authorizationCode?: { authorizationUrl?: string; tokenUrl?: string } } }).flows;
|
|
1063
1185
|
const authCodeFlow = flows?.authorizationCode;
|
|
1064
1186
|
if (!authCodeFlow?.authorizationUrl) {
|
|
1065
|
-
return {
|
|
1187
|
+
return {
|
|
1188
|
+
type: "oauth2",
|
|
1189
|
+
complete: false,
|
|
1190
|
+
fields: [
|
|
1191
|
+
{ name: "client_id", label: "Client ID", secret: false },
|
|
1192
|
+
{ name: "client_secret", label: "Client Secret", secret: true },
|
|
1193
|
+
],
|
|
1194
|
+
};
|
|
1066
1195
|
}
|
|
1067
1196
|
|
|
1068
1197
|
const authUrl = authCodeFlow.authorizationUrl;
|
|
@@ -1116,9 +1245,14 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
1116
1245
|
}
|
|
1117
1246
|
|
|
1118
1247
|
if (!clientId) {
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
)
|
|
1248
|
+
// Return fields telling the caller what OAuth credentials to provide
|
|
1249
|
+
const missingFields: AuthChallengeField[] = [];
|
|
1250
|
+
if (!clientId) {
|
|
1251
|
+
missingFields.push({ name: "client_id", label: "Client ID", secret: false });
|
|
1252
|
+
}
|
|
1253
|
+
// Always ask for client_secret alongside client_id — most providers need it
|
|
1254
|
+
missingFields.push({ name: "client_secret", label: "Client Secret", secret: true });
|
|
1255
|
+
return { type: "oauth2", complete: false, fields: missingFields };
|
|
1122
1256
|
}
|
|
1123
1257
|
|
|
1124
1258
|
// State ties the callback back to this ref. Encode as base64 JSON
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -204,17 +204,35 @@ export interface OAuth2SecurityScheme {
|
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
206
|
* API key authentication.
|
|
207
|
-
* Used by agents that wrap APIs using
|
|
207
|
+
* Used by agents that wrap APIs using one or more keys
|
|
208
208
|
* (e.g. OpenAI, Anthropic, Stripe, Datadog).
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* // Single key (backwards-compatible)
|
|
212
|
+
* security: { type: 'apiKey', in: 'header', name: 'Authorization', prefix: 'Bearer' }
|
|
213
|
+
*
|
|
214
|
+
* // Multiple keys (e.g. Datadog)
|
|
215
|
+
* security: {
|
|
216
|
+
* type: 'apiKey',
|
|
217
|
+
* headers: {
|
|
218
|
+
* 'DD-API-KEY': { description: 'Your Datadog API key' },
|
|
219
|
+
* 'DD-APPLICATION-KEY': { description: 'Your Datadog application key' },
|
|
220
|
+
* }
|
|
221
|
+
* }
|
|
209
222
|
*/
|
|
210
223
|
export interface ApiKeySecurityScheme {
|
|
211
224
|
type: "apiKey";
|
|
212
|
-
/** Where the key is sent */
|
|
213
|
-
in
|
|
214
|
-
/** Header or query parameter name (e.g. "X-API-Key"
|
|
215
|
-
name
|
|
216
|
-
/** Optional prefix (e.g. "Bearer"
|
|
225
|
+
/** Where the key is sent (single-key mode) */
|
|
226
|
+
in?: "header" | "query";
|
|
227
|
+
/** Header or query parameter name (single-key mode, e.g. "X-API-Key") */
|
|
228
|
+
name?: string;
|
|
229
|
+
/** Optional prefix (single-key mode, e.g. "Bearer") */
|
|
217
230
|
prefix?: string;
|
|
231
|
+
/**
|
|
232
|
+
* Named headers the user must provide values for (multi-key mode).
|
|
233
|
+
* When present, this is the source of truth — `in`/`name`/`prefix` are ignored.
|
|
234
|
+
*/
|
|
235
|
+
headers?: Record<string, { description?: string }>;
|
|
218
236
|
}
|
|
219
237
|
|
|
220
238
|
/**
|