oc-hush 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,92 @@
1
- # Temporary Holding Version
1
+ # oc-hush
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ![oc-hush: your agent uses the key, it never sees it](media/banner.png)
4
+
5
+ Credentials your agent can use without reading them.
6
+
7
+ **WIP.** An OpenCode V2 plugin with a server part, a TUI part, and an experimental desktop app part. Requires OpenCode 2.2.6 or newer.
8
+
9
+ ## Install
10
+
11
+ ```sh
12
+ opencode plugin add oc-hush
13
+ ```
14
+
15
+ This installs Hush from npm and adds it to your global config, so it works in the TUI and the desktop app.
16
+
17
+ ## Try it locally
18
+
19
+ From this package:
20
+
21
+ ```sh
22
+ bun run build
23
+ cp opencode.example.jsonc opencode.jsonc
24
+ opencode --standalone
25
+ ```
26
+
27
+ For another project, add this package’s absolute `dist` directory to `plugins` in its `opencode.jsonc`. Build before loading it.
28
+
29
+ Ask the agent to use Hush when a command needs a credential. When it asks, a **Temporary Credential** dialog opens by itself and names the credential, such as "GitHub token". Type or paste the credential: it shows as dots and stays out of the prompt and its history. Enter approves; Escape denies. Never paste credentials into chat.
30
+
31
+ Try it:
32
+
33
+ ```
34
+ Use Hush to get my GitHub token, then run gh repo list with it.
35
+ ```
36
+
37
+ A request made in another session shows a toast that offers to open it, and a system notification while the terminal is in the background.
38
+
39
+ Requests made by subagents appear in the parent session, so you can answer them from the session you are watching.
40
+ Run `/hush-forget` to revoke this session’s credentials, including its subagents’, and cancel its waiting requests.
41
+
42
+ ### Desktop app
43
+
44
+ The `./gui-experimental` bundle adds the same flow to the OpenCode desktop app. It uses the app’s experimental plugin GUI API and runs only in the desktop app, not on the web.
45
+
46
+ The desktop app loads plugin GUIs only from your global config (`~/.config/opencode/opencode.jsonc`), not from a project’s `opencode.jsonc`. To try it locally, add the absolute path to this package’s `dist` folder to the global `plugins` list and restart the app.
47
+
48
+ - The review dialog opens by itself when the agent asks in the session on screen. It names the credential, with a password field the app neither stores nor logs. **Approve** sends the credential to Hush; **Deny** cancels the request. Escape only closes the dialog: the request keeps waiting in a lock pill above the composer, which reopens it.
49
+ - A toast with a **Review** button appears in windows that have the session open, and a system notification is sent while no app window has focus.
50
+ - `/hush-forget` works in the composer and the command palette.
51
+
52
+ ## Options
53
+
54
+ ```jsonc
55
+ { "plugins": [{ "package": "oc-hush", "options": { "ttlMinutes": 15 } }] }
56
+ ```
57
+
58
+ - `ttlMinutes`: how long an approved credential is kept, counted from approval. Default `15`, maximum `1440`. `0` forgets it after its first use.
59
+
60
+ ## Agent tool
61
+
62
+ - `hush_request_secret` with `{ label }`: asks you for a credential and returns `{ file }`, a private file holding it. The agent reads the file inside the command that needs it, with OpenCode’s normal shell tool: `GH_TOKEN="$(cat <file>)" gh repo list`. The credential itself is never returned.
63
+
64
+ It works as a native tool call when Code Mode is off and through the Code Mode catalog when it is on, where programs use the file through the shell tool.
65
+
66
+ ## Security boundary
67
+
68
+ - Credentials are kept in the server plugin’s memory and written to a file in a directory only your user can read (`<tmp>/oc-hush-<pid>/`, file mode 600). The file is deleted when the credential expires, on `/hush-forget`, on plugin reload, and when OpenCode exits. If the process is killed, the next start removes the leftover directory. No conversation messages or form answers are created by this plugin.
69
+ - **Every tool result is redacted.** Before the model sees any tool’s output, Hush replaces each live credential, and its base64, hex, URL-encoded and reversed forms, with `[redacted by Hush]`. This applies to all tools, including the shell and Code Mode, and to what OpenCode saves in the session.
70
+ - Credentials expire 15 minutes after you approve them. Change this with the `ttlMinutes` option (up to 1440); `0` forgets a credential after the first tool call that reads its file. Requests expire after five minutes. Reloading the server plugin drops all credentials.
71
+ - The private reply RPC accepts credentials but never returns them. There is no credential-read RPC or tool. Custom clients must submit replies directly, not through an agent tool call.
72
+ - The TUI reads the credential through a focused input whose text is invisible, and renders only a mask. Nothing reaches the prompt or its history.
73
+
74
+ This protects the conversation boundary, not a compromised machine, malicious plugin, terminal recorder, HTTP proxy, debugger, or hostile process running as your user. JavaScript strings are garbage-collected, not securely zeroized. OpenCode’s authenticated client/server transport is trusted; use an encrypted connection for remote servers. Do not enable request-body capture on that transport.
75
+
76
+ **The agent never sees your credential.** You never paste it into chat, the agent only ever gets a file path, and anything a command prints is redacted before the model reads it.
77
+
78
+ > [!NOTE]
79
+ > Commands run with your permissions, so they can read the file. Redaction catches the credential whole or encoded, but a command written to print it in pieces (such as `cut -c1-6 <file>`) would get past it. That takes a command you approve, and OpenCode’s permission prompt shows it in full first. Approve commands that use the file as you would any command that handles a secret.
80
+
81
+ ## Development
82
+
83
+ ```sh
84
+ bun run check
85
+ bun run test
86
+ bun run build
87
+ bun run publish:dry-run
88
+ ```
89
+
90
+ `bun run build` runs tsup for the server and TUI parts and Vite for `dist/gui-experimental.cjs`. `src/gui.tsx` compiles Solid for the DOM, so it has its own `tsconfig.gui.json`.
91
+
92
+ The `./rpc` export exposes the typed UI contract for other trusted clients. It contains pending request metadata, reply, and session revocation methods, and a `changed` event with the affected session IDs and any new request’s metadata. Events never carry credentials or file paths.
@@ -0,0 +1,20 @@
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const E=require("solid-js/web"),B=require("@opencode/plugin/gui"),F=require("solid-js"),we=require("solid-js/store"),vn=require("@opencode/plugin/rpc");function s(e,n,t){function r(u,a){var l;Object.defineProperty(u,"_zod",{value:u._zod??{},enumerable:!1}),(l=u._zod).traits??(l.traits=new Set),u._zod.traits.add(e),n(u,a);for(const h in i.prototype)h in u||Object.defineProperty(u,h,{value:i.prototype[h].bind(u)});u._zod.constr=i,u._zod.def=a}const o=t?.Parent??Object;class c extends o{}Object.defineProperty(c,"name",{value:e});function i(u){var a;const l=t?.Parent?new c:this;r(l,u),(a=l._zod).deferred??(a.deferred=[]);for(const h of l._zod.deferred)h();return l}return Object.defineProperty(i,"init",{value:r}),Object.defineProperty(i,Symbol.hasInstance,{value:u=>t?.Parent&&u instanceof t.Parent?!0:u?._zod?.traits?.has(e)}),Object.defineProperty(i,"name",{value:e}),i}class L extends Error{constructor(){super("Encountered Promise during synchronous parse. Use .parseAsync() instead.")}}class We extends Error{constructor(n){super(`Encountered unidirectional transform during encode: ${n}`),this.name="ZodEncodeError"}}const Ge={};function j(e){return Ge}function bn(e){const n=Object.values(e).filter(r=>typeof r=="number");return Object.entries(e).filter(([r,o])=>n.indexOf(+r)===-1).map(([r,o])=>o)}function ae(e,n){return typeof n=="bigint"?n.toString():n}function fe(e){return{get value(){{const n=e();return Object.defineProperty(this,"value",{value:n}),n}}}}function pe(e){return e==null}function he(e){const n=e.startsWith("^")?1:0,t=e.endsWith("$")?e.length-1:e.length;return e.slice(n,t)}function zn(e,n){const t=(e.toString().split(".")[1]||"").length,r=n.toString();let o=(r.split(".")[1]||"").length;if(o===0&&/\d?e-\d?/.test(r)){const a=r.match(/\d?e-(\d?)/);a?.[1]&&(o=Number.parseInt(a[1]))}const c=t>o?t:o,i=Number.parseInt(e.toFixed(c).replace(".","")),u=Number.parseInt(n.toFixed(c).replace(".",""));return i%u/10**c}const ke=Symbol("evaluating");function m(e,n,t){let r;Object.defineProperty(e,n,{get(){if(r!==ke)return r===void 0&&(r=ke,r=t()),r},set(o){Object.defineProperty(e,n,{value:o})},configurable:!0})}function wn(e){return Object.create(Object.getPrototypeOf(e),Object.getOwnPropertyDescriptors(e))}function N(e,n,t){Object.defineProperty(e,n,{value:t,writable:!0,enumerable:!0,configurable:!0})}function V(...e){const n={};for(const t of e){const r=Object.getOwnPropertyDescriptors(t);Object.assign(n,r)}return Object.defineProperties({},n)}function Ze(e){return JSON.stringify(e)}const Je="captureStackTrace"in Error?Error.captureStackTrace:(...e)=>{};function X(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}const kn=fe(()=>{if(typeof navigator<"u"&&navigator?.userAgent?.includes("Cloudflare"))return!1;try{const e=Function;return new e(""),!0}catch{return!1}});function W(e){if(X(e)===!1)return!1;const n=e.constructor;if(n===void 0)return!0;const t=n.prototype;return!(X(t)===!1||Object.prototype.hasOwnProperty.call(t,"isPrototypeOf")===!1)}function Ke(e){return W(e)?{...e}:Array.isArray(e)?[...e]:e}const Zn=new Set(["string","number","symbol"]);function ne(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function D(e,n,t){const r=new e._zod.constr(n??e._zod.def);return(!n||t?.parent)&&(r._zod.parent=e),r}function d(e){const n=e;if(!n)return{};if(typeof n=="string")return{error:()=>n};if(n?.message!==void 0){if(n?.error!==void 0)throw new Error("Cannot specify both `message` and `error` params");n.error=n.message}return delete n.message,typeof n.error=="string"?{...n,error:()=>n.error}:n}function $n(e){return Object.keys(e).filter(n=>e[n]._zod.optin==="optional"&&e[n]._zod.optout==="optional")}const yn={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-34028234663852886e22,34028234663852886e22],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]};function In(e,n){const t=e._zod.def,r=V(e._zod.def,{get shape(){const o={};for(const c in n){if(!(c in t.shape))throw new Error(`Unrecognized key: "${c}"`);n[c]&&(o[c]=t.shape[c])}return N(this,"shape",o),o},checks:[]});return D(e,r)}function En(e,n){const t=e._zod.def,r=V(e._zod.def,{get shape(){const o={...e._zod.def.shape};for(const c in n){if(!(c in t.shape))throw new Error(`Unrecognized key: "${c}"`);n[c]&&delete o[c]}return N(this,"shape",o),o},checks:[]});return D(e,r)}function An(e,n){if(!W(n))throw new Error("Invalid input to extend: expected a plain object");const t=e._zod.def.checks;if(t&&t.length>0)throw new Error("Object schemas containing refinements cannot be extended. Use `.safeExtend()` instead.");const o=V(e._zod.def,{get shape(){const c={...e._zod.def.shape,...n};return N(this,"shape",c),c},checks:[]});return D(e,o)}function Pn(e,n){if(!W(n))throw new Error("Invalid input to safeExtend: expected a plain object");const t={...e._zod.def,get shape(){const r={...e._zod.def.shape,...n};return N(this,"shape",r),r},checks:e._zod.def.checks};return D(e,t)}function Tn(e,n){const t=V(e._zod.def,{get shape(){const r={...e._zod.def.shape,...n._zod.def.shape};return N(this,"shape",r),r},get catchall(){return n._zod.def.catchall},checks:[]});return D(e,t)}function On(e,n,t){const r=V(n._zod.def,{get shape(){const o=n._zod.def.shape,c={...o};if(t)for(const i in t){if(!(i in o))throw new Error(`Unrecognized key: "${i}"`);t[i]&&(c[i]=e?new e({type:"optional",innerType:o[i]}):o[i])}else for(const i in o)c[i]=e?new e({type:"optional",innerType:o[i]}):o[i];return N(this,"shape",c),c},checks:[]});return D(n,r)}function Sn(e,n,t){const r=V(n._zod.def,{get shape(){const o=n._zod.def.shape,c={...o};if(t)for(const i in t){if(!(i in c))throw new Error(`Unrecognized key: "${i}"`);t[i]&&(c[i]=new e({type:"nonoptional",innerType:o[i]}))}else for(const i in o)c[i]=new e({type:"nonoptional",innerType:o[i]});return N(this,"shape",c),c},checks:[]});return D(n,r)}function U(e,n=0){if(e.aborted===!0)return!0;for(let t=n;t<e.issues.length;t++)if(e.issues[t]?.continue!==!0)return!0;return!1}function qe(e,n){return n.map(t=>{var r;return(r=t).path??(r.path=[]),t.path.unshift(e),t})}function K(e){return typeof e=="string"?e:e?.message}function R(e,n,t){const r={...e,path:e.path??[]};if(!e.message){const o=K(e.inst?._zod.def?.error?.(e))??K(n?.error?.(e))??K(t.customError?.(e))??K(t.localeError?.(e))??"Invalid input";r.message=o}return delete r.inst,delete r.continue,n?.reportInput||delete r.input,r}function me(e){return Array.isArray(e)?"array":typeof e=="string"?"string":"unknown"}function G(...e){const[n,t,r]=e;return typeof n=="string"?{message:n,code:"custom",input:t,inst:r}:{...n}}const He=(e,n)=>{e.name="$ZodError",Object.defineProperty(e,"_zod",{value:e._zod,enumerable:!1}),Object.defineProperty(e,"issues",{value:n,enumerable:!1}),e.message=JSON.stringify(n,ae,2),Object.defineProperty(e,"toString",{value:()=>e.message,enumerable:!1})},Ye=s("$ZodError",He),Xe=s("$ZodError",He,{Parent:Error});function Cn(e,n=t=>t.message){const t={},r=[];for(const o of e.issues)o.path.length>0?(t[o.path[0]]=t[o.path[0]]||[],t[o.path[0]].push(n(o))):r.push(n(o));return{formErrors:r,fieldErrors:t}}function Nn(e,n){const t=n||function(c){return c.message},r={_errors:[]},o=c=>{for(const i of c.issues)if(i.code==="invalid_union"&&i.errors.length)i.errors.map(u=>o({issues:u}));else if(i.code==="invalid_key")o({issues:i.issues});else if(i.code==="invalid_element")o({issues:i.issues});else if(i.path.length===0)r._errors.push(t(i));else{let u=r,a=0;for(;a<i.path.length;){const l=i.path[a];a===i.path.length-1?(u[l]=u[l]||{_errors:[]},u[l]._errors.push(t(i))):u[l]=u[l]||{_errors:[]},u=u[l],a++}}};return o(e),r}const _e=e=>(n,t,r,o)=>{const c=r?Object.assign(r,{async:!1}):{async:!1},i=n._zod.run({value:t,issues:[]},c);if(i instanceof Promise)throw new L;if(i.issues.length){const u=new(o?.Err??e)(i.issues.map(a=>R(a,c,j())));throw Je(u,o?.callee),u}return i.value},ge=e=>async(n,t,r,o)=>{const c=r?Object.assign(r,{async:!0}):{async:!0};let i=n._zod.run({value:t,issues:[]},c);if(i instanceof Promise&&(i=await i),i.issues.length){const u=new(o?.Err??e)(i.issues.map(a=>R(a,c,j())));throw Je(u,o?.callee),u}return i.value},te=e=>(n,t,r)=>{const o=r?{...r,async:!1}:{async:!1},c=n._zod.run({value:t,issues:[]},o);if(c instanceof Promise)throw new L;return c.issues.length?{success:!1,error:new(e??Ye)(c.issues.map(i=>R(i,o,j())))}:{success:!0,data:c.value}},Dn=te(Xe),re=e=>async(n,t,r)=>{const o=r?Object.assign(r,{async:!0}):{async:!0};let c=n._zod.run({value:t,issues:[]},o);return c instanceof Promise&&(c=await c),c.issues.length?{success:!1,error:new e(c.issues.map(i=>R(i,o,j())))}:{success:!0,data:c.value}},jn=re(Xe),Rn=e=>(n,t,r)=>{const o=r?Object.assign(r,{direction:"backward"}):{direction:"backward"};return _e(e)(n,t,o)},xn=e=>(n,t,r)=>_e(e)(n,t,r),Fn=e=>async(n,t,r)=>{const o=r?Object.assign(r,{direction:"backward"}):{direction:"backward"};return ge(e)(n,t,o)},Un=e=>async(n,t,r)=>ge(e)(n,t,r),Ln=e=>(n,t,r)=>{const o=r?Object.assign(r,{direction:"backward"}):{direction:"backward"};return te(e)(n,t,o)},Mn=e=>(n,t,r)=>te(e)(n,t,r),Vn=e=>async(n,t,r)=>{const o=r?Object.assign(r,{direction:"backward"}):{direction:"backward"};return re(e)(n,t,o)},Bn=e=>async(n,t,r)=>re(e)(n,t,r),Wn=/^[cC][^\s-]{8,}$/,Gn=/^[0-9a-z]+$/,Jn=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,Kn=/^[0-9a-vA-V]{20}$/,qn=/^[A-Za-z0-9]{27}$/,Hn=/^[a-zA-Z0-9_-]{21}$/,Yn=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,Xn=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,$e=e=>e?new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${e}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`):/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/,Qn=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/,et="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";function nt(){return new RegExp(et,"u")}const tt=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,rt=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,ot=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,it=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,ct=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,Qe=/^[A-Za-z0-9_-]*$/,st=/^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/,ut=/^\+(?:[0-9]){6,14}[0-9]$/,en="(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))",at=new RegExp(`^${en}$`);function nn(e){const n="(?:[01]\\d|2[0-3]):[0-5]\\d";return typeof e.precision=="number"?e.precision===-1?`${n}`:e.precision===0?`${n}:[0-5]\\d`:`${n}:[0-5]\\d\\.\\d{${e.precision}}`:`${n}(?::[0-5]\\d(?:\\.\\d+)?)?`}function lt(e){return new RegExp(`^${nn(e)}$`)}function dt(e){const n=nn({precision:e.precision}),t=["Z"];e.local&&t.push(""),e.offset&&t.push("([+-](?:[01]\\d|2[0-3]):[0-5]\\d)");const r=`${n}(?:${t.join("|")})`;return new RegExp(`^${en}T(?:${r})$`)}const ft=e=>{const n=e?`[\\s\\S]{${e?.minimum??0},${e?.maximum??""}}`:"[\\s\\S]*";return new RegExp(`^${n}$`)},pt=/^-?\d+$/,ht=/^-?\d+(?:\.\d+)?/,mt=/^(?:true|false)$/i,_t=/^[^A-Z]*$/,gt=/^[^a-z]*$/,A=s("$ZodCheck",(e,n)=>{var t;e._zod??(e._zod={}),e._zod.def=n,(t=e._zod).onattach??(t.onattach=[])}),tn={number:"number",bigint:"bigint",object:"date"},rn=s("$ZodCheckLessThan",(e,n)=>{A.init(e,n);const t=tn[typeof n.value];e._zod.onattach.push(r=>{const o=r._zod.bag,c=(n.inclusive?o.maximum:o.exclusiveMaximum)??Number.POSITIVE_INFINITY;n.value<c&&(n.inclusive?o.maximum=n.value:o.exclusiveMaximum=n.value)}),e._zod.check=r=>{(n.inclusive?r.value<=n.value:r.value<n.value)||r.issues.push({origin:t,code:"too_big",maximum:n.value,input:r.value,inclusive:n.inclusive,inst:e,continue:!n.abort})}}),on=s("$ZodCheckGreaterThan",(e,n)=>{A.init(e,n);const t=tn[typeof n.value];e._zod.onattach.push(r=>{const o=r._zod.bag,c=(n.inclusive?o.minimum:o.exclusiveMinimum)??Number.NEGATIVE_INFINITY;n.value>c&&(n.inclusive?o.minimum=n.value:o.exclusiveMinimum=n.value)}),e._zod.check=r=>{(n.inclusive?r.value>=n.value:r.value>n.value)||r.issues.push({origin:t,code:"too_small",minimum:n.value,input:r.value,inclusive:n.inclusive,inst:e,continue:!n.abort})}}),vt=s("$ZodCheckMultipleOf",(e,n)=>{A.init(e,n),e._zod.onattach.push(t=>{var r;(r=t._zod.bag).multipleOf??(r.multipleOf=n.value)}),e._zod.check=t=>{if(typeof t.value!=typeof n.value)throw new Error("Cannot mix number and bigint in multiple_of check.");(typeof t.value=="bigint"?t.value%n.value===BigInt(0):zn(t.value,n.value)===0)||t.issues.push({origin:typeof t.value,code:"not_multiple_of",divisor:n.value,input:t.value,inst:e,continue:!n.abort})}}),bt=s("$ZodCheckNumberFormat",(e,n)=>{A.init(e,n),n.format=n.format||"float64";const t=n.format?.includes("int"),r=t?"int":"number",[o,c]=yn[n.format];e._zod.onattach.push(i=>{const u=i._zod.bag;u.format=n.format,u.minimum=o,u.maximum=c,t&&(u.pattern=pt)}),e._zod.check=i=>{const u=i.value;if(t){if(!Number.isInteger(u)){i.issues.push({expected:r,format:n.format,code:"invalid_type",continue:!1,input:u,inst:e});return}if(!Number.isSafeInteger(u)){u>0?i.issues.push({input:u,code:"too_big",maximum:Number.MAX_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:e,origin:r,continue:!n.abort}):i.issues.push({input:u,code:"too_small",minimum:Number.MIN_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:e,origin:r,continue:!n.abort});return}}u<o&&i.issues.push({origin:"number",input:u,code:"too_small",minimum:o,inclusive:!0,inst:e,continue:!n.abort}),u>c&&i.issues.push({origin:"number",input:u,code:"too_big",maximum:c,inst:e})}}),zt=s("$ZodCheckMaxLength",(e,n)=>{var t;A.init(e,n),(t=e._zod.def).when??(t.when=r=>{const o=r.value;return!pe(o)&&o.length!==void 0}),e._zod.onattach.push(r=>{const o=r._zod.bag.maximum??Number.POSITIVE_INFINITY;n.maximum<o&&(r._zod.bag.maximum=n.maximum)}),e._zod.check=r=>{const o=r.value;if(o.length<=n.maximum)return;const i=me(o);r.issues.push({origin:i,code:"too_big",maximum:n.maximum,inclusive:!0,input:o,inst:e,continue:!n.abort})}}),wt=s("$ZodCheckMinLength",(e,n)=>{var t;A.init(e,n),(t=e._zod.def).when??(t.when=r=>{const o=r.value;return!pe(o)&&o.length!==void 0}),e._zod.onattach.push(r=>{const o=r._zod.bag.minimum??Number.NEGATIVE_INFINITY;n.minimum>o&&(r._zod.bag.minimum=n.minimum)}),e._zod.check=r=>{const o=r.value;if(o.length>=n.minimum)return;const i=me(o);r.issues.push({origin:i,code:"too_small",minimum:n.minimum,inclusive:!0,input:o,inst:e,continue:!n.abort})}}),kt=s("$ZodCheckLengthEquals",(e,n)=>{var t;A.init(e,n),(t=e._zod.def).when??(t.when=r=>{const o=r.value;return!pe(o)&&o.length!==void 0}),e._zod.onattach.push(r=>{const o=r._zod.bag;o.minimum=n.length,o.maximum=n.length,o.length=n.length}),e._zod.check=r=>{const o=r.value,c=o.length;if(c===n.length)return;const i=me(o),u=c>n.length;r.issues.push({origin:i,...u?{code:"too_big",maximum:n.length}:{code:"too_small",minimum:n.length},inclusive:!0,exact:!0,input:r.value,inst:e,continue:!n.abort})}}),oe=s("$ZodCheckStringFormat",(e,n)=>{var t,r;A.init(e,n),e._zod.onattach.push(o=>{const c=o._zod.bag;c.format=n.format,n.pattern&&(c.patterns??(c.patterns=new Set),c.patterns.add(n.pattern))}),n.pattern?(t=e._zod).check??(t.check=o=>{n.pattern.lastIndex=0,!n.pattern.test(o.value)&&o.issues.push({origin:"string",code:"invalid_format",format:n.format,input:o.value,...n.pattern?{pattern:n.pattern.toString()}:{},inst:e,continue:!n.abort})}):(r=e._zod).check??(r.check=()=>{})}),Zt=s("$ZodCheckRegex",(e,n)=>{oe.init(e,n),e._zod.check=t=>{n.pattern.lastIndex=0,!n.pattern.test(t.value)&&t.issues.push({origin:"string",code:"invalid_format",format:"regex",input:t.value,pattern:n.pattern.toString(),inst:e,continue:!n.abort})}}),$t=s("$ZodCheckLowerCase",(e,n)=>{n.pattern??(n.pattern=_t),oe.init(e,n)}),yt=s("$ZodCheckUpperCase",(e,n)=>{n.pattern??(n.pattern=gt),oe.init(e,n)}),It=s("$ZodCheckIncludes",(e,n)=>{A.init(e,n);const t=ne(n.includes),r=new RegExp(typeof n.position=="number"?`^.{${n.position}}${t}`:t);n.pattern=r,e._zod.onattach.push(o=>{const c=o._zod.bag;c.patterns??(c.patterns=new Set),c.patterns.add(r)}),e._zod.check=o=>{o.value.includes(n.includes,n.position)||o.issues.push({origin:"string",code:"invalid_format",format:"includes",includes:n.includes,input:o.value,inst:e,continue:!n.abort})}}),Et=s("$ZodCheckStartsWith",(e,n)=>{A.init(e,n);const t=new RegExp(`^${ne(n.prefix)}.*`);n.pattern??(n.pattern=t),e._zod.onattach.push(r=>{const o=r._zod.bag;o.patterns??(o.patterns=new Set),o.patterns.add(t)}),e._zod.check=r=>{r.value.startsWith(n.prefix)||r.issues.push({origin:"string",code:"invalid_format",format:"starts_with",prefix:n.prefix,input:r.value,inst:e,continue:!n.abort})}}),At=s("$ZodCheckEndsWith",(e,n)=>{A.init(e,n);const t=new RegExp(`.*${ne(n.suffix)}$`);n.pattern??(n.pattern=t),e._zod.onattach.push(r=>{const o=r._zod.bag;o.patterns??(o.patterns=new Set),o.patterns.add(t)}),e._zod.check=r=>{r.value.endsWith(n.suffix)||r.issues.push({origin:"string",code:"invalid_format",format:"ends_with",suffix:n.suffix,input:r.value,inst:e,continue:!n.abort})}}),Pt=s("$ZodCheckOverwrite",(e,n)=>{A.init(e,n),e._zod.check=t=>{t.value=n.tx(t.value)}});class Tt{constructor(n=[]){this.content=[],this.indent=0,this&&(this.args=n)}indented(n){this.indent+=1,n(this),this.indent-=1}write(n){if(typeof n=="function"){n(this,{execution:"sync"}),n(this,{execution:"async"});return}const r=n.split(`
2
+ `).filter(i=>i),o=Math.min(...r.map(i=>i.length-i.trimStart().length)),c=r.map(i=>i.slice(o)).map(i=>" ".repeat(this.indent*2)+i);for(const i of c)this.content.push(i)}compile(){const n=Function,t=this?.args,o=[...(this?.content??[""]).map(c=>` ${c}`)];return new n(...t,o.join(`
3
+ `))}}const Ot={major:4,minor:1,patch:8},z=s("$ZodType",(e,n)=>{var t;e??(e={}),e._zod.def=n,e._zod.bag=e._zod.bag||{},e._zod.version=Ot;const r=[...e._zod.def.checks??[]];e._zod.traits.has("$ZodCheck")&&r.unshift(e);for(const o of r)for(const c of o._zod.onattach)c(e);if(r.length===0)(t=e._zod).deferred??(t.deferred=[]),e._zod.deferred?.push(()=>{e._zod.run=e._zod.parse});else{const o=(i,u,a)=>{let l=U(i),h;for(const w of u){if(w._zod.def.when){if(!w._zod.def.when(i))continue}else if(l)continue;const k=i.issues.length,v=w._zod.check(i);if(v instanceof Promise&&a?.async===!1)throw new L;if(h||v instanceof Promise)h=(h??Promise.resolve()).then(async()=>{await v,i.issues.length!==k&&(l||(l=U(i,k)))});else{if(i.issues.length===k)continue;l||(l=U(i,k))}}return h?h.then(()=>i):i},c=(i,u,a)=>{if(U(i))return i.aborted=!0,i;const l=o(u,r,a);if(l instanceof Promise){if(a.async===!1)throw new L;return l.then(h=>e._zod.parse(h,a))}return e._zod.parse(l,a)};e._zod.run=(i,u)=>{if(u.skipChecks)return e._zod.parse(i,u);if(u.direction==="backward"){const l=e._zod.parse({value:i.value,issues:[]},{...u,skipChecks:!0});return l instanceof Promise?l.then(h=>c(h,i,u)):c(l,i,u)}const a=e._zod.parse(i,u);if(a instanceof Promise){if(u.async===!1)throw new L;return a.then(l=>o(l,r,u))}return o(a,r,u)}}e["~standard"]={validate:o=>{try{const c=Dn(e,o);return c.success?{value:c.data}:{issues:c.error?.issues}}catch{return jn(e,o).then(i=>i.success?{value:i.data}:{issues:i.error?.issues})}},vendor:"zod",version:1}}),ve=s("$ZodString",(e,n)=>{z.init(e,n),e._zod.pattern=[...e?._zod.bag?.patterns??[]].pop()??ft(e._zod.bag),e._zod.parse=(t,r)=>{if(n.coerce)try{t.value=String(t.value)}catch{}return typeof t.value=="string"||t.issues.push({expected:"string",code:"invalid_type",input:t.value,inst:e}),t}}),_=s("$ZodStringFormat",(e,n)=>{oe.init(e,n),ve.init(e,n)}),St=s("$ZodGUID",(e,n)=>{n.pattern??(n.pattern=Xn),_.init(e,n)}),Ct=s("$ZodUUID",(e,n)=>{if(n.version){const r={v1:1,v2:2,v3:3,v4:4,v5:5,v6:6,v7:7,v8:8}[n.version];if(r===void 0)throw new Error(`Invalid UUID version: "${n.version}"`);n.pattern??(n.pattern=$e(r))}else n.pattern??(n.pattern=$e());_.init(e,n)}),Nt=s("$ZodEmail",(e,n)=>{n.pattern??(n.pattern=Qn),_.init(e,n)}),Dt=s("$ZodURL",(e,n)=>{_.init(e,n),e._zod.check=t=>{try{const r=t.value.trim(),o=new URL(r);n.hostname&&(n.hostname.lastIndex=0,n.hostname.test(o.hostname)||t.issues.push({code:"invalid_format",format:"url",note:"Invalid hostname",pattern:st.source,input:t.value,inst:e,continue:!n.abort})),n.protocol&&(n.protocol.lastIndex=0,n.protocol.test(o.protocol.endsWith(":")?o.protocol.slice(0,-1):o.protocol)||t.issues.push({code:"invalid_format",format:"url",note:"Invalid protocol",pattern:n.protocol.source,input:t.value,inst:e,continue:!n.abort})),n.normalize?t.value=o.href:t.value=r;return}catch{t.issues.push({code:"invalid_format",format:"url",input:t.value,inst:e,continue:!n.abort})}}}),jt=s("$ZodEmoji",(e,n)=>{n.pattern??(n.pattern=nt()),_.init(e,n)}),Rt=s("$ZodNanoID",(e,n)=>{n.pattern??(n.pattern=Hn),_.init(e,n)}),xt=s("$ZodCUID",(e,n)=>{n.pattern??(n.pattern=Wn),_.init(e,n)}),Ft=s("$ZodCUID2",(e,n)=>{n.pattern??(n.pattern=Gn),_.init(e,n)}),Ut=s("$ZodULID",(e,n)=>{n.pattern??(n.pattern=Jn),_.init(e,n)}),Lt=s("$ZodXID",(e,n)=>{n.pattern??(n.pattern=Kn),_.init(e,n)}),Mt=s("$ZodKSUID",(e,n)=>{n.pattern??(n.pattern=qn),_.init(e,n)}),Vt=s("$ZodISODateTime",(e,n)=>{n.pattern??(n.pattern=dt(n)),_.init(e,n)}),Bt=s("$ZodISODate",(e,n)=>{n.pattern??(n.pattern=at),_.init(e,n)}),Wt=s("$ZodISOTime",(e,n)=>{n.pattern??(n.pattern=lt(n)),_.init(e,n)}),Gt=s("$ZodISODuration",(e,n)=>{n.pattern??(n.pattern=Yn),_.init(e,n)}),Jt=s("$ZodIPv4",(e,n)=>{n.pattern??(n.pattern=tt),_.init(e,n),e._zod.onattach.push(t=>{const r=t._zod.bag;r.format="ipv4"})}),Kt=s("$ZodIPv6",(e,n)=>{n.pattern??(n.pattern=rt),_.init(e,n),e._zod.onattach.push(t=>{const r=t._zod.bag;r.format="ipv6"}),e._zod.check=t=>{try{new URL(`http://[${t.value}]`)}catch{t.issues.push({code:"invalid_format",format:"ipv6",input:t.value,inst:e,continue:!n.abort})}}}),qt=s("$ZodCIDRv4",(e,n)=>{n.pattern??(n.pattern=ot),_.init(e,n)}),Ht=s("$ZodCIDRv6",(e,n)=>{n.pattern??(n.pattern=it),_.init(e,n),e._zod.check=t=>{const r=t.value.split("/");try{if(r.length!==2)throw new Error;const[o,c]=r;if(!c)throw new Error;const i=Number(c);if(`${i}`!==c)throw new Error;if(i<0||i>128)throw new Error;new URL(`http://[${o}]`)}catch{t.issues.push({code:"invalid_format",format:"cidrv6",input:t.value,inst:e,continue:!n.abort})}}});function cn(e){if(e==="")return!0;if(e.length%4!==0)return!1;try{return atob(e),!0}catch{return!1}}const Yt=s("$ZodBase64",(e,n)=>{n.pattern??(n.pattern=ct),_.init(e,n),e._zod.onattach.push(t=>{t._zod.bag.contentEncoding="base64"}),e._zod.check=t=>{cn(t.value)||t.issues.push({code:"invalid_format",format:"base64",input:t.value,inst:e,continue:!n.abort})}});function Xt(e){if(!Qe.test(e))return!1;const n=e.replace(/[-_]/g,r=>r==="-"?"+":"/"),t=n.padEnd(Math.ceil(n.length/4)*4,"=");return cn(t)}const Qt=s("$ZodBase64URL",(e,n)=>{n.pattern??(n.pattern=Qe),_.init(e,n),e._zod.onattach.push(t=>{t._zod.bag.contentEncoding="base64url"}),e._zod.check=t=>{Xt(t.value)||t.issues.push({code:"invalid_format",format:"base64url",input:t.value,inst:e,continue:!n.abort})}}),er=s("$ZodE164",(e,n)=>{n.pattern??(n.pattern=ut),_.init(e,n)});function nr(e,n=null){try{const t=e.split(".");if(t.length!==3)return!1;const[r]=t;if(!r)return!1;const o=JSON.parse(atob(r));return!("typ"in o&&o?.typ!=="JWT"||!o.alg||n&&(!("alg"in o)||o.alg!==n))}catch{return!1}}const tr=s("$ZodJWT",(e,n)=>{_.init(e,n),e._zod.check=t=>{nr(t.value,n.alg)||t.issues.push({code:"invalid_format",format:"jwt",input:t.value,inst:e,continue:!n.abort})}}),sn=s("$ZodNumber",(e,n)=>{z.init(e,n),e._zod.pattern=e._zod.bag.pattern??ht,e._zod.parse=(t,r)=>{if(n.coerce)try{t.value=Number(t.value)}catch{}const o=t.value;if(typeof o=="number"&&!Number.isNaN(o)&&Number.isFinite(o))return t;const c=typeof o=="number"?Number.isNaN(o)?"NaN":Number.isFinite(o)?void 0:"Infinity":void 0;return t.issues.push({expected:"number",code:"invalid_type",input:o,inst:e,...c?{received:c}:{}}),t}}),rr=s("$ZodNumber",(e,n)=>{bt.init(e,n),sn.init(e,n)}),or=s("$ZodBoolean",(e,n)=>{z.init(e,n),e._zod.pattern=mt,e._zod.parse=(t,r)=>{if(n.coerce)try{t.value=!!t.value}catch{}const o=t.value;return typeof o=="boolean"||t.issues.push({expected:"boolean",code:"invalid_type",input:o,inst:e}),t}}),ir=s("$ZodUnknown",(e,n)=>{z.init(e,n),e._zod.parse=t=>t}),cr=s("$ZodNever",(e,n)=>{z.init(e,n),e._zod.parse=(t,r)=>(t.issues.push({expected:"never",code:"invalid_type",input:t.value,inst:e}),t)});function ye(e,n,t){e.issues.length&&n.issues.push(...qe(t,e.issues)),n.value[t]=e.value}const sr=s("$ZodArray",(e,n)=>{z.init(e,n),e._zod.parse=(t,r)=>{const o=t.value;if(!Array.isArray(o))return t.issues.push({expected:"array",code:"invalid_type",input:o,inst:e}),t;t.value=Array(o.length);const c=[];for(let i=0;i<o.length;i++){const u=o[i],a=n.element._zod.run({value:u,issues:[]},r);a instanceof Promise?c.push(a.then(l=>ye(l,t,i))):ye(a,t,i)}return c.length?Promise.all(c).then(()=>t):t}});function Q(e,n,t,r){e.issues.length&&n.issues.push(...qe(t,e.issues)),e.value===void 0?t in r&&(n.value[t]=void 0):n.value[t]=e.value}function un(e){const n=Object.keys(e.shape);for(const r of n)if(!e.shape?.[r]?._zod?.traits?.has("$ZodType"))throw new Error(`Invalid element at key "${r}": expected a Zod schema`);const t=$n(e.shape);return{...e,keys:n,keySet:new Set(n),numKeys:n.length,optionalKeys:new Set(t)}}function an(e,n,t,r,o,c){const i=[],u=o.keySet,a=o.catchall._zod,l=a.def.type;for(const h of Object.keys(n)){if(u.has(h))continue;if(l==="never"){i.push(h);continue}const w=a.run({value:n[h],issues:[]},r);w instanceof Promise?e.push(w.then(k=>Q(k,t,h,n))):Q(w,t,h,n)}return i.length&&t.issues.push({code:"unrecognized_keys",keys:i,input:n,inst:c}),e.length?Promise.all(e).then(()=>t):t}const ur=s("$ZodObject",(e,n)=>{z.init(e,n);const t=fe(()=>un(n));m(e._zod,"propValues",()=>{const i=n.shape,u={};for(const a in i){const l=i[a]._zod;if(l.values){u[a]??(u[a]=new Set);for(const h of l.values)u[a].add(h)}}return u});const r=X,o=n.catchall;let c;e._zod.parse=(i,u)=>{c??(c=t.value);const a=i.value;if(!r(a))return i.issues.push({expected:"object",code:"invalid_type",input:a,inst:e}),i;i.value={};const l=[],h=c.shape;for(const w of c.keys){const v=h[w]._zod.run({value:a[w],issues:[]},u);v instanceof Promise?l.push(v.then(f=>Q(f,i,w,a))):Q(v,i,w,a)}return o?an(l,a,i,u,t.value,e):l.length?Promise.all(l).then(()=>i):i}}),ar=s("$ZodObjectJIT",(e,n)=>{ur.init(e,n);const t=e._zod.parse,r=fe(()=>un(n)),o=k=>{const v=new Tt(["shape","payload","ctx"]),f=r.value,p=y=>{const I=Ze(y);return`shape[${I}]._zod.run({ value: input[${I}], issues: [] }, ctx)`};v.write("const input = payload.value;");const b=Object.create(null);let $=0;for(const y of f.keys)b[y]=`key_${$++}`;v.write("const newResult = {};");for(const y of f.keys){const I=b[y],O=Ze(y);v.write(`const ${I} = ${p(y)};`),v.write(`
4
+ if (${I}.issues.length) {
5
+ payload.issues = payload.issues.concat(${I}.issues.map(iss => ({
6
+ ...iss,
7
+ path: iss.path ? [${O}, ...iss.path] : [${O}]
8
+ })));
9
+ }
10
+
11
+
12
+ if (${I}.value === undefined) {
13
+ if (${O} in input) {
14
+ newResult[${O}] = undefined;
15
+ }
16
+ } else {
17
+ newResult[${O}] = ${I}.value;
18
+ }
19
+
20
+ `)}v.write("payload.value = newResult;"),v.write("return payload;");const T=v.compile();return(y,I)=>T(k,y,I)};let c;const i=X,u=!Ge.jitless,l=u&&kn.value,h=n.catchall;let w;e._zod.parse=(k,v)=>{w??(w=r.value);const f=k.value;return i(f)?u&&l&&v?.async===!1&&v.jitless!==!0?(c||(c=o(n.shape)),k=c(k,v),h?an([],f,k,v,w,e):k):t(k,v):(k.issues.push({expected:"object",code:"invalid_type",input:f,inst:e}),k)}});function Ie(e,n,t,r){for(const c of e)if(c.issues.length===0)return n.value=c.value,n;const o=e.filter(c=>!U(c));return o.length===1?(n.value=o[0].value,o[0]):(n.issues.push({code:"invalid_union",input:n.value,inst:t,errors:e.map(c=>c.issues.map(i=>R(i,r,j())))}),n)}const lr=s("$ZodUnion",(e,n)=>{z.init(e,n),m(e._zod,"optin",()=>n.options.some(o=>o._zod.optin==="optional")?"optional":void 0),m(e._zod,"optout",()=>n.options.some(o=>o._zod.optout==="optional")?"optional":void 0),m(e._zod,"values",()=>{if(n.options.every(o=>o._zod.values))return new Set(n.options.flatMap(o=>Array.from(o._zod.values)))}),m(e._zod,"pattern",()=>{if(n.options.every(o=>o._zod.pattern)){const o=n.options.map(c=>c._zod.pattern);return new RegExp(`^(${o.map(c=>he(c.source)).join("|")})$`)}});const t=n.options.length===1,r=n.options[0]._zod.run;e._zod.parse=(o,c)=>{if(t)return r(o,c);let i=!1;const u=[];for(const a of n.options){const l=a._zod.run({value:o.value,issues:[]},c);if(l instanceof Promise)u.push(l),i=!0;else{if(l.issues.length===0)return l;u.push(l)}}return i?Promise.all(u).then(a=>Ie(a,o,e,c)):Ie(u,o,e,c)}}),dr=s("$ZodIntersection",(e,n)=>{z.init(e,n),e._zod.parse=(t,r)=>{const o=t.value,c=n.left._zod.run({value:o,issues:[]},r),i=n.right._zod.run({value:o,issues:[]},r);return c instanceof Promise||i instanceof Promise?Promise.all([c,i]).then(([a,l])=>Ee(t,a,l)):Ee(t,c,i)}});function le(e,n){if(e===n)return{valid:!0,data:e};if(e instanceof Date&&n instanceof Date&&+e==+n)return{valid:!0,data:e};if(W(e)&&W(n)){const t=Object.keys(n),r=Object.keys(e).filter(c=>t.indexOf(c)!==-1),o={...e,...n};for(const c of r){const i=le(e[c],n[c]);if(!i.valid)return{valid:!1,mergeErrorPath:[c,...i.mergeErrorPath]};o[c]=i.data}return{valid:!0,data:o}}if(Array.isArray(e)&&Array.isArray(n)){if(e.length!==n.length)return{valid:!1,mergeErrorPath:[]};const t=[];for(let r=0;r<e.length;r++){const o=e[r],c=n[r],i=le(o,c);if(!i.valid)return{valid:!1,mergeErrorPath:[r,...i.mergeErrorPath]};t.push(i.data)}return{valid:!0,data:t}}return{valid:!1,mergeErrorPath:[]}}function Ee(e,n,t){if(n.issues.length&&e.issues.push(...n.issues),t.issues.length&&e.issues.push(...t.issues),U(e))return e;const r=le(n.value,t.value);if(!r.valid)throw new Error(`Unmergable intersection. Error path: ${JSON.stringify(r.mergeErrorPath)}`);return e.value=r.data,e}const fr=s("$ZodEnum",(e,n)=>{z.init(e,n);const t=bn(n.entries),r=new Set(t);e._zod.values=r,e._zod.pattern=new RegExp(`^(${t.filter(o=>Zn.has(typeof o)).map(o=>typeof o=="string"?ne(o):o.toString()).join("|")})$`),e._zod.parse=(o,c)=>{const i=o.value;return r.has(i)||o.issues.push({code:"invalid_value",values:t,input:i,inst:e}),o}}),pr=s("$ZodTransform",(e,n)=>{z.init(e,n),e._zod.parse=(t,r)=>{if(r.direction==="backward")throw new We(e.constructor.name);const o=n.transform(t.value,t);if(r.async)return(o instanceof Promise?o:Promise.resolve(o)).then(i=>(t.value=i,t));if(o instanceof Promise)throw new L;return t.value=o,t}});function Ae(e,n){return e.issues.length&&n===void 0?{issues:[],value:void 0}:e}const hr=s("$ZodOptional",(e,n)=>{z.init(e,n),e._zod.optin="optional",e._zod.optout="optional",m(e._zod,"values",()=>n.innerType._zod.values?new Set([...n.innerType._zod.values,void 0]):void 0),m(e._zod,"pattern",()=>{const t=n.innerType._zod.pattern;return t?new RegExp(`^(${he(t.source)})?$`):void 0}),e._zod.parse=(t,r)=>{if(n.innerType._zod.optin==="optional"){const o=n.innerType._zod.run(t,r);return o instanceof Promise?o.then(c=>Ae(c,t.value)):Ae(o,t.value)}return t.value===void 0?t:n.innerType._zod.run(t,r)}}),mr=s("$ZodNullable",(e,n)=>{z.init(e,n),m(e._zod,"optin",()=>n.innerType._zod.optin),m(e._zod,"optout",()=>n.innerType._zod.optout),m(e._zod,"pattern",()=>{const t=n.innerType._zod.pattern;return t?new RegExp(`^(${he(t.source)}|null)$`):void 0}),m(e._zod,"values",()=>n.innerType._zod.values?new Set([...n.innerType._zod.values,null]):void 0),e._zod.parse=(t,r)=>t.value===null?t:n.innerType._zod.run(t,r)}),_r=s("$ZodDefault",(e,n)=>{z.init(e,n),e._zod.optin="optional",m(e._zod,"values",()=>n.innerType._zod.values),e._zod.parse=(t,r)=>{if(r.direction==="backward")return n.innerType._zod.run(t,r);if(t.value===void 0)return t.value=n.defaultValue,t;const o=n.innerType._zod.run(t,r);return o instanceof Promise?o.then(c=>Pe(c,n)):Pe(o,n)}});function Pe(e,n){return e.value===void 0&&(e.value=n.defaultValue),e}const gr=s("$ZodPrefault",(e,n)=>{z.init(e,n),e._zod.optin="optional",m(e._zod,"values",()=>n.innerType._zod.values),e._zod.parse=(t,r)=>(r.direction==="backward"||t.value===void 0&&(t.value=n.defaultValue),n.innerType._zod.run(t,r))}),vr=s("$ZodNonOptional",(e,n)=>{z.init(e,n),m(e._zod,"values",()=>{const t=n.innerType._zod.values;return t?new Set([...t].filter(r=>r!==void 0)):void 0}),e._zod.parse=(t,r)=>{const o=n.innerType._zod.run(t,r);return o instanceof Promise?o.then(c=>Te(c,e)):Te(o,e)}});function Te(e,n){return!e.issues.length&&e.value===void 0&&e.issues.push({code:"invalid_type",expected:"nonoptional",input:e.value,inst:n}),e}const br=s("$ZodCatch",(e,n)=>{z.init(e,n),m(e._zod,"optin",()=>n.innerType._zod.optin),m(e._zod,"optout",()=>n.innerType._zod.optout),m(e._zod,"values",()=>n.innerType._zod.values),e._zod.parse=(t,r)=>{if(r.direction==="backward")return n.innerType._zod.run(t,r);const o=n.innerType._zod.run(t,r);return o instanceof Promise?o.then(c=>(t.value=c.value,c.issues.length&&(t.value=n.catchValue({...t,error:{issues:c.issues.map(i=>R(i,r,j()))},input:t.value}),t.issues=[]),t)):(t.value=o.value,o.issues.length&&(t.value=n.catchValue({...t,error:{issues:o.issues.map(c=>R(c,r,j()))},input:t.value}),t.issues=[]),t)}}),zr=s("$ZodPipe",(e,n)=>{z.init(e,n),m(e._zod,"values",()=>n.in._zod.values),m(e._zod,"optin",()=>n.in._zod.optin),m(e._zod,"optout",()=>n.out._zod.optout),m(e._zod,"propValues",()=>n.in._zod.propValues),e._zod.parse=(t,r)=>{if(r.direction==="backward"){const c=n.out._zod.run(t,r);return c instanceof Promise?c.then(i=>q(i,n.in,r)):q(c,n.in,r)}const o=n.in._zod.run(t,r);return o instanceof Promise?o.then(c=>q(c,n.out,r)):q(o,n.out,r)}});function q(e,n,t){return e.issues.length?(e.aborted=!0,e):n._zod.run({value:e.value,issues:e.issues},t)}const wr=s("$ZodReadonly",(e,n)=>{z.init(e,n),m(e._zod,"propValues",()=>n.innerType._zod.propValues),m(e._zod,"values",()=>n.innerType._zod.values),m(e._zod,"optin",()=>n.innerType._zod.optin),m(e._zod,"optout",()=>n.innerType._zod.optout),e._zod.parse=(t,r)=>{if(r.direction==="backward")return n.innerType._zod.run(t,r);const o=n.innerType._zod.run(t,r);return o instanceof Promise?o.then(Oe):Oe(o)}});function Oe(e){return e.value=Object.freeze(e.value),e}const kr=s("$ZodCustom",(e,n)=>{A.init(e,n),z.init(e,n),e._zod.parse=(t,r)=>t,e._zod.check=t=>{const r=t.value,o=n.fn(r);if(o instanceof Promise)return o.then(c=>Se(c,t,r,e));Se(o,t,r,e)}});function Se(e,n,t,r){if(!e){const o={code:"custom",input:t,inst:r,path:[...r._zod.def.path??[]],continue:!r._zod.def.abort};r._zod.def.params&&(o.params=r._zod.def.params),n.issues.push(G(o))}}class Zr{constructor(){this._map=new WeakMap,this._idmap=new Map}add(n,...t){const r=t[0];if(this._map.set(n,r),r&&typeof r=="object"&&"id"in r){if(this._idmap.has(r.id))throw new Error(`ID ${r.id} already exists in the registry`);this._idmap.set(r.id,n)}return this}clear(){return this._map=new WeakMap,this._idmap=new Map,this}remove(n){const t=this._map.get(n);return t&&typeof t=="object"&&"id"in t&&this._idmap.delete(t.id),this._map.delete(n),this}get(n){const t=n._zod.parent;if(t){const r={...this.get(t)??{}};delete r.id;const o={...r,...this._map.get(n)};return Object.keys(o).length?o:void 0}return this._map.get(n)}has(n){return this._map.has(n)}}function $r(){return new Zr}const H=$r();function yr(e,n){return new e({type:"string",...d(n)})}function Ir(e,n){return new e({type:"string",format:"email",check:"string_format",abort:!1,...d(n)})}function Ce(e,n){return new e({type:"string",format:"guid",check:"string_format",abort:!1,...d(n)})}function Er(e,n){return new e({type:"string",format:"uuid",check:"string_format",abort:!1,...d(n)})}function Ar(e,n){return new e({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v4",...d(n)})}function Pr(e,n){return new e({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v6",...d(n)})}function Tr(e,n){return new e({type:"string",format:"uuid",check:"string_format",abort:!1,version:"v7",...d(n)})}function Or(e,n){return new e({type:"string",format:"url",check:"string_format",abort:!1,...d(n)})}function Sr(e,n){return new e({type:"string",format:"emoji",check:"string_format",abort:!1,...d(n)})}function Cr(e,n){return new e({type:"string",format:"nanoid",check:"string_format",abort:!1,...d(n)})}function Nr(e,n){return new e({type:"string",format:"cuid",check:"string_format",abort:!1,...d(n)})}function Dr(e,n){return new e({type:"string",format:"cuid2",check:"string_format",abort:!1,...d(n)})}function jr(e,n){return new e({type:"string",format:"ulid",check:"string_format",abort:!1,...d(n)})}function Rr(e,n){return new e({type:"string",format:"xid",check:"string_format",abort:!1,...d(n)})}function xr(e,n){return new e({type:"string",format:"ksuid",check:"string_format",abort:!1,...d(n)})}function Fr(e,n){return new e({type:"string",format:"ipv4",check:"string_format",abort:!1,...d(n)})}function Ur(e,n){return new e({type:"string",format:"ipv6",check:"string_format",abort:!1,...d(n)})}function Lr(e,n){return new e({type:"string",format:"cidrv4",check:"string_format",abort:!1,...d(n)})}function Mr(e,n){return new e({type:"string",format:"cidrv6",check:"string_format",abort:!1,...d(n)})}function Vr(e,n){return new e({type:"string",format:"base64",check:"string_format",abort:!1,...d(n)})}function Br(e,n){return new e({type:"string",format:"base64url",check:"string_format",abort:!1,...d(n)})}function Wr(e,n){return new e({type:"string",format:"e164",check:"string_format",abort:!1,...d(n)})}function Gr(e,n){return new e({type:"string",format:"jwt",check:"string_format",abort:!1,...d(n)})}function Jr(e,n){return new e({type:"string",format:"datetime",check:"string_format",offset:!1,local:!1,precision:null,...d(n)})}function Kr(e,n){return new e({type:"string",format:"date",check:"string_format",...d(n)})}function qr(e,n){return new e({type:"string",format:"time",check:"string_format",precision:null,...d(n)})}function Hr(e,n){return new e({type:"string",format:"duration",check:"string_format",...d(n)})}function Yr(e,n){return new e({type:"number",checks:[],...d(n)})}function Xr(e,n){return new e({type:"number",check:"number_format",abort:!1,format:"safeint",...d(n)})}function Qr(e,n){return new e({type:"boolean",...d(n)})}function eo(e){return new e({type:"unknown"})}function no(e,n){return new e({type:"never",...d(n)})}function Ne(e,n){return new rn({check:"less_than",...d(n),value:e,inclusive:!1})}function se(e,n){return new rn({check:"less_than",...d(n),value:e,inclusive:!0})}function De(e,n){return new on({check:"greater_than",...d(n),value:e,inclusive:!1})}function ue(e,n){return new on({check:"greater_than",...d(n),value:e,inclusive:!0})}function je(e,n){return new vt({check:"multiple_of",...d(n),value:e})}function ln(e,n){return new zt({check:"max_length",...d(n),maximum:e})}function ee(e,n){return new wt({check:"min_length",...d(n),minimum:e})}function dn(e,n){return new kt({check:"length_equals",...d(n),length:e})}function to(e,n){return new Zt({check:"string_format",format:"regex",...d(n),pattern:e})}function ro(e){return new $t({check:"string_format",format:"lowercase",...d(e)})}function oo(e){return new yt({check:"string_format",format:"uppercase",...d(e)})}function io(e,n){return new It({check:"string_format",format:"includes",...d(n),includes:e})}function co(e,n){return new Et({check:"string_format",format:"starts_with",...d(n),prefix:e})}function so(e,n){return new At({check:"string_format",format:"ends_with",...d(n),suffix:e})}function J(e){return new Pt({check:"overwrite",tx:e})}function uo(e){return J(n=>n.normalize(e))}function ao(){return J(e=>e.trim())}function lo(){return J(e=>e.toLowerCase())}function fo(){return J(e=>e.toUpperCase())}function po(e,n,t){return new e({type:"array",element:n,...d(t)})}function ho(e,n,t){return new e({type:"custom",check:"custom",fn:n,...d(t)})}function mo(e){const n=_o(t=>(t.addIssue=r=>{if(typeof r=="string")t.issues.push(G(r,t.value,n._zod.def));else{const o=r;o.fatal&&(o.continue=!1),o.code??(o.code="custom"),o.input??(o.input=t.value),o.inst??(o.inst=n),o.continue??(o.continue=!n._zod.def.abort),t.issues.push(G(o))}},e(t.value,t)));return n}function _o(e,n){const t=new A({check:"custom",...d(n)});return t._zod.check=e,t}const go=s("ZodISODateTime",(e,n)=>{Vt.init(e,n),g.init(e,n)});function vo(e){return Jr(go,e)}const bo=s("ZodISODate",(e,n)=>{Bt.init(e,n),g.init(e,n)});function zo(e){return Kr(bo,e)}const wo=s("ZodISOTime",(e,n)=>{Wt.init(e,n),g.init(e,n)});function ko(e){return qr(wo,e)}const Zo=s("ZodISODuration",(e,n)=>{Gt.init(e,n),g.init(e,n)});function $o(e){return Hr(Zo,e)}const yo=(e,n)=>{Ye.init(e,n),e.name="ZodError",Object.defineProperties(e,{format:{value:t=>Nn(e,t)},flatten:{value:t=>Cn(e,t)},addIssue:{value:t=>{e.issues.push(t),e.message=JSON.stringify(e.issues,ae,2)}},addIssues:{value:t=>{e.issues.push(...t),e.message=JSON.stringify(e.issues,ae,2)}},isEmpty:{get(){return e.issues.length===0}}})},P=s("ZodError",yo,{Parent:Error}),Io=_e(P),Eo=ge(P),Ao=te(P),Po=re(P),To=Rn(P),Oo=xn(P),So=Fn(P),Co=Un(P),No=Ln(P),Do=Mn(P),jo=Vn(P),Ro=Bn(P),Z=s("ZodType",(e,n)=>(z.init(e,n),e.def=n,e.type=n.type,Object.defineProperty(e,"_def",{value:n}),e.check=(...t)=>e.clone({...n,checks:[...n.checks??[],...t.map(r=>typeof r=="function"?{_zod:{check:r,def:{check:"custom"},onattach:[]}}:r)]}),e.clone=(t,r)=>D(e,t,r),e.brand=()=>e,e.register=((t,r)=>(t.add(e,r),e)),e.parse=(t,r)=>Io(e,t,r,{callee:e.parse}),e.safeParse=(t,r)=>Ao(e,t,r),e.parseAsync=async(t,r)=>Eo(e,t,r,{callee:e.parseAsync}),e.safeParseAsync=async(t,r)=>Po(e,t,r),e.spa=e.safeParseAsync,e.encode=(t,r)=>To(e,t,r),e.decode=(t,r)=>Oo(e,t,r),e.encodeAsync=async(t,r)=>So(e,t,r),e.decodeAsync=async(t,r)=>Co(e,t,r),e.safeEncode=(t,r)=>No(e,t,r),e.safeDecode=(t,r)=>Do(e,t,r),e.safeEncodeAsync=async(t,r)=>jo(e,t,r),e.safeDecodeAsync=async(t,r)=>Ro(e,t,r),e.refine=(t,r)=>e.check(Ti(t,r)),e.superRefine=t=>e.check(Oi(t)),e.overwrite=t=>e.check(J(t)),e.optional=()=>Ue(e),e.nullable=()=>Le(e),e.nullish=()=>Ue(Le(e)),e.nonoptional=t=>Zi(e,t),e.array=()=>be(e),e.or=t=>fi([e,t]),e.and=t=>hi(e,t),e.transform=t=>Me(e,gi(t)),e.default=t=>zi(e,t),e.prefault=t=>ki(e,t),e.catch=t=>yi(e,t),e.pipe=t=>Me(e,t),e.readonly=()=>Ai(e),e.describe=t=>{const r=e.clone();return H.add(r,{description:t}),r},Object.defineProperty(e,"description",{get(){return H.get(e)?.description},configurable:!0}),e.meta=(...t)=>{if(t.length===0)return H.get(e);const r=e.clone();return H.add(r,t[0]),r},e.isOptional=()=>e.safeParse(void 0).success,e.isNullable=()=>e.safeParse(null).success,e)),fn=s("_ZodString",(e,n)=>{ve.init(e,n),Z.init(e,n);const t=e._zod.bag;e.format=t.format??null,e.minLength=t.minimum??null,e.maxLength=t.maximum??null,e.regex=(...r)=>e.check(to(...r)),e.includes=(...r)=>e.check(io(...r)),e.startsWith=(...r)=>e.check(co(...r)),e.endsWith=(...r)=>e.check(so(...r)),e.min=(...r)=>e.check(ee(...r)),e.max=(...r)=>e.check(ln(...r)),e.length=(...r)=>e.check(dn(...r)),e.nonempty=(...r)=>e.check(ee(1,...r)),e.lowercase=r=>e.check(ro(r)),e.uppercase=r=>e.check(oo(r)),e.trim=()=>e.check(ao()),e.normalize=(...r)=>e.check(uo(...r)),e.toLowerCase=()=>e.check(lo()),e.toUpperCase=()=>e.check(fo())}),xo=s("ZodString",(e,n)=>{ve.init(e,n),fn.init(e,n),e.email=t=>e.check(Ir(Fo,t)),e.url=t=>e.check(Or(Uo,t)),e.jwt=t=>e.check(Gr(ni,t)),e.emoji=t=>e.check(Sr(Lo,t)),e.guid=t=>e.check(Ce(Re,t)),e.uuid=t=>e.check(Er(Y,t)),e.uuidv4=t=>e.check(Ar(Y,t)),e.uuidv6=t=>e.check(Pr(Y,t)),e.uuidv7=t=>e.check(Tr(Y,t)),e.nanoid=t=>e.check(Cr(Mo,t)),e.guid=t=>e.check(Ce(Re,t)),e.cuid=t=>e.check(Nr(Vo,t)),e.cuid2=t=>e.check(Dr(Bo,t)),e.ulid=t=>e.check(jr(Wo,t)),e.base64=t=>e.check(Vr(Xo,t)),e.base64url=t=>e.check(Br(Qo,t)),e.xid=t=>e.check(Rr(Go,t)),e.ksuid=t=>e.check(xr(Jo,t)),e.ipv4=t=>e.check(Fr(Ko,t)),e.ipv6=t=>e.check(Ur(qo,t)),e.cidrv4=t=>e.check(Lr(Ho,t)),e.cidrv6=t=>e.check(Mr(Yo,t)),e.e164=t=>e.check(Wr(ei,t)),e.datetime=t=>e.check(vo(t)),e.date=t=>e.check(zo(t)),e.time=t=>e.check(ko(t)),e.duration=t=>e.check($o(t))});function x(e){return yr(xo,e)}const g=s("ZodStringFormat",(e,n)=>{_.init(e,n),fn.init(e,n)}),Fo=s("ZodEmail",(e,n)=>{Nt.init(e,n),g.init(e,n)}),Re=s("ZodGUID",(e,n)=>{St.init(e,n),g.init(e,n)}),Y=s("ZodUUID",(e,n)=>{Ct.init(e,n),g.init(e,n)}),Uo=s("ZodURL",(e,n)=>{Dt.init(e,n),g.init(e,n)}),Lo=s("ZodEmoji",(e,n)=>{jt.init(e,n),g.init(e,n)}),Mo=s("ZodNanoID",(e,n)=>{Rt.init(e,n),g.init(e,n)}),Vo=s("ZodCUID",(e,n)=>{xt.init(e,n),g.init(e,n)}),Bo=s("ZodCUID2",(e,n)=>{Ft.init(e,n),g.init(e,n)}),Wo=s("ZodULID",(e,n)=>{Ut.init(e,n),g.init(e,n)}),Go=s("ZodXID",(e,n)=>{Lt.init(e,n),g.init(e,n)}),Jo=s("ZodKSUID",(e,n)=>{Mt.init(e,n),g.init(e,n)}),Ko=s("ZodIPv4",(e,n)=>{Jt.init(e,n),g.init(e,n)}),qo=s("ZodIPv6",(e,n)=>{Kt.init(e,n),g.init(e,n)}),Ho=s("ZodCIDRv4",(e,n)=>{qt.init(e,n),g.init(e,n)}),Yo=s("ZodCIDRv6",(e,n)=>{Ht.init(e,n),g.init(e,n)}),Xo=s("ZodBase64",(e,n)=>{Yt.init(e,n),g.init(e,n)}),Qo=s("ZodBase64URL",(e,n)=>{Qt.init(e,n),g.init(e,n)}),ei=s("ZodE164",(e,n)=>{er.init(e,n),g.init(e,n)}),ni=s("ZodJWT",(e,n)=>{tr.init(e,n),g.init(e,n)}),pn=s("ZodNumber",(e,n)=>{sn.init(e,n),Z.init(e,n),e.gt=(r,o)=>e.check(De(r,o)),e.gte=(r,o)=>e.check(ue(r,o)),e.min=(r,o)=>e.check(ue(r,o)),e.lt=(r,o)=>e.check(Ne(r,o)),e.lte=(r,o)=>e.check(se(r,o)),e.max=(r,o)=>e.check(se(r,o)),e.int=r=>e.check(xe(r)),e.safe=r=>e.check(xe(r)),e.positive=r=>e.check(De(0,r)),e.nonnegative=r=>e.check(ue(0,r)),e.negative=r=>e.check(Ne(0,r)),e.nonpositive=r=>e.check(se(0,r)),e.multipleOf=(r,o)=>e.check(je(r,o)),e.step=(r,o)=>e.check(je(r,o)),e.finite=()=>e;const t=e._zod.bag;e.minValue=Math.max(t.minimum??Number.NEGATIVE_INFINITY,t.exclusiveMinimum??Number.NEGATIVE_INFINITY)??null,e.maxValue=Math.min(t.maximum??Number.POSITIVE_INFINITY,t.exclusiveMaximum??Number.POSITIVE_INFINITY)??null,e.isInt=(t.format??"").includes("int")||Number.isSafeInteger(t.multipleOf??.5),e.isFinite=!0,e.format=t.format??null});function ti(e){return Yr(pn,e)}const ri=s("ZodNumberFormat",(e,n)=>{rr.init(e,n),pn.init(e,n)});function xe(e){return Xr(ri,e)}const oi=s("ZodBoolean",(e,n)=>{or.init(e,n),Z.init(e,n)});function ii(e){return Qr(oi,e)}const ci=s("ZodUnknown",(e,n)=>{ir.init(e,n),Z.init(e,n)});function Fe(){return eo(ci)}const si=s("ZodNever",(e,n)=>{cr.init(e,n),Z.init(e,n)});function ui(e){return no(si,e)}const ai=s("ZodArray",(e,n)=>{sr.init(e,n),Z.init(e,n),e.element=n.element,e.min=(t,r)=>e.check(ee(t,r)),e.nonempty=t=>e.check(ee(1,t)),e.max=(t,r)=>e.check(ln(t,r)),e.length=(t,r)=>e.check(dn(t,r)),e.unwrap=()=>e.element});function be(e,n){return po(ai,e,n)}const li=s("ZodObject",(e,n)=>{ar.init(e,n),Z.init(e,n),m(e,"shape",()=>n.shape),e.keyof=()=>mi(Object.keys(e._zod.def.shape)),e.catchall=t=>e.clone({...e._zod.def,catchall:t}),e.passthrough=()=>e.clone({...e._zod.def,catchall:Fe()}),e.loose=()=>e.clone({...e._zod.def,catchall:Fe()}),e.strict=()=>e.clone({...e._zod.def,catchall:ui()}),e.strip=()=>e.clone({...e._zod.def,catchall:void 0}),e.extend=t=>An(e,t),e.safeExtend=t=>Pn(e,t),e.merge=t=>Tn(e,t),e.pick=t=>In(e,t),e.omit=t=>En(e,t),e.partial=(...t)=>On(hn,e,t[0]),e.required=(...t)=>Sn(mn,e,t[0])});function M(e,n){const t={type:"object",get shape(){return N(this,"shape",e?wn(e):{}),this.shape},...d(n)};return new li(t)}const di=s("ZodUnion",(e,n)=>{lr.init(e,n),Z.init(e,n),e.options=n.options});function fi(e,n){return new di({type:"union",options:e,...d(n)})}const pi=s("ZodIntersection",(e,n)=>{dr.init(e,n),Z.init(e,n)});function hi(e,n){return new pi({type:"intersection",left:e,right:n})}const de=s("ZodEnum",(e,n)=>{fr.init(e,n),Z.init(e,n),e.enum=n.entries,e.options=Object.values(n.entries);const t=new Set(Object.keys(n.entries));e.extract=(r,o)=>{const c={};for(const i of r)if(t.has(i))c[i]=n.entries[i];else throw new Error(`Key ${i} not found in enum`);return new de({...n,checks:[],...d(o),entries:c})},e.exclude=(r,o)=>{const c={...n.entries};for(const i of r)if(t.has(i))delete c[i];else throw new Error(`Key ${i} not found in enum`);return new de({...n,checks:[],...d(o),entries:c})}});function mi(e,n){const t=Array.isArray(e)?Object.fromEntries(e.map(r=>[r,r])):e;return new de({type:"enum",entries:t,...d(n)})}const _i=s("ZodTransform",(e,n)=>{pr.init(e,n),Z.init(e,n),e._zod.parse=(t,r)=>{if(r.direction==="backward")throw new We(e.constructor.name);t.addIssue=c=>{if(typeof c=="string")t.issues.push(G(c,t.value,n));else{const i=c;i.fatal&&(i.continue=!1),i.code??(i.code="custom"),i.input??(i.input=t.value),i.inst??(i.inst=e),t.issues.push(G(i))}};const o=n.transform(t.value,t);return o instanceof Promise?o.then(c=>(t.value=c,t)):(t.value=o,t)}});function gi(e){return new _i({type:"transform",transform:e})}const hn=s("ZodOptional",(e,n)=>{hr.init(e,n),Z.init(e,n),e.unwrap=()=>e._zod.def.innerType});function Ue(e){return new hn({type:"optional",innerType:e})}const vi=s("ZodNullable",(e,n)=>{mr.init(e,n),Z.init(e,n),e.unwrap=()=>e._zod.def.innerType});function Le(e){return new vi({type:"nullable",innerType:e})}const bi=s("ZodDefault",(e,n)=>{_r.init(e,n),Z.init(e,n),e.unwrap=()=>e._zod.def.innerType,e.removeDefault=e.unwrap});function zi(e,n){return new bi({type:"default",innerType:e,get defaultValue(){return typeof n=="function"?n():Ke(n)}})}const wi=s("ZodPrefault",(e,n)=>{gr.init(e,n),Z.init(e,n),e.unwrap=()=>e._zod.def.innerType});function ki(e,n){return new wi({type:"prefault",innerType:e,get defaultValue(){return typeof n=="function"?n():Ke(n)}})}const mn=s("ZodNonOptional",(e,n)=>{vr.init(e,n),Z.init(e,n),e.unwrap=()=>e._zod.def.innerType});function Zi(e,n){return new mn({type:"nonoptional",innerType:e,...d(n)})}const $i=s("ZodCatch",(e,n)=>{br.init(e,n),Z.init(e,n),e.unwrap=()=>e._zod.def.innerType,e.removeCatch=e.unwrap});function yi(e,n){return new $i({type:"catch",innerType:e,catchValue:typeof n=="function"?n:()=>n})}const Ii=s("ZodPipe",(e,n)=>{zr.init(e,n),Z.init(e,n),e.in=n.in,e.out=n.out});function Me(e,n){return new Ii({type:"pipe",in:e,out:n})}const Ei=s("ZodReadonly",(e,n)=>{wr.init(e,n),Z.init(e,n),e.unwrap=()=>e._zod.def.innerType});function Ai(e){return new Ei({type:"readonly",innerType:e})}const Pi=s("ZodCustom",(e,n)=>{kr.init(e,n),Z.init(e,n)});function Ti(e,n={}){return ho(Pi,e,n)}function Oi(e){return mo(e)}const Si=M({label:x().min(1).max(80).describe('A short name for the credential, such as "GitHub token" or "Stripe API key". The user sees it when asked.')}).strict(),_n=Si.extend({id:x(),sessionID:x()}),Ve=M({sessionID:x().min(1)}).strict(),Ci=M({sessionIDs:be(x()),added:_n.optional()}),Be=vn.Rpc.define({id:"oc-hush",events:{changed:{schema:Ci}},methods:{pending:{input:Ve,output:be(_n)},reply:{input:M({id:x(),secret:x().min(1).max(8192).regex(/^[\x21-\x7e]+$/).optional()}).strict(),output:M({accepted:ii()})},forget:{input:Ve,output:M({forgotten:ti()})}}});var Ni=E.template("<style>.oc-hush-field[data-component]{width:100%}"),Di=E.template("<div style=display:flex;flex-direction:column;gap:12px;padding-bottom:16px><span>"),ji=E.template("<span>Credentials cannot contain spaces or line breaks.");const Ri=B.Plugin.define({setup(e){const[n,t]=we.createStore({}),r=new Set,o=new Set,c=f=>{const p="directory"in f&&typeof f.directory=="string"?f.directory:f.location?.directory;return p?{server:f.server,directory:p}:void 0},i=f=>f.server.client.rpc(Be),u=f=>({location:{directory:f.directory},signal:e.signal}),a=()=>e.toast.show({title:"Could not reach Hush",description:"Check that its server plugin is loaded.",variant:"error"}),l=async f=>{const p=c(f);if(p)try{const b=await i(p).pending({sessionID:f.id},u(p));if(e.signal.aborted)return;t(f.key,we.reconcile(b));const $=b.find(T=>!o.has(T.id));$&&!r.size&&e.sessions.current()?.key===f.key&&(o.add($.id),w(p,$))}catch{e.signal.aborted||t(f.key,[])}},h=f=>{const[p,b]=F.createSignal(""),[$,T]=F.createSignal(!1),y=()=>!/^[\x21-\x7e]*$/.test(p().trim())||p().length>8192;F.onCleanup(()=>{b(""),r.delete(f.item.id)});const I=async C=>{T(!0);try{(await i(f.at).reply({id:f.item.id,secret:C},u(f.at))).accepted||e.toast.show({title:"This credential request has expired."}),f.close()}catch{e.signal.aborted||a(),T(!1)}},O=()=>{const C=p().trim();C&&!y()&&!$()&&I(C)},S={opacity:.7};return E.createComponent(e.ui.Dialog,{title:"Temporary Credential",get footer(){return[E.createComponent(e.ui.Button,{variant:"ghost",get disabled(){return $()},onClick:()=>{I()},children:"Deny"}),E.createComponent(e.ui.Button,{variant:"contrast",get busy(){return $()},get disabled(){return!p().trim()||y()},onClick:O,children:"Approve"})]},get children(){return[Ni(),(()=>{var C=Di(),ie=C.firstChild;return E.insert(ie,()=>f.item.label),E.insert(C,E.createComponent(e.ui.TextInput,{class:"oc-hush-field",type:"password",size:"large",label:"Credential",placeholder:"Credential",get value(){return p()},onInput:b,onSubmit:O,get invalid(){return y()},autofocus:!0}),null),E.insert(C,(()=>{var ce=E.memo(()=>!!y());return()=>ce()&&(()=>{var ze=ji();return E.effect(gn=>E.style(ze,S,gn)),ze})()})(),null),E.effect(ce=>E.style(ie,S,ce)),C})()]}})},w=(f,p)=>{r.has(p.id)||(r.add(p.id),e.dialogs.open(b=>E.createComponent(h,{at:f,item:p,get close(){return b.close}})))},k=(f,p,b)=>{const $=e.sessions.list().filter(S=>S.server.id===f.id&&p.sessionIDs.includes(S.id));$.forEach(S=>{l(S)});const T=p.added;if(!T)return;const y="Hush needs a credential",I=T.label,O=p.sessionIDs[p.sessionIDs.length-1];e.attention.notify({title:y,message:I,session:{server:f.id,id:O},id:T.id}),$.length&&!$.some(S=>S.key===e.sessions.current()?.key)&&e.toast.show({title:y,description:I,icon:"lock",duration:1e4,actions:[{label:"Review",run:()=>w({server:f,directory:b},T)}]})};F.createMemo(F.mapArray(()=>e.servers.list(),f=>{B.createKeyed(()=>{const p=e.servers.get(f);return p?.connected?p:void 0},p=>{F.onCleanup(p.client.rpc(Be).events.on("changed",b=>k(p,b.data,b.location.directory),{signal:e.signal})),e.sessions.list().filter(b=>b.server.id===p.id).forEach(b=>{l(b)})})}))(),B.createKeyed(()=>e.sessions.current(),f=>{l(f)}),e.add(B.ComposerPill,{id:"requests",show:f=>{const p=n[f.session.key]??[],b=c(f.session);if(!(!p.length||!b))return{icon:"lock",tone:"warning",label:p.length===1?`Hush · ${p[0].label}`:`Hush · ${p.length} waiting`,title:"Credential requests",rows:p.map($=>({id:$.id,label:$.label,icon:"lock",run:()=>w(b,$)}))}}}),e.add(B.Command,{id:"forget",title:"Forget Hush credentials for this session",group:"Hush",section:"session",slash:{name:"hush-forget"},run:async()=>{const f=e.sessions.current(),p=f&&c(f);if(!(!f||!p))try{await i(p).forget({sessionID:f.id},u(p)),e.toast.show({title:"Session credentials forgotten.",variant:"success"})}catch{e.signal.aborted||a()}}})}});exports.default=Ri;
package/dist/hush.js ADDED
@@ -0,0 +1,145 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ const ended = {
6
+ denied: "The user declined this Hush credential request. Do not request it again unless they ask you to.",
7
+ expired: "The Hush credential request expired before the user answered. Request it again if it is still needed.",
8
+ interrupted: "The Hush credential request was interrupted before the user could answer. Request it again."
9
+ };
10
+ const REDACTED = "[redacted by Hush]";
11
+ const forms = (secret) => {
12
+ const bytes = Buffer.from(secret);
13
+ const encoded = secret.length < 8 ? [] : [
14
+ bytes.toString("base64"),
15
+ bytes.toString("base64").replace(/=+$/, ""),
16
+ bytes.toString("base64url"),
17
+ bytes.toString("hex"),
18
+ bytes.toString("hex").toUpperCase(),
19
+ encodeURIComponent(secret),
20
+ [...secret].reverse().join("")
21
+ ];
22
+ return [.../* @__PURE__ */ new Set([secret, ...encoded])].sort((a, b) => b.length - a.length);
23
+ };
24
+ const sweep = () => {
25
+ for (const name of readdirSync(tmpdir())) {
26
+ const pid = Number(/^oc-hush-(\d+)(-|$)/.exec(name)?.[1]);
27
+ if (!pid || pid === process.pid) continue;
28
+ try {
29
+ process.kill(pid, 0);
30
+ } catch (error) {
31
+ if (error.code === "ESRCH") rmSync(join(tmpdir(), name), { recursive: true, force: true });
32
+ }
33
+ }
34
+ };
35
+ function createHush(onChange = () => {
36
+ }, ttlMs = 15 * 60 * 1e3) {
37
+ const waiting = /* @__PURE__ */ new Map();
38
+ const entries = /* @__PURE__ */ new Map();
39
+ let directory;
40
+ let files = 0;
41
+ const clear = () => {
42
+ if (directory) rmSync(directory, { recursive: true, force: true });
43
+ };
44
+ try {
45
+ sweep();
46
+ } catch {
47
+ }
48
+ const remove = (file) => {
49
+ const entry = entries.get(file);
50
+ if (!entry) return;
51
+ clearTimeout(entry.timer);
52
+ rmSync(file, { force: true });
53
+ entries.delete(file);
54
+ };
55
+ const write = (secret) => {
56
+ if (!directory) {
57
+ const preferred = join(tmpdir(), `oc-hush-${process.pid}`);
58
+ try {
59
+ mkdirSync(preferred, { mode: 448 });
60
+ directory = preferred;
61
+ } catch {
62
+ directory = mkdtempSync(`${preferred}-`);
63
+ }
64
+ process.once("exit", clear);
65
+ }
66
+ const file = join(directory, String(++files));
67
+ writeFileSync(file, secret, { mode: 384, flag: "wx" });
68
+ return file;
69
+ };
70
+ return {
71
+ request(input, signal, lineage = [input.sessionID]) {
72
+ signal.throwIfAborted();
73
+ return new Promise((resolve, reject) => {
74
+ const id = randomUUID();
75
+ const finish = (secret, why = "denied") => {
76
+ if (!waiting.delete(id)) return;
77
+ clearTimeout(timer);
78
+ signal.removeEventListener("abort", cancel);
79
+ onChange({ sessionIDs: lineage });
80
+ if (!secret) {
81
+ reject(new Error(ended[why]));
82
+ return;
83
+ }
84
+ const file = write(secret);
85
+ const expiry = setTimeout(() => remove(file), ttlMs || 5 * 60 * 1e3);
86
+ expiry.unref?.();
87
+ entries.set(file, { lineage, secret, timer: expiry });
88
+ resolve({ file });
89
+ };
90
+ const cancel = () => finish(void 0, "interrupted");
91
+ const timer = setTimeout(() => finish(void 0, "expired"), 5 * 60 * 1e3);
92
+ timer.unref?.();
93
+ const info = { label: input.label, sessionID: input.sessionID, id };
94
+ waiting.set(id, { info, lineage, finish });
95
+ signal.addEventListener("abort", cancel, { once: true });
96
+ onChange({ sessionIDs: lineage, added: info });
97
+ });
98
+ },
99
+ pending(sessionID) {
100
+ return [...waiting.values()].filter((item) => item.lineage.includes(sessionID)).map((item) => item.info);
101
+ },
102
+ reply(id, secret) {
103
+ const item = waiting.get(id);
104
+ if (!item) return { accepted: false };
105
+ item.finish(secret);
106
+ return { accepted: true };
107
+ },
108
+ /** Replaces every live credential, and its common encodings, in any string inside `value`. */
109
+ redact(value) {
110
+ const patterns = [...entries.values()].flatMap((entry) => forms(entry.secret));
111
+ if (!patterns.length) return value;
112
+ const scrub = (item) => {
113
+ if (typeof item === "string") return patterns.reduce((text, form) => text.split(form).join(REDACTED), item);
114
+ if (Array.isArray(item)) return item.map(scrub);
115
+ if (item && typeof item === "object" && Object.getPrototypeOf(item) === Object.prototype) {
116
+ return Object.fromEntries(Object.entries(item).map(([key, field]) => [key, scrub(field)]));
117
+ }
118
+ return item;
119
+ };
120
+ return scrub(value);
121
+ },
122
+ /** Single-use credentials are forgotten once a tool call has read their file. */
123
+ used(input) {
124
+ if (ttlMs) return;
125
+ for (const file of [...entries.keys()]) if (input.includes(file)) remove(file);
126
+ },
127
+ forget(sessionID) {
128
+ const files2 = [...entries].filter(([, entry]) => entry.lineage.includes(sessionID)).map(([file]) => file);
129
+ files2.forEach(remove);
130
+ [...waiting.values()].filter((item) => item.lineage.includes(sessionID)).forEach((item) => item.finish());
131
+ return { forgotten: files2.length };
132
+ },
133
+ dispose() {
134
+ ;
135
+ [...waiting.values()].forEach((item) => item.finish(void 0, "interrupted"));
136
+ [...entries.keys()].forEach(remove);
137
+ clear();
138
+ process.off("exit", clear);
139
+ }
140
+ };
141
+ }
142
+ export {
143
+ createHush
144
+ };
145
+ //# sourceMappingURL=hush.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hush.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from 'node:fs'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\nimport type { Changed, Pending } from './rpc.js'\n\n// `lineage` is the requesting session followed by its ancestors, so a parent session can review and revoke what\n// its subagents requested.\ntype Entry = { lineage: string[]; secret: string; timer: ReturnType<typeof setTimeout> }\ntype Waiting = { info: Pending; lineage: string[]; finish: (secret?: string, why?: keyof typeof ended) => void }\n\n// What the agent is told when a request ends without a credential. Only a refusal by the user is final.\nconst ended = {\n denied: 'The user declined this Hush credential request. Do not request it again unless they ask you to.',\n expired: 'The Hush credential request expired before the user answered. Request it again if it is still needed.',\n interrupted: 'The Hush credential request was interrupted before the user could answer. Request it again.',\n}\n\nconst REDACTED = '[redacted by Hush]'\n// The secret as a command might print it. Short secrets only match exactly, to avoid redacting ordinary text.\nconst forms = (secret: string) => {\n const bytes = Buffer.from(secret)\n const encoded = secret.length < 8 ? [] : [\n bytes.toString('base64'), bytes.toString('base64').replace(/=+$/, ''), bytes.toString('base64url'),\n bytes.toString('hex'), bytes.toString('hex').toUpperCase(), encodeURIComponent(secret), [...secret].reverse().join(''),\n ]\n return [...new Set([secret, ...encoded])].sort((a, b) => b.length - a.length)\n}\n\n// Files of Hush instances whose process ended without cleaning up (killed, crashed). Directories are named after the pid.\nconst sweep = () => {\n for (const name of readdirSync(tmpdir())) {\n const pid = Number(/^oc-hush-(\\d+)(-|$)/.exec(name)?.[1])\n if (!pid || pid === process.pid) continue\n try { process.kill(pid, 0) } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ESRCH') rmSync(join(tmpdir(), name), { recursive: true, force: true })\n }\n }\n}\n\n/** `ttlMs` is how long an approved credential is kept; 0 forgets it after its first use. */\nexport function createHush(onChange: (change: Changed) => void = () => {}, ttlMs = 15 * 60 * 1000) {\n const waiting = new Map<string, Waiting>()\n // Approved credentials by the path of their file.\n const entries = new Map<string, Entry>()\n let directory: string | undefined\n let files = 0\n const clear = () => { if (directory) rmSync(directory, { recursive: true, force: true }) }\n try { sweep() } catch {}\n\n const remove = (file: string) => {\n const entry = entries.get(file)\n if (!entry) return\n clearTimeout(entry.timer)\n rmSync(file, { force: true })\n entries.delete(file)\n }\n // Short paths (…/oc-hush-<pid>/1), because agents copy them into commands. The directory is created on first use,\n // readable only by this user; if the name is taken, by another instance or anyone else, a random one is used.\n const write = (secret: string) => {\n if (!directory) {\n const preferred = join(tmpdir(), `oc-hush-${process.pid}`)\n try { mkdirSync(preferred, { mode: 0o700 }); directory = preferred } catch {\n directory = mkdtempSync(`${preferred}-`)\n }\n process.once('exit', clear)\n }\n const file = join(directory, String(++files))\n writeFileSync(file, secret, { mode: 0o600, flag: 'wx' })\n return file\n }\n\n return {\n request(input: Omit<Pending, 'id'>, signal: AbortSignal, lineage = [input.sessionID]) {\n signal.throwIfAborted()\n return new Promise<{ file: string }>((resolve, reject) => {\n const id = randomUUID()\n const finish = (secret?: string, why: keyof typeof ended = 'denied') => {\n if (!waiting.delete(id)) return\n clearTimeout(timer)\n signal.removeEventListener('abort', cancel)\n onChange({ sessionIDs: lineage })\n if (!secret) {\n reject(new Error(ended[why]))\n return\n }\n const file = write(secret)\n // A single-use credential still expires if it is never used.\n const expiry = setTimeout(() => remove(file), ttlMs || 5 * 60 * 1000)\n expiry.unref?.()\n entries.set(file, { lineage, secret, timer: expiry })\n resolve({ file })\n }\n const cancel = () => finish(undefined, 'interrupted')\n const timer = setTimeout(() => finish(undefined, 'expired'), 5 * 60 * 1000)\n timer.unref?.()\n const info = { label: input.label, sessionID: input.sessionID, id }\n waiting.set(id, { info, lineage, finish })\n signal.addEventListener('abort', cancel, { once: true })\n onChange({ sessionIDs: lineage, added: info })\n })\n },\n pending(sessionID: string) {\n return [...waiting.values()].filter(item => item.lineage.includes(sessionID)).map(item => item.info)\n },\n reply(id: string, secret?: string) {\n const item = waiting.get(id)\n if (!item) return { accepted: false }\n item.finish(secret)\n return { accepted: true }\n },\n /** Replaces every live credential, and its common encodings, in any string inside `value`. */\n redact<T>(value: T): T {\n const patterns = [...entries.values()].flatMap(entry => forms(entry.secret))\n if (!patterns.length) return value\n const scrub = (item: unknown): unknown => {\n if (typeof item === 'string') return patterns.reduce((text, form) => text.split(form).join(REDACTED), item)\n if (Array.isArray(item)) return item.map(scrub)\n if (item && typeof item === 'object' && Object.getPrototypeOf(item) === Object.prototype) {\n return Object.fromEntries(Object.entries(item).map(([key, field]) => [key, scrub(field)]))\n }\n return item\n }\n return scrub(value) as T\n },\n /** Single-use credentials are forgotten once a tool call has read their file. */\n used(input: string) {\n if (ttlMs) return\n for (const file of [...entries.keys()]) if (input.includes(file)) remove(file)\n },\n forget(sessionID: string) {\n const files = [...entries].filter(([, entry]) => entry.lineage.includes(sessionID)).map(([file]) => file)\n files.forEach(remove)\n ;[...waiting.values()].filter(item => item.lineage.includes(sessionID)).forEach(item => item.finish())\n return { forgotten: files.length }\n },\n dispose() {\n ;[...waiting.values()].forEach(item => item.finish(undefined, 'interrupted'))\n ;[...entries.keys()].forEach(remove)\n clear()\n process.off('exit', clear)\n },\n }\n}\n"],"mappings":"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","names":["files"]}
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ import { Plugin } from "@opencode/plugin";
2
+ import { z } from "zod";
3
+ import { createHush } from "./hush.js";
4
+ import { Hush, request } from "./rpc.js";
5
+ var index_default = Plugin.define({
6
+ id: "oc-hush",
7
+ async setup(ctx) {
8
+ const minutes = ctx.options.ttlMinutes ?? 15;
9
+ if (typeof minutes !== "number" || !(minutes >= 0 && minutes <= 1440)) throw new Error("oc-hush: ttlMinutes must be a number from 0 to 1440.");
10
+ const lifetime = minutes ? `valid for ${minutes} minute${minutes === 1 ? "" : "s"}` : "valid for one use";
11
+ let registration;
12
+ const hush = createHush((change) => void registration?.events.emit("changed", change).catch(() => {
13
+ }), minutes * 6e4);
14
+ const lineage = async (sessionID) => {
15
+ const ids = [sessionID];
16
+ while (ids.length < 16) {
17
+ const parent = await ctx.session.get({ sessionID: ids[ids.length - 1] }).then((info) => info.parentID, () => void 0);
18
+ if (!parent || ids.includes(parent)) break;
19
+ ids.push(parent);
20
+ }
21
+ return ids;
22
+ };
23
+ registration = await ctx.rpc.register(Hush, {
24
+ pending: async (input) => hush.pending(input.sessionID),
25
+ reply: async (input) => hush.reply(input.id, input.secret),
26
+ forget: async (input) => hush.forget(input.sessionID)
27
+ });
28
+ await ctx.tool.hook("execute.after", (call) => {
29
+ if (call.status === "completed") call.result = hush.redact(call.result);
30
+ if (call.status === "error") {
31
+ const error = call.error;
32
+ const message = hush.redact(error.message);
33
+ if (message !== error.message) call.error = new error.constructor({ message });
34
+ }
35
+ hush.used(JSON.stringify(call.input ?? ""));
36
+ });
37
+ await ctx.tool.transform((editor) => {
38
+ editor.namespace({ name: "hush", description: "Request credentials privately for shell commands. Never ask for credentials in chat." });
39
+ editor.add({
40
+ name: "request_secret",
41
+ options: { namespace: "hush" },
42
+ description: [
43
+ "Ask the user for a credential through a private prompt OpenCode shows them; wait for the result, and do not tell them to run a command. Never ask the user to paste a credential into chat.",
44
+ `Returns { file }: a private file holding the credential. Read it inside the command that needs it, e.g. GH_TOKEN="$(cat '<file>')" gh repo list, or curl -H "Authorization: Bearer $(cat '<file>')" <url>. Never print, copy, or move the file; command output is redacted anyway.`,
45
+ `The credential is ${lifetime}; request it again after that. Available in Code Mode and as a normal tool.`
46
+ ].join(" "),
47
+ input: request,
48
+ output: z.object({ file: z.string() }),
49
+ execute: async (input, context) => {
50
+ const output = await hush.request({ ...input, sessionID: context.sessionID }, context.signal, await lineage(context.sessionID));
51
+ return { output, content: JSON.stringify(output) };
52
+ }
53
+ });
54
+ });
55
+ return () => hush.dispose();
56
+ }
57
+ });
58
+ export {
59
+ index_default as default
60
+ };
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { Plugin } from '@opencode/plugin'\nimport { z } from 'zod'\nimport { createHush } from './hush.js'\nimport { Hush, request } from './rpc.js'\n\nexport default Plugin.define({\n id: 'oc-hush',\n async setup(ctx) {\n const minutes = ctx.options.ttlMinutes ?? 15\n if (typeof minutes !== 'number' || !(minutes >= 0 && minutes <= 1440)) throw new Error('oc-hush: ttlMinutes must be a number from 0 to 1440.')\n const lifetime = minutes ? `valid for ${minutes} minute${minutes === 1 ? '' : 's'}` : 'valid for one use'\n let registration: Awaited<ReturnType<typeof ctx.rpc.register<typeof Hush>>> | undefined\n const hush = createHush(change => void registration?.events.emit('changed', change).catch(() => {}), minutes * 60_000)\n // The session and its ancestors, so requests made by subagents surface in the session the user is looking at.\n const lineage = async (sessionID: string) => {\n const ids = [sessionID]\n while (ids.length < 16) {\n const parent = await ctx.session.get({ sessionID: ids[ids.length - 1] }).then(info => info.parentID, () => undefined)\n if (!parent || ids.includes(parent)) break\n ids.push(parent)\n }\n return ids\n }\n registration = await ctx.rpc.register(Hush, {\n pending: async input => hush.pending(input.sessionID),\n reply: async input => hush.reply(input.id, input.secret),\n forget: async input => hush.forget(input.sessionID),\n })\n // Every tool result passes through here before the model sees it, so a command that prints a credential, or an\n // encoding of it, shows the model a placeholder instead.\n await ctx.tool.hook('execute.after', call => {\n if (call.status === 'completed') call.result = hush.redact(call.result)\n if (call.status === 'error') {\n const error = call.error\n const message = hush.redact(error.message)\n // Tool errors are schema classes; rebuild one with the redacted message.\n if (message !== error.message) call.error = new (error.constructor as new (fields: { message: string }) => typeof error)({ message })\n }\n hush.used(JSON.stringify(call.input ?? ''))\n })\n await ctx.tool.transform(editor => {\n editor.namespace({ name: 'hush', description: 'Request credentials privately for shell commands. Never ask for credentials in chat.' })\n editor.add({\n name: 'request_secret',\n options: { namespace: 'hush' },\n description: [\n 'Ask the user for a credential through a private prompt OpenCode shows them; wait for the result, and do not tell them to run a command. Never ask the user to paste a credential into chat.',\n `Returns { file }: a private file holding the credential. Read it inside the command that needs it, e.g. GH_TOKEN=\"$(cat '<file>')\" gh repo list, or curl -H \"Authorization: Bearer $(cat '<file>')\" <url>. Never print, copy, or move the file; command output is redacted anyway.`,\n `The credential is ${lifetime}; request it again after that. Available in Code Mode and as a normal tool.`,\n ].join(' '),\n input: request,\n output: z.object({ file: z.string() }),\n execute: async (input, context) => {\n const output = await hush.request({ ...input, sessionID: context.sessionID }, context.signal, await lineage(context.sessionID))\n return { output, content: JSON.stringify(output) }\n },\n })\n })\n return () => hush.dispose()\n },\n})\n"],"mappings":"AAAA,SAAS,cAAc;AACvB,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B,SAAS,MAAM,eAAe;AAE9B,IAAO,gBAAQ,OAAO,OAAO;AAAA,EAC3B,IAAI;AAAA,EACJ,MAAM,MAAM,KAAK;AACf,UAAM,UAAU,IAAI,QAAQ,cAAc;AAC1C,QAAI,OAAO,YAAY,YAAY,EAAE,WAAW,KAAK,WAAW,MAAO,OAAM,IAAI,MAAM,sDAAsD;AAC7I,UAAM,WAAW,UAAU,aAAa,OAAO,UAAU,YAAY,IAAI,KAAK,GAAG,KAAK;AACtF,QAAI;AACJ,UAAM,OAAO,WAAW,YAAU,KAAK,cAAc,OAAO,KAAK,WAAW,MAAM,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC,GAAG,UAAU,GAAM;AAErH,UAAM,UAAU,OAAO,cAAsB;AAC3C,YAAM,MAAM,CAAC,SAAS;AACtB,aAAO,IAAI,SAAS,IAAI;AACtB,cAAM,SAAS,MAAM,IAAI,QAAQ,IAAI,EAAE,WAAW,IAAI,IAAI,SAAS,CAAC,EAAE,CAAC,EAAE,KAAK,UAAQ,KAAK,UAAU,MAAM,MAAS;AACpH,YAAI,CAAC,UAAU,IAAI,SAAS,MAAM,EAAG;AACrC,YAAI,KAAK,MAAM;AAAA,MACjB;AACA,aAAO;AAAA,IACT;AACA,mBAAe,MAAM,IAAI,IAAI,SAAS,MAAM;AAAA,MAC1C,SAAS,OAAM,UAAS,KAAK,QAAQ,MAAM,SAAS;AAAA,MACpD,OAAO,OAAM,UAAS,KAAK,MAAM,MAAM,IAAI,MAAM,MAAM;AAAA,MACvD,QAAQ,OAAM,UAAS,KAAK,OAAO,MAAM,SAAS;AAAA,IACpD,CAAC;AAGD,UAAM,IAAI,KAAK,KAAK,iBAAiB,UAAQ;AAC3C,UAAI,KAAK,WAAW,YAAa,MAAK,SAAS,KAAK,OAAO,KAAK,MAAM;AACtE,UAAI,KAAK,WAAW,SAAS;AAC3B,cAAM,QAAQ,KAAK;AACnB,cAAM,UAAU,KAAK,OAAO,MAAM,OAAO;AAEzC,YAAI,YAAY,MAAM,QAAS,MAAK,QAAQ,IAAK,MAAM,YAAkE,EAAE,QAAQ,CAAC;AAAA,MACtI;AACA,WAAK,KAAK,KAAK,UAAU,KAAK,SAAS,EAAE,CAAC;AAAA,IAC5C,CAAC;AACD,UAAM,IAAI,KAAK,UAAU,YAAU;AACjC,aAAO,UAAU,EAAE,MAAM,QAAQ,aAAa,uFAAuF,CAAC;AACtI,aAAO,IAAI;AAAA,QACT,MAAM;AAAA,QACN,SAAS,EAAE,WAAW,OAAO;AAAA,QAC7B,aAAa;AAAA,UACX;AAAA,UACA;AAAA,UACA,qBAAqB,QAAQ;AAAA,QAC/B,EAAE,KAAK,GAAG;AAAA,QACV,OAAO;AAAA,QACP,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAAA,QACrC,SAAS,OAAO,OAAO,YAAY;AACjC,gBAAM,SAAS,MAAM,KAAK,QAAQ,EAAE,GAAG,OAAO,WAAW,QAAQ,UAAU,GAAG,QAAQ,QAAQ,MAAM,QAAQ,QAAQ,SAAS,CAAC;AAC9H,iBAAO,EAAE,QAAQ,SAAS,KAAK,UAAU,MAAM,EAAE;AAAA,QACnD;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AACD,WAAO,MAAM,KAAK,QAAQ;AAAA,EAC5B;AACF,CAAC;","names":[]}
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "oc-hush",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./index.js",
6
+ "exports": {
7
+ ".": "./index.js",
8
+ "./tui": "./tui.js",
9
+ "./rpc": "./rpc.js",
10
+ "./gui-experimental": "./gui-experimental.cjs"
11
+ },
12
+ "dependencies": {
13
+ "@opencode/plugin": "2.2.6",
14
+ "zod": "4.1.8"
15
+ },
16
+ "peerDependencies": {
17
+ "@opentui/core": ">=0.8.1",
18
+ "@opentui/solid": ">=0.8.1",
19
+ "solid-js": ">=1.9.0"
20
+ }
21
+ }
package/dist/rpc.js ADDED
@@ -0,0 +1,35 @@
1
+ import { Rpc } from "@opencode/plugin/rpc";
2
+ import { z } from "zod";
3
+ const request = z.object({
4
+ label: z.string().min(1).max(80).describe('A short name for the credential, such as "GitHub token" or "Stripe API key". The user sees it when asked.')
5
+ }).strict();
6
+ const pending = request.extend({ id: z.string(), sessionID: z.string() });
7
+ const session = z.object({ sessionID: z.string().min(1) }).strict();
8
+ const changed = z.object({
9
+ /** The requesting session and its ancestors, so a parent session sees its subagents' requests. */
10
+ sessionIDs: z.array(z.string()),
11
+ /** Set when this change added a request. */
12
+ added: pending.optional()
13
+ });
14
+ const Hush = Rpc.define({
15
+ id: "oc-hush",
16
+ events: { changed: { schema: changed } },
17
+ methods: {
18
+ pending: { input: session, output: z.array(pending) },
19
+ reply: {
20
+ input: z.object({
21
+ id: z.string(),
22
+ secret: z.string().min(1).max(8192).regex(/^[\x21-\x7e]+$/).optional()
23
+ }).strict(),
24
+ output: z.object({ accepted: z.boolean() })
25
+ },
26
+ forget: { input: session, output: z.object({ forgotten: z.number() }) }
27
+ }
28
+ });
29
+ export {
30
+ Hush,
31
+ changed,
32
+ pending,
33
+ request
34
+ };
35
+ //# sourceMappingURL=rpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rpc.ts"],"sourcesContent":["import { Rpc } from '@opencode/plugin/rpc'\nimport { z } from 'zod'\n\nexport const request = z.object({\n label: z.string().min(1).max(80).describe('A short name for the credential, such as \"GitHub token\" or \"Stripe API key\". The user sees it when asked.'),\n}).strict()\n\nexport const pending = request.extend({ id: z.string(), sessionID: z.string() })\nexport type Pending = z.infer<typeof pending>\n\nconst session = z.object({ sessionID: z.string().min(1) }).strict()\n\n// Metadata only: never add the credential or a reference to an event, since every client of the server receives it.\nexport const changed = z.object({\n /** The requesting session and its ancestors, so a parent session sees its subagents' requests. */\n sessionIDs: z.array(z.string()),\n /** Set when this change added a request. */\n added: pending.optional(),\n})\nexport type Changed = z.infer<typeof changed>\n\nexport const Hush = Rpc.define({\n id: 'oc-hush',\n events: { changed: { schema: changed } },\n methods: {\n pending: { input: session, output: z.array(pending) },\n reply: {\n input: z.object({\n id: z.string(),\n secret: z.string().min(1).max(8192).regex(/^[\\x21-\\x7e]+$/).optional(),\n }).strict(),\n output: z.object({ accepted: z.boolean() }),\n },\n forget: { input: session, output: z.object({ forgotten: z.number() }) },\n },\n})\n"],"mappings":"AAAA,SAAS,WAAW;AACpB,SAAS,SAAS;AAEX,MAAM,UAAU,EAAE,OAAO;AAAA,EAC9B,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,2GAA2G;AACvJ,CAAC,EAAE,OAAO;AAEH,MAAM,UAAU,QAAQ,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,CAAC;AAG/E,MAAM,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,OAAO;AAG3D,MAAM,UAAU,EAAE,OAAO;AAAA;AAAA,EAE9B,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA;AAAA,EAE9B,OAAO,QAAQ,SAAS;AAC1B,CAAC;AAGM,MAAM,OAAO,IAAI,OAAO;AAAA,EAC7B,IAAI;AAAA,EACJ,QAAQ,EAAE,SAAS,EAAE,QAAQ,QAAQ,EAAE;AAAA,EACvC,SAAS;AAAA,IACP,SAAS,EAAE,OAAO,SAAS,QAAQ,EAAE,MAAM,OAAO,EAAE;AAAA,IACpD,OAAO;AAAA,MACL,OAAO,EAAE,OAAO;AAAA,QACd,IAAI,EAAE,OAAO;AAAA,QACb,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,EAAE,MAAM,gBAAgB,EAAE,SAAS;AAAA,MACvE,CAAC,EAAE,OAAO;AAAA,MACV,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AAAA,IAC5C;AAAA,IACA,QAAQ,EAAE,OAAO,SAAS,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE;AAAA,EACxE;AACF,CAAC;","names":[]}
package/dist/tui.js ADDED
@@ -0,0 +1,309 @@
1
+ import { use as _$use } from "@opentui/solid";
2
+ import { effect as _$effect } from "@opentui/solid";
3
+ import { insert as _$insert } from "@opentui/solid";
4
+ import { createComponent as _$createComponent } from "@opentui/solid";
5
+ import { createTextNode as _$createTextNode } from "@opentui/solid";
6
+ import { insertNode as _$insertNode } from "@opentui/solid";
7
+ import { setProp as _$setProp } from "@opentui/solid";
8
+ import { createElement as _$createElement } from "@opentui/solid";
9
+ import { Plugin } from "@opencode/plugin/tui";
10
+ import { createEffect, createSignal, on, Show } from "solid-js";
11
+ import { Hush } from "./rpc.js";
12
+ const bar = {
13
+ topLeft: "\u2503",
14
+ topRight: " ",
15
+ bottomLeft: "\u2503",
16
+ bottomRight: " ",
17
+ horizontal: " ",
18
+ vertical: "\u2503",
19
+ topT: " ",
20
+ bottomT: " ",
21
+ leftT: " ",
22
+ rightT: " ",
23
+ cross: " "
24
+ };
25
+ var tui_default = Plugin.define({
26
+ id: "oc-hush.tui",
27
+ setup(ctx) {
28
+ const controller = new AbortController();
29
+ const options = () => ({
30
+ location: ctx.location ?? ctx.data.location.default(),
31
+ signal: controller.signal
32
+ });
33
+ const session = () => {
34
+ const route = ctx.ui.router.current();
35
+ return route.type === "session" ? route.sessionID : void 0;
36
+ };
37
+ const report = () => ctx.ui.toast.show({
38
+ title: "oc-hush",
39
+ message: "Could not reach Hush. Check that its server plugin is loaded.",
40
+ variant: "error"
41
+ });
42
+ let open;
43
+ const reply = async (id, secret) => {
44
+ try {
45
+ const result = await ctx.client.rpc(Hush).reply({
46
+ id,
47
+ secret
48
+ }, options());
49
+ if (!result.accepted) ctx.ui.toast.show({
50
+ title: "oc-hush",
51
+ message: "This credential request has expired.",
52
+ variant: "warning"
53
+ });
54
+ } catch {
55
+ if (!controller.signal.aborted) report();
56
+ }
57
+ };
58
+ const ask = (item) => {
59
+ let answered = false;
60
+ const answer = (secret, send = true) => {
61
+ if (answered) return;
62
+ answered = true;
63
+ open = void 0;
64
+ if (send) void reply(item.id, secret).then(refresh);
65
+ };
66
+ const Dialog = () => {
67
+ const [length, setLength] = createSignal(0);
68
+ const [error, setError] = createSignal("");
69
+ let field;
70
+ const invalid = (value) => !/^[\x21-\x7e]*$/.test(value) || value.length > 8192;
71
+ const input = (value) => {
72
+ setLength(value.length);
73
+ setError(invalid(value.trim()) ? "Credentials cannot contain spaces or line breaks." : "");
74
+ };
75
+ const submit = () => {
76
+ const secret = (field?.value ?? "").trim();
77
+ if (!secret || invalid(secret)) return;
78
+ answer(secret);
79
+ ctx.ui.dialog.clear();
80
+ };
81
+ const Space = () => (() => {
82
+ var _el$ = _$createElement("box");
83
+ _$setProp(_el$, "height", 1);
84
+ _$setProp(_el$, "flexShrink", 0);
85
+ return _el$;
86
+ })();
87
+ return (() => {
88
+ var _el$2 = _$createElement("box"), _el$3 = _$createElement("box"), _el$4 = _$createElement("text"), _el$5 = _$createElement("b"), _el$7 = _$createElement("text"), _el$9 = _$createElement("text"), _el$0 = _$createElement("box"), _el$10 = _$createElement("input"), _el$12 = _$createElement("box"), _el$13 = _$createElement("text"), _el$14 = _$createElement("span"), _el$16 = _$createTextNode(` approve`), _el$17 = _$createElement("text"), _el$18 = _$createElement("span"), _el$20 = _$createTextNode(` deny`);
89
+ _$insertNode(_el$2, _el$3);
90
+ _$insertNode(_el$2, _el$9);
91
+ _$insertNode(_el$2, _el$0);
92
+ _$insertNode(_el$2, _el$12);
93
+ _$setProp(_el$2, "paddingLeft", 2);
94
+ _$setProp(_el$2, "paddingRight", 2);
95
+ _$setProp(_el$2, "paddingBottom", 1);
96
+ _$insertNode(_el$3, _el$4);
97
+ _$insertNode(_el$3, _el$7);
98
+ _$setProp(_el$3, "flexDirection", "row");
99
+ _$setProp(_el$3, "justifyContent", "space-between");
100
+ _$setProp(_el$3, "flexShrink", 0);
101
+ _$insertNode(_el$4, _el$5);
102
+ _$insertNode(_el$5, _$createTextNode(`Temporary Credential`));
103
+ _$insertNode(_el$7, _$createTextNode(`esc`));
104
+ _$insert(_el$2, _$createComponent(Space, {}), _el$9);
105
+ _$setProp(_el$9, "flexShrink", 0);
106
+ _$insert(_el$9, () => item.label);
107
+ _$insert(_el$2, _$createComponent(Space, {}), _el$0);
108
+ _$insertNode(_el$0, _el$10);
109
+ _$setProp(_el$0, "flexDirection", "row");
110
+ _$setProp(_el$0, "flexShrink", 0);
111
+ _$setProp(_el$0, "border", ["left"]);
112
+ _$setProp(_el$0, "customBorderChars", bar);
113
+ _$setProp(_el$0, "paddingLeft", 1);
114
+ _$setProp(_el$0, "paddingRight", 1);
115
+ _$setProp(_el$0, "paddingTop", 1);
116
+ _$setProp(_el$0, "paddingBottom", 1);
117
+ _$setProp(_el$0, "overflow", "hidden");
118
+ _$insert(_el$0, _$createComponent(Show, {
119
+ get when() {
120
+ return length();
121
+ },
122
+ get children() {
123
+ var _el$1 = _$createElement("text");
124
+ _$setProp(_el$1, "flexShrink", 1);
125
+ _$setProp(_el$1, "minWidth", 0);
126
+ _$setProp(_el$1, "wrapMode", "none");
127
+ _$insert(_el$1, () => "\u2022".repeat(Math.min(length(), 200)));
128
+ _$effect((_$p) => _$setProp(_el$1, "fg", ctx.theme.text.base, _$p));
129
+ return _el$1;
130
+ }
131
+ }), _el$10);
132
+ _$use((element) => {
133
+ field = element;
134
+ }, _el$10);
135
+ _$setProp(_el$10, "focused", true);
136
+ _$setProp(_el$10, "flexShrink", 0);
137
+ _$setProp(_el$10, "placeholder", "Paste or type credential");
138
+ _$setProp(_el$10, "textColor", "transparent");
139
+ _$setProp(_el$10, "focusedTextColor", "transparent");
140
+ _$setProp(_el$10, "backgroundColor", "transparent");
141
+ _$setProp(_el$10, "focusedBackgroundColor", "transparent");
142
+ _$setProp(_el$10, "onInput", input);
143
+ _$setProp(_el$10, "onSubmit", submit);
144
+ _$insert(_el$2, _$createComponent(Show, {
145
+ get when() {
146
+ return error();
147
+ },
148
+ get children() {
149
+ var _el$11 = _$createElement("text");
150
+ _$setProp(_el$11, "flexShrink", 0);
151
+ _$insert(_el$11, error);
152
+ _$effect((_$p) => _$setProp(_el$11, "fg", ctx.theme.text.feedback.error.base, _$p));
153
+ return _el$11;
154
+ }
155
+ }), _el$12);
156
+ _$insert(_el$2, _$createComponent(Space, {}), _el$12);
157
+ _$insertNode(_el$12, _el$13);
158
+ _$insertNode(_el$12, _el$17);
159
+ _$setProp(_el$12, "flexDirection", "row");
160
+ _$setProp(_el$12, "gap", 3);
161
+ _$setProp(_el$12, "flexShrink", 0);
162
+ _$insertNode(_el$13, _el$14);
163
+ _$insertNode(_el$13, _el$16);
164
+ _$insertNode(_el$14, _$createTextNode(`enter`));
165
+ _$insertNode(_el$17, _el$18);
166
+ _$insertNode(_el$17, _el$20);
167
+ _$insertNode(_el$18, _$createTextNode(`esc`));
168
+ _$effect((_p$) => {
169
+ var _v$ = ctx.theme.text.base, _v$2 = ctx.theme.text.muted, _v$3 = ctx.theme.text.muted, _v$4 = error() ? ctx.theme.text.feedback.error.base : ctx.theme.background.action.primary.focused, _v$5 = ctx.theme.background.raised.high, _v$6 = length() ? 0 : 1, _v$7 = length() ? 1 : void 0, _v$8 = ctx.theme.text.muted, _v$9 = ctx.theme.text.muted, _v$0 = {
170
+ fg: ctx.theme.text.base
171
+ }, _v$1 = ctx.theme.text.muted, _v$10 = {
172
+ fg: ctx.theme.text.base
173
+ };
174
+ _v$ !== _p$.e && (_p$.e = _$setProp(_el$4, "fg", _v$, _p$.e));
175
+ _v$2 !== _p$.t && (_p$.t = _$setProp(_el$7, "fg", _v$2, _p$.t));
176
+ _v$3 !== _p$.a && (_p$.a = _$setProp(_el$9, "fg", _v$3, _p$.a));
177
+ _v$4 !== _p$.o && (_p$.o = _$setProp(_el$0, "borderColor", _v$4, _p$.o));
178
+ _v$5 !== _p$.i && (_p$.i = _$setProp(_el$0, "backgroundColor", _v$5, _p$.i));
179
+ _v$6 !== _p$.n && (_p$.n = _$setProp(_el$10, "flexGrow", _v$6, _p$.n));
180
+ _v$7 !== _p$.s && (_p$.s = _$setProp(_el$10, "width", _v$7, _p$.s));
181
+ _v$8 !== _p$.h && (_p$.h = _$setProp(_el$10, "placeholderColor", _v$8, _p$.h));
182
+ _v$9 !== _p$.r && (_p$.r = _$setProp(_el$13, "fg", _v$9, _p$.r));
183
+ _v$0 !== _p$.d && (_p$.d = _$setProp(_el$14, "style", _v$0, _p$.d));
184
+ _v$1 !== _p$.l && (_p$.l = _$setProp(_el$17, "fg", _v$1, _p$.l));
185
+ _v$10 !== _p$.u && (_p$.u = _$setProp(_el$18, "style", _v$10, _p$.u));
186
+ return _p$;
187
+ }, {
188
+ e: void 0,
189
+ t: void 0,
190
+ a: void 0,
191
+ o: void 0,
192
+ i: void 0,
193
+ n: void 0,
194
+ s: void 0,
195
+ h: void 0,
196
+ r: void 0,
197
+ d: void 0,
198
+ l: void 0,
199
+ u: void 0
200
+ });
201
+ return _el$2;
202
+ })();
203
+ };
204
+ open = {
205
+ id: item.id,
206
+ close: () => {
207
+ answer(void 0, false);
208
+ ctx.ui.dialog.clear();
209
+ }
210
+ };
211
+ ctx.ui.dialog.show(Dialog, () => answer());
212
+ };
213
+ const refresh = async () => {
214
+ const sessionID = session();
215
+ if (!sessionID) return;
216
+ try {
217
+ const items = await ctx.client.rpc(Hush).pending({
218
+ sessionID
219
+ }, options());
220
+ if (session() !== sessionID || controller.signal.aborted) return;
221
+ if (open && !items.some((item) => item.id === open?.id)) open.close();
222
+ if (!open && items.length) ask(items[0]);
223
+ } catch {
224
+ if (!controller.signal.aborted) report();
225
+ }
226
+ };
227
+ ctx.keymap.layer(() => ({
228
+ mode: "global",
229
+ commands: [{
230
+ id: "oc-hush.forget",
231
+ title: "Forget Hush credentials for this session",
232
+ group: "oc-hush",
233
+ palette: true,
234
+ slash: {
235
+ name: "hush-forget"
236
+ },
237
+ run: async () => {
238
+ const sessionID = session();
239
+ if (!sessionID) return;
240
+ try {
241
+ await ctx.client.rpc(Hush).forget({
242
+ sessionID
243
+ }, options());
244
+ ctx.ui.toast.show({
245
+ title: "oc-hush",
246
+ message: "Session credentials forgotten.",
247
+ variant: "success"
248
+ });
249
+ } catch {
250
+ report();
251
+ }
252
+ }
253
+ }]
254
+ }));
255
+ let stopEvents = () => {
256
+ };
257
+ const subscribe = () => {
258
+ stopEvents();
259
+ stopEvents = ctx.client.rpc(Hush).events.on("changed", (event) => {
260
+ if (event.location.directory !== options().location.directory) return;
261
+ const current = session();
262
+ const here = !!current && event.data.sessionIDs.includes(current);
263
+ if (here) void refresh();
264
+ const added = event.data.added;
265
+ if (!added) return;
266
+ const message = added.label;
267
+ if (!here) {
268
+ const root = event.data.sessionIDs[event.data.sessionIDs.length - 1];
269
+ ctx.ui.toast.show({
270
+ title: "Hush needs a credential",
271
+ message,
272
+ variant: "warning",
273
+ duration: 1e4,
274
+ sessionID: root
275
+ });
276
+ }
277
+ void ctx.attention.notify({
278
+ title: "Hush needs a credential",
279
+ message,
280
+ notification: {
281
+ when: "blurred"
282
+ },
283
+ sound: {
284
+ name: "question",
285
+ when: "blurred"
286
+ }
287
+ });
288
+ }, {
289
+ signal: controller.signal
290
+ });
291
+ void refresh();
292
+ };
293
+ const stopReconnect = ctx.data.on("server.connected", subscribe);
294
+ subscribe();
295
+ createEffect(on(session, () => void refresh(), {
296
+ defer: true
297
+ }));
298
+ return () => {
299
+ controller.abort();
300
+ stopEvents();
301
+ stopReconnect();
302
+ open?.close();
303
+ };
304
+ }
305
+ });
306
+ export {
307
+ tui_default as default
308
+ };
309
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tui.tsx"],"sourcesContent":["import { use as _$use } from \"@opentui/solid\";\nimport { effect as _$effect } from \"@opentui/solid\";\nimport { insert as _$insert } from \"@opentui/solid\";\nimport { createComponent as _$createComponent } from \"@opentui/solid\";\nimport { createTextNode as _$createTextNode } from \"@opentui/solid\";\nimport { insertNode as _$insertNode } from \"@opentui/solid\";\nimport { setProp as _$setProp } from \"@opentui/solid\";\nimport { createElement as _$createElement } from \"@opentui/solid\";\nimport { Plugin } from '@opencode/plugin/tui';\nimport { createEffect, createSignal, on, Show } from 'solid-js';\nimport { Hush } from './rpc.js';\n\n// OpenCode's prompt bar: only the left side is drawn, as a heavy vertical line.\nconst bar = {\n topLeft: '┃',\n topRight: ' ',\n bottomLeft: '┃',\n bottomRight: ' ',\n horizontal: ' ',\n vertical: '┃',\n topT: ' ',\n bottomT: ' ',\n leftT: ' ',\n rightT: ' ',\n cross: ' '\n};\nexport default Plugin.define({\n id: 'oc-hush.tui',\n setup(ctx) {\n const controller = new AbortController();\n const options = () => ({\n location: ctx.location ?? ctx.data.location.default(),\n signal: controller.signal\n });\n const session = () => {\n const route = ctx.ui.router.current();\n return route.type === 'session' ? route.sessionID : undefined;\n };\n const report = () => ctx.ui.toast.show({\n title: 'oc-hush',\n message: 'Could not reach Hush. Check that its server plugin is loaded.',\n variant: 'error'\n });\n // The request whose dialog is open, and a way to close it when the plugin goes away.\n let open;\n const reply = async (id, secret) => {\n try {\n const result = await ctx.client.rpc(Hush).reply({\n id,\n secret\n }, options());\n if (!result.accepted) ctx.ui.toast.show({\n title: 'oc-hush',\n message: 'This credential request has expired.',\n variant: 'warning'\n });\n } catch {\n if (!controller.signal.aborted) report();\n }\n };\n\n // Asks as soon as the agent does. Typing goes to a focused input whose text is invisible; the dialog draws a mask\n // from its length. Nothing reaches the prompt or its history. Enter approves; closing the dialog denies.\n const ask = item => {\n let answered = false;\n // `send: false` only closes the dialog: the request keeps waiting and is asked again.\n const answer = (secret, send = true) => {\n if (answered) return;\n answered = true;\n open = undefined;\n if (send) void reply(item.id, secret).then(refresh);\n };\n const Dialog = () => {\n const [length, setLength] = createSignal(0);\n const [error, setError] = createSignal('');\n let field;\n // Credentials are header values: printable ASCII without spaces.\n const invalid = value => !/^[\\x21-\\x7e]*$/.test(value) || value.length > 8192;\n const input = value => {\n setLength(value.length);\n setError(invalid(value.trim()) ? 'Credentials cannot contain spaces or line breaks.' : '');\n };\n const submit = () => {\n const secret = (field?.value ?? '').trim();\n if (!secret || invalid(secret)) return;\n answer(secret);\n ctx.ui.dialog.clear();\n };\n // Laid out like OpenCode's own dialogs: bold title with `esc` opposite, muted detail, field, key hints.\n const Space = () => (() => {\n var _el$ = _$createElement(\"box\");\n _$setProp(_el$, \"height\", 1);\n _$setProp(_el$, \"flexShrink\", 0);\n return _el$;\n })();\n return (() => {\n var _el$2 = _$createElement(\"box\"),\n _el$3 = _$createElement(\"box\"),\n _el$4 = _$createElement(\"text\"),\n _el$5 = _$createElement(\"b\"),\n _el$7 = _$createElement(\"text\"),\n _el$9 = _$createElement(\"text\"),\n _el$0 = _$createElement(\"box\"),\n _el$10 = _$createElement(\"input\"),\n _el$12 = _$createElement(\"box\"),\n _el$13 = _$createElement(\"text\"),\n _el$14 = _$createElement(\"span\"),\n _el$16 = _$createTextNode(` approve`),\n _el$17 = _$createElement(\"text\"),\n _el$18 = _$createElement(\"span\"),\n _el$20 = _$createTextNode(` deny`);\n _$insertNode(_el$2, _el$3);\n _$insertNode(_el$2, _el$9);\n _$insertNode(_el$2, _el$0);\n _$insertNode(_el$2, _el$12);\n _$setProp(_el$2, \"paddingLeft\", 2);\n _$setProp(_el$2, \"paddingRight\", 2);\n _$setProp(_el$2, \"paddingBottom\", 1);\n _$insertNode(_el$3, _el$4);\n _$insertNode(_el$3, _el$7);\n _$setProp(_el$3, \"flexDirection\", \"row\");\n _$setProp(_el$3, \"justifyContent\", \"space-between\");\n _$setProp(_el$3, \"flexShrink\", 0);\n _$insertNode(_el$4, _el$5);\n _$insertNode(_el$5, _$createTextNode(`Temporary Credential`));\n _$insertNode(_el$7, _$createTextNode(`esc`));\n _$insert(_el$2, _$createComponent(Space, {}), _el$9);\n _$setProp(_el$9, \"flexShrink\", 0);\n _$insert(_el$9, () => item.label);\n _$insert(_el$2, _$createComponent(Space, {}), _el$0);\n _$insertNode(_el$0, _el$10);\n _$setProp(_el$0, \"flexDirection\", \"row\");\n _$setProp(_el$0, \"flexShrink\", 0);\n _$setProp(_el$0, \"border\", ['left']);\n _$setProp(_el$0, \"customBorderChars\", bar);\n _$setProp(_el$0, \"paddingLeft\", 1);\n _$setProp(_el$0, \"paddingRight\", 1);\n _$setProp(_el$0, \"paddingTop\", 1);\n _$setProp(_el$0, \"paddingBottom\", 1);\n _$setProp(_el$0, \"overflow\", \"hidden\");\n _$insert(_el$0, _$createComponent(Show, {\n get when() {\n return length();\n },\n get children() {\n var _el$1 = _$createElement(\"text\");\n _$setProp(_el$1, \"flexShrink\", 1);\n _$setProp(_el$1, \"minWidth\", 0);\n _$setProp(_el$1, \"wrapMode\", \"none\");\n _$insert(_el$1, () => '•'.repeat(Math.min(length(), 200)));\n _$effect(_$p => _$setProp(_el$1, \"fg\", ctx.theme.text.base, _$p));\n return _el$1;\n }\n }), _el$10);\n _$use(element => {\n field = element;\n }, _el$10);\n _$setProp(_el$10, \"focused\", true);\n _$setProp(_el$10, \"flexShrink\", 0);\n _$setProp(_el$10, \"placeholder\", \"Paste or type credential\");\n _$setProp(_el$10, \"textColor\", \"transparent\");\n _$setProp(_el$10, \"focusedTextColor\", \"transparent\");\n _$setProp(_el$10, \"backgroundColor\", \"transparent\");\n _$setProp(_el$10, \"focusedBackgroundColor\", \"transparent\");\n _$setProp(_el$10, \"onInput\", input);\n _$setProp(_el$10, \"onSubmit\", submit);\n _$insert(_el$2, _$createComponent(Show, {\n get when() {\n return error();\n },\n get children() {\n var _el$11 = _$createElement(\"text\");\n _$setProp(_el$11, \"flexShrink\", 0);\n _$insert(_el$11, error);\n _$effect(_$p => _$setProp(_el$11, \"fg\", ctx.theme.text.feedback.error.base, _$p));\n return _el$11;\n }\n }), _el$12);\n _$insert(_el$2, _$createComponent(Space, {}), _el$12);\n _$insertNode(_el$12, _el$13);\n _$insertNode(_el$12, _el$17);\n _$setProp(_el$12, \"flexDirection\", \"row\");\n _$setProp(_el$12, \"gap\", 3);\n _$setProp(_el$12, \"flexShrink\", 0);\n _$insertNode(_el$13, _el$14);\n _$insertNode(_el$13, _el$16);\n _$insertNode(_el$14, _$createTextNode(`enter`));\n _$insertNode(_el$17, _el$18);\n _$insertNode(_el$17, _el$20);\n _$insertNode(_el$18, _$createTextNode(`esc`));\n _$effect(_p$ => {\n var _v$ = ctx.theme.text.base,\n _v$2 = ctx.theme.text.muted,\n _v$3 = ctx.theme.text.muted,\n _v$4 = error() ? ctx.theme.text.feedback.error.base : ctx.theme.background.action.primary.focused,\n _v$5 = ctx.theme.background.raised.high,\n _v$6 = length() ? 0 : 1,\n _v$7 = length() ? 1 : undefined,\n _v$8 = ctx.theme.text.muted,\n _v$9 = ctx.theme.text.muted,\n _v$0 = {\n fg: ctx.theme.text.base\n },\n _v$1 = ctx.theme.text.muted,\n _v$10 = {\n fg: ctx.theme.text.base\n };\n _v$ !== _p$.e && (_p$.e = _$setProp(_el$4, \"fg\", _v$, _p$.e));\n _v$2 !== _p$.t && (_p$.t = _$setProp(_el$7, \"fg\", _v$2, _p$.t));\n _v$3 !== _p$.a && (_p$.a = _$setProp(_el$9, \"fg\", _v$3, _p$.a));\n _v$4 !== _p$.o && (_p$.o = _$setProp(_el$0, \"borderColor\", _v$4, _p$.o));\n _v$5 !== _p$.i && (_p$.i = _$setProp(_el$0, \"backgroundColor\", _v$5, _p$.i));\n _v$6 !== _p$.n && (_p$.n = _$setProp(_el$10, \"flexGrow\", _v$6, _p$.n));\n _v$7 !== _p$.s && (_p$.s = _$setProp(_el$10, \"width\", _v$7, _p$.s));\n _v$8 !== _p$.h && (_p$.h = _$setProp(_el$10, \"placeholderColor\", _v$8, _p$.h));\n _v$9 !== _p$.r && (_p$.r = _$setProp(_el$13, \"fg\", _v$9, _p$.r));\n _v$0 !== _p$.d && (_p$.d = _$setProp(_el$14, \"style\", _v$0, _p$.d));\n _v$1 !== _p$.l && (_p$.l = _$setProp(_el$17, \"fg\", _v$1, _p$.l));\n _v$10 !== _p$.u && (_p$.u = _$setProp(_el$18, \"style\", _v$10, _p$.u));\n return _p$;\n }, {\n e: undefined,\n t: undefined,\n a: undefined,\n o: undefined,\n i: undefined,\n n: undefined,\n s: undefined,\n h: undefined,\n r: undefined,\n d: undefined,\n l: undefined,\n u: undefined\n });\n return _el$2;\n })();\n };\n open = {\n id: item.id,\n close: () => {\n answer(undefined, false);\n ctx.ui.dialog.clear();\n }\n };\n ctx.ui.dialog.show(Dialog, () => answer());\n };\n const refresh = async () => {\n const sessionID = session();\n if (!sessionID) return;\n try {\n const items = await ctx.client.rpc(Hush).pending({\n sessionID\n }, options());\n if (session() !== sessionID || controller.signal.aborted) return;\n // The request on screen expired, was interrupted, or was answered elsewhere: its dialog can no longer answer it.\n if (open && !items.some(item => item.id === open?.id)) open.close();\n if (!open && items.length) ask(items[0]);\n } catch {\n if (!controller.signal.aborted) report();\n }\n };\n ctx.keymap.layer(() => ({\n mode: 'global',\n commands: [{\n id: 'oc-hush.forget',\n title: 'Forget Hush credentials for this session',\n group: 'oc-hush',\n palette: true,\n slash: {\n name: 'hush-forget'\n },\n run: async () => {\n const sessionID = session();\n if (!sessionID) return;\n try {\n await ctx.client.rpc(Hush).forget({\n sessionID\n }, options());\n ctx.ui.toast.show({\n title: 'oc-hush',\n message: 'Session credentials forgotten.',\n variant: 'success'\n });\n } catch {\n report();\n }\n }\n }]\n }));\n let stopEvents = () => {};\n const subscribe = () => {\n stopEvents();\n stopEvents = ctx.client.rpc(Hush).events.on('changed', event => {\n if (event.location.directory !== options().location.directory) return;\n const current = session();\n const here = !!current && event.data.sessionIDs.includes(current);\n if (here) void refresh();\n const added = event.data.added;\n if (!added) return;\n const message = added.label;\n // On screen, the dialog is the alert. Elsewhere, the toast offers to open the top-level session.\n if (!here) {\n const root = event.data.sessionIDs[event.data.sessionIDs.length - 1];\n ctx.ui.toast.show({\n title: 'Hush needs a credential',\n message,\n variant: 'warning',\n duration: 10_000,\n sessionID: root\n });\n }\n void ctx.attention.notify({\n title: 'Hush needs a credential',\n message,\n notification: {\n when: 'blurred'\n },\n sound: {\n name: 'question',\n when: 'blurred'\n }\n });\n }, {\n signal: controller.signal\n });\n void refresh();\n };\n const stopReconnect = ctx.data.on('server.connected', subscribe);\n subscribe();\n createEffect(on(session, () => void refresh(), {\n defer: true\n }));\n return () => {\n controller.abort();\n stopEvents();\n stopReconnect();\n open?.close();\n };\n }\n});"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,70 @@
1
1
  {
2
2
  "name": "oc-hush",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Let OpenCode use credentials without putting them in the conversation.",
5
+ "keywords": [
6
+ "opencode",
7
+ "opencode-plugin",
8
+ "secrets",
9
+ "credentials",
10
+ "codemode"
11
+ ],
12
+ "author": "Ope Oginni",
13
+ "license": "MIT",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/OpeOginni/oc-plugins.git",
17
+ "directory": "packages/oc-hush"
18
+ },
19
+ "homepage": "https://github.com/OpeOginni/oc-plugins/tree/main/packages/oc-hush#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/OpeOginni/oc-plugins/issues"
22
+ },
23
+ "type": "module",
24
+ "exports": {
25
+ ".": "./dist/index.js",
26
+ "./tui": "./dist/tui.js",
27
+ "./rpc": "./dist/rpc.js",
28
+ "./gui-experimental": "./dist/gui-experimental.cjs"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "registry": "https://registry.npmjs.org/"
37
+ },
38
+ "scripts": {
39
+ "build": "tsup && vite build",
40
+ "prepack": "bun run build",
41
+ "check": "tsc --noEmit && tsc --noEmit -p tsconfig.gui.json",
42
+ "test": "bun test test/*.test.ts",
43
+ "prepublishOnly": "bun run check && bun run test",
44
+ "publish:dry-run": "bun publish --dry-run",
45
+ "release": "bun publish"
46
+ },
47
+ "dependencies": {
48
+ "@opencode/plugin": "2.2.6",
49
+ "zod": "4.1.8"
50
+ },
51
+ "peerDependencies": {
52
+ "@opentui/core": ">=0.8.1",
53
+ "@opentui/solid": ">=0.8.1",
54
+ "solid-js": ">=1.9.0"
55
+ },
56
+ "devDependencies": {
57
+ "@babel/core": "^7.28.0",
58
+ "@babel/preset-typescript": "^7.27.1",
59
+ "@opentui/core": "0.8.3",
60
+ "@opentui/solid": "0.8.3",
61
+ "@types/bun": "^1.4.2",
62
+ "@types/node": "^22",
63
+ "babel-preset-solid": "^1.9.12",
64
+ "solid-js": "1.9.15",
65
+ "tsup": "^8.5.1",
66
+ "typescript": "^5.9.0",
67
+ "vite": "7.3.7",
68
+ "vite-plugin-solid": "2.11.10"
69
+ }
70
+ }