@jini-ai/core 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.
Files changed (54) hide show
  1. package/LICENSE +201 -0
  2. package/dist/api-token-auth.d.ts +28 -0
  3. package/dist/api-token-auth.d.ts.map +1 -0
  4. package/dist/api-token-auth.js +30 -0
  5. package/dist/api-token-auth.js.map +1 -0
  6. package/dist/bindings.d.ts +18 -0
  7. package/dist/bindings.d.ts.map +1 -0
  8. package/dist/bindings.js +41 -0
  9. package/dist/bindings.js.map +1 -0
  10. package/dist/compose.typecheck.d.ts +2 -0
  11. package/dist/compose.typecheck.d.ts.map +1 -0
  12. package/dist/compose.typecheck.js +29 -0
  13. package/dist/compose.typecheck.js.map +1 -0
  14. package/dist/daemon.d.ts +40 -0
  15. package/dist/daemon.d.ts.map +1 -0
  16. package/dist/daemon.js +37 -0
  17. package/dist/daemon.js.map +1 -0
  18. package/dist/index.d.ts +13 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +11 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/internal.d.ts +36 -0
  23. package/dist/internal.d.ts.map +1 -0
  24. package/dist/internal.js +35 -0
  25. package/dist/internal.js.map +1 -0
  26. package/dist/origin-validation.d.ts +63 -0
  27. package/dist/origin-validation.d.ts.map +1 -0
  28. package/dist/origin-validation.js +198 -0
  29. package/dist/origin-validation.js.map +1 -0
  30. package/dist/pack.d.ts +26 -0
  31. package/dist/pack.d.ts.map +1 -0
  32. package/dist/pack.js +4 -0
  33. package/dist/pack.js.map +1 -0
  34. package/dist/principal.d.ts +16 -0
  35. package/dist/principal.d.ts.map +1 -0
  36. package/dist/principal.js +2 -0
  37. package/dist/principal.js.map +1 -0
  38. package/dist/redact.d.ts +48 -0
  39. package/dist/redact.d.ts.map +1 -0
  40. package/dist/redact.js +190 -0
  41. package/dist/redact.js.map +1 -0
  42. package/dist/token.d.ts +22 -0
  43. package/dist/token.d.ts.map +1 -0
  44. package/dist/token.js +7 -0
  45. package/dist/token.js.map +1 -0
  46. package/dist/tool-registry.d.ts +114 -0
  47. package/dist/tool-registry.d.ts.map +1 -0
  48. package/dist/tool-registry.js +43 -0
  49. package/dist/tool-registry.js.map +1 -0
  50. package/dist/tool-tokens.d.ts +3 -0
  51. package/dist/tool-tokens.d.ts.map +1 -0
  52. package/dist/tool-tokens.js +10 -0
  53. package/dist/tool-tokens.js.map +1 -0
  54. package/package.json +48 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @module origin-validation
3
+ *
4
+ * Same-origin / allow-listed-origin validation for a locally-bound HTTP
5
+ * daemon: is this browser request actually coming from a page the daemon
6
+ * itself served (or an operator-configured allow-listed origin), as
7
+ * opposed to an arbitrary third-party site making a cross-site request to
8
+ * the daemon's loopback/private-LAN port.
9
+ *
10
+ * Generalized from an upstream flat daemon module: the origin hardcoded its
11
+ * host product's env-var names, now fields on
12
+ * {@link OriginValidationEnvConfig} — see `source-map.md` for the exact
13
+ * mapping. One function, a bypass for a specific product's own
14
+ * browser-extension-driven ingest route, was deliberately **not** ported —
15
+ * it names a specific product route and product feature with no generic
16
+ * equivalent; see `source-map.md`.
17
+ */
18
+ export interface ParsedHostHeader {
19
+ hostname: string;
20
+ host: string;
21
+ port: string;
22
+ }
23
+ export interface RequestWithOriginHeaders {
24
+ headers?: {
25
+ host?: unknown;
26
+ origin?: unknown;
27
+ 'sec-fetch-site'?: unknown;
28
+ };
29
+ }
30
+ /** Names the env vars this port reads. */
31
+ export interface OriginValidationEnvConfig {
32
+ /** Env var carrying a comma-separated list of extra allow-listed origins. */
33
+ allowedOriginsEnvVar: string;
34
+ /** Env var carrying an additional browser-facing port to treat as local (e.g. a separate web dev server). */
35
+ webPortEnvVar: string;
36
+ /** Env var carrying the host the daemon itself is bound to (default `'127.0.0.1'`). */
37
+ bindHostEnvVar: string;
38
+ }
39
+ /** Parses and validates {@link OriginValidationEnvConfig.allowedOriginsEnvVar} into normalized origins. */
40
+ export declare function configuredAllowedOrigins(config: OriginValidationEnvConfig, env?: NodeJS.ProcessEnv): string[];
41
+ /** The `host` (hostname:port) component of each configured allowed origin. */
42
+ export declare function configuredAllowedHosts(origins: string[]): string[];
43
+ /** The set of browser-facing ports considered "local": the daemon's own port plus an optional configured web port. */
44
+ export declare function allowedBrowserPorts(config: OriginValidationEnvConfig, port: number | string | null | undefined, env?: NodeJS.ProcessEnv): number[];
45
+ /** Parses a raw `Host` header value into hostname/host/port, or `null` when unparseable. */
46
+ export declare function parseHostHeader(value: unknown): ParsedHostHeader | null;
47
+ /** Whether `hostname` is an RFC 1918 private IPv4 address (10/8, 172.16/12, 192.168/16) or link-local (169.254/16). */
48
+ export declare function isPrivateIpv4(hostname: unknown): boolean;
49
+ /** Whether `hostname` is a literal IPv4 address or bracketed IPv6 literal (as opposed to a DNS name). */
50
+ export declare function isIpLiteralHostname(hostname: unknown): boolean;
51
+ /** Whether `hostname` is loopback, unspecified, or a private-LAN address. */
52
+ export declare function isLoopbackOrPrivateLanHost(hostname: unknown): boolean;
53
+ /** Whether a request's `Host` header names a locally-served or explicitly allow-listed host. */
54
+ export declare function isAllowedBrowserHost(config: OriginValidationEnvConfig, hostHeader: unknown, ports: number[], bindHost: string, extraAllowedOrigins: string[]): boolean;
55
+ /** Whether an `Origin` header value names a locally-served or explicitly allow-listed origin. */
56
+ export declare function isAllowedBrowserOrigin(config: OriginValidationEnvConfig, origin: unknown, hostHeader: unknown, ports: number[], bindHost: string, extraAllowedOrigins: string[]): boolean;
57
+ /**
58
+ * The top-level same-origin gate: is `req` a request the daemon should treat
59
+ * as coming from one of its own locally-served (or explicitly allow-listed)
60
+ * pages.
61
+ */
62
+ export declare function isLocalSameOrigin(config: OriginValidationEnvConfig, req: RequestWithOriginHeaders, port: number | string | null | undefined, env?: NodeJS.ProcessEnv): boolean;
63
+ //# sourceMappingURL=origin-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin-validation.d.ts","sourceRoot":"","sources":["../src/origin-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,gBAAgB,CAAC,EAAE,OAAO,CAAC;KAC5B,CAAC;CACH;AAED,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACxC,6EAA6E;IAC7E,oBAAoB,EAAE,MAAM,CAAC;IAC7B,6GAA6G;IAC7G,aAAa,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,2GAA2G;AAC3G,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,yBAAyB,EACjC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,EAAE,CAcV;AAED,8EAA8E;AAC9E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAElE;AAED,sHAAsH;AACtH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,yBAAyB,EACjC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,EAAE,CAOV;AAUD,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CASvE;AAED,uHAAuH;AACvH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAQxD;AAED,yGAAyG;AACzG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAQ9D;AAED,6EAA6E;AAC7E,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAWrE;AAED,gGAAgG;AAChG,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,yBAAyB,EACjC,UAAU,EAAE,OAAO,EACnB,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM,EAAE,GAC5B,OAAO,CAaT;AAED,iGAAiG;AACjG,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,yBAAyB,EACjC,MAAM,EAAE,OAAO,EACf,UAAU,EAAE,OAAO,EACnB,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,MAAM,EAChB,mBAAmB,EAAE,MAAM,EAAE,GAC5B,OAAO,CA4BT;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,yBAAyB,EACjC,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAmCT"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * @module origin-validation
3
+ *
4
+ * Same-origin / allow-listed-origin validation for a locally-bound HTTP
5
+ * daemon: is this browser request actually coming from a page the daemon
6
+ * itself served (or an operator-configured allow-listed origin), as
7
+ * opposed to an arbitrary third-party site making a cross-site request to
8
+ * the daemon's loopback/private-LAN port.
9
+ *
10
+ * Generalized from an upstream flat daemon module: the origin hardcoded its
11
+ * host product's env-var names, now fields on
12
+ * {@link OriginValidationEnvConfig} — see `source-map.md` for the exact
13
+ * mapping. One function, a bypass for a specific product's own
14
+ * browser-extension-driven ingest route, was deliberately **not** ported —
15
+ * it names a specific product route and product feature with no generic
16
+ * equivalent; see `source-map.md`.
17
+ */
18
+ /** Parses and validates {@link OriginValidationEnvConfig.allowedOriginsEnvVar} into normalized origins. */
19
+ export function configuredAllowedOrigins(config, env = process.env) {
20
+ const raw = env[config.allowedOriginsEnvVar] || '';
21
+ if (!raw.trim())
22
+ return [];
23
+ return raw
24
+ .split(',')
25
+ .map((origin) => origin.trim())
26
+ .filter(Boolean)
27
+ .map((origin) => {
28
+ const parsed = new URL(origin);
29
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
30
+ throw new Error(`${config.allowedOriginsEnvVar} only supports http:// and https:// origins`);
31
+ }
32
+ return parsed.origin;
33
+ });
34
+ }
35
+ /** The `host` (hostname:port) component of each configured allowed origin. */
36
+ export function configuredAllowedHosts(origins) {
37
+ return origins.map((origin) => new URL(origin).host);
38
+ }
39
+ /** The set of browser-facing ports considered "local": the daemon's own port plus an optional configured web port. */
40
+ export function allowedBrowserPorts(config, port, env = process.env) {
41
+ const ports = [];
42
+ const primary = Number(port);
43
+ if (primary)
44
+ ports.push(primary);
45
+ const webPort = Number(env[config.webPortEnvVar]);
46
+ if (webPort && webPort !== primary)
47
+ ports.push(webPort);
48
+ return ports;
49
+ }
50
+ function headerValue(value) {
51
+ if (Array.isArray(value)) {
52
+ const first = value[0];
53
+ return first == null ? undefined : String(first);
54
+ }
55
+ return value == null ? undefined : String(value);
56
+ }
57
+ /** Parses a raw `Host` header value into hostname/host/port, or `null` when unparseable. */
58
+ export function parseHostHeader(value) {
59
+ const raw = String(headerValue(value) || '').trim();
60
+ if (!raw)
61
+ return null;
62
+ try {
63
+ const parsed = new URL(`http://${raw}`);
64
+ return { hostname: parsed.hostname, host: parsed.host, port: parsed.port || '80' };
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ /** Whether `hostname` is an RFC 1918 private IPv4 address (10/8, 172.16/12, 192.168/16) or link-local (169.254/16). */
71
+ export function isPrivateIpv4(hostname) {
72
+ const parts = String(hostname || '').split('.');
73
+ if (parts.length !== 4)
74
+ return false;
75
+ if (!parts.every((part) => /^\d+$/.test(part)))
76
+ return false;
77
+ const octets = parts.map((part) => Number(part));
78
+ if (!octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255))
79
+ return false;
80
+ const [a, b] = octets;
81
+ return a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || (a === 169 && b === 254);
82
+ }
83
+ /** Whether `hostname` is a literal IPv4 address or bracketed IPv6 literal (as opposed to a DNS name). */
84
+ export function isIpLiteralHostname(hostname) {
85
+ const host = String(hostname || '').trim();
86
+ if (!host)
87
+ return false;
88
+ if (host.startsWith('[') && host.endsWith(']'))
89
+ return true;
90
+ const parts = host.split('.');
91
+ if (parts.length !== 4)
92
+ return false;
93
+ if (!parts.every((part) => /^\d+$/.test(part)))
94
+ return false;
95
+ return parts.map(Number).every((n) => Number.isInteger(n) && n >= 0 && n <= 255);
96
+ }
97
+ /** Whether `hostname` is loopback, unspecified, or a private-LAN address. */
98
+ export function isLoopbackOrPrivateLanHost(hostname) {
99
+ const host = String(hostname || '').toLowerCase();
100
+ return (host === 'localhost' ||
101
+ host === '127.0.0.1' ||
102
+ host === '::1' ||
103
+ host === '[::1]' ||
104
+ host === '0.0.0.0' ||
105
+ host === '::' ||
106
+ isPrivateIpv4(host));
107
+ }
108
+ /** Whether a request's `Host` header names a locally-served or explicitly allow-listed host. */
109
+ export function isAllowedBrowserHost(config, hostHeader, ports, bindHost, extraAllowedOrigins) {
110
+ const requestHost = parseHostHeader(hostHeader);
111
+ if (!requestHost)
112
+ return false;
113
+ const loopbackHosts = ['127.0.0.1', 'localhost', '[::1]'];
114
+ const explicitHosts = new Set([
115
+ ...ports.flatMap((p) => [...loopbackHosts.map((h) => `${h}:${p}`), `${bindHost}:${p}`]),
116
+ ...configuredAllowedHosts(extraAllowedOrigins),
117
+ ]);
118
+ if (explicitHosts.has(requestHost.host))
119
+ return true;
120
+ if (!ports.map(String).includes(requestHost.port))
121
+ return false;
122
+ return isLoopbackOrPrivateLanHost(requestHost.hostname);
123
+ }
124
+ /** Whether an `Origin` header value names a locally-served or explicitly allow-listed origin. */
125
+ export function isAllowedBrowserOrigin(config, origin, hostHeader, ports, bindHost, extraAllowedOrigins) {
126
+ if (extraAllowedOrigins.includes(String(origin)))
127
+ return true;
128
+ let parsedOrigin;
129
+ try {
130
+ parsedOrigin = new URL(String(origin));
131
+ }
132
+ catch {
133
+ return false;
134
+ }
135
+ if (parsedOrigin.protocol !== 'http:' && parsedOrigin.protocol !== 'https:')
136
+ return false;
137
+ const requestHost = parseHostHeader(hostHeader);
138
+ if (!requestHost)
139
+ return false;
140
+ const schemes = ['http', 'https'];
141
+ const loopbackHosts = ['127.0.0.1', 'localhost', '[::1]'];
142
+ const explicitOrigins = new Set(ports.flatMap((p) => [
143
+ ...schemes.flatMap((s) => loopbackHosts.map((h) => `${s}://${h}:${p}`)),
144
+ ...schemes.map((s) => `${s}://${bindHost}:${p}`),
145
+ ]));
146
+ if (explicitOrigins.has(String(origin)))
147
+ return true;
148
+ const originPort = parsedOrigin.port || (parsedOrigin.protocol === 'https:' ? '443' : '80');
149
+ if (!ports.map(String).includes(originPort))
150
+ return false;
151
+ if (parsedOrigin.hostname !== requestHost.hostname)
152
+ return false;
153
+ return isLoopbackOrPrivateLanHost(parsedOrigin.hostname);
154
+ }
155
+ /**
156
+ * The top-level same-origin gate: is `req` a request the daemon should treat
157
+ * as coming from one of its own locally-served (or explicitly allow-listed)
158
+ * pages.
159
+ */
160
+ export function isLocalSameOrigin(config, req, port, env = process.env) {
161
+ const host = String(headerValue(req.headers?.host) || '');
162
+ const origin = headerValue(req.headers?.origin);
163
+ const ports = allowedBrowserPorts(config, port, env);
164
+ const bindHost = env[config.bindHostEnvVar] || '127.0.0.1';
165
+ const extraAllowedOrigins = configuredAllowedOrigins(config, env);
166
+ const ipOnlyExtraOrigins = extraAllowedOrigins.filter((o) => isIpLiteralHostname(new URL(o).hostname));
167
+ const localHostAllowed = isAllowedBrowserHost(config, host, ports, bindHost, ipOnlyExtraOrigins);
168
+ if (origin == null || origin === '') {
169
+ if (localHostAllowed)
170
+ return true;
171
+ // Browsers (Firefox, Chrome) omit Origin on same-origin GET subresource
172
+ // requests per the Fetch spec, which makes hostname entries in the
173
+ // allow-list unreachable for legitimate same-origin GETs through a
174
+ // reverse proxy. Sec-Fetch-Site is set by the user agent and cannot be
175
+ // modified by JavaScript, so a value of "same-origin" attests that the
176
+ // request originated from the same origin as the target — a cross-site
177
+ // `<img>`/`<script>` exploit would carry "cross-site" instead. Only
178
+ // consult the broader allow-list once that signal is present.
179
+ const fetchSite = headerValue(req.headers?.['sec-fetch-site']);
180
+ if (fetchSite === 'same-origin') {
181
+ return isAllowedBrowserHost(config, host, ports, bindHost, extraAllowedOrigins);
182
+ }
183
+ return false;
184
+ }
185
+ // Reverse-proxy deployments terminate the browser connection at the proxy
186
+ // and open a fresh upstream connection to the daemon. The Host header the
187
+ // daemon sees is the proxy upstream's address, not the browser-visible
188
+ // origin, so the host check below fails even when the user explicitly
189
+ // listed their proxy origin in the allow-list. Trust the Origin header in
190
+ // that case: a client-supplied origin that exactly matches an explicitly
191
+ // allow-listed entry is the documented escape hatch for these deployments.
192
+ if (extraAllowedOrigins.includes(origin))
193
+ return true;
194
+ if (!isAllowedBrowserHost(config, host, ports, bindHost, extraAllowedOrigins))
195
+ return false;
196
+ return isAllowedBrowserOrigin(config, origin, host, ports, bindHost, extraAllowedOrigins);
197
+ }
198
+ //# sourceMappingURL=origin-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origin-validation.js","sourceRoot":"","sources":["../src/origin-validation.ts"],"names":[],"mappings":"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"}
package/dist/pack.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { AnyToken, ManyToken, Token } from './token.js';
2
+ /**
3
+ * The only resolver a pack's `services` factory ever sees. Scoped to that
4
+ * pack's own declared `deps` — resolving a token the pack didn't declare is a
5
+ * bug (kernel escape hatch), not a convenience, so it throws rather than
6
+ * silently falling through to a global container.
7
+ */
8
+ export interface PackContainer {
9
+ get<T>(t: Token<T, string>): T;
10
+ getMany<T>(t: ManyToken<T, string>): T[];
11
+ }
12
+ export interface Pack<Deps extends readonly AnyToken<unknown, string>[] = readonly AnyToken<unknown, string>[], Services = unknown, Name extends string = string> {
13
+ readonly name: Name;
14
+ readonly deps: Deps;
15
+ readonly services: (c: PackContainer) => Services;
16
+ readonly http?: (app: unknown, services: Services) => void;
17
+ readonly cli?: (reg: unknown, services: Services) => void;
18
+ }
19
+ export declare function definePack<const Name extends string, const Deps extends readonly AnyToken<unknown, string>[], Services>(def: {
20
+ name: Name;
21
+ deps: Deps;
22
+ services: (c: PackContainer) => Services;
23
+ http?: (app: unknown, services: Services) => void;
24
+ cli?: (reg: unknown, services: Services) => void;
25
+ }): Pack<Deps, Services, Name>;
26
+ //# sourceMappingURL=pack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,IAAI,CACnB,IAAI,SAAS,SAAS,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,GAAG,SAAS,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,EACxF,QAAQ,GAAG,OAAO,EAClB,IAAI,SAAS,MAAM,GAAG,MAAM;IAE5B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,QAAQ,CAAC;IAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IAC3D,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CAC3D;AAED,wBAAgB,UAAU,CACxB,KAAK,CAAC,IAAI,SAAS,MAAM,EACzB,KAAK,CAAC,IAAI,SAAS,SAAS,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,EACvD,QAAQ,EACR,GAAG,EAAE;IACL,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,QAAQ,CAAC;IACzC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IAClD,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;CAClD,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAE7B"}
package/dist/pack.js ADDED
@@ -0,0 +1,4 @@
1
+ export function definePack(def) {
2
+ return def;
3
+ }
4
+ //# sourceMappingURL=pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.js","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAyBA,MAAM,UAAU,UAAU,CAIxB,GAMD;IACC,OAAO,GAA4C,CAAC;AACtD,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `Principal` — the authenticated/authorized identity a kernel operation
3
+ * (a tool call, a run) is performed on behalf of. Deliberately minimal per
4
+ * extraction-plan.md §3 ("Principal/Authorizer, pure interfaces"): the
5
+ * kernel only needs an opaque, stable identity plus an optional coarse
6
+ * role/capability hint a `ToolPolicy` can branch on — anything richer
7
+ * (session, auth provider, user profile) is a consumer concern layered on
8
+ * top, never a kernel noun.
9
+ */
10
+ export interface Principal {
11
+ /** Opaque, stable identity — never assumed to be a product user id. */
12
+ readonly id: string;
13
+ /** Optional coarse role/capability hints a policy may consult. */
14
+ readonly roles?: readonly string[];
15
+ }
16
+ //# sourceMappingURL=principal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principal.d.ts","sourceRoot":"","sources":["../src/principal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=principal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principal.js","sourceRoot":"","sources":["../src/principal.ts"],"names":[],"mappings":""}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @module redact
3
+ *
4
+ * Lexical secret / PII scrubber for telemetry payloads. Verbatim port of
5
+ * OD's `apps/daemon/src/redact.ts` — no product-identity strings or
6
+ * hardcoded env vars in the original, so no genericization was needed.
7
+ *
8
+ * Runs before any prompt or assistant text is sent to an external
9
+ * observability sink. The patterns here are intentionally conservative:
10
+ * each one matches a well-defined token shape with extremely low
11
+ * false-positive rate (API keys have a fixed prefix, JWTs have the
12
+ * "header.payload.signature" triple, credit-card matches go through a Luhn
13
+ * check). What this file does NOT do — and any caller-facing copy must
14
+ * reflect that — is detect names, addresses, business secrets, or anything
15
+ * that requires understanding the meaning of the surrounding text. That's
16
+ * an ML / LLM problem this module doesn't take on.
17
+ *
18
+ * Output format: every match is replaced by `[REDACTED:<kind>]` so a
19
+ * reviewer reading a trace can see exactly which category fired without
20
+ * recovering the original value.
21
+ *
22
+ * References:
23
+ * - Langfuse client-side masking guidance:
24
+ * https://langfuse.com/docs/observability/features/masking
25
+ * - GitHub token format:
26
+ * https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-authentication-to-github
27
+ * - AWS access key shape: 'AKIA' + 16 uppercase alphanumerics.
28
+ * - Luhn algorithm (credit cards): https://en.wikipedia.org/wiki/Luhn_algorithm
29
+ */
30
+ /**
31
+ * Returns `input` with every recognised secret / PII pattern replaced by a
32
+ * `[REDACTED:<kind>]` marker. Idempotent — re-running on already redacted
33
+ * text only matches new tokens.
34
+ *
35
+ * Empty / non-string input passes through unchanged so the caller can use
36
+ * this as a no-op on optional fields.
37
+ */
38
+ export declare function redactSecrets(input: string): string;
39
+ /**
40
+ * Same as {@link redactSecrets} but also returns per-category counts so the
41
+ * caller can attach an audit summary to the trace metadata ('we stripped 2
42
+ * keys + 1 email before send').
43
+ */
44
+ export declare function redactSecretsWithCounts(input: string): {
45
+ redacted: string;
46
+ counts: Record<string, number>;
47
+ };
48
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAiHH;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgBnD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC,CAmCA"}
package/dist/redact.js ADDED
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @module redact
3
+ *
4
+ * Lexical secret / PII scrubber for telemetry payloads. Verbatim port of
5
+ * OD's `apps/daemon/src/redact.ts` — no product-identity strings or
6
+ * hardcoded env vars in the original, so no genericization was needed.
7
+ *
8
+ * Runs before any prompt or assistant text is sent to an external
9
+ * observability sink. The patterns here are intentionally conservative:
10
+ * each one matches a well-defined token shape with extremely low
11
+ * false-positive rate (API keys have a fixed prefix, JWTs have the
12
+ * "header.payload.signature" triple, credit-card matches go through a Luhn
13
+ * check). What this file does NOT do — and any caller-facing copy must
14
+ * reflect that — is detect names, addresses, business secrets, or anything
15
+ * that requires understanding the meaning of the surrounding text. That's
16
+ * an ML / LLM problem this module doesn't take on.
17
+ *
18
+ * Output format: every match is replaced by `[REDACTED:<kind>]` so a
19
+ * reviewer reading a trace can see exactly which category fired without
20
+ * recovering the original value.
21
+ *
22
+ * References:
23
+ * - Langfuse client-side masking guidance:
24
+ * https://langfuse.com/docs/observability/features/masking
25
+ * - GitHub token format:
26
+ * https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-authentication-to-github
27
+ * - AWS access key shape: 'AKIA' + 16 uppercase alphanumerics.
28
+ * - Luhn algorithm (credit cards): https://en.wikipedia.org/wiki/Luhn_algorithm
29
+ */
30
+ // Order matters: list specific rules before more general ones. Langfuse
31
+ // keys (`sk-lf-...`) would otherwise be eaten by the generic `sk-...`
32
+ // rule and labeled as a generic OpenAI-style key.
33
+ const PATTERNS = [
34
+ // Langfuse public/secret keys (pk-lf- / sk-lf-). Must run before the
35
+ // generic sk- rule so the more specific label wins.
36
+ { name: 'langfuse_key', regex: /\b(?:pk|sk)-lf-[A-Za-z0-9-]{16,}\b/g },
37
+ // Anthropic / OpenAI-style keys: 'sk-' + optional sub-prefix + base64-ish.
38
+ // Both vendors plus a long tail of OpenAI-compatible providers (DeepSeek,
39
+ // MiniMax, Together, etc.) ship keys in this shape, so a single rule
40
+ // covers most "sk-..." secrets a user might paste into a prompt.
41
+ { name: 'sk_key', regex: /\bsk-(?:proj-|live-|test-|ant-)?[A-Za-z0-9_-]{20,}\b/g },
42
+ // GitHub personal / OAuth / server / user-server / refresh tokens.
43
+ { name: 'github_token', regex: /\bgh[opsur]_[A-Za-z0-9]{36,251}\b/g },
44
+ // GitHub legacy 40-hex personal access tokens that ship with no prefix
45
+ // are indistinguishable from a sha1 hash, so we don't try to match them
46
+ // here — false positives would be brutal in commit logs / artifact slugs.
47
+ // AWS access key id. The matching secret access key is just 40 base64
48
+ // chars with no fixed shape, so we cannot reliably redact it without
49
+ // huge collateral damage; flagging the access key id at least
50
+ // signals a paste happened.
51
+ { name: 'aws_access_key', regex: /\bAKIA[0-9A-Z]{16}\b/g },
52
+ // Google API keys — legacy AIza… (Maps / AI Studio) and service-account-
53
+ // bound AQ.… keys AI Studio now issues. AQ. must run before the generic
54
+ // sk_ rule would mis-label unrelated tokens.
55
+ { name: 'google_api_key', regex: /\bAQ\.[A-Za-z0-9_-]{20,}\b/g },
56
+ { name: 'google_api_key', regex: /\bAIza[0-9A-Za-z_-]{35}\b/g },
57
+ // Slack tokens.
58
+ { name: 'slack_token', regex: /\bxox[abprs]-[0-9A-Za-z-]{10,}\b/g },
59
+ // Stripe keys.
60
+ { name: 'stripe_key', regex: /\b(?:sk|pk|rk)_(?:live|test)_[0-9a-zA-Z]{16,}\b/g },
61
+ // JSON Web Tokens. The "header.payload.signature" triple is distinctive
62
+ // enough that false positives are rare (the literal "eyJ" prefix is the
63
+ // base64 encoding of '{"' which is how every JOSE header starts).
64
+ { name: 'jwt', regex: /\beyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g },
65
+ // Bearer tokens in HTTP Authorization header copy/paste. We only match
66
+ // the value, not the literal 'Bearer ', so the marker stays readable in
67
+ // the redacted output ("Authorization: Bearer [REDACTED:bearer_token]").
68
+ { name: 'bearer_token', regex: /(?<=\bBearer\s+)[A-Za-z0-9._~+/-]{16,}={0,2}/g },
69
+ // Email addresses. Conservative enough to require a TLD-like trailer.
70
+ { name: 'email', regex: /\b[\w.+-]+@[\w-]+\.[\w.-]+\b/g },
71
+ // IPv4. Reject all-zero / 255.255.255.255-ish junk shapes by gating on
72
+ // each octet being 0-255.
73
+ {
74
+ name: 'ipv4',
75
+ regex: /\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\b/g,
76
+ },
77
+ // Phone numbers. Tight US-leaning shape; a global PII detector would
78
+ // need a real lib. We keep this so US-based test prompts ('call me at
79
+ // (415) 555-...') don't ship. Note: no leading \b — '(' isn't a word
80
+ // char, so a starting boundary would refuse to match `(415)`. We
81
+ // require a non-digit (or start of string) before the run instead.
82
+ {
83
+ name: 'phone',
84
+ regex: /(?<!\d)(?:\+?\d{1,3}[\s.-]?)?(?:\(\d{3}\)|\d{3})[\s.-]?\d{3}[\s.-]?\d{4}(?!\d)/g,
85
+ },
86
+ ];
87
+ // Credit card sweep is special: a naive 13-19 digit run matches a lot of
88
+ // non-card numbers (timestamps, IDs, hashes). We isolate the candidate
89
+ // then run a Luhn check before redacting.
90
+ const CARD_CANDIDATE = /\b(?:\d[ -]?){12,18}\d\b/g;
91
+ const API_KEY_HEADER = /(^|[^?&\w-])("?)(x-api-key|api-key|x-goog-api-key)\2(\s*[:=]\s*)("[^"]*"|[^\s,;"'#}]+)/gi;
92
+ const API_KEY_QUERY = /([?&](?:key|api_key|api-key)=)[^&#\s,;"']+/gi;
93
+ // The length guard and the per-digit `d < 0 || d > 9` guard from the origin
94
+ // were both dropped here (a documented deviation from the otherwise-verbatim
95
+ // port — see source-map.md): `isLuhnValid` is private with exactly one call
96
+ // site (the `CARD_CANDIDATE.replace` callbacks below), and `CARD_CANDIDATE`'s
97
+ // own regex (`\b(?:\d[ -]?){12,18}\d\b`) already guarantees any string
98
+ // reaching here is 13-19 characters, and the preceding `match.replace(/\D/g,
99
+ // '')` strip guarantees every character is an ASCII digit — so both guards
100
+ // were provably unreachable, not speculative.
101
+ function isLuhnValid(digits) {
102
+ let sum = 0;
103
+ let alt = false;
104
+ for (let i = digits.length - 1; i >= 0; i -= 1) {
105
+ let d = digits.charCodeAt(i) - 48;
106
+ if (alt) {
107
+ d *= 2;
108
+ if (d > 9)
109
+ d -= 9;
110
+ }
111
+ sum += d;
112
+ alt = !alt;
113
+ }
114
+ return sum % 10 === 0;
115
+ }
116
+ function redactApiKeyHeaderValue(prefix, quote, name, separator, value) {
117
+ const redactedValue = value.startsWith('"') ? '"[REDACTED:api_key_header]"' : '[REDACTED:api_key_header]';
118
+ return `${prefix}${quote}${name}${quote}${separator}${redactedValue}`;
119
+ }
120
+ /**
121
+ * Returns `input` with every recognised secret / PII pattern replaced by a
122
+ * `[REDACTED:<kind>]` marker. Idempotent — re-running on already redacted
123
+ * text only matches new tokens.
124
+ *
125
+ * Empty / non-string input passes through unchanged so the caller can use
126
+ * this as a no-op on optional fields.
127
+ */
128
+ export function redactSecrets(input) {
129
+ if (!input)
130
+ return input;
131
+ let out = input;
132
+ for (const { name, regex } of PATTERNS) {
133
+ out = out.replace(regex, `[REDACTED:${name}]`);
134
+ }
135
+ out = out
136
+ .replace(API_KEY_HEADER, (_match, prefix, quote, name, separator, value) => redactApiKeyHeaderValue(prefix, quote, name, separator, value))
137
+ .replace(API_KEY_QUERY, '$1[REDACTED:api_key_query]');
138
+ out = out.replace(CARD_CANDIDATE, (match) => {
139
+ const digits = match.replace(/\D/g, '');
140
+ return isLuhnValid(digits) ? '[REDACTED:credit_card]' : match;
141
+ });
142
+ return out;
143
+ }
144
+ /**
145
+ * Same as {@link redactSecrets} but also returns per-category counts so the
146
+ * caller can attach an audit summary to the trace metadata ('we stripped 2
147
+ * keys + 1 email before send').
148
+ */
149
+ export function redactSecretsWithCounts(input) {
150
+ const counts = {};
151
+ if (!input)
152
+ return { redacted: input, counts };
153
+ let out = input;
154
+ for (const { name, regex } of PATTERNS) {
155
+ let matched = 0;
156
+ out = out.replace(regex, () => {
157
+ matched += 1;
158
+ return `[REDACTED:${name}]`;
159
+ });
160
+ if (matched > 0)
161
+ counts[name] = matched;
162
+ }
163
+ let apiKeyHeaderCount = 0;
164
+ out = out.replace(API_KEY_HEADER, (_match, prefix, quote, name, separator, value) => {
165
+ apiKeyHeaderCount += 1;
166
+ return redactApiKeyHeaderValue(prefix, quote, name, separator, value);
167
+ });
168
+ if (apiKeyHeaderCount > 0)
169
+ counts.api_key_header = apiKeyHeaderCount;
170
+ let apiKeyQueryCount = 0;
171
+ out = out.replace(API_KEY_QUERY, (_match, prefix) => {
172
+ apiKeyQueryCount += 1;
173
+ return `${prefix}[REDACTED:api_key_query]`;
174
+ });
175
+ if (apiKeyQueryCount > 0)
176
+ counts.api_key_query = apiKeyQueryCount;
177
+ let cardCount = 0;
178
+ out = out.replace(CARD_CANDIDATE, (match) => {
179
+ const digits = match.replace(/\D/g, '');
180
+ if (isLuhnValid(digits)) {
181
+ cardCount += 1;
182
+ return '[REDACTED:credit_card]';
183
+ }
184
+ return match;
185
+ });
186
+ if (cardCount > 0)
187
+ counts.credit_card = cardCount;
188
+ return { redacted: out, counts };
189
+ }
190
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface Token<T, Id extends string = string> {
2
+ readonly id: Id;
3
+ readonly version: number;
4
+ readonly cardinality: 'one';
5
+ /** Phantom — never assigned; carries T for inference only. */
6
+ readonly __type?: T;
7
+ }
8
+ export interface ManyToken<T, Id extends string = string> {
9
+ readonly id: Id;
10
+ readonly version: number;
11
+ readonly cardinality: 'many';
12
+ /** Phantom — never assigned; carries T for inference only. */
13
+ readonly __type?: T;
14
+ }
15
+ export type AnyToken<T = unknown, Id extends string = string> = Token<T, Id> | ManyToken<T, Id>;
16
+ export declare function token<T, const Id extends string = string>(id: Id, opts?: {
17
+ version?: number;
18
+ }): Token<T, Id>;
19
+ export declare function manyToken<T, const Id extends string = string>(id: Id, opts?: {
20
+ version?: number;
21
+ }): ManyToken<T, Id>;
22
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../src/token.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK,CAAC,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM;IAClD,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,KAAK,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;CACrB;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM;IACtD,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC;IAChB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;CACrB;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAEhG,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAEhH;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC3D,EAAE,EAAE,EAAE,EACN,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAC9B,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAElB"}
package/dist/token.js ADDED
@@ -0,0 +1,7 @@
1
+ export function token(id, opts = {}) {
2
+ return { id, version: opts.version ?? 1, cardinality: 'one' };
3
+ }
4
+ export function manyToken(id, opts = {}) {
5
+ return { id, version: opts.version ?? 1, cardinality: 'many' };
6
+ }
7
+ //# sourceMappingURL=token.js.map