@sapiom/tools 0.15.0 → 0.17.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/CHANGELOG.md +32 -0
- package/README.md +23 -1
- package/dist/cjs/_client/analytics.d.ts +66 -0
- package/dist/cjs/_client/analytics.d.ts.map +1 -0
- package/dist/cjs/_client/analytics.js +283 -0
- package/dist/cjs/_client/analytics.js.map +1 -0
- package/dist/cjs/_client/index.d.ts +28 -20
- package/dist/cjs/_client/index.d.ts.map +1 -1
- package/dist/cjs/_client/index.js +76 -13
- package/dist/cjs/_client/index.js.map +1 -1
- package/dist/cjs/_generated/version.d.ts +7 -0
- package/dist/cjs/_generated/version.d.ts.map +1 -0
- package/dist/cjs/_generated/version.js +10 -0
- package/dist/cjs/_generated/version.js.map +1 -0
- package/dist/{esm/orchestrations → cjs/agents}/index.d.ts +15 -15
- package/dist/cjs/agents/index.d.ts.map +1 -0
- package/dist/cjs/{orchestrations → agents}/index.js +21 -24
- package/dist/cjs/agents/index.js.map +1 -0
- package/dist/cjs/client.d.ts +24 -11
- package/dist/cjs/client.d.ts.map +1 -1
- package/dist/cjs/client.js +5 -4
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/index.d.ts +11 -11
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +21 -21
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/{agent → models}/index.d.ts +26 -26
- package/dist/cjs/models/index.d.ts.map +1 -0
- package/dist/cjs/{agent → models}/index.js +24 -23
- package/dist/cjs/models/index.js.map +1 -0
- package/dist/cjs/stub/index.d.ts.map +1 -1
- package/dist/cjs/stub/index.js +26 -23
- package/dist/cjs/stub/index.js.map +1 -1
- package/dist/esm/_client/analytics.d.ts +66 -0
- package/dist/esm/_client/analytics.d.ts.map +1 -0
- package/dist/esm/_client/analytics.js +242 -0
- package/dist/esm/_client/analytics.js.map +1 -0
- package/dist/esm/_client/index.d.ts +28 -20
- package/dist/esm/_client/index.d.ts.map +1 -1
- package/dist/esm/_client/index.js +72 -9
- package/dist/esm/_client/index.js.map +1 -1
- package/dist/esm/_generated/version.d.ts +7 -0
- package/dist/esm/_generated/version.d.ts.map +1 -0
- package/dist/esm/_generated/version.js +7 -0
- package/dist/esm/_generated/version.js.map +1 -0
- package/dist/{cjs/orchestrations → esm/agents}/index.d.ts +15 -15
- package/dist/esm/agents/index.d.ts.map +1 -0
- package/dist/esm/{orchestrations → agents}/index.js +19 -22
- package/dist/esm/agents/index.js.map +1 -0
- package/dist/esm/client.d.ts +24 -11
- package/dist/esm/client.d.ts.map +1 -1
- package/dist/esm/client.js +9 -8
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +11 -11
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +14 -14
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/{agent → models}/index.d.ts +26 -26
- package/dist/esm/models/index.d.ts.map +1 -0
- package/dist/esm/{agent → models}/index.js +22 -21
- package/dist/esm/models/index.js.map +1 -0
- package/dist/esm/stub/index.d.ts.map +1 -1
- package/dist/esm/stub/index.js +26 -23
- package/dist/esm/stub/index.js.map +1 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +15 -11
- package/src/{orchestrations → agents}/README.md +13 -13
- package/dist/cjs/agent/index.d.ts.map +0 -1
- package/dist/cjs/agent/index.js.map +0 -1
- package/dist/cjs/orchestrations/index.d.ts.map +0 -1
- package/dist/cjs/orchestrations/index.js.map +0 -1
- package/dist/esm/agent/index.d.ts.map +0 -1
- package/dist/esm/agent/index.js.map +0 -1
- package/dist/esm/orchestrations/index.d.ts.map +0 -1
- package/dist/esm/orchestrations/index.js.map +0 -1
- /package/src/{agent → models}/README.md +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# @sapiom/tools
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- aee376a: Emit `capability.call` usage analytics from the capability transport via `@sapiom/analytics-core`.
|
|
8
|
+
|
|
9
|
+
Every capability HTTP call now enqueues one `capability.call` event at the transport choke point, carrying the capability path/name (the routed capability id, e.g. `web.scrape`, or the request path), the request URL path (query strings and fragments are stripped, never recorded), HTTP status, duration, request size, and the transport's attribution fields (agent, trace, metadata). Request and response bodies are never captured. The emitted `sdk_version` comes from a build-time constant generated from package.json, so it survives bundling.
|
|
10
|
+
|
|
11
|
+
Analytics ships dark: unless a collector endpoint is configured the emitter is a silent no-op — zero network calls, zero disk writes. Events are enqueued synchronously and delivered in background batches, so nothing is ever awaited, thrown, or slowed on the call path; capability behavior is byte-identical with telemetry on, off, or the collector unreachable. Opt out any time with `SAPIOM_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1`.
|
|
12
|
+
|
|
13
|
+
Adds `Sapiom.shutdown(): Promise<void>` (additive): flushes buffered events and detaches the emitter's process exit hook. Call it once per client in hosts that construct many clients per process (e.g. an engine worker creating a per-execution client) so exit hooks don't accumulate; it's idempotent, never rejects, resolves immediately when there's nothing to release, and covers clients derived via `withAttribution` (the stub client implements it as an immediate resolve).
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [3f25008]
|
|
18
|
+
- Updated dependencies [55462b3]
|
|
19
|
+
- @sapiom/analytics-core@0.2.0
|
|
20
|
+
|
|
21
|
+
## 0.16.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- cc1261e: Rename the composition SDK to **agents** and the coding/LLM capability to **models**.
|
|
26
|
+
|
|
27
|
+
**Breaking — the package names changed. Install the new names; the old ones are deprecated.**
|
|
28
|
+
|
|
29
|
+
- Packages: `@sapiom/orchestration` → `@sapiom/agent`, `@sapiom/orchestration-core` → `@sapiom/agent-core`, `@sapiom/orchestration-runtime` → `@sapiom/agent-runtime`. (`@sapiom/create-orchestration` is retired — scaffold with the CLI or the developer MCP.)
|
|
30
|
+
- API: `defineOrchestration` → `defineAgent`; `Orchestration*` types/errors → `Agent*`.
|
|
31
|
+
- `@sapiom/tools`: the `agent` capability namespace is now `models` (e.g. `sapiom.models.coding`); the `orchestrations` namespace is now `agents`.
|
|
32
|
+
- CLI: `sapiom orchestrations …` → `sapiom agents …`.
|
|
33
|
+
- Developer MCP tools: `sapiom_dev_orchestrations_*` → `sapiom_dev_agents_*`.
|
|
34
|
+
|
|
3
35
|
## 0.15.0
|
|
4
36
|
|
|
5
37
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ const sapiom = createClient({ apiKey: process.env.SAPIOM_API_KEY });
|
|
|
19
19
|
|
|
20
20
|
// Create a repo, have a coding agent build into it, then publish.
|
|
21
21
|
const repo = await sapiom.repositories.create("landing-page");
|
|
22
|
-
const run = await sapiom.
|
|
22
|
+
const run = await sapiom.models.coding.run({
|
|
23
23
|
task: "Build a one-page marketing site in index.html.",
|
|
24
24
|
gitRepository: repo, // cloned into the run's sandbox at /workspace/landing-page
|
|
25
25
|
});
|
|
@@ -105,6 +105,28 @@ const { downloadUrl } = await fileStorage.getDownloadUrl(
|
|
|
105
105
|
);
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
+
## Usage analytics
|
|
109
|
+
|
|
110
|
+
The SDK can emit anonymous usage analytics — one `capability.call` event per
|
|
111
|
+
capability request, carrying the capability name, the request URL path (query
|
|
112
|
+
strings are stripped, never recorded), HTTP status, duration, and request size
|
|
113
|
+
(never request or response bodies). Events describe calls to Sapiom's own API
|
|
114
|
+
only; nothing is captured about any third-party traffic.
|
|
115
|
+
|
|
116
|
+
By default nothing is sent anywhere: the emitter (see
|
|
117
|
+
[`@sapiom/analytics-core`](https://github.com/sapiom/sapiom-js/tree/main/packages/analytics-core))
|
|
118
|
+
is a no-op unless a collector endpoint is configured. Delivery is batched in the background and can
|
|
119
|
+
never throw, block, or slow a capability call. Opt out any time with
|
|
120
|
+
`SAPIOM_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1`.
|
|
121
|
+
|
|
122
|
+
If your process constructs many clients over its lifetime (one per execution,
|
|
123
|
+
for example), call `await client.shutdown()` when you're done with each one: it
|
|
124
|
+
flushes any buffered events and detaches the emitter's process exit hook.
|
|
125
|
+
It's idempotent, never rejects, resolves immediately when there's nothing to
|
|
126
|
+
release, and covers every client derived via `withAttribution`. One-shot
|
|
127
|
+
scripts don't need it — events flush on process exit. Capability calls made
|
|
128
|
+
after shutdown still work; they just no longer emit analytics.
|
|
129
|
+
|
|
108
130
|
## License
|
|
109
131
|
|
|
110
132
|
MIT
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type SapiomAnalytics } from "@sapiom/analytics-core";
|
|
2
|
+
import type { Attribution } from "./index.js";
|
|
3
|
+
/** The one event this package emits: one per capability HTTP call. Dot-form is canonical. */
|
|
4
|
+
export declare const CAPABILITY_CALL_EVENT = "capability.call";
|
|
5
|
+
/**
|
|
6
|
+
* Lazily-created emitter shared by a Transport and every transport derived
|
|
7
|
+
* from it via `withAttribution` — one emitter (one batch queue, one exit
|
|
8
|
+
* hook) per client, no matter how many attributed views are derived.
|
|
9
|
+
*/
|
|
10
|
+
export interface AnalyticsHolder {
|
|
11
|
+
instance?: SapiomAnalytics;
|
|
12
|
+
}
|
|
13
|
+
/** Get (creating on first use) the holder's emitter. Never throws — `createAnalytics` can't. */
|
|
14
|
+
export declare function analyticsFor(holder: AnalyticsHolder, apiKey: string | undefined): SapiomAnalytics;
|
|
15
|
+
/** Everything the Transport knows about one finished (or failed) capability HTTP call. */
|
|
16
|
+
export interface CapabilityCallInfo {
|
|
17
|
+
url: string;
|
|
18
|
+
/** `RequestInit.method`; absent means GET (fetch semantics). */
|
|
19
|
+
method: string | undefined;
|
|
20
|
+
/** `RequestInit.body` — only its SIZE is recorded, never its content. */
|
|
21
|
+
requestBody: unknown;
|
|
22
|
+
durationMs: number;
|
|
23
|
+
/** HTTP status; absent when the fetch itself threw (network-level failure). */
|
|
24
|
+
status?: number;
|
|
25
|
+
ok: boolean;
|
|
26
|
+
/** The thrown value when the fetch failed before producing a response. */
|
|
27
|
+
error?: unknown;
|
|
28
|
+
/** The transport's attribution — execution context, recorded as-is. */
|
|
29
|
+
attribution: Attribution;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build the `capability.call` event `data`. All requests are Sapiom-bound, so
|
|
33
|
+
* request metadata (method, sizes, attribution) is captured; per-field size
|
|
34
|
+
* caps are enforced downstream by the emitter (~16 KB). The recorded `url` is
|
|
35
|
+
* the request URL PATH only — query strings and fragments are stripped, so a
|
|
36
|
+
* caller putting a secret in a query parameter can never leak it into an
|
|
37
|
+
* event. Request/response BODIES are never captured — only the request body size.
|
|
38
|
+
*/
|
|
39
|
+
export declare function capabilityCallData(info: CapabilityCallInfo): Record<string, unknown>;
|
|
40
|
+
/**
|
|
41
|
+
* Derive the capability name from the request URL:
|
|
42
|
+
* - a routed call (`POST <core>/v1/capabilities/<id>`) → the capability id
|
|
43
|
+
* (`web.scrape`, `email.verify`, …);
|
|
44
|
+
* - anything else (service-gateway capabilities) → the URL path, which is the
|
|
45
|
+
* stable per-operation identifier on those hosts (`/v1/sandboxes`, …);
|
|
46
|
+
* - an unparseable URL → the raw string (capped downstream).
|
|
47
|
+
*/
|
|
48
|
+
export declare function capabilityFromUrl(url: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* The request URL with query string and fragment stripped (origin + pathname),
|
|
51
|
+
* consistent with {@link capabilityFromUrl}. Query parameters are deliberately
|
|
52
|
+
* never recorded: today's capabilities carry no secrets there, but a future or
|
|
53
|
+
* direct `transport.fetch(urlWithSecretQuery)` must not silently exfiltrate one.
|
|
54
|
+
*/
|
|
55
|
+
export declare function urlWithoutQuery(url: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* This package's own version. Primary source is the build-time constant
|
|
58
|
+
* generated from package.json (`src/_generated/version.ts`, regenerated by
|
|
59
|
+
* `gen:version` on every build/test/typecheck) — a constant survives bundlers,
|
|
60
|
+
* which inline it where no package.json exists on disk near the bundled
|
|
61
|
+
* module. The runtime package.json walk-up remains only as a fallback for the
|
|
62
|
+
* (should-be-impossible) case of an empty constant, and degrades to
|
|
63
|
+
* `"unknown"` rather than ever throwing.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveSdkVersion(): string;
|
|
66
|
+
//# sourceMappingURL=analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../../src/_client/analytics.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,gGAAgG;AAChG,wBAAgB,YAAY,CAC1B,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,eAAe,CASjB;AAMD,0FAA0F;AAC1F,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,yEAAyE;IACzE,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,OAAO,CAAC;IACZ,0EAA0E;IAC1E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uEAAuE;IACvE,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,kBAAkB,GACvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuBzB;AAKD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CASrD;AAUD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQnD;AAmCD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CAPABILITY_CALL_EVENT = void 0;
|
|
37
|
+
exports.analyticsFor = analyticsFor;
|
|
38
|
+
exports.capabilityCallData = capabilityCallData;
|
|
39
|
+
exports.capabilityFromUrl = capabilityFromUrl;
|
|
40
|
+
exports.urlWithoutQuery = urlWithoutQuery;
|
|
41
|
+
exports.resolveSdkVersion = resolveSdkVersion;
|
|
42
|
+
/**
|
|
43
|
+
* Usage analytics for `@sapiom/tools` — the Transport (this package's single
|
|
44
|
+
* HTTP choke point) enqueues ONE `capability.call` event per capability HTTP
|
|
45
|
+
* call through `@sapiom/analytics-core`.
|
|
46
|
+
*
|
|
47
|
+
* Non-negotiables, guaranteed regardless of configuration:
|
|
48
|
+
* - Live by default: the emitter delivers to the hosted Sapiom collector unless
|
|
49
|
+
* opted out. Opt out with `SAPIOM_TELEMETRY_DISABLED=1`, `DO_NOT_TRACK=1`,
|
|
50
|
+
* or `disabled: true` in the config — any of these makes the emitter a
|
|
51
|
+
* complete no-op (zero network calls, zero disk writes, no notice).
|
|
52
|
+
* `SAPIOM_ANALYTICS_ENDPOINT` overrides the destination (useful in tests).
|
|
53
|
+
* - Enqueue-only on the call path: `track()` is synchronous, delivery is
|
|
54
|
+
* batched off the call path, and no failure here can surface to a caller.
|
|
55
|
+
*/
|
|
56
|
+
const fs = __importStar(require("node:fs"));
|
|
57
|
+
const path = __importStar(require("node:path"));
|
|
58
|
+
const node_url_1 = require("node:url");
|
|
59
|
+
const analytics_core_1 = require("@sapiom/analytics-core");
|
|
60
|
+
const version_js_1 = require("../_generated/version.js");
|
|
61
|
+
/** The one event this package emits: one per capability HTTP call. Dot-form is canonical. */
|
|
62
|
+
exports.CAPABILITY_CALL_EVENT = "capability.call";
|
|
63
|
+
/** Get (creating on first use) the holder's emitter. Never throws — `createAnalytics` can't. */
|
|
64
|
+
function analyticsFor(holder, apiKey) {
|
|
65
|
+
return (holder.instance ?? (holder.instance = (0, analytics_core_1.createAnalytics)({
|
|
66
|
+
source: "tools",
|
|
67
|
+
sdkName: "@sapiom/tools",
|
|
68
|
+
sdkVersion: resolveSdkVersion(),
|
|
69
|
+
// Sent as `x-sapiom-api-key` on collector batches so the server can
|
|
70
|
+
// enrich events with the tenant — never placed in event payloads.
|
|
71
|
+
apiKey,
|
|
72
|
+
})));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build the `capability.call` event `data`. All requests are Sapiom-bound, so
|
|
76
|
+
* request metadata (method, sizes, attribution) is captured; per-field size
|
|
77
|
+
* caps are enforced downstream by the emitter (~16 KB). The recorded `url` is
|
|
78
|
+
* the request URL PATH only — query strings and fragments are stripped, so a
|
|
79
|
+
* caller putting a secret in a query parameter can never leak it into an
|
|
80
|
+
* event. Request/response BODIES are never captured — only the request body size.
|
|
81
|
+
*/
|
|
82
|
+
function capabilityCallData(info) {
|
|
83
|
+
const data = {
|
|
84
|
+
capability: capabilityFromUrl(info.url),
|
|
85
|
+
method: (info.method ?? "GET").toUpperCase(),
|
|
86
|
+
url: urlWithoutQuery(info.url),
|
|
87
|
+
ok: info.ok,
|
|
88
|
+
duration_ms: info.durationMs,
|
|
89
|
+
};
|
|
90
|
+
if (info.status !== undefined)
|
|
91
|
+
data.status = info.status;
|
|
92
|
+
const requestBytes = bodySize(info.requestBody);
|
|
93
|
+
if (requestBytes !== undefined)
|
|
94
|
+
data.request_bytes = requestBytes;
|
|
95
|
+
if (info.error !== undefined) {
|
|
96
|
+
data.error = errorLabel(info.error);
|
|
97
|
+
const message = errorMessage(info.error);
|
|
98
|
+
if (message !== undefined)
|
|
99
|
+
data.error_message = message;
|
|
100
|
+
}
|
|
101
|
+
const a = info.attribution;
|
|
102
|
+
if (a.agentId)
|
|
103
|
+
data.agent_id = a.agentId;
|
|
104
|
+
if (a.agentName)
|
|
105
|
+
data.agent_name = a.agentName;
|
|
106
|
+
if (a.traceId)
|
|
107
|
+
data.trace_id = a.traceId;
|
|
108
|
+
if (a.traceExternalId)
|
|
109
|
+
data.trace_external_id = a.traceExternalId;
|
|
110
|
+
if (a.metadata)
|
|
111
|
+
data.attribution_metadata = a.metadata;
|
|
112
|
+
return data;
|
|
113
|
+
}
|
|
114
|
+
/** Matches the routed-capability seam's path shape: `/v1/capabilities/<id>`. */
|
|
115
|
+
const ROUTED_CAPABILITY_PATH = /^\/v1\/capabilities\/([^/]+)\/?$/;
|
|
116
|
+
/**
|
|
117
|
+
* Derive the capability name from the request URL:
|
|
118
|
+
* - a routed call (`POST <core>/v1/capabilities/<id>`) → the capability id
|
|
119
|
+
* (`web.scrape`, `email.verify`, …);
|
|
120
|
+
* - anything else (service-gateway capabilities) → the URL path, which is the
|
|
121
|
+
* stable per-operation identifier on those hosts (`/v1/sandboxes`, …);
|
|
122
|
+
* - an unparseable URL → the raw string (capped downstream).
|
|
123
|
+
*/
|
|
124
|
+
function capabilityFromUrl(url) {
|
|
125
|
+
try {
|
|
126
|
+
const parsed = new URL(url);
|
|
127
|
+
const routed = ROUTED_CAPABILITY_PATH.exec(parsed.pathname);
|
|
128
|
+
if (routed)
|
|
129
|
+
return safeDecode(routed[1]);
|
|
130
|
+
return parsed.pathname || url;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return url;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function safeDecode(segment) {
|
|
137
|
+
try {
|
|
138
|
+
return decodeURIComponent(segment);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return segment;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The request URL with query string and fragment stripped (origin + pathname),
|
|
146
|
+
* consistent with {@link capabilityFromUrl}. Query parameters are deliberately
|
|
147
|
+
* never recorded: today's capabilities carry no secrets there, but a future or
|
|
148
|
+
* direct `transport.fetch(urlWithSecretQuery)` must not silently exfiltrate one.
|
|
149
|
+
*/
|
|
150
|
+
function urlWithoutQuery(url) {
|
|
151
|
+
try {
|
|
152
|
+
const parsed = new URL(url);
|
|
153
|
+
return `${parsed.origin}${parsed.pathname}`;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Unparseable — strip conservatively at the first `?` or `#`.
|
|
157
|
+
return url.split(/[?#]/)[0] ?? url;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/** Size in bytes of a request body when cheaply knowable (string / Blob / buffer views). */
|
|
161
|
+
function bodySize(body) {
|
|
162
|
+
try {
|
|
163
|
+
if (typeof body === "string")
|
|
164
|
+
return Buffer.byteLength(body);
|
|
165
|
+
if (body !== null && typeof body === "object") {
|
|
166
|
+
const { size, byteLength } = body;
|
|
167
|
+
if (typeof size === "number")
|
|
168
|
+
return size;
|
|
169
|
+
if (typeof byteLength === "number")
|
|
170
|
+
return byteLength;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Analytics never throws; an unknown body simply has no recorded size.
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
function errorLabel(error) {
|
|
179
|
+
if (error instanceof Error)
|
|
180
|
+
return error.name || "Error";
|
|
181
|
+
return typeof error;
|
|
182
|
+
}
|
|
183
|
+
function errorMessage(error) {
|
|
184
|
+
return error instanceof Error ? error.message : undefined;
|
|
185
|
+
}
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// SDK version
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
let cachedSdkVersion = null;
|
|
190
|
+
/**
|
|
191
|
+
* This package's own version. Primary source is the build-time constant
|
|
192
|
+
* generated from package.json (`src/_generated/version.ts`, regenerated by
|
|
193
|
+
* `gen:version` on every build/test/typecheck) — a constant survives bundlers,
|
|
194
|
+
* which inline it where no package.json exists on disk near the bundled
|
|
195
|
+
* module. The runtime package.json walk-up remains only as a fallback for the
|
|
196
|
+
* (should-be-impossible) case of an empty constant, and degrades to
|
|
197
|
+
* `"unknown"` rather than ever throwing.
|
|
198
|
+
*/
|
|
199
|
+
function resolveSdkVersion() {
|
|
200
|
+
return (cachedSdkVersion ?? (cachedSdkVersion = typeof version_js_1.VERSION === "string" && version_js_1.VERSION.length > 0
|
|
201
|
+
? version_js_1.VERSION
|
|
202
|
+
: readOwnPackageVersion()));
|
|
203
|
+
}
|
|
204
|
+
function readOwnPackageVersion() {
|
|
205
|
+
try {
|
|
206
|
+
let dir = resolveModuleDir();
|
|
207
|
+
for (let hops = 0; dir !== ""; hops++) {
|
|
208
|
+
if (hops >= 8)
|
|
209
|
+
break;
|
|
210
|
+
const version = versionIfOwnPackage(path.join(dir, "package.json"));
|
|
211
|
+
if (version !== undefined)
|
|
212
|
+
return version;
|
|
213
|
+
const parent = path.dirname(dir);
|
|
214
|
+
if (parent === dir)
|
|
215
|
+
break;
|
|
216
|
+
dir = parent;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// Version resolution must never break a capability call.
|
|
221
|
+
}
|
|
222
|
+
return "unknown";
|
|
223
|
+
}
|
|
224
|
+
/** The `version` of the manifest at `manifestPath` iff it is this package's own. */
|
|
225
|
+
function versionIfOwnPackage(manifestPath) {
|
|
226
|
+
try {
|
|
227
|
+
const raw = fs.readFileSync(manifestPath, "utf8");
|
|
228
|
+
const manifest = JSON.parse(raw);
|
|
229
|
+
if (manifest.name === "@sapiom/tools" &&
|
|
230
|
+
typeof manifest.version === "string") {
|
|
231
|
+
return manifest.version;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// Missing or unparseable manifest at this level — keep walking up.
|
|
236
|
+
}
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Directory of this compiled module, resolved for BOTH build formats:
|
|
241
|
+
* - CommonJS build (and ts-jest) → `__dirname` (a real global);
|
|
242
|
+
* - ESM build → recovered from a stack trace, where V8 reports this module
|
|
243
|
+
* as a `file://` URL.
|
|
244
|
+
*
|
|
245
|
+
* The stack route is used because a literal `import.meta` is a hard compile
|
|
246
|
+
* error (TS1343) in this single source file's CommonJS compilation, and
|
|
247
|
+
* `eval("import.meta.url")` throws at runtime — eval'd code is parsed as a
|
|
248
|
+
* script, where `import.meta` is invalid. A stack frame is the one anchor
|
|
249
|
+
* available to both formats; any failure degrades to `"unknown"`, never throws.
|
|
250
|
+
*/
|
|
251
|
+
function resolveModuleDir() {
|
|
252
|
+
if (typeof __dirname !== "undefined")
|
|
253
|
+
return __dirname;
|
|
254
|
+
try {
|
|
255
|
+
const file = ownFileFromStack();
|
|
256
|
+
if (file !== undefined) {
|
|
257
|
+
return path.dirname(file.startsWith("file:") ? (0, node_url_1.fileURLToPath)(file) : file);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
// No usable anchor — version resolution degrades to "unknown".
|
|
262
|
+
}
|
|
263
|
+
return "";
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* This module's own file (path or `file://` URL), read from the top-most
|
|
267
|
+
* user frame of a fresh stack — which is this function itself, i.e. this file.
|
|
268
|
+
*/
|
|
269
|
+
function ownFileFromStack() {
|
|
270
|
+
const stack = new Error().stack;
|
|
271
|
+
if (typeof stack !== "string")
|
|
272
|
+
return undefined;
|
|
273
|
+
for (const line of stack.split("\n").slice(1)) {
|
|
274
|
+
// V8 frames: " at fn (file:///p/analytics.js:1:2)" / " at /p/analytics.js:1:2"
|
|
275
|
+
const match = /(?:\(|at )((?:file:\/\/)?[^\s()]+):\d+:\d+\)?\s*$/.exec(line);
|
|
276
|
+
const file = match?.[1];
|
|
277
|
+
if (!file || file.startsWith("node:") || file === "<anonymous>")
|
|
278
|
+
continue;
|
|
279
|
+
return file;
|
|
280
|
+
}
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../src/_client/analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth + transport core for `@sapiom/tools` — the private "runtime half" every
|
|
3
|
-
* capability namespace wraps. Capability modules call THIS; they never import each
|
|
4
|
-
* other's runtime, so the cross-capability mesh (`repo.pushFromSandbox(sandbox)`)
|
|
5
|
-
* has no package/module cycles. See docs/plans/capability-authoring-sdk.md.
|
|
6
|
-
*
|
|
7
|
-
* The credential resolves from one of two sources, same surface either way:
|
|
8
|
-
* - explicit: createClient({ apiKey }) — standalone / open-source use
|
|
9
|
-
* - ambient: SAPIOM_API_KEY (env), which the workflow engine injects per
|
|
10
|
-
* execution so step authors write zero auth plumbing.
|
|
11
|
-
*
|
|
12
|
-
* Attribution (which agent / trace a call belongs to) is execution context the
|
|
13
|
-
* runtime owns, NOT a parameter of any operation — so it lives here on the
|
|
14
|
-
* transport, set once and injected on every request, never threaded through
|
|
15
|
-
* capability methods. The engine sets it per execution by constructing the
|
|
16
|
-
* per-execution client; `withAttribution(...)` is a quiet escape hatch for the
|
|
17
|
-
* router / standalone cases. Capability method signatures deliberately have no
|
|
18
|
-
* attribution argument: that absence keeps LLM-authored step code from setting
|
|
19
|
-
* (and getting wrong) context it doesn't own.
|
|
20
|
-
*/
|
|
21
1
|
/**
|
|
22
2
|
* Per-request attribution recorded with the gateway transaction. Every field is
|
|
23
3
|
* optional and maps 1:1 to an `x-sapiom-*` header the collapsed flow understands.
|
|
@@ -85,6 +65,12 @@ export declare class Transport {
|
|
|
85
65
|
* which injects `SAPIOM_CAPABILITY_RESUME_TOKEN` — keeps working unchanged.
|
|
86
66
|
*/
|
|
87
67
|
readonly resumeToken: string | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Lazily-created usage-analytics emitter (see `./analytics.ts`). Not readonly:
|
|
70
|
+
* `withAttribution` re-points the derived transport at ITS holder so one client
|
|
71
|
+
* keeps one emitter no matter how many attributed views are derived from it.
|
|
72
|
+
*/
|
|
73
|
+
private analyticsHolder;
|
|
88
74
|
constructor(config?: TransportConfig);
|
|
89
75
|
/**
|
|
90
76
|
* A new transport sharing this one's credential, fetch, and resume token, with
|
|
@@ -93,6 +79,20 @@ export declare class Transport {
|
|
|
93
79
|
* step-authoring code reaches for.
|
|
94
80
|
*/
|
|
95
81
|
withAttribution(attribution: Attribution): Transport;
|
|
82
|
+
/**
|
|
83
|
+
* Flush and shut down this client's usage-analytics emitter: buffered events
|
|
84
|
+
* are delivered best-effort and the emitter's `beforeExit` hook is detached.
|
|
85
|
+
* Resolves immediately when no emitter was ever created (no calls made, or
|
|
86
|
+
* analytics disabled); idempotent; never rejects. One call covers every
|
|
87
|
+
* transport derived via {@link withAttribution} (they share the emitter).
|
|
88
|
+
*
|
|
89
|
+
* Call this once per client in hosts that construct MANY clients in one
|
|
90
|
+
* process — e.g. an engine worker building a per-execution client — so exit
|
|
91
|
+
* hooks don't accumulate across executions. One-shot processes don't need
|
|
92
|
+
* it: the emitter flushes on process exit by itself. Capability calls made
|
|
93
|
+
* after shutdown still work; they just no longer emit analytics.
|
|
94
|
+
*/
|
|
95
|
+
shutdown(): Promise<void>;
|
|
96
96
|
/**
|
|
97
97
|
* Authenticated raw fetch — capabilities that need streaming or custom
|
|
98
98
|
* response handling (filesystem, log streams) use this and inspect the
|
|
@@ -104,6 +104,14 @@ export declare class Transport {
|
|
|
104
104
|
* instead (the value is identical — the Transport owns the key either way).
|
|
105
105
|
*/
|
|
106
106
|
fetch(url: string, init?: RequestInit, options?: TransportRequestOptions): Promise<Response>;
|
|
107
|
+
/**
|
|
108
|
+
* Enqueue ONE `capability.call` usage event for a finished (or failed) HTTP
|
|
109
|
+
* call. Synchronous — nothing on the call path is awaited — and never throws;
|
|
110
|
+
* a silent no-op unless a collector endpoint is configured (see `./analytics.ts`).
|
|
111
|
+
* `request()` funnels through `fetch()`, so every capability call is counted
|
|
112
|
+
* exactly once.
|
|
113
|
+
*/
|
|
114
|
+
private trackCapabilityCall;
|
|
107
115
|
/** Authenticated JSON request — parses the body and throws on a non-2xx status. */
|
|
108
116
|
request<T>(url: string, init?: RequestInit, options?: TransportRequestOptions): Promise<T>;
|
|
109
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_client/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_client/index.ts"],"names":[],"mappings":"AA+BA;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,eAAe;IAC9B,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,yFAAyF;IACzF,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,kBAAkB,GAAG,WAAW,CAAC;AAI1D,0FAA0F;AAC1F,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAYD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,CAShD;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAAuB;gBAElC,MAAM,GAAE,eAAoB;IAUxC;;;;;OAKG;IACH,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS;IAWpD;;;;;;;;;;;;OAYG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B;;;;;;;;;OASG;IACG,KAAK,CACT,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,WAAgB,EACtB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,QAAQ,CAAC;IA0BpB;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IA0B3B,mFAAmF;IAC7E,OAAO,CAAC,CAAC,EACb,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,WAAgB,EACtB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,CAAC,CAAC;CAmBd;AAID,wBAAgB,gBAAgB,IAAI,SAAS,CAE5C;AAED,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveCoreBaseUrl = exports.capabilityCall = exports.Transport = void 0;
|
|
4
|
+
exports.attributionFromEnv = attributionFromEnv;
|
|
5
|
+
exports.defaultTransport = defaultTransport;
|
|
2
6
|
/**
|
|
3
7
|
* Auth + transport core for `@sapiom/tools` — the private "runtime half" every
|
|
4
8
|
* capability namespace wraps. Capability modules call THIS; they never import each
|
|
@@ -18,11 +22,12 @@
|
|
|
18
22
|
* router / standalone cases. Capability method signatures deliberately have no
|
|
19
23
|
* attribution argument: that absence keeps LLM-authored step code from setting
|
|
20
24
|
* (and getting wrong) context it doesn't own.
|
|
25
|
+
*
|
|
26
|
+
* Being the single HTTP choke point also makes this the (one) instrumentation
|
|
27
|
+
* seam: every call enqueues a `capability.call` usage event — synchronously,
|
|
28
|
+
* never awaited, live by default. See `./analytics.ts`.
|
|
21
29
|
*/
|
|
22
|
-
|
|
23
|
-
exports.resolveCoreBaseUrl = exports.capabilityCall = exports.Transport = void 0;
|
|
24
|
-
exports.attributionFromEnv = attributionFromEnv;
|
|
25
|
-
exports.defaultTransport = defaultTransport;
|
|
30
|
+
const analytics_js_1 = require("./analytics.js");
|
|
26
31
|
const DEFAULT_AUTH_HEADER = "x-sapiom-api-key";
|
|
27
32
|
function attributionToHeaders(a) {
|
|
28
33
|
const h = {};
|
|
@@ -59,6 +64,12 @@ function attributionFromEnv() {
|
|
|
59
64
|
}
|
|
60
65
|
class Transport {
|
|
61
66
|
constructor(config = {}) {
|
|
67
|
+
/**
|
|
68
|
+
* Lazily-created usage-analytics emitter (see `./analytics.ts`). Not readonly:
|
|
69
|
+
* `withAttribution` re-points the derived transport at ITS holder so one client
|
|
70
|
+
* keeps one emitter no matter how many attributed views are derived from it.
|
|
71
|
+
*/
|
|
72
|
+
this.analyticsHolder = {};
|
|
62
73
|
this.apiKey = config.apiKey ?? process.env.SAPIOM_API_KEY ?? undefined;
|
|
63
74
|
this.fetchImpl = config.fetch ?? globalThis.fetch;
|
|
64
75
|
this.attribution = config.attribution ?? {};
|
|
@@ -74,12 +85,30 @@ class Transport {
|
|
|
74
85
|
* step-authoring code reaches for.
|
|
75
86
|
*/
|
|
76
87
|
withAttribution(attribution) {
|
|
77
|
-
|
|
88
|
+
const derived = new Transport({
|
|
78
89
|
apiKey: this.apiKey,
|
|
79
90
|
fetch: this.fetchImpl,
|
|
80
91
|
attribution: { ...this.attribution, ...attribution },
|
|
81
92
|
resumeToken: this.resumeToken,
|
|
82
93
|
});
|
|
94
|
+
derived.analyticsHolder = this.analyticsHolder;
|
|
95
|
+
return derived;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Flush and shut down this client's usage-analytics emitter: buffered events
|
|
99
|
+
* are delivered best-effort and the emitter's `beforeExit` hook is detached.
|
|
100
|
+
* Resolves immediately when no emitter was ever created (no calls made, or
|
|
101
|
+
* analytics disabled); idempotent; never rejects. One call covers every
|
|
102
|
+
* transport derived via {@link withAttribution} (they share the emitter).
|
|
103
|
+
*
|
|
104
|
+
* Call this once per client in hosts that construct MANY clients in one
|
|
105
|
+
* process — e.g. an engine worker building a per-execution client — so exit
|
|
106
|
+
* hooks don't accumulate across executions. One-shot processes don't need
|
|
107
|
+
* it: the emitter flushes on process exit by itself. Capability calls made
|
|
108
|
+
* after shutdown still work; they just no longer emit analytics.
|
|
109
|
+
*/
|
|
110
|
+
async shutdown() {
|
|
111
|
+
await this.analyticsHolder.instance?.shutdown();
|
|
83
112
|
}
|
|
84
113
|
/**
|
|
85
114
|
* Authenticated raw fetch — capabilities that need streaming or custom
|
|
@@ -96,14 +125,48 @@ class Transport {
|
|
|
96
125
|
throw new Error("@sapiom/tools: no tenant credential. Pass createClient({ apiKey }) for standalone use, " +
|
|
97
126
|
"or run inside a Sapiom workflow (the engine injects SAPIOM_API_KEY).");
|
|
98
127
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
...
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
128
|
+
const startedAt = Date.now();
|
|
129
|
+
let response;
|
|
130
|
+
try {
|
|
131
|
+
response = await this.fetchImpl(url, {
|
|
132
|
+
...init,
|
|
133
|
+
headers: {
|
|
134
|
+
[options.authHeader ?? DEFAULT_AUTH_HEADER]: this.apiKey,
|
|
135
|
+
...attributionToHeaders(this.attribution),
|
|
136
|
+
...(init.headers ?? {}),
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
this.trackCapabilityCall(url, init, startedAt, undefined, error);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
this.trackCapabilityCall(url, init, startedAt, response);
|
|
145
|
+
return response;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Enqueue ONE `capability.call` usage event for a finished (or failed) HTTP
|
|
149
|
+
* call. Synchronous — nothing on the call path is awaited — and never throws;
|
|
150
|
+
* a silent no-op unless a collector endpoint is configured (see `./analytics.ts`).
|
|
151
|
+
* `request()` funnels through `fetch()`, so every capability call is counted
|
|
152
|
+
* exactly once.
|
|
153
|
+
*/
|
|
154
|
+
trackCapabilityCall(url, init, startedAt, response, error) {
|
|
155
|
+
try {
|
|
156
|
+
(0, analytics_js_1.analyticsFor)(this.analyticsHolder, this.apiKey).track(analytics_js_1.CAPABILITY_CALL_EVENT, (0, analytics_js_1.capabilityCallData)({
|
|
157
|
+
url,
|
|
158
|
+
method: init.method,
|
|
159
|
+
requestBody: init.body,
|
|
160
|
+
durationMs: Date.now() - startedAt,
|
|
161
|
+
status: response?.status,
|
|
162
|
+
ok: response?.ok ?? false,
|
|
163
|
+
error,
|
|
164
|
+
attribution: this.attribution,
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Usage analytics must never affect a capability call.
|
|
169
|
+
}
|
|
107
170
|
}
|
|
108
171
|
/** Authenticated JSON request — parses the body and throws on a non-2xx status. */
|
|
109
172
|
async request(url, init = {}, options = {}) {
|