@forgeintel/sdk 0.5.0-beta.4 → 0.5.0-beta.6
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 +8 -10
- package/dist/core.d.ts +6 -5
- package/dist/core.js +12 -7
- package/dist/openapi.d.ts +2 -2
- package/dist/openapi.js +10 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @forgeintel/sdk
|
|
2
2
|
|
|
3
|
-
> **Beta.** Install with `npm install @forgeintel/sdk
|
|
3
|
+
> **Beta.** Install with `npm install @forgeintel/sdk`. The API may change before 1.0.
|
|
4
4
|
|
|
5
5
|
The Forge SDK for x402 paid APIs. Paid responses carry a `feedback_id`, the 402 challenge asks agents to rate the call, a rating is one free GET, and every call is reported to your Forge dashboard in the background. Never on your critical path: no network calls while your API serves a request, and it fails open. Aware of x402 v1 and v2, and of OpenAPI 2.0 through 3.2. Every change is additive, and anything it doesn't understand passes through untouched.
|
|
6
6
|
|
|
@@ -10,9 +10,9 @@ The Forge SDK for x402 paid APIs. Paid responses carry a `feedback_id`, the 402
|
|
|
10
10
|
import { createForge } from "@forgeintel/sdk";
|
|
11
11
|
|
|
12
12
|
const forge = createForge({
|
|
13
|
-
apiKey: process.env.FORGE_API_KEY,
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
apiKey: process.env.FORGE_API_KEY,
|
|
14
|
+
agentContext: false,
|
|
15
|
+
feedback: true,
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
app.use(forge.middleware()); // 1. first: before payments and your /openapi.json route
|
|
@@ -26,7 +26,7 @@ Works with `@x402/express` (v2) and `x402-express` (v1), on Express 4.21+ or 5.
|
|
|
26
26
|
```ts
|
|
27
27
|
import { createForge } from "@forgeintel/sdk/hono";
|
|
28
28
|
|
|
29
|
-
const forge = createForge({ apiKey,
|
|
29
|
+
const forge = createForge({ apiKey, agentContext: false, feedback: true });
|
|
30
30
|
app.use(forge.middleware()); // before paymentMiddleware from @x402/hono
|
|
31
31
|
app.use(paymentMiddleware(routes, resourceServer));
|
|
32
32
|
```
|
|
@@ -36,7 +36,7 @@ app.use(paymentMiddleware(routes, resourceServer));
|
|
|
36
36
|
```ts
|
|
37
37
|
import { createForge } from "@forgeintel/sdk/next";
|
|
38
38
|
|
|
39
|
-
export const forge = createForge({ apiKey,
|
|
39
|
+
export const forge = createForge({ apiKey, agentContext: false, feedback: true });
|
|
40
40
|
// app/api/…/route.ts: Forge outermost, around @x402/next's withX402
|
|
41
41
|
export const POST = forge.withForge(withX402(handler, route, resourceServer));
|
|
42
42
|
// app/feedback/[[...path]]/route.ts: Forge's own routes
|
|
@@ -84,8 +84,8 @@ OpenAPI: Swagger 2.0 and OpenAPI 3.0, 3.1 and 3.2. Additive only; shared compone
|
|
|
84
84
|
| Option | Default | |
|
|
85
85
|
| --- | --- | --- |
|
|
86
86
|
| `apiKey` | required | Merchant key. Also the HMAC key for IDs. |
|
|
87
|
-
| `backendUrl` |
|
|
88
|
-
| `publicUrl` |
|
|
87
|
+
| `backendUrl` | Forge production collector | Optional override for a different Forge environment. |
|
|
88
|
+
| `publicUrl` | Same-origin paths | Optional public origin for absolute rating URLs. Never infer it from request headers. |
|
|
89
89
|
| `feedback` | `true` | Master switch for rating prompts, IDs, response additions and feedback routes. `false` leaves telemetry, client headers and independent agent context on. |
|
|
90
90
|
| `basePath` | `/feedback` | Feedback routes (`/feedback`, `/feedback/rate`, `/feedback/summary`). |
|
|
91
91
|
| `tone` | `"soft"` | `"lifecycle"` describes this service's flow as four steps (402, pay, response, rate). Both stay soft asks. |
|
|
@@ -114,8 +114,6 @@ Client headers are captured automatically at discovery, challenge, payment, and
|
|
|
114
114
|
```ts
|
|
115
115
|
const forge = createForge({
|
|
116
116
|
apiKey: process.env.FORGE_API_KEY!,
|
|
117
|
-
backendUrl: "https://your-forge-backend.example/api/sdk/v2",
|
|
118
|
-
publicUrl: "https://api.example.com",
|
|
119
117
|
agentContext: true,
|
|
120
118
|
});
|
|
121
119
|
```
|
package/dist/core.d.ts
CHANGED
|
@@ -16,12 +16,12 @@ export interface ForgeOptions {
|
|
|
16
16
|
/** Merchant API key issued by the Forge backend. Also the HMAC key for feedback IDs. */
|
|
17
17
|
apiKey: string;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
* /v1
|
|
19
|
+
* Optional collector override. Defaults to Forge's production /api/sdk/v2 collector.
|
|
20
|
+
* An origin-only override uses its /v1 routes; a URL with a path uses {path}/events and so on.
|
|
21
21
|
*/
|
|
22
|
-
backendUrl
|
|
23
|
-
/**
|
|
24
|
-
publicUrl
|
|
22
|
+
backendUrl?: string;
|
|
23
|
+
/** Optional public origin for absolute feedback links. By default links use same-origin paths. Never derived from the Host header. */
|
|
24
|
+
publicUrl?: string;
|
|
25
25
|
/** Enable rating prompts, feedback IDs and feedback routes. Default true. Telemetry and agent context are independent. */
|
|
26
26
|
feedback?: boolean;
|
|
27
27
|
/** Path for the feedback routes. Default "/feedback" (quick rating at "/feedback/rate"). */
|
|
@@ -178,6 +178,7 @@ export declare const FEEDBACK_HEADERS: {
|
|
|
178
178
|
export declare const BODY_LIMIT: number;
|
|
179
179
|
/** Max OpenAPI document an adapter should buffer for enrichment. */
|
|
180
180
|
export declare const SPEC_LIMIT: number;
|
|
181
|
+
export declare const DEFAULT_BACKEND_URL = "https://app-api.forgeintel.co/api/sdk/v2";
|
|
181
182
|
/**
|
|
182
183
|
* Check options without throwing. Invalid required options are errors (Forge runs disabled);
|
|
183
184
|
* invalid optional values are warnings and fall back to their defaults.
|
package/dist/core.js
CHANGED
|
@@ -14,6 +14,7 @@ export const FEEDBACK_HEADERS = { "Cache-Control": "no-store", "X-Robots-Tag": "
|
|
|
14
14
|
export const BODY_LIMIT = 8 * 1024;
|
|
15
15
|
/** Max OpenAPI document an adapter should buffer for enrichment. */
|
|
16
16
|
export const SPEC_LIMIT = 10 * 1024 * 1024;
|
|
17
|
+
export const DEFAULT_BACKEND_URL = "https://app-api.forgeintel.co/api/sdk/v2";
|
|
17
18
|
/**
|
|
18
19
|
* Check options without throwing. Invalid required options are errors (Forge runs disabled);
|
|
19
20
|
* invalid optional values are warnings and fall back to their defaults.
|
|
@@ -36,10 +37,12 @@ export function checkOptions(input) {
|
|
|
36
37
|
};
|
|
37
38
|
if (typeof raw.apiKey !== "string" || !raw.apiKey.trim())
|
|
38
39
|
errors.push("apiKey is missing (set it to your merchant key, ffk_…)");
|
|
39
|
-
if (
|
|
40
|
+
if (raw.backendUrl === undefined)
|
|
41
|
+
options.backendUrl = DEFAULT_BACKEND_URL;
|
|
42
|
+
else if (!absoluteUrl(raw.backendUrl))
|
|
40
43
|
errors.push(`backendUrl must be an absolute http(s) URL (got ${JSON.stringify(raw.backendUrl ?? null)})`);
|
|
41
|
-
if (!absoluteUrl(raw.publicUrl))
|
|
42
|
-
errors.push(`publicUrl must be an absolute http(s) URL
|
|
44
|
+
if (raw.publicUrl !== undefined && !absoluteUrl(raw.publicUrl))
|
|
45
|
+
errors.push(`publicUrl must be an absolute http(s) URL (got ${JSON.stringify(raw.publicUrl ?? null)})`);
|
|
43
46
|
const fallback = (key, ok, expected) => {
|
|
44
47
|
if (raw[key] === undefined || ok)
|
|
45
48
|
return;
|
|
@@ -156,17 +159,19 @@ export function createForgeCore(input) {
|
|
|
156
159
|
}
|
|
157
160
|
}
|
|
158
161
|
function enabledCore(options, configWarnings) {
|
|
159
|
-
const { apiKey,
|
|
162
|
+
const { apiKey, publicUrl } = options;
|
|
163
|
+
const backendUrl = options.backendUrl ?? DEFAULT_BACKEND_URL;
|
|
160
164
|
// Feedback IDs are signed with a key derived from the API key, never with the API key itself.
|
|
161
165
|
const signingKey = deriveSigningKey(apiKey);
|
|
162
166
|
const backendPath = new URL(backendUrl).pathname.replace(/\/+$/, "") || "/v1";
|
|
163
167
|
const backend = (name) => new URL(`${backendPath}/${name}`, backendUrl).href;
|
|
164
168
|
const basePath = (options.basePath ?? "/feedback").replace(/\/+$/, "");
|
|
165
169
|
const ratePath = `${basePath}/rate`;
|
|
166
|
-
const
|
|
167
|
-
const
|
|
170
|
+
const sameOriginUrl = (path) => publicUrl ? new URL(path, publicUrl).href : path;
|
|
171
|
+
const rateUrl = sameOriginUrl(ratePath);
|
|
172
|
+
const formUrl = sameOriginUrl(basePath);
|
|
168
173
|
const summaryPath = `${basePath}/summary`;
|
|
169
|
-
const summaryUrl =
|
|
174
|
+
const summaryUrl = sameOriginUrl(summaryPath);
|
|
170
175
|
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
171
176
|
const fetchImpl = options.fetch ?? fetch;
|
|
172
177
|
const feedback = options.feedback !== false;
|
package/dist/openapi.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ export type ResponseSupport = "extended" | "incompatible" | "undocumented";
|
|
|
4
4
|
export interface EnrichOptions {
|
|
5
5
|
/** Disable all feedback additions while retaining enabled agent context. */
|
|
6
6
|
feedback?: boolean;
|
|
7
|
-
/**
|
|
8
|
-
publicUrl
|
|
7
|
+
/** Optional merchant public origin. Without it, feedback links use same-origin paths. */
|
|
8
|
+
publicUrl?: string;
|
|
9
9
|
/** Public path of the feedback routes, e.g. /feedback */
|
|
10
10
|
basePath: string;
|
|
11
11
|
/** Sentence appended to x-guidance and paid operation descriptions. */
|
package/dist/openapi.js
CHANGED
|
@@ -46,7 +46,7 @@ function feedbackProperties(rateUrl, hintField = false) {
|
|
|
46
46
|
},
|
|
47
47
|
feedback_url: {
|
|
48
48
|
type: "string",
|
|
49
|
-
format: "uri",
|
|
49
|
+
format: "uri-reference",
|
|
50
50
|
description: `Quick-rating URL with outcome left blank; outcome values: ${Object.keys(OUTCOMES).join(", ")}. Base: ${rateUrl}`,
|
|
51
51
|
},
|
|
52
52
|
};
|
|
@@ -170,20 +170,21 @@ function appendSentence(text, sentence, marker) {
|
|
|
170
170
|
}
|
|
171
171
|
/** Work out the path prefix document paths are relative to, and whether it's this service's origin. */
|
|
172
172
|
function serverBase(doc, version, publicUrl) {
|
|
173
|
-
const origin = new URL(publicUrl).origin;
|
|
173
|
+
const origin = publicUrl ? new URL(publicUrl).origin : undefined;
|
|
174
|
+
const baseUrl = publicUrl ?? "https://forge.invalid";
|
|
174
175
|
try {
|
|
175
176
|
if (version === "2.0") {
|
|
176
|
-
const scheme = Array.isArray(doc.schemes) && doc.schemes[0] ? doc.schemes[0] : new URL(
|
|
177
|
+
const scheme = Array.isArray(doc.schemes) && doc.schemes[0] ? doc.schemes[0] : new URL(baseUrl).protocol.slice(0, -1);
|
|
177
178
|
const base = typeof doc.basePath === "string" ? doc.basePath : "";
|
|
178
|
-
const url = typeof doc.host === "string" ? new URL(`${scheme}://${doc.host}${base}`) : new URL(base || "/",
|
|
179
|
-
return { prefix: url.pathname.replace(/\/+$/, ""), sameOrigin: url.origin === origin };
|
|
179
|
+
const url = typeof doc.host === "string" ? new URL(`${scheme}://${doc.host}${base}`) : new URL(base || "/", baseUrl);
|
|
180
|
+
return { prefix: url.pathname.replace(/\/+$/, ""), sameOrigin: !origin || url.origin === origin };
|
|
180
181
|
}
|
|
181
182
|
const server = Array.isArray(doc.servers) ? doc.servers[0] : undefined;
|
|
182
183
|
if (!isObj(server) || typeof server.url !== "string")
|
|
183
184
|
return { prefix: "", sameOrigin: true };
|
|
184
185
|
const raw = server.url.replace(/\{([^}]+)\}/g, (_, name) => server.variables?.[name]?.default ?? "");
|
|
185
|
-
const url = new URL(raw,
|
|
186
|
-
return { prefix: url.pathname.replace(/\/+$/, ""), sameOrigin: url.origin === origin };
|
|
186
|
+
const url = new URL(raw, baseUrl);
|
|
187
|
+
return { prefix: url.pathname.replace(/\/+$/, ""), sameOrigin: !origin || url.origin === origin };
|
|
187
188
|
}
|
|
188
189
|
catch {
|
|
189
190
|
return { prefix: "", sameOrigin: false };
|
|
@@ -262,7 +263,7 @@ export function enrichOpenApi(input, options) {
|
|
|
262
263
|
}
|
|
263
264
|
try {
|
|
264
265
|
const doc = clone(original);
|
|
265
|
-
const origin = new URL(options.publicUrl).origin;
|
|
266
|
+
const origin = options.publicUrl ? new URL(options.publicUrl).origin : "";
|
|
266
267
|
const base = options.basePath.replace(/\/+$/, "");
|
|
267
268
|
const rateUrl = `${origin}${base}/rate`;
|
|
268
269
|
const formUrl = `${origin}${base}`;
|
|
@@ -377,7 +378,7 @@ export function enrichOpenApi(input, options) {
|
|
|
377
378
|
}
|
|
378
379
|
else {
|
|
379
380
|
const ops = feedbackOperations(version, formUrl, rateUrl, operationIds);
|
|
380
|
-
const servers = inPrefix ? {} : { servers: [{ url: origin }] };
|
|
381
|
+
const servers = inPrefix ? {} : { servers: [{ url: origin || "/" }] };
|
|
381
382
|
paths[docBase] = { ...servers, get: ops.form, post: ops.submit };
|
|
382
383
|
paths[`${docBase}/rate`] = { ...servers, get: ops.rate };
|
|
383
384
|
paths[`${docBase}/summary`] = { ...servers, get: ops.summary };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeintel/sdk",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.6",
|
|
4
4
|
"description": "The Forge SDK for x402 paid APIs: agent feedback, required agent context, OpenAPI and challenge enrichment, and passive call signals. Express, Hono, Next.js and fetch handlers. No Forge network request on the merchant response path.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
},
|
|
74
74
|
"publishConfig": {
|
|
75
75
|
"access": "public",
|
|
76
|
-
"tag": "
|
|
76
|
+
"tag": "latest"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"express": ">=4.21 <6",
|