@agentvalet/client 0.1.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 ADDED
@@ -0,0 +1,144 @@
1
+ # @agentvalet/client
2
+
3
+ Call approved SaaS platforms from any Node agent. Your agent never holds the
4
+ downstream credential — AgentValet signs a short-lived identity assertion,
5
+ checks the call against the owner's grants and policy, injects the credential
6
+ at call time, and writes an audit record.
7
+
8
+ **Not in an MCP host?** This is your package. If you *are* inside Claude Code,
9
+ Claude Desktop, Cursor, or another MCP-aware host, install
10
+ [`@agentvalet/mcp-server`](https://www.npmjs.com/package/@agentvalet/mcp-server)
11
+ instead and call `use_platform` — you get the same guarantees with no code.
12
+
13
+ Building your own MCP *server* and want to enforce policy inside it? That's
14
+ [`@agentvalet/mcp-broker`](https://www.npmjs.com/package/@agentvalet/mcp-broker).
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install @agentvalet/client
20
+ ```
21
+
22
+ Node 18+. One dependency (`jose`, for RS256 signing).
23
+
24
+ ## Get an agent identity
25
+
26
+ ```bash
27
+ npx @agentvalet/register
28
+ ```
29
+
30
+ This generates an RSA keypair **locally** — the private key never leaves your
31
+ machine — registers the public half, and writes `~/.agentvalet/agent.key`.
32
+
33
+ ## Use it
34
+
35
+ ```typescript
36
+ import { AgentValet } from "@agentvalet/client";
37
+
38
+ const av = AgentValet.fromEnv();
39
+
40
+ const result = await av.call({
41
+ platform: "slack",
42
+ endpoint: "/api/chat.postMessage",
43
+ method: "POST",
44
+ scope: "chat:write",
45
+ data: { channel: "#general", text: "Deploy finished." },
46
+ });
47
+ ```
48
+
49
+ `fromEnv()` reads `AGENTVALET_AGENT_ID` / `AGENTVALET_OWNER_ID` (or the bare
50
+ `AGENT_ID` / `OWNER_ID` the CLI writes) and finds the key via
51
+ `AGENT_PRIVATE_KEY_B64`, `AGENT_PRIVATE_KEY_PATH`, `AGENT_PRIVATE_KEY`, or
52
+ `~/.agentvalet/agent.key`. To wire it up explicitly:
53
+
54
+ ```typescript
55
+ const av = new AgentValet({
56
+ agentId: process.env.AGENT_ID!,
57
+ ownerId: process.env.OWNER_ID!,
58
+ privateKey: process.env.AGENT_PRIVATE_KEY!,
59
+ proxyUrl: "https://api.agentvalet.ai", // default
60
+ });
61
+ ```
62
+
63
+ ## Approvals are just a slower call
64
+
65
+ When the owner has marked a scope as requiring approval, the proxy holds the
66
+ action and `call()` waits. If the owner approves, the proxy re-runs the call and
67
+ you get the result back — from your code's point of view it simply took longer.
68
+
69
+ ```typescript
70
+ const av = AgentValet.fromEnv({
71
+ onApprovalPending: ({ elapsedMs }) =>
72
+ console.log(`waiting for owner… ${Math.round(elapsedMs / 1000)}s`),
73
+ });
74
+ ```
75
+
76
+ If nobody responds inside the budget (50s by default), you get an
77
+ `ApprovalTimeoutError` — **not** a failure. The action stays queued. Keep the
78
+ `approvalId` and resume whenever you like:
79
+
80
+ ```typescript
81
+ import { ApprovalTimeoutError } from "@agentvalet/client";
82
+
83
+ try {
84
+ await av.call({ platform: "stripe", endpoint: "/v1/refunds", method: "POST", scope: "charge" });
85
+ } catch (err) {
86
+ if (err instanceof ApprovalTimeoutError) {
87
+ await saveForLater(err.approvalId); // e.g. into your job queue
88
+ }
89
+ }
90
+
91
+ // …later, in another process:
92
+ const result = await av.waitForApproval(approvalId);
93
+ ```
94
+
95
+ Set `approvalTimeoutMs: 0` if you never want to block — `call()` then throws
96
+ `ApprovalTimeoutError` the moment approval is required.
97
+
98
+ ## Errors you can branch on
99
+
100
+ Every throw is typed, so you never string-match an error envelope:
101
+
102
+ | Error | Means | What to do |
103
+ |---|---|---|
104
+ | `ConfigError` | Bad/missing identity or key | Fix config; thrown before any network call |
105
+ | `NetworkError` | Transport failed | `.message` diagnoses DNS / TLS / firewall / timeout |
106
+ | `AccessDeniedError` | No grant, or policy blocked it | `requestAccess()` — see below |
107
+ | `ApprovalDeniedError` | Owner said no | Terminal. Don't retry |
108
+ | `ApprovalExpiredError` | Aged out server-side | Re-issue the call |
109
+ | `ApprovalTimeoutError` | You stopped waiting | Resume via `waitForApproval(approvalId)` |
110
+ | `UpstreamError` | The SaaS itself returned non-2xx | `.status` / `.data` hold the upstream reply |
111
+ | `ProxyError` | Anything else from the broker | `.status` / `.body` |
112
+
113
+ ## Asking for access you don't have
114
+
115
+ Deny-by-default means a scope you were never granted returns
116
+ `AccessDeniedError`. Your agent can ask for it:
117
+
118
+ ```typescript
119
+ const { status } = await av.requestAccess({
120
+ platform: "slack",
121
+ scope: "chat:write",
122
+ reason: "Post deploy notifications to #general",
123
+ });
124
+ if (status === "approved") { /* retry the original call */ }
125
+ ```
126
+
127
+ ## Checking before you act
128
+
129
+ ```typescript
130
+ await av.listPlatforms(); // what this agent is actually granted
131
+ await av.pendingActions(); // queued behind an approval
132
+ await av.evaluate("stripe", "charge"); // dry-run the decision, no side effect
133
+ ```
134
+
135
+ `evaluate()` is worth a call before anything destructive — it tells you whether
136
+ the action would be allowed without performing it.
137
+
138
+ ## Self-hosting
139
+
140
+ Point `proxyUrl` at your own deployment. Everything else is identical.
141
+
142
+ ## License
143
+
144
+ MIT
@@ -0,0 +1,111 @@
1
+ import { type FetchLike } from "./http.js";
2
+ export interface AgentValetOptions {
3
+ agentId: string;
4
+ ownerId: string;
5
+ /** RS256 private key, PEM (PKCS#8). */
6
+ privateKey: string;
7
+ /** Defaults to https://api.agentvalet.ai. */
8
+ proxyUrl?: string;
9
+ /** Per-request network timeout. Default 15s. */
10
+ timeoutMs?: number;
11
+ /**
12
+ * How long `call()` waits on an owner approval before throwing
13
+ * ApprovalTimeoutError. Default 50s. Set 0 to never wait — `call()` then
14
+ * throws immediately with the approval_id for you to resume later.
15
+ */
16
+ approvalTimeoutMs?: number;
17
+ /** Approval poll interval. Default 2s. */
18
+ approvalPollMs?: number;
19
+ /** Injectable for tests / custom agents. Defaults to global fetch. */
20
+ fetch?: FetchLike;
21
+ /** Called roughly every poll while an approval is outstanding. */
22
+ onApprovalPending?: (info: {
23
+ approvalId: string;
24
+ elapsedMs: number;
25
+ platform: string;
26
+ scope: string;
27
+ endpoint: string;
28
+ }) => void;
29
+ }
30
+ export interface CallRequest {
31
+ platform: string;
32
+ endpoint: string;
33
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
34
+ scope: string;
35
+ data?: unknown;
36
+ /** Pick a specific connection when the platform has several. */
37
+ connectionId?: string;
38
+ /** Justification shown to the approver. Normalized and capped server-side. */
39
+ reason?: string;
40
+ /** Overrides the client-level approval budget for this one call. */
41
+ approvalTimeoutMs?: number;
42
+ }
43
+ export declare class AgentValet {
44
+ readonly agentId: string;
45
+ readonly ownerId: string;
46
+ readonly proxyUrl: string;
47
+ private readonly privateKeyPem;
48
+ private readonly timeoutMs;
49
+ private readonly approvalTimeoutMs;
50
+ private readonly approvalPollMs;
51
+ private readonly doFetch;
52
+ private readonly onApprovalPending?;
53
+ private importedKey;
54
+ constructor(opts: AgentValetOptions);
55
+ /**
56
+ * Builds a client from the environment. Works out of the box on a machine
57
+ * that has run `npx @agentvalet/register`. See env.ts for the accepted names.
58
+ */
59
+ static fromEnv(overrides?: Partial<AgentValetOptions>): AgentValet;
60
+ /**
61
+ * Calls an approved platform through the broker. Returns the upstream SaaS
62
+ * response body, parsed.
63
+ *
64
+ * If the owner must approve, this blocks for up to `approvalTimeoutMs` and
65
+ * then returns the result transparently — from the caller's point of view an
66
+ * approved call just took longer. If nobody approves in time it throws
67
+ * ApprovalTimeoutError, whose `approvalId` you can resume with
68
+ * `waitForApproval()`; the action stays queued server-side either way.
69
+ */
70
+ call<T = unknown>(req: CallRequest): Promise<T>;
71
+ /**
72
+ * Resumes waiting on an outstanding approval. Use with the `approvalId` from
73
+ * a caught ApprovalTimeoutError — the action is queued server-side, so this
74
+ * picks up exactly where `call()` left off.
75
+ */
76
+ waitForApproval<T = unknown>(approvalId: string, ctx?: {
77
+ platform?: string;
78
+ scope?: string;
79
+ endpoint?: string;
80
+ timeoutMs?: number;
81
+ }): Promise<T>;
82
+ /** Platforms and scopes this agent is actually granted. Deny-by-default: if
83
+ * it isn't here, `call()` will 403. */
84
+ listPlatforms<T = unknown>(): Promise<T>;
85
+ /** Actions queued behind an owner approval that haven't resolved yet. */
86
+ pendingActions<T = unknown>(): Promise<T>;
87
+ /**
88
+ * Dry-run an authorization decision without performing the action. Cheap way
89
+ * to check a destructive scope before committing to a side effect.
90
+ */
91
+ evaluate<T = unknown>(platform: string, scope: string): Promise<T>;
92
+ /**
93
+ * Asks an org admin to grant platform+scope, then polls for the decision.
94
+ * Resolves "approved" | "denied" | "pending" — on "approved", retry the
95
+ * original `call()`.
96
+ */
97
+ requestAccess(req: {
98
+ platform: string;
99
+ scope?: string;
100
+ reason?: string;
101
+ timeoutMs?: number;
102
+ }): Promise<{
103
+ status: "approved" | "denied" | "pending";
104
+ requestToken: string;
105
+ }>;
106
+ /** Signs a short-lived (60s) RS256 assertion of this agent's identity. */
107
+ signJWT(): Promise<string>;
108
+ private authedFetch;
109
+ private getJson;
110
+ }
111
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,269 @@
1
+ // packages/client/src/client.ts
2
+ //
3
+ // The AgentValet client. This is the non-MCP path: a developer writing their
4
+ // own agent (LangChain, a cron job, a plain Node service) gets the same
5
+ // governance guarantees the MCP server provides — signed agent identity,
6
+ // deny-by-default scope enforcement, the owner-approval long-poll, and the
7
+ // audit record — without hand-rolling RS256 JWTs against /v1/actions.
8
+ //
9
+ // Deliberate parity with apps/mcp-server: same endpoints, same 50s approval
10
+ // budget, same 2s poll interval, same one-shot 401 retry. If these drift, an
11
+ // operator debugging the SDK can no longer reason from the MCP server's
12
+ // behaviour (and vice versa).
13
+ import { SignJWT, importPKCS8 } from "jose";
14
+ import { AccessDeniedError, ApprovalDeniedError, ApprovalExpiredError, ApprovalTimeoutError, ConfigError, ProxyError, UpstreamError, } from "./errors.js";
15
+ import { diagnoseNetworkError, fetchWithTimeout, sleep, tryParseJson } from "./http.js";
16
+ import { DEFAULT_PROXY_URL, agentIdFromEnv, ownerIdFromEnv, privateKeyFromEnv, proxyUrlFromEnv, } from "./env.js";
17
+ /** Matches the mcp-server's budget: under Claude Desktop's 60s tool timeout. */
18
+ const DEFAULT_APPROVAL_TIMEOUT_MS = 50_000;
19
+ const DEFAULT_APPROVAL_POLL_MS = 2_000;
20
+ const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
21
+ export class AgentValet {
22
+ agentId;
23
+ ownerId;
24
+ proxyUrl;
25
+ privateKeyPem;
26
+ timeoutMs;
27
+ approvalTimeoutMs;
28
+ approvalPollMs;
29
+ doFetch;
30
+ onApprovalPending;
31
+ importedKey = null;
32
+ constructor(opts) {
33
+ if (!opts.agentId)
34
+ throw new ConfigError("agentId is required");
35
+ if (!opts.ownerId)
36
+ throw new ConfigError("ownerId is required");
37
+ if (!opts.privateKey)
38
+ throw new ConfigError("privateKey is required");
39
+ this.agentId = opts.agentId;
40
+ this.ownerId = opts.ownerId;
41
+ this.privateKeyPem = opts.privateKey;
42
+ this.proxyUrl = (opts.proxyUrl ?? DEFAULT_PROXY_URL).replace(/\/$/, "");
43
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
44
+ this.approvalTimeoutMs = opts.approvalTimeoutMs ?? DEFAULT_APPROVAL_TIMEOUT_MS;
45
+ this.approvalPollMs = opts.approvalPollMs ?? DEFAULT_APPROVAL_POLL_MS;
46
+ this.doFetch = opts.fetch ?? ((url, init) => fetch(url, init));
47
+ this.onApprovalPending = opts.onApprovalPending;
48
+ }
49
+ /**
50
+ * Builds a client from the environment. Works out of the box on a machine
51
+ * that has run `npx @agentvalet/register`. See env.ts for the accepted names.
52
+ */
53
+ static fromEnv(overrides = {}) {
54
+ const agentId = overrides.agentId ?? agentIdFromEnv();
55
+ const ownerId = overrides.ownerId ?? ownerIdFromEnv();
56
+ if (!agentId || !ownerId) {
57
+ throw new ConfigError("AgentValet identity not found in the environment. Set AGENTVALET_AGENT_ID and " +
58
+ "AGENTVALET_OWNER_ID (or AGENT_ID / OWNER_ID), or run `npx @agentvalet/register`.");
59
+ }
60
+ return new AgentValet({
61
+ ...overrides,
62
+ agentId,
63
+ ownerId,
64
+ privateKey: overrides.privateKey ?? privateKeyFromEnv(),
65
+ proxyUrl: overrides.proxyUrl ?? proxyUrlFromEnv(),
66
+ });
67
+ }
68
+ // ---- core ---------------------------------------------------------------
69
+ /**
70
+ * Calls an approved platform through the broker. Returns the upstream SaaS
71
+ * response body, parsed.
72
+ *
73
+ * If the owner must approve, this blocks for up to `approvalTimeoutMs` and
74
+ * then returns the result transparently — from the caller's point of view an
75
+ * approved call just took longer. If nobody approves in time it throws
76
+ * ApprovalTimeoutError, whose `approvalId` you can resume with
77
+ * `waitForApproval()`; the action stays queued server-side either way.
78
+ */
79
+ async call(req) {
80
+ const body = {
81
+ platform: req.platform,
82
+ endpoint: req.endpoint,
83
+ method: req.method ?? "GET",
84
+ scope: req.scope,
85
+ ...(req.data !== undefined && { data: req.data }),
86
+ ...(req.connectionId ? { connection_id: req.connectionId } : {}),
87
+ ...(req.reason ? { reason: req.reason } : {}),
88
+ };
89
+ const res = await this.authedFetch("/v1/actions", { method: "POST", body: JSON.stringify(body) });
90
+ const text = await res.text();
91
+ // 202 + approval_id → the owner needs to approve. Enter the long-poll.
92
+ if (res.status === 202) {
93
+ const approvalId = tryParseJson(text)?.approval_id;
94
+ if (approvalId) {
95
+ return (await this.waitForApproval(approvalId, {
96
+ platform: req.platform,
97
+ scope: req.scope,
98
+ endpoint: req.endpoint,
99
+ timeoutMs: req.approvalTimeoutMs ?? this.approvalTimeoutMs,
100
+ }));
101
+ }
102
+ }
103
+ if (!res.ok) {
104
+ // 403 is the proxy's authorize-pipeline denial (no grant, or policy
105
+ // blocked). Surface it as its own type so callers can branch into
106
+ // requestAccess() instead of dead-ending on a generic failure.
107
+ if (res.status === 403) {
108
+ throw new AccessDeniedError(res.status, text, req.platform, req.scope);
109
+ }
110
+ throw new ProxyError(res.status, text);
111
+ }
112
+ return (tryParseJson(text) ?? text);
113
+ }
114
+ /**
115
+ * Resumes waiting on an outstanding approval. Use with the `approvalId` from
116
+ * a caught ApprovalTimeoutError — the action is queued server-side, so this
117
+ * picks up exactly where `call()` left off.
118
+ */
119
+ async waitForApproval(approvalId, ctx = {}) {
120
+ const budget = ctx.timeoutMs ?? this.approvalTimeoutMs;
121
+ const startedAt = Date.now();
122
+ while (Date.now() - startedAt < budget) {
123
+ await sleep(this.approvalPollMs);
124
+ const elapsed = Date.now() - startedAt;
125
+ let res;
126
+ try {
127
+ res = await this.authedFetch(`/v1/approvals/${encodeURIComponent(approvalId)}`, { method: "GET" });
128
+ }
129
+ catch {
130
+ continue; // transient — try again next tick
131
+ }
132
+ if (!res.ok)
133
+ throw new ProxyError(res.status, await res.text());
134
+ const status = (await res.json());
135
+ if (status.status === "denied") {
136
+ throw new ApprovalDeniedError(approvalId, status.execution_error);
137
+ }
138
+ if (status.status === "expired") {
139
+ throw new ApprovalExpiredError(approvalId);
140
+ }
141
+ if (status.execution_error) {
142
+ // Approved, but the re-execution failed (upstream down, agent revoked).
143
+ throw new ProxyError(502, `Approved but re-execution failed: ${status.execution_error}`);
144
+ }
145
+ if (status.status === "approved" && status.executed_at && status.result) {
146
+ const r = status.result;
147
+ if (r.status < 200 || r.status >= 300)
148
+ throw new UpstreamError(r.status, r.data);
149
+ return r.data;
150
+ }
151
+ this.onApprovalPending?.({
152
+ approvalId,
153
+ elapsedMs: elapsed,
154
+ platform: ctx.platform ?? "",
155
+ scope: ctx.scope ?? "",
156
+ endpoint: ctx.endpoint ?? "",
157
+ });
158
+ }
159
+ throw new ApprovalTimeoutError(approvalId, budget);
160
+ }
161
+ // ---- discovery / governance --------------------------------------------
162
+ /** Platforms and scopes this agent is actually granted. Deny-by-default: if
163
+ * it isn't here, `call()` will 403. */
164
+ async listPlatforms() {
165
+ return this.getJson("/v1/agent/permissions");
166
+ }
167
+ /** Actions queued behind an owner approval that haven't resolved yet. */
168
+ async pendingActions() {
169
+ return this.getJson("/v1/agents/me/pending-actions");
170
+ }
171
+ /**
172
+ * Dry-run an authorization decision without performing the action. Cheap way
173
+ * to check a destructive scope before committing to a side effect.
174
+ */
175
+ async evaluate(platform, scope) {
176
+ const res = await this.authedFetch("/v1/authzen/access", {
177
+ method: "POST",
178
+ body: JSON.stringify({
179
+ subject: { type: "agent", id: this.agentId },
180
+ action: { name: "tool_call" },
181
+ resource: { type: "platform_scope", id: `${platform}:${scope}` },
182
+ }),
183
+ });
184
+ const text = await res.text();
185
+ if (!res.ok)
186
+ throw new ProxyError(res.status, text);
187
+ return (tryParseJson(text) ?? text);
188
+ }
189
+ /**
190
+ * Asks an org admin to grant platform+scope, then polls for the decision.
191
+ * Resolves "approved" | "denied" | "pending" — on "approved", retry the
192
+ * original `call()`.
193
+ */
194
+ async requestAccess(req) {
195
+ const submit = await this.authedFetch("/v1/access-request", {
196
+ method: "POST",
197
+ body: JSON.stringify({ platform: req.platform, scope: req.scope, reason: req.reason }),
198
+ });
199
+ if (!submit.ok && submit.status !== 202) {
200
+ throw new ProxyError(submit.status, await submit.text());
201
+ }
202
+ const { request_token: token } = (await submit.json());
203
+ if (!token)
204
+ throw new ProxyError(submit.status, "Access request did not return a request_token.");
205
+ const deadline = Date.now() + (req.timeoutMs ?? this.approvalTimeoutMs);
206
+ while (Date.now() < deadline) {
207
+ await sleep(this.approvalPollMs);
208
+ let res;
209
+ try {
210
+ res = await this.authedFetch(`/v1/access-request/status/${encodeURIComponent(token)}`, { method: "GET" });
211
+ }
212
+ catch {
213
+ continue;
214
+ }
215
+ if (!res.ok)
216
+ continue;
217
+ const s = (await res.json());
218
+ if (s.status === "approved")
219
+ return { status: "approved", requestToken: token };
220
+ if (s.status === "denied")
221
+ return { status: "denied", requestToken: token };
222
+ }
223
+ return { status: "pending", requestToken: token };
224
+ }
225
+ // ---- transport ----------------------------------------------------------
226
+ /** Signs a short-lived (60s) RS256 assertion of this agent's identity. */
227
+ async signJWT() {
228
+ if (!this.importedKey) {
229
+ try {
230
+ this.importedKey = await importPKCS8(this.privateKeyPem, "RS256");
231
+ }
232
+ catch (err) {
233
+ throw new ConfigError(`Private key is not a valid PKCS#8 RS256 PEM: ${err instanceof Error ? err.message : err}`);
234
+ }
235
+ }
236
+ return new SignJWT({ agent_id: this.agentId, owner_id: this.ownerId })
237
+ .setProtectedHeader({ alg: "RS256" })
238
+ .setIssuedAt()
239
+ .setExpirationTime("60s")
240
+ .sign(this.importedKey);
241
+ }
242
+ async authedFetch(path, init) {
243
+ const url = `${this.proxyUrl}${path}`;
244
+ const once = async () => {
245
+ const token = await this.signJWT();
246
+ try {
247
+ return await fetchWithTimeout(this.doFetch, url, {
248
+ ...init,
249
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", ...init.headers },
250
+ }, this.timeoutMs);
251
+ }
252
+ catch (err) {
253
+ throw diagnoseNetworkError(err, this.proxyUrl);
254
+ }
255
+ };
256
+ const res = await once();
257
+ // Retry once on 401 — the JWT may have been minted either side of a clock
258
+ // skew boundary. Same one-shot retry the mcp-server does.
259
+ return res.status === 401 ? once() : res;
260
+ }
261
+ async getJson(path) {
262
+ const res = await this.authedFetch(path, { method: "GET" });
263
+ const text = await res.text();
264
+ if (!res.ok)
265
+ throw new ProxyError(res.status, text);
266
+ return (tryParseJson(text) ?? text);
267
+ }
268
+ }
269
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/env.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ export declare const DEFAULT_PROXY_URL = "https://api.agentvalet.ai";
2
+ export declare function agentIdFromEnv(): string | null;
3
+ export declare function ownerIdFromEnv(): string | null;
4
+ export declare function proxyUrlFromEnv(): string;
5
+ /**
6
+ * Resolves the RS256 private key PEM, in precedence order:
7
+ * 1. AGENT_PRIVATE_KEY_B64 — base64-encoded PEM (12-factor / container safe)
8
+ * 2. AGENT_PRIVATE_KEY_PATH — path to a PEM file
9
+ * 3. AGENT_PRIVATE_KEY — raw multi-line PEM, or \n-escaped single line
10
+ * 4. ~/.agentvalet/agent.key — written by `npx @agentvalet/register`
11
+ *
12
+ * Same order and same formats as the mcp-server's pem.ts, so a dev who already
13
+ * has the MCP server working can construct the SDK with no new setup.
14
+ */
15
+ export declare function privateKeyFromEnv(): string;
16
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,iBAAiB,8BAA8B,CAAC;AAmB7D,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAiC1C"}
package/dist/env.js ADDED
@@ -0,0 +1,90 @@
1
+ // packages/client/src/env.ts
2
+ //
3
+ // Environment resolution for AgentValet.fromEnv().
4
+ //
5
+ // Accepts BOTH naming conventions already in the wild: the bare names the
6
+ // mcp-server/MCPB write (AGENT_ID, OWNER_ID, PROXY_URL, AGENT_PRIVATE_KEY*)
7
+ // and AGENTVALET_-prefixed aliases, which is what the docs and the
8
+ // credentials-not-configured message tell people to set. A machine that has
9
+ // been through `npx @agentvalet/register` already satisfies the bare set, so
10
+ // fromEnv() works with zero extra config there.
11
+ import { readFileSync } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { ConfigError } from "./errors.js";
15
+ export const DEFAULT_PROXY_URL = "https://api.agentvalet.ai";
16
+ // Treat empty strings and unresolved MCPB placeholders ("${user_config.foo}")
17
+ // as not-set. The MCPB writes literal placeholders when a user_config field is
18
+ // left blank, and a literal "${...}" agent id fails far more confusingly later.
19
+ function readEnv(name) {
20
+ const v = process.env[name];
21
+ if (v === undefined)
22
+ return null;
23
+ const t = v.trim();
24
+ if (t === "")
25
+ return null;
26
+ if (t.startsWith("${") && t.endsWith("}"))
27
+ return null;
28
+ return t;
29
+ }
30
+ /** AGENTVALET_-prefixed name wins over the bare name when both are set. */
31
+ function readEither(suffix) {
32
+ return readEnv(`AGENTVALET_${suffix}`) ?? readEnv(suffix);
33
+ }
34
+ export function agentIdFromEnv() {
35
+ return readEither("AGENT_ID");
36
+ }
37
+ export function ownerIdFromEnv() {
38
+ return readEither("OWNER_ID");
39
+ }
40
+ export function proxyUrlFromEnv() {
41
+ return (readEither("PROXY_URL") ?? DEFAULT_PROXY_URL).replace(/\/$/, "");
42
+ }
43
+ /**
44
+ * Resolves the RS256 private key PEM, in precedence order:
45
+ * 1. AGENT_PRIVATE_KEY_B64 — base64-encoded PEM (12-factor / container safe)
46
+ * 2. AGENT_PRIVATE_KEY_PATH — path to a PEM file
47
+ * 3. AGENT_PRIVATE_KEY — raw multi-line PEM, or \n-escaped single line
48
+ * 4. ~/.agentvalet/agent.key — written by `npx @agentvalet/register`
49
+ *
50
+ * Same order and same formats as the mcp-server's pem.ts, so a dev who already
51
+ * has the MCP server working can construct the SDK with no new setup.
52
+ */
53
+ export function privateKeyFromEnv() {
54
+ const b64 = readEither("AGENT_PRIVATE_KEY_B64");
55
+ if (b64)
56
+ return Buffer.from(b64, "base64").toString("utf-8").trim();
57
+ const path = readEither("AGENT_PRIVATE_KEY_PATH");
58
+ if (path) {
59
+ try {
60
+ return readFileSync(path, "utf-8").trim();
61
+ }
62
+ catch (err) {
63
+ throw new ConfigError(`Cannot read AGENT_PRIVATE_KEY_PATH (${path}): ${err instanceof Error ? err.message : err}`);
64
+ }
65
+ }
66
+ const raw = readEither("AGENT_PRIVATE_KEY");
67
+ if (raw) {
68
+ const unescaped = raw.replace(/\\n/g, "\n");
69
+ // Tolerate a bare base64 blob with the PEM armour stripped — a common
70
+ // casualty of pasting a key through a secrets UI that trims delimiters.
71
+ if (!unescaped.startsWith("-----")) {
72
+ return `-----BEGIN PRIVATE KEY-----\n${unescaped}\n-----END PRIVATE KEY-----`;
73
+ }
74
+ return unescaped;
75
+ }
76
+ const diskKey = readDiskKey();
77
+ if (diskKey)
78
+ return diskKey;
79
+ throw new ConfigError("No private key found. Set AGENT_PRIVATE_KEY, AGENT_PRIVATE_KEY_PATH, or AGENT_PRIVATE_KEY_B64, " +
80
+ "or run `npx @agentvalet/register` to create an agent and write ~/.agentvalet/agent.key.");
81
+ }
82
+ function readDiskKey() {
83
+ try {
84
+ return readFileSync(join(homedir(), ".agentvalet", "agent.key"), "utf-8").trim() || null;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ export declare class AgentValetError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ /** Missing/!malformed identity or key — thrown before any network call. */
5
+ export declare class ConfigError extends AgentValetError {
6
+ }
7
+ /** fetch() itself failed. `.hint` carries an actionable diagnosis. */
8
+ export declare class NetworkError extends AgentValetError {
9
+ readonly cause: unknown;
10
+ constructor(message: string, cause: unknown);
11
+ }
12
+ /** Non-2xx from the AgentValet proxy that isn't a more specific case below. */
13
+ export declare class ProxyError extends AgentValetError {
14
+ readonly status: number;
15
+ readonly body: string;
16
+ constructor(status: number, body: string);
17
+ }
18
+ /**
19
+ * 403 — the agent has no grant for this platform+scope, or policy blocked it.
20
+ * Recoverable: call `requestAccess({ platform, scope, reason })`.
21
+ */
22
+ export declare class AccessDeniedError extends ProxyError {
23
+ readonly platform: string;
24
+ readonly scope: string;
25
+ constructor(status: number, body: string, platform: string, scope: string);
26
+ }
27
+ /** The owner explicitly denied the action. Terminal — do not retry. */
28
+ export declare class ApprovalDeniedError extends AgentValetError {
29
+ readonly approvalId: string;
30
+ constructor(approvalId: string, detail?: string);
31
+ }
32
+ /** The approval request aged out server-side before anyone responded. */
33
+ export declare class ApprovalExpiredError extends AgentValetError {
34
+ readonly approvalId: string;
35
+ constructor(approvalId: string);
36
+ }
37
+ /**
38
+ * We stopped waiting, but the action is still queued server-side and will run
39
+ * if the owner approves. NOT a failure — hold `approvalId` and resume later
40
+ * with `client.waitForApproval(approvalId)`.
41
+ */
42
+ export declare class ApprovalTimeoutError extends AgentValetError {
43
+ readonly approvalId: string;
44
+ constructor(approvalId: string, waitedMs: number);
45
+ }
46
+ /** Approved and executed, but the upstream SaaS returned a non-2xx. */
47
+ export declare class UpstreamError extends AgentValetError {
48
+ readonly status: number;
49
+ readonly data: unknown;
50
+ constructor(status: number, data: unknown);
51
+ }
52
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAMA,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,2EAA2E;AAC3E,qBAAa,WAAY,SAAQ,eAAe;CAAG;AAEnD,sEAAsE;AACtE,qBAAa,YAAa,SAAQ,eAAe;IAClB,QAAQ,CAAC,KAAK,EAAE,OAAO;gBAAxC,OAAO,EAAE,MAAM,EAAW,KAAK,EAAE,OAAO;CAGrD;AAED,+EAA+E;AAC/E,qBAAa,UAAW,SAAQ,eAAe;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAArC,MAAM,EAAE,MAAM,EAAW,IAAI,EAAE,MAAM;CAG3D;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IACL,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM;gBAA/E,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAW,QAAQ,EAAE,MAAM,EAAW,KAAK,EAAE,MAAM;CAM5F;AAED,uEAAuE;AACvE,qBAAa,mBAAoB,SAAQ,eAAe;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAGzD;AAED,yEAAyE;AACzE,qBAAa,oBAAqB,SAAQ,eAAe;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM;CAGxC;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,eAAe;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM;gBAAlB,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAM1D;AAED,uEAAuE;AACvE,qBAAa,aAAc,SAAQ,eAAe;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO;gBAAtC,MAAM,EAAE,MAAM,EAAW,IAAI,EAAE,OAAO;CAG5D"}
package/dist/errors.js ADDED
@@ -0,0 +1,88 @@
1
+ // packages/client/src/errors.ts
2
+ //
3
+ // Typed error hierarchy. The whole point of the SDK over raw /v1/actions is
4
+ // that a caller can branch on WHY a call failed without string-matching the
5
+ // proxy's error envelope. Every throw from the client is one of these.
6
+ export class AgentValetError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = new.target.name;
10
+ }
11
+ }
12
+ /** Missing/!malformed identity or key — thrown before any network call. */
13
+ export class ConfigError extends AgentValetError {
14
+ }
15
+ /** fetch() itself failed. `.hint` carries an actionable diagnosis. */
16
+ export class NetworkError extends AgentValetError {
17
+ cause;
18
+ constructor(message, cause) {
19
+ super(message);
20
+ this.cause = cause;
21
+ }
22
+ }
23
+ /** Non-2xx from the AgentValet proxy that isn't a more specific case below. */
24
+ export class ProxyError extends AgentValetError {
25
+ status;
26
+ body;
27
+ constructor(status, body) {
28
+ super(`Proxy error ${status}: ${body}`);
29
+ this.status = status;
30
+ this.body = body;
31
+ }
32
+ }
33
+ /**
34
+ * 403 — the agent has no grant for this platform+scope, or policy blocked it.
35
+ * Recoverable: call `requestAccess({ platform, scope, reason })`.
36
+ */
37
+ export class AccessDeniedError extends ProxyError {
38
+ platform;
39
+ scope;
40
+ constructor(status, body, platform, scope) {
41
+ super(status, body);
42
+ this.platform = platform;
43
+ this.scope = scope;
44
+ this.message =
45
+ `Access denied for ${platform}:${scope} — ${body}. ` +
46
+ `Call requestAccess({ platform, scope, reason }) to ask an org admin to grant it.`;
47
+ }
48
+ }
49
+ /** The owner explicitly denied the action. Terminal — do not retry. */
50
+ export class ApprovalDeniedError extends AgentValetError {
51
+ approvalId;
52
+ constructor(approvalId, detail) {
53
+ super(`Owner denied this action${detail ? `: ${detail}` : "."}`);
54
+ this.approvalId = approvalId;
55
+ }
56
+ }
57
+ /** The approval request aged out server-side before anyone responded. */
58
+ export class ApprovalExpiredError extends AgentValetError {
59
+ approvalId;
60
+ constructor(approvalId) {
61
+ super("Approval request expired before the owner responded.");
62
+ this.approvalId = approvalId;
63
+ }
64
+ }
65
+ /**
66
+ * We stopped waiting, but the action is still queued server-side and will run
67
+ * if the owner approves. NOT a failure — hold `approvalId` and resume later
68
+ * with `client.waitForApproval(approvalId)`.
69
+ */
70
+ export class ApprovalTimeoutError extends AgentValetError {
71
+ approvalId;
72
+ constructor(approvalId, waitedMs) {
73
+ super(`Owner hasn't approved within ${Math.round(waitedMs / 1000)}s. The action is still queued — ` +
74
+ `keep approval_id ${approvalId} and call waitForApproval(approvalId) to resume.`);
75
+ this.approvalId = approvalId;
76
+ }
77
+ }
78
+ /** Approved and executed, but the upstream SaaS returned a non-2xx. */
79
+ export class UpstreamError extends AgentValetError {
80
+ status;
81
+ data;
82
+ constructor(status, data) {
83
+ super(`Upstream returned ${status}: ${JSON.stringify(data)}`);
84
+ this.status = status;
85
+ this.data = data;
86
+ }
87
+ }
88
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,uEAAuE;AAEvE,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED,2EAA2E;AAC3E,MAAM,OAAO,WAAY,SAAQ,eAAe;CAAG;AAEnD,sEAAsE;AACtE,MAAM,OAAO,YAAa,SAAQ,eAAe;IACT;IAAtC,YAAY,OAAe,EAAW,KAAc;QAClD,KAAK,CAAC,OAAO,CAAC,CAAC;QADqB,UAAK,GAAL,KAAK,CAAS;IAEpD,CAAC;CACF;AAED,+EAA+E;AAC/E,MAAM,OAAO,UAAW,SAAQ,eAAe;IACxB;IAAyB;IAA9C,YAAqB,MAAc,EAAW,IAAY;QACxD,KAAK,CAAC,eAAe,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QADrB,WAAM,GAAN,MAAM,CAAQ;QAAW,SAAI,GAAJ,IAAI,CAAQ;IAE1D,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,UAAU;IACI;IAA2B;IAA9E,YAAY,MAAc,EAAE,IAAY,EAAW,QAAgB,EAAW,KAAa;QACzF,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAD6B,aAAQ,GAAR,QAAQ,CAAQ;QAAW,UAAK,GAAL,KAAK,CAAQ;QAEzF,IAAI,CAAC,OAAO;YACV,qBAAqB,QAAQ,IAAI,KAAK,MAAM,IAAI,IAAI;gBACpD,kFAAkF,CAAC;IACvF,CAAC;CACF;AAED,uEAAuE;AACvE,MAAM,OAAO,mBAAoB,SAAQ,eAAe;IACjC;IAArB,YAAqB,UAAkB,EAAE,MAAe;QACtD,KAAK,CAAC,2BAA2B,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAD9C,eAAU,GAAV,UAAU,CAAQ;IAEvC,CAAC;CACF;AAED,yEAAyE;AACzE,MAAM,OAAO,oBAAqB,SAAQ,eAAe;IAClC;IAArB,YAAqB,UAAkB;QACrC,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAD3C,eAAU,GAAV,UAAU,CAAQ;IAEvC,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,oBAAqB,SAAQ,eAAe;IAClC;IAArB,YAAqB,UAAkB,EAAE,QAAgB;QACvD,KAAK,CACH,gCAAgC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,kCAAkC;YAC3F,oBAAoB,UAAU,kDAAkD,CACnF,CAAC;QAJiB,eAAU,GAAV,UAAU,CAAQ;IAKvC,CAAC;CACF;AAED,uEAAuE;AACvE,MAAM,OAAO,aAAc,SAAQ,eAAe;IAC3B;IAAyB;IAA9C,YAAqB,MAAc,EAAW,IAAa;QACzD,KAAK,CAAC,qBAAqB,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAD3C,WAAM,GAAN,MAAM,CAAQ;QAAW,SAAI,GAAJ,IAAI,CAAS;IAE3D,CAAC;CACF"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { NetworkError } from "./errors.js";
2
+ export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
3
+ export declare function fetchWithTimeout(doFetch: FetchLike, url: string, init: RequestInit, timeoutMs: number): Promise<Response>;
4
+ export declare function sleep(ms: number): Promise<void>;
5
+ export declare function tryParseJson(text: string): unknown;
6
+ export declare function diagnoseNetworkError(err: unknown, proxyUrl: string): NetworkError;
7
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjF,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,SAAS,EAClB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,CAAC,CAInB;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQlD;AAKD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,CAiBjF"}
package/dist/http.js ADDED
@@ -0,0 +1,50 @@
1
+ // packages/client/src/http.ts
2
+ //
3
+ // Stateless transport helpers. Kept behaviourally identical to the mcp-server's
4
+ // net.ts so the SDK and the MCP server fail the same way on the same network
5
+ // conditions — an operator debugging one is debugging both.
6
+ import { NetworkError } from "./errors.js";
7
+ export function fetchWithTimeout(doFetch, url, init, timeoutMs) {
8
+ const ac = new AbortController();
9
+ const timer = setTimeout(() => ac.abort(), timeoutMs);
10
+ return doFetch(url, { ...init, signal: ac.signal }).finally(() => clearTimeout(timer));
11
+ }
12
+ export function sleep(ms) {
13
+ return new Promise((resolve) => setTimeout(resolve, ms));
14
+ }
15
+ export function tryParseJson(text) {
16
+ const trimmed = text.trim();
17
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("["))
18
+ return undefined;
19
+ try {
20
+ return JSON.parse(trimmed);
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ }
26
+ // Translates a fetch() failure into something a developer can act on. The
27
+ // default "fetch failed" tells you nothing about whether it's DNS, a corporate
28
+ // MITM proxy, or the proxy being down.
29
+ export function diagnoseNetworkError(err, proxyUrl) {
30
+ const raw = err instanceof Error ? err.message : String(err);
31
+ const lower = raw.toLowerCase();
32
+ let message;
33
+ if (lower.includes("enotfound") || lower.includes("getaddrinfo")) {
34
+ message = `Cannot resolve ${proxyUrl}. Check DNS / corporate proxy / VPN, or confirm proxyUrl is correct. Raw: ${raw}`;
35
+ }
36
+ else if (lower.includes("econnrefused") || lower.includes("econnreset")) {
37
+ message = `Connection to ${proxyUrl} was refused or reset. The proxy may be down — check https://status.agentvalet.ai — or a firewall is blocking it. Raw: ${raw}`;
38
+ }
39
+ else if (lower.includes("etimedout") || lower.includes("timeout") || lower.includes("aborterror")) {
40
+ message = `Request to ${proxyUrl} timed out. Likely VPN routing, corporate proxy buffering, or a slow network. Raw: ${raw}`;
41
+ }
42
+ else if (lower.includes("self signed") || lower.includes("cert") || lower.includes("ssl") || lower.includes("tls")) {
43
+ message = `TLS / certificate problem talking to ${proxyUrl}. A corporate MITM proxy may be intercepting traffic. Raw: ${raw}`;
44
+ }
45
+ else {
46
+ message = `Network error reaching ${proxyUrl}: ${raw}. Check VPN, corporate proxy, and firewall rules for api.agentvalet.ai.`;
47
+ }
48
+ return new NetworkError(message, err);
49
+ }
50
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,EAAE;AACF,gFAAgF;AAChF,6EAA6E;AAC7E,4DAA4D;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAI3C,MAAM,UAAU,gBAAgB,CAC9B,OAAkB,EAClB,GAAW,EACX,IAAiB,EACjB,SAAiB;IAEjB,MAAM,EAAE,GAAG,IAAI,eAAe,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;IACtD,OAAO,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3E,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,+EAA+E;AAC/E,uCAAuC;AACvC,MAAM,UAAU,oBAAoB,CAAC,GAAY,EAAE,QAAgB;IACjE,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAEhC,IAAI,OAAe,CAAC;IACpB,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACjE,OAAO,GAAG,kBAAkB,QAAQ,6EAA6E,GAAG,EAAE,CAAC;IACzH,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1E,OAAO,GAAG,iBAAiB,QAAQ,0HAA0H,GAAG,EAAE,CAAC;IACrK,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACpG,OAAO,GAAG,cAAc,QAAQ,sFAAsF,GAAG,EAAE,CAAC;IAC9H,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrH,OAAO,GAAG,wCAAwC,QAAQ,8DAA8D,GAAG,EAAE,CAAC;IAChI,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,0BAA0B,QAAQ,KAAK,GAAG,yEAAyE,CAAC;IAChI,CAAC;IACD,OAAO,IAAI,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { AgentValet } from "./client.js";
2
+ export type { AgentValetOptions, CallRequest } from "./client.js";
3
+ export { AgentValetError, AccessDeniedError, ApprovalDeniedError, ApprovalExpiredError, ApprovalTimeoutError, ConfigError, NetworkError, ProxyError, UpstreamError, } from "./errors.js";
4
+ export { DEFAULT_PROXY_URL } from "./env.js";
5
+ export type { FetchLike } from "./http.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ // packages/client/src/index.ts
2
+ //
3
+ // @agentvalet/client — the non-MCP path onto the AgentValet broker.
4
+ //
5
+ // If you're inside an MCP host (Claude Code, Claude Desktop, Cursor), install
6
+ // @agentvalet/mcp-server instead and call use_platform. This package is for
7
+ // agents that aren't MCP: LangChain tools, cron jobs, plain Node services.
8
+ export { AgentValet } from "./client.js";
9
+ export { AgentValetError, AccessDeniedError, ApprovalDeniedError, ApprovalExpiredError, ApprovalTimeoutError, ConfigError, NetworkError, ProxyError, UpstreamError, } from "./errors.js";
10
+ export { DEFAULT_PROXY_URL } from "./env.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,oEAAoE;AACpE,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAE3E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@agentvalet/client",
3
+ "version": "0.1.0",
4
+ "description": "AgentValet client SDK — call approved SaaS platforms from any Node agent. Signs the agent JWT, brokers through the AgentValet proxy, and waits out owner approvals.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": "./dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md"
14
+ ],
15
+ "dependencies": {
16
+ "jose": "^5.10.0"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^20.0.0",
20
+ "typescript": "^5.4.0",
21
+ "vitest": "^1.5.0"
22
+ },
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/MCSEdwin/agentvalet.git",
29
+ "directory": "packages/client"
30
+ },
31
+ "keywords": [
32
+ "ai",
33
+ "agents",
34
+ "agentvalet",
35
+ "credentials",
36
+ "authorization",
37
+ "sdk"
38
+ ],
39
+ "license": "MIT",
40
+ "scripts": {
41
+ "build": "tsc",
42
+ "dev": "tsc --watch",
43
+ "typecheck": "tsc --noEmit",
44
+ "test": "vitest run"
45
+ }
46
+ }