@slashfi/agents-sdk 0.80.0 → 0.82.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.js +3 -28
- package/dist/adk.js.map +1 -1
- package/dist/cjs/config-store.js +20 -212
- package/dist/cjs/config-store.js.map +1 -1
- package/dist/cjs/define-config.js.map +1 -1
- package/dist/cjs/materialize.js +3 -4
- package/dist/cjs/materialize.js.map +1 -1
- package/dist/cjs/registry-consumer.js +0 -8
- package/dist/cjs/registry-consumer.js.map +1 -1
- package/dist/cjs/server.js +0 -8
- package/dist/cjs/server.js.map +1 -1
- package/dist/config-store.d.ts +24 -19
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +20 -212
- package/dist/config-store.js.map +1 -1
- package/dist/define-config.d.ts +0 -29
- package/dist/define-config.d.ts.map +1 -1
- package/dist/define-config.js.map +1 -1
- package/dist/materialize.d.ts.map +1 -1
- package/dist/materialize.js +3 -4
- package/dist/materialize.js.map +1 -1
- package/dist/registry-consumer.d.ts +0 -10
- package/dist/registry-consumer.d.ts.map +1 -1
- package/dist/registry-consumer.js +0 -8
- package/dist/registry-consumer.js.map +1 -1
- package/dist/server.d.ts +0 -11
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +0 -8
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/src/adk.ts +3 -31
- package/src/config-store.test.ts +56 -185
- package/src/config-store.ts +38 -259
- package/src/define-config.ts +0 -31
- package/src/materialize.ts +3 -4
- package/src/registry-consumer.ts +0 -27
- package/src/server.ts +0 -19
package/dist/config-store.js
CHANGED
|
@@ -32,37 +32,40 @@ const SECRET_PREFIX = "secret:";
|
|
|
32
32
|
* we evaluate satisfaction against the entry's current `config`.
|
|
33
33
|
*
|
|
34
34
|
* Behavior:
|
|
35
|
-
* - `mode: 'proxy'` refs → always true. Auth lives server-side; the
|
|
36
|
-
* proxy is the source of truth, no entry-side fields involved.
|
|
37
35
|
* - Cache miss (no `authFields` for this ref yet) → returns `null`,
|
|
38
36
|
* signaling "I don't know — caller should fall back to its own
|
|
39
37
|
* heuristic or call `auth-status` to populate the cache".
|
|
40
38
|
* - Cache hit → for every required, non-automated field, checks
|
|
41
|
-
* presence in `entry.config
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
39
|
+
* presence in `entry.config` OR (if `opts.resolvableFields`
|
|
40
|
+
* includes the field name) treats it as satisfied externally.
|
|
41
|
+
* Mirrors the `present || resolvable` check in `auth-status`.
|
|
42
|
+
* `automated` fields (e.g. dynamic OAuth client_id minted by the
|
|
43
|
+
* registry) always count as satisfied.
|
|
45
44
|
*
|
|
46
45
|
* Returning `null` for cache miss is intentional. A boolean would
|
|
47
46
|
* force callers to choose a default that's wrong half the time;
|
|
48
47
|
* `null` lets them branch explicitly.
|
|
49
48
|
*/
|
|
50
|
-
export function isRefAuthComplete(entry, cacheEntry) {
|
|
49
|
+
export function isRefAuthComplete(entry, cacheEntry, opts) {
|
|
51
50
|
if (typeof entry === "string")
|
|
52
51
|
return false;
|
|
53
|
-
if (entry.mode === "proxy")
|
|
54
|
-
return true;
|
|
55
52
|
const authFields = cacheEntry?.authFields;
|
|
56
53
|
if (!authFields)
|
|
57
54
|
return null;
|
|
58
55
|
const config = entry.config ?? {};
|
|
56
|
+
const resolvable = opts?.resolvableFields && opts.resolvableFields.length > 0
|
|
57
|
+
? new Set(opts.resolvableFields)
|
|
58
|
+
: null;
|
|
59
59
|
for (const [field, info] of Object.entries(authFields)) {
|
|
60
60
|
if (!info.required)
|
|
61
61
|
continue;
|
|
62
62
|
if (info.automated)
|
|
63
63
|
continue;
|
|
64
|
-
if (
|
|
65
|
-
|
|
64
|
+
if (field in config)
|
|
65
|
+
continue;
|
|
66
|
+
if (resolvable && resolvable.has(field))
|
|
67
|
+
continue;
|
|
68
|
+
return false;
|
|
66
69
|
}
|
|
67
70
|
return true;
|
|
68
71
|
}
|
|
@@ -617,78 +620,6 @@ export function createAdk(fs, options = {}) {
|
|
|
617
620
|
return fallback;
|
|
618
621
|
}
|
|
619
622
|
// ==========================================
|
|
620
|
-
// Proxy Routing
|
|
621
|
-
// ==========================================
|
|
622
|
-
/**
|
|
623
|
-
* Find the configured RegistryEntry for a ref, consulting `sourceRegistry`
|
|
624
|
-
* first and falling back to the first registry in config. Returns `null` when
|
|
625
|
-
* the ref is sourced from a raw URL (no registry), in which case proxy routing
|
|
626
|
-
* does not apply.
|
|
627
|
-
*/
|
|
628
|
-
async function findRegistryEntryForRef(entry) {
|
|
629
|
-
const sourceUrl = entry.sourceRegistry?.url;
|
|
630
|
-
if (!sourceUrl)
|
|
631
|
-
return null;
|
|
632
|
-
const config = await readConfig();
|
|
633
|
-
const match = (config.registries ?? []).find((r) => {
|
|
634
|
-
if (typeof r === "string")
|
|
635
|
-
return r === sourceUrl;
|
|
636
|
-
return r.url === sourceUrl;
|
|
637
|
-
});
|
|
638
|
-
if (!match || typeof match === "string")
|
|
639
|
-
return null;
|
|
640
|
-
return match;
|
|
641
|
-
}
|
|
642
|
-
/**
|
|
643
|
-
* Returns the proxy settings for a ref when its source registry has
|
|
644
|
-
* `proxy` configured. `null` means "run locally".
|
|
645
|
-
*
|
|
646
|
-
* Callers pass `{ preferLocal: true }` to opt out of `mode: 'optional'`
|
|
647
|
-
* proxying when they already hold credentials locally. `mode: 'required'`
|
|
648
|
-
* cannot be bypassed — the registry owns auth server-side and there is
|
|
649
|
-
* nothing useful the local SDK can do.
|
|
650
|
-
*/
|
|
651
|
-
async function resolveProxyForRef(entry, opts) {
|
|
652
|
-
const reg = await findRegistryEntryForRef(entry);
|
|
653
|
-
if (!reg?.proxy)
|
|
654
|
-
return null;
|
|
655
|
-
if (reg.proxy.mode === "optional" && opts?.preferLocal)
|
|
656
|
-
return null;
|
|
657
|
-
return { reg, agent: reg.proxy.agent ?? "@config" };
|
|
658
|
-
}
|
|
659
|
-
/**
|
|
660
|
-
* Forward an `@config ref` operation to the proxy agent on a remote registry.
|
|
661
|
-
*
|
|
662
|
-
* The remote side speaks the standard adk-tools surface, so the call shape is
|
|
663
|
-
* identical to what the local `ref` API would do — the only difference is
|
|
664
|
-
* that tokens and secrets live server-side. `callRegistry` returns the
|
|
665
|
-
* standard CallAgentResponse envelope: `{ success: true, result }` on
|
|
666
|
-
* success or `{ success: false, error }` on failure. We unwrap once and
|
|
667
|
-
* throw on error so callers get a result that matches the local signature.
|
|
668
|
-
*/
|
|
669
|
-
async function forwardRefOpToProxy(reg, agent, operation, params) {
|
|
670
|
-
const consumer = await buildConsumerForRef({
|
|
671
|
-
ref: "",
|
|
672
|
-
name: "",
|
|
673
|
-
sourceRegistry: { url: reg.url, agentPath: agent },
|
|
674
|
-
});
|
|
675
|
-
const resolved = consumer.registries().find((r) => r.url === reg.url);
|
|
676
|
-
if (!resolved)
|
|
677
|
-
throw new Error(`Registry ${reg.url} not resolvable for proxy forwarding`);
|
|
678
|
-
const response = await consumer.callRegistry(resolved, {
|
|
679
|
-
action: "execute_tool",
|
|
680
|
-
path: agent,
|
|
681
|
-
tool: "ref",
|
|
682
|
-
params: { operation, ...params },
|
|
683
|
-
});
|
|
684
|
-
if (!response.success) {
|
|
685
|
-
const errResponse = response;
|
|
686
|
-
const msg = errResponse.error ?? `Proxy ${agent}.ref(${operation}) failed`;
|
|
687
|
-
throw new Error(msg);
|
|
688
|
-
}
|
|
689
|
-
return response.result;
|
|
690
|
-
}
|
|
691
|
-
// ==========================================
|
|
692
623
|
// Registry API
|
|
693
624
|
// ==========================================
|
|
694
625
|
/**
|
|
@@ -701,42 +632,6 @@ export function createAdk(fs, options = {}) {
|
|
|
701
632
|
return value;
|
|
702
633
|
return `${SECRET_PREFIX}${await encryptSecret(value, options.encryptionKey)}`;
|
|
703
634
|
}
|
|
704
|
-
/**
|
|
705
|
-
* Re-probe a registry with the current stored credentials to see whether it
|
|
706
|
-
* advertises `capabilities.registry.proxy` in its MCP `initialize` response,
|
|
707
|
-
* and persist the proxy config when it does. Safe to call after a successful
|
|
708
|
-
* `auth()` / `authLocal()` — on the add path we skip the proxy probe when
|
|
709
|
-
* auth is required, so this is the second chance to back-fill it.
|
|
710
|
-
*
|
|
711
|
-
* Respects explicit user config: if `proxy` is already set, we leave it
|
|
712
|
-
* alone. Any discovery failure is swallowed — proxy is an optimization,
|
|
713
|
-
* not a correctness requirement.
|
|
714
|
-
*/
|
|
715
|
-
async function discoverProxyAfterAuth(nameOrUrl) {
|
|
716
|
-
const config = await readConfig();
|
|
717
|
-
const target = findRegistry(config.registries ?? [], nameOrUrl);
|
|
718
|
-
if (!target || typeof target === "string")
|
|
719
|
-
return;
|
|
720
|
-
if (target.proxy)
|
|
721
|
-
return;
|
|
722
|
-
try {
|
|
723
|
-
const consumer = await buildConsumer(nameOrUrl);
|
|
724
|
-
const discovered = await consumer.discover(target.url);
|
|
725
|
-
if (!discovered.proxy?.mode)
|
|
726
|
-
return;
|
|
727
|
-
await updateRegistryEntry(nameOrUrl, (existing) => {
|
|
728
|
-
if (existing.proxy)
|
|
729
|
-
return;
|
|
730
|
-
existing.proxy = {
|
|
731
|
-
mode: discovered.proxy.mode,
|
|
732
|
-
...(discovered.proxy.agent && { agent: discovered.proxy.agent }),
|
|
733
|
-
};
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
catch {
|
|
737
|
-
// Proxy probe is best-effort — auth itself already succeeded.
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
635
|
/**
|
|
741
636
|
* Atomic read-modify-write on a registry entry by name or URL. Used by
|
|
742
637
|
* `authLocal` to persist both `auth` and `oauth` together, which `auth()`
|
|
@@ -877,14 +772,10 @@ export function createAdk(fs, options = {}) {
|
|
|
877
772
|
const config = await readConfig();
|
|
878
773
|
const alias = entry.name ?? entry.url;
|
|
879
774
|
const registries = (config.registries ?? []).filter((r) => registryDisplayName(r) !== alias);
|
|
880
|
-
// Probe the registry before saving.
|
|
881
|
-
//
|
|
882
|
-
//
|
|
883
|
-
//
|
|
884
|
-
// 2. Proxy capability — the MCP `initialize` response may advertise
|
|
885
|
-
// `capabilities.registry.proxy`, which auto-populates `proxy`.
|
|
886
|
-
// Users who set `proxy` or `auth` explicitly on the entry always win:
|
|
887
|
-
// discovery only fills in blanks.
|
|
775
|
+
// Probe the registry before saving. If it returns 401 with a
|
|
776
|
+
// WWW-Authenticate / RFC 9728 resource metadata pointer, persist
|
|
777
|
+
// that on `authRequirement` so subsequent ops can refuse early
|
|
778
|
+
// with a friendly message.
|
|
888
779
|
let final = entry;
|
|
889
780
|
let authRequirement;
|
|
890
781
|
const hasUsableAuth = entry.auth && entry.auth.type !== "none"
|
|
@@ -899,30 +790,6 @@ export function createAdk(fs, options = {}) {
|
|
|
899
790
|
final = { ...final, authRequirement };
|
|
900
791
|
}
|
|
901
792
|
}
|
|
902
|
-
if (!entry.proxy && !authRequirement) {
|
|
903
|
-
try {
|
|
904
|
-
const probeConsumer = await createRegistryConsumer({ registries: [entry], refs: [] }, { token: options.token, fetch: options.fetch });
|
|
905
|
-
const resolved = probeConsumer.registries()[0];
|
|
906
|
-
if (resolved) {
|
|
907
|
-
const discovered = await probeConsumer.discover(resolved.url);
|
|
908
|
-
if (discovered.proxy?.mode) {
|
|
909
|
-
final = {
|
|
910
|
-
...final,
|
|
911
|
-
proxy: {
|
|
912
|
-
mode: discovered.proxy.mode,
|
|
913
|
-
...(discovered.proxy.agent && {
|
|
914
|
-
agent: discovered.proxy.agent,
|
|
915
|
-
}),
|
|
916
|
-
},
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
catch {
|
|
922
|
-
// Discovery is best-effort — offline, unreachable, or non-adk
|
|
923
|
-
// registries simply skip proxy auto-configuration.
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
793
|
registries.push(final);
|
|
927
794
|
await writeConfig({ ...config, registries });
|
|
928
795
|
return authRequirement ? { authRequirement } : {};
|
|
@@ -968,8 +835,6 @@ export function createAdk(fs, options = {}) {
|
|
|
968
835
|
existing.auth = updates.auth;
|
|
969
836
|
if (updates.headers)
|
|
970
837
|
existing.headers = { ...existing.headers, ...updates.headers };
|
|
971
|
-
if (updates.proxy !== undefined)
|
|
972
|
-
existing.proxy = updates.proxy;
|
|
973
838
|
return existing;
|
|
974
839
|
});
|
|
975
840
|
if (!found)
|
|
@@ -1069,8 +934,6 @@ export function createAdk(fs, options = {}) {
|
|
|
1069
934
|
}
|
|
1070
935
|
delete existing.authRequirement;
|
|
1071
936
|
});
|
|
1072
|
-
if (updated)
|
|
1073
|
-
await discoverProxyAfterAuth(nameOrUrl);
|
|
1074
937
|
return updated;
|
|
1075
938
|
},
|
|
1076
939
|
async authLocal(nameOrUrl, opts) {
|
|
@@ -1217,7 +1080,6 @@ export function createAdk(fs, options = {}) {
|
|
|
1217
1080
|
};
|
|
1218
1081
|
delete existing.authRequirement;
|
|
1219
1082
|
});
|
|
1220
|
-
await discoverProxyAfterAuth(displayName);
|
|
1221
1083
|
resOut.writeHead(200, { "Content-Type": "text/html" });
|
|
1222
1084
|
resOut.end(renderAuthSuccess(displayName));
|
|
1223
1085
|
server.close();
|
|
@@ -1596,12 +1458,6 @@ export function createAdk(fs, options = {}) {
|
|
|
1596
1458
|
const entry = findRef(config.refs ?? [], name);
|
|
1597
1459
|
if (!entry)
|
|
1598
1460
|
throw new Error(`Ref "${name}" not found`);
|
|
1599
|
-
// Registry-proxied refs: ask the remote @config for state (secrets live
|
|
1600
|
-
// server-side so local inspection would always return "missing").
|
|
1601
|
-
const proxy = await resolveProxyForRef(entry);
|
|
1602
|
-
if (proxy) {
|
|
1603
|
-
return forwardRefOpToProxy(proxy.reg, proxy.agent, "auth-status", { name });
|
|
1604
|
-
}
|
|
1605
1461
|
let security = null;
|
|
1606
1462
|
try {
|
|
1607
1463
|
const consumer = await buildConsumerForRef(entry);
|
|
@@ -1723,25 +1579,6 @@ export function createAdk(fs, options = {}) {
|
|
|
1723
1579
|
const entry = findRef(config.refs ?? [], name);
|
|
1724
1580
|
if (!entry)
|
|
1725
1581
|
throw new Error(`Ref "${name}" not found`);
|
|
1726
|
-
// Registry-proxied auth: forward the start-of-flow to the remote @config
|
|
1727
|
-
// agent. The registry owns the client_id/secret and returns an authorize
|
|
1728
|
-
// URL pointing at the registry's OAuth callback domain, so the user
|
|
1729
|
-
// completes the flow against the registry instead of localhost.
|
|
1730
|
-
const proxy = await resolveProxyForRef(entry, {
|
|
1731
|
-
preferLocal: opts?.preferLocal,
|
|
1732
|
-
});
|
|
1733
|
-
if (proxy) {
|
|
1734
|
-
const params = { name };
|
|
1735
|
-
if (opts?.apiKey !== undefined)
|
|
1736
|
-
params.apiKey = opts.apiKey;
|
|
1737
|
-
if (opts?.credentials)
|
|
1738
|
-
params.credentials = opts.credentials;
|
|
1739
|
-
if (opts?.scopes)
|
|
1740
|
-
params.scopes = opts.scopes;
|
|
1741
|
-
if (opts?.stateContext)
|
|
1742
|
-
params.stateContext = opts.stateContext;
|
|
1743
|
-
return forwardRefOpToProxy(proxy.reg, proxy.agent, "auth", params);
|
|
1744
|
-
}
|
|
1745
1582
|
const status = await ref.authStatus(name);
|
|
1746
1583
|
const security = status.security;
|
|
1747
1584
|
const tryResolve = makeTryResolve({ name, entry, security });
|
|
@@ -1995,32 +1832,16 @@ export function createAdk(fs, options = {}) {
|
|
|
1995
1832
|
return { type: security.type, complete: false };
|
|
1996
1833
|
},
|
|
1997
1834
|
async authLocal(name, opts) {
|
|
1998
|
-
// `ref.auth` is already proxy-aware — for proxied refs it returns
|
|
1999
|
-
// the authorizeUrl that the registry minted against its own
|
|
2000
|
-
// callback domain. Everything below is identical for local and
|
|
2001
|
-
// proxied refs except the last step (polling for the callback),
|
|
2002
|
-
// which only makes sense when we own the redirect URI.
|
|
2003
1835
|
const result = await ref.auth(name);
|
|
2004
1836
|
if (result.complete)
|
|
2005
1837
|
return { complete: true };
|
|
2006
|
-
const config = await readConfig();
|
|
2007
|
-
const entry = findRef(config.refs ?? [], name);
|
|
2008
|
-
const proxy = entry ? await resolveProxyForRef(entry) : null;
|
|
2009
1838
|
const port = options.oauthCallbackPort ?? 8919;
|
|
2010
1839
|
const timeout = opts?.timeoutMs ?? 300_000;
|
|
2011
1840
|
const { createServer } = await import("node:http");
|
|
2012
1841
|
// API key / HTTP auth — local credential form.
|
|
2013
|
-
//
|
|
2014
|
-
// We refuse to serve the form for a proxied ref: the registry
|
|
2015
|
-
// owns the credential store, so the user needs to submit via
|
|
2016
|
-
// whatever UI the registry exposes. Supporting this through the
|
|
2017
|
-
// proxy would need a remote form endpoint — out of scope here.
|
|
2018
1842
|
if (result.fields &&
|
|
2019
1843
|
result.fields.length > 0 &&
|
|
2020
1844
|
result.type !== "oauth2") {
|
|
2021
|
-
if (proxy) {
|
|
2022
|
-
throw new Error(`Ref "${name}" is sourced from a proxied registry; submit credentials through ${proxy.agent} instead of a local form.`);
|
|
2023
|
-
}
|
|
2024
1845
|
return new Promise((resolve, reject) => {
|
|
2025
1846
|
const server = createServer(async (req, res) => {
|
|
2026
1847
|
const reqUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
@@ -2082,13 +1903,8 @@ export function createAdk(fs, options = {}) {
|
|
|
2082
1903
|
if (opts?.onAuthorizeUrl) {
|
|
2083
1904
|
opts.onAuthorizeUrl(result.authorizeUrl);
|
|
2084
1905
|
}
|
|
2085
|
-
//
|
|
2086
|
-
//
|
|
2087
|
-
// schedule once the user finishes the remote consent screen.
|
|
2088
|
-
if (proxy)
|
|
2089
|
-
return { complete: false };
|
|
2090
|
-
// Local refs: spin up the callback server on oauthCallbackPort and
|
|
2091
|
-
// block until the OAuth provider redirects back.
|
|
1906
|
+
// Spin up the callback server on oauthCallbackPort and block
|
|
1907
|
+
// until the OAuth provider redirects back.
|
|
2092
1908
|
return new Promise((resolve, reject) => {
|
|
2093
1909
|
const server = createServer(async (req, res) => {
|
|
2094
1910
|
const reqUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
@@ -2127,14 +1943,6 @@ export function createAdk(fs, options = {}) {
|
|
|
2127
1943
|
});
|
|
2128
1944
|
},
|
|
2129
1945
|
async refreshToken(name) {
|
|
2130
|
-
// Registry-proxied refs: the remote @config holds the refresh_token.
|
|
2131
|
-
const entryForProxy = await ref.get(name);
|
|
2132
|
-
if (entryForProxy) {
|
|
2133
|
-
const proxy = await resolveProxyForRef(entryForProxy);
|
|
2134
|
-
if (proxy) {
|
|
2135
|
-
return forwardRefOpToProxy(proxy.reg, proxy.agent, "refresh-token", { name });
|
|
2136
|
-
}
|
|
2137
|
-
}
|
|
2138
1946
|
// Read stored refresh_token
|
|
2139
1947
|
const refreshToken = await readRefSecret(name, "refresh_token");
|
|
2140
1948
|
if (!refreshToken)
|