@oracle-agent/oracle 0.23.1 → 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -13
- package/SECURITY.md +9 -8
- package/SETUP.md +36 -9
- package/dist/assets/skills/oracle-hlp-vault-tracker/SKILL.md +16 -0
- package/dist/assets/skills/oracle-meme-token-sniper/SKILL.md +3 -0
- package/dist/bin/desk-server.mjs +18 -18
- package/dist/bin/oracle-data-mcp.mjs +6 -6
- package/dist/bin/oracle.mjs +13 -8
- package/dist/cli/commands/sign.mjs +23 -0
- package/dist/cli/commands/signer.mjs +13 -0
- package/dist/data/desk-data.mjs +17 -15
- package/dist/index.mjs +21 -21
- package/dist/local-signer/index.mjs +4 -0
- package/package.json +4 -3
- package/public/service/la.oracle.local-signer.plist +7 -0
- package/public/service/oracle-signer-task.ps1 +5 -0
- package/public/service/oracle-signer.service +16 -0
package/dist/bin/oracle.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{realpathSync as U}from"node:fs";import{fileURLToPath as re}from"node:url";import h from"node:fs";import g from"node:path";import{pathToFileURL as Y}from"node:url";import d from"node:fs";import
|
|
2
|
+
import{realpathSync as U}from"node:fs";import{fileURLToPath as re}from"node:url";import h from"node:fs";import g from"node:path";import{pathToFileURL as Y}from"node:url";import d from"node:fs";import D from"node:os";import i from"node:path";import{fileURLToPath as T}from"node:url";var P=i.dirname(T(import.meta.url)),l=i.resolve(P,"../..");function u(){return process.env.ORACLE_FAKE_HOME||D.homedir()}function A(){return process.env.ORACLE_PUBLIC_DESKTOP==="1"}function f(){return A()?process.env.ORACLE_CONFIG_DIR||i.join(u(),".config","oracle"):i.join(u(),".config","oracle")}function $(){return i.join(f(),"exec.env")}function _(){return i.join(f(),"desk.json")}function R(){return i.join(f(),"env")}function p({force:e=!1}={}){if(p._done&&!e)return p._result||null;p._done=!0;let o={},r=(a,t)=>{if(t==null)return;let n=String(t).trim();n&&(process.env[a]!=null&&String(process.env[a]).trim()!==""||(process.env[a]=n,o[a]=n))};try{let a=R();if(d.existsSync(a))for(let t of d.readFileSync(a,"utf8").split(/\r?\n/)){let n=t.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*?)\s*$/);if(!n||n[2].startsWith("#"))continue;let c=n[2];(c.startsWith('"')&&c.endsWith('"')||c.startsWith("'")&&c.endsWith("'"))&&(c=c.slice(1,-1)),r(n[1],c)}}catch{}try{let a=_();if(d.existsSync(a)){let t=JSON.parse(d.readFileSync(a,"utf8"));t&&typeof t=="object"&&(r("ORACLE_DESK_URL",t.deskUrl||t.dataUrl||t.url),r("ORACLE_DATA_URL",t.dataUrl||t.deskUrl||t.url),r("ORACLE_API_URL",t.apiUrl||t.dataUrl||t.deskUrl),r("ORACLE_PUBLIC_URL",t.publicUrl||t.deskUrl||t.dataUrl),r("ORACLE_EXEC_URL",t.execUrl),r("MAD_DESK_URL",t.deskUrl||t.dataUrl))}}catch{}let s=process.env.ORACLE_DESK_URL||process.env.ORACLE_DATA_URL||"";return s&&(r("ORACLE_DESK_URL",s),r("ORACLE_DATA_URL",s),r("ORACLE_API_URL",s),r("ORACLE_PUBLIC_URL",s),r("MAD_DESK_URL",s)),p._result=Object.keys(o).length?o:null,p._result}try{p()}catch{}function I(){return process.env.HERMES_HOME?i.resolve(process.env.HERMES_HOME):i.join(u(),".hermes")}function M(){return i.join(u(),".claude.json")}function H(e=process.cwd()){return i.join(e,".mcp.json")}function N(){return process.platform==="darwin"?i.join(u(),"Library","Application Support","Claude","claude_desktop_config.json"):i.join(u(),".config","Claude","claude_desktop_config.json")}function F(){return i.join(u(),".codex","config.toml")}function k(){return i.join(f(),"connectors")}function q(){return i.join(k(),"chatgpt-openapi.json")}function K(){return i.join(f(),"active-chain.json")}function y(e){let o=[i.join(l,"bin",e),i.join(l,"dist","bin",e),i.join(l,"..","bin",e)];return o.find(r=>d.existsSync(r))||o[0]}function G(e){return d.mkdirSync(e,{recursive:!0}),e}var C={homeDir:u,isPublicDesktop:A,oracleConfigDir:f,oracleExecEnvPath:$,oracleDeskJsonPath:_,oracleEnvFilePath:R,loadDeskRouting:p,hermesRoot:I,activeChainPath:K,claudeCodeUserConfigPath:M,claudeCodeProjectMcpPath:H,claudeDesktopConfigPath:N,codexConfigPath:F,chatgptConnectorDir:k,chatgptOpenApiPath:q,packageBin:y,ensureDir:G,PACKAGE_ROOT:l};import{execFile as B}from"node:child_process";import{existsSync as de,mkdirSync as fe,readFileSync as V,realpathSync as me,writeFileSync as he}from"node:fs";import j from"node:path";import{promisify as J}from"node:util";var xe=J(B);var Ee=1440*60*1e3;function v(e,o){let r=t=>String(t).replace(/^v/,"").split("-")[0].split(".").map(n=>Number.parseInt(n,10)||0),s=r(e),a=r(o);for(let t=0;t<Math.max(s.length,a.length);t+=1){let n=s[t]??0,c=a[t]??0;if(n>c)return 1;if(n<c)return-1}return 0}function W(e){let o=e?.stateDir||j.join(process.env.HOME||".",".oracle");return j.join(o,"update-check.json")}function w(e){try{return JSON.parse(V(W(e),"utf8"))}catch{return null}}var z="\u25C7 ORACLE // self-custody multichain agent control plane",X=`START
|
|
3
3
|
oracle open the native Oracle chat (TTY)
|
|
4
4
|
oracle chat open chat explicitly
|
|
5
5
|
oracle setup connect a model and optional messaging
|
|
@@ -20,6 +20,10 @@ PREPARE
|
|
|
20
20
|
oracle prepare build an unsigned swap (alias of route prepare)
|
|
21
21
|
oracle strategy draft|validate|backtest|optimize|evidence \xB7 shadow \xB7 prepare
|
|
22
22
|
|
|
23
|
+
LOCAL SIGNING (SELF-HOSTED)
|
|
24
|
+
oracle sign init|import|status|lock|doctor
|
|
25
|
+
oracle signer short-lived loopback-only signer daemon
|
|
26
|
+
|
|
23
27
|
EXTEND
|
|
24
28
|
oracle data-mcp expose the read-only MCP server
|
|
25
29
|
oracle mcp install <t> wire Oracle into claude-code|claude-desktop|codex|chatgpt
|
|
@@ -35,19 +39,20 @@ SYSTEM
|
|
|
35
39
|
oracle upgrade update installed agent lanes
|
|
36
40
|
|
|
37
41
|
BOUNDARY
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
Default and hosted Oracle remain prepare-only. Self-hosters may explicitly
|
|
43
|
+
enable the encrypted local signer with exact confirmation and policy rails.
|
|
44
|
+
Autonomous execution additionally requires ORACLE_AUTONOMOUS_TRADING=1.
|
|
40
45
|
Use oracle <command> --help for command details.
|
|
41
|
-
`;function
|
|
46
|
+
`;function S(){try{return JSON.parse(h.readFileSync(g.join(l,"package.json"),"utf8")).version||"0.0.0"}catch{return"0.0.0"}}function Q(){let e=g.join(l,"src","cli","commands");return h.existsSync(e)?e:g.join(l,"dist","cli","commands")}async function m(){let e=Q(),o=new Map;if(!h.existsSync(e))return o;let r=h.readdirSync(e).filter(s=>s.endsWith(".mjs")).sort();for(let s of r){let a=g.join(e,s);try{let n=(await import(Y(a).href+`?t=${Date.now()}`)).default;if(!n||typeof n!="object"||typeof n.run!="function")continue;let c=n.name||s.replace(/\.mjs$/,"");o.set(c,{...n,name:c,__file:a})}catch(t){process.env.ORACLE_CLI_DEBUG&&console.error(`oracle: failed loading command ${s}: ${t.message}`)}}return o}function Z(e,o=w()){return!o?.latest||!e||v(o.latest,e)<=0?"":`
|
|
42
47
|
Update available: ${e} \u2192 ${o.latest}. Run 'oracle update'.
|
|
43
|
-
`}function x(e,o){let r=[];r.push(`${z} (v${e})`),r.push(""),r.push(X.trimEnd());let s=[];for(let a of o.values()){if(["help","version"].includes(a.name))continue;!new Set(["init","doctor","chat","model","auth","chain","setup","data","data-mcp","scan","route","prepare","swap","farm","follow","equities","strategy","public","mcp","plugins","harness","upgrade","bootstrap","eval","fees"]).has(a.name)&&a.summary&&s.push(` oracle ${a.name.padEnd(20)} ${a.summary}`)}return s.length&&(r.push(""),r.push("ADDITIONAL"),r.push(...s)),r.join(`
|
|
48
|
+
`}function x(e,o){let r=[];r.push(`${z} (v${e})`),r.push(""),r.push(X.trimEnd());let s=[];for(let a of o.values()){if(["help","version"].includes(a.name))continue;!new Set(["init","doctor","chat","model","auth","chain","setup","data","data-mcp","scan","route","prepare","swap","farm","follow","equities","strategy","public","mcp","plugins","harness","upgrade","bootstrap","eval","fees","sign","signer"]).has(a.name)&&a.summary&&s.push(` oracle ${a.name.padEnd(20)} ${a.summary}`)}return s.length&&(r.push(""),r.push("ADDITIONAL"),r.push(...s)),r.join(`
|
|
44
49
|
`)+`
|
|
45
50
|
`+Z(e)}function L(e,o){let r=[...o.keys()].sort(),s=r.length?r.join(", "):"(none loaded yet)";return`oracle: unknown command '${e}'. Known: ${s}
|
|
46
51
|
Try: oracle --help
|
|
47
|
-
`}function E(e){return{argv:e,root:l,bin:y,paths:C}}async function
|
|
52
|
+
`}function E(e){return{argv:e,root:l,bin:y,paths:C}}async function b(e=[]){let o=S();if(e.includes("--version")||e.includes("-V"))return O();if(e[0]==="--help"||e[0]==="-h"){let n=await m();return process.stdout.write(x(o,n)),0}if(e.length===0){let n=await m();if(ee(e)){let c=n.get("chat");if(c)return c.run(E([]))}return process.stdout.write(x(o,n)),0}if(te(e[0])){let c=(await m()).get("chat");if(c)return c.run(E(e))}let r=e[0],s=e.slice(1);if(r==="version")return O();if(r==="help"){let n=await m();if(s[0]){let c=n.get(s[0]);return c?(process.stdout.write((c.usage||`oracle ${c.name}`)+`
|
|
48
53
|
`),c.summary&&process.stdout.write(c.summary+`
|
|
49
54
|
`),0):(process.stderr.write(L(s[0],n)),1)}return process.stdout.write(x(o,n)),0}let a=await m(),t=a.get(r);if(t){if(s[0]==="--help"||s[0]==="-h")return process.stdout.write((t.usage||`oracle ${t.name}`)+`
|
|
50
55
|
`),t.summary&&process.stdout.write(t.summary+`
|
|
51
56
|
`),0;try{let n=await t.run(E(s));return typeof n=="number"?n:0}catch(n){return process.stderr.write(`oracle ${r}: ${n?.message||n}
|
|
52
|
-
`),1}}return process.stderr.write(L(r,a)),1}async function
|
|
53
|
-
`),0}function ee(e=[],o={stdin:process.stdin,stdout:process.stdout}){return e.length!==0?!1:process.env.ORACLE_FORCE_CHAT==="1"?!0:o.stdin?.isTTY===!0&&o.stdout?.isTTY===!0}function te(e){return["-m","--model","--provider","-q","--query","-Q","--quiet","-c","--continue","-r","--resume","-s","--skills","-t","--toolsets","--reasoning","--tui","--cli","--yolo","--checkpoints","--source","--max-turns"].includes(String(e||""))}var
|
|
57
|
+
`),1}}return process.stderr.write(L(r,a)),1}async function O(){let e=S();return process.stdout.write(`oracle ${e}
|
|
58
|
+
`),0}function ee(e=[],o={stdin:process.stdin,stdout:process.stdout}){return e.length!==0?!1:process.env.ORACLE_FORCE_CHAT==="1"?!0:o.stdin?.isTTY===!0&&o.stdout?.isTTY===!0}function te(e){return["-m","--model","--provider","-q","--query","-Q","--quiet","-c","--continue","-r","--resume","-s","--skills","-t","--toolsets","--reasoning","--tui","--cli","--yolo","--checkpoints","--source","--max-turns"].includes(String(e||""))}var Se=Object.freeze({data:"./desk-server.mjs",public:"./oracle-public-server.mjs","data-mcp":"./oracle-data-mcp.mjs",init:"./oracle-init.mjs",upgrade:"./oracle-upgrade.mjs",scan:"./oracle-scan.mjs",route:"./oracle-route.mjs",equities:"./oracle-equities.mjs"});function ne(){if(!process.argv[1])return!1;try{return U(re(import.meta.url))===U(process.argv[1])}catch{return!1}}async function oe(e=process.argv.slice(2)){return b(e)}if(ne()){let e=await oe();process.exit(typeof e=="number"?e:0)}export{Se as COMMANDS,oe as main};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import f from"node:fs";import b from"node:path";import S from"node:fs";import B from"node:os";import h from"node:path";import{fileURLToPath as M}from"node:url";var U=h.dirname(M(import.meta.url)),ue=h.resolve(U,"../..");function L(){return process.env.ORACLE_FAKE_HOME||B.homedir()}function F(){return process.env.ORACLE_PUBLIC_DESKTOP==="1"}function E(){return F()?process.env.ORACLE_CONFIG_DIR||h.join(L(),".config","oracle"):h.join(L(),".config","oracle")}function $(){return h.join(E(),"desk.json")}function G(){return h.join(E(),"env")}function m({force:e=!1}={}){if(m._done&&!e)return m._result||null;m._done=!0;let i={},r=(s,n)=>{if(n==null)return;let a=String(n).trim();a&&(process.env[s]!=null&&String(process.env[s]).trim()!==""||(process.env[s]=a,i[s]=a))};try{let s=G();if(S.existsSync(s))for(let n of S.readFileSync(s,"utf8").split(/\r?\n/)){let a=n.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*?)\s*$/);if(!a||a[2].startsWith("#"))continue;let c=a[2];(c.startsWith('"')&&c.endsWith('"')||c.startsWith("'")&&c.endsWith("'"))&&(c=c.slice(1,-1)),r(a[1],c)}}catch{}try{let s=$();if(S.existsSync(s)){let n=JSON.parse(S.readFileSync(s,"utf8"));n&&typeof n=="object"&&(r("ORACLE_DESK_URL",n.deskUrl||n.dataUrl||n.url),r("ORACLE_DATA_URL",n.dataUrl||n.deskUrl||n.url),r("ORACLE_API_URL",n.apiUrl||n.dataUrl||n.deskUrl),r("ORACLE_PUBLIC_URL",n.publicUrl||n.deskUrl||n.dataUrl),r("ORACLE_EXEC_URL",n.execUrl),r("MAD_DESK_URL",n.deskUrl||n.dataUrl))}}catch{}let t=process.env.ORACLE_DESK_URL||process.env.ORACLE_DATA_URL||"";return t&&(r("ORACLE_DESK_URL",t),r("ORACLE_DATA_URL",t),r("ORACLE_API_URL",t),r("ORACLE_PUBLIC_URL",t),r("MAD_DESK_URL",t)),m._result=Object.keys(i).length?i:null,m._result}try{m()}catch{}import{createCipheriv as W,createDecipheriv as pe,randomBytes as A,scryptSync as H}from"node:crypto";import o from"node:fs";import d from"node:path";var J="ORACLE-LOCAL-VAULT",q=1,C=Object.freeze({N:1<<17,r:8,p:1}),V=256*1024*1024,ge=16*1024;function K(e,i,r=C){let t=String(e??"");if(t.length<12)throw new Error("local-vault: passphrase must be at least 12 characters");return H(t,i,32,{...r,maxmem:V})}function T(e,i,r={}){let t=Buffer.isBuffer(e)?Buffer.from(e):Buffer.from(String(e));if(!t.length)throw new Error("local-vault: refusing empty key material");let s=A(32),n=A(12),a={magic:J,version:q,kdf:{name:"scrypt",...C,salt:s.toString("base64")},cipher:{name:"aes-256-gcm",iv:n.toString("base64")},meta:{address:r.address?String(r.address).toLowerCase():null,kind:r.kind||"evm",createdAt:new Date().toISOString()}},c=K(i,s);try{let l=W("aes-256-gcm",c,n);l.setAAD(Buffer.from(JSON.stringify(a)));let u=Buffer.concat([l.update(t),l.final()]);return{...a,ciphertext:u.toString("base64"),tag:l.getAuthTag().toString("base64")}}finally{c.fill(0),t.fill(0)}}function z(e){let i=o.lstatSync(e);if(i.isSymbolicLink())throw new Error("local-vault: refusing symlink");if(!i.isFile())throw new Error("local-vault: secret source must be a regular file");if(i.uid!==process.getuid?.())throw new Error("local-vault: secret source must be owned by this user");if(i.nlink!==1)throw new Error("local-vault: refusing hard-linked secret source");if((i.mode&63)!==0)throw new Error("local-vault: secret source must be owner-only (0600)")}function R(e){let i=d.dirname(e),r=o.lstatSync(i);if(!r.isDirectory()||r.isSymbolicLink())throw new Error("local-vault: unsafe destination directory");if(r.uid!==process.getuid?.())throw new Error("local-vault: destination directory must be owned by this user");try{let t=o.lstatSync(e);if(t.isSymbolicLink())throw new Error("local-vault: refusing symlink destination");if(!t.isFile()||t.uid!==process.getuid?.()||t.nlink!==1)throw new Error("local-vault: unsafe destination file");if(t.dev!==r.dev)throw new Error("local-vault: destination device mismatch")}catch(t){if(t.code!=="ENOENT")throw t}}function p(e){let i=o.lstatSync(e);z(e);let r=o.openSync(e,o.constants.O_RDONLY|o.constants.O_NOFOLLOW);try{let t=o.fstatSync(r);if(i.dev!==t.dev||i.ino!==t.ino||t.size>1024*1024)throw new Error("local-vault: secret source changed or is too large");return o.readFileSync(r,"utf8").replace(/[\r\n]+$/,"")}finally{o.closeSync(r)}}function P(e,i){o.mkdirSync(d.dirname(e),{recursive:!0,mode:448}),R(e);let r=d.join(d.dirname(e),`.${d.basename(e)}.${process.pid}.${A(6).toString("hex")}.tmp`),t;try{t=o.openSync(r,o.constants.O_CREAT|o.constants.O_EXCL|o.constants.O_WRONLY|o.constants.O_NOFOLLOW,384),o.writeFileSync(t,JSON.stringify(i,null,2)+`
|
|
2
|
+
`),o.fsyncSync(t),o.closeSync(t),t=void 0;let s=o.openSync(r,o.constants.O_RDONLY|o.constants.O_NOFOLLOW),n;try{n=o.fstatSync(s)}finally{o.closeSync(s)}let a=o.lstatSync(d.dirname(e));if(n.nlink!==1||n.dev!==a.dev||n.uid!==process.getuid?.())throw new Error("local-vault: unsafe temporary file");o.renameSync(r,e),o.chmodSync(e,384);let c=o.openSync(d.dirname(e),"r");try{o.fsyncSync(c)}finally{o.closeSync(c)}}catch(s){if(t!==void 0)try{o.closeSync(t)}catch{}try{o.unlinkSync(r)}catch{}throw s}return e}function g(e,i){o.mkdirSync(d.dirname(e),{recursive:!0,mode:448}),R(e);let r=d.join(d.dirname(e),`.${d.basename(e)}.${process.pid}.${A(6).toString("hex")}.tmp`);try{let t=o.openSync(r,o.constants.O_CREAT|o.constants.O_EXCL|o.constants.O_WRONLY|o.constants.O_NOFOLLOW,384);try{o.writeFileSync(t,String(i)),o.fsyncSync(t)}finally{o.closeSync(t)}o.renameSync(r,e),o.chmodSync(e,384);let s=o.openSync(d.dirname(e),"r");try{o.fsyncSync(s)}finally{o.closeSync(s)}}catch(t){try{o.unlinkSync(r)}catch{}throw t}}import w from"node:path";var Y=new Set(["evm","evm-swap","evm-bridge","btc","sol"]),ye=Object.freeze({swap:5,bridge:5,perps:0,nft:0});var Se=Object.freeze([...Y]);import{createHmac as Q,timingSafeEqual as Fe}from"node:crypto";import{homedir as O}from"node:os";import{join as _}from"node:path";var N=process.env.ORACLE_PUBLIC_DESKTOP==="1",Z=N?process.env.ORACLE_CONFIG_DIR||_(O(),".config","oracle"):_(O(),".config","oracle"),Te=N?Z:_(O(),".config","mad-desk");var je="0".repeat(64),Ie=Object.freeze({PREPARE:"exec.prepare",SIGN:"exec.sign",BROADCAST:"exec.broadcast",POLICY_REJECT:"policy.reject",AUTH_GRANT:"auth.grant",AUTH_OK:"auth.verify.ok",AUTH_FAIL:"auth.verify.fail"});function x(e){return e==null?"null":typeof e!="object"?JSON.stringify(e):Array.isArray(e)?"["+e.map(x).join(",")+"]":"{"+Object.keys(e).sort().map(r=>JSON.stringify(r)+":"+x(e[r])).join(",")+"}"}var Be=Promise.resolve();var ee=(e,i)=>Q("sha256",i).update(x(e)).digest("hex");function re(e,i){if(!e||typeof e!="object"||Array.isArray(e))throw new Error("policy: object required");return{policy:e,integrity:{algorithm:"hmac-sha256-canonical-json-v1",mac:ee(e,i)}}}function D(e,i,r){let t=p(i);g(e,JSON.stringify(re(r,t),null,2)+`
|
|
3
|
+
`)}function j(e){return{vault:w.join(e,"vault.json"),policy:w.join(e,"signer-policy.json"),policyKey:w.join(e,"policy-integrity-key"),ledger:w.join(e,"signer-ledger.json"),token:w.join(e,"signer-token"),lock:w.join(e,"signer.lock")}}import{randomBytes as te}from"node:crypto";var I=`oracle sign init|import|policy|status|lock|doctor
|
|
4
|
+
|
|
5
|
+
oracle sign init [--passphrase-file <0600 file>]
|
|
6
|
+
oracle sign import --key-file <0600 file> [--passphrase-file <0600 file>]
|
|
7
|
+
oracle sign policy --policy-file <0600 JSON file>
|
|
8
|
+
oracle sign status
|
|
9
|
+
oracle sign lock
|
|
10
|
+
oracle sign doctor
|
|
11
|
+
|
|
12
|
+
Secrets are accepted only from a hidden TTY or protected owner-only files.
|
|
13
|
+
They are never accepted as argument values or inherited environment variables.`,k=(e,i)=>{let r=e.indexOf(i);return r>=0?e[r+1]:null};async function ne(e,i={}){if(i.readHidden)return i.readHidden(e);if(!process.stdin.isTTY)throw new Error("passphrase requires a TTY or --passphrase-file");return process.stderr.write(e),process.stdin.setRawMode(!0),process.stdin.resume(),new Promise((r,t)=>{let s="",n=c=>{for(let l of c){if(l===3){a(),t(new Error("cancelled"));return}if(l===13||l===10){a(),process.stderr.write(`
|
|
14
|
+
`),r(s);return}l===127?s=s.slice(0,-1):s+=String.fromCharCode(l)}},a=()=>{process.stdin.off("data",n),process.stdin.setRawMode(!1),process.stdin.pause()};process.stdin.on("data",n)})}async function oe(e,i){let r=k(e,"--passphrase-file");return r?p(r):ne("Vault passphrase: ",i)}async function ie(e=[],i={}){let r=i.out||process.stdout,t=e[0],s=i.configDir||E(),n=j(b.join(s,"signer"));if(!t||t==="help")return r.write(I+`
|
|
15
|
+
`),0;if(t==="status")return r.write(JSON.stringify({initialized:f.existsSync(n.vault),policyConfigured:f.existsSync(n.policy),armed:f.existsSync(n.vault)&&!f.existsSync(n.lock),disarmed:f.existsSync(n.lock)})+`
|
|
16
|
+
`),0;if(t==="lock")return f.mkdirSync(b.dirname(n.lock),{recursive:!0,mode:448}),g(n.lock,`locked
|
|
17
|
+
`),r.write(`Local signer locked.
|
|
18
|
+
`),0;if(t==="doctor"){let u=[];return f.existsSync(n.vault)||u.push("vault missing"),f.existsSync(n.policy)||u.push("policy missing (empty allowlists fail closed)"),r.write(u.length?`Signer not ready: ${u.join("; ")}
|
|
19
|
+
`:`Signer files ready; vault remains locked until a short-lived signer session starts.
|
|
20
|
+
`),u.length?1:0}if(t==="policy"){if(!f.existsSync(n.vault)||!f.existsSync(n.policyKey))throw new Error("initialize the local signer before sealing policy");let u=k(e,"--policy-file");if(!u)throw new Error("policy requires --policy-file <0600 JSON file>");let y;try{y=JSON.parse(p(u))}catch{throw new Error("policy file must contain valid JSON")}for(let v of["surfaces","actions","chains","destinations","owners"])if(!Array.isArray(y[v])||y[v].length===0)throw new Error(`policy: ${v} allowlist is empty`);return D(n.policy,n.policyKey,y),r.write(`Signer policy sealed with local integrity protection.
|
|
21
|
+
`),0}if(t!=="init"&&t!=="import")throw new Error(`unknown subcommand '${t}'`);let a=await oe(e,i),{Wallet:c}=await import("ethers"),l;if(t==="import"){let u=k(e,"--key-file");if(!u)throw new Error("import requires --key-file <0600 file>");l=new c(p(u))}else l=c.createRandom();f.mkdirSync(b.dirname(n.vault),{recursive:!0,mode:448}),P(n.vault,T(Buffer.from(l.privateKey),a,{address:l.address,kind:"evm"})),f.existsSync(n.policyKey)||g(n.policyKey,te(32).toString("hex")+`
|
|
22
|
+
`);try{f.unlinkSync(n.lock)}catch(u){if(u.code!=="ENOENT")throw u}return r.write(`Encrypted local vault initialized for ${l.address}. User-initiated actions are armed; non-empty sealed policy rails are still required and disarm always wins.
|
|
23
|
+
`),0}var hr={name:"sign",summary:"manage the encrypted self-hosted signing vault",usage:I,run:e=>ie(e.argv||[])};export{hr as default,ie as run};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import ur from"node:path";import N from"node:fs";import be from"node:os";import O from"node:path";import{fileURLToPath as Le}from"node:url";var Ce=O.dirname(Le(import.meta.url)),hr=O.resolve(Ce,"../..");function ee(){return process.env.ORACLE_FAKE_HOME||be.homedir()}function ke(){return process.env.ORACLE_PUBLIC_DESKTOP==="1"}function D(){return ke()?process.env.ORACLE_CONFIG_DIR||O.join(ee(),".config","oracle"):O.join(ee(),".config","oracle")}function Te(){return O.join(D(),"desk.json")}function Re(){return O.join(D(),"env")}function v({force:e=!1}={}){if(v._done&&!e)return v._result||null;v._done=!0;let t={},r=(i,o)=>{if(o==null)return;let s=String(o).trim();s&&(process.env[i]!=null&&String(process.env[i]).trim()!==""||(process.env[i]=s,t[i]=s))};try{let i=Re();if(N.existsSync(i))for(let o of N.readFileSync(i,"utf8").split(/\r?\n/)){let s=o.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*?)\s*$/);if(!s||s[2].startsWith("#"))continue;let u=s[2];(u.startsWith('"')&&u.endsWith('"')||u.startsWith("'")&&u.endsWith("'"))&&(u=u.slice(1,-1)),r(s[1],u)}}catch{}try{let i=Te();if(N.existsSync(i)){let o=JSON.parse(N.readFileSync(i,"utf8"));o&&typeof o=="object"&&(r("ORACLE_DESK_URL",o.deskUrl||o.dataUrl||o.url),r("ORACLE_DATA_URL",o.dataUrl||o.deskUrl||o.url),r("ORACLE_API_URL",o.apiUrl||o.dataUrl||o.deskUrl),r("ORACLE_PUBLIC_URL",o.publicUrl||o.deskUrl||o.dataUrl),r("ORACLE_EXEC_URL",o.execUrl),r("MAD_DESK_URL",o.deskUrl||o.dataUrl))}}catch{}let n=process.env.ORACLE_DESK_URL||process.env.ORACLE_DATA_URL||"";return n&&(r("ORACLE_DESK_URL",n),r("ORACLE_DATA_URL",n),r("ORACLE_API_URL",n),r("ORACLE_PUBLIC_URL",n),r("MAD_DESK_URL",n)),v._result=Object.keys(t).length?t:null,v._result}try{v()}catch{}import ye from"node:fs";import b from"node:path";import{randomBytes as ir}from"node:crypto";import Fe from"node:http";import{timingSafeEqual as $e}from"node:crypto";import{createHash as Ie,randomBytes as je,timingSafeEqual as Be}from"node:crypto";var te=new Set(["evm","evm-swap","evm-bridge","btc","sol"]),re=Object.freeze({swap:5,bridge:5,perps:0,nft:0}),I=(e,t,r=256)=>{if(typeof e!="string"||!e||e.length>r)throw new Error(`envelope: bounded ${t} required`);return e},E=(e,t)=>{try{let r=BigInt(e);if(r<0n)throw 0;return r}catch{throw new Error(`envelope: ${t} must be a non-negative integer`)}},Pe=(e,t)=>{if(!Array.isArray(e)||e.length===0)throw new Error(`policy: ${t} allowlist is empty`);return e},g=(e,t,r)=>{if(!Pe(t,r).map(n=>String(n).toLowerCase()).includes(String(e).toLowerCase()))throw new Error(`policy: ${r} not allowed`)};function Ne(e,{localsWaiver:t=!1}={}){if(!(e in re))throw new Error("fee: unknown action");return t?0:re[e]}function H(e,t,{nowMs:r=Date.now()}={}){if(!e||e.version!==1)throw new Error("envelope: version 1 required");let n=I(e.surface,"surface",16);if(!te.has(n))throw new Error("envelope: unsupported public surface");let i=I(e.action,"action",32);if(g(n,t.surfaces,"surface"),g(i,t.actions,"action"),g(e.owner,t.owners,"owner"),g(e.destination,t.destinations,"destination"),!Number.isFinite(e.expiresAtMs)||e.expiresAtMs<=r||e.expiresAtMs-r>Number(t.maxTtlMs))throw new Error("policy: TTL outside bounds");if(!Number.isFinite(e.slippageBps)||e.slippageBps<0||e.slippageBps>Number(t.maxSlippageBps))throw new Error("policy: slippage exceeds cap");let o=e.artifact;if(!o||typeof o!="object"||Array.isArray(o))throw new Error("envelope: structured artifact required");if(n.startsWith("evm")){g(Number(o.chainId),t.chains,"chain");let u=String(o.data||"0x").slice(0,10).toLowerCase();if(g(u,t.selectors,"selector"),String(o.to).toLowerCase()!==String(e.destination).toLowerCase())throw new Error("envelope: destination mismatch");if(E(o.value??0,"value")>E(t.maxValueWei,"maxValueWei"))throw new Error("policy: value exceeds cap");let p=E(o.gasLimit,"gasLimit"),a=E(o.maxFeePerGas??o.gasPrice,"feePerGas");if(p>E(t.maxGasLimit,"maxGasLimit")||p*a>E(t.maxFeeWei,"maxFeeWei"))throw new Error("policy: gas/fee exceeds cap")}else if(n==="btc"){if(I(o.psbt,"PSBT",1e5),g(o.network,t.chains,"chain"),!Array.isArray(o.outputs)||o.outputs.length===0||o.outputs.some(u=>!u.address||E(u.valueSats,"output value")<0n))throw new Error("envelope: bounded BTC outputs required")}else if(n==="sol"){if(I(o.messageBase64,"Solana message",1e5),g(o.cluster,t.chains,"chain"),!Array.isArray(o.programIds)||o.programIds.length===0)throw new Error("envelope: Solana program ids required");for(let u of o.programIds)g(u,t.programs,"program")}else throw new Error("envelope: unsupported public surface");let s=Ne(i,{localsWaiver:e.fee?.waiver==="locals-only-verified"});if(Number(e.fee?.oracleFeeBps)!==s)throw new Error("fee: final artifact fee mismatch");return!0}var De=Object.freeze([...te]);function ne(e={}){let t={};for(let r of De){let n=e[r];if(n){if(typeof n.sign!="function"||typeof n.broadcast!="function")throw new Error(`adapter: ${r} requires sign and broadcast functions`);t[r]=Object.freeze({sign:(i,o)=>n.sign(structuredClone(i),structuredClone(o)),broadcast:(i,o,s)=>n.broadcast(i,structuredClone(o),structuredClone(s))})}}return Object.freeze(t)}function J(e,t){let r=e?.surface||"evm",n=t?.[r];if(!n)throw new Error(`adapter: ${r} is not configured on this self-host`);return n}var j=e=>String(e||"").toLowerCase(),A=(e,t)=>{try{let r=BigInt(e??0);if(r<0n)throw 0;return r}catch{throw new Error(`policy: ${t} must be a non-negative integer`)}},C=(e,t,r,n=String)=>{if(!Array.isArray(t)||t.length===0)throw new Error(`policy: ${r} allowlist is empty`);if(!t.map(n).includes(n(e)))throw new Error(`policy: ${r} not allowed`)};function y(e){return Ie("sha256").update(JSON.stringify(e)).digest("hex")}function Me(e){let t=String(e||"").toLowerCase();return!t.startsWith("0x095ea7b3")||t.length!==138?null:{spender:`0x${t.slice(34,74)}`,amount:BigInt(`0x${t.slice(74)}`)}}function oe(e,t,{nowMs:r=Date.now()}={}){if(!e||!t)throw new Error("policy: artifact and policy required");C(Number(e.chainId),t.chains,"chain",Number),C(e.to,t.destinations,"destination",j),C(String(e.data||"0x").slice(0,10),t.selectors,"selector",s=>String(s).toLowerCase()),C(e.from,t.owners,"owner",j);let n=Me(e.data);if(String(e.data||"").slice(0,10).toLowerCase()==="0x095ea7b3"){if(!n)throw new Error("policy: malformed approval calldata");if(C(n.spender,t.spenders,"spender",j),n.amount>A(t.maxApproval,"maxApproval"))throw new Error("policy: approval exceeds cap")}if(A(e.value,"value")>A(t.maxValueWei,"maxValueWei"))throw new Error("policy: value exceeds cap");let i=A(e.gasLimit,"gasLimit");if(i>A(t.maxGasLimit,"maxGasLimit"))throw new Error("policy: gas exceeds cap");if(A(e.maxFeePerGas??e.gasPrice,"feePerGas")*i>A(t.maxFeeWei,"maxFeeWei"))throw new Error("policy: fee exceeds cap");if(!Number.isFinite(e.slippageBps)||e.slippageBps<0||e.slippageBps>Number(t.maxSlippageBps))throw new Error("policy: slippage attestation exceeds cap");if(!Number.isFinite(e.expiresAtMs)||e.expiresAtMs<=r)throw new Error("policy: artifact expired");return!0}var B=class{#e=new Map;constructor({now:t=Date.now}={}){this.now=t}issue({artifact:t,owner:r,ttlMs:n=6e4}){if(n<=0||n>12e4)throw new Error("confirmation: ttl out of range");let i=je(32).toString("hex");return this.#e.set(i,{digest:y(t),owner:j(r),expiresAtMs:this.now()+n}),i}consume(t,r){let n=this.#e.get(String(t));if(!n)throw new Error("confirmation: used or unknown nonce");if(n.expiresAtMs<=this.now())throw this.#e.delete(String(t)),new Error("confirmation: expired nonce");let i=Buffer.from(n.digest,"hex"),o=Buffer.from(y(r),"hex");if(!Be(i,o))throw new Error("confirmation: artifact mismatch");return this.#e.delete(String(t)),n}clear(){this.#e.clear()}};function Ue(e={},t={}){let r={};for(let n of new Set([...Object.keys(e),...Object.keys(t)]))r[n]=(BigInt(t[n]??0)-BigInt(e[n]??0)).toString();return r}async function ie(e,t,r){if(!r.isArmed())throw new Error("signer: disarmed");let n=e?.version===1&&e?.surface?e:null,i=n?n.artifact:e,o=typeof r.readPolicy=="function"?r.readPolicy():r.policy,s=o?.confirmationMode||"exact-human";if(r.autonomous===!0){if(r.env?.ORACLE_AUTONOMOUS_TRADING!=="1")throw new Error("signer: autonomous execution requires ORACLE_AUTONOMOUS_TRADING=1");if(typeof r.verifyTriggerAttestation!="function"||await r.verifyTriggerAttestation(n?.triggerAttestation,e)!==!0)throw new Error("signer: autonomous trigger attestation required")}else if(s==="exact-human")r.confirmations.consume(t,e);else if(s!=="auto-within-rails")throw new Error("signer: unsupported confirmation mode");let u=y(e),p=()=>typeof r.nowMs=="function"?r.nowMs():r.nowMs??Date.now();n?H(n,o,{nowMs:p()}):oe(e,o,{nowMs:p()}),typeof r.reserveLedger=="function"&&r.reserveLedger(n||e,o);let a=await r.sign(i,n);if(!r.isArmed())throw new Error("signer: disarmed before broadcast");if(y(e)!==u)throw new Error("signer: artifact mutated after signing");o=typeof r.readPolicy=="function"?r.readPolicy():r.policy,n?H(n,o,{nowMs:p()}):oe(e,o,{nowMs:p()});let f=await r.broadcast(a,i,n),c=f?.status,L=c===1||c==="0x1"?"success":c===0||c==="0x0"?"failed":"pending",x=f?.verifiedBefore??f?.before,S=f?.verifiedAfter??f?.after;return{prepared:e,signed:a,receipt:{transactionHash:f?.hash||f?.transactionHash||null,status:L,balanceDeltaVerified:!!(f?.verifiedBefore&&f?.verifiedAfter),delta:Ue(x,S)}}}var se=new Set(["127.0.0.1","::1","localhost"]),Ge=(e,t)=>{let r=Buffer.from(String(e)),n=Buffer.from(String(t));return r.length===n.length&&$e(r,n)},m=(e,t,r)=>{let n=JSON.stringify(r);e.writeHead(t,{"content-type":"application/json","cache-control":"no-store","content-length":Buffer.byteLength(n)}),e.end(n)},ae=e=>new Promise((t,r)=>{let n=0,i=[],o=!1;e.on("data",s=>{n+=s.length,n>64*1024&&!o?(o=!0,r(new Error("request refused")),e.resume()):o||i.push(s)}),e.on("end",()=>{if(!o)try{t(JSON.parse(Buffer.concat(i).toString()||"{}"))}catch{r(new Error("request refused"))}}),e.on("error",r)});function ce(e={}){let t=e.host||"127.0.0.1";if(!se.has(t))throw new Error("signer: loopback bind required");if(String(e.token||"").length<32)throw new Error("signer: strong auth token required");let r=e.confirmations||new B,n=e.origin||`http://${t}`;return Fe.createServer(async(o,s)=>{try{let u=String(o.headers.authorization||"").replace(/^Bearer /i,"");if(!Ge(u,e.token))return m(s,401,{error:"unauthorized"});let p=String(o.headers.host||"").replace(/^\[|\](?=:|$)/g,"").split(":")[0];if(!se.has(p))return m(s,403,{error:"host refused"});if(o.headers.origin&&o.headers.origin!==n)return m(s,403,{error:"origin refused"});if(o.method==="GET"&&o.url==="/status")return m(s,200,{ok:!0,armed:e.isArmed?.()===!0,sessionExpiresAtMs:e.sessionExpiresAtMs||null});if(o.method==="POST"&&o.url==="/disarm")return e.disarm?.(),r.clear(),m(s,200,{ok:!0,armed:!1});if(!["GET","POST"].includes(o.method))return m(s,405,{error:"request refused"});if(o.method==="POST"&&String(o.headers["content-type"]||"").split(";",1)[0].trim().toLowerCase()!=="application/json")return m(s,415,{error:"request refused"});if(o.method==="POST"&&o.url==="/confirm"){let a=await ae(o),f=y(a.artifact);if(e.autonomous!==!0){if(typeof e.confirmArtifact!="function")return m(s,403,{error:"local human confirmation unavailable"});if(await e.confirmArtifact(a.artifact,f)!==!0)return m(s,400,{error:"exact human confirmation refused"})}return m(s,200,{confirmationNonce:r.issue({artifact:a.artifact,owner:a.artifact?.from,ttlMs:a.ttlMs})})}if(o.method==="POST"&&o.url==="/execute"){let a=await ae(o),f=await ie(a.artifact,a.confirmationNonce,{...e,confirmations:r});return m(s,200,f)}return m(s,404,{error:"not found"})}catch(u){return e.onError?.(String(u?.message||u)),m(s,400,{error:"request refused"})}})}import{createCipheriv as br,createDecipheriv as We,randomBytes as He,scryptSync as Je}from"node:crypto";import l from"node:fs";import _ from"node:path";var Ve="ORACLE-LOCAL-VAULT",qe=1,U=Object.freeze({N:1<<17,r:8,p:1}),ze=256*1024*1024,Ke=16*1024;function M(e,t,r,{max:n=t}={}){if(typeof e!="string"||!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e))throw new Error(`local-vault: invalid ${r} encoding`);let i=Buffer.from(e,"base64");if(i.toString("base64")!==e||i.length<t||i.length>n)throw new Error(`local-vault: invalid ${r} length`);return i}function Ye(e,t,r=U){let n=String(e??"");if(n.length<12)throw new Error("local-vault: passphrase must be at least 12 characters");return Je(n,t,32,{...r,maxmem:ze})}function ue(e,t){if(e?.magic!==Ve||e?.version!==qe)throw new Error("local-vault: unsupported vault file");if(e?.kdf?.name!=="scrypt"||e?.cipher?.name!=="aes-256-gcm")throw new Error("local-vault: unsupported cryptography");if(e.kdf.N!==U.N||e.kdf.r!==U.r||e.kdf.p!==U.p)throw new Error("local-vault: unsupported KDF parameters");if(Object.keys(e.kdf).sort().join(",")!=="N,name,p,r,salt"||Object.keys(e.cipher).sort().join(",")!=="iv,name")throw new Error("local-vault: unexpected cryptography field");if(!e.meta||Object.keys(e.meta).some(c=>!["address","kind","createdAt"].includes(c))||String(e.meta.kind||"").length>16||String(e.meta.address||"").length>128||!Number.isFinite(Date.parse(e.meta.createdAt)))throw new Error("local-vault: invalid metadata");let r=["magic","version","kdf","cipher","meta","ciphertext","tag"];if(Object.keys(e).some(c=>!r.includes(c)))throw new Error("local-vault: unexpected vault field");let n=M(e.kdf.salt,32,"salt"),i=M(e.cipher.iv,12,"iv"),o=M(e.tag,16,"tag"),s=M(e.ciphertext,1,"ciphertext",{max:Ke}),{ciphertext:u,tag:p,...a}=e,f=Ye(t,n);try{let c=We("aes-256-gcm",f,i);return c.setAAD(Buffer.from(JSON.stringify(a))),c.setAuthTag(o),Buffer.concat([c.update(s),c.final()])}catch{throw new Error("local-vault: wrong passphrase or tampered vault")}finally{f.fill(0)}}function le(e){let t=l.lstatSync(e);if(t.isSymbolicLink())throw new Error("local-vault: refusing symlink");if(!t.isFile())throw new Error("local-vault: secret source must be a regular file");if(t.uid!==process.getuid?.())throw new Error("local-vault: secret source must be owned by this user");if(t.nlink!==1)throw new Error("local-vault: refusing hard-linked secret source");if((t.mode&63)!==0)throw new Error("local-vault: secret source must be owner-only (0600)")}function Xe(e){let t=_.dirname(e),r=l.lstatSync(t);if(!r.isDirectory()||r.isSymbolicLink())throw new Error("local-vault: unsafe destination directory");if(r.uid!==process.getuid?.())throw new Error("local-vault: destination directory must be owned by this user");try{let n=l.lstatSync(e);if(n.isSymbolicLink())throw new Error("local-vault: refusing symlink destination");if(!n.isFile()||n.uid!==process.getuid?.()||n.nlink!==1)throw new Error("local-vault: unsafe destination file");if(n.dev!==r.dev)throw new Error("local-vault: destination device mismatch")}catch(n){if(n.code!=="ENOENT")throw n}}function w(e){let t=l.lstatSync(e);le(e);let r=l.openSync(e,l.constants.O_RDONLY|l.constants.O_NOFOLLOW);try{let n=l.fstatSync(r);if(t.dev!==n.dev||t.ino!==n.ino||n.size>1024*1024)throw new Error("local-vault: secret source changed or is too large");return l.readFileSync(r,"utf8").replace(/[\r\n]+$/,"")}finally{l.closeSync(r)}}function fe(e){return le(e),JSON.parse(l.readFileSync(e,"utf8"))}function k(e,t){l.mkdirSync(_.dirname(e),{recursive:!0,mode:448}),Xe(e);let r=_.join(_.dirname(e),`.${_.basename(e)}.${process.pid}.${He(6).toString("hex")}.tmp`);try{let n=l.openSync(r,l.constants.O_CREAT|l.constants.O_EXCL|l.constants.O_WRONLY|l.constants.O_NOFOLLOW,384);try{l.writeFileSync(n,String(t)),l.fsyncSync(n)}finally{l.closeSync(n)}l.renameSync(r,e),l.chmodSync(e,384);let i=l.openSync(_.dirname(e),"r");try{l.fsyncSync(i)}finally{l.closeSync(i)}}catch(n){try{l.unlinkSync(r)}catch{}throw n}}import{createHmac as Qe,timingSafeEqual as er}from"node:crypto";import{homedir as V}from"node:os";import{join as q}from"node:path";var de=process.env.ORACLE_PUBLIC_DESKTOP==="1",Ze=de?process.env.ORACLE_CONFIG_DIR||q(V(),".config","oracle"):q(V(),".config","oracle"),Pr=de?Ze:q(V(),".config","mad-desk");var Br="0".repeat(64),Mr=Object.freeze({PREPARE:"exec.prepare",SIGN:"exec.sign",BROADCAST:"exec.broadcast",POLICY_REJECT:"policy.reject",AUTH_GRANT:"auth.grant",AUTH_OK:"auth.verify.ok",AUTH_FAIL:"auth.verify.fail"});function F(e){return e==null?"null":typeof e!="object"?JSON.stringify(e):Array.isArray(e)?"["+e.map(F).join(",")+"]":"{"+Object.keys(e).sort().map(r=>JSON.stringify(r)+":"+F(e[r])).join(",")+"}"}var Ur=Promise.resolve();var rr=864e5,$=(e,t)=>Qe("sha256",t).update(F(e)).digest("hex"),z=(e,t)=>{let r=Buffer.from(String(e)),n=Buffer.from(String(t));return r.length===n.length&&er(r,n)};function tr(e,t){if(e?.integrity?.algorithm!=="hmac-sha256-canonical-json-v1"||!z(e.integrity.mac,$(e.policy,t)))throw new Error("policy: integrity verification failed");return structuredClone(e.policy)}function me(e,t){let r=w(t),n;try{n=JSON.parse(w(e))}catch(i){throw new Error(`policy: cannot read sealed policy: ${i?.message||i}`)}return tr(n,r)}function nr(e,t,r){try{let n=JSON.parse(w(e));if(!z(n?.integrity?.mac,$(n.ledger,t)))throw new Error("integrity verification failed");let i=n.ledger;if(!Number.isFinite(i?.windowStartMs)||!Array.isArray(i?.entries))throw new Error("invalid ledger");return r-i.windowStartMs>=rr?{windowStartMs:r,entries:[]}:i}catch(n){if(n?.code==="ENOENT")return{windowStartMs:r,entries:[]};throw new Error(`ledger: corrupt or unreadable: ${n?.message||n}`)}}function pe(e,t){return e.reduce((r,n)=>r+BigInt(n[t]??0),0n)}function he({file:e,keyFile:t,tradeId:r,value:n=0,notional:i=0,policy:o,nowMs:s=Date.now()}){if(!/^[A-Za-z0-9._:-]{1,128}$/.test(String(r||"")))throw new Error("ledger: bounded tradeId required");let u=w(t),p=nr(e,u,s);if(p.entries.some(S=>S.tradeId===r))throw new Error("ledger: replayed tradeId");let a=BigInt(n),f=BigInt(i),c=BigInt(o.maxDailyValueWei),L=BigInt(o.maxDailyNotional??o.maxDailyValueWei),x=BigInt(o.maxPerTrade??o.maxValueWei);if(a<0n||f<0n||f>x)throw new Error("ledger: per-trade cap exceeded");if(pe(p.entries,"value")+a>c||pe(p.entries,"notional")+f>L)throw new Error("ledger: daily cap exceeded");return p.entries.push({tradeId:String(r),value:a.toString(),notional:f.toString(),atMs:s}),k(e,JSON.stringify({ledger:p,integrity:{algorithm:"hmac-sha256-canonical-json-v1",mac:$(p,u)}})+`
|
|
2
|
+
`),structuredClone(p)}function ge(e,t,r,n=Date.now()){if(e?.algorithm!=="hmac-sha256-canonical-json-v1"||!/^[A-Za-z0-9._:-]{1,128}$/.test(String(e?.triggerId||""))||!Number.isFinite(e.issuedAtMs)||!Number.isFinite(e.expiresAtMs)||e.issuedAtMs>n||e.expiresAtMs<=n||e.expiresAtMs-e.issuedAtMs>12e4)return!1;let i={triggerId:e.triggerId,issuedAtMs:e.issuedAtMs,expiresAtMs:e.expiresAtMs,artifactDigest:y(t)};return z(e.mac,$(i,w(r)))}import{spawnSync as or}from"node:child_process";function we(e="oracle-local-signer",t=process.env.USER||process.env.USERNAME||"oracle"){let r,n;if(process.platform==="darwin")r="/usr/bin/security",n=["find-generic-password","-s",e,"-a",t,"-w"];else if(process.platform==="linux")r="secret-tool",n=["lookup","service",e,"account",t];else throw new Error("signer: OS credential lookup is supported on macOS and Linux; use a protected passphrase file on this platform");let i=or(r,n,{encoding:"utf8",stdio:["ignore","pipe","ignore"],env:{PATH:process.env.PATH,HOME:process.env.HOME,USER:process.env.USER,LOGNAME:process.env.LOGNAME,DBUS_SESSION_BUS_ADDRESS:process.env.DBUS_SESSION_BUS_ADDRESS}}),o=String(i.stdout||"").replace(/[\r\n]+$/,"");if(i.status!==0||o.length<12)throw new Error("signer: OS credential passphrase unavailable");return o}function sr(e){return{vault:b.join(e,"vault.json"),policy:b.join(e,"signer-policy.json"),policyKey:b.join(e,"policy-integrity-key"),ledger:b.join(e,"signer-ledger.json"),token:b.join(e,"signer-token"),lock:b.join(e,"signer.lock")}}function ar(e){try{return w(e)}catch(r){if(r.code!=="ENOENT")throw r}let t=ir(32).toString("hex");return k(e,t+`
|
|
3
|
+
`),t}async function Se({configDir:e,passphraseFile:t,credentialService:r,surfaceTransports:n={},host:i="127.0.0.1",port:o=9462,sessionMs:s=12e4}={}){let{JsonRpcProvider:u,Wallet:p}=await import("ethers");if(!t&&!r)throw new Error("signer: --credential-service or --passphrase-file is required for non-interactive service start");if(s<1e3||s>12e4)throw new Error("signer: session must be at most 120 seconds");let a=sr(e),f=()=>Object.freeze(me(a.policy,a.policyKey)),c=f();for(let d of["surfaces","actions","chains","destinations","spenders","owners"])if(!Array.isArray(c[d])||c[d].length===0)throw new Error(`signer: ${d} allowlist is empty`);if(c.surfaces.some(d=>String(d).startsWith("evm"))&&(!Array.isArray(c.selectors)||c.selectors.length===0))throw new Error("signer: selectors allowlist is empty");if(c.surfaces.includes("sol")&&(!Array.isArray(c.programs)||c.programs.length===0))throw new Error("signer: programs allowlist is empty");if(c.surfaces.some(d=>d==="hl"||d==="poly"))throw new Error("signer: hl/poly are not public signer families");let L=r?we(r):w(t),x=ue(fe(a.vault),L),S;try{S=new p(x.toString())}finally{x.fill(0)}let T=new u(c.rpcUrl,Number(c.chains?.[0])),Ae=S.connect(T),W={sign:d=>Ae.signTransaction(d),broadcast:async(d,h)=>{let P={native:(await T.getBalance(h.from)).toString()},Z=await T.broadcastTransaction(d),Q=await Z.wait(),_e={native:(await T.getBalance(h.from)).toString()};return{hash:Q?.hash??Z.hash,status:Q?.status??null,verifiedBefore:P,verifiedAfter:_e}}},K=ne({evm:W,"evm-swap":W,"evm-bridge":W,...n}),xe=Date.now(),Y=xe+s,ve=()=>ye.existsSync(a.vault)&&!ye.existsSync(a.lock)&&Date.now()<Y,Oe=()=>(k(a.lock,`disarmed ${new Date().toISOString()}
|
|
4
|
+
`),!0),R=ce({host:i,port:o,token:ar(a.token),policy:c,readPolicy:f,isArmed:ve,disarm:Oe,sessionExpiresAtMs:Y,nowMs:Date.now,sign:(d,h)=>J(h,K).sign(d,h),broadcast:(d,h,P)=>J(P,K).broadcast(d,h,P),confirmArtifact:cr,reserveLedger:(d,h)=>he({file:a.ledger,keyFile:a.policyKey,tradeId:d.tradeId,value:d.artifact?.value??0,notional:d.notional??d.artifact?.value??0,policy:h}),verifyTriggerAttestation:(d,h)=>ge(d,h,a.policyKey),autonomous:process.env.ORACLE_AUTONOMOUS_TRADING==="1",env:process.env}),X=setTimeout(()=>R.close(),s);return X.unref(),R.on("close",()=>{clearTimeout(X),S=null}),R.listen(o,i),R}async function cr(e,t){return process.stdin.isTTY?(process.stderr.write(`
|
|
5
|
+
Confirm exact action
|
|
6
|
+
chain=${e.chainId} owner=${e.from}
|
|
7
|
+
to=${e.to} selector=${String(e.data||"0x").slice(0,10)} value=${e.value}
|
|
8
|
+
gas=${e.gasLimit} maxFeePerGas=${e.maxFeePerGas??e.gasPrice}
|
|
9
|
+
slippageBps=${e.slippageBps} expiresAtMs=${e.expiresAtMs}
|
|
10
|
+
Type digest ${t}: `),process.stdin.resume(),new Promise(r=>{let n=i=>{process.stdin.off("data",n),process.stdin.pause(),r(String(i).trim()===t)};process.stdin.once("data",n)})):!1}var Ee=`oracle signer (--credential-service oracle-local-signer | --passphrase-file <0600 file>) [--port 9462] [--session-seconds 120]
|
|
11
|
+
Starts a loopback-only, short-lived local signer. No secret may be passed directly or through the environment.`,G=(e,t,r)=>{let n=e.indexOf(t);return n>=0?e[n+1]:r};async function lr(e=[],t={}){if(e.includes("--help"))return(t.out||process.stdout).write(Ee+`
|
|
12
|
+
`),0;let r=G(e,"--passphrase-file"),n=await Se({configDir:ur.join(t.configDir||D(),"signer"),passphraseFile:r,credentialService:G(e,"--credential-service"),port:Number(G(e,"--port",9462)),sessionMs:Number(G(e,"--session-seconds",120))*1e3});return(t.out||process.stdout).write(`Oracle signer listening on loopback for a short-lived session.
|
|
13
|
+
`),await new Promise(i=>n.on("close",i)),0}var st={name:"signer",summary:"run the short-lived loopback signer",usage:Ee,run:e=>lr(e.argv||[])};export{st as default,lr as run};
|