@pinta-ai/pinta-copilot 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/dist/index.js +14 -71
- package/dist/tools/doctor.js +4 -81
- package/dist/tools/install-hooks.js +3 -101
- package/package.json +8 -2
- package/dist/core/config.d.ts +0 -15
- package/dist/core/config.js +0 -19
- package/dist/core/config.js.map +0 -1
- package/dist/core/env-bridge.d.ts +0 -25
- package/dist/core/env-bridge.js +0 -43
- package/dist/core/env-bridge.js.map +0 -1
- package/dist/core/guard.d.ts +0 -14
- package/dist/core/guard.js +0 -53
- package/dist/core/guard.js.map +0 -1
- package/dist/core/otlp.d.ts +0 -55
- package/dist/core/otlp.js +0 -184
- package/dist/core/otlp.js.map +0 -1
- package/dist/core/redact.d.ts +0 -57
- package/dist/core/redact.js +0 -149
- package/dist/core/redact.js.map +0 -1
- package/dist/core/retry-queue.d.ts +0 -26
- package/dist/core/retry-queue.js +0 -127
- package/dist/core/retry-queue.js.map +0 -1
- package/dist/core/surface.d.ts +0 -21
- package/dist/core/surface.js +0 -13
- package/dist/core/surface.js.map +0 -1
- package/dist/core/trace.d.ts +0 -20
- package/dist/core/trace.js +0 -80
- package/dist/core/trace.js.map +0 -1
- package/dist/core/transport.d.ts +0 -18
- package/dist/core/transport.js +0 -127
- package/dist/core/transport.js.map +0 -1
- package/dist/core/types.d.ts +0 -49
- package/dist/core/types.js +0 -124
- package/dist/core/types.js.map +0 -1
- package/dist/env-file.d.ts +0 -4
- package/dist/env-file.js +0 -69
- package/dist/env-file.js.map +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js.map +0 -1
- package/dist/tools/doctor.d.ts +0 -1
- package/dist/tools/doctor.js.map +0 -1
- package/dist/tools/install-hooks.d.ts +0 -1
- package/dist/tools/install-hooks.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,50 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to pinta-copilot are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.4.0] - 2026-07-12
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- **Shared core extraction.** The duplicated low-level utilities (otlp,
|
|
10
|
+
transport, retry-queue, redact, guard, session trace) now come from the
|
|
11
|
+
private `@pinta-ai/core` package; `src/core/*.ts` keeps only thin bindings
|
|
12
|
+
plus the copilot-specific bits (event flattening, surface detection, the
|
|
13
|
+
`envFilePath` override). Core is a devDependency, bundled into `dist/` by
|
|
14
|
+
esbuild at build time, so npmjs consumers never need private-registry access
|
|
15
|
+
and there is no runtime dependency on it.
|
|
16
|
+
- `@pinta-ai/core` bumped `^0.2.0` → `^0.3.0`. The upgrade is additive; no
|
|
17
|
+
adaptor source change was needed.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `pinta.client.rtt_ms` / `pinta.client.op` span attributes on guarded spans.
|
|
22
|
+
`buildOtlpPayload` already forwards its `guard` result into core's
|
|
23
|
+
`buildPayload`, and core `0.3.0` derives the client-call timing from the
|
|
24
|
+
`GuardResult.clientRttMs` it now measures. The manager can only time its own
|
|
25
|
+
handler, so `clientRttMs - durationMs` gives it the transport overhead.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- `package-lock.json` resolved `@pinta-ai/core` as a local link to
|
|
30
|
+
`../pinta-core` while `package.json` declared a version range. `npm ci` did
|
|
31
|
+
not error on the mismatch — it created a dangling symlink to a dev-machine-only
|
|
32
|
+
path and the build then failed `TS2307: Cannot find module '@pinta-ai/core'`,
|
|
33
|
+
breaking pr-validate, build-dist and publish. The lock now pins core's
|
|
34
|
+
GitHub Packages tarball with its integrity hash.
|
|
35
|
+
- `npm publish` would have failed `ENEEDAUTH`: the committed `.npmrc` pointed
|
|
36
|
+
npmjs auth at `${NPM_TOKEN}`, but no such secret exists, so it expanded to an
|
|
37
|
+
empty token instead of falling through to OIDC trusted publishing. The npmjs
|
|
38
|
+
`_authToken` line is gone and `setup-node`'s `registry-url` is back;
|
|
39
|
+
`NODE_AUTH_TOKEN` is now scoped to the `npm ci` step so it is not sent to
|
|
40
|
+
npmjs during publish.
|
|
41
|
+
- `npm` pinned to `11.18.0` in the publish workflow. `npm@latest` is now 12.x,
|
|
42
|
+
which requires node >=22.22 and aborts the node-20 publish job with
|
|
43
|
+
`EBADENGINE`.
|
|
44
|
+
- `tests/core/guard.test.ts` asserted the core `0.2.0` `GuardResult` shape with
|
|
45
|
+
an exhaustive `toEqual` and broke once `clientRttMs` was added. It now matches
|
|
46
|
+
that field by type, since it is a measured wall-clock rather than a value
|
|
47
|
+
echoed from the response body.
|
|
48
|
+
|
|
5
49
|
## [0.2.0] - 2026-06-16
|
|
6
50
|
|
|
7
51
|
### Changed
|
package/dist/index.js
CHANGED
|
@@ -1,71 +1,14 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
(0,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
async function readStdin() {
|
|
16
|
-
const chunks = [];
|
|
17
|
-
for await (const chunk of process.stdin)
|
|
18
|
-
chunks.push(chunk);
|
|
19
|
-
return Buffer.concat(chunks).toString("utf-8");
|
|
20
|
-
}
|
|
21
|
-
async function main() {
|
|
22
|
-
// ⚠️ CLI preToolUse hooks are FAIL-CLOSED: a non-zero exit / crash / timeout
|
|
23
|
-
// DENIES the tool — and a crashing hook bricks the whole agent (report_intent
|
|
24
|
-
// / ask_user get blocked too, verified §9.6). Therefore this process MUST
|
|
25
|
-
// exit 0 on every path. All work is wrapped; the catch-all stays exit 0.
|
|
26
|
-
try {
|
|
27
|
-
const config = (0, config_js_1.loadConfig)();
|
|
28
|
-
const surface = (0, surface_js_1.detectSurface)();
|
|
29
|
-
const raw = await readStdin();
|
|
30
|
-
const event = JSON.parse(raw);
|
|
31
|
-
const kind = (0, types_js_1.classify)(event);
|
|
32
|
-
const sid = (0, types_js_1.sessionId)(event);
|
|
33
|
-
const transport = new transport_js_1.Transport(config);
|
|
34
|
-
await transport.flush(); // drain retry queue first
|
|
35
|
-
const trace = new trace_js_1.TraceManager(config);
|
|
36
|
-
// UserPromptSubmit starts a new per-turn trace; everything else reuses it.
|
|
37
|
-
const traceId = kind === "UserPromptSubmit" ? trace.newTrace(sid) : trace.currentTrace(sid);
|
|
38
|
-
// Guard runs on the two tool-gating events (PreToolUse: all surfaces;
|
|
39
|
-
// PermissionRequest: CLI-only). Internal agent tools (report_intent,
|
|
40
|
-
// ask_user) are telemetry-only — never guarded (would brick the turn).
|
|
41
|
-
const toolNm = (0, types_js_1.toolName)(event);
|
|
42
|
-
let guard = null;
|
|
43
|
-
if ((0, types_js_1.isGuardEvent)(kind) && !(0, types_js_1.isInternalTool)(toolNm)) {
|
|
44
|
-
const ti = (0, types_js_1.toolInput)(event);
|
|
45
|
-
guard = await (0, guard_js_1.evaluateGuard)({
|
|
46
|
-
spanId: sid ?? "unknown",
|
|
47
|
-
toolName: toolNm,
|
|
48
|
-
toolInput: ti,
|
|
49
|
-
rawTextFields: {
|
|
50
|
-
toolInput: typeof ti === "string" ? ti : JSON.stringify(ti ?? null),
|
|
51
|
-
},
|
|
52
|
-
}, process.env.PINTA_GUARD_ENDPOINT);
|
|
53
|
-
}
|
|
54
|
-
// Telemetry: one span per event (Bronze flattening, copilot.* prefix).
|
|
55
|
-
const payload = (0, otlp_js_1.buildOtlpPayload)({ event, traceId, surface, guard });
|
|
56
|
-
await transport.send(payload);
|
|
57
|
-
// Enforcement: emit a deny decision in the format the firing event expects.
|
|
58
|
-
if (guard?.decision === "DENY") {
|
|
59
|
-
const out = (0, types_js_1.formatDeny)(kind, guard.userMessage ?? guard.reason ?? "guard_deny");
|
|
60
|
-
if (out)
|
|
61
|
-
process.stdout.write(out + "\n");
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
catch (err) {
|
|
65
|
-
process.stderr.write(`[pinta-copilot] error: ${err}\n`);
|
|
66
|
-
// fail-open by design — never block a tool because the adapter crashed.
|
|
67
|
-
}
|
|
68
|
-
process.exit(0);
|
|
69
|
-
}
|
|
70
|
-
main();
|
|
71
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
"use strict";var He=Object.create;var se=Object.defineProperty;var Qe=Object.getOwnPropertyDescriptor;var We=Object.getOwnPropertyNames;var et=Object.getPrototypeOf,tt=Object.prototype.hasOwnProperty;var d=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var rt=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of We(t))!tt.call(e,o)&&o!==r&&se(e,o,{get:()=>t[o],enumerable:!(n=Qe(t,o))||n.enumerable});return e};var _=(e,t,r)=>(r=e!=null?He(et(e)):{},rt(t||!e||!e.__esModule?se(r,"default",{value:e,enumerable:!0}):r,e));var G=d(c=>{"use strict";Object.defineProperty(c,"__esModule",{value:!0});c.PATTERNS=c.MAX_BYTES=void 0;c.truncate=nt;c.collectMatches=ie;c.resolveOverlaps=ae;c.applyMatches=ue;c.redact=ot;c.MAX_BYTES=102400;function nt(e){let t=Buffer.from(e,"utf-8");return t.length<=c.MAX_BYTES?e:`${t.subarray(0,c.MAX_BYTES).toString("utf-8")}\u2026[TRUNCATED:${t.length}]`}c.PATTERNS=[{type:"aws_access_key",regex:/AKIA[0-9A-Z]{16}/g},{type:"aws_secret_key",regex:/(?:aws[_-]?secret(?:[_-]?(?:access)?[_-]?key)?)\s*[:=]\s*["']?([A-Za-z0-9/+=]{40})(?![A-Za-z0-9/+=])/gi,captureGroup:1},{type:"gcp_service_account",regex:/\{[\s\S]{0,200}?"type"\s*:\s*"service_account"[\s\S]*?\}/g},{type:"github_token",regex:/gh[pousr]_[A-Za-z0-9]{36,}/g},{type:"gitlab_token",regex:/glpat-[A-Za-z0-9_-]{20}/g},{type:"slack_token",regex:/xox[abrsp]-[0-9A-Za-z-]{10,}/g},{type:"openai_key",regex:/sk-(?:proj-)?[A-Za-z0-9_-]{40,}/g},{type:"anthropic_key",regex:/sk-ant-[A-Za-z0-9_-]{50,}/g},{type:"stripe_key",regex:/(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{20,}/g},{type:"jwt",regex:/eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g},{type:"private_key_block",regex:/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g},{type:"bearer_token",regex:/bearer\s+([A-Za-z0-9._~+/=-]{12,})/gi,captureGroup:1},{type:"basic_auth",regex:/basic\s+([A-Za-z0-9+/=]{12,})/gi,captureGroup:1},{type:"db_url_password",regex:/\b(?:postgres|postgresql|mysql|mariadb|mongodb(?:\+srv)?|redis):\/\/[^:\s/]+:([^@\s]+)@/gi,captureGroup:1},{type:"cli_password_flag",regex:/(?:--password|--pass|--pwd)[=\s]([^\s'"]+)/g,captureGroup:1},{type:"cli_password_short",regex:/\s-p([^\s'"]+)/g,captureGroup:1,requireContext:"bash"},{type:"env_var_secret",regex:/^(?:export\s+)?([A-Z][A-Z0-9_]*(?:KEY|SECRET|TOKEN|PASSWORD|PASSWD|PWD|API_KEY)[A-Z0-9_]*)\s*=\s*["']?([^\s"'\n]+)/gm,captureGroup:2}];function ie(e,t){let r=[];for(let n of c.PATTERNS){if(n.requireContext&&n.requireContext!==t.context)continue;let o=new RegExp(n.regex.source,n.regex.flags),i;for(;(i=o.exec(e))!==null;){let a=n.captureGroup??0,u=i[a];if(u===void 0){i.index===o.lastIndex&&o.lastIndex++;continue}let R=i.index,I=i.index+i[0].length,h=R+i[0].indexOf(u),U=h+u.length;r.push({start:R,end:I,replaceStart:h,replaceEnd:U,type:n.type}),i.index===o.lastIndex&&o.lastIndex++}}return r}function ae(e){let t=[...e].sort((o,i)=>o.start!==i.start?o.start-i.start:i.end-i.start-(o.end-o.start)),r=[],n=-1;for(let o of t)o.start<n||(r.push(o),n=o.end);return r}function ue(e,t){let r=[...t].sort((o,i)=>i.replaceStart-o.replaceStart),n=e;for(let o of r)n=n.slice(0,o.replaceStart)+`[REDACTED:${o.type}]`+n.slice(o.replaceEnd);return n}function ot(e,t={}){if(e.length===0)return e;let r=ie(e,t);if(r.length===0)return e;let n=ae(r);return ue(e,n)}});var V=d(p=>{"use strict";var st=p&&p.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(p,"__esModule",{value:!0});p.ulidToTraceId=pe;p.newSpanId=de;p.snakeCase=ut;p.toOtlpValue=fe;p.attrsFromRecord=ct;p.guardAttrs=ge;p.clientAttrs=he;p.buildPayload=lt;p.mergeBatch=pt;var it=st(require("crypto")),ce=G(),at="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function pe(e){if(e.length!==26)throw new Error(`ulidToTraceId: expected 26 chars, got ${e.length}`);let t=0n;for(let n of e){let o=at.indexOf(n);if(o<0)throw new Error(`ulidToTraceId: invalid Crockford char "${n}"`);t=t<<5n|BigInt(o)}let r=(1n<<128n)-1n;return t&=r,t.toString(16).padStart(32,"0")}function de(){return it.default.randomBytes(8).toString("hex")}function ut(e){return e.replace(/([a-z0-9])([A-Z])/g,"$1_$2").replace(/([A-Z])([A-Z][a-z])/g,"$1_$2").toLowerCase()}var le=new Set;function x(e,t,r){let n=(0,ce.truncate)(t);if((r.skipRedactKeys??le).has(e))return n;let o=(r.bashContextKeys??le).has(e)?"bash":void 0;return(0,ce.redact)(n,{context:o})}function fe(e,t,r={}){if(t==null)return null;switch(typeof t){case"string":return{stringValue:x(e,t,r)};case"boolean":return{boolValue:t};case"number":return Number.isInteger(t)?{intValue:t}:{doubleValue:t};case"object":try{return{stringValue:x(e,JSON.stringify(t),r)}}catch{return{stringValue:x(e,String(t),r)}}default:return{stringValue:x(e,String(t),r)}}}function ct(e,t,r={}){let n=[];for(let[o,i]of Object.entries(e)){let a=`${t}.${o}`,u=fe(a,i,r);u!==null&&n.push({key:a,value:u})}return n}function ge(e){let t=[{key:"pinta.guard.decision",value:{stringValue:e.decision.toLowerCase()}},{key:"pinta.guard.duration_ms",value:{intValue:e.durationMs}}];return e.reason&&t.push({key:"pinta.guard.matched_rule",value:{stringValue:e.reason}}),e.failOpenReason&&t.push({key:"pinta.guard.fail_open_reason",value:{stringValue:e.failOpenReason}}),t}function he(e){return[{key:"pinta.client.rtt_ms",value:Number.isInteger(e.rttMs)?{intValue:e.rttMs}:{doubleValue:e.rttMs}},{key:"pinta.client.op",value:{stringValue:e.op}}]}function lt(e){let t=e.now??Date.now(),r=(BigInt(t)*1000000n).toString(),n=[...e.attributes];e.guard&&n.push(...ge(e.guard));let o=e.clientTiming??(e.guard?.clientRttMs!==void 0?{op:"guard",rttMs:e.guard.clientRttMs}:null);o&&n.push(...he(o));let i={traceId:pe(e.traceId),spanId:de(),name:e.spanName,kind:e.spanKind??1,startTimeUnixNano:r,endTimeUnixNano:r,attributes:n};return{resourceSpans:[{resource:{attributes:e.resource},scopeSpans:[{scope:e.scope,spans:[i]}]}]}}function pt(e){let t=[];for(let r of e)t.push(...r.resourceSpans);return{resourceSpans:t}}});var ye=d($=>{"use strict";Object.defineProperty($,"__esModule",{value:!0});$.evaluateGuard=ht;var dt=1e4,ft="pinta-core";function _e(e,t){return{decision:"ALLOW",reason:null,userMessage:null,durationMs:e,clientRttMs:e,failOpenReason:t}}function gt(e){return new Promise((t,r)=>setTimeout(()=>{let n=new Error("Guard request timed out");n.name="TimeoutError",r(n)},e))}async function ht(e,t,r={}){if(!t||r.disabled)return null;let n=r.timeoutMs??dt,o=Date.now();try{let i=await Promise.race([fetch(t,{method:"POST",headers:{"content-type":"application/json","user-agent":r.userAgent??ft,"x-pinta-relay-token":r.token??""},body:JSON.stringify({input:e})}),gt(n)]);if(i.status!==200)return _e(Date.now()-o,"error");let a=await i.json(),u=Date.now()-o;return{decision:a.decision,reason:a.reason,userMessage:a.userMessage??null,durationMs:a.durationMs??u,clientRttMs:u}}catch(i){let a=i.name==="TimeoutError"?"timeout":"error";return _e(Date.now()-o,a)}}});var Z=d(y=>{"use strict";var me=y&&y.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(y,"__esModule",{value:!0});y.MemoryRetryQueue=y.DiskRetryQueue=void 0;var l=me(require("fs")),M=me(require("path")),N=1e3,_t=50,yt=5,K=class{filePath;lockPath;logPrefix;constructor(t,r){this.filePath=M.default.join(t,"failed-spans.jsonl"),this.lockPath=this.filePath+".lock",this.logPrefix=r}enqueue(t){try{l.default.mkdirSync(M.default.dirname(this.filePath),{recursive:!0});let r=JSON.stringify({savedAt:new Date().toISOString(),payload:t})+`
|
|
2
|
+
`;l.default.appendFileSync(this.filePath,r),this.trim()}catch(r){process.stderr.write(`[${this.logPrefix}] retry-queue enqueue failed: ${r}
|
|
3
|
+
`)}}readAll(){try{let t=l.default.readFileSync(this.filePath,"utf-8"),r=[];for(let n of t.split(`
|
|
4
|
+
`))if(n.trim())try{r.push(JSON.parse(n))}catch{}return r}catch{return[]}}rewrite(t){try{if(t.length===0){l.default.existsSync(this.filePath)&&l.default.unlinkSync(this.filePath);return}l.default.mkdirSync(M.default.dirname(this.filePath),{recursive:!0}),l.default.writeFileSync(this.filePath,t.map(r=>JSON.stringify(r)).join(`
|
|
5
|
+
`)+`
|
|
6
|
+
`)}catch(r){process.stderr.write(`[${this.logPrefix}] retry-queue rewrite failed: ${r}
|
|
7
|
+
`)}}tryAcquireLock(){let t=Date.now();for(l.default.mkdirSync(M.default.dirname(this.lockPath),{recursive:!0});Date.now()-t<_t;)try{let r=l.default.openSync(this.lockPath,"wx");return l.default.writeSync(r,String(process.pid)),l.default.closeSync(r),!0}catch(r){if(r?.code!=="EEXIST")return process.stderr.write(`[${this.logPrefix}] retry-queue lock open failed: ${r}
|
|
8
|
+
`),!1;try{let i=l.default.statSync(this.lockPath);if(Date.now()-i.mtimeMs>3e4){l.default.unlinkSync(this.lockPath);continue}}catch{}let n=yt,o=Date.now()+n;for(;Date.now()<o;);}return!1}release(){try{l.default.unlinkSync(this.lockPath)}catch{}}trim(){let t=this.readAll();if(t.length<=N)return;let r=t.length-N;process.stderr.write(`[${this.logPrefix}] retry-queue full, dropping ${r} oldest entries
|
|
9
|
+
`),this.rewrite(t.slice(r))}};y.DiskRetryQueue=K;var B=class{entries=[];enqueue(t){this.entries.push(t),this.entries.length>N&&this.entries.splice(0,this.entries.length-N)}drain(){let t=this.entries;return this.entries=[],t}get size(){return this.entries.length}};y.MemoryRetryQueue=B});var ve=d(f=>{"use strict";Object.defineProperty(f,"__esModule",{value:!0});f.MemoryTransport=f.DiskTransport=void 0;f.parseHeadersEnv=Te;f.envOptionsResolver=Se;f.postOtlp=k;var Pe=Z(),Oe=V(),mt=5e3;function Te(e){if(!e)return{};if(typeof e=="object")return{...e};let t={};for(let r of e.split(",")){let[n,...o]=r.split("=");n&&o.length>0&&(t[n.trim()]=o.join("=").trim())}return t}function Se(){let e=process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,t=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,r;return e?r=e.replace(/\/+$/,""):t&&(r=t.replace(/\/+$/,"")+"/v1/traces"),r?{endpoint:r,headers:Te(process.env.OTEL_EXPORTER_OTLP_HEADERS)}:null}async function k(e,t,r){let n=new AbortController,o=setTimeout(()=>n.abort(),mt);try{let i=await fetch(t.endpoint,{method:"POST",headers:{"Content-Type":"application/json",...t.headers},body:JSON.stringify(e),signal:n.signal});if(!i.ok){let a="";try{a=(await i.text()).slice(0,200)}catch{}let u=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(`[${r}] OTLP POST ${i.status} ${t.endpoint}${u}${a?` body=${a}`:""}
|
|
10
|
+
`),!1}return!0}catch(i){return process.stderr.write(`[${r}] OTLP POST failed: ${i.message??String(i)}
|
|
11
|
+
`),!1}finally{clearTimeout(o)}}var X=class{queue;logPrefix;resolveOptions;constructor(t){this.queue=new Pe.DiskRetryQueue(t.pluginData,t.logPrefix),this.logPrefix=t.logPrefix,this.resolveOptions=t.resolveOptions??Se}async send(t){let r=this.resolveOptions();if(!r)return;await k(t,r,this.logPrefix)||this.queue.enqueue(t)}async flush(){let t=this.resolveOptions();if(t&&this.queue.tryAcquireLock())try{let r=this.queue.readAll();if(r.length===0)return;let n=(0,Oe.mergeBatch)(r.map(i=>i.payload));await k(n,t,this.logPrefix)&&this.queue.rewrite([])}finally{this.queue.release()}}};f.DiskTransport=X;var J=class{queue=new Pe.MemoryRetryQueue;logPrefix;resolveOptions;constructor(t){this.logPrefix=t.logPrefix,this.resolveOptions=t.resolveOptions}async send(t){let r=this.resolveOptions();if(!r)return;await k(t,r,this.logPrefix)||this.queue.enqueue(t)}async flush(){let t=this.resolveOptions();if(!t)return;let r=this.queue.drain();if(r.length===0)return;let n=(0,Oe.mergeBatch)(r);if(!await k(n,t,this.logPrefix))for(let i of r)this.queue.enqueue(i)}};f.MemoryTransport=J});var W=d(O=>{"use strict";var Q=O&&O.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(O,"__esModule",{value:!0});O.TraceManager=void 0;O.generateUlid=Ee;var Y=Q(require("fs")),Pt=Q(require("path")),Ot=Q(require("crypto")),z="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function Ee(){let e=Date.now(),t="",r=e;for(let i=0;i<10;i++)t=z[r&31]+t,r=Math.floor(r/32);let n=Ot.default.randomBytes(10),o="";for(let i=0;i<10;i++)o+=z[n[i]&31];for(;o.length<16;)o+=z[0];return t+o}var H=class{tracePath;constructor(t){this.tracePath=t}newTrace(){let t=Ee();return this.save(t),t}currentTrace(){try{let t=Y.default.readFileSync(this.tracePath,"utf-8"),{traceId:r}=JSON.parse(t);if(r)return r}catch{}return this.newTrace()}save(t){Y.default.mkdirSync(Pt.default.dirname(this.tracePath),{recursive:!0}),Y.default.writeFileSync(this.tracePath,JSON.stringify({traceId:t}))}};O.TraceManager=H});var Ae=d(m=>{"use strict";var be=m&&m.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(m,"__esModule",{value:!0});m.MemorySessionTraceManager=m.DiskSessionTraceManager=void 0;var ee=be(require("fs")),Tt=be(require("path")),ke=W(),we=200,te=class{tracePath;maxSessions;constructor(t,r={}){this.tracePath=t,this.maxSessions=r.maxSessions??we}read(){try{let t=JSON.parse(ee.default.readFileSync(this.tracePath,"utf-8"));if(t&&typeof t=="object"&&!("traceId"in t))return t}catch{}return{}}write(t){try{ee.default.mkdirSync(Tt.default.dirname(this.tracePath),{recursive:!0});let r=Object.entries(t),n=r.length>this.maxSessions?Object.fromEntries(r.slice(-this.maxSessions)):t;ee.default.writeFileSync(this.tracePath,JSON.stringify(n))}catch{}}newTrace(t){let r=t||"default",n=(0,ke.generateUlid)(),o=this.read();return o[r]=n,this.write(o),n}currentTrace(t){let r=t||"default",n=this.read()[r];return n||this.newTrace(r)}};m.DiskSessionTraceManager=te;var re=class{map=new Map;maxSessions;constructor(t={}){this.maxSessions=t.maxSessions??we}newTrace(t){let r=t||"default",n=(0,ke.generateUlid)();if(this.map.set(r,n),this.map.size>this.maxSessions){let o=this.map.keys().next().value;o!==void 0&&this.map.delete(o)}return n}currentTrace(t){let r=t||"default";return this.map.get(r)??this.newTrace(r)}};m.MemorySessionTraceManager=re});var xe=d(T=>{"use strict";var ne=T&&T.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(T,"__esModule",{value:!0});T.envFilePath=Et;T.parseEnvFile=Ie;T.loadEnvFile=bt;var St=ne(require("node:fs")),vt=ne(require("node:os")),Re=ne(require("node:path"));function Et(e,t,r){let n=r?process.env[r]:void 0,o=n&&n.length>0?n:Re.default.join(vt.default.homedir(),e);return Re.default.join(o,t)}function Ie(e){let t={};for(let r of e.split(`
|
|
12
|
+
`)){let n=r.trim();if(!n||n.startsWith("#"))continue;let o=n.indexOf("=");if(o<0)continue;let i=n.slice(0,o).trim(),a=n.slice(o+1).trim();(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),i&&(t[i]=a)}return t}function bt(e){let t;try{t=St.default.readFileSync(e,"utf-8")}catch{return}let r=Ie(t);for(let[n,o]of Object.entries(r))process.env[n]===void 0&&(process.env[n]=o)}});var b=d(s=>{"use strict";Object.defineProperty(s,"__esModule",{value:!0});s.loadEnvFile=s.parseEnvFile=s.envFilePath=s.MemorySessionTraceManager=s.DiskSessionTraceManager=s.generateUlid=s.TraceManager=s.parseHeadersEnv=s.envOptionsResolver=s.postOtlp=s.MemoryTransport=s.DiskTransport=s.MemoryRetryQueue=s.DiskRetryQueue=s.evaluateGuard=s.mergeBatch=s.buildPayload=s.clientAttrs=s.guardAttrs=s.attrsFromRecord=s.toOtlpValue=s.snakeCase=s.newSpanId=s.ulidToTraceId=s.MAX_BYTES=s.PATTERNS=s.applyMatches=s.resolveOverlaps=s.collectMatches=s.truncate=s.redact=void 0;var S=G();Object.defineProperty(s,"redact",{enumerable:!0,get:function(){return S.redact}});Object.defineProperty(s,"truncate",{enumerable:!0,get:function(){return S.truncate}});Object.defineProperty(s,"collectMatches",{enumerable:!0,get:function(){return S.collectMatches}});Object.defineProperty(s,"resolveOverlaps",{enumerable:!0,get:function(){return S.resolveOverlaps}});Object.defineProperty(s,"applyMatches",{enumerable:!0,get:function(){return S.applyMatches}});Object.defineProperty(s,"PATTERNS",{enumerable:!0,get:function(){return S.PATTERNS}});Object.defineProperty(s,"MAX_BYTES",{enumerable:!0,get:function(){return S.MAX_BYTES}});var g=V();Object.defineProperty(s,"ulidToTraceId",{enumerable:!0,get:function(){return g.ulidToTraceId}});Object.defineProperty(s,"newSpanId",{enumerable:!0,get:function(){return g.newSpanId}});Object.defineProperty(s,"snakeCase",{enumerable:!0,get:function(){return g.snakeCase}});Object.defineProperty(s,"toOtlpValue",{enumerable:!0,get:function(){return g.toOtlpValue}});Object.defineProperty(s,"attrsFromRecord",{enumerable:!0,get:function(){return g.attrsFromRecord}});Object.defineProperty(s,"guardAttrs",{enumerable:!0,get:function(){return g.guardAttrs}});Object.defineProperty(s,"clientAttrs",{enumerable:!0,get:function(){return g.clientAttrs}});Object.defineProperty(s,"buildPayload",{enumerable:!0,get:function(){return g.buildPayload}});Object.defineProperty(s,"mergeBatch",{enumerable:!0,get:function(){return g.mergeBatch}});var kt=ye();Object.defineProperty(s,"evaluateGuard",{enumerable:!0,get:function(){return kt.evaluateGuard}});var Me=Z();Object.defineProperty(s,"DiskRetryQueue",{enumerable:!0,get:function(){return Me.DiskRetryQueue}});Object.defineProperty(s,"MemoryRetryQueue",{enumerable:!0,get:function(){return Me.MemoryRetryQueue}});var w=ve();Object.defineProperty(s,"DiskTransport",{enumerable:!0,get:function(){return w.DiskTransport}});Object.defineProperty(s,"MemoryTransport",{enumerable:!0,get:function(){return w.MemoryTransport}});Object.defineProperty(s,"postOtlp",{enumerable:!0,get:function(){return w.postOtlp}});Object.defineProperty(s,"envOptionsResolver",{enumerable:!0,get:function(){return w.envOptionsResolver}});Object.defineProperty(s,"parseHeadersEnv",{enumerable:!0,get:function(){return w.parseHeadersEnv}});var Ne=W();Object.defineProperty(s,"TraceManager",{enumerable:!0,get:function(){return Ne.TraceManager}});Object.defineProperty(s,"generateUlid",{enumerable:!0,get:function(){return Ne.generateUlid}});var De=Ae();Object.defineProperty(s,"DiskSessionTraceManager",{enumerable:!0,get:function(){return De.DiskSessionTraceManager}});Object.defineProperty(s,"MemorySessionTraceManager",{enumerable:!0,get:function(){return De.MemorySessionTraceManager}});var oe=xe();Object.defineProperty(s,"envFilePath",{enumerable:!0,get:function(){return oe.envFilePath}});Object.defineProperty(s,"parseEnvFile",{enumerable:!0,get:function(){return oe.parseEnvFile}});Object.defineProperty(s,"loadEnvFile",{enumerable:!0,get:function(){return oe.loadEnvFile}})});var A=_(b());function wt(){return(0,A.envFilePath)(".copilot","pinta-copilot.env","COPILOT_HOME")}function je(e=wt()){(0,A.loadEnvFile)(e)}var Ce=_(require("os")),D=_(require("path"));function At(){return process.env.COPILOT_HOME||D.default.join(Ce.default.homedir(),".copilot")}function qe(){let e=process.env.COPILOT_PLUGIN_DATA||D.default.join(At(),"pinta-copilot-data");return{pluginData:e,tracePath:D.default.join(e,"trace.json")}}function Le(e=process.env){return e.COPILOT_AGENT_JOB_ID||e.COPILOT_AGENT_SESSION_ID||e.COPILOT_AGENT_PROMPT?"cloud":e.ELECTRON_RUN_AS_NODE||e.VSCODE_IPC_HOOK||e.VSCODE_PID?"ext":"cli"}function v(e){return typeof e=="string"?e:void 0}function j(e){return v(e.hook_event_name)??v(e.hookEventName)??v(e.hookName)??(process.env.PINTA_COPILOT_EVENT||void 0)}function Fe(e){return v(e.session_id)??v(e.sessionId)}function Ue(e){return v(e.tool_name)??v(e.toolName)}function Ge(e){return e.tool_input??e.toolArgs??e.toolInput}var Rt={sessionstart:"SessionStart",sessionend:"SessionEnd",userpromptsubmit:"UserPromptSubmit",userpromptsubmitted:"UserPromptSubmit",pretooluse:"PreToolUse",posttooluse:"PostToolUse",posttoolusefailure:"PostToolUseFailure",stop:"Stop",agentstop:"Stop",subagentstart:"SubagentStart",subagentstop:"SubagentStop",precompact:"PreCompact",notification:"Notification",permissionrequest:"PermissionRequest",erroroccurred:"ErrorOccurred"};function Ve(e){let t=j(e);return t?Rt[t.toLowerCase()]??"Unknown":"Unknown"}function $e(e){return e==="PreToolUse"||e==="PermissionRequest"}var It=new Set(["report_intent","ask_user"]);function Ke(e){return e!==void 0&&It.has(e)}function Be(e,t){return e==="PreToolUse"?JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:t}}):e==="PermissionRequest"?JSON.stringify({behavior:"deny",message:t}):null}var q=_(b());function xt(){let e=process.env.COPILOT_PLUGIN_OPTION_ENDPOINT||process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,t=process.env.OTEL_EXPORTER_OTLP_ENDPOINT,r;return e?r=e.replace(/\/+$/,""):t&&(r=t.replace(/\/+$/,"")+"/v1/traces"),r?{endpoint:r,headers:(0,q.parseHeadersEnv)(process.env.COPILOT_PLUGIN_OPTION_HEADERS||process.env.OTEL_EXPORTER_OTLP_HEADERS)}:null}var C=class extends q.DiskTransport{constructor(t){super({pluginData:t.pluginData,logPrefix:"pinta-copilot",resolveOptions:xt})}};var Ze=_(b()),L=class extends Ze.DiskSessionTraceManager{constructor(t){super(t.tracePath)}};var F=_(require("os")),E=_(b());var Xe="0.4.0";function Mt(){return process.env.COPILOT_CLI_VERSION||"unknown"}var Nt=new Set(["copilot.hook","copilot.tool_name","copilot.toolName","copilot.tool_use_id","copilot.toolUseId","copilot.session_id","copilot.sessionId","copilot.transcript_path","copilot.transcriptPath","copilot.cwd","copilot.permission_mode","copilot.surface","copilot.agent_id","copilot.agent_type","copilot.agent_name","copilot.agent_display_name","copilot.stop_reason","copilot.notification_type"]),Dt=new Set(["copilot.tool_input","copilot.toolInput","copilot.tool_response","copilot.tool_result"]),jt={skipRedactKeys:Nt,bashContextKeys:Dt},Ct=new Set(["hook_event_name","hookEventName","hookName"]);function qt(e,t){let r=Object.fromEntries(Object.entries(e).filter(([n])=>!Ct.has(n)));return[{key:"ingest.type",value:{stringValue:"copilot"}},{key:"copilot.hook",value:{stringValue:j(e)??"unknown"}},{key:"copilot.surface",value:{stringValue:t}},...(0,E.attrsFromRecord)(r,"copilot",jt)]}function Lt(){return[{key:"service.name",value:{stringValue:"copilot"}},{key:"service.version",value:{stringValue:Mt()}},{key:"telemetry.sdk.name",value:{stringValue:"pinta-copilot"}},{key:"telemetry.sdk.language",value:{stringValue:"nodejs"}},{key:"telemetry.sdk.version",value:{stringValue:Xe}},{key:"process.pid",value:{intValue:process.pid}},{key:"process.owner",value:{stringValue:F.default.userInfo().username}},{key:"host.name",value:{stringValue:F.default.hostname()}},{key:"host.arch",value:{stringValue:F.default.arch()}}]}function Je(e){return(0,E.buildPayload)({traceId:e.traceId,spanName:`copilot.${(0,E.snakeCase)(j(e.event)??"unknown")}`,attributes:qt(e.event,e.surface),resource:Lt(),scope:{name:"pinta-copilot",version:Xe},now:e.now,guard:e.guard})}var Ye=_(b());function Ft(){return Number(process.env.PINTA_GUARD_TIMEOUT_MS)||50}var Ut="pinta-copilot/0.4.0";function ze(e,t){return(0,Ye.evaluateGuard)(e,t,{timeoutMs:Ft(),token:process.env.PINTA_RELAY_TOKEN??"",disabled:process.env.PINTA_GUARD_DISABLED==="1",userAgent:Ut})}je();async function Gt(){let e=[];for await(let t of process.stdin)e.push(t);return Buffer.concat(e).toString("utf-8")}async function Vt(){try{let e=qe(),t=Le(),r=await Gt(),n=JSON.parse(r),o=Ve(n),i=Fe(n),a=new C(e);await a.flush();let u=new L(e),R=o==="UserPromptSubmit"?u.newTrace(i):u.currentTrace(i),I=Ue(n),h=null;if($e(o)&&!Ke(I)){let P=Ge(n);h=await ze({spanId:i??"unknown",toolName:I,toolInput:P,rawTextFields:{toolInput:typeof P=="string"?P:JSON.stringify(P??null)}},process.env.PINTA_GUARD_ENDPOINT)}let U=Je({event:n,traceId:R,surface:t,guard:h});if(await a.send(U),h?.decision==="DENY"){let P=Be(o,h.userMessage??h.reason??"guard_deny");P&&process.stdout.write(P+`
|
|
13
|
+
`)}}catch(e){process.stderr.write(`[pinta-copilot] error: ${e}
|
|
14
|
+
`)}process.exit(0)}Vt();
|
package/dist/tools/doctor.js
CHANGED
|
@@ -1,81 +1,4 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
/**
|
|
7
|
-
* doctor — read-only health check for a pinta-copilot install.
|
|
8
|
-
*
|
|
9
|
-
* node dist/tools/doctor.js (or: npm run doctor)
|
|
10
|
-
*
|
|
11
|
-
* Verifies the hook file, env file / OTel endpoint, and the built adapter.
|
|
12
|
-
* Exits 1 if anything required is missing; 0 if healthy.
|
|
13
|
-
*/
|
|
14
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
15
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
16
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
-
const surface_js_1 = require("../core/surface.js");
|
|
18
|
-
function copilotHome() {
|
|
19
|
-
return process.env.COPILOT_HOME || node_path_1.default.join(node_os_1.default.homedir(), ".copilot");
|
|
20
|
-
}
|
|
21
|
-
const checks = [];
|
|
22
|
-
function check(required, ok, label, detail) {
|
|
23
|
-
checks.push({ ok, label, detail, required });
|
|
24
|
-
}
|
|
25
|
-
const home = copilotHome();
|
|
26
|
-
const hookFile = node_path_1.default.join(home, "hooks", "pinta-copilot.json");
|
|
27
|
-
const envFile = node_path_1.default.join(home, "pinta-copilot.env");
|
|
28
|
-
const adapter = node_path_1.default.join(node_path_1.default.dirname(node_path_1.default.resolve(process.argv[1])), "..", "index.js");
|
|
29
|
-
// 1. adapter built
|
|
30
|
-
check(true, node_fs_1.default.existsSync(adapter), "adapter built (dist/index.js)", adapter);
|
|
31
|
-
// 2. hook file installed + points at this adapter
|
|
32
|
-
let hookOk = false;
|
|
33
|
-
let hookDetail = `${hookFile} (missing — run \`npm run install-hooks\`)`;
|
|
34
|
-
try {
|
|
35
|
-
const j = JSON.parse(node_fs_1.default.readFileSync(hookFile, "utf-8"));
|
|
36
|
-
const cmd = j?.hooks?.PreToolUse?.[0]?.command ?? "";
|
|
37
|
-
const events = Object.keys(j?.hooks ?? {}).length;
|
|
38
|
-
hookOk = cmd.includes("index.js");
|
|
39
|
-
hookDetail = `${hookFile} (${events} events)`;
|
|
40
|
-
}
|
|
41
|
-
catch {
|
|
42
|
-
/* missing */
|
|
43
|
-
}
|
|
44
|
-
check(true, hookOk, "hook file installed", hookDetail);
|
|
45
|
-
// 3. env file (optional) + OTel endpoint configured (required for telemetry)
|
|
46
|
-
const hasEnvFile = node_fs_1.default.existsSync(envFile);
|
|
47
|
-
check(false, hasEnvFile, "env file present", hasEnvFile ? envFile : `${envFile} (none — config may come from process.env)`);
|
|
48
|
-
let endpoint = process.env.COPILOT_PLUGIN_OPTION_ENDPOINT ||
|
|
49
|
-
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
|
|
50
|
-
process.env.OTEL_EXPORTER_OTLP_ENDPOINT;
|
|
51
|
-
if (!endpoint && hasEnvFile) {
|
|
52
|
-
const m = node_fs_1.default
|
|
53
|
-
.readFileSync(envFile, "utf-8")
|
|
54
|
-
.match(/(?:COPILOT_PLUGIN_OPTION_ENDPOINT|OTEL_EXPORTER_OTLP_(?:TRACES_)?ENDPOINT)=(.+)/);
|
|
55
|
-
if (m)
|
|
56
|
-
endpoint = m[1].trim();
|
|
57
|
-
}
|
|
58
|
-
check(true, Boolean(endpoint), "traces endpoint configured", endpoint || "(unset — spans will be dropped)");
|
|
59
|
-
// 4. guard endpoint (optional)
|
|
60
|
-
let guardEp = process.env.PINTA_GUARD_ENDPOINT;
|
|
61
|
-
if (!guardEp && hasEnvFile) {
|
|
62
|
-
const m = node_fs_1.default.readFileSync(envFile, "utf-8").match(/PINTA_GUARD_ENDPOINT=(.+)/);
|
|
63
|
-
if (m)
|
|
64
|
-
guardEp = m[1].trim();
|
|
65
|
-
}
|
|
66
|
-
check(false, Boolean(guardEp), "guard endpoint (optional)", guardEp || "(none — telemetry only, no allow/deny)");
|
|
67
|
-
// --- report ---
|
|
68
|
-
let failed = 0;
|
|
69
|
-
for (const c of checks) {
|
|
70
|
-
const mark = c.ok ? "✅" : c.required ? "❌" : "⚠️ ";
|
|
71
|
-
if (!c.ok && c.required)
|
|
72
|
-
failed++;
|
|
73
|
-
// eslint-disable-next-line no-console
|
|
74
|
-
console.log(`${mark} ${c.label}${c.detail ? ` — ${c.detail}` : ""}`);
|
|
75
|
-
}
|
|
76
|
-
// eslint-disable-next-line no-console
|
|
77
|
-
console.log(`\nsurface (this shell): ${(0, surface_js_1.detectSurface)()}`);
|
|
78
|
-
// eslint-disable-next-line no-console
|
|
79
|
-
console.log(failed === 0 ? "\nhealthy ✅" : `\n${failed} required check(s) failed ❌`);
|
|
80
|
-
process.exit(failed === 0 ? 0 : 1);
|
|
81
|
-
//# sourceMappingURL=doctor.js.map
|
|
1
|
+
"use strict";var k=Object.create;var _=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var x=Object.getPrototypeOf,R=Object.prototype.hasOwnProperty;var j=(e,o,n,l)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of C(o))!R.call(e,r)&&r!==n&&_(e,r,{get:()=>o[r],enumerable:!(l=L(o,r))||l.enumerable});return e};var a=(e,o,n)=>(n=e!=null?k(x(e)):{},j(o||!e||!e.__esModule?_(n,"default",{value:e,enumerable:!0}):n,e));var s=a(require("node:fs")),t=a(require("node:path"));function P(e=process.env){return e.COPILOT_AGENT_JOB_ID||e.COPILOT_AGENT_SESSION_ID||e.COPILOT_AGENT_PROMPT?"cloud":e.ELECTRON_RUN_AS_NODE||e.VSCODE_IPC_HOOK||e.VSCODE_PID?"ext":"cli"}var T=a(require("os")),E=a(require("path"));function m(){return process.env.COPILOT_HOME||E.default.join(T.default.homedir(),".copilot")}var h=[];function c(e,o,n,l){h.push({ok:o,label:n,detail:l,required:e})}function I(e,o){let n=s.default.readFileSync(e,"utf-8").match(o);return n?n[1].trim():void 0}var g=m(),u=t.default.join(g,"hooks","pinta-copilot.json"),i=t.default.join(g,"pinta-copilot.env"),N=t.default.join(t.default.dirname(t.default.resolve(process.argv[1])),"..","index.js");c(!0,s.default.existsSync(N),"adapter built (dist/index.js)",N);var D=!1,S=`${u} (missing \u2014 run \`npm run install-hooks\`)`;try{let e=JSON.parse(s.default.readFileSync(u,"utf-8")),o=e?.hooks?.PreToolUse?.[0]?.command??"",n=Object.keys(e?.hooks??{}).length;D=o.includes("index.js"),S=`${u} (${n} events)`}catch{}c(!0,D,"hook file installed",S);var p=s.default.existsSync(i);c(!1,p,"env file present",p?i:`${i} (none \u2014 config may come from process.env)`);var O=process.env.COPILOT_PLUGIN_OPTION_ENDPOINT||process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT||process.env.OTEL_EXPORTER_OTLP_ENDPOINT;!O&&p&&(O=I(i,/(?:COPILOT_PLUGIN_OPTION_ENDPOINT|OTEL_EXPORTER_OTLP_(?:TRACES_)?ENDPOINT)=(.+)/));c(!0,!!O,"traces endpoint configured",O||"(unset \u2014 spans will be dropped)");var d=process.env.PINTA_GUARD_ENDPOINT;!d&&p&&(d=I(i,/PINTA_GUARD_ENDPOINT=(.+)/));c(!1,!!d,"guard endpoint (optional)",d||"(none \u2014 telemetry only, no allow/deny)");var f=0;for(let e of h){let o=e.ok?"\u2705":e.required?"\u274C":"\u26A0\uFE0F ";!e.ok&&e.required&&f++,console.log(`${o} ${e.label}${e.detail?` \u2014 ${e.detail}`:""}`)}console.log(`
|
|
2
|
+
surface (this shell): ${P()}`);console.log(f===0?`
|
|
3
|
+
healthy \u2705`:`
|
|
4
|
+
${f} required check(s) failed \u274C`);process.exit(f===0?0:1);
|
|
@@ -1,101 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
/**
|
|
7
|
-
* install-hooks — write the user-level Copilot hook file that points every
|
|
8
|
-
* event at this adapter's `dist/index.js` (D4: direct hook-file install,
|
|
9
|
-
* plugin auto-discovery channel unused).
|
|
10
|
-
*
|
|
11
|
-
* Target: `$COPILOT_HOME/hooks/pinta-copilot.json` (default `~/.copilot/hooks/`).
|
|
12
|
-
* One file fires on BOTH the Copilot CLI and the VS Code extension — no VS Code
|
|
13
|
-
* setting required (`chat.useClaudeHooks` default works, §9.7). Add the same
|
|
14
|
-
* file under a repo's `.github/hooks/` for cloud/repo-scoped runs (out of scope
|
|
15
|
-
* for now).
|
|
16
|
-
*
|
|
17
|
-
* Idempotent: re-running rewrites our file in place. `--uninstall` removes it.
|
|
18
|
-
* Absolute paths are baked in at install time because user-level hooks don't
|
|
19
|
-
* receive `${COPILOT_PLUGIN_ROOT}` substitution.
|
|
20
|
-
*
|
|
21
|
-
* Usage:
|
|
22
|
-
* node dist/tools/install-hooks.js [--dry-run] [--uninstall]
|
|
23
|
-
*/
|
|
24
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
25
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
26
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
27
|
-
const HOOK_FILE = "pinta-copilot.json";
|
|
28
|
-
// Every event routes to the single adapter binary; it branches internally
|
|
29
|
-
// (telemetry for all; guard deny on PreToolUse + permissionRequest).
|
|
30
|
-
const EVENTS = [
|
|
31
|
-
"SessionStart",
|
|
32
|
-
"SessionEnd",
|
|
33
|
-
"UserPromptSubmit",
|
|
34
|
-
"PreToolUse",
|
|
35
|
-
"PostToolUse",
|
|
36
|
-
"PostToolUseFailure",
|
|
37
|
-
"Stop",
|
|
38
|
-
"SubagentStart",
|
|
39
|
-
"SubagentStop",
|
|
40
|
-
"PreCompact",
|
|
41
|
-
"Notification",
|
|
42
|
-
"permissionRequest",
|
|
43
|
-
];
|
|
44
|
-
function copilotHome() {
|
|
45
|
-
return process.env.COPILOT_HOME || node_path_1.default.join(node_os_1.default.homedir(), ".copilot");
|
|
46
|
-
}
|
|
47
|
-
function hooksDir() {
|
|
48
|
-
return node_path_1.default.join(copilotHome(), "hooks");
|
|
49
|
-
}
|
|
50
|
-
/** Absolute path to dist/index.js. This script runs as dist/tools/install-hooks.js,
|
|
51
|
-
* so resolve relative to the invoked script path (CJS/ESM agnostic). */
|
|
52
|
-
function adapterEntry() {
|
|
53
|
-
const here = node_path_1.default.dirname(node_path_1.default.resolve(process.argv[1])); // .../dist/tools
|
|
54
|
-
return node_path_1.default.join(here, "..", "index.js"); // .../dist/index.js
|
|
55
|
-
}
|
|
56
|
-
function buildHooksFile() {
|
|
57
|
-
const cmd = `${process.execPath} ${adapterEntry()}`;
|
|
58
|
-
const hooks = {};
|
|
59
|
-
for (const ev of EVENTS) {
|
|
60
|
-
// Stamp the registered event name into env as a fail-safe discriminator —
|
|
61
|
-
// some payloads (CLI subagentStart) omit any hook-name field entirely.
|
|
62
|
-
hooks[ev] = [{ type: "command", command: cmd, env: { PINTA_COPILOT_EVENT: ev } }];
|
|
63
|
-
}
|
|
64
|
-
return JSON.stringify({
|
|
65
|
-
version: 1,
|
|
66
|
-
_note: "Managed by @pinta-ai/pinta-copilot install-hooks. Do not edit by hand.",
|
|
67
|
-
hooks,
|
|
68
|
-
}, null, 2) + "\n";
|
|
69
|
-
}
|
|
70
|
-
function main() {
|
|
71
|
-
const args = process.argv.slice(2);
|
|
72
|
-
const dryRun = args.includes("--dry-run");
|
|
73
|
-
const uninstall = args.includes("--uninstall");
|
|
74
|
-
const target = node_path_1.default.join(hooksDir(), HOOK_FILE);
|
|
75
|
-
if (uninstall) {
|
|
76
|
-
if (node_fs_1.default.existsSync(target)) {
|
|
77
|
-
if (!dryRun)
|
|
78
|
-
node_fs_1.default.rmSync(target);
|
|
79
|
-
console.log(`${dryRun ? "[dry-run] would remove" : "removed"}: ${target}`);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
console.log(`nothing to remove: ${target}`);
|
|
83
|
-
}
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
const content = buildHooksFile();
|
|
87
|
-
if (node_fs_1.default.existsSync(target) && node_fs_1.default.readFileSync(target, "utf-8") === content) {
|
|
88
|
-
console.log(`already up to date: ${target}`);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (dryRun) {
|
|
92
|
-
console.log(`[dry-run] would write: ${target}\n${content}`);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
node_fs_1.default.mkdirSync(hooksDir(), { recursive: true });
|
|
96
|
-
node_fs_1.default.writeFileSync(target, content);
|
|
97
|
-
console.log(`installed: ${target}`);
|
|
98
|
-
console.log("→ restart the Copilot CLI / reload the VS Code window to load hooks.");
|
|
99
|
-
}
|
|
100
|
-
main();
|
|
101
|
-
//# sourceMappingURL=install-hooks.js.map
|
|
1
|
+
"use strict";var m=Object.create;var a=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,h=Object.prototype.hasOwnProperty;var y=(o,n,e,t)=>{if(n&&typeof n=="object"||typeof n=="function")for(let r of f(n))!h.call(o,r)&&r!==e&&a(o,r,{get:()=>n[r],enumerable:!(t=g(n,r))||t.enumerable});return o};var c=(o,n,e)=>(e=o!=null?m(S(o)):{},y(n||!o||!o.__esModule?a(e,"default",{value:o,enumerable:!0}):e,o));var i=c(require("node:fs")),s=c(require("node:path"));var l=c(require("os")),u=c(require("path"));function p(){return process.env.COPILOT_HOME||u.default.join(l.default.homedir(),".copilot")}var P="pinta-copilot.json",v=["SessionStart","SessionEnd","UserPromptSubmit","PreToolUse","PostToolUse","PostToolUseFailure","Stop","SubagentStart","SubagentStop","PreCompact","Notification","permissionRequest"];function d(){return s.default.join(p(),"hooks")}function C(){let o=s.default.dirname(s.default.resolve(process.argv[1]));return s.default.join(o,"..","index.js")}function O(){let o=`${process.execPath} ${C()}`,n={};for(let e of v)n[e]=[{type:"command",command:o,env:{PINTA_COPILOT_EVENT:e}}];return JSON.stringify({version:1,_note:"Managed by @pinta-ai/pinta-copilot install-hooks. Do not edit by hand.",hooks:n},null,2)+`
|
|
2
|
+
`}function T(){let o=process.argv.slice(2),n=o.includes("--dry-run"),e=o.includes("--uninstall"),t=s.default.join(d(),P);if(e){i.default.existsSync(t)?(n||i.default.rmSync(t),console.log(`${n?"[dry-run] would remove":"removed"}: ${t}`)):console.log(`nothing to remove: ${t}`);return}let r=O();if(i.default.existsSync(t)&&i.default.readFileSync(t,"utf-8")===r){console.log(`already up to date: ${t}`);return}if(n){console.log(`[dry-run] would write: ${t}
|
|
3
|
+
${r}`);return}i.default.mkdirSync(d(),{recursive:!0}),i.default.writeFileSync(t,r),console.log(`installed: ${t}`),console.log("\u2192 restart the Copilot CLI / reload the VS Code window to load hooks.")}T();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pinta-ai/pinta-copilot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Generic OTLP forwarder + guard for GitHub Copilot hook events (CLI + VS Code extension)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
22
|
"bump": "node scripts/bump.mjs",
|
|
23
|
-
"build": "tsc",
|
|
23
|
+
"build": "tsc --noEmit && esbuild src/index.ts src/tools/doctor.ts src/tools/install-hooks.ts --bundle --platform=node --format=cjs --target=node18 --minify --outbase=src --outdir=dist",
|
|
24
24
|
"dev": "tsc --watch",
|
|
25
25
|
"test": "vitest run",
|
|
26
26
|
"test:watch": "vitest",
|
|
@@ -31,9 +31,15 @@
|
|
|
31
31
|
"prepublishOnly": "npm run build"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
+
"@pinta-ai/core": "^0.3.0",
|
|
34
35
|
"@types/node": "^22.0.0",
|
|
36
|
+
"esbuild": "^0.24.0",
|
|
35
37
|
"tsx": "^4.0.0",
|
|
36
38
|
"typescript": "^5.7.0",
|
|
37
39
|
"vitest": "^2.1.0"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"registry": "https://registry.npmjs.org",
|
|
43
|
+
"access": "public"
|
|
38
44
|
}
|
|
39
45
|
}
|
package/dist/core/config.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime config. Endpoint/headers come from OTEL_EXPORTER_OTLP_* env vars
|
|
3
|
-
* (loaded from ~/.copilot/pinta-copilot.env at startup) — not in this struct.
|
|
4
|
-
*
|
|
5
|
-
* pinta-copilot is installed as a direct hook file (D4), so the data dir
|
|
6
|
-
* (trace + retry-queue) must be a STABLE location independent of cwd —
|
|
7
|
-
* otherwise the per-turn trace written by UserPromptSubmit can't be read back
|
|
8
|
-
* by the following PreToolUse if cwd differs. We anchor it under the Copilot
|
|
9
|
-
* home (`$COPILOT_HOME` or `~/.copilot`).
|
|
10
|
-
*/
|
|
11
|
-
export interface PintaConfig {
|
|
12
|
-
pluginData: string;
|
|
13
|
-
tracePath: string;
|
|
14
|
-
}
|
|
15
|
-
export declare function loadConfig(): PintaConfig;
|
package/dist/core/config.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.loadConfig = loadConfig;
|
|
7
|
-
const os_1 = __importDefault(require("os"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
function copilotHome() {
|
|
10
|
-
return process.env.COPILOT_HOME || path_1.default.join(os_1.default.homedir(), ".copilot");
|
|
11
|
-
}
|
|
12
|
-
function loadConfig() {
|
|
13
|
-
const pluginData = process.env.COPILOT_PLUGIN_DATA || path_1.default.join(copilotHome(), "pinta-copilot-data");
|
|
14
|
-
return {
|
|
15
|
-
pluginData,
|
|
16
|
-
tracePath: path_1.default.join(pluginData, "trace.json"),
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=config.js.map
|
package/dist/core/config.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":";;;;;AAsBA,gCAOC;AA7BD,4CAAoB;AACpB,gDAAwB;AAiBxB,SAAS,WAAW;IAClB,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,cAAI,CAAC,IAAI,CAAC,YAAE,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AACzE,CAAC;AAED,SAAgB,UAAU;IACxB,MAAM,UAAU,GACd,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,oBAAoB,CAAC,CAAC;IACpF,OAAO;QACL,UAAU;QACV,SAAS,EAAE,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC;KAC/C,CAAC;AACJ,CAAC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bridge Claude Code's userConfig env vars (CLAUDE_PLUGIN_OPTION_*) to the
|
|
3
|
-
* OTel SDK standard env vars (OTEL_EXPORTER_OTLP_*).
|
|
4
|
-
*
|
|
5
|
-
* Claude Code maps each plugin.json `userConfig.<key>` to a corresponding
|
|
6
|
-
* `CLAUDE_PLUGIN_OPTION_<KEY>` env var on hook spawn. We keep the user-
|
|
7
|
-
* facing names friendly (`endpoint`, `api_key`) and translate them into the
|
|
8
|
-
* canonical OTel env names so transport.ts (and any future OTel SDK adoption)
|
|
9
|
-
* can read them via the OTel-spec names.
|
|
10
|
-
*
|
|
11
|
-
* The user-facing `endpoint` is treated as a *full* OTLP/HTTP traces URL
|
|
12
|
-
* (e.g., `http://127.0.0.1:5147/v1/traces`), so we map it to
|
|
13
|
-
* `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` (signal-specific full URL per OTel
|
|
14
|
-
* spec) rather than `OTEL_EXPORTER_OTLP_ENDPOINT` (which is a base URL the
|
|
15
|
-
* SDK appends `/v1/traces` to). This way both the OTel SDK (when used) and
|
|
16
|
-
* pinta-cc's hand-written transport agree on the URL without any path
|
|
17
|
-
* manipulation downstream.
|
|
18
|
-
*
|
|
19
|
-
* Pinta Manager auto-injects `CLAUDE_PLUGIN_OPTION_*` via Claude Code's
|
|
20
|
-
* settings.json. OSS users fill them in via the `/plugin install` UI.
|
|
21
|
-
*
|
|
22
|
-
* Existing OTEL_EXPORTER_OTLP_TRACES_ENDPOINT / HEADERS take precedence
|
|
23
|
-
* (explicit override).
|
|
24
|
-
*/
|
|
25
|
-
export declare function bridgeUserConfigToOtelEnv(): void;
|
package/dist/core/env-bridge.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.bridgeUserConfigToOtelEnv = bridgeUserConfigToOtelEnv;
|
|
4
|
-
/**
|
|
5
|
-
* Bridge Claude Code's userConfig env vars (CLAUDE_PLUGIN_OPTION_*) to the
|
|
6
|
-
* OTel SDK standard env vars (OTEL_EXPORTER_OTLP_*).
|
|
7
|
-
*
|
|
8
|
-
* Claude Code maps each plugin.json `userConfig.<key>` to a corresponding
|
|
9
|
-
* `CLAUDE_PLUGIN_OPTION_<KEY>` env var on hook spawn. We keep the user-
|
|
10
|
-
* facing names friendly (`endpoint`, `api_key`) and translate them into the
|
|
11
|
-
* canonical OTel env names so transport.ts (and any future OTel SDK adoption)
|
|
12
|
-
* can read them via the OTel-spec names.
|
|
13
|
-
*
|
|
14
|
-
* The user-facing `endpoint` is treated as a *full* OTLP/HTTP traces URL
|
|
15
|
-
* (e.g., `http://127.0.0.1:5147/v1/traces`), so we map it to
|
|
16
|
-
* `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` (signal-specific full URL per OTel
|
|
17
|
-
* spec) rather than `OTEL_EXPORTER_OTLP_ENDPOINT` (which is a base URL the
|
|
18
|
-
* SDK appends `/v1/traces` to). This way both the OTel SDK (when used) and
|
|
19
|
-
* pinta-cc's hand-written transport agree on the URL without any path
|
|
20
|
-
* manipulation downstream.
|
|
21
|
-
*
|
|
22
|
-
* Pinta Manager auto-injects `CLAUDE_PLUGIN_OPTION_*` via Claude Code's
|
|
23
|
-
* settings.json. OSS users fill them in via the `/plugin install` UI.
|
|
24
|
-
*
|
|
25
|
-
* Existing OTEL_EXPORTER_OTLP_TRACES_ENDPOINT / HEADERS take precedence
|
|
26
|
-
* (explicit override).
|
|
27
|
-
*/
|
|
28
|
-
function bridgeUserConfigToOtelEnv() {
|
|
29
|
-
if (process.env.CLAUDE_PLUGIN_OPTION_ENDPOINT &&
|
|
30
|
-
!process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) {
|
|
31
|
-
process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT =
|
|
32
|
-
process.env.CLAUDE_PLUGIN_OPTION_ENDPOINT;
|
|
33
|
-
}
|
|
34
|
-
if (process.env.CLAUDE_PLUGIN_OPTION_API_KEY && !process.env.OTEL_EXPORTER_OTLP_HEADERS) {
|
|
35
|
-
process.env.OTEL_EXPORTER_OTLP_HEADERS =
|
|
36
|
-
`x-pinta-relay-token=${process.env.CLAUDE_PLUGIN_OPTION_API_KEY}`;
|
|
37
|
-
}
|
|
38
|
-
// Expose for guard.ts (~/guard/evaluate auth header)
|
|
39
|
-
if (!process.env.PINTA_RELAY_TOKEN && process.env.CLAUDE_PLUGIN_OPTION_API_KEY) {
|
|
40
|
-
process.env.PINTA_RELAY_TOKEN = process.env.CLAUDE_PLUGIN_OPTION_API_KEY;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=env-bridge.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"env-bridge.js","sourceRoot":"","sources":["../../src/core/env-bridge.ts"],"names":[],"mappings":";;AAwBA,8DAgBC;AAxCD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAgB,yBAAyB;IACvC,IACE,OAAO,CAAC,GAAG,CAAC,6BAA6B;QACzC,CAAC,OAAO,CAAC,GAAG,CAAC,kCAAkC,EAC/C,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,kCAAkC;YAC5C,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC;QACxF,OAAO,CAAC,GAAG,CAAC,0BAA0B;YACpC,uBAAuB,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,CAAC;IACtE,CAAC;IACD,qDAAqD;IACrD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,CAAC;QAC/E,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC;IAC3E,CAAC;AACH,CAAC"}
|
package/dist/core/guard.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface GuardInput {
|
|
2
|
-
spanId: string;
|
|
3
|
-
toolName?: string;
|
|
4
|
-
toolInput?: unknown;
|
|
5
|
-
rawTextFields?: Record<string, string>;
|
|
6
|
-
}
|
|
7
|
-
export interface GuardResult {
|
|
8
|
-
decision: 'ALLOW' | 'DENY' | 'REVIEW';
|
|
9
|
-
reason: string | null;
|
|
10
|
-
userMessage: string | null;
|
|
11
|
-
durationMs: number;
|
|
12
|
-
failOpenReason?: 'timeout' | 'refused' | 'error';
|
|
13
|
-
}
|
|
14
|
-
export declare function evaluateGuard(input: GuardInput, endpoint: string | undefined): Promise<GuardResult | null>;
|
package/dist/core/guard.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.evaluateGuard = evaluateGuard;
|
|
4
|
-
// Guard must be fast or fail-open. 50ms default keeps the hook snappy;
|
|
5
|
-
// override for slower relays (or test harnesses) via PINTA_GUARD_TIMEOUT_MS.
|
|
6
|
-
const TIMEOUT_MS = Number(process.env.PINTA_GUARD_TIMEOUT_MS) || 50;
|
|
7
|
-
// Self-identify to the manager's guard route so it can attribute calls to this
|
|
8
|
-
// adaptor (the route parses `pinta-*/<version>` out of the User-Agent). Keep the
|
|
9
|
-
// version in sync with package.json.
|
|
10
|
-
const GUARD_UA = 'pinta-copilot/0.3.1';
|
|
11
|
-
function sleep(ms) {
|
|
12
|
-
return new Promise((_, reject) => setTimeout(() => {
|
|
13
|
-
const err = new Error('Guard request timed out');
|
|
14
|
-
err.name = 'TimeoutError';
|
|
15
|
-
reject(err);
|
|
16
|
-
}, ms));
|
|
17
|
-
}
|
|
18
|
-
async function evaluateGuard(input, endpoint) {
|
|
19
|
-
if (!endpoint)
|
|
20
|
-
return null;
|
|
21
|
-
if (process.env.PINTA_GUARD_DISABLED === '1')
|
|
22
|
-
return null;
|
|
23
|
-
const start = Date.now();
|
|
24
|
-
try {
|
|
25
|
-
const res = await Promise.race([
|
|
26
|
-
fetch(endpoint, {
|
|
27
|
-
method: 'POST',
|
|
28
|
-
headers: {
|
|
29
|
-
'content-type': 'application/json',
|
|
30
|
-
'user-agent': GUARD_UA,
|
|
31
|
-
'x-pinta-relay-token': process.env.PINTA_RELAY_TOKEN ?? '',
|
|
32
|
-
},
|
|
33
|
-
body: JSON.stringify({ input }),
|
|
34
|
-
}),
|
|
35
|
-
sleep(TIMEOUT_MS),
|
|
36
|
-
]);
|
|
37
|
-
if (res.status !== 200) {
|
|
38
|
-
return { decision: 'ALLOW', reason: null, userMessage: null, durationMs: Date.now() - start, failOpenReason: 'error' };
|
|
39
|
-
}
|
|
40
|
-
const body = (await res.json());
|
|
41
|
-
return {
|
|
42
|
-
decision: body.decision,
|
|
43
|
-
reason: body.reason,
|
|
44
|
-
userMessage: body.userMessage ?? null,
|
|
45
|
-
durationMs: body.durationMs ?? (Date.now() - start),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
catch (err) {
|
|
49
|
-
const reason = err.name === 'TimeoutError' ? 'timeout' : 'error';
|
|
50
|
-
return { decision: 'ALLOW', reason: null, userMessage: null, durationMs: Date.now() - start, failOpenReason: reason };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
//# sourceMappingURL=guard.js.map
|
package/dist/core/guard.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/core/guard.ts"],"names":[],"mappings":";;AAqCA,sCAuCC;AA1DD,uEAAuE;AACvE,6EAA6E;AAC7E,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,EAAE,CAAC;AAEpE,+EAA+E;AAC/E,iFAAiF;AACjF,qCAAqC;AACrC,MAAM,QAAQ,GAAG,qBAAqB,CAAC;AAEvC,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE;QACd,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACjD,GAAG,CAAC,IAAI,GAAG,cAAc,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,CAAC;IACd,CAAC,EAAE,EAAE,CAAC,CACP,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,aAAa,CACjC,KAAiB,EACjB,QAA4B;IAE5B,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAC7B,KAAK,CAAC,QAAQ,EAAE;gBACd,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,YAAY,EAAE,QAAQ;oBACtB,qBAAqB,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE;iBAC3D;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;aAChC,CAAC;YACF,KAAK,CAAC,UAAU,CAAC;SAClB,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACvB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;QACzH,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAK7B,CAAC;QACF,OAAO;YACL,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI;YACrC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;SACpD,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAmC,GAAa,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QAC3G,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,CAAC;IACxH,CAAC;AACH,CAAC"}
|