cortad 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.
- package/README.md +53 -0
- package/lib/door.mjs +171 -0
- package/lib/identities.mjs +397 -0
- package/lib/lock.mjs +76 -0
- package/lib/mint.mjs +133 -0
- package/lib/pyhook/sitecustomize.py +301 -0
- package/lib/replay.mjs +49 -0
- package/lib/start.mjs +85 -0
- package/lib/trace.cjs +103 -0
- package/local.mjs +620 -0
- package/package.json +29 -0
package/lib/trace.cjs
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Loaded into your app by the command that started it (node --require), and only then. It watches
|
|
2
|
+
// for one thing: a request to your app during which your app called a model. That request is your
|
|
3
|
+
// AI's door, with the exact body it takes and the sign-in it carried, learned from a message you sent
|
|
4
|
+
// yourself rather than guessed from code. What it sees is written to a file only you can read, in the
|
|
5
|
+
// command's own folder on this machine. Nothing here talks to a network.
|
|
6
|
+
"use strict";
|
|
7
|
+
const FILE = process.env.CORTAD_TRACE_FILE;
|
|
8
|
+
if (FILE) {
|
|
9
|
+
try {
|
|
10
|
+
const { AsyncLocalStorage } = require("node:async_hooks");
|
|
11
|
+
const fs = require("node:fs");
|
|
12
|
+
const http = require("node:http");
|
|
13
|
+
const https = require("node:https");
|
|
14
|
+
const als = new AsyncLocalStorage();
|
|
15
|
+
// Said once, so the command knows a message sent to this app can be seen arriving.
|
|
16
|
+
try { fs.appendFileSync(FILE, JSON.stringify({ hello: "node", pid: process.pid }) + "\n", { mode: 0o600 }); } catch { /* the command is gone */ }
|
|
17
|
+
const MAX = 65536;
|
|
18
|
+
// Where models are served, by host, and the paths every OpenAI-shaped or vendor endpoint ends with.
|
|
19
|
+
const MODEL_HOST = /(?:^|\.)(?:openai\.com|anthropic\.com|fireworks\.ai|openrouter\.ai|groq\.com|mistral\.ai|together\.xyz|together\.ai|deepseek\.com|cohere\.ai|cohere\.com|perplexity\.ai|x\.ai|googleapis\.com|openai\.azure\.com|cognitiveservices\.azure\.com|amazonaws\.com|replicate\.com|huggingface\.co|cerebras\.ai|deepinfra\.com|novita\.ai|moonshot\.cn|dashscope\.aliyuncs\.com|bigmodel\.cn)$/i;
|
|
20
|
+
const MODEL_PATH = /\/(?:chat\/completions|completions|responses|messages|embeddings)$|:(?:generateContent|streamGenerateContent)|\/invoke(?:-with-response-stream)?$|\/api\/(?:chat|generate)$/i;
|
|
21
|
+
const isModelCall = (host, path) => {
|
|
22
|
+
const h = String(host || "").replace(/:\d+$/, "");
|
|
23
|
+
const p = String(path || "").split("?")[0];
|
|
24
|
+
if (/googleapis\.com$/i.test(h)) return /generativelanguage|aiplatform/i.test(h) && MODEL_PATH.test(p);
|
|
25
|
+
if (/amazonaws\.com$/i.test(h)) return /^bedrock/i.test(h);
|
|
26
|
+
return MODEL_HOST.test(h) ? true : MODEL_PATH.test(p) && /\/v\d|\/api\//.test(p);
|
|
27
|
+
};
|
|
28
|
+
const said = new Set();
|
|
29
|
+
const note = (ctx, sent) => {
|
|
30
|
+
if (!ctx || ctx.noted) return;
|
|
31
|
+
ctx.noted = true;
|
|
32
|
+
const key = `${ctx.method} ${ctx.path.split("?")[0]}`;
|
|
33
|
+
if (said.has(key)) return;
|
|
34
|
+
said.add(key);
|
|
35
|
+
const record = { at: Date.now(), method: ctx.method, path: ctx.path, headers: ctx.headers, body: Buffer.concat(ctx.chunks).toString("utf8").slice(0, MAX), sent: String(sent || "").slice(0, MAX) };
|
|
36
|
+
try { fs.appendFileSync(FILE, JSON.stringify(record) + "\n", { mode: 0o600 }); } catch { /* the command is gone */ }
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Inbound: each request is handled inside its own context, and its body is seen as it arrives
|
|
40
|
+
// without reading it, so your own body parser is untouched.
|
|
41
|
+
const emit = http.Server.prototype.emit;
|
|
42
|
+
http.Server.prototype.emit = function (type, req, ...rest) {
|
|
43
|
+
if (type !== "request" || !req || !req.method || /^(?:GET|HEAD|OPTIONS)$/.test(req.method)) return emit.call(this, type, req, ...rest);
|
|
44
|
+
const ctx = { method: req.method, path: req.url || "/", headers: { ...req.headers }, chunks: [], size: 0, noted: false };
|
|
45
|
+
const push = req.push;
|
|
46
|
+
req.push = function (chunk, encoding) {
|
|
47
|
+
if (chunk && ctx.size < MAX) { const b = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding); ctx.chunks.push(b); ctx.size += b.length; }
|
|
48
|
+
return push.call(this, chunk, encoding);
|
|
49
|
+
};
|
|
50
|
+
// A body parser carries on from the stream's own "end" event, and that event fires from the
|
|
51
|
+
// socket's context, not the request's: every Express app lost its request there. Listeners put
|
|
52
|
+
// on this request run inside it, the way OpenTelemetry binds a request's emitter.
|
|
53
|
+
const bound = new WeakMap();
|
|
54
|
+
for (const name of ["on", "addListener", "once", "prependListener", "prependOnceListener"]) {
|
|
55
|
+
const add = req[name];
|
|
56
|
+
req[name] = function (event, fn) {
|
|
57
|
+
if (typeof fn !== "function") return add.call(this, event, fn);
|
|
58
|
+
const inside = bound.get(fn) || function (...a) { return als.run(ctx, () => fn.apply(this, a)); };
|
|
59
|
+
bound.set(fn, inside);
|
|
60
|
+
return add.call(this, event, inside);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
for (const name of ["off", "removeListener"]) {
|
|
64
|
+
const remove = req[name];
|
|
65
|
+
req[name] = function (event, fn) { return remove.call(this, event, (typeof fn === "function" && bound.get(fn)) || fn); };
|
|
66
|
+
}
|
|
67
|
+
return als.run(ctx, () => emit.call(this, type, req, ...rest));
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Outbound: the model call your app makes while it handles that request.
|
|
71
|
+
const bodyText = (body) => (typeof body === "string" ? body : Buffer.isBuffer(body) ? body.toString("utf8") : body instanceof Uint8Array ? Buffer.from(body).toString("utf8") : "");
|
|
72
|
+
if (typeof globalThis.fetch === "function") {
|
|
73
|
+
const realFetch = globalThis.fetch;
|
|
74
|
+
globalThis.fetch = function (input, init) {
|
|
75
|
+
try {
|
|
76
|
+
const url = new URL(typeof input === "string" ? input : input && input.url ? input.url : String(input));
|
|
77
|
+
if (isModelCall(url.host, url.pathname)) note(als.getStore(), bodyText(init && init.body));
|
|
78
|
+
} catch { /* not a URL we can read */ }
|
|
79
|
+
return realFetch.apply(this, arguments);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
for (const mod of [http, https]) {
|
|
83
|
+
const request = mod.request;
|
|
84
|
+
mod.request = function (...args) {
|
|
85
|
+
const req = request.apply(this, args);
|
|
86
|
+
try {
|
|
87
|
+
const o = typeof args[0] === "string" || args[0] instanceof URL ? new URL(String(args[0])) : args[0] || {};
|
|
88
|
+
const host = o.host || o.hostname;
|
|
89
|
+
const path = o.pathname ? o.pathname : o.path;
|
|
90
|
+
if (isModelCall(host, path)) {
|
|
91
|
+
const ctx = als.getStore();
|
|
92
|
+
const parts = [];
|
|
93
|
+
const write = req.write;
|
|
94
|
+
const end = req.end;
|
|
95
|
+
req.write = function (chunk, ...r) { if (chunk && typeof chunk !== "function") parts.push(Buffer.from(chunk)); return write.call(this, chunk, ...r); };
|
|
96
|
+
req.end = function (chunk, ...r) { if (chunk && typeof chunk !== "function") parts.push(Buffer.from(chunk)); note(ctx, Buffer.concat(parts).toString("utf8")); return end.call(this, chunk, ...r); };
|
|
97
|
+
}
|
|
98
|
+
} catch { /* leave the request alone */ }
|
|
99
|
+
return req;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
} catch { /* an app must never fail to start because of this file */ }
|
|
103
|
+
}
|