@slashfi/agents-sdk 0.90.1 → 0.90.4
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.js +61 -11
- package/dist/adk.js.map +1 -1
- package/dist/cjs/config-store.js +227 -33
- package/dist/cjs/config-store.js.map +1 -1
- package/dist/cjs/define-config.js.map +1 -1
- package/dist/cjs/registry-consumer.js +10 -1
- package/dist/cjs/registry-consumer.js.map +1 -1
- package/dist/config-store.d.ts +36 -0
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +227 -33
- package/dist/config-store.js.map +1 -1
- package/dist/define-config.d.ts +4 -0
- package/dist/define-config.d.ts.map +1 -1
- package/dist/define-config.js.map +1 -1
- package/dist/registry-consumer.d.ts.map +1 -1
- package/dist/registry-consumer.js +10 -1
- package/dist/registry-consumer.js.map +1 -1
- package/package.json +1 -1
- package/src/adk.ts +61 -13
- package/src/config-store.test.ts +350 -0
- package/src/config-store.ts +316 -36
- package/src/consumer.test.ts +54 -0
- package/src/define-config.ts +1 -0
- package/src/registry-consumer.ts +10 -1
package/src/adk.ts
CHANGED
|
@@ -74,19 +74,37 @@ function hasFlag(flag: string): boolean {
|
|
|
74
74
|
return args.includes(flag);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
function getCredentialArgs(startIndex: number, excludeFlags = new Set<string>()): Record<string, string> {
|
|
78
|
+
const credentials: Record<string, string> = {};
|
|
79
|
+
for (let i = startIndex; i < args.length; i++) {
|
|
80
|
+
const arg = args[i];
|
|
81
|
+
if (!arg?.startsWith("--")) continue;
|
|
82
|
+
const key = arg.slice(2);
|
|
83
|
+
if (excludeFlags.has(key)) {
|
|
84
|
+
i++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const value = args[i + 1];
|
|
88
|
+
if (value === undefined || value.startsWith("--")) continue;
|
|
89
|
+
credentials[key] = value;
|
|
90
|
+
i++;
|
|
91
|
+
}
|
|
92
|
+
return credentials;
|
|
93
|
+
}
|
|
94
|
+
|
|
77
95
|
function wantsHelp(): boolean {
|
|
78
96
|
return args.includes("--help") || args.includes("-h");
|
|
79
97
|
}
|
|
80
98
|
|
|
81
99
|
const HELP_SECTIONS: Record<string, string> = {
|
|
82
100
|
registry: `Registry operations:
|
|
83
|
-
adk registry add <url> --name <name> [--auth-type bearer|api-key|none]
|
|
101
|
+
adk registry add <url> --name <name> [--auth-type bearer|basic|api-key|none]
|
|
84
102
|
adk registry remove <name>
|
|
85
103
|
adk registry list
|
|
86
104
|
adk registry browse <name> [--query <q>]
|
|
87
105
|
adk registry inspect <name>
|
|
88
106
|
adk registry test [name]
|
|
89
|
-
adk registry auth <name> [--token <t>] [--api-key <k>] [--header <h>]`,
|
|
107
|
+
adk registry auth <name> [--token <t>] [--username <u> --password <p>] [--api-key <k>] [--header <h>]`,
|
|
90
108
|
ref: `Ref operations:
|
|
91
109
|
adk ref add <name> Install from default (public) registry
|
|
92
110
|
adk ref add <name> --registry <reg> Install from a specific registry
|
|
@@ -123,18 +141,22 @@ Usage:
|
|
|
123
141
|
adk search <query> [options] BM25 search over materialized refs/tools
|
|
124
142
|
adk registry <op> [options] Manage registry connections
|
|
125
143
|
adk ref <op> [options] Manage agent refs
|
|
144
|
+
adk list List configured refs
|
|
145
|
+
adk call <name> <tool> [params] Call a ref tool
|
|
146
|
+
adk auth <name> [--<field> <val>] Authenticate a ref
|
|
147
|
+
adk auth-status <name> Show ref auth status
|
|
126
148
|
adk config-path Print config directory path
|
|
127
149
|
adk error [id] View recent errors or a specific error
|
|
128
150
|
adk version | --version | -v Print the installed adk SDK version
|
|
129
151
|
|
|
130
152
|
Registry operations:
|
|
131
|
-
adk registry add <url> --name <name> [--auth-type bearer|api-key|none]
|
|
153
|
+
adk registry add <url> --name <name> [--auth-type bearer|basic|api-key|none]
|
|
132
154
|
adk registry remove <name>
|
|
133
155
|
adk registry list
|
|
134
156
|
adk registry browse <name> [--query <q>]
|
|
135
157
|
adk registry inspect <name>
|
|
136
158
|
adk registry test [name]
|
|
137
|
-
adk registry auth <name> [--token <t>] [--api-key <k>] [--header <h>]
|
|
159
|
+
adk registry auth <name> [--token <t>] [--username <u> --password <p>] [--api-key <k>] [--header <h>]
|
|
138
160
|
|
|
139
161
|
Ref operations:
|
|
140
162
|
adk ref add <ref> [--name <name>] [--registry <name>] [--url <url>] [--scheme mcp|https|registry]
|
|
@@ -205,9 +227,9 @@ async function runRegistry() {
|
|
|
205
227
|
console.error("Usage: adk registry add <url> --name <name>");
|
|
206
228
|
process.exit(1);
|
|
207
229
|
}
|
|
208
|
-
const authType = getArg("--auth-type") as "bearer" | "api-key" | "none" | undefined;
|
|
230
|
+
const authType = getArg("--auth-type") as "bearer" | "basic" | "api-key" | "none" | undefined;
|
|
209
231
|
const auth = authType && authType !== "none"
|
|
210
|
-
? { type: authType as "bearer" | "api-key" }
|
|
232
|
+
? { type: authType as "bearer" | "basic" | "api-key" }
|
|
211
233
|
: undefined;
|
|
212
234
|
const displayName = name ?? new URL(url).hostname;
|
|
213
235
|
const addResult = await adk.registry.add({
|
|
@@ -290,16 +312,20 @@ async function runRegistry() {
|
|
|
290
312
|
}
|
|
291
313
|
case "auth": {
|
|
292
314
|
const name = args[2];
|
|
293
|
-
if (!name) { console.error("Usage: adk registry auth <name> [--token <t>] [--api-key <k>] [--header <h>]"); process.exit(1); }
|
|
315
|
+
if (!name) { console.error("Usage: adk registry auth <name> [--token <t>] [--username <u> --password <p>] [--api-key <k>] [--header <h>]"); process.exit(1); }
|
|
294
316
|
const token = getArg("--token");
|
|
317
|
+
const username = getArg("--username");
|
|
318
|
+
const password = getArg("--password");
|
|
295
319
|
const apiKey = getArg("--api-key");
|
|
296
320
|
const header = getArg("--header");
|
|
297
321
|
|
|
298
|
-
// Explicit credential mode — user supplied a token/key directly.
|
|
299
|
-
if (token || apiKey) {
|
|
322
|
+
// Explicit credential mode — user supplied a token/basic/key directly.
|
|
323
|
+
if (token || username || apiKey) {
|
|
300
324
|
const credential = token
|
|
301
325
|
? { token }
|
|
302
|
-
:
|
|
326
|
+
: username
|
|
327
|
+
? { username, ...(password !== undefined && { password }) }
|
|
328
|
+
: { apiKey: apiKey!, ...(header && { header }) };
|
|
303
329
|
const updated = await adk.registry.auth(name, credential);
|
|
304
330
|
if (!updated) {
|
|
305
331
|
console.error(`Registry not found: ${name}`);
|
|
@@ -500,13 +526,19 @@ async function runRef() {
|
|
|
500
526
|
}
|
|
501
527
|
case "auth": {
|
|
502
528
|
const name = args[2];
|
|
503
|
-
if (!name) { console.error("Usage: adk ref auth <name> [--api-key <key>]"); process.exit(1); }
|
|
529
|
+
if (!name) { console.error("Usage: adk ref auth <name> [--api-key <key>] [--<field> <value> ...]"); process.exit(1); }
|
|
504
530
|
const apiKey = getArg("--api-key");
|
|
531
|
+
const credentials = getCredentialArgs(3, new Set(["api-key"]));
|
|
505
532
|
|
|
506
|
-
if (apiKey) {
|
|
507
|
-
const result = await adk.ref.auth(name, {
|
|
533
|
+
if (apiKey || Object.keys(credentials).length > 0) {
|
|
534
|
+
const result = await adk.ref.auth(name, {
|
|
535
|
+
...(apiKey && { apiKey }),
|
|
536
|
+
...(Object.keys(credentials).length > 0 && { credentials }),
|
|
537
|
+
});
|
|
508
538
|
if (result.complete) {
|
|
509
539
|
console.log(`\x1b[32m\u2713\x1b[0m Auth complete for ${name} (${result.type})`);
|
|
540
|
+
} else if (result.fields?.length) {
|
|
541
|
+
console.log(JSON.stringify(result, null, 2));
|
|
510
542
|
}
|
|
511
543
|
break;
|
|
512
544
|
}
|
|
@@ -662,6 +694,22 @@ switch (command) {
|
|
|
662
694
|
}
|
|
663
695
|
break;
|
|
664
696
|
}
|
|
697
|
+
case "list":
|
|
698
|
+
args.splice(0, 1, "ref", "list");
|
|
699
|
+
await runRef();
|
|
700
|
+
break;
|
|
701
|
+
case "call":
|
|
702
|
+
args.splice(0, 1, "ref", "call");
|
|
703
|
+
await runRef();
|
|
704
|
+
break;
|
|
705
|
+
case "auth":
|
|
706
|
+
args.splice(0, 1, "ref", "auth");
|
|
707
|
+
await runRef();
|
|
708
|
+
break;
|
|
709
|
+
case "auth-status":
|
|
710
|
+
args.splice(0, 1, "ref", "auth-status");
|
|
711
|
+
await runRef();
|
|
712
|
+
break;
|
|
665
713
|
case "registry":
|
|
666
714
|
await runRegistry();
|
|
667
715
|
break;
|
package/src/config-store.test.ts
CHANGED
|
@@ -583,6 +583,104 @@ describe("ADK ref.call() full auto-refresh flow", () => {
|
|
|
583
583
|
expect(cache.refs.math.authFields).toEqual({});
|
|
584
584
|
});
|
|
585
585
|
|
|
586
|
+
|
|
587
|
+
test("ref http basic auth-status/auth/call/cache uses token with username/password parts", async () => {
|
|
588
|
+
let receivedAccessToken: string | undefined;
|
|
589
|
+
const basicTool = defineTool({
|
|
590
|
+
name: "get_profile",
|
|
591
|
+
description: "Requires HTTP Basic token",
|
|
592
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
593
|
+
execute: async (input: any) => {
|
|
594
|
+
receivedAccessToken = input?.accessToken;
|
|
595
|
+
return { ok: input?.accessToken === Buffer.from("ashby-key:", "utf8").toString("base64") };
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
const basicAgent = defineAgent({
|
|
599
|
+
path: "basic-api",
|
|
600
|
+
entrypoint: "Basic API agent",
|
|
601
|
+
tools: [basicTool],
|
|
602
|
+
visibility: "public",
|
|
603
|
+
config: {
|
|
604
|
+
security: { type: "http", scheme: "basic" },
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
const registry = createAgentRegistry();
|
|
609
|
+
registry.register(basicAgent);
|
|
610
|
+
const port = 19961;
|
|
611
|
+
const server = createAgentServer(registry, { port });
|
|
612
|
+
await server.start();
|
|
613
|
+
try {
|
|
614
|
+
const fs = createMemoryFs();
|
|
615
|
+
const adk = createAdk(fs, {
|
|
616
|
+
encryptionKey: "test-key-32-chars-long-enough!!",
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
await adk.registry.add({ name: "basic-reg", url: `http://localhost:${port}` });
|
|
620
|
+
await adk.ref.add({
|
|
621
|
+
ref: "basic-api",
|
|
622
|
+
name: "basic-api",
|
|
623
|
+
sourceRegistry: {
|
|
624
|
+
url: `http://localhost:${port}`,
|
|
625
|
+
agentPath: "basic-api",
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
const statusBefore = await adk.ref.authStatus("basic-api");
|
|
630
|
+
expect(statusBefore.complete).toBe(false);
|
|
631
|
+
expect(statusBefore.fields.token).toEqual({
|
|
632
|
+
required: true,
|
|
633
|
+
automated: false,
|
|
634
|
+
present: false,
|
|
635
|
+
resolvable: false,
|
|
636
|
+
format: "basic",
|
|
637
|
+
parts: [
|
|
638
|
+
{ name: "username", label: "Username", secret: false },
|
|
639
|
+
{ name: "password", label: "Password", secret: true, optional: true },
|
|
640
|
+
],
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
let cacheRaw = await fs.readFile("registry-cache.json");
|
|
644
|
+
expect(cacheRaw).not.toBeNull();
|
|
645
|
+
let cache = JSON.parse(cacheRaw!) as { refs: Record<string, { authFields?: Record<string, unknown> }> };
|
|
646
|
+
expect(cache.refs["basic-api"].authFields).toEqual({
|
|
647
|
+
token: {
|
|
648
|
+
required: true,
|
|
649
|
+
automated: false,
|
|
650
|
+
format: "basic",
|
|
651
|
+
parts: [
|
|
652
|
+
{ name: "username", label: "Username", secret: false },
|
|
653
|
+
{ name: "password", label: "Password", secret: true, optional: true },
|
|
654
|
+
],
|
|
655
|
+
},
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
const authResult = await adk.ref.auth("basic-api", {
|
|
659
|
+
credentials: { username: "ashby-key", password: "" },
|
|
660
|
+
});
|
|
661
|
+
expect(authResult.complete).toBe(true);
|
|
662
|
+
|
|
663
|
+
const configAfterAuth = await adk.readConfig();
|
|
664
|
+
const refEntry = configAfterAuth.refs?.find((r) => r.name === "basic-api");
|
|
665
|
+
expect(refEntry?.config?.token).toMatch(/^secret:/);
|
|
666
|
+
|
|
667
|
+
const statusAfter = await adk.ref.authStatus("basic-api");
|
|
668
|
+
expect(statusAfter.complete).toBe(true);
|
|
669
|
+
expect(statusAfter.fields.token.present).toBe(true);
|
|
670
|
+
|
|
671
|
+
cacheRaw = await fs.readFile("registry-cache.json");
|
|
672
|
+
cache = JSON.parse(cacheRaw!) as { refs: Record<string, { authFields?: Record<string, unknown> }> };
|
|
673
|
+
const { isRefAuthComplete } = await import("./config-store");
|
|
674
|
+
expect(isRefAuthComplete(refEntry!, cache.refs["basic-api"] as any)).toBe(true);
|
|
675
|
+
|
|
676
|
+
const callResult = await adk.ref.call("basic-api", "get_profile");
|
|
677
|
+
expect((callResult as any)?.result?.ok).toBe(true);
|
|
678
|
+
expect(receivedAccessToken).toBe(Buffer.from("ashby-key:", "utf8").toString("base64"));
|
|
679
|
+
} finally {
|
|
680
|
+
await server.stop();
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
|
|
586
684
|
test("ref.authStatus maps form security to required access_token authFields", async () => {
|
|
587
685
|
// Form security (e.g. databases) asks the user for structured
|
|
588
686
|
// connection fields, but the ADK call path stores the encoded form
|
|
@@ -744,6 +842,258 @@ describe("ADK ref.call() full auto-refresh flow", () => {
|
|
|
744
842
|
expect(status.fields?.access_token?.automated).toBe(false);
|
|
745
843
|
expect(status.fields?.access_token?.present).toBe(false);
|
|
746
844
|
});
|
|
845
|
+
|
|
846
|
+
test("ref.authStatus honors registry-declared authFields over oauth2 heuristics", async () => {
|
|
847
|
+
const platformRegPort = 19924;
|
|
848
|
+
const platformTool = defineTool({
|
|
849
|
+
name: "get_data",
|
|
850
|
+
description: "Platform minted bearer",
|
|
851
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
852
|
+
execute: async () => ({ ok: true }),
|
|
853
|
+
});
|
|
854
|
+
const platformAgent = defineAgent({
|
|
855
|
+
path: "platform-api",
|
|
856
|
+
entrypoint: "Platform bearer agent",
|
|
857
|
+
tools: [platformTool],
|
|
858
|
+
visibility: "public",
|
|
859
|
+
config: {
|
|
860
|
+
security: {
|
|
861
|
+
type: "oauth2",
|
|
862
|
+
flows: {
|
|
863
|
+
authorizationCode: {
|
|
864
|
+
authorizationUrl: "http://localhost/authorize",
|
|
865
|
+
tokenUrl: "http://localhost/token",
|
|
866
|
+
},
|
|
867
|
+
},
|
|
868
|
+
authFields: {
|
|
869
|
+
access_token: { required: true, automated: true },
|
|
870
|
+
},
|
|
871
|
+
},
|
|
872
|
+
},
|
|
873
|
+
});
|
|
874
|
+
const platformRegistry = createAgentRegistry();
|
|
875
|
+
platformRegistry.register(platformAgent);
|
|
876
|
+
const platformServer = createAgentServer(platformRegistry, {
|
|
877
|
+
port: platformRegPort,
|
|
878
|
+
});
|
|
879
|
+
await platformServer.start();
|
|
880
|
+
|
|
881
|
+
try {
|
|
882
|
+
const fs = createMemoryFs();
|
|
883
|
+
const adk = createAdk(fs, {
|
|
884
|
+
resolveCredentials: async ({ field }) =>
|
|
885
|
+
field === "access_token" ? "minted" : null,
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
await adk.registry.add({
|
|
889
|
+
name: "platform-reg",
|
|
890
|
+
url: `http://localhost:${platformRegPort}`,
|
|
891
|
+
});
|
|
892
|
+
await adk.ref.add({
|
|
893
|
+
ref: "platform-api",
|
|
894
|
+
name: "platform-api",
|
|
895
|
+
sourceRegistry: {
|
|
896
|
+
url: `http://localhost:${platformRegPort}`,
|
|
897
|
+
agentPath: "platform-api",
|
|
898
|
+
},
|
|
899
|
+
config: {},
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
const status = await adk.ref.authStatus("platform-api");
|
|
903
|
+
expect(status.fields?.access_token?.automated).toBe(true);
|
|
904
|
+
expect(status.fields?.access_token?.resolvable).toBe(true);
|
|
905
|
+
expect(status.complete).toBe(true);
|
|
906
|
+
|
|
907
|
+
const cacheRaw = await fs.readFile("registry-cache.json");
|
|
908
|
+
const cache = JSON.parse(cacheRaw!) as {
|
|
909
|
+
refs: Record<string, { authFields?: Record<string, unknown> }>;
|
|
910
|
+
};
|
|
911
|
+
expect(cache.refs["platform-api"].authFields).toEqual({
|
|
912
|
+
access_token: { required: true, automated: true },
|
|
913
|
+
});
|
|
914
|
+
} finally {
|
|
915
|
+
await platformServer.stop();
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
describe("ADK ref.call() resolveCredentials fallback", () => {
|
|
921
|
+
let registryServer: AgentServer;
|
|
922
|
+
const REG_PORT = 19922;
|
|
923
|
+
let receivedToken: string | undefined;
|
|
924
|
+
|
|
925
|
+
beforeAll(async () => {
|
|
926
|
+
const apiTool = defineTool({
|
|
927
|
+
name: "get_data",
|
|
928
|
+
description: "Echo accessToken",
|
|
929
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
930
|
+
execute: async (input: any) => {
|
|
931
|
+
receivedToken = input?.accessToken;
|
|
932
|
+
if (!receivedToken) {
|
|
933
|
+
return {
|
|
934
|
+
content: [{ type: "text", text: '{"error":"401 Unauthorized"}' }],
|
|
935
|
+
_httpStatus: 401,
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
return { ok: true, token: receivedToken };
|
|
939
|
+
},
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
const agent = defineAgent({
|
|
943
|
+
path: "resolved-api",
|
|
944
|
+
entrypoint: "Resolved creds agent",
|
|
945
|
+
tools: [apiTool],
|
|
946
|
+
visibility: "public",
|
|
947
|
+
config: {
|
|
948
|
+
security: {
|
|
949
|
+
type: "oauth2",
|
|
950
|
+
flows: {
|
|
951
|
+
authorizationCode: {
|
|
952
|
+
authorizationUrl: "http://localhost/authorize",
|
|
953
|
+
tokenUrl: "http://localhost/token",
|
|
954
|
+
},
|
|
955
|
+
},
|
|
956
|
+
},
|
|
957
|
+
},
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
const registry = createAgentRegistry();
|
|
961
|
+
registry.register(agent);
|
|
962
|
+
registryServer = createAgentServer(registry, { port: REG_PORT });
|
|
963
|
+
await registryServer.start();
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
afterAll(async () => {
|
|
967
|
+
await registryServer.stop();
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
test("uses resolveCredentials when access_token is not stored", async () => {
|
|
971
|
+
receivedToken = undefined;
|
|
972
|
+
const fs = createMemoryFs();
|
|
973
|
+
const adk = createAdk(fs, {
|
|
974
|
+
resolveCredentials: async ({ field }) => {
|
|
975
|
+
if (field === "access_token") return "minted-call-token";
|
|
976
|
+
return null;
|
|
977
|
+
},
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
await adk.registry.add({
|
|
981
|
+
name: "resolved-reg",
|
|
982
|
+
url: `http://localhost:${REG_PORT}`,
|
|
983
|
+
});
|
|
984
|
+
await adk.ref.add({
|
|
985
|
+
ref: "resolved-api",
|
|
986
|
+
sourceRegistry: {
|
|
987
|
+
url: `http://localhost:${REG_PORT}`,
|
|
988
|
+
agentPath: "resolved-api",
|
|
989
|
+
},
|
|
990
|
+
config: {},
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
const result = await adk.ref.call("resolved-api", "get_data");
|
|
994
|
+
expect(receivedToken).toBe("minted-call-token");
|
|
995
|
+
expect((result as any)?.result?.ok).toBe(true);
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
test("stored access_token still wins over resolveCredentials", async () => {
|
|
999
|
+
receivedToken = undefined;
|
|
1000
|
+
const fs = createMemoryFs();
|
|
1001
|
+
const adk = createAdk(fs, {
|
|
1002
|
+
resolveCredentials: async () => "should-not-be-used",
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
await adk.registry.add({
|
|
1006
|
+
name: "resolved-reg",
|
|
1007
|
+
url: `http://localhost:${REG_PORT}`,
|
|
1008
|
+
});
|
|
1009
|
+
await adk.ref.add({
|
|
1010
|
+
ref: "resolved-api",
|
|
1011
|
+
name: "resolved-api-stored",
|
|
1012
|
+
sourceRegistry: {
|
|
1013
|
+
url: `http://localhost:${REG_PORT}`,
|
|
1014
|
+
agentPath: "resolved-api",
|
|
1015
|
+
},
|
|
1016
|
+
config: { access_token: "stored-token" },
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
await adk.ref.call("resolved-api-stored", "get_data");
|
|
1020
|
+
expect(receivedToken).toBe("stored-token");
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
test("uses resolveCredentials for cached apiKey header fields", async () => {
|
|
1024
|
+
let receivedHeaders: Record<string, string> | undefined;
|
|
1025
|
+
const headerRegPort = 19923;
|
|
1026
|
+
const headerServer = createAgentServer(
|
|
1027
|
+
(() => {
|
|
1028
|
+
const apiTool = defineTool({
|
|
1029
|
+
name: "get_data",
|
|
1030
|
+
description: "Echo _headers",
|
|
1031
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
1032
|
+
execute: async (input: any) => {
|
|
1033
|
+
receivedHeaders = input?._headers;
|
|
1034
|
+
return { ok: true, headers: receivedHeaders };
|
|
1035
|
+
},
|
|
1036
|
+
});
|
|
1037
|
+
const agent = defineAgent({
|
|
1038
|
+
path: "header-api",
|
|
1039
|
+
entrypoint: "Header creds agent",
|
|
1040
|
+
tools: [apiTool],
|
|
1041
|
+
visibility: "public",
|
|
1042
|
+
});
|
|
1043
|
+
const registry = createAgentRegistry();
|
|
1044
|
+
registry.register(agent);
|
|
1045
|
+
return registry;
|
|
1046
|
+
})(),
|
|
1047
|
+
{ port: headerRegPort },
|
|
1048
|
+
);
|
|
1049
|
+
await headerServer.start();
|
|
1050
|
+
|
|
1051
|
+
try {
|
|
1052
|
+
receivedHeaders = undefined;
|
|
1053
|
+
const fs = createMemoryFs();
|
|
1054
|
+
const adk = createAdk(fs, {
|
|
1055
|
+
resolveCredentials: async ({ field }) => {
|
|
1056
|
+
if (field === "x_api_key") return "resolved-header-key";
|
|
1057
|
+
return null;
|
|
1058
|
+
},
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
await adk.registry.add({
|
|
1062
|
+
name: "header-reg",
|
|
1063
|
+
url: `http://localhost:${headerRegPort}`,
|
|
1064
|
+
});
|
|
1065
|
+
await adk.ref.add({
|
|
1066
|
+
ref: "header-api",
|
|
1067
|
+
name: "header-api",
|
|
1068
|
+
sourceRegistry: {
|
|
1069
|
+
url: `http://localhost:${headerRegPort}`,
|
|
1070
|
+
agentPath: "header-api",
|
|
1071
|
+
},
|
|
1072
|
+
config: {},
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
await fs.writeFile(
|
|
1076
|
+
"registry-cache.json",
|
|
1077
|
+
JSON.stringify({
|
|
1078
|
+
refs: {
|
|
1079
|
+
"header-api": {
|
|
1080
|
+
ref: "header-api",
|
|
1081
|
+
fetchedAt: new Date().toISOString(),
|
|
1082
|
+
authFields: {
|
|
1083
|
+
x_api_key: { required: true, automated: false },
|
|
1084
|
+
},
|
|
1085
|
+
},
|
|
1086
|
+
},
|
|
1087
|
+
}),
|
|
1088
|
+
);
|
|
1089
|
+
|
|
1090
|
+
const result = await adk.ref.call("header-api", "get_data");
|
|
1091
|
+
expect(receivedHeaders?.["X-API-KEY"]).toBe("resolved-header-key");
|
|
1092
|
+
expect((result as any)?.result?.ok).toBe(true);
|
|
1093
|
+
} finally {
|
|
1094
|
+
await headerServer.stop();
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
747
1097
|
});
|
|
748
1098
|
|
|
749
1099
|
describe("ADK ref.call() auto-refresh on direct MCP 401", () => {
|