@pinta-ai/pinta-opencode 0.10.0 → 0.11.1
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/CHANGELOG.md +42 -0
- package/README.md +50 -8
- package/dist/index.mjs +441 -25
- package/dist/plugin.js +24 -19
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.1
|
|
4
|
+
|
|
5
|
+
- Evaluate the original `tool.execute.after` payload before OpenCode delivers
|
|
6
|
+
supported builtin and raw MCP output. DENY uses fixed safe feedback, preserves
|
|
7
|
+
original masked execution evidence/IDs, and attaches the output target only
|
|
8
|
+
after evaluation. ALLOW, REVIEW and explicit fail-open behavior are unchanged.
|
|
9
|
+
- Return before/after DENY without collector IO. Retain telemetry in a bounded,
|
|
10
|
+
endpoint-aware memory queue using the existing UTF-8 POST limit; flush it
|
|
11
|
+
before the next idle-event POST, rather than waiting behind that POST's ACK.
|
|
12
|
+
The queue is not durable across process termination.
|
|
13
|
+
- Bundle published `@pinta-ai/core` 0.9.1, including its mysql-family-only short
|
|
14
|
+
password masking fix. The effective guard timeout remains 100ms.
|
|
15
|
+
- Isolate private-dependency installation from setup-node's npmjs auth binding.
|
|
16
|
+
The GitHub token is bound only to GitHub Packages; public publishing uses OIDC.
|
|
17
|
+
- Verify the actual OpenCode 1.18.31 CLI with a keyless loopback provider:
|
|
18
|
+
returned builtin success/nonzero exit and raw MCP success/structured output
|
|
19
|
+
are gated. MCP `isError: true` and other thrown executions skip the after hook
|
|
20
|
+
in that host and remain an explicit unsupported error-output boundary.
|
|
21
|
+
- Keep Manager 0.1.11 as the existing envelope floor, without implying it
|
|
22
|
+
supplies native after-phase projection and output-only policy evaluation.
|
|
23
|
+
|
|
24
|
+
Refs PTA-584.
|
|
25
|
+
|
|
26
|
+
## 0.11.0
|
|
27
|
+
|
|
28
|
+
- Guard default timeout 50ms -> 100ms. At 50ms a manager answering in its
|
|
29
|
+
normal range still lost the race often enough to show up as timeout
|
|
30
|
+
fail-opens. The plugin awaits the verdict in-process before the tool runs
|
|
31
|
+
(`tool.execute.before`), so a tool call can now stall on the guard for up to
|
|
32
|
+
100ms instead of 50ms. `PINTA_OPENCODE_GUARD_TIMEOUT_MS` / `guardTimeoutMs`
|
|
33
|
+
still override it.
|
|
34
|
+
- `@pinta-ai/core` `^0.8.0` -> `^0.9.0`: the effective timeout (default or
|
|
35
|
+
override) is declared to the manager as `x-pinta-guard-budget-ms`. Manager
|
|
36
|
+
0.1.10+ plans its own work around it instead of the 50ms it had copied for
|
|
37
|
+
this adaptor, so the backend package check gets 80ms rather than 40ms. Older
|
|
38
|
+
managers ignore the header.
|
|
39
|
+
- README no longer says the guard never blocks tool execution; it does, for up
|
|
40
|
+
to the timeout.
|
|
41
|
+
- Pinta Manager 0.1.11 or later remains the guard-payload compatibility floor.
|
|
42
|
+
|
|
43
|
+
Refs PTA-579.
|
|
44
|
+
|
|
3
45
|
## 0.10.0
|
|
4
46
|
|
|
5
47
|
- Emit exact scalar `opencode.model` with source/provider evidence from verified
|
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@ Unlike the Claude Code / Codex / Copilot adapters (which spawn a process per hoo
|
|
|
6
6
|
|
|
7
7
|
> Status: spec complete and validated end-to-end against **opencode 1.15.3**. See [`SPEC.md`](./SPEC.md), [`PLAN.md`](./PLAN.md), and the empirical record in [`HYPOTHESIS_VALIDATION.md`](./HYPOTHESIS_VALIDATION.md) (§10–13).
|
|
8
8
|
|
|
9
|
+
Returned-output enforcement is additionally verified against **OpenCode 1.18.31**
|
|
10
|
+
using its actual CLI and a keyless loopback provider. The native MCP-error
|
|
11
|
+
limitation below is explicitly excluded from that enforcement claim.
|
|
12
|
+
|
|
9
13
|
## How it hooks in
|
|
10
14
|
|
|
11
15
|
opencode fires plugin hooks around every built-in **and** MCP tool. This adapter uses only non-experimental hooks:
|
|
@@ -16,9 +20,9 @@ opencode fires plugin hooks around every built-in **and** MCP tool. This adapter
|
|
|
16
20
|
| `chat.params` | remember exact-message request model evidence (no extra span) | `{ sessionID, agent, model, message }` |
|
|
17
21
|
| `event` | lifecycle span (Bronze flatten) + flush on `session.idle` | `{ event: { id, type, properties } }`, every event carries `properties.sessionID` |
|
|
18
22
|
| `tool.execute.before` | query guard → **`throw` on DENY** + emit span | input `{ tool, sessionID, callID }`, output `{ args }` (full tool args, mutable) |
|
|
19
|
-
| `tool.execute.after` | tool-result span
|
|
23
|
+
| `tool.execute.after` | query output guard before delivery + original tool-result span | builtin `{ title, output, metadata{ exit, truncated, … }, attachments? }`; raw MCP `{ content, structuredContent?, isError? }` |
|
|
20
24
|
|
|
21
|
-
>
|
|
25
|
+
> Do not depend on the declared `permission.ask` plugin hook. Execution gating uses `tool.execute.before`; output delivery gating uses the awaited `tool.execute.after`.
|
|
22
26
|
|
|
23
27
|
## Install
|
|
24
28
|
|
|
@@ -55,9 +59,9 @@ PINTA_OPENCODE_GUARD=https://your-relay.example.com/guard
|
|
|
55
59
|
|---|---|
|
|
56
60
|
| `endpoint` / `PINTA_OPENCODE_ENDPOINT` | Full OTLP/HTTP traces URL. Falls back to `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` → `OTEL_EXPORTER_OTLP_ENDPOINT` (+`/v1/traces`). No endpoint → telemetry disabled. |
|
|
57
61
|
| `headers` / `PINTA_OPENCODE_HEADERS` | `key=val,key=val` request headers (auth). Falls back to `OTEL_EXPORTER_OTLP_HEADERS`. |
|
|
58
|
-
| `guard` / `PINTA_OPENCODE_GUARD` | Optional. POST'd on `tool.execute.before
|
|
62
|
+
| `guard` / `PINTA_OPENCODE_GUARD` | Optional. POST'd on `tool.execute.before` and `tool.execute.after`; DENY blocks execution or withholds returned output, respectively. No endpoint → governance disabled. |
|
|
59
63
|
| `token` / `PINTA_OPENCODE_TOKEN` | Sent as `x-pinta-relay-token` on guard + OTLP. |
|
|
60
|
-
| `PINTA_OPENCODE_GUARD_TIMEOUT_MS` | Guard client timeout (default `
|
|
64
|
+
| `PINTA_OPENCODE_GUARD_TIMEOUT_MS` | Guard client timeout (default `100`). Declared to the Pinta Manager as its budget, so raising it also lets the manager wait longer. |
|
|
61
65
|
| `PINTA_OPENCODE_GUARD_DISABLED=1` | Force-disable the guard. |
|
|
62
66
|
|
|
63
67
|
Telemetry and governance are independent — endpoint only, guard only, both, or neither all work.
|
|
@@ -75,9 +79,47 @@ body: the OTLP payload itself — { "resourceSpans": [ … one span, opencode.*
|
|
|
75
79
|
|
|
76
80
|
The verdict is then attached to that same span (`pinta.guard.*`) before it is sent, so the span the guard judged is the span the collector stores.
|
|
77
81
|
|
|
78
|
-
A `DENY` becomes `throw new Error(userMessage ?? reason ?? "guard_deny")`. Verified effect: **only that tool is blocked** (`tool.execute.after` does not fire), the reason shows as `✗ … failed` + `Error: <reason>` to the model/TUI, and the session stays alive. `ALLOW`/`REVIEW` pass through.
|
|
79
|
-
|
|
80
|
-
Guard is **fail-open** (no endpoint / `
|
|
82
|
+
A before-hook `DENY` becomes `throw new Error(userMessage ?? reason ?? "guard_deny")`. Verified effect: **only that tool is blocked** (`tool.execute.after` does not fire), the reason shows as `✗ … failed` + `Error: <reason>` to the model/TUI, and the session stays alive. `ALLOW`/`REVIEW` pass through.
|
|
83
|
+
|
|
84
|
+
Guard is **fail-open** (no endpoint / `PINTA_OPENCODE_GUARD_DISABLED=1` / non-200 / timeout / error → allow), so infrastructure failures do not break a session. Each guard call is awaited for at most the configured timeout (100ms by default).
|
|
85
|
+
|
|
86
|
+
### Returned-output enforcement
|
|
87
|
+
|
|
88
|
+
The awaited after hook submits the original redaction-aware `opencode.tool.after`
|
|
89
|
+
span before OpenCode returns the result to the model. Builtin output retains its
|
|
90
|
+
text, exit code and truncation evidence; MCP results retain the full raw result,
|
|
91
|
+
including `content`, `structuredContent` and any `isError` field. The original
|
|
92
|
+
invocation arguments, when provided by the host, remain audit evidence, not new
|
|
93
|
+
operations to evaluate. The guard must recognize
|
|
94
|
+
`opencode.hook=tool.execute.after` and apply returned-content policies without
|
|
95
|
+
rejudging completed input or package operations. The existing Manager envelope
|
|
96
|
+
floor does not establish that output capability.
|
|
97
|
+
|
|
98
|
+
After evaluation, every non-null verdict carries `pinta.guard.target=tool_output`
|
|
99
|
+
on the same span and IDs. This marker is audit metadata, not an input needed to
|
|
100
|
+
activate output policies. DENY throws the fixed safe message
|
|
101
|
+
`Pinta withheld this tool output because it violated an active policy.`
|
|
102
|
+
It never reflects guard-supplied text back to the model. OpenCode surfaces a tool
|
|
103
|
+
error and can continue its session; the adapter does not undo side effects or
|
|
104
|
+
claim that a successfully executed tool failed. The original masked output and
|
|
105
|
+
native exit/`isError` evidence are retained independently of delivery denial.
|
|
106
|
+
|
|
107
|
+
Neither before nor after DENY waits for collector IO. With telemetry configured,
|
|
108
|
+
the original annotated payload is retained in a bounded in-memory queue and
|
|
109
|
+
drained on a later `session.idle` flush before the idle-event POST. The shared 900 KiB UTF-8 POST limit still
|
|
110
|
+
applies; oversized payloads and retention errors are diagnosed without delaying
|
|
111
|
+
or suppressing denial. No telemetry endpoint means no retention. Like the
|
|
112
|
+
existing retry buffer, this queue is not durable across process termination.
|
|
113
|
+
|
|
114
|
+
The host must dispatch the after callback: a tool that throws before returning
|
|
115
|
+
skips it. In OpenCode 1.18.31, **MCP `isError: true` is converted to an exception
|
|
116
|
+
before the after hook**, so this adapter cannot withhold that native error path.
|
|
117
|
+
A directly invoked after-hook test with `isError` is not native error coverage.
|
|
118
|
+
The pinned [MCP conversion](https://github.com/anomalyco/opencode/blob/v1.18.31/packages/opencode/src/mcp/catalog.ts)
|
|
119
|
+
and [tool runner](https://github.com/anomalyco/opencode/blob/v1.18.31/packages/opencode/src/session/tools.ts)
|
|
120
|
+
define this boundary. Returned builtin results, including nonzero shell exit
|
|
121
|
+
codes, and successful raw MCP results use the awaited after hook; undispatched
|
|
122
|
+
host paths are not treated as protected.
|
|
81
123
|
|
|
82
124
|
## Span conventions
|
|
83
125
|
|
|
@@ -157,7 +199,7 @@ src/
|
|
|
157
199
|
│ ├── trace.ts # ULID trace, keyed by sessionID, rotated on chat.message
|
|
158
200
|
│ ├── transport.ts # POST OTLP/HTTP traces (5s), in-memory retry queue
|
|
159
201
|
│ ├── retry-queue.ts # batched flush on next event
|
|
160
|
-
│ ├── guard.ts # POST PINTA_GUARD_ENDPOINT (
|
|
202
|
+
│ ├── guard.ts # POST PINTA_GUARD_ENDPOINT (100ms), fail-open
|
|
161
203
|
│ └── redact.ts # Tier-1 redaction + Tier-3 truncation
|
|
162
204
|
```
|
|
163
205
|
|
package/dist/index.mjs
CHANGED
|
@@ -31,6 +31,370 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
mod
|
|
32
32
|
));
|
|
33
33
|
|
|
34
|
+
// node_modules/@pinta-ai/core/dist/mysql-password.js
|
|
35
|
+
var require_mysql_password = __commonJS({
|
|
36
|
+
"node_modules/@pinta-ai/core/dist/mysql-password.js"(exports) {
|
|
37
|
+
"use strict";
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.mysqlPasswordRanges = mysqlPasswordRanges;
|
|
40
|
+
var EXECUTABLES = /* @__PURE__ */ new Set([
|
|
41
|
+
"mysql",
|
|
42
|
+
"mysqldump",
|
|
43
|
+
"mysqladmin",
|
|
44
|
+
"mysqlcheck",
|
|
45
|
+
"mysqlimport",
|
|
46
|
+
"mysqlshow",
|
|
47
|
+
"mariadb",
|
|
48
|
+
"mariadb-dump",
|
|
49
|
+
"mariadb-admin",
|
|
50
|
+
"mariadb-check",
|
|
51
|
+
"mariadb-import",
|
|
52
|
+
"mariadb-show"
|
|
53
|
+
]);
|
|
54
|
+
var SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "dash", "zsh", "ksh"]);
|
|
55
|
+
var VALUE_OPTIONS = /* @__PURE__ */ new Set([
|
|
56
|
+
"-u",
|
|
57
|
+
"--user",
|
|
58
|
+
"-h",
|
|
59
|
+
"--host",
|
|
60
|
+
"-P",
|
|
61
|
+
"--port",
|
|
62
|
+
"-D",
|
|
63
|
+
"--database",
|
|
64
|
+
"-S",
|
|
65
|
+
"--socket",
|
|
66
|
+
"-e",
|
|
67
|
+
"--execute",
|
|
68
|
+
"--protocol",
|
|
69
|
+
"--default-character-set",
|
|
70
|
+
"--defaults-file",
|
|
71
|
+
"--defaults-extra-file",
|
|
72
|
+
"--login-path",
|
|
73
|
+
"--init-command",
|
|
74
|
+
"--ssl-ca",
|
|
75
|
+
"--ssl-capath",
|
|
76
|
+
"--ssl-cert",
|
|
77
|
+
"--ssl-key",
|
|
78
|
+
"--ssl-cipher"
|
|
79
|
+
]);
|
|
80
|
+
var MAX_NESTING = 16;
|
|
81
|
+
function slice(text, start, end = text.value.length) {
|
|
82
|
+
return { value: text.value.slice(start, end), ranges: text.ranges.slice(start, end) };
|
|
83
|
+
}
|
|
84
|
+
function append(target, text, start, end) {
|
|
85
|
+
target.value += text.value.slice(start, end);
|
|
86
|
+
for (let index = start; index < end; index++)
|
|
87
|
+
target.ranges.push(text.ranges[index]);
|
|
88
|
+
}
|
|
89
|
+
function executable(word) {
|
|
90
|
+
if (!word?.literal)
|
|
91
|
+
return void 0;
|
|
92
|
+
return word.value.slice(word.value.lastIndexOf("/") + 1).replace(/\.exe$/, "");
|
|
93
|
+
}
|
|
94
|
+
function executionIndex(words) {
|
|
95
|
+
let index = 0;
|
|
96
|
+
for (let wrappers = 0; wrappers <= MAX_NESTING; wrappers++) {
|
|
97
|
+
while (/^[A-Za-z_][A-Za-z0-9_]*=/.test(words[index]?.value ?? ""))
|
|
98
|
+
index++;
|
|
99
|
+
const name = executable(words[index]);
|
|
100
|
+
if (!name)
|
|
101
|
+
return void 0;
|
|
102
|
+
if (["!", "if", "then", "elif", "else", "while", "until", "do"].includes(name)) {
|
|
103
|
+
index++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const optionValues = {
|
|
107
|
+
env: ["-u", "--unset", "-C", "--chdir"],
|
|
108
|
+
sudo: [
|
|
109
|
+
"-u",
|
|
110
|
+
"--user",
|
|
111
|
+
"-g",
|
|
112
|
+
"--group",
|
|
113
|
+
"-p",
|
|
114
|
+
"--prompt",
|
|
115
|
+
"-D",
|
|
116
|
+
"--chdir",
|
|
117
|
+
"-R",
|
|
118
|
+
"--chroot",
|
|
119
|
+
"-C",
|
|
120
|
+
"--close-from",
|
|
121
|
+
"-T",
|
|
122
|
+
"--command-timeout"
|
|
123
|
+
],
|
|
124
|
+
exec: ["-a"],
|
|
125
|
+
nice: ["-n", "--adjustment"],
|
|
126
|
+
timeout: ["-s", "--signal", "-k", "--kill-after"]
|
|
127
|
+
};
|
|
128
|
+
const flags = {
|
|
129
|
+
env: ["-i", "--ignore-environment", "-0", "--null"],
|
|
130
|
+
sudo: ["-n", "-E", "-H", "-S", "-b", "-k"],
|
|
131
|
+
command: ["-p"],
|
|
132
|
+
exec: ["-c", "-l"],
|
|
133
|
+
nohup: [],
|
|
134
|
+
nice: [],
|
|
135
|
+
time: ["-p"],
|
|
136
|
+
timeout: ["--foreground", "--preserve-status"]
|
|
137
|
+
};
|
|
138
|
+
if (!(name in flags))
|
|
139
|
+
return index;
|
|
140
|
+
index++;
|
|
141
|
+
while (words[index]?.value.startsWith("-")) {
|
|
142
|
+
const option = words[index++].value;
|
|
143
|
+
if (option === "--")
|
|
144
|
+
break;
|
|
145
|
+
if (flags[name].includes(option))
|
|
146
|
+
continue;
|
|
147
|
+
if ((optionValues[name] ?? []).includes(option)) {
|
|
148
|
+
if (!words[index++])
|
|
149
|
+
return void 0;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if ((optionValues[name] ?? []).some((key2) => key2.startsWith("--") ? option.startsWith(key2 + "=") : option.startsWith(key2) && option.length > 2))
|
|
153
|
+
continue;
|
|
154
|
+
if (name === "nice" && /^-\d+$/.test(option))
|
|
155
|
+
continue;
|
|
156
|
+
return void 0;
|
|
157
|
+
}
|
|
158
|
+
if (name === "timeout") {
|
|
159
|
+
if (!/^\d+(?:\.\d+)?[smhd]?$/.test(words[index++]?.value ?? ""))
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return void 0;
|
|
164
|
+
}
|
|
165
|
+
function substitutionEnd(text, start) {
|
|
166
|
+
let nesting = 1;
|
|
167
|
+
let quote = "";
|
|
168
|
+
for (let index = start; index < text.length; index++) {
|
|
169
|
+
const char = text[index];
|
|
170
|
+
if (char === "\\" && quote !== "'") {
|
|
171
|
+
index++;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (quote) {
|
|
175
|
+
if (char === quote)
|
|
176
|
+
quote = "";
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
if (char === "'" || char === '"')
|
|
180
|
+
quote = char;
|
|
181
|
+
else if (char === "(")
|
|
182
|
+
nesting++;
|
|
183
|
+
else if (char === ")" && --nesting === 0)
|
|
184
|
+
return index;
|
|
185
|
+
}
|
|
186
|
+
return text.length;
|
|
187
|
+
}
|
|
188
|
+
function readWord(text, start, out, depth) {
|
|
189
|
+
const word = { value: "", ranges: [], start, end: start, literal: true };
|
|
190
|
+
let quote = "";
|
|
191
|
+
let index = start;
|
|
192
|
+
while (index < text.value.length) {
|
|
193
|
+
const char = text.value[index];
|
|
194
|
+
if (!quote && /[\s;|&()<>{}]/.test(char))
|
|
195
|
+
break;
|
|
196
|
+
if (char === quote) {
|
|
197
|
+
quote = "";
|
|
198
|
+
index++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (!quote && (char === "'" || char === '"')) {
|
|
202
|
+
quote = char;
|
|
203
|
+
index++;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
if (char === "\\" && quote !== "'") {
|
|
207
|
+
const next = text.value[index + 1];
|
|
208
|
+
if (next === "\n") {
|
|
209
|
+
index += 2;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
if (next && (!quote || /[$`"\\]/.test(next))) {
|
|
213
|
+
append(word, text, index + 1, index + 2);
|
|
214
|
+
index += 2;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (quote !== "'" && text.value.startsWith("$(", index)) {
|
|
219
|
+
const end = substitutionEnd(text.value, index + 2);
|
|
220
|
+
if (text.value[index + 2] !== "(" && depth < MAX_NESTING) {
|
|
221
|
+
scanShell(slice(text, index + 2, end), out, depth + 1);
|
|
222
|
+
}
|
|
223
|
+
append(word, text, index, Math.min(end + 1, text.value.length));
|
|
224
|
+
word.literal = false;
|
|
225
|
+
index = Math.min(end + 1, text.value.length);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (char === "`" && quote !== "'") {
|
|
229
|
+
let end = index + 1;
|
|
230
|
+
while (end < text.value.length && text.value[end] !== "`") {
|
|
231
|
+
end += text.value[end] === "\\" ? 2 : 1;
|
|
232
|
+
}
|
|
233
|
+
if (depth < MAX_NESTING)
|
|
234
|
+
scanShell(slice(text, index + 1, end), out, depth + 1);
|
|
235
|
+
append(word, text, index, Math.min(end + 1, text.value.length));
|
|
236
|
+
word.literal = false;
|
|
237
|
+
index = Math.min(end + 1, text.value.length);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
if (char === "$" && quote !== "'")
|
|
241
|
+
word.literal = false;
|
|
242
|
+
append(word, text, index, index + 1);
|
|
243
|
+
index++;
|
|
244
|
+
}
|
|
245
|
+
word.end = index;
|
|
246
|
+
return word;
|
|
247
|
+
}
|
|
248
|
+
function scanCommand(text, words, out, depth) {
|
|
249
|
+
const index = executionIndex(words);
|
|
250
|
+
if (index === void 0)
|
|
251
|
+
return;
|
|
252
|
+
const name = executable(words[index]);
|
|
253
|
+
if (SHELLS.has(name) && depth < MAX_NESTING) {
|
|
254
|
+
for (let option = index + 1; option < words.length; option++) {
|
|
255
|
+
const value = words[option].value;
|
|
256
|
+
if (value === "--command" || /^-[a-zA-Z]*c[a-zA-Z]*$/.test(value)) {
|
|
257
|
+
const script = words[option + 1];
|
|
258
|
+
if (script)
|
|
259
|
+
scanShell(script, out, depth + 1);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (!value.startsWith("-") || value === "--")
|
|
263
|
+
return;
|
|
264
|
+
if (value === "-o" || value === "-O")
|
|
265
|
+
option++;
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (!EXECUTABLES.has(name))
|
|
270
|
+
return;
|
|
271
|
+
for (let option = index + 1; option < words.length; option++) {
|
|
272
|
+
const word = words[option];
|
|
273
|
+
if (word.value === "--")
|
|
274
|
+
return;
|
|
275
|
+
if (VALUE_OPTIONS.has(word.value)) {
|
|
276
|
+
option++;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (word.value.startsWith("-p") && word.value.length > 2) {
|
|
280
|
+
out.push({ start: text.ranges[word.start].start, end: text.ranges[word.end - 1].end });
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
function scanShell(text, out, depth = 0) {
|
|
285
|
+
let words = [];
|
|
286
|
+
let redirect;
|
|
287
|
+
const documents = [];
|
|
288
|
+
let index = 0;
|
|
289
|
+
const flush = () => {
|
|
290
|
+
scanCommand(text, words, out, depth);
|
|
291
|
+
words = [];
|
|
292
|
+
redirect = void 0;
|
|
293
|
+
};
|
|
294
|
+
while (index < text.value.length) {
|
|
295
|
+
const char = text.value[index];
|
|
296
|
+
if (char === "\n") {
|
|
297
|
+
flush();
|
|
298
|
+
index++;
|
|
299
|
+
for (const document of documents.splice(0)) {
|
|
300
|
+
while (index < text.value.length) {
|
|
301
|
+
const end = text.value.indexOf("\n", index);
|
|
302
|
+
const stop = end < 0 ? text.value.length : end;
|
|
303
|
+
const line = text.value.slice(index, stop);
|
|
304
|
+
index = end < 0 ? stop : stop + 1;
|
|
305
|
+
if ((document.stripTabs ? line.replace(/^\t+/, "") : line) === document.delimiter)
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
} else if (/\s/.test(char)) {
|
|
310
|
+
index++;
|
|
311
|
+
} else if (char === "#") {
|
|
312
|
+
const end = text.value.indexOf("\n", index);
|
|
313
|
+
index = end < 0 ? text.value.length : end;
|
|
314
|
+
} else if (text.value.startsWith("((", index)) {
|
|
315
|
+
flush();
|
|
316
|
+
index = Math.min(substitutionEnd(text.value, index + 1) + 1, text.value.length);
|
|
317
|
+
} else if (/[;|&(){}]/.test(char)) {
|
|
318
|
+
flush();
|
|
319
|
+
index++;
|
|
320
|
+
} else if (char === "<" || char === ">") {
|
|
321
|
+
if (words.at(-1)?.end === index && /^\d+$/.test(words.at(-1).value))
|
|
322
|
+
words.pop();
|
|
323
|
+
const match = /^(?:<<<|<<-?|>>|<&|>&|[<>])/.exec(text.value.slice(index));
|
|
324
|
+
redirect = match?.[0] ?? char;
|
|
325
|
+
index += redirect.length;
|
|
326
|
+
} else {
|
|
327
|
+
const word = readWord(text, index, out, depth);
|
|
328
|
+
index = word.end;
|
|
329
|
+
if (redirect === "<<" || redirect === "<<-") {
|
|
330
|
+
documents.push({ delimiter: word.value, stripTabs: redirect === "<<-" });
|
|
331
|
+
}
|
|
332
|
+
if (!redirect)
|
|
333
|
+
words.push(word);
|
|
334
|
+
redirect = void 0;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
flush();
|
|
338
|
+
}
|
|
339
|
+
function jsonString(text, start, end) {
|
|
340
|
+
const result = { value: "", ranges: [] };
|
|
341
|
+
for (let index = start + 1; index < end - 1; index++) {
|
|
342
|
+
if (text.value[index] !== "\\") {
|
|
343
|
+
append(result, text, index, index + 1);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
const stop = index + (text.value[index + 1] === "u" ? 6 : 2);
|
|
347
|
+
const decoded = JSON.parse(`"${text.value.slice(index, stop)}"`);
|
|
348
|
+
result.value += decoded;
|
|
349
|
+
for (let unit = 0; unit < decoded.length; unit++) {
|
|
350
|
+
result.ranges.push({ start: text.ranges[index].start, end: text.ranges[stop - 1].end });
|
|
351
|
+
}
|
|
352
|
+
index = stop - 1;
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
function mysqlPasswordRanges(input) {
|
|
357
|
+
const text = {
|
|
358
|
+
value: input,
|
|
359
|
+
ranges: Array.from({ length: input.length }, (_, index) => ({ start: index, end: index + 1 }))
|
|
360
|
+
};
|
|
361
|
+
const out = [];
|
|
362
|
+
if (/^\s*[{[]/.test(input)) {
|
|
363
|
+
let parsed;
|
|
364
|
+
try {
|
|
365
|
+
parsed = JSON.parse(input);
|
|
366
|
+
} catch {
|
|
367
|
+
scanShell(text, out);
|
|
368
|
+
return out;
|
|
369
|
+
}
|
|
370
|
+
if (parsed !== null && typeof parsed === "object") {
|
|
371
|
+
const strings = /"(?:[^"\\]|\\.)*"/g;
|
|
372
|
+
let match;
|
|
373
|
+
while (match = strings.exec(input)) {
|
|
374
|
+
const key2 = JSON.parse(match[0]);
|
|
375
|
+
if (key2 !== "command" && key2 !== "cmd")
|
|
376
|
+
continue;
|
|
377
|
+
const separator = /^\s*:\s*/.exec(input.slice(strings.lastIndex));
|
|
378
|
+
if (!separator)
|
|
379
|
+
continue;
|
|
380
|
+
const start = strings.lastIndex + separator[0].length;
|
|
381
|
+
if (input[start] !== '"')
|
|
382
|
+
continue;
|
|
383
|
+
strings.lastIndex = start;
|
|
384
|
+
const value = strings.exec(input);
|
|
385
|
+
if (value?.index === start) {
|
|
386
|
+
scanShell(jsonString(text, start, start + value[0].length), out);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
scanShell(text, out);
|
|
393
|
+
return out;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
34
398
|
// node_modules/@pinta-ai/core/dist/redact.js
|
|
35
399
|
var require_redact = __commonJS({
|
|
36
400
|
"node_modules/@pinta-ai/core/dist/redact.js"(exports) {
|
|
@@ -43,6 +407,7 @@ var require_redact = __commonJS({
|
|
|
43
407
|
exports.resolveOverlaps = resolveOverlaps;
|
|
44
408
|
exports.applyMatches = applyMatches;
|
|
45
409
|
exports.redact = redact;
|
|
410
|
+
var mysql_password_js_1 = require_mysql_password();
|
|
46
411
|
exports.MAX_BYTES = 102400;
|
|
47
412
|
function truncateTo(input, maxBytes) {
|
|
48
413
|
const buf = Buffer.from(input, "utf-8");
|
|
@@ -96,7 +461,7 @@ var require_redact = __commonJS({
|
|
|
96
461
|
},
|
|
97
462
|
{
|
|
98
463
|
type: "cli_password_short",
|
|
99
|
-
//
|
|
464
|
+
// Option shape only. collectMatches also requires a literal mysql-family executable.
|
|
100
465
|
regex: /\s-p([^\s'"]+)/g,
|
|
101
466
|
captureGroup: 1,
|
|
102
467
|
requireContext: "bash"
|
|
@@ -114,6 +479,19 @@ var require_redact = __commonJS({
|
|
|
114
479
|
for (const pattern of exports.PATTERNS) {
|
|
115
480
|
if (pattern.requireContext && pattern.requireContext !== opts.context)
|
|
116
481
|
continue;
|
|
482
|
+
if (pattern.type === "cli_password_short") {
|
|
483
|
+
for (const { start, end } of (0, mysql_password_js_1.mysqlPasswordRanges)(input)) {
|
|
484
|
+
out.push({
|
|
485
|
+
start,
|
|
486
|
+
end,
|
|
487
|
+
replaceStart: start,
|
|
488
|
+
replaceEnd: end,
|
|
489
|
+
type: pattern.type,
|
|
490
|
+
replacement: `-p[REDACTED:${pattern.type}]`
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
117
495
|
const re = new RegExp(pattern.regex.source, pattern.regex.flags);
|
|
118
496
|
let m;
|
|
119
497
|
while ((m = re.exec(input)) !== null) {
|
|
@@ -155,7 +533,7 @@ var require_redact = __commonJS({
|
|
|
155
533
|
const sorted = [...matches].sort((a, b) => b.replaceStart - a.replaceStart);
|
|
156
534
|
let out = input;
|
|
157
535
|
for (const m of sorted) {
|
|
158
|
-
out = out.slice(0, m.replaceStart) + `[REDACTED:${m.type}]` + out.slice(m.replaceEnd);
|
|
536
|
+
out = out.slice(0, m.replaceStart) + (m.replacement ?? `[REDACTED:${m.type}]`) + out.slice(m.replaceEnd);
|
|
159
537
|
}
|
|
160
538
|
return out;
|
|
161
539
|
}
|
|
@@ -356,10 +734,11 @@ var require_guard = __commonJS({
|
|
|
356
734
|
"node_modules/@pinta-ai/core/dist/guard.js"(exports) {
|
|
357
735
|
"use strict";
|
|
358
736
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
359
|
-
exports.GUARD_STATUS_REFUSED = void 0;
|
|
737
|
+
exports.GUARD_STATUS_REFUSED = exports.GUARD_BUDGET_HEADER = void 0;
|
|
360
738
|
exports.evaluateGuard = evaluateGuard2;
|
|
361
739
|
var DEFAULT_TIMEOUT_MS2 = 1e4;
|
|
362
740
|
var DEFAULT_UA = "pinta-core";
|
|
741
|
+
exports.GUARD_BUDGET_HEADER = "x-pinta-guard-budget-ms";
|
|
363
742
|
function failOpen2(clientRttMs, failOpenReason) {
|
|
364
743
|
return {
|
|
365
744
|
decision: "ALLOW",
|
|
@@ -391,7 +770,10 @@ var require_guard = __commonJS({
|
|
|
391
770
|
// Spread rather than defaulted: an empty string is not "unknown" to the
|
|
392
771
|
// manager, it is a claim that fails validation, and sending one would
|
|
393
772
|
// make every adapter that never sets this look like it tried.
|
|
394
|
-
...opts.agentType ? { "x-pinta-agent-type": opts.agentType } : {}
|
|
773
|
+
...opts.agentType ? { "x-pinta-agent-type": opts.agentType } : {},
|
|
774
|
+
// Same rule: the manager discards a non-positive or non-numeric budget
|
|
775
|
+
// and falls back to its table, so such a value is not sent at all.
|
|
776
|
+
...Number.isFinite(timeoutMs) && timeoutMs > 0 ? { [exports.GUARD_BUDGET_HEADER]: String(timeoutMs) } : {}
|
|
395
777
|
},
|
|
396
778
|
body: JSON.stringify(payload),
|
|
397
779
|
signal: controller.signal
|
|
@@ -577,7 +959,7 @@ var require_retry_queue = __commonJS({
|
|
|
577
959
|
}
|
|
578
960
|
};
|
|
579
961
|
exports.DiskRetryQueue = DiskRetryQueue;
|
|
580
|
-
var
|
|
962
|
+
var MemoryRetryQueue2 = class {
|
|
581
963
|
entries = [];
|
|
582
964
|
enqueue(payload) {
|
|
583
965
|
this.entries.push(payload);
|
|
@@ -595,7 +977,7 @@ var require_retry_queue = __commonJS({
|
|
|
595
977
|
return this.entries.length;
|
|
596
978
|
}
|
|
597
979
|
};
|
|
598
|
-
exports.MemoryRetryQueue =
|
|
980
|
+
exports.MemoryRetryQueue = MemoryRetryQueue2;
|
|
599
981
|
}
|
|
600
982
|
});
|
|
601
983
|
|
|
@@ -1026,7 +1408,7 @@ var require_dist = __commonJS({
|
|
|
1026
1408
|
"node_modules/@pinta-ai/core/dist/index.js"(exports) {
|
|
1027
1409
|
"use strict";
|
|
1028
1410
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1029
|
-
exports.loadEnvFile = exports.parseEnvFile = exports.envFilePath = exports.MemorySessionTraceManager = exports.DiskSessionTraceManager = exports.generateUlid = exports.TraceManager = exports.MAX_POST_BYTES = exports.parseHeadersEnv = exports.envOptionsResolver = exports.postOtlp = exports.MemoryTransport = exports.DiskTransport = exports.MemoryRetryQueue = exports.DiskRetryQueue = exports.GUARD_STATUS_REFUSED = exports.evaluateGuard = exports.MAX_SPAN_BYTES = exports.enforceSpanBudget = exports.mergeBatch = exports.attachGuard = exports.buildPayload = exports.clientAttrs = exports.guardAttrs = exports.attrsFromRecord = exports.toOtlpValue = exports.snakeCase = exports.newSpanId = exports.ulidToTraceId = exports.MAX_BYTES = exports.PATTERNS = exports.applyMatches = exports.resolveOverlaps = exports.collectMatches = exports.truncateTo = exports.truncate = exports.redact = void 0;
|
|
1411
|
+
exports.loadEnvFile = exports.parseEnvFile = exports.envFilePath = exports.MemorySessionTraceManager = exports.DiskSessionTraceManager = exports.generateUlid = exports.TraceManager = exports.MAX_POST_BYTES = exports.parseHeadersEnv = exports.envOptionsResolver = exports.postOtlp = exports.MemoryTransport = exports.DiskTransport = exports.MemoryRetryQueue = exports.DiskRetryQueue = exports.GUARD_BUDGET_HEADER = exports.GUARD_STATUS_REFUSED = exports.evaluateGuard = exports.MAX_SPAN_BYTES = exports.enforceSpanBudget = exports.mergeBatch = exports.attachGuard = exports.buildPayload = exports.clientAttrs = exports.guardAttrs = exports.attrsFromRecord = exports.toOtlpValue = exports.snakeCase = exports.newSpanId = exports.ulidToTraceId = exports.MAX_BYTES = exports.PATTERNS = exports.applyMatches = exports.resolveOverlaps = exports.collectMatches = exports.truncateTo = exports.truncate = exports.redact = void 0;
|
|
1030
1412
|
var redact_js_1 = require_redact();
|
|
1031
1413
|
Object.defineProperty(exports, "redact", { enumerable: true, get: function() {
|
|
1032
1414
|
return redact_js_1.redact;
|
|
@@ -1096,6 +1478,9 @@ var require_dist = __commonJS({
|
|
|
1096
1478
|
Object.defineProperty(exports, "GUARD_STATUS_REFUSED", { enumerable: true, get: function() {
|
|
1097
1479
|
return guard_js_1.GUARD_STATUS_REFUSED;
|
|
1098
1480
|
} });
|
|
1481
|
+
Object.defineProperty(exports, "GUARD_BUDGET_HEADER", { enumerable: true, get: function() {
|
|
1482
|
+
return guard_js_1.GUARD_BUDGET_HEADER;
|
|
1483
|
+
} });
|
|
1099
1484
|
var retry_queue_js_1 = require_retry_queue();
|
|
1100
1485
|
Object.defineProperty(exports, "DiskRetryQueue", { enumerable: true, get: function() {
|
|
1101
1486
|
return retry_queue_js_1.DiskRetryQueue;
|
|
@@ -1209,7 +1594,7 @@ function resolveConfig(options = {}) {
|
|
|
1209
1594
|
if (relayToken && !Object.keys(headers).some((k) => k.toLowerCase() === "x-pinta-relay-token")) {
|
|
1210
1595
|
headers["x-pinta-relay-token"] = relayToken;
|
|
1211
1596
|
}
|
|
1212
|
-
const guardTimeoutMs = options.guardTimeoutMs ?? (Number(process.env.PINTA_OPENCODE_GUARD_TIMEOUT_MS) ||
|
|
1597
|
+
const guardTimeoutMs = options.guardTimeoutMs ?? (Number(process.env.PINTA_OPENCODE_GUARD_TIMEOUT_MS) || 100);
|
|
1213
1598
|
return {
|
|
1214
1599
|
endpoint: resolveEndpoint(options),
|
|
1215
1600
|
headers,
|
|
@@ -1229,6 +1614,23 @@ var Transport = class extends import_core3.MemoryTransport {
|
|
|
1229
1614
|
logPrefix: "pinta-opencode",
|
|
1230
1615
|
resolveOptions: () => config.endpoint ? { endpoint: config.endpoint, headers: config.headers } : null
|
|
1231
1616
|
});
|
|
1617
|
+
this.config = config;
|
|
1618
|
+
}
|
|
1619
|
+
deferred = new import_core3.MemoryRetryQueue();
|
|
1620
|
+
/** A decided DENY must return to the host without collector IO. */
|
|
1621
|
+
defer(payload) {
|
|
1622
|
+
if (!this.config.endpoint) return;
|
|
1623
|
+
const bytes = Buffer.byteLength(JSON.stringify(payload), "utf8");
|
|
1624
|
+
if (bytes > import_core3.MAX_POST_BYTES) {
|
|
1625
|
+
process.stderr.write(`[pinta-opencode] deferred payload exceeds POST budget (${bytes} bytes); dropped
|
|
1626
|
+
`);
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
this.deferred.enqueue(payload);
|
|
1630
|
+
}
|
|
1631
|
+
async flush() {
|
|
1632
|
+
await super.flush();
|
|
1633
|
+
for (const payload of this.deferred.drain()) await super.send(payload);
|
|
1232
1634
|
}
|
|
1233
1635
|
};
|
|
1234
1636
|
|
|
@@ -1242,10 +1644,10 @@ var import_core8 = __toESM(require_dist(), 1);
|
|
|
1242
1644
|
var import_core5 = __toESM(require_dist(), 1);
|
|
1243
1645
|
|
|
1244
1646
|
// src/core/version.ts
|
|
1245
|
-
var ADAPTER_VERSION = "0.
|
|
1647
|
+
var ADAPTER_VERSION = "0.11.1";
|
|
1246
1648
|
|
|
1247
1649
|
// src/core/guard.ts
|
|
1248
|
-
var DEFAULT_TIMEOUT_MS =
|
|
1650
|
+
var DEFAULT_TIMEOUT_MS = 100;
|
|
1249
1651
|
var GUARD_UA = `pinta-opencode/${ADAPTER_VERSION}`;
|
|
1250
1652
|
function evaluateGuard(payload, endpoint, opts = {}) {
|
|
1251
1653
|
return (0, import_core5.evaluateGuard)(payload, endpoint, {
|
|
@@ -1609,6 +2011,7 @@ var Telemetry = class {
|
|
|
1609
2011
|
const props = record(ev.properties) ?? {};
|
|
1610
2012
|
const sessionId = eventSessionID(ev.type, props);
|
|
1611
2013
|
const model = this.models.event(ev.type, props);
|
|
2014
|
+
if (ev.type === "session.idle") await this.transport.flush();
|
|
1612
2015
|
await this.emit(`opencode.event.${ev.type ?? "unknown"}`, sessionId, modelFields({
|
|
1613
2016
|
hook: "event",
|
|
1614
2017
|
event_type: ev.type,
|
|
@@ -1616,7 +2019,6 @@ var Telemetry = class {
|
|
|
1616
2019
|
cwd: process.cwd(),
|
|
1617
2020
|
...props
|
|
1618
2021
|
}, model));
|
|
1619
|
-
if (ev.type === "session.idle") await this.transport.flush();
|
|
1620
2022
|
}
|
|
1621
2023
|
/**
|
|
1622
2024
|
* Tool span from `tool.execute.before`, before the guard has been asked. The
|
|
@@ -1638,17 +2040,23 @@ var Telemetry = class {
|
|
|
1638
2040
|
async toolBefore(input, args, guard) {
|
|
1639
2041
|
await this.send((0, import_core7.attachGuard)(this.toolBeforePayload(input, args), guard));
|
|
1640
2042
|
}
|
|
1641
|
-
/**
|
|
1642
|
-
|
|
2043
|
+
/** Raw MCP results arrive before OpenCode converts their content to output. */
|
|
2044
|
+
toolAfterPayload(input, output) {
|
|
1643
2045
|
const meta = output.metadata ?? {};
|
|
1644
|
-
|
|
2046
|
+
return this.build("opencode.tool.after", input.sessionID, modelFields({
|
|
1645
2047
|
...toolIdentity("tool.execute.after", input),
|
|
2048
|
+
tool_input: input.args,
|
|
1646
2049
|
title: output.title,
|
|
1647
|
-
tool_response: output.output,
|
|
2050
|
+
tool_response: output.content !== void 0 ? output : output.output,
|
|
2051
|
+
attachments: output.attachments,
|
|
1648
2052
|
exit: meta.exit,
|
|
1649
2053
|
truncated: meta.truncated
|
|
1650
2054
|
}, this.models.afterTool(input)));
|
|
1651
2055
|
}
|
|
2056
|
+
/** Tool result span from `tool.execute.after`, incl. exit code / truncation. */
|
|
2057
|
+
async toolAfter(input, output) {
|
|
2058
|
+
await this.send(this.toolAfterPayload(input, output));
|
|
2059
|
+
}
|
|
1652
2060
|
};
|
|
1653
2061
|
function toolIdentity(hook, input) {
|
|
1654
2062
|
return {
|
|
@@ -1670,6 +2078,7 @@ function warn(scope, err) {
|
|
|
1670
2078
|
process.stderr.write(`[pinta-opencode] ${scope}: ${err?.message ?? String(err)}
|
|
1671
2079
|
`);
|
|
1672
2080
|
}
|
|
2081
|
+
var OUTPUT_DENIED_MESSAGE = "Pinta withheld this tool output because it violated an active policy.";
|
|
1673
2082
|
function failOpen(scope, fn) {
|
|
1674
2083
|
return async (...args) => {
|
|
1675
2084
|
try {
|
|
@@ -1684,18 +2093,26 @@ var PintaOpencode = async (_input, options) => {
|
|
|
1684
2093
|
const transport = new Transport({ endpoint: config.endpoint, headers: config.headers });
|
|
1685
2094
|
const trace = new import_core4.MemorySessionTraceManager();
|
|
1686
2095
|
const telemetry = new Telemetry(transport, trace, config);
|
|
1687
|
-
async function guardAndTrace(
|
|
2096
|
+
async function guardAndTrace(hook, build) {
|
|
1688
2097
|
let guard = null;
|
|
1689
2098
|
try {
|
|
1690
|
-
const payload =
|
|
2099
|
+
const payload = build();
|
|
1691
2100
|
guard = await evaluateGuard(
|
|
1692
2101
|
payload,
|
|
1693
2102
|
config.guardEndpoint,
|
|
1694
2103
|
{ timeoutMs: config.guardTimeoutMs, token: config.relayToken, disabled: config.guardDisabled }
|
|
1695
2104
|
);
|
|
1696
|
-
|
|
2105
|
+
(0, import_core8.attachGuard)(payload, guard);
|
|
2106
|
+
if (hook === "tool.execute.after" && guard) {
|
|
2107
|
+
payload.resourceSpans[0].scopeSpans[0].spans[0].attributes.push({
|
|
2108
|
+
key: "pinta.guard.target",
|
|
2109
|
+
value: { stringValue: "tool_output" }
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
if (guard?.decision === "DENY") transport.defer(payload);
|
|
2113
|
+
else await telemetry.send(payload);
|
|
1697
2114
|
} catch (err) {
|
|
1698
|
-
warn(
|
|
2115
|
+
warn(hook, err);
|
|
1699
2116
|
}
|
|
1700
2117
|
return guard;
|
|
1701
2118
|
}
|
|
@@ -1715,16 +2132,15 @@ var PintaOpencode = async (_input, options) => {
|
|
|
1715
2132
|
// ★ governance gate: guard query → DENY throws (blocks just this tool).
|
|
1716
2133
|
// Telemetry/guard-infra errors are fail-open; only a DENY decision escapes.
|
|
1717
2134
|
"tool.execute.before": async (input, output) => {
|
|
1718
|
-
const guard = await guardAndTrace(input, output?.args);
|
|
2135
|
+
const guard = await guardAndTrace("tool.execute.before", () => telemetry.toolBeforePayload(input, output?.args));
|
|
1719
2136
|
if (guard?.decision === "DENY") {
|
|
1720
2137
|
throw new Error(guard.userMessage ?? guard.reason ?? "guard_deny");
|
|
1721
2138
|
}
|
|
1722
2139
|
},
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
"
|
|
1726
|
-
|
|
1727
|
-
)
|
|
2140
|
+
"tool.execute.after": async (input, output) => {
|
|
2141
|
+
const guard = await guardAndTrace("tool.execute.after", () => telemetry.toolAfterPayload(input, output));
|
|
2142
|
+
if (guard?.decision === "DENY") throw new Error(OUTPUT_DENIED_MESSAGE);
|
|
2143
|
+
}
|
|
1728
2144
|
};
|
|
1729
2145
|
};
|
|
1730
2146
|
var plugin_default = PintaOpencode;
|
package/dist/plugin.js
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
import { createRequire as __pintaCreateRequire } from 'module'; const require = __pintaCreateRequire(import.meta.url);
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
`)
|
|
5
|
-
|
|
2
|
+
var Dt=Object.create;var Ae=Object.defineProperty;var It=Object.getOwnPropertyDescriptor;var At=Object.getOwnPropertyNames;var Mt=Object.getPrototypeOf,wt=Object.prototype.hasOwnProperty;var v=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,n)=>(typeof require<"u"?require:e)[n]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var T=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var kt=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of At(e))!wt.call(t,s)&&s!==n&&Ae(t,s,{get:()=>e[s],enumerable:!(r=It(e,s))||r.enumerable});return t};var D=(t,e,n)=>(n=t!=null?Dt(Mt(t)):{},kt(e||!t||!t.__esModule?Ae(n,"default",{value:t,enumerable:!0}):n,t));var Re=T(se=>{"use strict";Object.defineProperty(se,"__esModule",{value:!0});se.mysqlPasswordRanges=$t;var Rt=new Set(["mysql","mysqldump","mysqladmin","mysqlcheck","mysqlimport","mysqlshow","mariadb","mariadb-dump","mariadb-admin","mariadb-check","mariadb-import","mariadb-show"]),xt=new Set(["sh","bash","dash","zsh","ksh"]),jt=new Set(["-u","--user","-h","--host","-P","--port","-D","--database","-S","--socket","-e","--execute","--protocol","--default-character-set","--defaults-file","--defaults-extra-file","--login-path","--init-command","--ssl-ca","--ssl-capath","--ssl-cert","--ssl-key","--ssl-cipher"]),X=16;function Me(t,e,n=t.value.length){return{value:t.value.slice(e,n),ranges:t.ranges.slice(e,n)}}function G(t,e,n,r){t.value+=e.value.slice(n,r);for(let s=n;s<r;s++)t.ranges.push(e.ranges[s])}function we(t){if(t?.literal)return t.value.slice(t.value.lastIndexOf("/")+1).replace(/\.exe$/,"")}function Nt(t){let e=0;for(let n=0;n<=X;n++){for(;/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[e]?.value??"");)e++;let r=we(t[e]);if(!r)return;if(["!","if","then","elif","else","while","until","do"].includes(r)){e++;continue}let s={env:["-u","--unset","-C","--chdir"],sudo:["-u","--user","-g","--group","-p","--prompt","-D","--chdir","-R","--chroot","-C","--close-from","-T","--command-timeout"],exec:["-a"],nice:["-n","--adjustment"],timeout:["-s","--signal","-k","--kill-after"]},i={env:["-i","--ignore-environment","-0","--null"],sudo:["-n","-E","-H","-S","-b","-k"],command:["-p"],exec:["-c","-l"],nohup:[],nice:[],time:["-p"],timeout:["--foreground","--preserve-status"]};if(!(r in i))return e;for(e++;t[e]?.value.startsWith("-");){let a=t[e++].value;if(a==="--")break;if(!i[r].includes(a)){if((s[r]??[]).includes(a)){if(!t[e++])return;continue}if(!(s[r]??[]).some(o=>o.startsWith("--")?a.startsWith(o+"="):a.startsWith(o)&&a.length>2)&&!(r==="nice"&&/^-\d+$/.test(a)))return}}if(r==="timeout"&&!/^\d+(?:\.\d+)?[smhd]?$/.test(t[e++]?.value??""))return}}function ke(t,e){let n=1,r="";for(let s=e;s<t.length;s++){let i=t[s];if(i==="\\"&&r!=="'"){s++;continue}if(r){i===r&&(r="");continue}if(i==="'"||i==='"')r=i;else if(i==="(")n++;else if(i===")"&&--n===0)return s}return t.length}function qt(t,e,n,r){let s={value:"",ranges:[],start:e,end:e,literal:!0},i="",a=e;for(;a<t.value.length;){let o=t.value[a];if(!i&&/[\s;|&()<>{}]/.test(o))break;if(o===i){i="",a++;continue}if(!i&&(o==="'"||o==='"')){i=o,a++;continue}if(o==="\\"&&i!=="'"){let u=t.value[a+1];if(u===`
|
|
3
|
+
`){a+=2;continue}if(u&&(!i||/[$`"\\]/.test(u))){G(s,t,a+1,a+2),a+=2;continue}}if(i!=="'"&&t.value.startsWith("$(",a)){let u=ke(t.value,a+2);t.value[a+2]!=="("&&r<X&&x(Me(t,a+2,u),n,r+1),G(s,t,a,Math.min(u+1,t.value.length)),s.literal=!1,a=Math.min(u+1,t.value.length);continue}if(o==="`"&&i!=="'"){let u=a+1;for(;u<t.value.length&&t.value[u]!=="`";)u+=t.value[u]==="\\"?2:1;r<X&&x(Me(t,a+1,u),n,r+1),G(s,t,a,Math.min(u+1,t.value.length)),s.literal=!1,a=Math.min(u+1,t.value.length);continue}o==="$"&&i!=="'"&&(s.literal=!1),G(s,t,a,a+1),a++}return s.end=a,s}function Ct(t,e,n,r){let s=Nt(e);if(s===void 0)return;let i=we(e[s]);if(xt.has(i)&&r<X){for(let a=s+1;a<e.length;a++){let o=e[a].value;if(o==="--command"||/^-[a-zA-Z]*c[a-zA-Z]*$/.test(o)){let u=e[a+1];u&&x(u,n,r+1);return}if(!o.startsWith("-")||o==="--")return;(o==="-o"||o==="-O")&&a++}return}if(Rt.has(i))for(let a=s+1;a<e.length;a++){let o=e[a];if(o.value==="--")return;if(jt.has(o.value)){a++;continue}o.value.startsWith("-p")&&o.value.length>2&&n.push({start:t.ranges[o.start].start,end:t.ranges[o.end-1].end})}}function x(t,e,n=0){let r=[],s,i=[],a=0,o=()=>{Ct(t,r,e,n),r=[],s=void 0};for(;a<t.value.length;){let u=t.value[a];if(u===`
|
|
4
|
+
`){o(),a++;for(let l of i.splice(0))for(;a<t.value.length;){let d=t.value.indexOf(`
|
|
5
|
+
`,a),h=d<0?t.value.length:d,$=t.value.slice(a,h);if(a=d<0?h:h+1,(l.stripTabs?$.replace(/^\t+/,""):$)===l.delimiter)break}}else if(/\s/.test(u))a++;else if(u==="#"){let l=t.value.indexOf(`
|
|
6
|
+
`,a);a=l<0?t.value.length:l}else if(t.value.startsWith("((",a))o(),a=Math.min(ke(t.value,a+1)+1,t.value.length);else if(/[;|&(){}]/.test(u))o(),a++;else if(u==="<"||u===">")r.at(-1)?.end===a&&/^\d+$/.test(r.at(-1).value)&&r.pop(),s=/^(?:<<<|<<-?|>>|<&|>&|[<>])/.exec(t.value.slice(a))?.[0]??u,a+=s.length;else{let l=qt(t,a,e,n);a=l.end,(s==="<<"||s==="<<-")&&i.push({delimiter:l.value,stripTabs:s==="<<-"}),s||r.push(l),s=void 0}}o()}function Bt(t,e,n){let r={value:"",ranges:[]};for(let s=e+1;s<n-1;s++){if(t.value[s]!=="\\"){G(r,t,s,s+1);continue}let i=s+(t.value[s+1]==="u"?6:2),a=JSON.parse(`"${t.value.slice(s,i)}"`);r.value+=a;for(let o=0;o<a.length;o++)r.ranges.push({start:t.ranges[s].start,end:t.ranges[i-1].end});s=i-1}return r}function $t(t){let e={value:t,ranges:Array.from({length:t.length},(r,s)=>({start:s,end:s+1}))},n=[];if(/^\s*[{[]/.test(t)){let r;try{r=JSON.parse(t)}catch{return x(e,n),n}if(r!==null&&typeof r=="object"){let s=/"(?:[^"\\]|\\.)*"/g,i;for(;i=s.exec(t);){let a=JSON.parse(i[0]);if(a!=="command"&&a!=="cmd")continue;let o=/^\s*:\s*/.exec(t.slice(s.lastIndex));if(!o)continue;let u=s.lastIndex+o[0].length;if(t[u]!=='"')continue;s.lastIndex=u;let l=s.exec(t);l?.index===u&&x(Bt(e,u,u+l[0].length),n)}}return n}return x(e,n),n}});var oe=T(m=>{"use strict";Object.defineProperty(m,"__esModule",{value:!0});m.PATTERNS=m.MAX_BYTES=void 0;m.truncateTo=xe;m.truncate=Ft;m.collectMatches=je;m.resolveOverlaps=Ne;m.applyMatches=qe;m.redact=Ut;var Gt=Re();m.MAX_BYTES=102400;function xe(t,e){let n=Buffer.from(t,"utf-8");return n.length<=e?t:`${n.subarray(0,Math.max(0,e)).toString("utf-8")}\u2026[TRUNCATED:${n.length}]`}function Ft(t){return xe(t,m.MAX_BYTES)}m.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 je(t,e){let n=[];for(let r of m.PATTERNS){if(r.requireContext&&r.requireContext!==e.context)continue;if(r.type==="cli_password_short"){for(let{start:a,end:o}of(0,Gt.mysqlPasswordRanges)(t))n.push({start:a,end:o,replaceStart:a,replaceEnd:o,type:r.type,replacement:`-p[REDACTED:${r.type}]`});continue}let s=new RegExp(r.regex.source,r.regex.flags),i;for(;(i=s.exec(t))!==null;){let a=r.captureGroup??0,o=i[a];if(o===void 0){i.index===s.lastIndex&&s.lastIndex++;continue}let u=i.index,l=i.index+i[0].length,d=u+i[0].indexOf(o),h=d+o.length;n.push({start:u,end:l,replaceStart:d,replaceEnd:h,type:r.type}),i.index===s.lastIndex&&s.lastIndex++}}return n}function Ne(t){let e=[...t].sort((s,i)=>s.start!==i.start?s.start-i.start:i.end-i.start-(s.end-s.start)),n=[],r=-1;for(let s of e)s.start<r||(n.push(s),r=s.end);return n}function qe(t,e){let n=[...e].sort((s,i)=>i.replaceStart-s.replaceStart),r=t;for(let s of n)r=r.slice(0,s.replaceStart)+(s.replacement??`[REDACTED:${s.type}]`)+r.slice(s.replaceEnd);return r}function Ut(t,e={}){if(t.length===0)return t;let n=je(t,e);if(n.length===0)return t;let r=Ne(n);return qe(t,r)}});var ae=T(p=>{"use strict";var Vt=p&&p.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(p,"__esModule",{value:!0});p.MAX_SPAN_BYTES=void 0;p.ulidToTraceId=Ge;p.newSpanId=Fe;p.snakeCase=Yt;p.toOtlpValue=Ue;p.attrsFromRecord=Wt;p.guardAttrs=Ve;p.clientAttrs=Le;p.enforceSpanBudget=Xe;p.buildPayload=zt;p.attachGuard=Ye;p.mergeBatch=Zt;var Lt=Vt(v("crypto")),ie=oe(),Xt="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ge(t){if(t.length!==26)throw new Error(`ulidToTraceId: expected 26 chars, got ${t.length}`);let e=0n;for(let r of t){let s=Xt.indexOf(r);if(s<0)throw new Error(`ulidToTraceId: invalid Crockford char "${r}"`);e=e<<5n|BigInt(s)}let n=(1n<<128n)-1n;return e&=n,e.toString(16).padStart(32,"0")}function Fe(){return Lt.default.randomBytes(8).toString("hex")}function Yt(t){return t.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 Y(t,e,n){let r=(0,ie.truncate)(e);if((n.skipRedactKeys??Ce).has(t))return r;let s=(n.bashContextKeys??Ce).has(t)?"bash":void 0;return(0,ie.redact)(r,{context:s})}function Ue(t,e,n={}){if(e==null)return null;switch(typeof e){case"string":return{stringValue:Y(t,e,n)};case"boolean":return{boolValue:e};case"number":return Number.isInteger(e)?{intValue:e}:{doubleValue:e};case"object":try{return{stringValue:Y(t,JSON.stringify(e),n)}}catch{return{stringValue:Y(t,String(e),n)}}default:return{stringValue:Y(t,String(e),n)}}}function Wt(t,e,n={}){let r=[];for(let[s,i]of Object.entries(t)){let a=`${e}.${s}`,o=Ue(a,i,n);o!==null&&r.push({key:a,value:o})}return r}function Ve(t){let e=[{key:"pinta.guard.decision",value:{stringValue:t.decision.toLowerCase()}},{key:"pinta.guard.duration_ms",value:{intValue:t.durationMs}}];return t.reason&&e.push({key:"pinta.guard.matched_rule",value:{stringValue:t.reason}}),t.failOpenReason&&e.push({key:"pinta.guard.fail_open_reason",value:{stringValue:t.failOpenReason}}),e}function Le(t){return[{key:"pinta.client.rtt_ms",value:Number.isInteger(t.rttMs)?{intValue:t.rttMs}:{doubleValue:t.rttMs}},{key:"pinta.client.op",value:{stringValue:t.op}}]}p.MAX_SPAN_BYTES=800*1024;function Be(t){let e=JSON.stringify(t);return e===void 0?0:Buffer.byteLength(e,"utf-8")}var $e=1024;function Xe(t,e=p.MAX_SPAN_BYTES){let n=Be(t);for(let r=0;r<100&&n>e;r++){let s=null,i=0;for(let u of t.attributes){if(!("stringValue"in u.value))continue;let l=Buffer.byteLength(u.value.stringValue,"utf-8");l>i&&(i=l,s=u.value)}if(s===null||i<=$e)break;let a=n-e,o=Math.max($e,i-a-64);s.stringValue=(0,ie.truncateTo)(s.stringValue,o),n=Be(t)}return t}function zt(t){let e=t.now??Date.now(),n=(BigInt(e)*1000000n).toString(),r=[...t.attributes],s={traceId:Ge(t.traceId),spanId:Fe(),name:t.spanName,kind:t.spanKind??1,startTimeUnixNano:n,endTimeUnixNano:n,attributes:r};Xe(s);let i={resourceSpans:[{resource:{attributes:t.resource},scopeSpans:[{scope:t.scope,spans:[s]}]}]};return Ye(i,t.guard,t.clientTiming)}function Ye(t,e,n){let r=t.resourceSpans[0]?.scopeSpans[0]?.spans[0];if(!r)return t;e&&r.attributes.push(...Ve(e));let s=n??(e?.clientRttMs!==void 0?{op:"guard",rttMs:e.clientRttMs}:null);return s&&r.attributes.push(...Le(s)),t}function Zt(t){let e=[];for(let n of t)e.push(...n.resourceSpans);return{resourceSpans:e}}});var We=T(P=>{"use strict";Object.defineProperty(P,"__esModule",{value:!0});P.GUARD_STATUS_REFUSED=P.GUARD_BUDGET_HEADER=void 0;P.evaluateGuard=Qt;var Kt=1e4,Ht="pinta-core";P.GUARD_BUDGET_HEADER="x-pinta-guard-budget-ms";function W(t,e){return{decision:"ALLOW",reason:null,userMessage:null,durationMs:t,clientRttMs:t,failOpenReason:e}}var Jt=new Set(["ALLOW","DENY","REVIEW"]);P.GUARD_STATUS_REFUSED=410;async function Qt(t,e,n={}){if(!e||n.disabled)return null;let r=n.timeoutMs??Kt,s=Date.now(),i=new AbortController,a=setTimeout(()=>i.abort(),r);try{let o=await fetch(e,{method:"POST",headers:{"content-type":"application/json","user-agent":n.userAgent??Ht,"x-pinta-relay-token":n.token??"",...n.agentType?{"x-pinta-agent-type":n.agentType}:{},...Number.isFinite(r)&&r>0?{[P.GUARD_BUDGET_HEADER]:String(r)}:{}},body:JSON.stringify(t),signal:i.signal});if(o.status===P.GUARD_STATUS_REFUSED)return W(Date.now()-s,"refused");if(o.status!==200)return W(Date.now()-s,"error");let u=await o.json(),l=u?.durationMs===void 0||typeof u.durationMs=="number"&&Number.isFinite(u.durationMs);if(!u||!Jt.has(u.decision)||!l)return W(Date.now()-s,"error");let d=Date.now()-s;return{decision:u.decision,reason:u.reason??null,userMessage:u.userMessage??null,durationMs:u.durationMs??d,clientRttMs:d}}catch(o){let u=o.name,l=u==="AbortError"||u==="TimeoutError"?"timeout":"error";return W(Date.now()-s,l)}finally{clearTimeout(a)}}});var z=T(F=>{"use strict";var en=F&&F.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(F,"__esModule",{value:!0});F.atomicWriteFileSync=tn;var ze=en(v("fs"));function tn(t,e){let n=`${t}.${process.pid}.tmp`;ze.default.writeFileSync(n,e),ze.default.renameSync(n,t)}});var le=T(I=>{"use strict";var Ze=I&&I.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(I,"__esModule",{value:!0});I.MemoryRetryQueue=I.DiskRetryQueue=void 0;var y=Ze(v("fs")),Z=Ze(v("path")),nn=z(),K=1e3,rn=50,sn=5,ue=class{filePath;lockPath;logPrefix;constructor(e,n){this.filePath=Z.default.join(e,"failed-spans.jsonl"),this.lockPath=this.filePath+".lock",this.logPrefix=n}enqueue(e){try{y.default.mkdirSync(Z.default.dirname(this.filePath),{recursive:!0});let n=JSON.stringify({savedAt:new Date().toISOString(),payload:e})+`
|
|
7
|
+
`;y.default.appendFileSync(this.filePath,n),this.trim()}catch(n){process.stderr.write(`[${this.logPrefix}] retry-queue enqueue failed: ${n}
|
|
8
|
+
`)}}readAll(){try{let e=y.default.readFileSync(this.filePath,"utf-8"),n=[];for(let r of e.split(`
|
|
9
|
+
`))if(r.trim())try{let s=JSON.parse(r);Array.isArray(s?.payload?.resourceSpans)&&n.push(s)}catch{}return n}catch{return[]}}rewrite(e){try{if(e.length===0){y.default.existsSync(this.filePath)&&y.default.unlinkSync(this.filePath);return}y.default.mkdirSync(Z.default.dirname(this.filePath),{recursive:!0}),(0,nn.atomicWriteFileSync)(this.filePath,e.map(n=>JSON.stringify(n)).join(`
|
|
6
10
|
`)+`
|
|
7
|
-
`)}catch(
|
|
8
|
-
`)}}tryAcquireLock(){let e=Date.now();try{
|
|
9
|
-
`),!1}for(;Date.now()-e
|
|
10
|
-
`),!1;try{let i=
|
|
11
|
-
`),this.rewrite(e.slice(
|
|
12
|
-
`),!1}return!0}catch(i){return process.stderr.write(`[${
|
|
13
|
-
`),!1}finally{clearTimeout(s)}}var
|
|
14
|
-
`);let i=[].concat(...
|
|
15
|
-
`)}finally{this.queue.release()}}dropIfOversized(e){let
|
|
16
|
-
`),!0)}};f.DiskTransport=
|
|
17
|
-
`);return}await
|
|
18
|
-
`);let i=[].concat(...
|
|
19
|
-
`)}}};
|
|
20
|
-
`)){let n=t.trim();if(!n||n.startsWith("#"))continue;let s=n.indexOf("=");if(s<0)continue;let i=n.slice(0,s).trim(),c=n.slice(s+1).trim();(c.startsWith('"')&&c.endsWith('"')||c.startsWith("'")&&c.endsWith("'"))&&(c=c.slice(1,-1)),i&&(e[i]=c)}return e}function Jt(r){let e;try{e=Wt.default.readFileSync(r,"utf-8")}catch{return}let t=et(e);for(let[n,s]of Object.entries(t))process.env[n]===void 0&&(process.env[n]=s)}});var S=P(a=>{"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.loadEnvFile=a.parseEnvFile=a.envFilePath=a.MemorySessionTraceManager=a.DiskSessionTraceManager=a.generateUlid=a.TraceManager=a.MAX_POST_BYTES=a.parseHeadersEnv=a.envOptionsResolver=a.postOtlp=a.MemoryTransport=a.DiskTransport=a.MemoryRetryQueue=a.DiskRetryQueue=a.GUARD_STATUS_REFUSED=a.evaluateGuard=a.MAX_SPAN_BYTES=a.enforceSpanBudget=a.mergeBatch=a.attachGuard=a.buildPayload=a.clientAttrs=a.guardAttrs=a.attrsFromRecord=a.toOtlpValue=a.snakeCase=a.newSpanId=a.ulidToTraceId=a.MAX_BYTES=a.PATTERNS=a.applyMatches=a.resolveOverlaps=a.collectMatches=a.truncateTo=a.truncate=a.redact=void 0;var M=ee();Object.defineProperty(a,"redact",{enumerable:!0,get:function(){return M.redact}});Object.defineProperty(a,"truncate",{enumerable:!0,get:function(){return M.truncate}});Object.defineProperty(a,"truncateTo",{enumerable:!0,get:function(){return M.truncateTo}});Object.defineProperty(a,"collectMatches",{enumerable:!0,get:function(){return M.collectMatches}});Object.defineProperty(a,"resolveOverlaps",{enumerable:!0,get:function(){return M.resolveOverlaps}});Object.defineProperty(a,"applyMatches",{enumerable:!0,get:function(){return M.applyMatches}});Object.defineProperty(a,"PATTERNS",{enumerable:!0,get:function(){return M.PATTERNS}});Object.defineProperty(a,"MAX_BYTES",{enumerable:!0,get:function(){return M.MAX_BYTES}});var _=re();Object.defineProperty(a,"ulidToTraceId",{enumerable:!0,get:function(){return _.ulidToTraceId}});Object.defineProperty(a,"newSpanId",{enumerable:!0,get:function(){return _.newSpanId}});Object.defineProperty(a,"snakeCase",{enumerable:!0,get:function(){return _.snakeCase}});Object.defineProperty(a,"toOtlpValue",{enumerable:!0,get:function(){return _.toOtlpValue}});Object.defineProperty(a,"attrsFromRecord",{enumerable:!0,get:function(){return _.attrsFromRecord}});Object.defineProperty(a,"guardAttrs",{enumerable:!0,get:function(){return _.guardAttrs}});Object.defineProperty(a,"clientAttrs",{enumerable:!0,get:function(){return _.clientAttrs}});Object.defineProperty(a,"buildPayload",{enumerable:!0,get:function(){return _.buildPayload}});Object.defineProperty(a,"attachGuard",{enumerable:!0,get:function(){return _.attachGuard}});Object.defineProperty(a,"mergeBatch",{enumerable:!0,get:function(){return _.mergeBatch}});Object.defineProperty(a,"enforceSpanBudget",{enumerable:!0,get:function(){return _.enforceSpanBudget}});Object.defineProperty(a,"MAX_SPAN_BYTES",{enumerable:!0,get:function(){return _.MAX_SPAN_BYTES}});var rt=qe();Object.defineProperty(a,"evaluateGuard",{enumerable:!0,get:function(){return rt.evaluateGuard}});Object.defineProperty(a,"GUARD_STATUS_REFUSED",{enumerable:!0,get:function(){return rt.GUARD_STATUS_REFUSED}});var nt=oe();Object.defineProperty(a,"DiskRetryQueue",{enumerable:!0,get:function(){return nt.DiskRetryQueue}});Object.defineProperty(a,"MemoryRetryQueue",{enumerable:!0,get:function(){return nt.MemoryRetryQueue}});var x=Xe();Object.defineProperty(a,"DiskTransport",{enumerable:!0,get:function(){return x.DiskTransport}});Object.defineProperty(a,"MemoryTransport",{enumerable:!0,get:function(){return x.MemoryTransport}});Object.defineProperty(a,"postOtlp",{enumerable:!0,get:function(){return x.postOtlp}});Object.defineProperty(a,"envOptionsResolver",{enumerable:!0,get:function(){return x.envOptionsResolver}});Object.defineProperty(a,"parseHeadersEnv",{enumerable:!0,get:function(){return x.parseHeadersEnv}});Object.defineProperty(a,"MAX_POST_BYTES",{enumerable:!0,get:function(){return x.MAX_POST_BYTES}});var st=le();Object.defineProperty(a,"TraceManager",{enumerable:!0,get:function(){return st.TraceManager}});Object.defineProperty(a,"generateUlid",{enumerable:!0,get:function(){return st.generateUlid}});var ot=He();Object.defineProperty(a,"DiskSessionTraceManager",{enumerable:!0,get:function(){return ot.DiskSessionTraceManager}});Object.defineProperty(a,"MemorySessionTraceManager",{enumerable:!0,get:function(){return ot.MemorySessionTraceManager}});var he=tt();Object.defineProperty(a,"envFilePath",{enumerable:!0,get:function(){return he.envFilePath}});Object.defineProperty(a,"parseEnvFile",{enumerable:!0,get:function(){return he.parseEnvFile}});Object.defineProperty(a,"loadEnvFile",{enumerable:!0,get:function(){return he.loadEnvFile}})});var at=b(S(),1);import Qt from"node:fs";import me from"node:path";var F=b(S(),1);function Ht(){return(0,F.envFilePath)(".config/opencode","pinta-opencode.env","OPENCODE_CONFIG_DIR")}function it(r=Ht()){(0,F.loadEnvFile)(r)}function ye(...r){return r.find(e=>e)||void 0}function er(r){let e=ye(r.endpoint,process.env.PINTA_OPENCODE_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"}var tr="opencode-ai";function rr(){let r=me.dirname(process.execPath);for(let e=0;e<5;e++){try{let n=JSON.parse(Qt.readFileSync(me.join(r,"package.json"),"utf8"));if(n.name===tr&&typeof n.version=="string"&&n.version)return n.version}catch{}let t=me.dirname(r);if(t===r)break;r=t}}function nr(){let r=process.env.OPENCODE_VERSION?.trim();return r||rr()}function ut(r={}){it();let e=ye(r.token,process.env.PINTA_OPENCODE_TOKEN),t=(0,at.parseHeadersEnv)(r.headers??process.env.PINTA_OPENCODE_HEADERS??process.env.OTEL_EXPORTER_OTLP_HEADERS);e&&!Object.keys(t).some(s=>s.toLowerCase()==="x-pinta-relay-token")&&(t["x-pinta-relay-token"]=e);let n=r.guardTimeoutMs??(Number(process.env.PINTA_OPENCODE_GUARD_TIMEOUT_MS)||50);return{endpoint:er(r),headers:t,guardEndpoint:ye(r.guard,process.env.PINTA_OPENCODE_GUARD),relayToken:e,guardTimeoutMs:n,guardDisabled:process.env.PINTA_OPENCODE_GUARD_DISABLED==="1",serviceVersion:nr()}}var ct=b(S(),1),z=class extends ct.MemoryTransport{constructor(e){super({logPrefix:"pinta-opencode",resolveOptions:()=>e.endpoint?{endpoint:e.endpoint,headers:e.headers}:null})}};var _e=b(S(),1);var mt=b(S(),1);var dt=b(S(),1);var W="0.10.0";var sr=50,or=`pinta-opencode/${W}`;function lt(r,e,t={}){return(0,dt.evaluateGuard)(r,e,{timeoutMs:t.timeoutMs??sr,token:t.token,disabled:t.disabled,userAgent:or})}var ht=b(S(),1);import ve from"os";var j=b(S(),1);var K;function ir(){if(K===void 0)try{K=ve.userInfo().username}catch{K=process.env.USER??process.env.LOGNAME??(typeof process.getuid=="function"?String(process.getuid()):"unknown")}return K}var ft=W,ar=new Set(["opencode.hook","opencode.event_type","opencode.tool_name","opencode.session_id","opencode.sessionID","opencode.tool_use_id","opencode.cwd","opencode.agent","opencode.model","opencode.exit","opencode.truncated","opencode.title"]),ur=new Set(["opencode.tool_input","opencode.tool_response"]),cr={skipRedactKeys:ar,bashContextKeys:ur};function dr(r){let e=[{key:"ingest.type",value:{stringValue:"opencode"}}];return e.push(...(0,j.attrsFromRecord)(r,"opencode",cr)),e}function lr(r){return[{key:"service.name",value:{stringValue:"opencode"}},...r?[{key:"service.version",value:{stringValue:r}}]:[],{key:"telemetry.sdk.name",value:{stringValue:"pinta-opencode"}},{key:"telemetry.sdk.language",value:{stringValue:"nodejs"}},{key:"telemetry.sdk.version",value:{stringValue:ft}},{key:"process.pid",value:{intValue:process.pid}},{key:"process.owner",value:{stringValue:ir()}},{key:"host.name",value:{stringValue:ve.hostname()}},{key:"host.arch",value:{stringValue:ve.arch()}}]}function pt(r){return(0,j.buildPayload)({traceId:r.traceId,spanName:r.name,attributes:dr(r.fields),resource:lr(r.serviceVersion),scope:{name:"pinta-opencode",version:ft},now:r.now})}var N={messages:1024,requests:512,calls:2048,endedSessions:128,ttlMs:9e5},fr=new Set(["unknown","undefined","null","n/a","none","-","auto","default"]);function g(r){if(typeof r!="string")return;let e=r.trim();return e&&e.length<=1024?e:void 0}function w(r){let e=g(r);if(!(!e||e.startsWith("{")||e.startsWith("[")||fr.has(e.toLowerCase())))return e}function E(r){return r!==null&&typeof r=="object"&&!Array.isArray(r)?r:void 0}function v(r,e,t){let n=E(r),s=n?w(n.modelID)??w(n.id):w(r);if(s)return{model:s,source:n?`${e}.${w(n.modelID)?"modelID":"id"}`:e,provider:w(n?.providerID??t)}}function T(...r){return JSON.stringify(r)}function C(...r){let e=r.map(g).filter(t=>t!==void 0);return e.every(t=>t===e[0])?e[0]:void 0}function Te(r,e){let t=E(e.info),n=E(e.part);return typeof r=="string"&&r.startsWith("session.")?C(e.sessionID,t?.id):C(e.sessionID,t?.sessionID,n?.sessionID)}var $=class{constructor(e,t){this.limit=e;this.now=t}entries=new Map;get(e){let t=this.entries.get(e);if(t&&t.expires>this.now())return t.value;this.entries.delete(e)}set(e,t){this.entries.delete(e),this.entries.set(e,{value:t,expires:this.now()+N.ttlMs}),this.entries.size>this.limit&&this.entries.delete(this.entries.keys().next().value)}removeWhere(e){for(let[t,n]of this.entries)e(n.value)&&this.entries.delete(t)}clearSession(e){this.removeWhere(t=>t.sessionID===e)}},Z=class{constructor(e=Date.now){this.now=e;this.messages=new $(N.messages,e),this.requests=new $(N.requests,e),this.calls=new $(N.calls,e)}messages;requests;calls;ended=new Map;chatMessage(e,t){let n=C(e.sessionID,t?.message?.sessionID);if(!n)return;this.ended.delete(n);let s=C(e.messageID,t?.message?.id);if(!s)return;let i=g(t?.message?.agent??e.agent),c=t?.message?.model!==void 0;this.requests.set(T(n,s,i??""),{sessionID:n,messageID:s,evidence:v(c?t.message?.model:e.model,c?"requested:chat.message.message.model":"requested:chat.message.model")})}chatParams(e){let t=C(e.sessionID,e.message?.sessionID),n=g(e.message?.id),s=g(e.agent);!t||!n||!s||this.ended.has(t)||this.requests.set(T(t,n,s),{sessionID:t,messageID:n,evidence:v(e.model,"requested:chat.params.model")})}fresh(e){return e===void 0||typeof e=="number"&&Number.isFinite(e)&&e>=this.now()-N.ttlMs}clearSession(e){this.messages.clearSession(e),this.requests.clearSession(e),this.calls.clearSession(e)}event(e,t){let n=Te(e,t),s=E(t.info),i=E(t.part),c=["session.idle","session.deleted","session.error"].includes(e??"")||e==="session.status"&&E(t.status)?.type==="idle";if(n&&c?(this.clearSession(n),this.ended.delete(n),this.ended.set(n,!0),this.ended.size>N.endedSessions&&this.ended.delete(this.ended.keys().next().value)):n&&e==="session.created"&&(this.clearSession(n),this.ended.delete(n)),e==="message.updated"&&s){if(!C(t.sessionID,s.sessionID)&&(t.sessionID||s.sessionID))return;let o=g(s.id),u=g(s.agent),d=s.role,l=d==="assistant"?v(w(s.modelID),"reported:message.updated.info.modelID",s.providerID)??v(s.model,"reported:message.updated.info.model"):d==="user"?v(s.model,"requested:message.updated.info.model"):void 0;if(!l&&d==="user"&&!("model"in s)&&n&&o&&(l=this.requests.get(T(n,o,u??""))?.evidence),d==="user"&&"model"in s&&!l&&n&&o&&this.requests.set(T(n,o,u??""),{sessionID:n,messageID:o}),l&&(l={...l,agent:u,messageID:o}),n&&o&&!this.ended.has(n)&&this.fresh(E(s.time)?.created)){let O=this.messages.get(T(n,o)),Oe=O?.ambiguous||!!(d==="assistant"&&O?.evidence&&l&&(O.evidence.model!==l.model||O.evidence.provider!==l.provider||O.evidence.agent!==l.agent));this.messages.set(T(n,o),{sessionID:n,messageID:o,evidence:Oe?void 0:l,ambiguous:Oe})}return l}if(n&&e==="message.removed"){let o=g(t.messageID);this.messages.removeWhere(u=>u.sessionID===n&&u.messageID===o),this.requests.removeWhere(u=>u.sessionID===n&&u.messageID===o),this.calls.removeWhere(u=>u.sessionID===n&&u.messageID===o)}if(n&&e==="message.part.removed"){let o=g(t.partID);o&&this.calls.removeWhere(u=>u.sessionID===n&&u.partID===o)}if(e==="message.part.delta"){let o=g(t.messageID);return n&&o&&!this.ended.has(n)?this.messages.get(T(n,o))?.evidence:void 0}if(e==="message.part.updated"&&i){if(!n||this.ended.has(n)||!this.fresh(t.time))return;let o=g(i.messageID);if(!o)return;let u=g(i.callID);if(i.type==="tool"&&u){let d=T(n,u),l=this.calls.get(d);if(!l?.closed){let O=g(i.tool);this.calls.set(d,{sessionID:n,messageID:o,partID:g(i.id),tool:O,pinned:l?.pinned,ambiguous:l?.ambiguous||!!(l&&(l.messageID!==o||l.tool!==O))})}}return this.messages.get(T(n,o))?.evidence}return typeof e=="string"&&e.startsWith("session.")?v(s?.model,`selected:${e}.info.model`)??v(t.model,`selected:${e}.model`):v(t.model,"reported:event.model")??v(w(t.modelID),"reported:event.modelID",t.providerID)}tool(e,t){let n=v(w(e.modelID),"reported:tool.modelID",e.providerID)??v(e.model,"reported:tool.model",e.providerID),s=g(e.sessionID),i=g(e.callID);if(!s||!i)return n;let c=T(s,i),o=this.calls.get(c),u=n;if(!("model"in e)&&!("modelID"in e)&&o&&!o.closed&&!o.ambiguous&&!this.ended.has(s)&&(!o.tool||o.tool===e.tool)){let d=this.messages.get(T(s,o.messageID));d?.ambiguous||(u=o.pinned??d?.evidence),(e.agent&&u?.agent&&e.agent!==u.agent||e.messageID&&e.messageID!==o.messageID)&&(u=void 0)}return o&&!o.closed&&this.calls.set(c,{...o,pinned:t?void 0:u,closed:t}),u}beforeTool(e){return this.tool(e,!1)}afterTool(e){return this.tool(e,!0)}};function J(r,e){let{model:t,model_source:n,...s}=r;return"model"in r&&t!==e?.model&&(s.model_raw=t),"model_source"in r&&(s.model_source_raw=n),e&&(s.model=e.model,s.model_source=e.source,e.provider&&("provider"in s&&s.provider!==e.provider&&(s.provider_raw=s.provider),s.provider=e.provider),s.agent===void 0&&(s.agent=e.agent),s.message_id===void 0&&(s.message_id=e.messageID)),s}var H=class{constructor(e,t,n){this.transport=e;this.trace=t;this.config=n}models=new Z;build(e,t,n){let s=this.trace.currentTrace(t);return pt({name:e,traceId:s,fields:n,serviceVersion:this.config.serviceVersion})}async send(e){await this.transport.send(e)}async emit(e,t,n){await this.send(this.build(e,t,n))}chatMessage(e,t){this.models.chatMessage(e,t)}chatParams(e){this.models.chatParams(e)}async lifecycle(e){let t=E(e.properties)??{},n=Te(e.type,t),s=this.models.event(e.type,t);await this.emit(`opencode.event.${e.type??"unknown"}`,n,J({hook:"event",event_type:e.type,session_id:n,cwd:process.cwd(),...t},s)),e.type==="session.idle"&&await this.transport.flush()}toolBeforePayload(e,t){return this.build("opencode.tool.before",e.sessionID,J({...gt("tool.execute.before",e),tool_input:t},this.models.beforeTool(e)))}async toolBefore(e,t,n){await this.send((0,ht.attachGuard)(this.toolBeforePayload(e,t),n))}async toolAfter(e,t){let n=t.metadata??{};await this.emit("opencode.tool.after",e.sessionID,J({...gt("tool.execute.after",e),title:t.title,tool_response:t.output,exit:n.exit,truncated:n.truncated},this.models.afterTool(e)))}};function gt(r,e){return{hook:r,tool_name:e.tool,session_id:e.sessionID,tool_use_id:e.callID,cwd:process.cwd(),...e.model!==void 0?{model:e.model}:{},...e.modelID!==void 0?{modelID:e.modelID}:{},...e.providerID!==void 0?{providerID:e.providerID}:{},...e.agent!==void 0?{agent:e.agent}:{},...e.messageID!==void 0?{message_id:e.messageID}:{}}}function yt(r,e){process.stderr.write(`[pinta-opencode] ${r}: ${e?.message??String(e)}
|
|
21
|
-
`)}function
|
|
11
|
+
`)}catch(n){process.stderr.write(`[${this.logPrefix}] retry-queue rewrite failed: ${n}
|
|
12
|
+
`)}}tryAcquireLock(){let e=Date.now();try{y.default.mkdirSync(Z.default.dirname(this.lockPath),{recursive:!0})}catch(n){return process.stderr.write(`[${this.logPrefix}] retry-queue lock mkdir failed: ${n}
|
|
13
|
+
`),!1}for(;Date.now()-e<rn;)try{let n=y.default.openSync(this.lockPath,"wx");return y.default.writeSync(n,String(process.pid)),y.default.closeSync(n),!0}catch(n){if(n?.code!=="EEXIST")return process.stderr.write(`[${this.logPrefix}] retry-queue lock open failed: ${n}
|
|
14
|
+
`),!1;try{let i=y.default.statSync(this.lockPath);if(Date.now()-i.mtimeMs>3e4){y.default.unlinkSync(this.lockPath);continue}}catch{}let r=sn,s=Date.now()+r;for(;Date.now()<s;);}return!1}release(){try{y.default.unlinkSync(this.lockPath)}catch{}}trim(){let e=this.readAll();if(e.length<=K)return;let n=e.length-K;process.stderr.write(`[${this.logPrefix}] retry-queue full, dropping ${n} oldest entries
|
|
15
|
+
`),this.rewrite(e.slice(n))}};I.DiskRetryQueue=ue;var ce=class{entries=[];enqueue(e){this.entries.push(e),this.entries.length>K&&this.entries.splice(0,this.entries.length-K)}drain(){let e=this.entries;return this.entries=[],e}get size(){return this.entries.length}};I.MemoryRetryQueue=ce});var tt=T(f=>{"use strict";Object.defineProperty(f,"__esModule",{value:!0});f.MemoryTransport=f.DiskTransport=f.MAX_POST_BYTES=void 0;f.parseHeadersEnv=Qe;f.envOptionsResolver=et;f.postOtlp=U;var Ke=le(),He=ae(),on=5e3;f.MAX_POST_BYTES=900*1024;function H(t){let e=JSON.stringify(t);return e===void 0?0:Buffer.byteLength(e,"utf-8")}function Je(t,e,n){let r=[],s=[],i=[],a=0;for(let o of t){let u=e(o);if(u>n){s.push(o);continue}i.length>0&&a+u>n&&(r.push(i),i=[],a=0),i.push(o),a+=u}return i.length>0&&r.push(i),{chunks:r,oversize:s}}function Qe(t){if(!t)return{};if(typeof t=="object")return{...t};let e={};for(let n of t.split(",")){let[r,...s]=n.split("=");r&&s.length>0&&(e[r.trim()]=s.join("=").trim())}return e}function et(){let t=process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,e=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,n;return t?n=t.replace(/\/+$/,""):e&&(n=e.replace(/\/+$/,"")+"/v1/traces"),n?{endpoint:n,headers:Qe(process.env.OTEL_EXPORTER_OTLP_HEADERS)}:null}async function U(t,e,n){let r=new AbortController,s=setTimeout(()=>r.abort(),on);try{let i=await fetch(e.endpoint,{method:"POST",headers:{"Content-Type":"application/json",...e.headers},body:JSON.stringify(t),signal:r.signal});if(!i.ok){let a="";try{a=(await i.text()).slice(0,200)}catch{}let o=i.status===401||i.status===403?" \u2014 check OTEL_EXPORTER_OTLP_HEADERS (relay token)":i.status===404?" \u2014 check OTEL_EXPORTER_OTLP_TRACES_ENDPOINT path":i.status>=500?" \u2014 collector may be down":"";return process.stderr.write(`[${n}] OTLP POST ${i.status} ${e.endpoint}${o}${a?` body=${a}`:""}
|
|
16
|
+
`),!1}return!0}catch(i){return process.stderr.write(`[${n}] OTLP POST failed: ${i.message??String(i)}
|
|
17
|
+
`),!1}finally{clearTimeout(s)}}var de=class{queue;logPrefix;resolveOptions;constructor(e){this.queue=new Ke.DiskRetryQueue(e.pluginData,e.logPrefix),this.logPrefix=e.logPrefix,this.resolveOptions=e.resolveOptions??et}async send(e){let n=this.resolveOptions();if(!n||this.dropIfOversized(e))return;await U(e,n,this.logPrefix)||this.queue.enqueue(e)}async flush(){let e=this.resolveOptions();if(e&&this.queue.tryAcquireLock())try{let n=this.queue.readAll();if(n.length===0)return;let{chunks:r,oversize:s}=Je(n,o=>H(o.payload),f.MAX_POST_BYTES);s.length>0&&process.stderr.write(`[${this.logPrefix}] dropping ${s.length} oversized retry ${s.length===1?"entry":"entries"} (each > ${f.MAX_POST_BYTES} bytes \u2014 the backend caps the request body, so they can never be delivered)
|
|
18
|
+
`);let i=[].concat(...r),a=0;for(let o of r){if(!await U((0,He.mergeBatch)(o.map(l=>l.payload)),e,this.logPrefix))break;a+=o.length}this.queue.rewrite(i.slice(a))}catch(n){process.stderr.write(`[${this.logPrefix}] flush failed: ${n?.message??String(n)}
|
|
19
|
+
`)}finally{this.queue.release()}}dropIfOversized(e){let n=H(e);return n<=f.MAX_POST_BYTES?!1:(process.stderr.write(`[${this.logPrefix}] dropping oversized span payload (${n} > ${f.MAX_POST_BYTES} bytes) \u2014 undeliverable, not queued
|
|
20
|
+
`),!0)}};f.DiskTransport=de;var fe=class{queue=new Ke.MemoryRetryQueue;logPrefix;resolveOptions;constructor(e){this.logPrefix=e.logPrefix,this.resolveOptions=e.resolveOptions}async send(e){let n=this.resolveOptions();if(!n)return;if(H(e)>f.MAX_POST_BYTES){process.stderr.write(`[${this.logPrefix}] dropping oversized span payload (> ${f.MAX_POST_BYTES} bytes) \u2014 undeliverable, not buffered
|
|
21
|
+
`);return}await U(e,n,this.logPrefix)||this.queue.enqueue(e)}async flush(){let e=this.resolveOptions();if(!e)return;let n=this.queue.drain();if(n.length===0)return;let{chunks:r,oversize:s}=Je(n,H,f.MAX_POST_BYTES);s.length>0&&process.stderr.write(`[${this.logPrefix}] dropping ${s.length} oversized buffered ${s.length===1?"payload":"payloads"} (each > ${f.MAX_POST_BYTES} bytes \u2014 undeliverable)
|
|
22
|
+
`);let i=[].concat(...r),a=0;for(let o of r){if(!await U((0,He.mergeBatch)(o),e,this.logPrefix))break;a+=o.length}for(let o of i.slice(a))this.queue.enqueue(o)}};f.MemoryTransport=fe});var me=T(k=>{"use strict";var he=k&&k.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(k,"__esModule",{value:!0});k.TraceManager=void 0;k.generateUlid=rt;var nt=he(v("fs")),an=he(v("path")),un=he(v("crypto")),cn=z(),pe="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function rt(){let t=Date.now(),e="",n=t;for(let i=0;i<10;i++)e=pe[n&31]+e,n=Math.floor(n/32);let r=un.default.randomBytes(10),s="";for(let i=0;i<10;i++)s+=pe[r[i]&31];for(;s.length<16;)s+=pe[0];return e+s}var ge=class{tracePath;constructor(e){this.tracePath=e}newTrace(){let e=rt();return this.save(e),e}currentTrace(){try{let e=nt.default.readFileSync(this.tracePath,"utf-8"),{traceId:n}=JSON.parse(e);if(n)return n}catch{}return this.newTrace()}save(e){try{nt.default.mkdirSync(an.default.dirname(this.tracePath),{recursive:!0}),(0,cn.atomicWriteFileSync)(this.tracePath,JSON.stringify({traceId:e}))}catch(n){process.stderr.write(`[pinta-core] trace persistence failed: ${n}
|
|
23
|
+
`)}}};k.TraceManager=ge});var ut=T(A=>{"use strict";var ot=A&&A.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(A,"__esModule",{value:!0});A.MemorySessionTraceManager=A.DiskSessionTraceManager=void 0;var st=ot(v("fs")),ln=ot(v("path")),it=me(),dn=z(),at=200,ye=class{tracePath;maxSessions;constructor(e,n={}){this.tracePath=e,this.maxSessions=n.maxSessions??at}read(){try{let e=JSON.parse(st.default.readFileSync(this.tracePath,"utf-8"));if(e&&typeof e=="object"&&!("traceId"in e))return e}catch{}return{}}write(e){try{st.default.mkdirSync(ln.default.dirname(this.tracePath),{recursive:!0});let n=Object.entries(e),r=n.length>this.maxSessions?Object.fromEntries(n.slice(-this.maxSessions)):e;(0,dn.atomicWriteFileSync)(this.tracePath,JSON.stringify(r))}catch{}}newTrace(e){let n=e||"default",r=(0,it.generateUlid)(),s=this.read();return s[n]=r,this.write(s),r}currentTrace(e){let n=e||"default",r=this.read()[n];return r||this.newTrace(n)}};A.DiskSessionTraceManager=ye;var ve=class{map=new Map;maxSessions;constructor(e={}){this.maxSessions=e.maxSessions??at}newTrace(e){let n=e||"default",r=(0,it.generateUlid)();if(this.map.set(n,r),this.map.size>this.maxSessions){let s=this.map.keys().next().value;s!==void 0&&this.map.delete(s)}return r}currentTrace(e){let n=e||"default";return this.map.get(n)??this.newTrace(n)}};A.MemorySessionTraceManager=ve});var dt=T(R=>{"use strict";var _e=R&&R.__importDefault||function(t){return t&&t.__esModule?t:{default:t}};Object.defineProperty(R,"__esModule",{value:!0});R.envFilePath=gn;R.parseEnvFile=lt;R.loadEnvFile=hn;var fn=_e(v("node:fs")),pn=_e(v("node:os")),ct=_e(v("node:path"));function gn(t,e,n){let r=n?process.env[n]:void 0,s=r&&r.length>0?r:ct.default.join(pn.default.homedir(),t);return ct.default.join(s,e)}function lt(t){let e={};for(let n of t.split(`
|
|
24
|
+
`)){let r=n.trim();if(!r||r.startsWith("#"))continue;let s=r.indexOf("=");if(s<0)continue;let i=r.slice(0,s).trim(),a=r.slice(s+1).trim();(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),i&&(e[i]=a)}return e}function hn(t){let e;try{e=fn.default.readFileSync(t,"utf-8")}catch{return}let n=lt(e);for(let[r,s]of Object.entries(n))process.env[r]===void 0&&(process.env[r]=s)}});var S=T(c=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});c.loadEnvFile=c.parseEnvFile=c.envFilePath=c.MemorySessionTraceManager=c.DiskSessionTraceManager=c.generateUlid=c.TraceManager=c.MAX_POST_BYTES=c.parseHeadersEnv=c.envOptionsResolver=c.postOtlp=c.MemoryTransport=c.DiskTransport=c.MemoryRetryQueue=c.DiskRetryQueue=c.GUARD_BUDGET_HEADER=c.GUARD_STATUS_REFUSED=c.evaluateGuard=c.MAX_SPAN_BYTES=c.enforceSpanBudget=c.mergeBatch=c.attachGuard=c.buildPayload=c.clientAttrs=c.guardAttrs=c.attrsFromRecord=c.toOtlpValue=c.snakeCase=c.newSpanId=c.ulidToTraceId=c.MAX_BYTES=c.PATTERNS=c.applyMatches=c.resolveOverlaps=c.collectMatches=c.truncateTo=c.truncate=c.redact=void 0;var M=oe();Object.defineProperty(c,"redact",{enumerable:!0,get:function(){return M.redact}});Object.defineProperty(c,"truncate",{enumerable:!0,get:function(){return M.truncate}});Object.defineProperty(c,"truncateTo",{enumerable:!0,get:function(){return M.truncateTo}});Object.defineProperty(c,"collectMatches",{enumerable:!0,get:function(){return M.collectMatches}});Object.defineProperty(c,"resolveOverlaps",{enumerable:!0,get:function(){return M.resolveOverlaps}});Object.defineProperty(c,"applyMatches",{enumerable:!0,get:function(){return M.applyMatches}});Object.defineProperty(c,"PATTERNS",{enumerable:!0,get:function(){return M.PATTERNS}});Object.defineProperty(c,"MAX_BYTES",{enumerable:!0,get:function(){return M.MAX_BYTES}});var _=ae();Object.defineProperty(c,"ulidToTraceId",{enumerable:!0,get:function(){return _.ulidToTraceId}});Object.defineProperty(c,"newSpanId",{enumerable:!0,get:function(){return _.newSpanId}});Object.defineProperty(c,"snakeCase",{enumerable:!0,get:function(){return _.snakeCase}});Object.defineProperty(c,"toOtlpValue",{enumerable:!0,get:function(){return _.toOtlpValue}});Object.defineProperty(c,"attrsFromRecord",{enumerable:!0,get:function(){return _.attrsFromRecord}});Object.defineProperty(c,"guardAttrs",{enumerable:!0,get:function(){return _.guardAttrs}});Object.defineProperty(c,"clientAttrs",{enumerable:!0,get:function(){return _.clientAttrs}});Object.defineProperty(c,"buildPayload",{enumerable:!0,get:function(){return _.buildPayload}});Object.defineProperty(c,"attachGuard",{enumerable:!0,get:function(){return _.attachGuard}});Object.defineProperty(c,"mergeBatch",{enumerable:!0,get:function(){return _.mergeBatch}});Object.defineProperty(c,"enforceSpanBudget",{enumerable:!0,get:function(){return _.enforceSpanBudget}});Object.defineProperty(c,"MAX_SPAN_BYTES",{enumerable:!0,get:function(){return _.MAX_SPAN_BYTES}});var Te=We();Object.defineProperty(c,"evaluateGuard",{enumerable:!0,get:function(){return Te.evaluateGuard}});Object.defineProperty(c,"GUARD_STATUS_REFUSED",{enumerable:!0,get:function(){return Te.GUARD_STATUS_REFUSED}});Object.defineProperty(c,"GUARD_BUDGET_HEADER",{enumerable:!0,get:function(){return Te.GUARD_BUDGET_HEADER}});var ft=le();Object.defineProperty(c,"DiskRetryQueue",{enumerable:!0,get:function(){return ft.DiskRetryQueue}});Object.defineProperty(c,"MemoryRetryQueue",{enumerable:!0,get:function(){return ft.MemoryRetryQueue}});var j=tt();Object.defineProperty(c,"DiskTransport",{enumerable:!0,get:function(){return j.DiskTransport}});Object.defineProperty(c,"MemoryTransport",{enumerable:!0,get:function(){return j.MemoryTransport}});Object.defineProperty(c,"postOtlp",{enumerable:!0,get:function(){return j.postOtlp}});Object.defineProperty(c,"envOptionsResolver",{enumerable:!0,get:function(){return j.envOptionsResolver}});Object.defineProperty(c,"parseHeadersEnv",{enumerable:!0,get:function(){return j.parseHeadersEnv}});Object.defineProperty(c,"MAX_POST_BYTES",{enumerable:!0,get:function(){return j.MAX_POST_BYTES}});var pt=me();Object.defineProperty(c,"TraceManager",{enumerable:!0,get:function(){return pt.TraceManager}});Object.defineProperty(c,"generateUlid",{enumerable:!0,get:function(){return pt.generateUlid}});var gt=ut();Object.defineProperty(c,"DiskSessionTraceManager",{enumerable:!0,get:function(){return gt.DiskSessionTraceManager}});Object.defineProperty(c,"MemorySessionTraceManager",{enumerable:!0,get:function(){return gt.MemorySessionTraceManager}});var Oe=dt();Object.defineProperty(c,"envFilePath",{enumerable:!0,get:function(){return Oe.envFilePath}});Object.defineProperty(c,"parseEnvFile",{enumerable:!0,get:function(){return Oe.parseEnvFile}});Object.defineProperty(c,"loadEnvFile",{enumerable:!0,get:function(){return Oe.loadEnvFile}})});var mt=D(S(),1);import yn from"node:fs";import Ee from"node:path";var V=D(S(),1);function mn(){return(0,V.envFilePath)(".config/opencode","pinta-opencode.env","OPENCODE_CONFIG_DIR")}function ht(t=mn()){(0,V.loadEnvFile)(t)}function Pe(...t){return t.find(e=>e)||void 0}function vn(t){let e=Pe(t.endpoint,process.env.PINTA_OPENCODE_ENDPOINT,process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT);if(e)return e.replace(/\/+$/,"");let n=process.env.OTEL_EXPORTER_OTLP_ENDPOINT;if(n)return n.replace(/\/+$/,"")+"/v1/traces"}var _n="opencode-ai";function Tn(){let t=Ee.dirname(process.execPath);for(let e=0;e<5;e++){try{let r=JSON.parse(yn.readFileSync(Ee.join(t,"package.json"),"utf8"));if(r.name===_n&&typeof r.version=="string"&&r.version)return r.version}catch{}let n=Ee.dirname(t);if(n===t)break;t=n}}function On(){let t=process.env.OPENCODE_VERSION?.trim();return t||Tn()}function yt(t={}){ht();let e=Pe(t.token,process.env.PINTA_OPENCODE_TOKEN),n=(0,mt.parseHeadersEnv)(t.headers??process.env.PINTA_OPENCODE_HEADERS??process.env.OTEL_EXPORTER_OTLP_HEADERS);e&&!Object.keys(n).some(s=>s.toLowerCase()==="x-pinta-relay-token")&&(n["x-pinta-relay-token"]=e);let r=t.guardTimeoutMs??(Number(process.env.PINTA_OPENCODE_GUARD_TIMEOUT_MS)||100);return{endpoint:vn(t),headers:n,guardEndpoint:Pe(t.guard,process.env.PINTA_OPENCODE_GUARD),relayToken:e,guardTimeoutMs:r,guardDisabled:process.env.PINTA_OPENCODE_GUARD_DISABLED==="1",serviceVersion:On()}}var N=D(S(),1),J=class extends N.MemoryTransport{constructor(n){super({logPrefix:"pinta-opencode",resolveOptions:()=>n.endpoint?{endpoint:n.endpoint,headers:n.headers}:null});this.config=n}deferred=new N.MemoryRetryQueue;defer(n){if(!this.config.endpoint)return;let r=Buffer.byteLength(JSON.stringify(n),"utf8");if(r>N.MAX_POST_BYTES){process.stderr.write(`[pinta-opencode] deferred payload exceeds POST budget (${r} bytes); dropped
|
|
25
|
+
`);return}this.deferred.enqueue(n)}async flush(){await super.flush();for(let n of this.deferred.drain())await super.send(n)}};var Se=D(S(),1);var St=D(S(),1);var vt=D(S(),1);var Q="0.11.1";var En=100,Pn=`pinta-opencode/${Q}`;function _t(t,e,n={}){return(0,vt.evaluateGuard)(t,e,{timeoutMs:n.timeoutMs??En,token:n.token,disabled:n.disabled,userAgent:Pn})}var Pt=D(S(),1);import be from"os";var q=D(S(),1);var ee;function Sn(){if(ee===void 0)try{ee=be.userInfo().username}catch{ee=process.env.USER??process.env.LOGNAME??(typeof process.getuid=="function"?String(process.getuid()):"unknown")}return ee}var Tt=Q,bn=new Set(["opencode.hook","opencode.event_type","opencode.tool_name","opencode.session_id","opencode.sessionID","opencode.tool_use_id","opencode.cwd","opencode.agent","opencode.model","opencode.exit","opencode.truncated","opencode.title"]),Dn=new Set(["opencode.tool_input","opencode.tool_response"]),In={skipRedactKeys:bn,bashContextKeys:Dn};function An(t){let e=[{key:"ingest.type",value:{stringValue:"opencode"}}];return e.push(...(0,q.attrsFromRecord)(t,"opencode",In)),e}function Mn(t){return[{key:"service.name",value:{stringValue:"opencode"}},...t?[{key:"service.version",value:{stringValue:t}}]:[],{key:"telemetry.sdk.name",value:{stringValue:"pinta-opencode"}},{key:"telemetry.sdk.language",value:{stringValue:"nodejs"}},{key:"telemetry.sdk.version",value:{stringValue:Tt}},{key:"process.pid",value:{intValue:process.pid}},{key:"process.owner",value:{stringValue:Sn()}},{key:"host.name",value:{stringValue:be.hostname()}},{key:"host.arch",value:{stringValue:be.arch()}}]}function Ot(t){return(0,q.buildPayload)({traceId:t.traceId,spanName:t.name,attributes:An(t.fields),resource:Mn(t.serviceVersion),scope:{name:"pinta-opencode",version:Tt},now:t.now})}var C={messages:1024,requests:512,calls:2048,endedSessions:128,ttlMs:9e5},wn=new Set(["unknown","undefined","null","n/a","none","-","auto","default"]);function g(t){if(typeof t!="string")return;let e=t.trim();return e&&e.length<=1024?e:void 0}function w(t){let e=g(t);if(!(!e||e.startsWith("{")||e.startsWith("[")||wn.has(e.toLowerCase())))return e}function b(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)?t:void 0}function O(t,e,n){let r=b(t),s=r?w(r.modelID)??w(r.id):w(t);if(s)return{model:s,source:r?`${e}.${w(r.modelID)?"modelID":"id"}`:e,provider:w(r?.providerID??n)}}function E(...t){return JSON.stringify(t)}function B(...t){let e=t.map(g).filter(n=>n!==void 0);return e.every(n=>n===e[0])?e[0]:void 0}function De(t,e){let n=b(e.info),r=b(e.part);return typeof t=="string"&&t.startsWith("session.")?B(e.sessionID,n?.id):B(e.sessionID,n?.sessionID,r?.sessionID)}var L=class{constructor(e,n){this.limit=e;this.now=n}entries=new Map;get(e){let n=this.entries.get(e);if(n&&n.expires>this.now())return n.value;this.entries.delete(e)}set(e,n){this.entries.delete(e),this.entries.set(e,{value:n,expires:this.now()+C.ttlMs}),this.entries.size>this.limit&&this.entries.delete(this.entries.keys().next().value)}removeWhere(e){for(let[n,r]of this.entries)e(r.value)&&this.entries.delete(n)}clearSession(e){this.removeWhere(n=>n.sessionID===e)}},te=class{constructor(e=Date.now){this.now=e;this.messages=new L(C.messages,e),this.requests=new L(C.requests,e),this.calls=new L(C.calls,e)}messages;requests;calls;ended=new Map;chatMessage(e,n){let r=B(e.sessionID,n?.message?.sessionID);if(!r)return;this.ended.delete(r);let s=B(e.messageID,n?.message?.id);if(!s)return;let i=g(n?.message?.agent??e.agent),a=n?.message?.model!==void 0;this.requests.set(E(r,s,i??""),{sessionID:r,messageID:s,evidence:O(a?n.message?.model:e.model,a?"requested:chat.message.message.model":"requested:chat.message.model")})}chatParams(e){let n=B(e.sessionID,e.message?.sessionID),r=g(e.message?.id),s=g(e.agent);!n||!r||!s||this.ended.has(n)||this.requests.set(E(n,r,s),{sessionID:n,messageID:r,evidence:O(e.model,"requested:chat.params.model")})}fresh(e){return e===void 0||typeof e=="number"&&Number.isFinite(e)&&e>=this.now()-C.ttlMs}clearSession(e){this.messages.clearSession(e),this.requests.clearSession(e),this.calls.clearSession(e)}event(e,n){let r=De(e,n),s=b(n.info),i=b(n.part),a=["session.idle","session.deleted","session.error"].includes(e??"")||e==="session.status"&&b(n.status)?.type==="idle";if(r&&a?(this.clearSession(r),this.ended.delete(r),this.ended.set(r,!0),this.ended.size>C.endedSessions&&this.ended.delete(this.ended.keys().next().value)):r&&e==="session.created"&&(this.clearSession(r),this.ended.delete(r)),e==="message.updated"&&s){if(!B(n.sessionID,s.sessionID)&&(n.sessionID||s.sessionID))return;let o=g(s.id),u=g(s.agent),l=s.role,d=l==="assistant"?O(w(s.modelID),"reported:message.updated.info.modelID",s.providerID)??O(s.model,"reported:message.updated.info.model"):l==="user"?O(s.model,"requested:message.updated.info.model"):void 0;if(!d&&l==="user"&&!("model"in s)&&r&&o&&(d=this.requests.get(E(r,o,u??""))?.evidence),l==="user"&&"model"in s&&!d&&r&&o&&this.requests.set(E(r,o,u??""),{sessionID:r,messageID:o}),d&&(d={...d,agent:u,messageID:o}),r&&o&&!this.ended.has(r)&&this.fresh(b(s.time)?.created)){let h=this.messages.get(E(r,o)),$=h?.ambiguous||!!(l==="assistant"&&h?.evidence&&d&&(h.evidence.model!==d.model||h.evidence.provider!==d.provider||h.evidence.agent!==d.agent));this.messages.set(E(r,o),{sessionID:r,messageID:o,evidence:$?void 0:d,ambiguous:$})}return d}if(r&&e==="message.removed"){let o=g(n.messageID);this.messages.removeWhere(u=>u.sessionID===r&&u.messageID===o),this.requests.removeWhere(u=>u.sessionID===r&&u.messageID===o),this.calls.removeWhere(u=>u.sessionID===r&&u.messageID===o)}if(r&&e==="message.part.removed"){let o=g(n.partID);o&&this.calls.removeWhere(u=>u.sessionID===r&&u.partID===o)}if(e==="message.part.delta"){let o=g(n.messageID);return r&&o&&!this.ended.has(r)?this.messages.get(E(r,o))?.evidence:void 0}if(e==="message.part.updated"&&i){if(!r||this.ended.has(r)||!this.fresh(n.time))return;let o=g(i.messageID);if(!o)return;let u=g(i.callID);if(i.type==="tool"&&u){let l=E(r,u),d=this.calls.get(l);if(!d?.closed){let h=g(i.tool);this.calls.set(l,{sessionID:r,messageID:o,partID:g(i.id),tool:h,pinned:d?.pinned,ambiguous:d?.ambiguous||!!(d&&(d.messageID!==o||d.tool!==h))})}}return this.messages.get(E(r,o))?.evidence}return typeof e=="string"&&e.startsWith("session.")?O(s?.model,`selected:${e}.info.model`)??O(n.model,`selected:${e}.model`):O(n.model,"reported:event.model")??O(w(n.modelID),"reported:event.modelID",n.providerID)}tool(e,n){let r=O(w(e.modelID),"reported:tool.modelID",e.providerID)??O(e.model,"reported:tool.model",e.providerID),s=g(e.sessionID),i=g(e.callID);if(!s||!i)return r;let a=E(s,i),o=this.calls.get(a),u=r;if(!("model"in e)&&!("modelID"in e)&&o&&!o.closed&&!o.ambiguous&&!this.ended.has(s)&&(!o.tool||o.tool===e.tool)){let l=this.messages.get(E(s,o.messageID));l?.ambiguous||(u=o.pinned??l?.evidence),(e.agent&&u?.agent&&e.agent!==u.agent||e.messageID&&e.messageID!==o.messageID)&&(u=void 0)}return o&&!o.closed&&this.calls.set(a,{...o,pinned:n?void 0:u,closed:n}),u}beforeTool(e){return this.tool(e,!1)}afterTool(e){return this.tool(e,!0)}};function ne(t,e){let{model:n,model_source:r,...s}=t;return"model"in t&&n!==e?.model&&(s.model_raw=n),"model_source"in t&&(s.model_source_raw=r),e&&(s.model=e.model,s.model_source=e.source,e.provider&&("provider"in s&&s.provider!==e.provider&&(s.provider_raw=s.provider),s.provider=e.provider),s.agent===void 0&&(s.agent=e.agent),s.message_id===void 0&&(s.message_id=e.messageID)),s}var re=class{constructor(e,n,r){this.transport=e;this.trace=n;this.config=r}models=new te;build(e,n,r){let s=this.trace.currentTrace(n);return Ot({name:e,traceId:s,fields:r,serviceVersion:this.config.serviceVersion})}async send(e){await this.transport.send(e)}async emit(e,n,r){await this.send(this.build(e,n,r))}chatMessage(e,n){this.models.chatMessage(e,n)}chatParams(e){this.models.chatParams(e)}async lifecycle(e){let n=b(e.properties)??{},r=De(e.type,n),s=this.models.event(e.type,n);e.type==="session.idle"&&await this.transport.flush(),await this.emit(`opencode.event.${e.type??"unknown"}`,r,ne({hook:"event",event_type:e.type,session_id:r,cwd:process.cwd(),...n},s))}toolBeforePayload(e,n){return this.build("opencode.tool.before",e.sessionID,ne({...Et("tool.execute.before",e),tool_input:n},this.models.beforeTool(e)))}async toolBefore(e,n,r){await this.send((0,Pt.attachGuard)(this.toolBeforePayload(e,n),r))}toolAfterPayload(e,n){let r=n.metadata??{};return this.build("opencode.tool.after",e.sessionID,ne({...Et("tool.execute.after",e),tool_input:e.args,title:n.title,tool_response:n.content!==void 0?n:n.output,attachments:n.attachments,exit:r.exit,truncated:r.truncated},this.models.afterTool(e)))}async toolAfter(e,n){await this.send(this.toolAfterPayload(e,n))}};function Et(t,e){return{hook:t,tool_name:e.tool,session_id:e.sessionID,tool_use_id:e.callID,cwd:process.cwd(),...e.model!==void 0?{model:e.model}:{},...e.modelID!==void 0?{modelID:e.modelID}:{},...e.providerID!==void 0?{providerID:e.providerID}:{},...e.agent!==void 0?{agent:e.agent}:{},...e.messageID!==void 0?{message_id:e.messageID}:{}}}function bt(t,e){process.stderr.write(`[pinta-opencode] ${t}: ${e?.message??String(e)}
|
|
26
|
+
`)}var kn="Pinta withheld this tool output because it violated an active policy.";function Ie(t,e){return async(...n)=>{try{await e(...n)}catch(r){bt(t,r)}}}var Rn=async(t,e)=>{let n=yt(e??{}),r=new J({endpoint:n.endpoint,headers:n.headers}),s=new Se.MemorySessionTraceManager,i=new re(r,s,n);async function a(o,u){let l=null;try{let d=u();l=await _t(d,n.guardEndpoint,{timeoutMs:n.guardTimeoutMs,token:n.relayToken,disabled:n.guardDisabled}),(0,St.attachGuard)(d,l),o==="tool.execute.after"&&l&&d.resourceSpans[0].scopeSpans[0].spans[0].attributes.push({key:"pinta.guard.target",value:{stringValue:"tool_output"}}),l?.decision==="DENY"?r.defer(d):await i.send(d)}catch(d){bt(o,d)}return l}return{"chat.message":Ie("chat.message",async(o,u)=>{s.newTrace(o?.sessionID),i.chatMessage(o,u)}),"chat.params":Ie("chat.params",async(o,u)=>{i.chatParams(o)}),event:Ie("event",async o=>{o?.event&&await i.lifecycle(o.event)}),"tool.execute.before":async(o,u)=>{let l=await a("tool.execute.before",()=>i.toolBeforePayload(o,u?.args));if(l?.decision==="DENY")throw new Error(l.userMessage??l.reason??"guard_deny")},"tool.execute.after":async(o,u)=>{if((await a("tool.execute.after",()=>i.toolAfterPayload(o,u)))?.decision==="DENY")throw new Error(kn)}}},pr=Rn;export{Rn as PintaOpencode,pr as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pinta-ai/pinta-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "OTLP forwarder + guard for opencode (in-process plugin)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/plugin.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"prepublishOnly": "npm run build"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@pinta-ai/core": "^0.
|
|
39
|
+
"@pinta-ai/core": "^0.9.1",
|
|
40
40
|
"@types/node": "^22.0.0",
|
|
41
41
|
"esbuild": "0.24.2",
|
|
42
42
|
"typescript": "^5.7.0",
|