agentfootprint 9.7.0 → 9.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/identity/vault.js +336 -0
- package/dist/adapters/identity/vault.js.map +1 -0
- package/dist/adapters/observability/file.js +307 -0
- package/dist/adapters/observability/file.js.map +1 -0
- package/dist/esm/adapters/identity/vault.d.ts +146 -0
- package/dist/esm/adapters/identity/vault.js +332 -0
- package/dist/esm/adapters/identity/vault.js.map +1 -0
- package/dist/esm/adapters/observability/file.d.ts +145 -0
- package/dist/esm/adapters/observability/file.js +303 -0
- package/dist/esm/adapters/observability/file.js.map +1 -0
- package/dist/esm/identity.d.ts +1 -0
- package/dist/esm/identity.js +4 -0
- package/dist/esm/identity.js.map +1 -1
- package/dist/esm/observability-providers.d.ts +4 -1
- package/dist/esm/observability-providers.js +7 -1
- package/dist/esm/observability-providers.js.map +1 -1
- package/dist/identity.js +6 -1
- package/dist/identity.js.map +1 -1
- package/dist/observability-providers.js +9 -2
- package/dist/observability-providers.js.map +1 -1
- package/dist/types/adapters/identity/vault.d.ts +147 -0
- package/dist/types/adapters/identity/vault.d.ts.map +1 -0
- package/dist/types/adapters/observability/file.d.ts +146 -0
- package/dist/types/adapters/observability/file.d.ts.map +1 -0
- package/dist/types/identity.d.ts +1 -0
- package/dist/types/identity.d.ts.map +1 -1
- package/dist/types/observability-providers.d.ts +4 -1
- package/dist/types/observability-providers.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fileObservability — the typed event stream, one JSON line per event, in a
|
|
3
|
+
* local file.
|
|
4
|
+
*
|
|
5
|
+
* The sink for the shop that has no collector. Every other adapter in this
|
|
6
|
+
* folder ships somewhere: CloudWatch, X-Ray, an OTLP endpoint. A great many
|
|
7
|
+
* on-premises deployments have none of those — they have a directory, a log
|
|
8
|
+
* shipper (Filebeat, Fluent Bit, Vector, `promtail`, `journald`, or a person
|
|
9
|
+
* with `grep`), and a rule that nothing leaves the network. NDJSON on disk is
|
|
10
|
+
* the format all of those already read, so the sink is the file.
|
|
11
|
+
*
|
|
12
|
+
* Zero dependencies: `node:fs`, lazily required at construction so merely
|
|
13
|
+
* importing `agentfootprint/observe` stays browser-safe (this factory is
|
|
14
|
+
* Node-only; calling it in a browser throws by name).
|
|
15
|
+
*
|
|
16
|
+
* ## The line
|
|
17
|
+
*
|
|
18
|
+
* One `JSON.stringify(event)` per line, newline-terminated, appended in
|
|
19
|
+
* dispatch order — the SAME envelope `cloudwatchObservability` puts in a log
|
|
20
|
+
* event, so a query written against one reads the other:
|
|
21
|
+
*
|
|
22
|
+
* ```jsonl
|
|
23
|
+
* {"type":"agentfootprint.agent.turn_start","payload":{…},"meta":{"runId":"…","sessionId":"…"}}
|
|
24
|
+
* {"type":"agentfootprint.stream.tool_end","payload":{…},"meta":{…}}
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Nothing is summarized, bounded or redacted on the way out. **A payload that
|
|
28
|
+
* must not be on that disk must not reach this strategy** — narrow it with
|
|
29
|
+
* `eventTypes` / `tier` / `sampleRate`, or apply a footprintjs
|
|
30
|
+
* `RedactionPolicy` upstream, exactly as with every other sink. (For a bounded
|
|
31
|
+
* record by construction, `auditExport({ payloadMode: 'bounded' })` is the
|
|
32
|
+
* adapter that does that job.)
|
|
33
|
+
*
|
|
34
|
+
* ## Buffered, not synchronous
|
|
35
|
+
*
|
|
36
|
+
* `exportEvent` is sync and never touches the disk: it serializes, buffers, and
|
|
37
|
+
* returns. Batches are appended asynchronously on a size trigger
|
|
38
|
+
* (`maxBufferEvents` / `maxBufferBytes`), on a timer (`flushIntervalMs`), and on
|
|
39
|
+
* `flush()`. A hard kill therefore loses at most the buffer — the price of not
|
|
40
|
+
* making telemetry a term in agent-loop latency. Call `flush()` (or
|
|
41
|
+
* `agent.shutdown()`, which does) at process end; see the 8.12.0 lifecycle laws
|
|
42
|
+
* on {@link BaseStrategy.flush}.
|
|
43
|
+
*
|
|
44
|
+
* ## Rotation is ONE generation, and that is deliberate
|
|
45
|
+
*
|
|
46
|
+
* With `maxBytes` set, a batch that would push the file past the ceiling first
|
|
47
|
+
* renames it to `<path>.1` — **replacing any previous `.1`** — and starts a
|
|
48
|
+
* fresh file. That is the whole policy. There is no `.2`, no compression, no
|
|
49
|
+
* time-based schedule, no cross-process coordination (two processes writing one
|
|
50
|
+
* file each keep their own byte count and will both rotate it). It exists so an
|
|
51
|
+
* unattended agent cannot fill a disk, and for nothing else: **retention is a
|
|
52
|
+
* log-management daemon's job**, and `logrotate` with `copytruncate`, Fluent Bit,
|
|
53
|
+
* or a systemd timer will do it properly. Omit `maxBytes` — the default — and
|
|
54
|
+
* this adapter never renames anything, which is the right choice when a real
|
|
55
|
+
* rotator already owns the file.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* import { fileObservability } from 'agentfootprint/observe';
|
|
60
|
+
*
|
|
61
|
+
* const telemetry = agent.enable.observability({
|
|
62
|
+
* strategy: fileObservability({
|
|
63
|
+
* path: '/var/log/agentfootprint/events.ndjson',
|
|
64
|
+
* maxBytes: 64 * 1024 * 1024, // safety ceiling; logrotate owns retention
|
|
65
|
+
* }),
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* // … run …
|
|
69
|
+
* await agent.shutdown(); // flushes + stops everything enabled
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
import type { AgentfootprintEvent, AgentfootprintEventType } from '../../events/registry.js';
|
|
73
|
+
import type { ObservabilityStrategy } from '../../strategies/types.js';
|
|
74
|
+
export interface FileObservabilityOptions {
|
|
75
|
+
/** Absolute or relative path to the NDJSON file. **Required.** Its parent
|
|
76
|
+
* directories are created (`recursive`) and the file itself is claimed at
|
|
77
|
+
* construction, so an unwritable path fails where you wrote it rather than
|
|
78
|
+
* at the first event — the only moment a caller is still watching. */
|
|
79
|
+
readonly path: string;
|
|
80
|
+
/** Rotation ceiling in bytes. Omitted → **never rotates** (the right choice
|
|
81
|
+
* when `logrotate` or a shipper already owns the file). Set → a batch that
|
|
82
|
+
* would cross the ceiling first renames the file to `<path>.1`, replacing
|
|
83
|
+
* any previous `.1`, and starts fresh. ONE generation, no compression, no
|
|
84
|
+
* cross-process coordination — see the note in this module's docstring. */
|
|
85
|
+
readonly maxBytes?: number;
|
|
86
|
+
/** Max events buffered before a forced append. Default 100. */
|
|
87
|
+
readonly maxBufferEvents?: number;
|
|
88
|
+
/** Max buffered payload bytes (UTF-8) before a forced append. Default 65536
|
|
89
|
+
* (64 KB) — a local write is cheap, so this is far larger than the network
|
|
90
|
+
* adapters' 10 KB. */
|
|
91
|
+
readonly maxBufferBytes?: number;
|
|
92
|
+
/** Forced-append interval when traffic is sparse, in ms. Default 1000.
|
|
93
|
+
* `0` disables the timer — only size triggers and `flush()` write. */
|
|
94
|
+
readonly flushIntervalMs?: number;
|
|
95
|
+
/** Narrow what lands on the disk. Becomes the strategy's
|
|
96
|
+
* {@link ObservabilityStrategy.relevantEventTypes}, so the dispatcher does
|
|
97
|
+
* not even forward the rest — the filter costs nothing at the hot path.
|
|
98
|
+
* Omitted → every event the `tier` lets through is written. */
|
|
99
|
+
readonly eventTypes?: readonly AgentfootprintEventType[];
|
|
100
|
+
/**
|
|
101
|
+
* Where delivery failures go — a full disk, a revoked permission, a path
|
|
102
|
+
* whose directory was removed under a long-running process.
|
|
103
|
+
*
|
|
104
|
+
* Same law as the network adapters (8.11.0): **telemetry that fails
|
|
105
|
+
* invisibly is indistinguishable from telemetry that works.** Unhandled,
|
|
106
|
+
* failures reach a rate-limited `console.error`. The batch that failed is
|
|
107
|
+
* dropped, never requeued, so a disk that has been full for an hour cannot
|
|
108
|
+
* grow the buffer without bound.
|
|
109
|
+
*/
|
|
110
|
+
readonly onError?: (error: Error, event?: AgentfootprintEvent) => void;
|
|
111
|
+
/** Test seam — inject a filesystem. Bypasses `node:fs` entirely, which is
|
|
112
|
+
* also what lets the rotation policy be asserted without a real disk. */
|
|
113
|
+
readonly _fs?: FileSinkFs;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The slice of the filesystem this adapter touches — five calls, named by what
|
|
117
|
+
* the adapter uses them FOR rather than by their `node:fs` signatures.
|
|
118
|
+
*
|
|
119
|
+
* Sync members run once, at construction; the append path is async so the agent
|
|
120
|
+
* loop never waits on a disk.
|
|
121
|
+
*/
|
|
122
|
+
export interface FileSinkFs {
|
|
123
|
+
/** Create the log file's parent directory chain. */
|
|
124
|
+
mkdirSync(dir: string, options: {
|
|
125
|
+
readonly recursive: boolean;
|
|
126
|
+
}): void;
|
|
127
|
+
/** Claim the file at construction. This is the writability refusal: an
|
|
128
|
+
* unwritable path throws HERE, with the caller still on the stack. */
|
|
129
|
+
appendFileSync(file: string, data: string): void;
|
|
130
|
+
/** Current size, so the rotation counter starts from what is already there
|
|
131
|
+
* rather than from zero on every restart. */
|
|
132
|
+
statSync(file: string): {
|
|
133
|
+
readonly size: number;
|
|
134
|
+
};
|
|
135
|
+
/** Append one batch of NDJSON lines. */
|
|
136
|
+
appendFile(file: string, data: string): Promise<void>;
|
|
137
|
+
/** Rotate: `<path>` → `<path>.1`, replacing any previous `.1`. */
|
|
138
|
+
rename(from: string, to: string): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* NDJSON-to-a-local-file observability strategy. See
|
|
142
|
+
* {@link FileObservabilityOptions} for the per-option contract, and this
|
|
143
|
+
* module's docstring for the rotation policy and what is NOT bounded.
|
|
144
|
+
*/
|
|
145
|
+
export declare function fileObservability(opts: FileObservabilityOptions): ObservabilityStrategy;
|
|
146
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../../../src/adapters/observability/file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAE7F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAMvE,MAAM,WAAW,wBAAwB;IACvC;;;2EAGuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;gFAI4E;IAC5E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;2BAEuB;IACvB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC;2EACuE;IACvE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;oEAGgE;IAChE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACzD;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACvE;8EAC0E;IAC1E,QAAQ,CAAC,GAAG,CAAC,EAAE,UAAU,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACvE;2EACuE;IACvE,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD;kDAC8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,wCAAwC;IACxC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,kEAAkE;IAClE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAID;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,qBAAqB,CA2LvF"}
|
package/dist/types/identity.d.ts
CHANGED
|
@@ -35,4 +35,5 @@ export type { AuthorizationRequiredMode, ConsentRequest } from './identity/conse
|
|
|
35
35
|
export { CredentialConsentRequiredError, type CredentialConsentRequiredContext, } from './identity/CredentialConsentRequiredError.js';
|
|
36
36
|
export { withCredentialRetry, type WithCredentialRetryOptions, } from './identity/withCredentialRetry.js';
|
|
37
37
|
export { agentCoreIdentity, type AgentCoreIdentityOptions, type AgentCoreIdentityClientLike, type AgentCoreOauthResponse, } from './adapters/identity/agentcore.js';
|
|
38
|
+
export { vaultCredentials, type VaultCredentialsOptions } from './adapters/identity/vault.js';
|
|
38
39
|
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,+BAA+B,EAC/B,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EACL,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAIpF,YAAY,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,8BAA8B,EAC9B,KAAK,gCAAgC,GACtC,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,mBAAmB,EACnB,KAAK,0BAA0B,GAChC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,GAC5B,MAAM,kCAAkC,CAAC"}
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,+BAA+B,EAC/B,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EACL,MAAM,EACN,MAAM,EACN,KAAK,EACL,OAAO,EACP,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,iBAAiB,GACvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAIpF,YAAY,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,8BAA8B,EAC9B,KAAK,gCAAgC,GACtC,MAAM,8CAA8C,CAAC;AACtD,OAAO,EACL,mBAAmB,EACnB,KAAK,0BAA0B,GAChC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,GAC5B,MAAM,kCAAkC,CAAC;AAI1C,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,8BAA8B,CAAC"}
|
|
@@ -30,7 +30,9 @@
|
|
|
30
30
|
* - agentcoreObservability ← v2.8.1
|
|
31
31
|
* - cloudwatchObservability ← v2.8.2
|
|
32
32
|
* - xrayObservability ← v2.8.3
|
|
33
|
-
* - otelObservability ← v2.9.0
|
|
33
|
+
* - otelObservability ← v2.9.0
|
|
34
|
+
* - fileObservability ← 9.8.0 (no vendor at all — NDJSON on disk,
|
|
35
|
+
* for the on-premises shop with no collector)
|
|
34
36
|
*
|
|
35
37
|
* Note: `datadogObservability` was on the v2.9 roadmap, but Datadog
|
|
36
38
|
* APM accepts OTLP — point your OTel SDK at Datadog's OTLP endpoint
|
|
@@ -46,6 +48,7 @@ export { agentcoreObservability, type AgentcoreObservabilityOptions, } from './a
|
|
|
46
48
|
export { cloudwatchObservability, type CloudwatchObservabilityOptions, } from './adapters/observability/cloudwatch.js';
|
|
47
49
|
export { xrayObservability, type XrayObservabilityOptions, type XRayLikeClient, } from './adapters/observability/xray.js';
|
|
48
50
|
export { otelObservability, type OtelObservabilityOptions, type OtelObservabilityStrategy, type OtelDecisionEvidenceRecorder, type OtelTracerLike, type OtelSpanLike, type OtelSpanOptions, type OtelAttributeValue, } from './adapters/observability/otel.js';
|
|
51
|
+
export { fileObservability, type FileObservabilityOptions, type FileSinkFs, } from './adapters/observability/file.js';
|
|
49
52
|
export { auditExport, verifyAuditBundle, AUDIT_BUNDLE_FORMAT, AUDIT_GENESIS_EVENT_TYPE, AUDIT_ZERO_HASH, type AuditBundle, type AuditBundleHeader, type AuditExportOptions, type AuditExportStrategy, type AuditRecord, type AuditVerifyResult, } from './adapters/observability/audit.js';
|
|
50
53
|
export { canonicalJson, CANONICAL_JSON_VERSION } from './lib/canonicalJson.js';
|
|
51
54
|
//# sourceMappingURL=observability-providers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observability-providers.d.ts","sourceRoot":"","sources":["../../src/observability-providers.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"observability-providers.d.ts","sourceRoot":"","sources":["../../src/observability-providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,OAAO,EACL,sBAAsB,EACtB,KAAK,6BAA6B,GACnC,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,uBAAuB,EACvB,KAAK,8BAA8B,GACpC,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,GACpB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,EACjC,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,kCAAkC,CAAC;AAI1C,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,UAAU,GAChB,MAAM,kCAAkC,CAAC;AAI1C,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,wBAAwB,EACxB,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentfootprint",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.8.0",
|
|
4
4
|
"description": "The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|