@pinta-ai/pinta-gemini 0.4.1 → 0.6.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/index.js +16 -709
- package/dist/index.mjs +16 -0
- package/package.json +17 -2
package/dist/index.js
CHANGED
|
@@ -1,709 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let value = line.slice(idx + 1).trim();
|
|
18
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
19
|
-
value = value.slice(1, -1);
|
|
20
|
-
}
|
|
21
|
-
if (key) out[key] = value;
|
|
22
|
-
}
|
|
23
|
-
return out;
|
|
24
|
-
}
|
|
25
|
-
function loadEnvFile(filePath = envFilePath()) {
|
|
26
|
-
let content;
|
|
27
|
-
try {
|
|
28
|
-
content = fs.readFileSync(filePath, "utf-8");
|
|
29
|
-
} catch {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
for (const [key, value] of Object.entries(parseEnvFile(content))) {
|
|
33
|
-
if (process.env[key] === void 0) process.env[key] = value;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// src/core/config.ts
|
|
38
|
-
import os2 from "os";
|
|
39
|
-
import path2 from "path";
|
|
40
|
-
function geminiHome() {
|
|
41
|
-
return process.env.GEMINI_HOME || path2.join(os2.homedir(), ".gemini");
|
|
42
|
-
}
|
|
43
|
-
function parseHeaders(raw) {
|
|
44
|
-
const out = {};
|
|
45
|
-
if (!raw) return out;
|
|
46
|
-
for (const pair of raw.split(",")) {
|
|
47
|
-
const [k, ...rest] = pair.split("=");
|
|
48
|
-
if (k && rest.length) out[k.trim()] = rest.join("=").trim();
|
|
49
|
-
}
|
|
50
|
-
return out;
|
|
51
|
-
}
|
|
52
|
-
function resolveEndpoint() {
|
|
53
|
-
const traces = process.env.GEMINI_PLUGIN_OPTION_ENDPOINT || process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;
|
|
54
|
-
if (traces) return traces.replace(/\/+$/, "");
|
|
55
|
-
const base = process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
56
|
-
if (base) return base.replace(/\/+$/, "") + "/v1/traces";
|
|
57
|
-
return void 0;
|
|
58
|
-
}
|
|
59
|
-
function resolveHeaders() {
|
|
60
|
-
const headers = parseHeaders(process.env.GEMINI_PLUGIN_OPTION_HEADERS || process.env.OTEL_EXPORTER_OTLP_HEADERS);
|
|
61
|
-
const apiKey = process.env.GEMINI_PLUGIN_OPTION_API_KEY;
|
|
62
|
-
if (apiKey && !headers["x-pinta-relay-token"]) headers["x-pinta-relay-token"] = apiKey;
|
|
63
|
-
return headers;
|
|
64
|
-
}
|
|
65
|
-
function loadConfig() {
|
|
66
|
-
const pluginData = process.env.GEMINI_PLUGIN_DATA || path2.join(geminiHome(), "pinta-gemini-data");
|
|
67
|
-
if (!process.env.PINTA_RELAY_TOKEN && process.env.GEMINI_PLUGIN_OPTION_API_KEY) {
|
|
68
|
-
process.env.PINTA_RELAY_TOKEN = process.env.GEMINI_PLUGIN_OPTION_API_KEY;
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
pluginData,
|
|
72
|
-
tracePath: path2.join(pluginData, "trace.json"),
|
|
73
|
-
endpoint: resolveEndpoint(),
|
|
74
|
-
headers: resolveHeaders(),
|
|
75
|
-
guardEndpoint: process.env.PINTA_GUARD_ENDPOINT
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// src/core/agent.ts
|
|
80
|
-
function parseInvocation(argv = process.argv) {
|
|
81
|
-
const get = (name) => {
|
|
82
|
-
const i = argv.indexOf(name);
|
|
83
|
-
return i >= 0 ? argv[i + 1] : void 0;
|
|
84
|
-
};
|
|
85
|
-
return { agent: get("--agent") || "gemini", event: get("--event") };
|
|
86
|
-
}
|
|
87
|
-
function antigravityProduct(ev) {
|
|
88
|
-
const tp = ev["transcriptPath"];
|
|
89
|
-
if (typeof tp !== "string") return void 0;
|
|
90
|
-
if (tp.includes("/antigravity-cli/brain/")) return "agy";
|
|
91
|
-
if (tp.includes("/antigravity/brain/")) return "antigravity2";
|
|
92
|
-
return void 0;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// src/core/types.ts
|
|
96
|
-
var isGemini = (agent) => agent === "gemini";
|
|
97
|
-
function identity(agent) {
|
|
98
|
-
return isGemini(agent) ? { prefix: "gemini", ingest: "gemini", service: "gemini-cli" } : { prefix: "antigravity", ingest: "antigravity", service: "antigravity-cli" };
|
|
99
|
-
}
|
|
100
|
-
function gateEvent(agent) {
|
|
101
|
-
return isGemini(agent) ? "BeforeTool" : "PreToolUse";
|
|
102
|
-
}
|
|
103
|
-
var SKIP_HOOKS = /* @__PURE__ */ new Set(["AfterModel"]);
|
|
104
|
-
var isSkippedHook = (hook) => SKIP_HOOKS.has(hook);
|
|
105
|
-
|
|
106
|
-
// src/core/normalize.ts
|
|
107
|
-
function normalize(agent, event, ev) {
|
|
108
|
-
if (isGemini(agent)) {
|
|
109
|
-
return {
|
|
110
|
-
hook: event || ev["hook_event_name"] || "unknown",
|
|
111
|
-
session_id: asString(ev["session_id"]),
|
|
112
|
-
cwd: asString(ev["cwd"]),
|
|
113
|
-
tool_name: asString(ev["tool_name"]),
|
|
114
|
-
tool_input: ev["tool_input"]
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
const workspacePaths = ev["workspacePaths"];
|
|
118
|
-
const toolCall = ev["toolCall"];
|
|
119
|
-
return {
|
|
120
|
-
hook: event || "unknown",
|
|
121
|
-
session_id: asString(ev["conversationId"]),
|
|
122
|
-
cwd: Array.isArray(workspacePaths) ? asString(workspacePaths[0]) : void 0,
|
|
123
|
-
tool_name: toolCall?.name,
|
|
124
|
-
tool_input: toolCall?.args
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
function asString(v) {
|
|
128
|
-
return typeof v === "string" ? v : void 0;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// src/core/guard.ts
|
|
132
|
-
var TIMEOUT_MS = 50;
|
|
133
|
-
var GUARD_UA = "pinta-gemini/0.4.1";
|
|
134
|
-
function shellCommandText(toolInput) {
|
|
135
|
-
if (!toolInput || typeof toolInput !== "object" || Array.isArray(toolInput)) return void 0;
|
|
136
|
-
const o = toolInput;
|
|
137
|
-
const v = o["command"] ?? o["CommandLine"];
|
|
138
|
-
return typeof v === "string" ? v : void 0;
|
|
139
|
-
}
|
|
140
|
-
function sleep(ms) {
|
|
141
|
-
return new Promise(
|
|
142
|
-
(_, reject) => setTimeout(() => {
|
|
143
|
-
const err = new Error("Guard request timed out");
|
|
144
|
-
err.name = "TimeoutError";
|
|
145
|
-
reject(err);
|
|
146
|
-
}, ms)
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
async function evaluateGuard(input, endpoint, relayToken) {
|
|
150
|
-
if (!endpoint) return null;
|
|
151
|
-
if (process.env.PINTA_GUARD_DISABLED === "1") return null;
|
|
152
|
-
const start = Date.now();
|
|
153
|
-
try {
|
|
154
|
-
const res = await Promise.race([
|
|
155
|
-
fetch(endpoint, {
|
|
156
|
-
method: "POST",
|
|
157
|
-
headers: {
|
|
158
|
-
"content-type": "application/json",
|
|
159
|
-
"user-agent": GUARD_UA,
|
|
160
|
-
"x-pinta-relay-token": relayToken ?? process.env.PINTA_RELAY_TOKEN ?? ""
|
|
161
|
-
},
|
|
162
|
-
body: JSON.stringify({ input })
|
|
163
|
-
}),
|
|
164
|
-
sleep(TIMEOUT_MS)
|
|
165
|
-
]);
|
|
166
|
-
if (res.status !== 200) {
|
|
167
|
-
return { decision: "ALLOW", reason: null, userMessage: null, durationMs: Date.now() - start, failOpenReason: "error" };
|
|
168
|
-
}
|
|
169
|
-
const body = await res.json();
|
|
170
|
-
return {
|
|
171
|
-
decision: body.decision,
|
|
172
|
-
reason: body.reason,
|
|
173
|
-
userMessage: body.userMessage ?? null,
|
|
174
|
-
durationMs: body.durationMs ?? Date.now() - start
|
|
175
|
-
};
|
|
176
|
-
} catch (err) {
|
|
177
|
-
const reason = err.name === "TimeoutError" ? "timeout" : "error";
|
|
178
|
-
return { decision: "ALLOW", reason: null, userMessage: null, durationMs: Date.now() - start, failOpenReason: reason };
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// src/core/retry-queue.ts
|
|
183
|
-
import fs2 from "fs";
|
|
184
|
-
import path3 from "path";
|
|
185
|
-
var MAX_ENTRIES = 1e3;
|
|
186
|
-
var LOCK_TIMEOUT_MS = 50;
|
|
187
|
-
var LOCK_POLL_MS = 5;
|
|
188
|
-
var RetryQueue = class {
|
|
189
|
-
filePath;
|
|
190
|
-
lockPath;
|
|
191
|
-
constructor(pluginData) {
|
|
192
|
-
this.filePath = path3.join(pluginData, "failed-spans.jsonl");
|
|
193
|
-
this.lockPath = this.filePath + ".lock";
|
|
194
|
-
}
|
|
195
|
-
/** Append a single payload. Best-effort: any IO error is swallowed (logged to stderr). */
|
|
196
|
-
enqueue(payload) {
|
|
197
|
-
try {
|
|
198
|
-
fs2.mkdirSync(path3.dirname(this.filePath), { recursive: true });
|
|
199
|
-
const line = JSON.stringify({ savedAt: (/* @__PURE__ */ new Date()).toISOString(), payload }) + "\n";
|
|
200
|
-
fs2.appendFileSync(this.filePath, line);
|
|
201
|
-
this.trim();
|
|
202
|
-
} catch (err) {
|
|
203
|
-
process.stderr.write(`[pinta-gemini] retry-queue enqueue failed: ${err}
|
|
204
|
-
`);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Read all entries oldest-first. Returns [] if the file does not exist or is unreadable.
|
|
209
|
-
* Does NOT delete the file — callers handle persistence via `rewrite`.
|
|
210
|
-
*/
|
|
211
|
-
readAll() {
|
|
212
|
-
try {
|
|
213
|
-
const raw = fs2.readFileSync(this.filePath, "utf-8");
|
|
214
|
-
const out = [];
|
|
215
|
-
for (const line of raw.split("\n")) {
|
|
216
|
-
if (!line.trim()) continue;
|
|
217
|
-
try {
|
|
218
|
-
out.push(JSON.parse(line));
|
|
219
|
-
} catch {
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return out;
|
|
223
|
-
} catch {
|
|
224
|
-
return [];
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/** Replace the queue with the given entries (or delete the file when empty). */
|
|
228
|
-
rewrite(entries) {
|
|
229
|
-
try {
|
|
230
|
-
if (entries.length === 0) {
|
|
231
|
-
if (fs2.existsSync(this.filePath)) fs2.unlinkSync(this.filePath);
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
fs2.mkdirSync(path3.dirname(this.filePath), { recursive: true });
|
|
235
|
-
fs2.writeFileSync(this.filePath, entries.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
236
|
-
} catch (err) {
|
|
237
|
-
process.stderr.write(`[pinta-gemini] retry-queue rewrite failed: ${err}
|
|
238
|
-
`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Try to acquire the lock for ~LOCK_TIMEOUT_MS. Returns true on success.
|
|
243
|
-
* Caller MUST call `release()` if true is returned.
|
|
244
|
-
*/
|
|
245
|
-
tryAcquireLock() {
|
|
246
|
-
const start = Date.now();
|
|
247
|
-
fs2.mkdirSync(path3.dirname(this.lockPath), { recursive: true });
|
|
248
|
-
while (Date.now() - start < LOCK_TIMEOUT_MS) {
|
|
249
|
-
try {
|
|
250
|
-
const fd = fs2.openSync(this.lockPath, "wx");
|
|
251
|
-
fs2.writeSync(fd, String(process.pid));
|
|
252
|
-
fs2.closeSync(fd);
|
|
253
|
-
return true;
|
|
254
|
-
} catch (err) {
|
|
255
|
-
if (err?.code !== "EEXIST") {
|
|
256
|
-
process.stderr.write(`[pinta-gemini] retry-queue lock open failed: ${err}
|
|
257
|
-
`);
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
try {
|
|
261
|
-
const st = fs2.statSync(this.lockPath);
|
|
262
|
-
if (Date.now() - st.mtimeMs > 3e4) {
|
|
263
|
-
fs2.unlinkSync(this.lockPath);
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
} catch {
|
|
267
|
-
}
|
|
268
|
-
const wait = LOCK_POLL_MS;
|
|
269
|
-
const end = Date.now() + wait;
|
|
270
|
-
while (Date.now() < end) {
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
return false;
|
|
275
|
-
}
|
|
276
|
-
release() {
|
|
277
|
-
try {
|
|
278
|
-
fs2.unlinkSync(this.lockPath);
|
|
279
|
-
} catch {
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
trim() {
|
|
283
|
-
const entries = this.readAll();
|
|
284
|
-
if (entries.length <= MAX_ENTRIES) return;
|
|
285
|
-
const drop = entries.length - MAX_ENTRIES;
|
|
286
|
-
process.stderr.write(`[pinta-gemini] retry-queue full, dropping ${drop} oldest entries
|
|
287
|
-
`);
|
|
288
|
-
this.rewrite(entries.slice(drop));
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
// src/core/otlp.ts
|
|
293
|
-
import crypto from "crypto";
|
|
294
|
-
import os3 from "os";
|
|
295
|
-
|
|
296
|
-
// src/core/redact.ts
|
|
297
|
-
var MAX_BYTES = 102400;
|
|
298
|
-
function truncate(input) {
|
|
299
|
-
const buf = Buffer.from(input, "utf-8");
|
|
300
|
-
if (buf.length <= MAX_BYTES) return input;
|
|
301
|
-
const head = buf.subarray(0, MAX_BYTES).toString("utf-8");
|
|
302
|
-
return `${head}\u2026[TRUNCATED:${buf.length}]`;
|
|
303
|
-
}
|
|
304
|
-
var PATTERNS = [
|
|
305
|
-
{ type: "aws_access_key", regex: /AKIA[0-9A-Z]{16}/g },
|
|
306
|
-
{
|
|
307
|
-
type: "aws_secret_key",
|
|
308
|
-
// Context word `aws_secret`/`AWS_SECRET` (with optional separator) followed
|
|
309
|
-
// by an assignment-ish character then a 40-char base64-ish blob.
|
|
310
|
-
regex: /(?:aws[_-]?secret(?:[_-]?(?:access)?[_-]?key)?)\s*[:=]\s*["']?([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])/gi,
|
|
311
|
-
captureGroup: 1
|
|
312
|
-
},
|
|
313
|
-
{
|
|
314
|
-
type: "gcp_service_account",
|
|
315
|
-
// Whole JSON blob starting with the service-account discriminator.
|
|
316
|
-
regex: /\{[\s\S]{0,200}?"type"\s*:\s*"service_account"[\s\S]*?\}/g
|
|
317
|
-
},
|
|
318
|
-
{ type: "github_token", regex: /gh[pousr]_[A-Za-z0-9]{36,}/g },
|
|
319
|
-
{ type: "gitlab_token", regex: /glpat-[A-Za-z0-9_-]{20}/g },
|
|
320
|
-
{ type: "slack_token", regex: /xox[abrsp]-[0-9A-Za-z-]{10,}/g },
|
|
321
|
-
{ type: "openai_key", regex: /sk-(?:proj-)?[A-Za-z0-9_-]{40,}/g },
|
|
322
|
-
{ type: "anthropic_key", regex: /sk-ant-[A-Za-z0-9_-]{50,}/g },
|
|
323
|
-
{ type: "stripe_key", regex: /(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{20,}/g },
|
|
324
|
-
{
|
|
325
|
-
type: "jwt",
|
|
326
|
-
regex: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
type: "private_key_block",
|
|
330
|
-
regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
|
|
331
|
-
},
|
|
332
|
-
{ type: "bearer_token", regex: /bearer\s+([A-Za-z0-9._~+/=-]{12,})/gi, captureGroup: 1 },
|
|
333
|
-
{ type: "basic_auth", regex: /basic\s+([A-Za-z0-9+/=]{12,})/gi, captureGroup: 1 },
|
|
334
|
-
{
|
|
335
|
-
type: "db_url_password",
|
|
336
|
-
regex: /\b(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|redis):\/\/[^:\s/]+:([^@\s]+)@/gi,
|
|
337
|
-
captureGroup: 1
|
|
338
|
-
},
|
|
339
|
-
{
|
|
340
|
-
type: "cli_password_flag",
|
|
341
|
-
regex: /(?:--password|--pass|--pwd)[=\s]([^\s'"]+)/g,
|
|
342
|
-
captureGroup: 1
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
type: "cli_password_short",
|
|
346
|
-
// mysql -p<pass>; only on bash context.
|
|
347
|
-
regex: /\s-p([^\s'"]+)/g,
|
|
348
|
-
captureGroup: 1,
|
|
349
|
-
requireContext: "bash"
|
|
350
|
-
},
|
|
351
|
-
{
|
|
352
|
-
type: "env_var_secret",
|
|
353
|
-
// Known false positive: trailing `[A-Z0-9_]*` is greedy, so names like
|
|
354
|
-
// `OPENAI_API_KEY_DESCRIPTION=Used` still match. Acceptable for Bronze.
|
|
355
|
-
regex: /^(?:export\s+)?([A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PWD|API_KEY)[A-Z0-9_]*)\s*=\s*["']?([^\s"'\n]+)/gm,
|
|
356
|
-
captureGroup: 2
|
|
357
|
-
}
|
|
358
|
-
];
|
|
359
|
-
function collectMatches(input, opts) {
|
|
360
|
-
const out = [];
|
|
361
|
-
for (const pattern of PATTERNS) {
|
|
362
|
-
if (pattern.requireContext && pattern.requireContext !== opts.context) continue;
|
|
363
|
-
const re = new RegExp(pattern.regex.source, pattern.regex.flags);
|
|
364
|
-
let m;
|
|
365
|
-
while ((m = re.exec(input)) !== null) {
|
|
366
|
-
const cg = pattern.captureGroup ?? 0;
|
|
367
|
-
const captured = m[cg];
|
|
368
|
-
if (captured === void 0) {
|
|
369
|
-
if (m.index === re.lastIndex) re.lastIndex++;
|
|
370
|
-
continue;
|
|
371
|
-
}
|
|
372
|
-
const start = m.index;
|
|
373
|
-
const end = m.index + m[0].length;
|
|
374
|
-
const replaceStart = start + m[0].indexOf(captured);
|
|
375
|
-
const replaceEnd = replaceStart + captured.length;
|
|
376
|
-
out.push({ start, end, replaceStart, replaceEnd, type: pattern.type });
|
|
377
|
-
if (m.index === re.lastIndex) re.lastIndex++;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
return out;
|
|
381
|
-
}
|
|
382
|
-
function resolveOverlaps(matches) {
|
|
383
|
-
const sorted = [...matches].sort((a, b) => {
|
|
384
|
-
if (a.start !== b.start) return a.start - b.start;
|
|
385
|
-
return b.end - b.start - (a.end - a.start);
|
|
386
|
-
});
|
|
387
|
-
const kept = [];
|
|
388
|
-
let lastEnd = -1;
|
|
389
|
-
for (const m of sorted) {
|
|
390
|
-
if (m.start < lastEnd) continue;
|
|
391
|
-
kept.push(m);
|
|
392
|
-
lastEnd = m.end;
|
|
393
|
-
}
|
|
394
|
-
return kept;
|
|
395
|
-
}
|
|
396
|
-
function applyMatches(input, matches) {
|
|
397
|
-
const sorted = [...matches].sort((a, b) => b.replaceStart - a.replaceStart);
|
|
398
|
-
let out = input;
|
|
399
|
-
for (const m of sorted) {
|
|
400
|
-
out = out.slice(0, m.replaceStart) + `[REDACTED:${m.type}]` + out.slice(m.replaceEnd);
|
|
401
|
-
}
|
|
402
|
-
return out;
|
|
403
|
-
}
|
|
404
|
-
function redact(input, opts = {}) {
|
|
405
|
-
if (input.length === 0) return input;
|
|
406
|
-
const all = collectMatches(input, opts);
|
|
407
|
-
if (all.length === 0) return input;
|
|
408
|
-
const kept = resolveOverlaps(all);
|
|
409
|
-
return applyMatches(input, kept);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// src/core/otlp.ts
|
|
413
|
-
var PLUGIN_VERSION = "0.4.1";
|
|
414
|
-
var CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
415
|
-
function ulidToTraceId(ulid) {
|
|
416
|
-
if (ulid.length !== 26) throw new Error(`ulidToTraceId: expected 26 chars, got ${ulid.length}`);
|
|
417
|
-
let n = 0n;
|
|
418
|
-
for (const ch of ulid) {
|
|
419
|
-
const idx = CROCKFORD.indexOf(ch);
|
|
420
|
-
if (idx < 0) throw new Error(`ulidToTraceId: invalid Crockford char "${ch}"`);
|
|
421
|
-
n = n << 5n | BigInt(idx);
|
|
422
|
-
}
|
|
423
|
-
n &= (1n << 128n) - 1n;
|
|
424
|
-
return n.toString(16).padStart(32, "0");
|
|
425
|
-
}
|
|
426
|
-
function newSpanId() {
|
|
427
|
-
return crypto.randomBytes(8).toString("hex");
|
|
428
|
-
}
|
|
429
|
-
function snakeCase(s) {
|
|
430
|
-
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase();
|
|
431
|
-
}
|
|
432
|
-
function maybeRedactString(prefix, key, raw) {
|
|
433
|
-
const truncated = truncate(raw);
|
|
434
|
-
const skip = /* @__PURE__ */ new Set([`${prefix}.hook`, `${prefix}.agent`, `${prefix}.tool_name`, `${prefix}.session_id`, `${prefix}.transcript_path`, `${prefix}.transcriptPath`, `${prefix}.cwd`]);
|
|
435
|
-
if (skip.has(key)) return truncated;
|
|
436
|
-
const context = key === `${prefix}.tool_input` || key === `${prefix}.tool_response` || key === `${prefix}.toolCall` ? "bash" : void 0;
|
|
437
|
-
return redact(truncated, { context });
|
|
438
|
-
}
|
|
439
|
-
function toOtlpValue(prefix, key, v) {
|
|
440
|
-
if (v === null || v === void 0) return null;
|
|
441
|
-
switch (typeof v) {
|
|
442
|
-
case "string":
|
|
443
|
-
return { stringValue: maybeRedactString(prefix, key, v) };
|
|
444
|
-
case "boolean":
|
|
445
|
-
return { boolValue: v };
|
|
446
|
-
case "number":
|
|
447
|
-
return Number.isInteger(v) ? { intValue: v } : { doubleValue: v };
|
|
448
|
-
case "object":
|
|
449
|
-
try {
|
|
450
|
-
return { stringValue: maybeRedactString(prefix, key, JSON.stringify(v)) };
|
|
451
|
-
} catch {
|
|
452
|
-
return { stringValue: maybeRedactString(prefix, key, String(v)) };
|
|
453
|
-
}
|
|
454
|
-
default:
|
|
455
|
-
return { stringValue: maybeRedactString(prefix, key, String(v)) };
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
function resourceAttrs(serviceName) {
|
|
459
|
-
return [
|
|
460
|
-
{ key: "service.name", value: { stringValue: serviceName } },
|
|
461
|
-
{ key: "telemetry.sdk.name", value: { stringValue: "pinta-gemini" } },
|
|
462
|
-
{ key: "telemetry.sdk.language", value: { stringValue: "nodejs" } },
|
|
463
|
-
{ key: "telemetry.sdk.version", value: { stringValue: PLUGIN_VERSION } },
|
|
464
|
-
{ key: "process.pid", value: { intValue: process.pid } },
|
|
465
|
-
{ key: "process.owner", value: { stringValue: os3.userInfo().username } },
|
|
466
|
-
{ key: "host.name", value: { stringValue: os3.hostname() } },
|
|
467
|
-
{ key: "host.arch", value: { stringValue: os3.arch() } }
|
|
468
|
-
];
|
|
469
|
-
}
|
|
470
|
-
function buildOtlpPayload(args) {
|
|
471
|
-
const id = identity(args.agent);
|
|
472
|
-
const ts = args.now ?? Date.now();
|
|
473
|
-
const tsNano = (BigInt(ts) * 1000000n).toString();
|
|
474
|
-
const attrs = [
|
|
475
|
-
{ key: "ingest.type", value: { stringValue: id.ingest } },
|
|
476
|
-
{ key: `${id.prefix}.hook`, value: { stringValue: args.canonical.hook } },
|
|
477
|
-
{ key: `${id.prefix}.agent`, value: { stringValue: args.agent } }
|
|
478
|
-
];
|
|
479
|
-
if (args.product) attrs.push({ key: `${id.prefix}.product`, value: { stringValue: args.product } });
|
|
480
|
-
for (const [k, v] of Object.entries(args.event)) {
|
|
481
|
-
const key = `${id.prefix}.${k}`;
|
|
482
|
-
const value = toOtlpValue(id.prefix, key, v);
|
|
483
|
-
if (value !== null) attrs.push({ key, value });
|
|
484
|
-
}
|
|
485
|
-
const have = new Set(attrs.map((a) => a.key));
|
|
486
|
-
for (const [field, val] of [
|
|
487
|
-
["session_id", args.canonical.session_id],
|
|
488
|
-
["cwd", args.canonical.cwd],
|
|
489
|
-
["tool_name", args.canonical.tool_name]
|
|
490
|
-
]) {
|
|
491
|
-
const key = `${id.prefix}.${field}`;
|
|
492
|
-
if (val != null && !have.has(key)) attrs.push({ key, value: { stringValue: maybeRedactString(id.prefix, key, String(val)) } });
|
|
493
|
-
}
|
|
494
|
-
if (args.guard) {
|
|
495
|
-
attrs.push(
|
|
496
|
-
{ key: "pinta.guard.decision", value: { stringValue: args.guard.decision.toLowerCase() } },
|
|
497
|
-
{ key: "pinta.guard.duration_ms", value: { intValue: args.guard.durationMs } }
|
|
498
|
-
);
|
|
499
|
-
if (args.guard.reason) attrs.push({ key: "pinta.guard.matched_rule", value: { stringValue: args.guard.reason } });
|
|
500
|
-
if (args.guard.failOpenReason) attrs.push({ key: "pinta.guard.fail_open_reason", value: { stringValue: args.guard.failOpenReason } });
|
|
501
|
-
}
|
|
502
|
-
const span = {
|
|
503
|
-
traceId: ulidToTraceId(args.traceId),
|
|
504
|
-
spanId: newSpanId(),
|
|
505
|
-
name: `${id.ingest}.${snakeCase(args.canonical.hook)}`,
|
|
506
|
-
kind: 1,
|
|
507
|
-
startTimeUnixNano: tsNano,
|
|
508
|
-
endTimeUnixNano: tsNano,
|
|
509
|
-
attributes: attrs
|
|
510
|
-
};
|
|
511
|
-
return {
|
|
512
|
-
resourceSpans: [{ resource: { attributes: resourceAttrs(id.service) }, scopeSpans: [{ scope: { name: "pinta-gemini", version: PLUGIN_VERSION }, spans: [span] }] }]
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
function mergeBatch(payloads) {
|
|
516
|
-
const out = [];
|
|
517
|
-
for (const p of payloads) out.push(...p.resourceSpans);
|
|
518
|
-
return { resourceSpans: out };
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// src/core/transport.ts
|
|
522
|
-
var TIMEOUT_MS2 = 5e3;
|
|
523
|
-
var Transport = class {
|
|
524
|
-
constructor(config) {
|
|
525
|
-
this.config = config;
|
|
526
|
-
this.queue = new RetryQueue(config.pluginData);
|
|
527
|
-
}
|
|
528
|
-
queue;
|
|
529
|
-
async send(payload) {
|
|
530
|
-
if (!this.config.endpoint) return;
|
|
531
|
-
const ok = await this.post(payload);
|
|
532
|
-
if (!ok) this.queue.enqueue(payload);
|
|
533
|
-
}
|
|
534
|
-
async flush() {
|
|
535
|
-
if (!this.config.endpoint) return;
|
|
536
|
-
if (!this.queue.tryAcquireLock()) return;
|
|
537
|
-
try {
|
|
538
|
-
const entries = this.queue.readAll();
|
|
539
|
-
if (entries.length === 0) return;
|
|
540
|
-
const ok = await this.post(mergeBatch(entries.map((e) => e.payload)));
|
|
541
|
-
if (ok) this.queue.rewrite([]);
|
|
542
|
-
} finally {
|
|
543
|
-
this.queue.release();
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
async post(payload) {
|
|
547
|
-
const endpoint = this.config.endpoint;
|
|
548
|
-
const ctrl = new AbortController();
|
|
549
|
-
const timer = setTimeout(() => ctrl.abort(), TIMEOUT_MS2);
|
|
550
|
-
try {
|
|
551
|
-
const res = await fetch(endpoint, {
|
|
552
|
-
method: "POST",
|
|
553
|
-
headers: { "Content-Type": "application/json", ...this.config.headers },
|
|
554
|
-
body: JSON.stringify(payload),
|
|
555
|
-
signal: ctrl.signal
|
|
556
|
-
});
|
|
557
|
-
if (!res.ok) {
|
|
558
|
-
process.stderr.write(`[pinta-gemini] OTLP POST ${res.status} ${endpoint}
|
|
559
|
-
`);
|
|
560
|
-
return false;
|
|
561
|
-
}
|
|
562
|
-
return true;
|
|
563
|
-
} catch (err) {
|
|
564
|
-
process.stderr.write(`[pinta-gemini] OTLP POST failed: ${err.message ?? String(err)}
|
|
565
|
-
`);
|
|
566
|
-
return false;
|
|
567
|
-
} finally {
|
|
568
|
-
clearTimeout(timer);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
|
|
573
|
-
// src/core/trace.ts
|
|
574
|
-
import fs3 from "fs";
|
|
575
|
-
import path4 from "path";
|
|
576
|
-
import crypto2 from "crypto";
|
|
577
|
-
var CROCKFORD2 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
578
|
-
function generateUlid() {
|
|
579
|
-
const now = Date.now();
|
|
580
|
-
let ts = "";
|
|
581
|
-
let t = now;
|
|
582
|
-
for (let i = 0; i < 10; i++) {
|
|
583
|
-
ts = CROCKFORD2[t & 31] + ts;
|
|
584
|
-
t = Math.floor(t / 32);
|
|
585
|
-
}
|
|
586
|
-
const rand = crypto2.randomBytes(10);
|
|
587
|
-
let r = "";
|
|
588
|
-
for (let i = 0; i < 10; i++) r += CROCKFORD2[rand[i] & 31];
|
|
589
|
-
while (r.length < 16) r += CROCKFORD2[0];
|
|
590
|
-
return ts + r;
|
|
591
|
-
}
|
|
592
|
-
var TraceManager = class {
|
|
593
|
-
tracePath;
|
|
594
|
-
constructor(config) {
|
|
595
|
-
this.tracePath = config.tracePath;
|
|
596
|
-
}
|
|
597
|
-
read() {
|
|
598
|
-
try {
|
|
599
|
-
const data = JSON.parse(fs3.readFileSync(this.tracePath, "utf-8"));
|
|
600
|
-
return data && typeof data === "object" ? data : {};
|
|
601
|
-
} catch {
|
|
602
|
-
return {};
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
write(map) {
|
|
606
|
-
try {
|
|
607
|
-
fs3.mkdirSync(path4.dirname(this.tracePath), { recursive: true });
|
|
608
|
-
fs3.writeFileSync(this.tracePath, JSON.stringify(map));
|
|
609
|
-
} catch {
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
/** Start a fresh trace for this session (turn boundary). */
|
|
613
|
-
newTrace(sessionId) {
|
|
614
|
-
const map = this.read();
|
|
615
|
-
const traceId = generateUlid();
|
|
616
|
-
map[sessionId] = traceId;
|
|
617
|
-
this.write(map);
|
|
618
|
-
return traceId;
|
|
619
|
-
}
|
|
620
|
-
/** Current trace for this session; creates one if absent. */
|
|
621
|
-
currentTrace(sessionId) {
|
|
622
|
-
const map = this.read();
|
|
623
|
-
if (map[sessionId]) return map[sessionId];
|
|
624
|
-
return this.newTrace(sessionId);
|
|
625
|
-
}
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
// src/core/decision.ts
|
|
629
|
-
function formatDecision(agent, event, guard) {
|
|
630
|
-
if (guard && guard.decision === "DENY") {
|
|
631
|
-
const reason = guard.userMessage ?? guard.reason ?? "guard_deny";
|
|
632
|
-
if (isGemini(agent)) return { decision: "deny", reason, systemMessage: guard.userMessage ?? void 0 };
|
|
633
|
-
return { decision: "deny", reason };
|
|
634
|
-
}
|
|
635
|
-
if (!isGemini(agent) && event === "PreToolUse") return { decision: "allow" };
|
|
636
|
-
return {};
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
// src/core/invocation-log.ts
|
|
640
|
-
import fs4 from "fs";
|
|
641
|
-
import path5 from "path";
|
|
642
|
-
function logInvocation(config, rec) {
|
|
643
|
-
if (process.env.PINTA_GEMINI_DEBUG !== "1") return;
|
|
644
|
-
try {
|
|
645
|
-
fs4.mkdirSync(config.pluginData, { recursive: true });
|
|
646
|
-
fs4.appendFileSync(path5.join(config.pluginData, "invocations.jsonl"), JSON.stringify(rec) + "\n");
|
|
647
|
-
} catch {
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// src/index.ts
|
|
652
|
-
loadEnvFile();
|
|
653
|
-
async function readStdin() {
|
|
654
|
-
const chunks = [];
|
|
655
|
-
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
656
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
657
|
-
}
|
|
658
|
-
function isTurnStart(agent, c, ev) {
|
|
659
|
-
if (isGemini(agent)) return c.hook === "BeforeAgent";
|
|
660
|
-
return c.hook === "PreInvocation" && ev["invocationNum"] === 1;
|
|
661
|
-
}
|
|
662
|
-
async function main() {
|
|
663
|
-
const { agent, event } = parseInvocation();
|
|
664
|
-
let out = {};
|
|
665
|
-
let ev = {};
|
|
666
|
-
let c;
|
|
667
|
-
let guard = null;
|
|
668
|
-
const config = loadConfig();
|
|
669
|
-
try {
|
|
670
|
-
ev = JSON.parse(await readStdin() || "{}");
|
|
671
|
-
c = normalize(agent, event, ev);
|
|
672
|
-
if (!isSkippedHook(c.hook)) {
|
|
673
|
-
const transport = new Transport(config);
|
|
674
|
-
await transport.flush();
|
|
675
|
-
const sessionId = c.session_id ?? "unknown";
|
|
676
|
-
const trace = new TraceManager(config);
|
|
677
|
-
const traceId = isTurnStart(agent, c, ev) ? trace.newTrace(sessionId) : trace.currentTrace(sessionId);
|
|
678
|
-
if (c.hook === gateEvent(agent)) {
|
|
679
|
-
const rawToolInput = shellCommandText(c.tool_input) ?? (typeof c.tool_input === "string" ? c.tool_input : JSON.stringify(c.tool_input ?? null));
|
|
680
|
-
guard = await evaluateGuard(
|
|
681
|
-
{ spanId: sessionId, toolName: c.tool_name, toolInput: c.tool_input, rawTextFields: { toolInput: rawToolInput } },
|
|
682
|
-
config.guardEndpoint,
|
|
683
|
-
config.headers["x-pinta-relay-token"]
|
|
684
|
-
);
|
|
685
|
-
}
|
|
686
|
-
const product = isGemini(agent) ? void 0 : antigravityProduct(ev);
|
|
687
|
-
await transport.send(buildOtlpPayload({ agent, canonical: c, event: ev, traceId, guard, product }));
|
|
688
|
-
out = formatDecision(agent, event, guard);
|
|
689
|
-
}
|
|
690
|
-
} catch (e) {
|
|
691
|
-
process.stderr.write(`[pinta-gemini] error: ${e}
|
|
692
|
-
`);
|
|
693
|
-
out = {};
|
|
694
|
-
}
|
|
695
|
-
logInvocation(config, {
|
|
696
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
697
|
-
pid: process.pid,
|
|
698
|
-
agent,
|
|
699
|
-
event,
|
|
700
|
-
argv: process.argv.slice(2),
|
|
701
|
-
received_payload: ev,
|
|
702
|
-
normalized: c ?? null,
|
|
703
|
-
guard,
|
|
704
|
-
decision_returned: out
|
|
705
|
-
});
|
|
706
|
-
process.stdout.write(JSON.stringify(out) + "\n");
|
|
707
|
-
process.exit(0);
|
|
708
|
-
}
|
|
709
|
-
main();
|
|
1
|
+
import { createRequire as __pintaCreateRequire } from 'module'; const require = __pintaCreateRequire(import.meta.url);
|
|
2
|
+
var rt=Object.create;var ie=Object.defineProperty;var nt=Object.getOwnPropertyDescriptor;var it=Object.getOwnPropertyNames;var ot=Object.getPrototypeOf,st=Object.prototype.hasOwnProperty;var p=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var g=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var at=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of it(t))!st.call(e,i)&&i!==r&&ie(e,i,{get:()=>t[i],enumerable:!(n=nt(t,i))||n.enumerable});return e};var T=(e,t,r)=>(r=e!=null?rt(ot(e)):{},at(t||!e||!e.__esModule?ie(r,"default",{value:e,enumerable:!0}):r,e));var C=g(c=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});c.PATTERNS=c.MAX_BYTES=void 0;c.truncate=ut;c.collectMatches=oe;c.resolveOverlaps=se;c.applyMatches=ae;c.redact=ct;c.MAX_BYTES=102400;function ut(e){let t=Buffer.from(e,"utf-8");return t.length<=c.MAX_BYTES?e:`${t.subarray(0,c.MAX_BYTES).toString("utf-8")}\u2026[TRUNCATED:${t.length}]`}c.PATTERNS=[{type:"aws_access_key",regex:/AKIA[0-9A-Z]{16}/g},{type:"aws_secret_key",regex:/(?:aws[_-]?secret(?:[_-]?(?:access)?[_-]?key)?)\s*[:=]\s*["']?([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])/gi,captureGroup:1},{type:"gcp_service_account",regex:/\{[\s\S]{0,200}?"type"\s*:\s*"service_account"[\s\S]*?\}/g},{type:"github_token",regex:/gh[pousr]_[A-Za-z0-9]{36,}/g},{type:"gitlab_token",regex:/glpat-[A-Za-z0-9_-]{20}/g},{type:"slack_token",regex:/xox[abrsp]-[0-9A-Za-z-]{10,}/g},{type:"openai_key",regex:/sk-(?:proj-)?[A-Za-z0-9_-]{40,}/g},{type:"anthropic_key",regex:/sk-ant-[A-Za-z0-9_-]{50,}/g},{type:"stripe_key",regex:/(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{20,}/g},{type:"jwt",regex:/eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g},{type:"private_key_block",regex:/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g},{type:"bearer_token",regex:/bearer\s+([A-Za-z0-9._~+/=-]{12,})/gi,captureGroup:1},{type:"basic_auth",regex:/basic\s+([A-Za-z0-9+/=]{12,})/gi,captureGroup:1},{type:"db_url_password",regex:/\b(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|redis):\/\/[^:\s/]+:([^@\s]+)@/gi,captureGroup:1},{type:"cli_password_flag",regex:/(?:--password|--pass|--pwd)[=\s]([^\s'"]+)/g,captureGroup:1},{type:"cli_password_short",regex:/\s-p([^\s'"]+)/g,captureGroup:1,requireContext:"bash"},{type:"env_var_secret",regex:/^(?:export\s+)?([A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PWD|API_KEY)[A-Z0-9_]*)\s*=\s*["']?([^\s"'\n]+)/gm,captureGroup:2}];function oe(e,t){let r=[];for(let n of c.PATTERNS){if(n.requireContext&&n.requireContext!==t.context)continue;let i=new RegExp(n.regex.source,n.regex.flags),s;for(;(s=i.exec(e))!==null;){let a=n.captureGroup??0,u=s[a];if(u===void 0){s.index===i.lastIndex&&i.lastIndex++;continue}let f=s.index,x=s.index+s[0].length,M=f+s[0].indexOf(u),q=M+u.length;r.push({start:f,end:x,replaceStart:M,replaceEnd:q,type:n.type}),s.index===i.lastIndex&&i.lastIndex++}}return r}function se(e){let t=[...e].sort((i,s)=>i.start!==s.start?i.start-s.start:s.end-s.start-(i.end-i.start)),r=[],n=-1;for(let i of t)i.start<n||(r.push(i),n=i.end);return r}function ae(e,t){let r=[...t].sort((i,s)=>s.replaceStart-i.replaceStart),n=e;for(let i of r)n=n.slice(0,i.replaceStart)+`[REDACTED:${i.type}]`+n.slice(i.replaceEnd);return n}function ct(e,t={}){if(e.length===0)return e;let r=oe(e,t);if(r.length===0)return e;let n=se(r);return ae(e,n)}});var $=g(d=>{"use strict";var lt=d&&d.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(d,"__esModule",{value:!0});d.ulidToTraceId=le;d.newSpanId=de;d.snakeCase=ft;d.toOtlpValue=pe;d.attrsFromRecord=gt;d.guardAttrs=fe;d.clientAttrs=ge;d.buildPayload=yt;d.mergeBatch=ht;var dt=lt(p("crypto")),ue=C(),pt="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function le(e){if(e.length!==26)throw new Error(`ulidToTraceId: expected 26 chars, got ${e.length}`);let t=0n;for(let n of e){let i=pt.indexOf(n);if(i<0)throw new Error(`ulidToTraceId: invalid Crockford char "${n}"`);t=t<<5n|BigInt(i)}let r=(1n<<128n)-1n;return t&=r,t.toString(16).padStart(32,"0")}function de(){return dt.default.randomBytes(8).toString("hex")}function ft(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()}var ce=new Set;function R(e,t,r){let n=(0,ue.truncate)(t);if((r.skipRedactKeys??ce).has(e))return n;let i=(r.bashContextKeys??ce).has(e)?"bash":void 0;return(0,ue.redact)(n,{context:i})}function pe(e,t,r={}){if(t==null)return null;switch(typeof t){case"string":return{stringValue:R(e,t,r)};case"boolean":return{boolValue:t};case"number":return Number.isInteger(t)?{intValue:t}:{doubleValue:t};case"object":try{return{stringValue:R(e,JSON.stringify(t),r)}}catch{return{stringValue:R(e,String(t),r)}}default:return{stringValue:R(e,String(t),r)}}}function gt(e,t,r={}){let n=[];for(let[i,s]of Object.entries(e)){let a=`${t}.${i}`,u=pe(a,s,r);u!==null&&n.push({key:a,value:u})}return n}function fe(e){let t=[{key:"pinta.guard.decision",value:{stringValue:e.decision.toLowerCase()}},{key:"pinta.guard.duration_ms",value:{intValue:e.durationMs}}];return e.reason&&t.push({key:"pinta.guard.matched_rule",value:{stringValue:e.reason}}),e.failOpenReason&&t.push({key:"pinta.guard.fail_open_reason",value:{stringValue:e.failOpenReason}}),t}function ge(e){return[{key:"pinta.client.rtt_ms",value:Number.isInteger(e.rttMs)?{intValue:e.rttMs}:{doubleValue:e.rttMs}},{key:"pinta.client.op",value:{stringValue:e.op}}]}function yt(e){let t=e.now??Date.now(),r=(BigInt(t)*1000000n).toString(),n=[...e.attributes];e.guard&&n.push(...fe(e.guard));let i=e.clientTiming??(e.guard?.clientRttMs!==void 0?{op:"guard",rttMs:e.guard.clientRttMs}:null);i&&n.push(...ge(i));let s={traceId:le(e.traceId),spanId:de(),name:e.spanName,kind:e.spanKind??1,startTimeUnixNano:r,endTimeUnixNano:r,attributes:n};return{resourceSpans:[{resource:{attributes:e.resource},scopeSpans:[{scope:e.scope,spans:[s]}]}]}}function ht(e){let t=[];for(let r of e)t.push(...r.resourceSpans);return{resourceSpans:t}}});var he=g(F=>{"use strict";Object.defineProperty(F,"__esModule",{value:!0});F.evaluateGuard=Pt;var mt=1e4,_t="pinta-core";function ye(e,t){return{decision:"ALLOW",reason:null,userMessage:null,durationMs:e,clientRttMs:e,failOpenReason:t}}function vt(e){return new Promise((t,r)=>setTimeout(()=>{let n=new Error("Guard request timed out");n.name="TimeoutError",r(n)},e))}async function Pt(e,t,r={}){if(!t||r.disabled)return null;let n=r.timeoutMs??mt,i=Date.now();try{let s=await Promise.race([fetch(t,{method:"POST",headers:{"content-type":"application/json","user-agent":r.userAgent??_t,"x-pinta-relay-token":r.token??""},body:JSON.stringify({input:e})}),vt(n)]);if(s.status!==200)return ye(Date.now()-i,"error");let a=await s.json(),u=Date.now()-i;return{decision:a.decision,reason:a.reason,userMessage:a.userMessage??null,durationMs:a.durationMs??u,clientRttMs:u}}catch(s){let a=s.name==="TimeoutError"?"timeout":"error";return ye(Date.now()-i,a)}}});var U=g(_=>{"use strict";var me=_&&_.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(_,"__esModule",{value:!0});_.MemoryRetryQueue=_.DiskRetryQueue=void 0;var l=me(p("fs")),j=me(p("path")),D=1e3,Ot=50,Tt=5,L=class{filePath;lockPath;logPrefix;constructor(t,r){this.filePath=j.default.join(t,"failed-spans.jsonl"),this.lockPath=this.filePath+".lock",this.logPrefix=r}enqueue(t){try{l.default.mkdirSync(j.default.dirname(this.filePath),{recursive:!0});let r=JSON.stringify({savedAt:new Date().toISOString(),payload:t})+`
|
|
3
|
+
`;l.default.appendFileSync(this.filePath,r),this.trim()}catch(r){process.stderr.write(`[${this.logPrefix}] retry-queue enqueue failed: ${r}
|
|
4
|
+
`)}}readAll(){try{let t=l.default.readFileSync(this.filePath,"utf-8"),r=[];for(let n of t.split(`
|
|
5
|
+
`))if(n.trim())try{r.push(JSON.parse(n))}catch{}return r}catch{return[]}}rewrite(t){try{if(t.length===0){l.default.existsSync(this.filePath)&&l.default.unlinkSync(this.filePath);return}l.default.mkdirSync(j.default.dirname(this.filePath),{recursive:!0}),l.default.writeFileSync(this.filePath,t.map(r=>JSON.stringify(r)).join(`
|
|
6
|
+
`)+`
|
|
7
|
+
`)}catch(r){process.stderr.write(`[${this.logPrefix}] retry-queue rewrite failed: ${r}
|
|
8
|
+
`)}}tryAcquireLock(){let t=Date.now();for(l.default.mkdirSync(j.default.dirname(this.lockPath),{recursive:!0});Date.now()-t<Ot;)try{let r=l.default.openSync(this.lockPath,"wx");return l.default.writeSync(r,String(process.pid)),l.default.closeSync(r),!0}catch(r){if(r?.code!=="EEXIST")return process.stderr.write(`[${this.logPrefix}] retry-queue lock open failed: ${r}
|
|
9
|
+
`),!1;try{let s=l.default.statSync(this.lockPath);if(Date.now()-s.mtimeMs>3e4){l.default.unlinkSync(this.lockPath);continue}}catch{}let n=Tt,i=Date.now()+n;for(;Date.now()<i;);}return!1}release(){try{l.default.unlinkSync(this.lockPath)}catch{}}trim(){let t=this.readAll();if(t.length<=D)return;let r=t.length-D;process.stderr.write(`[${this.logPrefix}] retry-queue full, dropping ${r} oldest entries
|
|
10
|
+
`),this.rewrite(t.slice(r))}};_.DiskRetryQueue=L;var V=class{entries=[];enqueue(t){this.entries.push(t),this.entries.length>D&&this.entries.splice(0,this.entries.length-D)}drain(){let t=this.entries;return this.entries=[],t}get size(){return this.entries.length}};_.MemoryRetryQueue=V});var Te=g(y=>{"use strict";Object.defineProperty(y,"__esModule",{value:!0});y.MemoryTransport=y.DiskTransport=void 0;y.parseHeadersEnv=Pe;y.envOptionsResolver=Oe;y.postOtlp=b;var _e=U(),ve=$(),Et=5e3;function Pe(e){if(!e)return{};if(typeof e=="object")return{...e};let t={};for(let r of e.split(",")){let[n,...i]=r.split("=");n&&i.length>0&&(t[n.trim()]=i.join("=").trim())}return t}function Oe(){let e=process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,t=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,r;return e?r=e.replace(/\/+$/,""):t&&(r=t.replace(/\/+$/,"")+"/v1/traces"),r?{endpoint:r,headers:Pe(process.env.OTEL_EXPORTER_OTLP_HEADERS)}:null}async function b(e,t,r){let n=new AbortController,i=setTimeout(()=>n.abort(),Et);try{let s=await fetch(t.endpoint,{method:"POST",headers:{"Content-Type":"application/json",...t.headers},body:JSON.stringify(e),signal:n.signal});if(!s.ok){let a="";try{a=(await s.text()).slice(0,200)}catch{}let u=s.status===401||s.status===403?" \u2014 check OTEL_EXPORTER_OTLP_HEADERS (relay token)":s.status===404?" \u2014 check OTEL_EXPORTER_OTLP_TRACES_ENDPOINT path":s.status>=500?" \u2014 collector may be down":"";return process.stderr.write(`[${r}] OTLP POST ${s.status} ${t.endpoint}${u}${a?` body=${a}`:""}
|
|
11
|
+
`),!1}return!0}catch(s){return process.stderr.write(`[${r}] OTLP POST failed: ${s.message??String(s)}
|
|
12
|
+
`),!1}finally{clearTimeout(i)}}var B=class{queue;logPrefix;resolveOptions;constructor(t){this.queue=new _e.DiskRetryQueue(t.pluginData,t.logPrefix),this.logPrefix=t.logPrefix,this.resolveOptions=t.resolveOptions??Oe}async send(t){let r=this.resolveOptions();if(!r)return;await b(t,r,this.logPrefix)||this.queue.enqueue(t)}async flush(){let t=this.resolveOptions();if(t&&this.queue.tryAcquireLock())try{let r=this.queue.readAll();if(r.length===0)return;let n=(0,ve.mergeBatch)(r.map(s=>s.payload));await b(n,t,this.logPrefix)&&this.queue.rewrite([])}finally{this.queue.release()}}};y.DiskTransport=B;var K=class{queue=new _e.MemoryRetryQueue;logPrefix;resolveOptions;constructor(t){this.logPrefix=t.logPrefix,this.resolveOptions=t.resolveOptions}async send(t){let r=this.resolveOptions();if(!r)return;await b(t,r,this.logPrefix)||this.queue.enqueue(t)}async flush(){let t=this.resolveOptions();if(!t)return;let r=this.queue.drain();if(r.length===0)return;let n=(0,ve.mergeBatch)(r);if(!await b(n,t,this.logPrefix))for(let s of r)this.queue.enqueue(s)}};y.MemoryTransport=K});var X=g(E=>{"use strict";var z=E&&E.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(E,"__esModule",{value:!0});E.TraceManager=void 0;E.generateUlid=Ee;var H=z(p("fs")),kt=z(p("path")),St=z(p("crypto")),Z="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ee(){let e=Date.now(),t="",r=e;for(let s=0;s<10;s++)t=Z[r&31]+t,r=Math.floor(r/32);let n=St.default.randomBytes(10),i="";for(let s=0;s<10;s++)i+=Z[n[s]&31];for(;i.length<16;)i+=Z[0];return t+i}var Y=class{tracePath;constructor(t){this.tracePath=t}newTrace(){let t=Ee();return this.save(t),t}currentTrace(){try{let t=H.default.readFileSync(this.tracePath,"utf-8"),{traceId:r}=JSON.parse(t);if(r)return r}catch{}return this.newTrace()}save(t){H.default.mkdirSync(kt.default.dirname(this.tracePath),{recursive:!0}),H.default.writeFileSync(this.tracePath,JSON.stringify({traceId:t}))}};E.TraceManager=Y});var we=g(v=>{"use strict";var ke=v&&v.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(v,"__esModule",{value:!0});v.MemorySessionTraceManager=v.DiskSessionTraceManager=void 0;var J=ke(p("fs")),bt=ke(p("path")),Se=X(),be=200,Q=class{tracePath;maxSessions;constructor(t,r={}){this.tracePath=t,this.maxSessions=r.maxSessions??be}read(){try{let t=JSON.parse(J.default.readFileSync(this.tracePath,"utf-8"));if(t&&typeof t=="object"&&!("traceId"in t))return t}catch{}return{}}write(t){try{J.default.mkdirSync(bt.default.dirname(this.tracePath),{recursive:!0});let r=Object.entries(t),n=r.length>this.maxSessions?Object.fromEntries(r.slice(-this.maxSessions)):t;J.default.writeFileSync(this.tracePath,JSON.stringify(n))}catch{}}newTrace(t){let r=t||"default",n=(0,Se.generateUlid)(),i=this.read();return i[r]=n,this.write(i),n}currentTrace(t){let r=t||"default",n=this.read()[r];return n||this.newTrace(r)}};v.DiskSessionTraceManager=Q;var W=class{map=new Map;maxSessions;constructor(t={}){this.maxSessions=t.maxSessions??be}newTrace(t){let r=t||"default",n=(0,Se.generateUlid)();if(this.map.set(r,n),this.map.size>this.maxSessions){let i=this.map.keys().next().value;i!==void 0&&this.map.delete(i)}return n}currentTrace(t){let r=t||"default";return this.map.get(r)??this.newTrace(r)}};v.MemorySessionTraceManager=W});var xe=g(k=>{"use strict";var ee=k&&k.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(k,"__esModule",{value:!0});k.envFilePath=It;k.parseEnvFile=Ie;k.loadEnvFile=xt;var wt=ee(p("node:fs")),At=ee(p("node:os")),Ae=ee(p("node:path"));function It(e,t,r){let n=r?process.env[r]:void 0,i=n&&n.length>0?n:Ae.default.join(At.default.homedir(),e);return Ae.default.join(i,t)}function Ie(e){let t={};for(let r of e.split(`
|
|
13
|
+
`)){let n=r.trim();if(!n||n.startsWith("#"))continue;let i=n.indexOf("=");if(i<0)continue;let s=n.slice(0,i).trim(),a=n.slice(i+1).trim();(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),s&&(t[s]=a)}return t}function xt(e){let t;try{t=wt.default.readFileSync(e,"utf-8")}catch{return}let r=Ie(t);for(let[n,i]of Object.entries(r))process.env[n]===void 0&&(process.env[n]=i)}});var P=g(o=>{"use strict";Object.defineProperty(o,"__esModule",{value:!0});o.loadEnvFile=o.parseEnvFile=o.envFilePath=o.MemorySessionTraceManager=o.DiskSessionTraceManager=o.generateUlid=o.TraceManager=o.parseHeadersEnv=o.envOptionsResolver=o.postOtlp=o.MemoryTransport=o.DiskTransport=o.MemoryRetryQueue=o.DiskRetryQueue=o.evaluateGuard=o.mergeBatch=o.buildPayload=o.clientAttrs=o.guardAttrs=o.attrsFromRecord=o.toOtlpValue=o.snakeCase=o.newSpanId=o.ulidToTraceId=o.MAX_BYTES=o.PATTERNS=o.applyMatches=o.resolveOverlaps=o.collectMatches=o.truncate=o.redact=void 0;var S=C();Object.defineProperty(o,"redact",{enumerable:!0,get:function(){return S.redact}});Object.defineProperty(o,"truncate",{enumerable:!0,get:function(){return S.truncate}});Object.defineProperty(o,"collectMatches",{enumerable:!0,get:function(){return S.collectMatches}});Object.defineProperty(o,"resolveOverlaps",{enumerable:!0,get:function(){return S.resolveOverlaps}});Object.defineProperty(o,"applyMatches",{enumerable:!0,get:function(){return S.applyMatches}});Object.defineProperty(o,"PATTERNS",{enumerable:!0,get:function(){return S.PATTERNS}});Object.defineProperty(o,"MAX_BYTES",{enumerable:!0,get:function(){return S.MAX_BYTES}});var h=$();Object.defineProperty(o,"ulidToTraceId",{enumerable:!0,get:function(){return h.ulidToTraceId}});Object.defineProperty(o,"newSpanId",{enumerable:!0,get:function(){return h.newSpanId}});Object.defineProperty(o,"snakeCase",{enumerable:!0,get:function(){return h.snakeCase}});Object.defineProperty(o,"toOtlpValue",{enumerable:!0,get:function(){return h.toOtlpValue}});Object.defineProperty(o,"attrsFromRecord",{enumerable:!0,get:function(){return h.attrsFromRecord}});Object.defineProperty(o,"guardAttrs",{enumerable:!0,get:function(){return h.guardAttrs}});Object.defineProperty(o,"clientAttrs",{enumerable:!0,get:function(){return h.clientAttrs}});Object.defineProperty(o,"buildPayload",{enumerable:!0,get:function(){return h.buildPayload}});Object.defineProperty(o,"mergeBatch",{enumerable:!0,get:function(){return h.mergeBatch}});var Mt=he();Object.defineProperty(o,"evaluateGuard",{enumerable:!0,get:function(){return Mt.evaluateGuard}});var Me=U();Object.defineProperty(o,"DiskRetryQueue",{enumerable:!0,get:function(){return Me.DiskRetryQueue}});Object.defineProperty(o,"MemoryRetryQueue",{enumerable:!0,get:function(){return Me.MemoryRetryQueue}});var w=Te();Object.defineProperty(o,"DiskTransport",{enumerable:!0,get:function(){return w.DiskTransport}});Object.defineProperty(o,"MemoryTransport",{enumerable:!0,get:function(){return w.MemoryTransport}});Object.defineProperty(o,"postOtlp",{enumerable:!0,get:function(){return w.postOtlp}});Object.defineProperty(o,"envOptionsResolver",{enumerable:!0,get:function(){return w.envOptionsResolver}});Object.defineProperty(o,"parseHeadersEnv",{enumerable:!0,get:function(){return w.parseHeadersEnv}});var Re=X();Object.defineProperty(o,"TraceManager",{enumerable:!0,get:function(){return Re.TraceManager}});Object.defineProperty(o,"generateUlid",{enumerable:!0,get:function(){return Re.generateUlid}});var je=we();Object.defineProperty(o,"DiskSessionTraceManager",{enumerable:!0,get:function(){return je.DiskSessionTraceManager}});Object.defineProperty(o,"MemorySessionTraceManager",{enumerable:!0,get:function(){return je.MemorySessionTraceManager}});var te=xe();Object.defineProperty(o,"envFilePath",{enumerable:!0,get:function(){return te.envFilePath}});Object.defineProperty(o,"parseEnvFile",{enumerable:!0,get:function(){return te.parseEnvFile}});Object.defineProperty(o,"loadEnvFile",{enumerable:!0,get:function(){return te.loadEnvFile}})});var A=T(P(),1);function Rt(){return(0,A.envFilePath)(".gemini","pinta-gemini.env","GEMINI_HOME")}function De(e=Rt()){(0,A.loadEnvFile)(e)}var Ne=T(P(),1);import jt from"os";import re from"path";function Dt(){return process.env.GEMINI_HOME||re.join(jt.homedir(),".gemini")}function Nt(){let e=process.env.GEMINI_PLUGIN_OPTION_ENDPOINT||process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;if(e)return e.replace(/\/+$/,"");let t=process.env.OTEL_EXPORTER_OTLP_ENDPOINT;if(t)return t.replace(/\/+$/,"")+"/v1/traces"}function Gt(){let e=(0,Ne.parseHeadersEnv)(process.env.GEMINI_PLUGIN_OPTION_HEADERS||process.env.OTEL_EXPORTER_OTLP_HEADERS),t=process.env.GEMINI_PLUGIN_OPTION_API_KEY;return t&&!e["x-pinta-relay-token"]&&(e["x-pinta-relay-token"]=t),e}function Ge(){let e=process.env.GEMINI_PLUGIN_DATA||re.join(Dt(),"pinta-gemini-data");return!process.env.PINTA_RELAY_TOKEN&&process.env.GEMINI_PLUGIN_OPTION_API_KEY&&(process.env.PINTA_RELAY_TOKEN=process.env.GEMINI_PLUGIN_OPTION_API_KEY),{pluginData:e,tracePath:re.join(e,"trace.json"),endpoint:Nt(),headers:Gt(),guardEndpoint:process.env.PINTA_GUARD_ENDPOINT}}function qe(e=process.argv){let t=r=>{let n=e.indexOf(r);return n>=0?e[n+1]:void 0};return{agent:t("--agent")||"gemini",event:t("--event")}}function Ce(e){let t=e.transcriptPath;if(typeof t=="string"){if(t.includes("/antigravity-cli/brain/"))return"agy";if(t.includes("/antigravity/brain/"))return"antigravity2"}}var m=e=>e==="gemini",qt={identity:{prefix:"gemini",ingest:"gemini",service:"gemini-cli"},gateEvent:"BeforeTool"},Ct={identity:{prefix:"antigravity",ingest:"antigravity",service:"antigravity-cli"},gateEvent:"PreToolUse"},$e=e=>m(e)?qt:Ct;function Fe(e){return $e(e).identity}function Le(e){return $e(e).gateEvent}var $t=new Set(["AfterModel"]),Ve=e=>$t.has(e);function Ue(e,t,r){if(m(e))return{hook:t||r.hook_event_name||"unknown",session_id:I(r.session_id),cwd:I(r.cwd),tool_name:I(r.tool_name),tool_input:r.tool_input};let n=r.workspacePaths,i=r.toolCall;return{hook:t||"unknown",session_id:I(r.conversationId),cwd:Array.isArray(n)?I(n[0]):void 0,tool_name:i?.name,tool_input:i?.args}}function I(e){return typeof e=="string"?e:void 0}var Be=T(P(),1),Ft=50,Lt="pinta-gemini/0.6.0";function Ke(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,r=t.command??t.CommandLine;return typeof r=="string"?r:void 0}function He(e,t,r){return(0,Be.evaluateGuard)(e,t,{timeoutMs:Ft,token:r??process.env.PINTA_RELAY_TOKEN??"",disabled:process.env.PINTA_GUARD_DISABLED==="1",userAgent:Lt})}var Ze=T(P(),1),N=class extends Ze.DiskTransport{constructor(t){super({pluginData:t.pluginData,logPrefix:"pinta-gemini",resolveOptions:()=>t.endpoint?{endpoint:t.endpoint,headers:t.headers}:null})}};var Ye=T(P(),1),G=class extends Ye.DiskSessionTraceManager{constructor(t){super(t.tracePath)}};import ne from"os";var O=T(P(),1),Bt=T(P(),1),ze="0.6.0";function Vt(e){return{skipRedactKeys:new Set([`${e}.hook`,`${e}.agent`,`${e}.tool_name`,`${e}.session_id`,`${e}.transcript_path`,`${e}.transcriptPath`,`${e}.cwd`]),bashContextKeys:new Set([`${e}.tool_input`,`${e}.tool_response`,`${e}.toolCall`])}}function Ut(e){return[{key:"service.name",value:{stringValue:e}},{key:"telemetry.sdk.name",value:{stringValue:"pinta-gemini"}},{key:"telemetry.sdk.language",value:{stringValue:"nodejs"}},{key:"telemetry.sdk.version",value:{stringValue:ze}},{key:"process.pid",value:{intValue:process.pid}},{key:"process.owner",value:{stringValue:ne.userInfo().username}},{key:"host.name",value:{stringValue:ne.hostname()}},{key:"host.arch",value:{stringValue:ne.arch()}}]}function Xe(e){let t=Fe(e.agent),r=Vt(t.prefix),n=[{key:"ingest.type",value:{stringValue:t.ingest}},{key:`${t.prefix}.hook`,value:{stringValue:e.canonical.hook}},{key:`${t.prefix}.agent`,value:{stringValue:e.agent}}];e.product&&n.push({key:`${t.prefix}.product`,value:{stringValue:e.product}}),n.push(...(0,O.attrsFromRecord)(e.event,t.prefix,r));let i=new Set(n.map(s=>s.key));for(let[s,a]of[["session_id",e.canonical.session_id],["cwd",e.canonical.cwd],["tool_name",e.canonical.tool_name]]){let u=`${t.prefix}.${s}`;if(a!=null&&!i.has(u)){let f=(0,O.toOtlpValue)(u,String(a),r);f!==null&&n.push({key:u,value:f})}}return(0,O.buildPayload)({traceId:e.traceId,spanName:`${t.ingest}.${(0,O.snakeCase)(e.canonical.hook)}`,attributes:n,resource:Ut(t.service),scope:{name:"pinta-gemini",version:ze},now:e.now,guard:e.guard})}function Je(e,t,r){if(r&&r.decision==="DENY"){let n=r.userMessage??r.reason??"guard_deny";return m(e)?{decision:"deny",reason:n,systemMessage:r.userMessage??void 0}:{decision:"deny",reason:n}}return!m(e)&&t==="PreToolUse"?{decision:"allow"}:{}}import Qe from"fs";import Kt from"path";function We(e,t){if(process.env.PINTA_GEMINI_DEBUG==="1")try{Qe.mkdirSync(e.pluginData,{recursive:!0}),Qe.appendFileSync(Kt.join(e.pluginData,"invocations.jsonl"),JSON.stringify(t)+`
|
|
14
|
+
`)}catch{}}async function Ht(){let e=[];for await(let t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf-8")}function Zt(e,t,r){return m(e)?t.hook==="BeforeAgent":t.hook==="PreInvocation"&&r.invocationNum===1}async function et(){let{agent:e,event:t}=qe(),r={},n={},i,s=null,a=Ge();try{if(n=JSON.parse(await Ht()||"{}"),i=Ue(e,t,n),!Ve(i.hook)){let u=new N(a);await u.flush();let f=i.session_id??"unknown",x=new G(a),M=Zt(e,i,n)?x.newTrace(f):x.currentTrace(f);if(i.hook===Le(e)){let tt=Ke(i.tool_input)??(typeof i.tool_input=="string"?i.tool_input:JSON.stringify(i.tool_input??null));s=await He({spanId:f,toolName:i.tool_name,toolInput:i.tool_input,rawTextFields:{toolInput:tt}},a.guardEndpoint,a.headers["x-pinta-relay-token"])}let q=m(e)?void 0:Ce(n);await u.send(Xe({agent:e,canonical:i,event:n,traceId:M,guard:s,product:q})),r=Je(e,t,s)}}catch(u){process.stderr.write(`[pinta-gemini] error: ${u}
|
|
15
|
+
`),r={}}We(a,{ts:new Date().toISOString(),pid:process.pid,agent:e,event:t,argv:process.argv.slice(2),received_payload:n,normalized:i??null,guard:s,decision_returned:r}),process.stdout.write(JSON.stringify(r)+`
|
|
16
|
+
`),process.exit(0)}De();et();
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createRequire as __pintaCreateRequire } from 'module'; const require = __pintaCreateRequire(import.meta.url);
|
|
2
|
+
var Pt=Object.create;var fe=Object.defineProperty;var vt=Object.getOwnPropertyDescriptor;var bt=Object.getOwnPropertyNames;var Ot=Object.getPrototypeOf,Tt=Object.prototype.hasOwnProperty;var m=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,n)=>(typeof require<"u"?require:t)[n]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var h=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var wt=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of bt(t))!Tt.call(e,i)&&i!==n&&fe(e,i,{get:()=>t[i],enumerable:!(r=vt(t,i))||r.enumerable});return e};var S=(e,t,n)=>(n=e!=null?Pt(Ot(e)):{},wt(t||!e||!e.__esModule?fe(n,"default",{value:e,enumerable:!0}):n,e));var B=h(d=>{"use strict";Object.defineProperty(d,"__esModule",{value:!0});d.PATTERNS=d.MAX_BYTES=void 0;d.truncate=Et;d.collectMatches=ge;d.resolveOverlaps=me;d.applyMatches=ye;d.redact=St;d.MAX_BYTES=102400;function Et(e){let t=Buffer.from(e,"utf-8");return t.length<=d.MAX_BYTES?e:`${t.subarray(0,d.MAX_BYTES).toString("utf-8")}\u2026[TRUNCATED:${t.length}]`}d.PATTERNS=[{type:"aws_access_key",regex:/AKIA[0-9A-Z]{16}/g},{type:"aws_secret_key",regex:/(?:aws[_-]?secret(?:[_-]?(?:access)?[_-]?key)?)\s*[:=]\s*["']?([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])/gi,captureGroup:1},{type:"gcp_service_account",regex:/\{[\s\S]{0,200}?"type"\s*:\s*"service_account"[\s\S]*?\}/g},{type:"github_token",regex:/gh[pousr]_[A-Za-z0-9]{36,}/g},{type:"gitlab_token",regex:/glpat-[A-Za-z0-9_-]{20}/g},{type:"slack_token",regex:/xox[abrsp]-[0-9A-Za-z-]{10,}/g},{type:"openai_key",regex:/sk-(?:proj-)?[A-Za-z0-9_-]{40,}/g},{type:"anthropic_key",regex:/sk-ant-[A-Za-z0-9_-]{50,}/g},{type:"stripe_key",regex:/(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{20,}/g},{type:"jwt",regex:/eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g},{type:"private_key_block",regex:/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g},{type:"bearer_token",regex:/bearer\s+([A-Za-z0-9._~+/=-]{12,})/gi,captureGroup:1},{type:"basic_auth",regex:/basic\s+([A-Za-z0-9+/=]{12,})/gi,captureGroup:1},{type:"db_url_password",regex:/\b(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|redis):\/\/[^:\s/]+:([^@\s]+)@/gi,captureGroup:1},{type:"cli_password_flag",regex:/(?:--password|--pass|--pwd)[=\s]([^\s'"]+)/g,captureGroup:1},{type:"cli_password_short",regex:/\s-p([^\s'"]+)/g,captureGroup:1,requireContext:"bash"},{type:"env_var_secret",regex:/^(?:export\s+)?([A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PWD|API_KEY)[A-Z0-9_]*)\s*=\s*["']?([^\s"'\n]+)/gm,captureGroup:2}];function ge(e,t){let n=[];for(let r of d.PATTERNS){if(r.requireContext&&r.requireContext!==t.context)continue;let i=new RegExp(r.regex.source,r.regex.flags),s;for(;(s=i.exec(e))!==null;){let a=r.captureGroup??0,c=s[a];if(c===void 0){s.index===i.lastIndex&&i.lastIndex++;continue}let u=s.index,f=s.index+s[0].length,y=u+s[0].indexOf(c),E=y+c.length;n.push({start:u,end:f,replaceStart:y,replaceEnd:E,type:r.type}),s.index===i.lastIndex&&i.lastIndex++}}return n}function me(e){let t=[...e].sort((i,s)=>i.start!==s.start?i.start-s.start:s.end-s.start-(i.end-i.start)),n=[],r=-1;for(let i of t)i.start<r||(n.push(i),r=i.end);return n}function ye(e,t){let n=[...t].sort((i,s)=>s.replaceStart-i.replaceStart),r=e;for(let i of n)r=r.slice(0,i.replaceStart)+`[REDACTED:${i.type}]`+r.slice(i.replaceEnd);return r}function St(e,t={}){if(e.length===0)return e;let n=ge(e,t);if(n.length===0)return e;let r=me(n);return ye(e,r)}});var H=h(g=>{"use strict";var kt=g&&g.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(g,"__esModule",{value:!0});g.ulidToTraceId=Pe;g.newSpanId=ve;g.snakeCase=xt;g.toOtlpValue=be;g.attrsFromRecord=jt;g.guardAttrs=Oe;g.clientAttrs=Te;g.buildPayload=Mt;g.mergeBatch=Rt;var At=kt(m("crypto")),he=B(),It="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Pe(e){if(e.length!==26)throw new Error(`ulidToTraceId: expected 26 chars, got ${e.length}`);let t=0n;for(let r of e){let i=It.indexOf(r);if(i<0)throw new Error(`ulidToTraceId: invalid Crockford char "${r}"`);t=t<<5n|BigInt(i)}let n=(1n<<128n)-1n;return t&=n,t.toString(16).padStart(32,"0")}function ve(){return At.default.randomBytes(8).toString("hex")}function xt(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()}var _e=new Set;function G(e,t,n){let r=(0,he.truncate)(t);if((n.skipRedactKeys??_e).has(e))return r;let i=(n.bashContextKeys??_e).has(e)?"bash":void 0;return(0,he.redact)(r,{context:i})}function be(e,t,n={}){if(t==null)return null;switch(typeof t){case"string":return{stringValue:G(e,t,n)};case"boolean":return{boolValue:t};case"number":return Number.isInteger(t)?{intValue:t}:{doubleValue:t};case"object":try{return{stringValue:G(e,JSON.stringify(t),n)}}catch{return{stringValue:G(e,String(t),n)}}default:return{stringValue:G(e,String(t),n)}}}function jt(e,t,n={}){let r=[];for(let[i,s]of Object.entries(e)){let a=`${t}.${i}`,c=be(a,s,n);c!==null&&r.push({key:a,value:c})}return r}function Oe(e){let t=[{key:"pinta.guard.decision",value:{stringValue:e.decision.toLowerCase()}},{key:"pinta.guard.duration_ms",value:{intValue:e.durationMs}}];return e.reason&&t.push({key:"pinta.guard.matched_rule",value:{stringValue:e.reason}}),e.failOpenReason&&t.push({key:"pinta.guard.fail_open_reason",value:{stringValue:e.failOpenReason}}),t}function Te(e){return[{key:"pinta.client.rtt_ms",value:Number.isInteger(e.rttMs)?{intValue:e.rttMs}:{doubleValue:e.rttMs}},{key:"pinta.client.op",value:{stringValue:e.op}}]}function Mt(e){let t=e.now??Date.now(),n=(BigInt(t)*1000000n).toString(),r=[...e.attributes];e.guard&&r.push(...Oe(e.guard));let i=e.clientTiming??(e.guard?.clientRttMs!==void 0?{op:"guard",rttMs:e.guard.clientRttMs}:null);i&&r.push(...Te(i));let s={traceId:Pe(e.traceId),spanId:ve(),name:e.spanName,kind:e.spanKind??1,startTimeUnixNano:n,endTimeUnixNano:n,attributes:r};return{resourceSpans:[{resource:{attributes:e.resource},scopeSpans:[{scope:e.scope,spans:[s]}]}]}}function Rt(e){let t=[];for(let n of e)t.push(...n.resourceSpans);return{resourceSpans:t}}});var Ee=h(z=>{"use strict";Object.defineProperty(z,"__esModule",{value:!0});z.evaluateGuard=qt;var Dt=1e4,Nt="pinta-core";function we(e,t){return{decision:"ALLOW",reason:null,userMessage:null,durationMs:e,clientRttMs:e,failOpenReason:t}}function Ct(e){return new Promise((t,n)=>setTimeout(()=>{let r=new Error("Guard request timed out");r.name="TimeoutError",n(r)},e))}async function qt(e,t,n={}){if(!t||n.disabled)return null;let r=n.timeoutMs??Dt,i=Date.now();try{let s=await Promise.race([fetch(t,{method:"POST",headers:{"content-type":"application/json","user-agent":n.userAgent??Nt,"x-pinta-relay-token":n.token??""},body:JSON.stringify({input:e})}),Ct(r)]);if(s.status!==200)return we(Date.now()-i,"error");let a=await s.json(),c=Date.now()-i;return{decision:a.decision,reason:a.reason,userMessage:a.userMessage??null,durationMs:a.durationMs??c,clientRttMs:c}}catch(s){let a=s.name==="TimeoutError"?"timeout":"error";return we(Date.now()-i,a)}}});var X=h(b=>{"use strict";var Se=b&&b.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(b,"__esModule",{value:!0});b.MemoryRetryQueue=b.DiskRetryQueue=void 0;var p=Se(m("fs")),F=Se(m("path")),$=1e3,Gt=50,Ft=5,Y=class{filePath;lockPath;logPrefix;constructor(t,n){this.filePath=F.default.join(t,"failed-spans.jsonl"),this.lockPath=this.filePath+".lock",this.logPrefix=n}enqueue(t){try{p.default.mkdirSync(F.default.dirname(this.filePath),{recursive:!0});let n=JSON.stringify({savedAt:new Date().toISOString(),payload:t})+`
|
|
3
|
+
`;p.default.appendFileSync(this.filePath,n),this.trim()}catch(n){process.stderr.write(`[${this.logPrefix}] retry-queue enqueue failed: ${n}
|
|
4
|
+
`)}}readAll(){try{let t=p.default.readFileSync(this.filePath,"utf-8"),n=[];for(let r of t.split(`
|
|
5
|
+
`))if(r.trim())try{n.push(JSON.parse(r))}catch{}return n}catch{return[]}}rewrite(t){try{if(t.length===0){p.default.existsSync(this.filePath)&&p.default.unlinkSync(this.filePath);return}p.default.mkdirSync(F.default.dirname(this.filePath),{recursive:!0}),p.default.writeFileSync(this.filePath,t.map(n=>JSON.stringify(n)).join(`
|
|
6
|
+
`)+`
|
|
7
|
+
`)}catch(n){process.stderr.write(`[${this.logPrefix}] retry-queue rewrite failed: ${n}
|
|
8
|
+
`)}}tryAcquireLock(){let t=Date.now();for(p.default.mkdirSync(F.default.dirname(this.lockPath),{recursive:!0});Date.now()-t<Gt;)try{let n=p.default.openSync(this.lockPath,"wx");return p.default.writeSync(n,String(process.pid)),p.default.closeSync(n),!0}catch(n){if(n?.code!=="EEXIST")return process.stderr.write(`[${this.logPrefix}] retry-queue lock open failed: ${n}
|
|
9
|
+
`),!1;try{let s=p.default.statSync(this.lockPath);if(Date.now()-s.mtimeMs>3e4){p.default.unlinkSync(this.lockPath);continue}}catch{}let r=Ft,i=Date.now()+r;for(;Date.now()<i;);}return!1}release(){try{p.default.unlinkSync(this.lockPath)}catch{}}trim(){let t=this.readAll();if(t.length<=$)return;let n=t.length-$;process.stderr.write(`[${this.logPrefix}] retry-queue full, dropping ${n} oldest entries
|
|
10
|
+
`),this.rewrite(t.slice(n))}};b.DiskRetryQueue=Y;var Z=class{entries=[];enqueue(t){this.entries.push(t),this.entries.length>$&&this.entries.splice(0,this.entries.length-$)}drain(){let t=this.entries;return this.entries=[],t}get size(){return this.entries.length}};b.MemoryRetryQueue=Z});var je=h(_=>{"use strict";Object.defineProperty(_,"__esModule",{value:!0});_.MemoryTransport=_.DiskTransport=void 0;_.parseHeadersEnv=Ie;_.envOptionsResolver=xe;_.postOtlp=R;var ke=X(),Ae=H(),$t=5e3;function Ie(e){if(!e)return{};if(typeof e=="object")return{...e};let t={};for(let n of e.split(",")){let[r,...i]=n.split("=");r&&i.length>0&&(t[r.trim()]=i.join("=").trim())}return t}function xe(){let e=process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,t=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,n;return e?n=e.replace(/\/+$/,""):t&&(n=t.replace(/\/+$/,"")+"/v1/traces"),n?{endpoint:n,headers:Ie(process.env.OTEL_EXPORTER_OTLP_HEADERS)}:null}async function R(e,t,n){let r=new AbortController,i=setTimeout(()=>r.abort(),$t);try{let s=await fetch(t.endpoint,{method:"POST",headers:{"Content-Type":"application/json",...t.headers},body:JSON.stringify(e),signal:r.signal});if(!s.ok){let a="";try{a=(await s.text()).slice(0,200)}catch{}let c=s.status===401||s.status===403?" \u2014 check OTEL_EXPORTER_OTLP_HEADERS (relay token)":s.status===404?" \u2014 check OTEL_EXPORTER_OTLP_TRACES_ENDPOINT path":s.status>=500?" \u2014 collector may be down":"";return process.stderr.write(`[${n}] OTLP POST ${s.status} ${t.endpoint}${c}${a?` body=${a}`:""}
|
|
11
|
+
`),!1}return!0}catch(s){return process.stderr.write(`[${n}] OTLP POST failed: ${s.message??String(s)}
|
|
12
|
+
`),!1}finally{clearTimeout(i)}}var J=class{queue;logPrefix;resolveOptions;constructor(t){this.queue=new ke.DiskRetryQueue(t.pluginData,t.logPrefix),this.logPrefix=t.logPrefix,this.resolveOptions=t.resolveOptions??xe}async send(t){let n=this.resolveOptions();if(!n)return;await R(t,n,this.logPrefix)||this.queue.enqueue(t)}async flush(){let t=this.resolveOptions();if(t&&this.queue.tryAcquireLock())try{let n=this.queue.readAll();if(n.length===0)return;let r=(0,Ae.mergeBatch)(n.map(s=>s.payload));await R(r,t,this.logPrefix)&&this.queue.rewrite([])}finally{this.queue.release()}}};_.DiskTransport=J;var W=class{queue=new ke.MemoryRetryQueue;logPrefix;resolveOptions;constructor(t){this.logPrefix=t.logPrefix,this.resolveOptions=t.resolveOptions}async send(t){let n=this.resolveOptions();if(!n)return;await R(t,n,this.logPrefix)||this.queue.enqueue(t)}async flush(){let t=this.resolveOptions();if(!t)return;let n=this.queue.drain();if(n.length===0)return;let r=(0,Ae.mergeBatch)(n);if(!await R(r,t,this.logPrefix))for(let s of n)this.queue.enqueue(s)}};_.MemoryTransport=W});var re=h(k=>{"use strict";var ne=k&&k.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(k,"__esModule",{value:!0});k.TraceManager=void 0;k.generateUlid=Me;var Q=ne(m("fs")),Lt=ne(m("path")),Vt=ne(m("crypto")),ee="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Me(){let e=Date.now(),t="",n=e;for(let s=0;s<10;s++)t=ee[n&31]+t,n=Math.floor(n/32);let r=Vt.default.randomBytes(10),i="";for(let s=0;s<10;s++)i+=ee[r[s]&31];for(;i.length<16;)i+=ee[0];return t+i}var te=class{tracePath;constructor(t){this.tracePath=t}newTrace(){let t=Me();return this.save(t),t}currentTrace(){try{let t=Q.default.readFileSync(this.tracePath,"utf-8"),{traceId:n}=JSON.parse(t);if(n)return n}catch{}return this.newTrace()}save(t){Q.default.mkdirSync(Lt.default.dirname(this.tracePath),{recursive:!0}),Q.default.writeFileSync(this.tracePath,JSON.stringify({traceId:t}))}};k.TraceManager=te});var Ce=h(O=>{"use strict";var Re=O&&O.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(O,"__esModule",{value:!0});O.MemorySessionTraceManager=O.DiskSessionTraceManager=void 0;var ie=Re(m("fs")),Kt=Re(m("path")),De=re(),Ne=200,se=class{tracePath;maxSessions;constructor(t,n={}){this.tracePath=t,this.maxSessions=n.maxSessions??Ne}read(){try{let t=JSON.parse(ie.default.readFileSync(this.tracePath,"utf-8"));if(t&&typeof t=="object"&&!("traceId"in t))return t}catch{}return{}}write(t){try{ie.default.mkdirSync(Kt.default.dirname(this.tracePath),{recursive:!0});let n=Object.entries(t),r=n.length>this.maxSessions?Object.fromEntries(n.slice(-this.maxSessions)):t;ie.default.writeFileSync(this.tracePath,JSON.stringify(r))}catch{}}newTrace(t){let n=t||"default",r=(0,De.generateUlid)(),i=this.read();return i[n]=r,this.write(i),r}currentTrace(t){let n=t||"default",r=this.read()[n];return r||this.newTrace(n)}};O.DiskSessionTraceManager=se;var oe=class{map=new Map;maxSessions;constructor(t={}){this.maxSessions=t.maxSessions??Ne}newTrace(t){let n=t||"default",r=(0,De.generateUlid)();if(this.map.set(n,r),this.map.size>this.maxSessions){let i=this.map.keys().next().value;i!==void 0&&this.map.delete(i)}return r}currentTrace(t){let n=t||"default";return this.map.get(n)??this.newTrace(n)}};O.MemorySessionTraceManager=oe});var Fe=h(A=>{"use strict";var ae=A&&A.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(A,"__esModule",{value:!0});A.envFilePath=Ht;A.parseEnvFile=Ge;A.loadEnvFile=zt;var Ut=ae(m("node:fs")),Bt=ae(m("node:os")),qe=ae(m("node:path"));function Ht(e,t,n){let r=n?process.env[n]:void 0,i=r&&r.length>0?r:qe.default.join(Bt.default.homedir(),e);return qe.default.join(i,t)}function Ge(e){let t={};for(let n of e.split(`
|
|
13
|
+
`)){let r=n.trim();if(!r||r.startsWith("#"))continue;let i=r.indexOf("=");if(i<0)continue;let s=r.slice(0,i).trim(),a=r.slice(i+1).trim();(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),s&&(t[s]=a)}return t}function zt(e){let t;try{t=Ut.default.readFileSync(e,"utf-8")}catch{return}let n=Ge(t);for(let[r,i]of Object.entries(n))process.env[r]===void 0&&(process.env[r]=i)}});var T=h(o=>{"use strict";Object.defineProperty(o,"__esModule",{value:!0});o.loadEnvFile=o.parseEnvFile=o.envFilePath=o.MemorySessionTraceManager=o.DiskSessionTraceManager=o.generateUlid=o.TraceManager=o.parseHeadersEnv=o.envOptionsResolver=o.postOtlp=o.MemoryTransport=o.DiskTransport=o.MemoryRetryQueue=o.DiskRetryQueue=o.evaluateGuard=o.mergeBatch=o.buildPayload=o.clientAttrs=o.guardAttrs=o.attrsFromRecord=o.toOtlpValue=o.snakeCase=o.newSpanId=o.ulidToTraceId=o.MAX_BYTES=o.PATTERNS=o.applyMatches=o.resolveOverlaps=o.collectMatches=o.truncate=o.redact=void 0;var I=B();Object.defineProperty(o,"redact",{enumerable:!0,get:function(){return I.redact}});Object.defineProperty(o,"truncate",{enumerable:!0,get:function(){return I.truncate}});Object.defineProperty(o,"collectMatches",{enumerable:!0,get:function(){return I.collectMatches}});Object.defineProperty(o,"resolveOverlaps",{enumerable:!0,get:function(){return I.resolveOverlaps}});Object.defineProperty(o,"applyMatches",{enumerable:!0,get:function(){return I.applyMatches}});Object.defineProperty(o,"PATTERNS",{enumerable:!0,get:function(){return I.PATTERNS}});Object.defineProperty(o,"MAX_BYTES",{enumerable:!0,get:function(){return I.MAX_BYTES}});var P=H();Object.defineProperty(o,"ulidToTraceId",{enumerable:!0,get:function(){return P.ulidToTraceId}});Object.defineProperty(o,"newSpanId",{enumerable:!0,get:function(){return P.newSpanId}});Object.defineProperty(o,"snakeCase",{enumerable:!0,get:function(){return P.snakeCase}});Object.defineProperty(o,"toOtlpValue",{enumerable:!0,get:function(){return P.toOtlpValue}});Object.defineProperty(o,"attrsFromRecord",{enumerable:!0,get:function(){return P.attrsFromRecord}});Object.defineProperty(o,"guardAttrs",{enumerable:!0,get:function(){return P.guardAttrs}});Object.defineProperty(o,"clientAttrs",{enumerable:!0,get:function(){return P.clientAttrs}});Object.defineProperty(o,"buildPayload",{enumerable:!0,get:function(){return P.buildPayload}});Object.defineProperty(o,"mergeBatch",{enumerable:!0,get:function(){return P.mergeBatch}});var Yt=Ee();Object.defineProperty(o,"evaluateGuard",{enumerable:!0,get:function(){return Yt.evaluateGuard}});var $e=X();Object.defineProperty(o,"DiskRetryQueue",{enumerable:!0,get:function(){return $e.DiskRetryQueue}});Object.defineProperty(o,"MemoryRetryQueue",{enumerable:!0,get:function(){return $e.MemoryRetryQueue}});var D=je();Object.defineProperty(o,"DiskTransport",{enumerable:!0,get:function(){return D.DiskTransport}});Object.defineProperty(o,"MemoryTransport",{enumerable:!0,get:function(){return D.MemoryTransport}});Object.defineProperty(o,"postOtlp",{enumerable:!0,get:function(){return D.postOtlp}});Object.defineProperty(o,"envOptionsResolver",{enumerable:!0,get:function(){return D.envOptionsResolver}});Object.defineProperty(o,"parseHeadersEnv",{enumerable:!0,get:function(){return D.parseHeadersEnv}});var Le=re();Object.defineProperty(o,"TraceManager",{enumerable:!0,get:function(){return Le.TraceManager}});Object.defineProperty(o,"generateUlid",{enumerable:!0,get:function(){return Le.generateUlid}});var Ve=Ce();Object.defineProperty(o,"DiskSessionTraceManager",{enumerable:!0,get:function(){return Ve.DiskSessionTraceManager}});Object.defineProperty(o,"MemorySessionTraceManager",{enumerable:!0,get:function(){return Ve.MemorySessionTraceManager}});var ce=Fe();Object.defineProperty(o,"envFilePath",{enumerable:!0,get:function(){return ce.envFilePath}});Object.defineProperty(o,"parseEnvFile",{enumerable:!0,get:function(){return ce.parseEnvFile}});Object.defineProperty(o,"loadEnvFile",{enumerable:!0,get:function(){return ce.loadEnvFile}})});import{realpathSync as Nn}from"node:fs";import{pathToFileURL as Cn}from"node:url";var N=S(T(),1);function Zt(){return(0,N.envFilePath)(".gemini","pinta-gemini.env","GEMINI_HOME")}function Ke(e=Zt()){(0,N.loadEnvFile)(e)}var Ue=S(T(),1);import Xt from"os";import ue from"path";function Jt(){return process.env.GEMINI_HOME||ue.join(Xt.homedir(),".gemini")}function Wt(){let e=process.env.GEMINI_PLUGIN_OPTION_ENDPOINT||process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT;if(e)return e.replace(/\/+$/,"");let t=process.env.OTEL_EXPORTER_OTLP_ENDPOINT;if(t)return t.replace(/\/+$/,"")+"/v1/traces"}function Qt(){let e=(0,Ue.parseHeadersEnv)(process.env.GEMINI_PLUGIN_OPTION_HEADERS||process.env.OTEL_EXPORTER_OTLP_HEADERS),t=process.env.GEMINI_PLUGIN_OPTION_API_KEY;return t&&!e["x-pinta-relay-token"]&&(e["x-pinta-relay-token"]=t),e}function Be(){let e=process.env.GEMINI_PLUGIN_DATA||ue.join(Jt(),"pinta-gemini-data");return!process.env.PINTA_RELAY_TOKEN&&process.env.GEMINI_PLUGIN_OPTION_API_KEY&&(process.env.PINTA_RELAY_TOKEN=process.env.GEMINI_PLUGIN_OPTION_API_KEY),{pluginData:e,tracePath:ue.join(e,"trace.json"),endpoint:Wt(),headers:Qt(),guardEndpoint:process.env.PINTA_GUARD_ENDPOINT}}function He(e=process.argv){let t=n=>{let r=e.indexOf(n);return r>=0?e[r+1]:void 0};return{agent:t("--agent")||"gemini",event:t("--event")}}function ze(e){let t=e.transcriptPath;if(typeof t=="string"){if(t.includes("/antigravity-cli/brain/"))return"agy";if(t.includes("/antigravity/brain/"))return"antigravity2"}}var v=e=>e==="gemini",en={identity:{prefix:"gemini",ingest:"gemini",service:"gemini-cli"},gateEvent:"BeforeTool"},tn={identity:{prefix:"antigravity",ingest:"antigravity",service:"antigravity-cli"},gateEvent:"PreToolUse"},Ye=e=>v(e)?en:tn;function Ze(e){return Ye(e).identity}function Xe(e){return Ye(e).gateEvent}var nn=new Set(["AfterModel"]),Je=e=>nn.has(e);function We(e,t,n){if(v(e))return{hook:t||n.hook_event_name||"unknown",session_id:C(n.session_id),cwd:C(n.cwd),tool_name:C(n.tool_name),tool_input:n.tool_input};let r=n.workspacePaths,i=n.toolCall;return{hook:t||"unknown",session_id:C(n.conversationId),cwd:Array.isArray(r)?C(r[0]):void 0,tool_name:i?.name,tool_input:i?.args}}function C(e){return typeof e=="string"?e:void 0}var Qe=S(T(),1),rn=50,sn="pinta-gemini/0.6.0";function et(e){if(!e||typeof e!="object"||Array.isArray(e))return;let t=e,n=t.command??t.CommandLine;return typeof n=="string"?n:void 0}function tt(e,t,n){return(0,Qe.evaluateGuard)(e,t,{timeoutMs:rn,token:n??process.env.PINTA_RELAY_TOKEN??"",disabled:process.env.PINTA_GUARD_DISABLED==="1",userAgent:sn})}var nt=S(T(),1),L=class extends nt.DiskTransport{constructor(t){super({pluginData:t.pluginData,logPrefix:"pinta-gemini",resolveOptions:()=>t.endpoint?{endpoint:t.endpoint,headers:t.headers}:null})}};var rt=S(T(),1),V=class extends rt.DiskSessionTraceManager{constructor(t){super(t.tracePath)}};import le from"os";var w=S(T(),1),cn=S(T(),1),it="0.6.0";function on(e){return{skipRedactKeys:new Set([`${e}.hook`,`${e}.agent`,`${e}.tool_name`,`${e}.session_id`,`${e}.transcript_path`,`${e}.transcriptPath`,`${e}.cwd`]),bashContextKeys:new Set([`${e}.tool_input`,`${e}.tool_response`,`${e}.toolCall`])}}function an(e){return[{key:"service.name",value:{stringValue:e}},{key:"telemetry.sdk.name",value:{stringValue:"pinta-gemini"}},{key:"telemetry.sdk.language",value:{stringValue:"nodejs"}},{key:"telemetry.sdk.version",value:{stringValue:it}},{key:"process.pid",value:{intValue:process.pid}},{key:"process.owner",value:{stringValue:le.userInfo().username}},{key:"host.name",value:{stringValue:le.hostname()}},{key:"host.arch",value:{stringValue:le.arch()}}]}function st(e){let t=Ze(e.agent),n=on(t.prefix),r=[{key:"ingest.type",value:{stringValue:t.ingest}},{key:`${t.prefix}.hook`,value:{stringValue:e.canonical.hook}},{key:`${t.prefix}.agent`,value:{stringValue:e.agent}}];e.product&&r.push({key:`${t.prefix}.product`,value:{stringValue:e.product}}),r.push(...(0,w.attrsFromRecord)(e.event,t.prefix,n));let i=new Set(r.map(s=>s.key));for(let[s,a]of[["session_id",e.canonical.session_id],["cwd",e.canonical.cwd],["tool_name",e.canonical.tool_name]]){let c=`${t.prefix}.${s}`;if(a!=null&&!i.has(c)){let u=(0,w.toOtlpValue)(c,String(a),n);u!==null&&r.push({key:c,value:u})}}return(0,w.buildPayload)({traceId:e.traceId,spanName:`${t.ingest}.${(0,w.snakeCase)(e.canonical.hook)}`,attributes:r,resource:an(t.service),scope:{name:"pinta-gemini",version:it},now:e.now,guard:e.guard})}function ot(e,t,n){if(n&&n.decision==="DENY"){let r=n.userMessage??n.reason??"guard_deny";return v(e)?{decision:"deny",reason:r,systemMessage:n.userMessage??void 0}:{decision:"deny",reason:r}}return!v(e)&&t==="PreToolUse"?{decision:"allow"}:{}}import at from"fs";import un from"path";function ct(e,t){if(process.env.PINTA_GEMINI_DEBUG==="1")try{at.mkdirSync(e.pluginData,{recursive:!0}),at.appendFileSync(un.join(e.pluginData,"invocations.jsonl"),JSON.stringify(t)+`
|
|
14
|
+
`)}catch{}}async function ln(){let e=[];for await(let t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf-8")}function dn(e,t,n){return v(e)?t.hook==="BeforeAgent":t.hook==="PreInvocation"&&n.invocationNum===1}async function ut(){let{agent:e,event:t}=He(),n={},r={},i,s=null,a=Be();try{if(r=JSON.parse(await ln()||"{}"),i=We(e,t,r),!Je(i.hook)){let c=new L(a);await c.flush();let u=i.session_id??"unknown",f=new V(a),y=dn(e,i,r)?f.newTrace(u):f.currentTrace(u);if(i.hook===Xe(e)){let U=et(i.tool_input)??(typeof i.tool_input=="string"?i.tool_input:JSON.stringify(i.tool_input??null));s=await tt({spanId:u,toolName:i.tool_name,toolInput:i.tool_input,rawTextFields:{toolInput:U}},a.guardEndpoint,a.headers["x-pinta-relay-token"])}let E=v(e)?void 0:ze(r);await c.send(st({agent:e,canonical:i,event:r,traceId:y,guard:s,product:E})),n=ot(e,t,s)}}catch(c){process.stderr.write(`[pinta-gemini] error: ${c}
|
|
15
|
+
`),n={}}ct(a,{ts:new Date().toISOString(),pid:process.pid,agent:e,event:t,argv:process.argv.slice(2),received_payload:r,normalized:i??null,guard:s,decision_returned:n}),process.stdout.write(JSON.stringify(n)+`
|
|
16
|
+
`),process.exit(0)}import{opendir as gt,stat as wn}from"node:fs/promises";import{homedir as En}from"node:os";import l from"node:path";import{spawn as dt}from"node:child_process";import{access as pn,copyFile as fn,mkdtemp as gn,stat as pt}from"node:fs/promises";import{constants as mn}from"node:fs";import yn from"node:os";import q from"node:path";var de,hn=["/usr/bin/sqlite3","/opt/homebrew/bin/sqlite3","/usr/local/bin/sqlite3"];async function _n(){for(let t of hn)try{return await pn(t,mn.X_OK),t}catch{}return await new Promise(t=>{let n;try{n=dt("sqlite3",["--version"],{stdio:"ignore"})}catch{t(!1);return}n.on("error",()=>t(!1)),n.on("close",r=>t(r===0))})?"sqlite3":null}async function Pn(){return de===void 0&&(de=await _n()),de}async function vn(e,t,n){await new Promise((r,i)=>{let s=dt(e,[t,`.backup '${n}'`],{stdio:["ignore","ignore","pipe"]}),a="";s.stderr?.on("data",c=>{a+=String(c)}),s.on("error",i),s.on("close",c=>{c===0?r():i(new Error(`sqlite3 .backup exited ${c}${a?`: ${a.trim()}`:""}`))})})}async function bn(e){try{let t=await pt(e);return`${t.mtimeMs}:${t.size}`}catch{return"absent"}}async function lt(e){let t=new Map;for(let n of e)t.set(n,await bn(n));return t}function On(e,t){for(let[n,r]of e)if(t.get(n)!==r)return!1;return!0}async function Tn(e,t,n,r){let i=r.minStableMs??2e3,s=r.maxRetries??3,a=r.now??Date.now,c=r.sleep??(f=>new Promise(y=>setTimeout(y,f))),u=[e,`${e}-wal`,`${e}-shm`];for(let f=1;f<=s;f++){let y=await lt(u);if(y.get(e)==="absent")throw new Error(`snapshot source vanished: ${e}`);let E=0;for(let M of u)try{let _t=await pt(M);E=Math.max(E,_t.mtimeMs)}catch{}if(a()-E<i){await c(i);continue}for(let M of u)y.get(M)!=="absent"&&await fn(M,q.join(t,q.basename(M)));r.onCopied&&await r.onCopied(f);let U=await lt(u);if(On(y,U))return n}throw new Error(`snapshot quiesce-copy did not stabilize after ${s} attempts: ${e}`)}async function ft(e,t={}){let n=t.tmpDirBase??yn.tmpdir(),r=await gn(q.join(n,"pinta-gemini-snap-")),i=q.join(r,q.basename(e.absPath)),s=t.sqlite3Path!==void 0?t.sqlite3Path:await Pn();return s?(await vn(s,e.absPath,i),i):Tn(e.absPath,r,i,t)}var Sn="pinta-gemini",K="antigravity";function kn(){let e=process.env.GEMINI_HOME;return e&&e.length>0?e:l.join(En(),".gemini")}function x(e,t){return l.relative(e,t).split(l.sep).join("/")}function mt(e){return e.endsWith("/.system_generated/logs/transcript.jsonl")||e.endsWith("/.system_generated/logs/transcript_full.jsonl")}function An(e){return e.endsWith(".jsonl")||e.endsWith(".json")}function pe(e){let t=l.extname(e);return t?e.slice(0,-t.length):e}async function yt(){return[kn()]}async function*ht(e){let t;try{t=await gt(e)}catch{return}try{for await(let n of t){let r=l.join(e,n.name);n.isDirectory()?yield*ht(r):n.isFile()&&(yield r)}}catch{return}}async function*j(e){let t;try{t=await gt(e)}catch{return}try{for await(let n of t)yield{name:n.name,isDir:n.isDirectory(),isFile:n.isFile()}}catch{return}}async function*In(e){let t=l.join(e,"tmp");for await(let n of j(t)){if(!n.isDir)continue;let r=n.name,i=l.join(t,r),s=l.join(i,"logs.json");yield{absPath:s,relPath:x(e,s),semantics:"rewritten-doc",projectKey:r};let a=l.join(i,"chats");for await(let c of j(a)){if(!c.isFile||!An(c.name))continue;let u=l.join(a,c.name);yield{absPath:u,relPath:x(e,u),semantics:"rewritten-doc",sessionId:pe(c.name),projectKey:r}}}}async function*xn(e){let t=l.join(e,"antigravity"),n=l.join(t,"conversations");for await(let s of j(n)){if(!s.isFile||!s.name.endsWith(".db"))continue;let a=l.join(n,s.name);yield{absPath:a,relPath:x(e,a),semantics:"database",sessionId:pe(s.name),projectKey:K}}let r=l.join(t,"brain");for await(let s of j(r)){if(!s.isDir)continue;let a=s.name,c=l.join(r,a);for await(let u of ht(c)){let f=x(e,u);yield{absPath:u,relPath:f,semantics:mt(f)?"append-log":"rewritten-doc",sessionId:a,projectKey:K}}}let i=l.join(t,"annotations");for await(let s of j(i)){if(!s.isFile||!s.name.endsWith(".pbtxt"))continue;let a=l.join(i,s.name);yield{absPath:a,relPath:x(e,a),semantics:"rewritten-doc",sessionId:pe(s.name),projectKey:K}}for await(let s of j(t))if(s.isFile&&(s.name.endsWith(".pb")||s.name.endsWith(".pbtxt")||s.name==="installation_id")){let a=l.join(t,s.name);yield{absPath:a,relPath:x(e,a),semantics:"rewritten-doc",projectKey:K}}}async function*jn(e){yield*In(e),yield*xn(e)}async function*Mn(e){let[t]=await yt(),n=e.since?.getTime();for await(let r of jn(t)){let i;try{i=await wn(r.absPath)}catch{continue}i.isFile()&&(n!==void 0&&i.mtime.getTime()<=n||(yield{relPath:r.relPath,absPath:r.absPath,size:i.size,mtime:i.mtime,sessionId:r.sessionId,projectKey:r.projectKey,semantics:r.semantics}))}}function Rn(e){let t=e.split("/");if(t[0]==="tmp")return t[2]==="chats"?"session-log":t[2]==="logs.json"&&t.length===3?"meta":"other";if(t[0]==="antigravity")switch(t[1]){case"conversations":return"session-log";case"brain":return mt(e)?"session-log":"meta";case"annotations":return"meta";default:return"other"}return"other"}var Dn={id:Sn,roots:yt,scan:Mn,classify:Rn,snapshot:e=>ft(e)};Ke();function qn(){let e=process.argv[1];if(!e)return!1;try{return import.meta.url===Cn(Nn(e)).href}catch{return!1}}qn()&&ut();export{Dn as lifecycle};
|
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pinta-ai/pinta-gemini",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Unified OTLP forwarder + guard adapter for Gemini CLI and Antigravity (agy v1.0.x / 2.0) hooks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
7
13
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
8
14
|
"files": [
|
|
9
15
|
"dist",
|
|
@@ -17,7 +23,11 @@
|
|
|
17
23
|
},
|
|
18
24
|
"scripts": {
|
|
19
25
|
"bump": "node scripts/bump.mjs",
|
|
20
|
-
"
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"build": "npm run build:hook && npm run build:esm",
|
|
28
|
+
"build:hook": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node18 --minify --outfile=dist/index.js --banner:js=\"import { createRequire as __pintaCreateRequire } from 'module'; const require = __pintaCreateRequire(import.meta.url);\"",
|
|
29
|
+
"build:esm": "esbuild src/index.mts --bundle --platform=node --format=esm --target=node18 --minify --outfile=dist/index.mjs --banner:js=\"import { createRequire as __pintaCreateRequire } from 'module'; const require = __pintaCreateRequire(import.meta.url);\"",
|
|
30
|
+
"smoke:import": "node scripts/smoke-import.mjs",
|
|
21
31
|
"test": "vitest run",
|
|
22
32
|
"install-hooks": "tsx tools/install-hooks.ts",
|
|
23
33
|
"uninstall-hooks": "tsx tools/install-hooks.ts --uninstall",
|
|
@@ -28,10 +38,15 @@
|
|
|
28
38
|
"demo": "tsx tools/demo-trace.ts"
|
|
29
39
|
},
|
|
30
40
|
"devDependencies": {
|
|
41
|
+
"@pinta-ai/core": "^0.3.0",
|
|
31
42
|
"@types/node": "^22.0.0",
|
|
32
43
|
"esbuild": "^0.24.0",
|
|
33
44
|
"tsx": "^4.0.0",
|
|
34
45
|
"typescript": "^5.7.0",
|
|
35
46
|
"vitest": "^2.1.0"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"registry": "https://registry.npmjs.org",
|
|
50
|
+
"access": "public"
|
|
36
51
|
}
|
|
37
52
|
}
|