orcareplay 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 (109) hide show
  1. package/dist/api.d.ts +127 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +159 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/args.d.ts +24 -0
  6. package/dist/args.d.ts.map +1 -0
  7. package/dist/args.js +124 -0
  8. package/dist/args.js.map +1 -0
  9. package/dist/cli.d.ts +3 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +4 -0
  12. package/dist/cli.js.map +1 -0
  13. package/dist/commands/compare.d.ts +27 -0
  14. package/dist/commands/compare.d.ts.map +1 -0
  15. package/dist/commands/compare.js +182 -0
  16. package/dist/commands/compare.js.map +1 -0
  17. package/dist/commands/doctor.d.ts +24 -0
  18. package/dist/commands/doctor.d.ts.map +1 -0
  19. package/dist/commands/doctor.js +358 -0
  20. package/dist/commands/doctor.js.map +1 -0
  21. package/dist/commands/gc.d.ts +24 -0
  22. package/dist/commands/gc.d.ts.map +1 -0
  23. package/dist/commands/gc.js +326 -0
  24. package/dist/commands/gc.js.map +1 -0
  25. package/dist/commands/inspect.d.ts +24 -0
  26. package/dist/commands/inspect.d.ts.map +1 -0
  27. package/dist/commands/inspect.js +262 -0
  28. package/dist/commands/inspect.js.map +1 -0
  29. package/dist/commands/record.d.ts +17 -0
  30. package/dist/commands/record.d.ts.map +1 -0
  31. package/dist/commands/record.js +497 -0
  32. package/dist/commands/record.js.map +1 -0
  33. package/dist/commands/replay.d.ts +43 -0
  34. package/dist/commands/replay.d.ts.map +1 -0
  35. package/dist/commands/replay.js +800 -0
  36. package/dist/commands/replay.js.map +1 -0
  37. package/dist/commands/scrub.d.ts +34 -0
  38. package/dist/commands/scrub.d.ts.map +1 -0
  39. package/dist/commands/scrub.js +452 -0
  40. package/dist/commands/scrub.js.map +1 -0
  41. package/dist/commands/setup.d.ts +23 -0
  42. package/dist/commands/setup.d.ts.map +1 -0
  43. package/dist/commands/setup.js +177 -0
  44. package/dist/commands/setup.js.map +1 -0
  45. package/dist/config.d.ts +80 -0
  46. package/dist/config.d.ts.map +1 -0
  47. package/dist/config.js +117 -0
  48. package/dist/config.js.map +1 -0
  49. package/dist/exchange-events.d.ts +72 -0
  50. package/dist/exchange-events.d.ts.map +1 -0
  51. package/dist/exchange-events.js +170 -0
  52. package/dist/exchange-events.js.map +1 -0
  53. package/dist/flags.d.ts +10 -0
  54. package/dist/flags.d.ts.map +1 -0
  55. package/dist/flags.js +83 -0
  56. package/dist/flags.js.map +1 -0
  57. package/dist/fs-events.d.ts +19 -0
  58. package/dist/fs-events.d.ts.map +1 -0
  59. package/dist/fs-events.js +53 -0
  60. package/dist/fs-events.js.map +1 -0
  61. package/dist/index.d.ts +18 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +18 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/main.d.ts +2 -0
  66. package/dist/main.d.ts.map +1 -0
  67. package/dist/main.js +282 -0
  68. package/dist/main.js.map +1 -0
  69. package/dist/mcp-server.d.ts +44 -0
  70. package/dist/mcp-server.d.ts.map +1 -0
  71. package/dist/mcp-server.js +245 -0
  72. package/dist/mcp-server.js.map +1 -0
  73. package/dist/mcp.d.ts +111 -0
  74. package/dist/mcp.d.ts.map +1 -0
  75. package/dist/mcp.js +221 -0
  76. package/dist/mcp.js.map +1 -0
  77. package/dist/out.d.ts +69 -0
  78. package/dist/out.d.ts.map +1 -0
  79. package/dist/out.js +138 -0
  80. package/dist/out.js.map +1 -0
  81. package/dist/rasterize.d.ts +62 -0
  82. package/dist/rasterize.d.ts.map +1 -0
  83. package/dist/rasterize.js +154 -0
  84. package/dist/rasterize.js.map +1 -0
  85. package/dist/serial.d.ts +19 -0
  86. package/dist/serial.d.ts.map +1 -0
  87. package/dist/serial.js +34 -0
  88. package/dist/serial.js.map +1 -0
  89. package/dist/share-card.d.ts +73 -0
  90. package/dist/share-card.d.ts.map +1 -0
  91. package/dist/share-card.js +413 -0
  92. package/dist/share-card.js.map +1 -0
  93. package/dist/snapshot.d.ts +30 -0
  94. package/dist/snapshot.d.ts.map +1 -0
  95. package/dist/snapshot.js +43 -0
  96. package/dist/snapshot.js.map +1 -0
  97. package/dist/tls-capture.d.ts +57 -0
  98. package/dist/tls-capture.d.ts.map +1 -0
  99. package/dist/tls-capture.js +198 -0
  100. package/dist/tls-capture.js.map +1 -0
  101. package/dist/upstream.d.ts +20 -0
  102. package/dist/upstream.d.ts.map +1 -0
  103. package/dist/upstream.js +31 -0
  104. package/dist/upstream.js.map +1 -0
  105. package/dist/version.d.ts +2 -0
  106. package/dist/version.d.ts.map +1 -0
  107. package/dist/version.js +2 -0
  108. package/dist/version.js.map +1 -0
  109. package/package.json +49 -0
@@ -0,0 +1,198 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ import { DEFAULT_TLS_HOSTS, HostPolicy, RunCa, } from '@orcareplay/proxy';
4
+ export async function setupTlsCapture(req) {
5
+ const { args, out, writer, writes } = req;
6
+ const interceptTls = args.bool('tls-intercept');
7
+ const tlsHosts = args.list('tls-hosts');
8
+ // Naming hosts without asking for interception captures nothing and says nothing, which reads
9
+ // as "orca ignored my traffic" rather than as "orca ignored my flag".
10
+ if (!interceptTls && tlsHosts.length > 0) {
11
+ out.warn('tls.hosts_ignored', { hosts: tlsHosts.join(','), next: 'add --tls-intercept' });
12
+ }
13
+ if (!interceptTls)
14
+ return { proxyOptions: {} };
15
+ const hosts = tlsHosts.length > 0 ? tlsHosts : DEFAULT_TLS_HOSTS;
16
+ HostPolicy.from(hosts);
17
+ const trustedOriginCerts = await extraOriginRoots();
18
+ const runDir = writer?.runDir ?? req.runDir;
19
+ if (!runDir)
20
+ throw new Error('TLS interception needs a run directory');
21
+ const ca = await RunCa.create({ runDir });
22
+ return {
23
+ ca,
24
+ proxyOptions: {
25
+ tls: {
26
+ ca,
27
+ hosts,
28
+ trustedOriginCerts,
29
+ ...(writer
30
+ ? {
31
+ onNetExchange: (exchange) => {
32
+ writes.push(() => persistNetExchange(writer, req.turn(), exchange));
33
+ },
34
+ }
35
+ : {}),
36
+ onTunnel: (tunnel) => {
37
+ if (writer)
38
+ writes.push(() => persistTunnel(writer, req.turn(), tunnel));
39
+ },
40
+ onFailure: (failure) => out.warn('tls.handshake_failed', { ...failure }),
41
+ },
42
+ },
43
+ };
44
+ }
45
+ /**
46
+ * What the launched agent needs in its environment to be intercepted, and the disclosure the
47
+ * operator needs before it happens.
48
+ *
49
+ * Lived in `orca record` alongside the setup, so a fork that minted a CA would have decrypted
50
+ * nothing: the child was never pointed at the proxy and never told to trust the root. Interception
51
+ * is only two halves working together, so both halves belong in one place.
52
+ */
53
+ export async function trustRunCa(writer, proxyTls, proxyUrl, env, out) {
54
+ // The child trusts the run CA through its own environment and through nothing else. Nothing here
55
+ // touches a system or browser trust store, and orca will not offer to: interception ends when
56
+ // this process does, which is the property that makes it defensible at all.
57
+ env.HTTPS_PROXY = proxyUrl;
58
+ env.https_proxy = proxyUrl;
59
+ // NODE_EXTRA_CA_CERTS *adds* to Node's trust store. The rest of these *replace* an OpenSSL
60
+ // client's store outright, so they get the bundle — the run CA plus the public roots — or the
61
+ // child would lose its ability to reach every host orca deliberately does not intercept.
62
+ env.NODE_EXTRA_CA_CERTS = proxyTls.caCertPath;
63
+ for (const variable of [
64
+ 'SSL_CERT_FILE',
65
+ 'REQUESTS_CA_BUNDLE',
66
+ 'CURL_CA_BUNDLE',
67
+ 'AWS_CA_BUNDLE',
68
+ 'DENO_CERT',
69
+ ]) {
70
+ env[variable] = proxyTls.caBundlePath;
71
+ }
72
+ // The agent also talks to the recording proxy directly over plain HTTP via the base-URL
73
+ // variables. Sending those through the proxy as well would be a loop.
74
+ const existing = process.env.NO_PROXY ?? process.env.no_proxy ?? '';
75
+ const noProxy = [existing, 'localhost', '127.0.0.1', '::1'].filter(Boolean).join(',');
76
+ env.NO_PROXY = noProxy;
77
+ env.no_proxy = noProxy;
78
+ out.warn('tls.intercepting', {
79
+ hosts: proxyTls.hosts,
80
+ ca_sha256: proxyTls.fingerprint,
81
+ ca_dir: dirname(proxyTls.caCertPath),
82
+ });
83
+ out.plain(' TLS interception is on for this run. Traffic to the hosts above is decrypted,');
84
+ out.plain(' recorded and re-encrypted; everything else is tunnelled unread. The certificate');
85
+ out.plain(' authority is unique to this run, trusted only by the agent orca launches, and');
86
+ out.plain(' deleted when the run ends. It is not installed anywhere.');
87
+ // A digest, never the certificate and never the key. The CA is deleted when the run ends, so
88
+ // this line is the only lasting evidence of which authority signed what — enough to match a
89
+ // certificate someone finds later to the run that minted it, and useless for anything else.
90
+ if (writer) {
91
+ await writer.append({
92
+ type: 'note',
93
+ actor: 'orca',
94
+ turn: 0,
95
+ attrs: { rule: 'tls_intercept', hosts: proxyTls.hosts, ca_sha256: proxyTls.fingerprint },
96
+ });
97
+ }
98
+ }
99
+ /**
100
+ * One decrypted HTTP exchange orca did not recognise as a model call.
101
+ *
102
+ * `net.request` / `net.response` have been in the spec since v0 and nothing has ever emitted them;
103
+ * this is what makes them real. The pair is deliberately shaped like `model.request` /
104
+ * `model.response` so the timeline reads the same either way.
105
+ */
106
+ export async function persistNetExchange(writer, turn, exchange) {
107
+ const request = await writer.append({
108
+ type: 'net.request',
109
+ // The agent made the call. No actor names "some server on the internet", so the reply is
110
+ // attributed to orca, which is the only party that witnessed it.
111
+ actor: 'agent',
112
+ turn,
113
+ attrs: {
114
+ host: exchange.host,
115
+ port: exchange.port,
116
+ method: exchange.method,
117
+ path: exchange.path,
118
+ intercepted: true,
119
+ headers: exchange.requestHeaders,
120
+ truncated: exchange.requestTruncated,
121
+ },
122
+ payload: exchange.requestBody,
123
+ });
124
+ await writer.append({
125
+ type: 'net.response',
126
+ actor: 'orca',
127
+ turn,
128
+ causes: [request.seq],
129
+ attrs: {
130
+ host: exchange.host,
131
+ port: exchange.port,
132
+ status: exchange.status,
133
+ intercepted: true,
134
+ headers: exchange.responseHeaders,
135
+ bytes: exchange.responseBytes,
136
+ truncated: exchange.responseTruncated,
137
+ duration_ms: exchange.durationMs,
138
+ },
139
+ payload: exchange.responseBody,
140
+ });
141
+ }
142
+ /**
143
+ * A connection orca refused to decrypt.
144
+ *
145
+ * Recorded because the refusal is itself the evidence — and because an operator whose harness talks
146
+ * to an endpoint nobody predicted finds it here, in a line naming the host and nothing else, rather
147
+ * than by guessing. No path, no headers, no payload: there is nothing to write, because orca never
148
+ * held the plaintext.
149
+ */
150
+ async function persistTunnel(writer, turn, tunnel) {
151
+ const request = await writer.append({
152
+ type: 'net.request',
153
+ actor: 'agent',
154
+ turn,
155
+ attrs: {
156
+ host: tunnel.host,
157
+ port: tunnel.port,
158
+ intercepted: false,
159
+ reason: 'host not in the TLS interception allowlist',
160
+ },
161
+ });
162
+ await writer.append({
163
+ type: 'net.response',
164
+ actor: 'orca',
165
+ turn,
166
+ causes: [request.seq],
167
+ attrs: {
168
+ host: tunnel.host,
169
+ port: tunnel.port,
170
+ intercepted: false,
171
+ bytes_to_origin: tunnel.bytesToOrigin,
172
+ bytes_to_client: tunnel.bytesToClient,
173
+ duration_ms: tunnel.durationMs,
174
+ ...(tunnel.error === undefined ? {} : { error: tunnel.error }),
175
+ },
176
+ });
177
+ }
178
+ /**
179
+ * Extra roots to trust when connecting onward to the real origin.
180
+ *
181
+ * An intercepting proxy terminates TLS, so it also has to make the outbound connection itself. In a
182
+ * corporate environment that connection goes through another intercepting proxy whose root Node
183
+ * does not ship, and the run fails at the first request with an opaque certificate error.
184
+ */
185
+ async function extraOriginRoots(env = process.env) {
186
+ const configured = env.ORCA_TLS_UPSTREAM_CA;
187
+ if (!configured)
188
+ return [];
189
+ const roots = [];
190
+ for (const path of configured
191
+ .split(',')
192
+ .map((p) => p.trim())
193
+ .filter(Boolean)) {
194
+ roots.push(await readFile(path, 'utf8'));
195
+ }
196
+ return roots;
197
+ }
198
+ //# sourceMappingURL=tls-capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tls-capture.js","sourceRoot":"","sources":["../src/tls-capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ParsedArgs } from './args.js';
2
+ /**
3
+ * Where live model calls go, and what they carry.
4
+ *
5
+ * Needed by record *and* by replay: `--loose` and any fork continue live, and a fork that ignored
6
+ * the override would quietly talk to the real provider instead of the gateway the user pointed it
7
+ * at. Same precedence whichever command is running — flag, then environment, then the gateway from
8
+ * `orca setup`.
9
+ *
10
+ * The headers are the half that makes `orca setup` worth having: a gateway needs a key, and this
11
+ * is the only place one is attached. It goes on the *outbound* request only. What gets recorded is
12
+ * built from the incoming request with auth stripped, so a key orca injects cannot reach a trace
13
+ * by construction rather than by a rule someone has to remember.
14
+ */
15
+ export interface UpstreamPlan {
16
+ upstream: Record<string, string> | undefined;
17
+ headers: Record<string, string> | undefined;
18
+ }
19
+ export declare function upstreamPlan(args: ParsedArgs, env?: NodeJS.ProcessEnv): Promise<UpstreamPlan>;
20
+ //# sourceMappingURL=upstream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream.d.ts","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG5C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAC7C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CAC7C;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,UAAU,EAChB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,YAAY,CAAC,CAqBvB"}
@@ -0,0 +1,31 @@
1
+ import { gatewayHeaders, readConfig, resolveUpstream } from './config.js';
2
+ export async function upstreamPlan(args, env = process.env) {
3
+ const config = await readConfig(env);
4
+ const upstream = await resolveUpstream(args, env);
5
+ const headers = gatewayHeaders(config, env);
6
+ // Unanimity, not `some`. `upstreamHeaders` are attached to every live call the proxy makes —
7
+ // there is no per-dialect header channel — so if any origin we might reach is not the gateway,
8
+ // attaching the key sends it there. A `.some()` check passed whenever *one* dialect still
9
+ // defaulted to the gateway, which is exactly what happens when a flag redirects the other: the
10
+ // gateway's credential went to api.anthropic.com. Verified against the built CLI.
11
+ //
12
+ // Erring towards withholding costs an unauthenticated request and a clear 401. Erring the other
13
+ // way hands a third party a key they were never meant to see, and nothing says it happened.
14
+ const origins = Object.values(upstream ?? {});
15
+ const goingToGateway = config.gateway?.url !== undefined &&
16
+ origins.length > 0 &&
17
+ origins.every((origin) => sameOrigin(origin, config.gateway.url));
18
+ return {
19
+ upstream,
20
+ headers: goingToGateway && Object.keys(headers).length > 0 ? headers : undefined,
21
+ };
22
+ }
23
+ function sameOrigin(a, b) {
24
+ try {
25
+ return new URL(a).origin === new URL(b).origin;
26
+ }
27
+ catch {
28
+ return a.replace(/\/+$/, '') === b.replace(/\/+$/, '');
29
+ }
30
+ }
31
+ //# sourceMappingURL=upstream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream.js","sourceRoot":"","sources":["../src/upstream.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAoB1E,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAgB,EAChB,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5C,6FAA6F;IAC7F,+FAA+F;IAC/F,0FAA0F;IAC1F,+FAA+F;IAC/F,kFAAkF;IAClF,EAAE;IACF,gGAAgG;IAChG,4FAA4F;IAC5F,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC9C,MAAM,cAAc,GAClB,MAAM,CAAC,OAAO,EAAE,GAAG,KAAK,SAAS;QACjC,OAAO,CAAC,MAAM,GAAG,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,OAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IACrE,OAAO;QACL,QAAQ;QACR,OAAO,EAAE,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KACjF,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const ORCA_VERSION = "0.1.0";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const ORCA_VERSION = '0.1.0';
2
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "orcareplay",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "Apache-2.0",
6
+ "description": "Record, replay and fork debugger for AI agents",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "orca": "./dist/cli.js",
11
+ "orcareplay": "./dist/cli.js"
12
+ },
13
+ "exports": {
14
+ ".": "./dist/index.js"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "engines": {
20
+ "node": ">=20.0.0"
21
+ },
22
+ "keywords": [
23
+ "ai",
24
+ "agent",
25
+ "debugger",
26
+ "replay",
27
+ "record",
28
+ "llm",
29
+ "observability",
30
+ "claude",
31
+ "openai"
32
+ ],
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/Continuum-AI-Corp/OrcaReplay.git"
36
+ },
37
+ "dependencies": {
38
+ "@orcareplay/schema": "0.1.0",
39
+ "@orcareplay/plugin-api": "0.1.0",
40
+ "@orcareplay/core": "0.1.0",
41
+ "@orcareplay/proxy": "0.1.0",
42
+ "@orcareplay/providers": "0.1.0",
43
+ "@orcareplay/adapters": "0.1.0",
44
+ "@orcareplay/fs-capture": "0.1.0",
45
+ "@orcareplay/viewer": "0.1.0",
46
+ "@orcareplay/mcp-shim": "0.1.0",
47
+ "@orcareplay/shell-shim": "0.1.0"
48
+ }
49
+ }