@ambprotocol/ride-cli 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +39 -0
- package/amb +479 -0
- package/package.json +24 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
TADA CLI — END USER LICENSE AGREEMENT
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TADA MOBILITY PTE. LTD. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software (the "CLI") is proprietary. It is published on a public
|
|
6
|
+
registry for distribution convenience only; publication does not place it
|
|
7
|
+
in the public domain and does not make it open source.
|
|
8
|
+
|
|
9
|
+
1. GRANT. Subject to this Agreement, TADA MOBILITY PTE. LTD. grants you a
|
|
10
|
+
personal, non-exclusive, non-transferable, revocable license to download,
|
|
11
|
+
install, and run the CLI for the sole purpose of accessing TADA/Throo
|
|
12
|
+
services through your own account.
|
|
13
|
+
|
|
14
|
+
2. RESTRICTIONS. You may not (a) redistribute, publish, sell, sublicense, or
|
|
15
|
+
otherwise make the CLI available to third parties; (b) modify, adapt, or
|
|
16
|
+
create derivative works of the CLI; (c) reverse engineer, decompile, or
|
|
17
|
+
disassemble the CLI, or attempt to derive its source code, except to the
|
|
18
|
+
extent this restriction is prohibited by applicable law; or (d) remove or
|
|
19
|
+
obscure any proprietary notice.
|
|
20
|
+
|
|
21
|
+
3. SERVICES. Use of the CLI to access TADA/Throo services is additionally
|
|
22
|
+
governed by the TADA/Throo Terms of Service and Privacy Policy. Nothing in
|
|
23
|
+
this Agreement grants any right to the services themselves.
|
|
24
|
+
|
|
25
|
+
4. NO WARRANTY. THE CLI IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
26
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
27
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
|
|
28
|
+
|
|
29
|
+
5. LIMITATION OF LIABILITY. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO
|
|
30
|
+
EVENT SHALL TADA MOBILITY PTE. LTD. BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
|
31
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE,
|
|
32
|
+
ARISING FROM, OUT OF, OR IN CONNECTION WITH THE CLI OR ITS USE.
|
|
33
|
+
|
|
34
|
+
6. TERMINATION. This license terminates automatically if you breach any term.
|
|
35
|
+
On termination you must stop using the CLI and delete all copies.
|
|
36
|
+
|
|
37
|
+
The TADA Ride Skill bundle (SKILL.md, reference documentation, and the skill
|
|
38
|
+
scripts) is distributed separately under the MIT No Attribution (MIT-0)
|
|
39
|
+
License and is not subject to this Agreement.
|
package/amb
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var y={services:{authServerUrl:"https://backend.clutchwalletserver.xyz",authWebappUrl:"https://tada-agent.clutchwalletserver.xyz",privyAppId:"cmoh8lukp03r90cl5vn7go5w5",cryptoGatewayUrl:"https://backend.clutchwalletserver.xyz/tada-ton",memberGatewayUrls:{SG:"https://cli.tadaapis.com",US:"https://cli-us.tadaapis.com"},defaultRegion:"SG"},networks:{depositNetworks:["ETHEREUM"],paymentNetwork:"BASE",paymentCurrency:"USDC_base",paymentUsdcAddress:"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",bridgeSource:"ETHEREUM",bridgeDestination:"BASE"},providers:{tada:{memberClientId:"tada-rider-cli",memberClientSecret:"5a04db79fc131fa632cf1d4ac5659ca49349f1746e949b5dde7649e4bd58547c"}},artifact:{cliVersion:"1.4.0",gitSha:"2c401ca5"},install:{mode:"npm",repoBranch:"main",expectedCliSha:"2c401ca5",minimumCliVersion:"1.3.0"},bridgePolicy:{minimumUsdc:"1",maxFeeBufferPct:20,burnGraceBlocks:30,mintMaxAttempts:5,leaseSeconds:120,logChunkBlocks:2e3}};import Bo from"fs";import{spawn as Yu}from"child_process";function Tn(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Sn(e){return e==null?"null":String(e)}function Ws(e,t){let r=" ".repeat(t),n=[];for(let s of e)if(Tn(s)||Array.isArray(s)){let i=An(s,t+1),o=i[0].slice((t+1)*2);n.push(`${r}- ${o}`,...i.slice(1))}else typeof s=="string"&&s.includes(`
|
|
3
|
+
`)?n.push(`${r}- |`,...s.split(`
|
|
4
|
+
`).map(i=>`${r} ${i}`)):s===""?n.push(`${r}- (none)`):n.push(`${r}- ${Sn(s)}`);return n}function An(e,t){let r=" ".repeat(t);if(Tn(e)){let n=Object.keys(e);if(n.length===0)return[`${r}(none)`];let s=[];for(let i of n){let o=e[i];Tn(o)?Object.keys(o).length===0?s.push(`${r}${i}: (none)`):(s.push(`${r}${i}:`),s.push(...An(o,t+1))):Array.isArray(o)?o.length===0?s.push(`${r}${i}: (none)`):(s.push(`${r}${i}:`),s.push(...Ws(o,t+1))):typeof o=="string"&&o.includes(`
|
|
5
|
+
`)?s.push(`${r}${i}: |`,...o.split(`
|
|
6
|
+
`).map(a=>`${r} ${a}`)):o===""?s.push(`${r}${i}: (none)`):s.push(`${r}${i}: ${Sn(o)}`)}return s}return Array.isArray(e)?e.length===0?[`${r}(none)`]:Ws(e,t):typeof e=="string"&&e.includes(`
|
|
7
|
+
`)?[`${r}|`,...e.split(`
|
|
8
|
+
`).map(n=>`${r} ${n}`)]:[`${r}${Sn(e)}`]}function Vs(e){return An(e,0).join(`
|
|
9
|
+
`)}var Jt="json";function Js(e){Jt=e}function wt(){return Jt}function Ks(e){return{json:e.includes("--json"),argv:e.filter(t=>t!=="--json")}}function w(e){console.log(Jt==="json"?JSON.stringify(e,null,2):Vs(e))}function g(e){console.error(Jt==="json"?JSON.stringify(e):Vs(e))}import Fn from"fs";import Ge from"path";import{fileURLToPath as Qd}from"url";import Ys from"os";import Ze from"path";function pd(e){return e.startsWith("~/")?Ze.join(Ys.homedir(),e.slice(2)):Ze.resolve(e)}function F(){return pd(process.env.AMB_RIDE_STATE_DIR??Ze.join(Ys.homedir(),".amb"))}function et(){return Ze.join(F(),"state","data")}function Xs(){return Ze.join(F(),"state","keys")}function Kt(e){return Ze.join(e,".tada-state")}import D from"fs";import le from"path";import jd from"crypto";import Ln from"crypto";import S from"fs";import x from"path";function zs(e,t){let r=t(e.legacyRoot),n=t(e.ambientRoot),s=t(e.backupRoot);return t(e.stagingRoot)?"conflict":!r&&!n&&!s?"fresh":r&&!n&&!s?"legacy_candidate":!r&&n&&!s?"ambient_fresh":!r&&n&&s?"already_migrated":r&&n&&!s?"resume_before_backup":"conflict"}var md={fresh:["initialize_ambient"],legacy_candidate:["migrate_legacy"],ambient_fresh:["use_ambient"],already_migrated:["validate_migrated"],resume_before_backup:["resume_backup"]};function Qs(e,t){if(t==="conflict")throw new Error("MIGRATION_CONFLICT: filesystem state is not safe to migrate");return{state:t,paths:{...e},actions:[...md[t]]}}var ei={TADA_AGENT_PASSPHRASE:"AMB_RIDE_PASSPHRASE",TADA_AGENT_STATE_DIR:"AMB_RIDE_STATE_DIR",TADA_AGENT_LOG_LEVEL:"AMB_RIDE_LOG_LEVEL",TADA_OPENCLAW_CLI:"AMB_RIDE_OPENCLAW_CLI"},Zs=new Set(Object.values(ei)),gd=/^(?:TADA_AGENT_|TADA_OPENCLAW_)/,fd=/^TADA_AGENT_DEPOSIT_RPC_URL_([A-Z0-9]+(?:_[A-Z0-9]+)*)$/,_d=/^TADA_AGENT_BRIDGE_RPC_URL_([A-Z0-9]+(?:_[A-Z0-9]+)*)$/,yd=/^[A-Z0-9]+(?:_[A-Z0-9]+)*$/;function Ne(e,t){throw new Error(`${e}: ${t}`)}function Et(e){if(typeof e.env!="string")return{...e.env};let t={};for(let[r,n]of e.env.split(/\r?\n/).entries()){let s=n.trim();if(s===""||s.startsWith("#"))continue;let i=s.indexOf("=");i<=0&&Ne("INVALID_ENV_LINE",`source=${e.name} line=${r+1}`);let o=s.slice(0,i).trim(),a=s.slice(i+1).trim();/^[A-Za-z_][A-Za-z0-9_]*$/.test(o)||Ne("INVALID_ENV_KEY",`source=${e.name} line=${r+1}`),Object.hasOwn(t,o)&&Ne("DUPLICATE_ENV_KEY",`source=${e.name} key=${o}`),t[o]=a}return t}function hd(e){let t=Array.isArray(e)?e:[{name:"input",env:e}],r=[],n=new Map;for(let s of t){let i=Et(s);for(let[o,a]of Object.entries(i)){if(a===void 0)continue;let c=n.get(o);if(c&&c.value!==a&&Ne("ENV_SOURCE_CONFLICT",`key=${o} sources=${c.source},${s.name}`),!c){let l={key:o,value:a,source:s.name};n.set(o,l),r.push(l)}}}return r}function wd(e,t){let r=fd.exec(e);if(r)return r[1];let n=_d.exec(e);if(n)return n[1];if(e==="TADA_AGENT_PAYMENT_RPC_URL")return t}function He(e,t){if(!yd.test(t))throw new Error(`INVALID_PAYMENT_NETWORK: network=${t}`);let r=hd(e),n={},s=new Map,i=new Map,o=[],a=[],c=(l,d)=>{let u=s.get(d);u&&u.value!==l.value&&Ne("ENV_TARGET_CONFLICT",`target=${d} keys=${u.key},${l.key} sources=${u.source},${l.source}`),u||(s.set(d,l),n[d]=l.value),a.push({from:l.key,to:d})};for(let l of r){let d=ei[l.key];if(d){c(l,d);continue}let u=wd(l.key,t);if(u){let m=i.get(u)??[];m.push(l),i.set(u,m);continue}if(Zs.has(l.key)||/^AMB_RIDE_RPC_URL_/.test(l.key)){!Zs.has(l.key)&&!/^AMB_RIDE_RPC_URL_[A-Z0-9]+(?:_[A-Z0-9]+)*$/.test(l.key)&&Ne("UNKNOWN_AMBIENT_ENV_KEY",`key=${l.key} source=${l.source}`),c(l,l.key);continue}l.key.startsWith("AMB_RIDE_")&&Ne("UNKNOWN_AMBIENT_ENV_KEY",`key=${l.key} source=${l.source}`),gd.test(l.key)&&o.push(l.key)}for(let[l,d]of i){new Set(d.map(p=>p.value)).size>1&&Ne("RPC_CONFIG_CONFLICT",`network=${l} keys=${d.map(p=>p.key).join(",")} sources=${d.map(p=>p.source).join(",")}`);let m=`AMB_RIDE_RPC_URL_${l}`;for(let p of d)c(p,m)}return{env:n,droppedLegacyConfig:[...new Set(o)].sort(),mappedLegacyConfig:a}}import qe from"fs";import be from"path";import vd from"better-sqlite3";import Re from"crypto";import X_ from"fs";import Q_ from"path";var ti=Buffer.from("RAGK"),ri=1,Yt=4,ni=1,Xt=32,zt=12,In=16,Ed=Yt+ni+Xt+zt+In,Rd=16384,bd=8,Td=1,Sd=32;function si(){let{publicKey:e,privateKey:t}=Re.generateKeyPairSync("ec",{namedCurve:"prime256v1",publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem"}});return{publicKeyPem:e,privateKeyPem:t}}function ii(e){return Re.createPublicKey(e).export({type:"spki",format:"der"}).toString("base64")}function Qt(e){let t=Re.createPrivateKey(e);return Re.createPublicKey(t).export({type:"spki",format:"der"}).toString("base64")}function oi(e,t){return Re.scryptSync(e,t,Sd,{N:Rd,r:bd,p:Td})}function ai(e,t){let r=Re.randomBytes(Xt),n=Re.randomBytes(zt),s=oi(t,r),i=Re.createCipheriv("aes-256-gcm",s,n),o=Buffer.concat([i.update(e),i.final()]),a=i.getAuthTag();return Buffer.concat([ti,Buffer.from([ri]),r,n,a,o])}function fe(e,t){if(e.length<Ed)throw new Error("Invalid encrypted data: too short");if(!e.subarray(0,Yt).equals(ti))throw new Error("Invalid encrypted data: bad magic");let n=e[Yt];if(n!==ri)throw new Error(`Unsupported encrypted data version: ${n}`);let s=Yt+ni,i=e.subarray(s,s+Xt);s+=Xt;let o=e.subarray(s,s+zt);s+=zt;let a=e.subarray(s,s+In);s+=In;let c=e.subarray(s),l=oi(t,i),d=Re.createDecipheriv("aes-256-gcm",l,o);return d.setAuthTag(a),Buffer.concat([d.update(c),d.final()])}function ne(e){e.fill(0)}import Zt from"path";import Ad from"fs";import Id from"better-sqlite3";function Q(e,t){return Zt.join(e,Zt.basename(t))}function er(e){return Zt.basename(e)}var xd=[{table:"privy_wallet_meta",id:"wallet_address"},{table:"pending_sessions",id:"session_token"}];function ci(e){if(!Ad.existsSync(e))return{normalized:0};let t=null;try{return t=new Id(e),t.transaction(()=>{let n=new Map,s=0;for(let i of xd){if(!t.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(i.table))continue;let a=t.prepare(`SELECT ${i.id} AS id, k_i_key_path FROM ${i.table}`).all(),c=t.prepare(`UPDATE ${i.table} SET k_i_key_path = ? WHERE ${i.id} = ?`);for(let l of a){let d=l.k_i_key_path.replace(/\\/g,"/"),u=Zt.posix.basename(d);if(!u||u==="."||u==="..")throw new Error(`INVALID_KEY_PATH: ${i.table}.${i.id}=${l.id}`);let m=n.get(u);if(m!==void 0&&m!==d)throw new Error(`KEY_PATH_COLLISION: ${u}`);n.set(u,d),l.k_i_key_path!==u&&(c.run(u,l.id),s++)}}return{normalized:s}})()}finally{t?.close()}}var pi=["identity","wallets","privy_wallet_meta","pending_sessions","jwt_cache","ride_requests","tada_members","places","bridge_jobs","member_login_sessions"];function W(e,t){throw new Error(t?`${e}: ${t}`:e)}function li(e){try{return new vd(e,{readonly:!0,fileMustExist:!0})}catch(t){return W("SQLITE_OPEN_FAILED",String(t))}}function xn(e,t){return!!e.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?").get(t)}function di(e){return Object.fromEntries(pi.map(t=>{if(!xn(e,t))return[t,"absent"];let r=e.prepare(`SELECT COUNT(*) AS count FROM ${t}`).get();return[t,r.count]}))}function Nd(e){let t=[];if(xn(e,"privy_wallet_meta")){let r=e.prepare("SELECT wallet_address AS id, k_i_pub AS publicKey, k_i_key_path AS storedPath FROM privy_wallet_meta").all();t.push(...r.map(n=>({table:"privy_wallet_meta",...n})))}if(xn(e,"pending_sessions")){let r=e.prepare("SELECT session_token AS id, k_i_pub AS publicKey, k_i_key_path AS storedPath FROM pending_sessions").all();t.push(...r.map(n=>({table:"pending_sessions",...n})))}return t}function ui(e){let t;try{t=e.pragma("integrity_check")}catch(r){W("SQLITE_INTEGRITY_FAILED",String(r))}(t.length!==1||t[0]?.integrity_check!=="ok")&&W("SQLITE_INTEGRITY_FAILED",t.map(r=>r.integrity_check).join("; "))}function kd(e,t){for(let r of pi)e[r]!==t[r]&&W("ROW_COUNT_MISMATCH",`${r}: source=${e[r]} staged=${t[r]}`)}function vn(e){let t=be.resolve(e.dataDir),r=be.resolve(e.keysDir),n=be.resolve(e.dbPath);for(let[a,c]of[["DATA_DIR",t],["KEYS_DIR",r]]){let l;try{l=qe.lstatSync(c)}catch{W(`MISSING_${a}`)}(!l.isDirectory()||l.isSymbolicLink())&&W(`INVALID_${a}`)}be.dirname(n)!==t&&W("DB_OUTSIDE_DATA_DIR"),qe.existsSync(n)||W("MISSING_DATABASE");let s=qe.lstatSync(n);(!s.isFile()||s.isSymbolicLink())&&W("INVALID_DATABASE_FILE");let i;if(e.sourceDbPath){let a=be.resolve(e.sourceDbPath);a===n&&W("SOURCE_EQUALS_STAGED");let c=li(a);try{ui(c),i=di(c)}finally{c.close()}}try{ci(n)}catch(a){let c=String(a);c.includes("KEY_PATH_COLLISION")&&W("KEY_PATH_COLLISION",c),/malformed|not a database|database disk image/i.test(c)&&W("SQLITE_INTEGRITY_FAILED",c),W("KEY_PATH_NORMALIZATION_FAILED",c)}let o=li(n);try{ui(o);let a=di(o);i&&kd(i,a);let c=Nd(o),l=0;for(let d of c){let u=Q(r,d.storedPath),m=be.relative(r,u);(m.startsWith("..")||be.isAbsolute(m))&&W("KEY_OUTSIDE_KEYS_DIR",`${d.table}:${d.id}`);let p;try{p=qe.lstatSync(u)}catch{W("MISSING_KEY_FILE",`${d.table}:${d.id}`)}(!p.isFile()||p.isSymbolicLink())&&W("INVALID_KEY_FILE",`${d.table}:${d.id}`);let f=qe.realpathSync(u),_=qe.realpathSync(r),h=be.relative(_,f);(h.startsWith("..")||be.isAbsolute(h))&&W("KEY_OUTSIDE_KEYS_DIR",`${d.table}:${d.id}`);let I;try{I=fe(qe.readFileSync(u),Buffer.from(e.passphrase,"utf8")),Qt(I.toString("utf8"))!==d.publicKey&&W("KEY_PUBLIC_MISMATCH",`${d.table}:${d.id}`)}catch(E){if(String(E).includes("KEY_PUBLIC_MISMATCH"))throw E;W("KEY_DECRYPT_FAILED",`${d.table}:${d.id}`)}finally{I&&ne(I)}l++}return{sqliteIntegrity:"ok",criticalRowCounts:a,referencedKeys:c.length,openedKeys:l}}finally{o.close()}}var nr=".amb-migration-provenance.json",Cn=".amb-migration-attempt",gi=".amb-migration-report.json",Nn=".env.legacy",tt=384;function rr(e){return`${e.stagingRoot}.owner`}function B(e,t){return new Error(t?`${e}: ${t}`:e)}function Cd(e){let t=Object.fromEntries(Object.entries(e).map(([r,n])=>[r,x.resolve(n)]));if(new Set(Object.values(t)).size!==Object.keys(t).length)throw B("MIGRATION_CONFLICT","migration paths must be distinct");if(x.dirname(t.stagingRoot)!==x.dirname(t.ambientRoot))throw B("MIGRATION_CONFLICT","staging must be an Ambient sibling");if(x.dirname(t.backupRoot)!==x.dirname(t.legacyRoot))throw B("MIGRATION_CONFLICT","backup must be a legacy sibling");for(let r of[t.legacyRoot,t.ambientRoot,t.stagingRoot,t.backupRoot]){let n=x.relative(r,t.lockPath);if(n===""||!n.startsWith("..")&&!x.isAbsolute(n))throw B("MIGRATION_CONFLICT","lock must be outside state trees")}}function kn(e,t,r){let n=e.lstat(t);if(!n.isDirectory()||n.isSymbolicLink())throw B(r)}function Dd(e,t,r){let n=e.lstat(t);if(!n.isFile()||n.isSymbolicLink())throw B(r)}function fi(e){return`${JSON.stringify(e,null,2)}
|
|
10
|
+
`}function _i(e,t){let r=Ln.createHash("sha256"),n=0;for(let s of[t,`${t}-wal`,`${t}-journal`]){if(!e.exists(s))continue;let i=e.readFile(s);r.update(x.basename(s)),r.update(i),n+=i.length}return{size:n,sha256:r.digest("hex")}}function ir(e){return x.join(e.oldDataDir,"tada-ride-agent.db")}function _e(e){return x.join(e.stagingRoot,"state","data")}function tr(e){return x.join(e.stagingRoot,"state","keys")}function Dn(e){return x.join(e.ambientRoot,"state","data")}function Pd(e){return x.join(e.ambientRoot,"state","keys")}function On(e,t,r){try{return JSON.parse(e.readFile(t).toString("utf8"))}catch{throw B(r)}}function rt(e,t,r){e.fsyncDirectory(x.dirname(t)),e.rename(t,r),e.fsyncDirectory(x.dirname(r))}function yi(e,t,r,n){if(e.exists(t))return;let s=`${t}.${n}.tmp`;e.writeFileExclusive(s,fi(r),tt),e.fsyncFile(s),rt(e,s,t),e.fsyncFile(t)}function Ld(e,t){let r=x.join(_e(e.paths),".env"),n=e.transformEnv??He,s=t.exists(r)?Et({name:"legacy-state",env:t.readFile(r).toString("utf8")}):{},i=n(s,e.paymentNetwork);if(t.exists(r)){let a=x.join(_e(e.paths),Nn);if(t.exists(a))throw B("MIGRATION_COLLISION");rt(t,r,a)}let o=Object.entries(i.env).sort(([a],[c])=>a.localeCompare(c)).map(([a,c])=>`${a}=${c}`);return t.writeFileExclusive(r,`${o.join(`
|
|
11
|
+
`)}${o.length?`
|
|
12
|
+
`:""}`,tt),t.fsyncFile(r),t.exists(x.join(_e(e.paths),Nn))&&t.unlink(x.join(_e(e.paths),Nn)),t.fsyncDirectory(x.dirname(r)),i}function Od(e,t,r,n,s){let i=_i(e,t);return{schemaVersion:1,attemptId:s,sourcePath:e.realpath(t),sourceDbSize:i.size,sourceDbSha256:i.sha256,criticalRowCounts:r.criticalRowCounts,mappedLegacyConfig:n.mappedLegacyConfig,droppedLegacyConfig:n.droppedLegacyConfig}}function hi(e,t,r){let n=x.join(t.ambientRoot,nr);if(!e.exists(n))throw B("MIGRATION_INTERRUPTED","missing provenance");let s=On(e,n,"MIGRATION_INTERRUPTED"),i=_i(e,r);if(s.schemaVersion!==1||s.sourcePath!==e.realpath(r)||s.sourceDbSize!==i.size||s.sourceDbSha256!==i.sha256)throw B("MIGRATION_INTERRUPTED","source provenance mismatch");return s}function wi(e,t,r){let n=x.join(t.ambientRoot,Cn);if(e.exists(n)){if(e.readFile(n).toString("utf8").trim()!==r)throw B("MIGRATION_INTERRUPTED","attempt provenance mismatch");e.unlink(n),e.fsyncDirectory(t.ambientRoot)}}function Md(e,t){if(!t.exists(e.managedLegacyLink)||!t.lstat(e.managedLegacyLink).isSymbolicLink())return;let n,s;try{n=t.realpath(e.managedLegacyLink),s=t.realpath(e.expectedLegacyLinkTarget)}catch{return}n===s&&(t.unlink(e.managedLegacyLink),t.fsyncDirectory(x.dirname(e.managedLegacyLink)))}function sr(e,t){let r=x.join(t,"state","data"),n=x.join(r,"ambient-ride.db"),s=x.join(r,".env");for(let i of[n,`${n}-wal`,`${n}-shm`,s])e.exists(i)&&e.fsyncFile(i);for(let i of[r,x.join(t,"state","keys"),x.join(t,"state"),t])e.fsyncDirectory(i)}function Pn(e,t){let r=e.validate??vn;try{return r({dataDir:x.join(t,"state","data"),keysDir:x.join(t,"state","keys"),dbPath:x.join(t,"state","data","ambient-ride.db"),sourceDbPath:ir(e),passphrase:Ei(e,t)})}catch{throw B("MIGRATION_VALIDATION_FAILED")}}function Ei(e,t){let r=x.join(t,"state","data",".env"),s=Et({name:"ambient-state",env:e.fs?.readFile(r).toString("utf8")??S.readFileSync(r,"utf8")}).AMB_RIDE_PASSPHRASE;if(!s)throw B("MIGRATION_VALIDATION_FAILED","missing passphrase");return s}function Ri(e,t,r,n){return{schemaVersion:1,status:e,sourcePath:t.paths.legacyRoot,sourceDataPath:t.oldDataDir,sourceKeysPath:t.oldKeysDir,backupPath:t.paths.backupRoot,mappedLegacyConfig:r.mappedLegacyConfig,droppedLegacyConfig:r.droppedLegacyConfig,validation:n}}function Ud(e,t){let r=Et({name:"ambient-state",env:e.readFile(x.join(Dn(t),".env")).toString("utf8")});return{env:Object.fromEntries(Object.entries(r).filter(n=>n[1]!==void 0)),droppedLegacyConfig:[],mappedLegacyConfig:[]}}function bi(e,t){let r=/^(?:TADA_AGENT_|TADA_OPENCLAW_)[A-Z0-9_]+$/,n=/^AMB_RIDE_[A-Z0-9_]+$/;return e.every(s=>typeof s=="object"&&s!==null&&typeof s.from=="string"&&typeof s.to=="string"&&(r.test(s.from)||n.test(s.from))&&n.test(s.to))&&t.every(s=>typeof s=="string"&&r.test(s))}function Ti(e,t){let r=On(e,x.join(t.ambientRoot,nr),"MIGRATION_INTERRUPTED");if(!Array.isArray(r.mappedLegacyConfig)||!Array.isArray(r.droppedLegacyConfig)||!bi(r.mappedLegacyConfig,r.droppedLegacyConfig))throw B("MIGRATION_INTERRUPTED","invalid provenance");return{...Ud(e,t),mappedLegacyConfig:r.mappedLegacyConfig,droppedLegacyConfig:r.droppedLegacyConfig}}function Si(e,t,r,n,s,i){if(e.installManagedCli(),e.smoke(e.paths.ambientRoot),hi(t,e.paths,ir(e)),t.exists(e.paths.backupRoot))throw B("MIGRATION_BACKUP_EXISTS");Md(e,t),rt(t,e.paths.legacyRoot,e.paths.backupRoot);let o=Ri(r,e,s,i);return yi(t,x.join(e.paths.ambientRoot,gi),o,n),o}function Bd(e,t,r){kn(t,e.paths.legacyRoot,"INVALID_LEGACY_ROOT"),kn(t,e.oldDataDir,"INVALID_LEGACY_DATA_DIR"),kn(t,e.oldKeysDir,"INVALID_LEGACY_KEYS_DIR");let n=ir(e);Dd(t,n,"INVALID_LEGACY_DATABASE");let s=e.quiesceLegacy(),i=!1;try{t.writeFileExclusive(rr(e.paths),`${r}
|
|
13
|
+
`,tt),t.fsyncFile(rr(e.paths)),t.fsyncDirectory(x.dirname(e.paths.stagingRoot)),i=!0,t.copyTreeExclusive(e.paths.legacyRoot,e.paths.stagingRoot,["cli"]),t.writeFileExclusive(x.join(e.paths.stagingRoot,Cn),`${r}
|
|
14
|
+
`,tt),t.fsyncFile(x.join(e.paths.stagingRoot,Cn));let o=x.join(e.paths.stagingRoot,x.relative(e.paths.legacyRoot,e.oldDataDir)),a=x.join(e.paths.stagingRoot,x.relative(e.paths.legacyRoot,e.oldKeysDir));if(x.resolve(o)!==x.resolve(_e(e.paths))){let f=x.join(e.paths.stagingRoot,"state");t.exists(f)||t.mkdirExclusive(f,448),t.exists(_e(e.paths))&&t.removeOwnedPath(_e(e.paths)),t.copyTreeExclusive(e.oldDataDir,_e(e.paths))}if(x.resolve(a)!==x.resolve(tr(e.paths))){let f=x.join(e.paths.stagingRoot,"state");t.exists(f)||t.mkdirExclusive(f,448),t.exists(tr(e.paths))&&t.removeOwnedPath(tr(e.paths)),t.copyTreeExclusive(e.oldKeysDir,tr(e.paths))}let c=Ld(e,t),l=x.join(_e(e.paths),"tada-ride-agent.db"),d=x.join(_e(e.paths),"ambient-ride.db");if(t.exists(d))throw B("MIGRATION_COLLISION");rt(t,l,d);for(let f of["-wal","-shm","-journal"]){let _=`${l}${f}`,h=`${d}${f}`;if(t.exists(_)){if(t.exists(h))throw B("MIGRATION_COLLISION");rt(t,_,h)}}let u=Pn(e,e.paths.stagingRoot);sr(t,e.paths.stagingRoot);let m=Od(t,n,u,c,r);t.writeFileExclusive(x.join(e.paths.stagingRoot,nr),fi(m),tt),t.fsyncFile(x.join(e.paths.stagingRoot,nr)),t.fsyncDirectory(e.paths.stagingRoot),rt(t,e.paths.stagingRoot,e.paths.ambientRoot),i=!1,t.unlink(rr(e.paths)),t.fsyncDirectory(x.dirname(e.paths.stagingRoot)),wi(t,e.paths,r);let p=Pn(e,e.paths.ambientRoot);return sr(t,e.paths.ambientRoot),Si(e,t,"migrated",r,c,p)}catch(o){if(i)try{t.removeOwnedStaging(e.paths.stagingRoot,r)}catch{throw B("MIGRATION_INTERRUPTED","staging cleanup failed")}throw o}finally{s.release()}}function Fd(e,t,r){let n=e.quiesceLegacy();try{let s=ir(e),i=hi(t,e.paths,s);wi(t,e.paths,i.attemptId);let o=Pn(e,e.paths.ambientRoot);return sr(t,e.paths.ambientRoot),Si(e,t,"resumed",r,Ti(t,e.paths),o)}finally{n.release()}}function $d(e,t){let r=rr(t);if(!e.exists(r))return;let n=e.readFile(r).toString("utf8").trim();if(!n||n.includes(`
|
|
15
|
+
`))throw B("MIGRATION_CONFLICT","invalid staging owner");e.removeOwnedStaging(t.stagingRoot,n)}function Hd(e,t,r){let n=x.join(e.paths.ambientRoot,gi),s;try{s=(e.validate??vn)({dataDir:Dn(e.paths),keysDir:Pd(e.paths),dbPath:x.join(Dn(e.paths),"ambient-ride.db"),passphrase:Ei(e,e.paths.ambientRoot)})}catch{throw B("MIGRATION_VALIDATION_FAILED")}sr(t,e.paths.ambientRoot);let i=Ti(t,e.paths);if(t.exists(n)){let a=On(t,n,"MIGRATION_INTERRUPTED");if(a.schemaVersion!==1||!["migrated","resumed","already_migrated"].includes(a.status)||!Array.isArray(a.mappedLegacyConfig)||!Array.isArray(a.droppedLegacyConfig)||!bi(a.mappedLegacyConfig,a.droppedLegacyConfig))throw B("MIGRATION_INTERRUPTED","invalid report");return{schemaVersion:1,status:a.status,sourcePath:a.sourcePath,sourceDataPath:a.sourceDataPath,sourceKeysPath:a.sourceKeysPath,backupPath:a.backupPath,mappedLegacyConfig:a.mappedLegacyConfig,droppedLegacyConfig:a.droppedLegacyConfig,validation:s}}let o=Ri("already_migrated",e,i,s);return yi(t,n,o,r),o}function Mn(e){let t=e.fs??Ii,r=e.attemptId??Ln.randomUUID();Cd(e.paths);let n;try{n=t.acquireLock(e.paths.lockPath)}catch{throw B("MIGRATION_CONFLICT","migration lock is held")}try{try{$d(t,e.paths);let s=zs(e.paths,o=>t.exists(o));switch(Qs(e.paths,s).state){case"fresh":return{schemaVersion:1,status:"fresh",mappedLegacyConfig:[],droppedLegacyConfig:[]};case"ambient_fresh":return{schemaVersion:1,status:"ambient_fresh",mappedLegacyConfig:[],droppedLegacyConfig:[]};case"legacy_candidate":return Bd(e,t,r);case"resume_before_backup":return Fd(e,t,r);case"already_migrated":return Hd(e,t,r)}}catch(s){throw/\b(?:MIGRATION_|INVALID_|UNSAFE_|STAGING_)[A-Z_]+/.test(String(s))?s:B("MIGRATION_INTERRUPTED")}}finally{n.release()}}function Ai(e,t,r=[]){let n=S.lstatSync(e);if(!n.isDirectory()||n.isSymbolicLink())throw B("UNSAFE_STATE_ENTRY");S.mkdirSync(t,{mode:n.mode&511,recursive:!1});try{for(let i of S.readdirSync(e)){if(r.includes(i))continue;let o=x.join(e,i),a=x.join(t,i),c=S.lstatSync(o);if(c.isSymbolicLink()||!c.isDirectory()&&!c.isFile())throw B("UNSAFE_STATE_ENTRY");if(c.isDirectory())Ai(o,a);else{S.copyFileSync(o,a,S.constants.COPYFILE_EXCL),S.chmodSync(a,c.mode&511),S.chownSync(a,c.uid,c.gid);let l=S.openSync(a,"r");try{S.fsyncSync(l)}finally{S.closeSync(l)}}}S.chmodSync(t,n.mode&511),S.chownSync(t,n.uid,n.gid);let s=S.openSync(t,"r");try{S.fsyncSync(s)}finally{S.closeSync(s)}}catch(s){throw S.rmSync(t,{recursive:!0}),s}}function qd(e,t){let r=`${e}.owner`;if(!S.existsSync(r)||S.readFileSync(r,"utf8").trim()!==t)throw B("STAGING_NOT_OWNED");S.existsSync(e)&&S.rmSync(e,{recursive:!0}),S.unlinkSync(r);let n=S.openSync(x.dirname(e),"r");try{S.fsyncSync(n)}finally{S.closeSync(n)}}function mi(e){let t=S.openSync(e,"r");try{S.fsyncSync(t)}finally{S.closeSync(t)}}var Ii={exists:S.existsSync,lstat:S.lstatSync,mkdirExclusive(e,t){S.mkdirSync(e,{mode:t,recursive:!1})},copyTreeExclusive:Ai,removeOwnedPath(e){S.rmSync(e,{recursive:!0})},rename(e,t){if(S.existsSync(t))throw B("MIGRATION_COLLISION");S.renameSync(e,t)},removeOwnedStaging:qd,writeFileExclusive(e,t,r){S.writeFileSync(e,t,{flag:"wx",mode:r})},readFile:S.readFileSync,readlink:S.readlinkSync,realpath:S.realpathSync,unlink:S.unlinkSync,fsyncFile:mi,fsyncDirectory:mi,acquireLock(e){let t=`${e}.reclaim`,r=(a,c)=>a.dev===c.dev&&a.ino===c.ino,n=()=>{let a=new Error(`EEXIST: migration lock is held, open '${e}'`);return a.code="EEXIST",a},s=(a=!1)=>{if(!a&&S.existsSync(t))throw n();let c;try{return c=S.openSync(e,"wx",tt),S.writeFileSync(c,`${process.pid}
|
|
16
|
+
${Ln.randomUUID()}
|
|
17
|
+
`,"utf8"),S.fsyncSync(c),{descriptor:c,identity:S.fstatSync(c)}}catch(l){if(c!==void 0){try{let d=S.lstatSync(e),u=S.fstatSync(c);r(d,u)&&S.unlinkSync(e)}catch{}S.closeSync(c)}throw l}},i;try{i=s()}catch(a){if(a.code!=="EEXIST"||S.existsSync(t))throw a;let c,l;try{c=S.openSync(e,"r"),l=S.fstatSync(c);let u=Number(S.readFileSync(c,"utf8").trim().split(/\s+/u)[0]);throw!Number.isSafeInteger(u)||u<=0||process.kill(u,0),a}catch(u){if(!(u instanceof Error)||!("code"in u)||u.code!=="ESRCH")throw u}finally{c!==void 0&&S.closeSync(c)}try{S.linkSync(e,t)}catch{throw a}let d=S.lstatSync(t);try{let u=S.lstatSync(e);if(l===void 0||!r(l,d)||!r(u,d))throw a;S.unlinkSync(e),i=s(!0)}finally{try{let u=S.lstatSync(t);r(u,d)&&S.unlinkSync(t)}catch{}}}let o=!1;return{release(){if(!o){o=!0,S.closeSync(i.descriptor);try{let a=S.lstatSync(e);r(a,i.identity)&&S.unlinkSync(e)}catch(a){if(a.code!=="ENOENT")throw a}}}}}};function xi(e){return jd.createHash("sha256").update(D.readFileSync(e)).digest("hex")}function Ni(e,t){let r=D.readdirSync(e).sort(),n=D.readdirSync(t).sort();if(JSON.stringify(r)!==JSON.stringify(n))throw new Error("MIGRATION_VALIDATION_FAILED: skill-local entry mismatch");for(let s of r){let i=le.join(e,s),o=le.join(t,s),a=D.lstatSync(i),c=D.lstatSync(o);if(a.isSymbolicLink()||c.isSymbolicLink())throw new Error("UNSAFE_STATE_ENTRY: skill-local symlink");if(a.isDirectory()&&c.isDirectory()){Ni(i,o);continue}if(!a.isFile()||!c.isFile()||xi(i)!==xi(o))throw new Error("MIGRATION_VALIDATION_FAILED: skill-local content mismatch")}}function ki(e){for(let r of D.readdirSync(e)){let n=le.join(e,r);if(D.lstatSync(n).isDirectory())ki(n);else{let i=D.openSync(n,"r");try{D.fsyncSync(i)}finally{D.closeSync(i)}}}let t=D.openSync(e,"r");try{D.fsyncSync(t)}finally{D.closeSync(t)}}function vi(e){let t=`${e}.${process.pid}.tmp`;D.writeFileSync(t,new Date().toISOString(),{flag:"wx",mode:384});let r=D.openSync(t,"r");try{D.fsyncSync(r)}finally{D.closeSync(r)}D.renameSync(t,e);let n=D.openSync(le.dirname(e),"r");try{D.fsyncSync(n)}finally{D.closeSync(n)}}function or(e,t){let r=le.join(t,".migrated-from-legacy");if(D.existsSync(r))return{migrated:!1,reason:"already-migrated"};if(!D.existsSync(e))return{migrated:!1,reason:"no-legacy"};let n=le.join(t,"data");if(D.existsSync(le.join(n,"tada-ride-agent.db")))return D.mkdirSync(t,{recursive:!0}),vi(r),{migrated:!1,reason:"target-already-populated"};if(D.existsSync(t))throw new Error("MIGRATION_CONFLICT: partial intermediate legacy state");let s=`${t}.skill-migrating`;D.mkdirSync(le.dirname(t),{recursive:!0,mode:448}),D.existsSync(s)&&D.rmSync(s,{recursive:!0}),D.mkdirSync(s,{recursive:!1,mode:448});try{for(let o of["data","keys"]){let a=le.join(e,o);if(!D.existsSync(a))continue;let c=le.join(s,o);D.cpSync(a,c,{recursive:!0,errorOnExist:!0,force:!1}),Ni(a,c)}ki(s),D.renameSync(s,t);let i=D.openSync(le.dirname(t),"r");try{D.fsyncSync(i)}finally{D.closeSync(i)}}catch(i){throw D.existsSync(s)&&D.rmSync(s,{recursive:!0}),i}return vi(r),{migrated:!0,reason:"copied"}}var Gd=/^\d+\.\d+\.\d+$/,Ci=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);function Z(e,t){return new Error(`Invalid AmbBuildConfig at ${e}: ${t}`)}function Te(e,t){if(typeof e!="object"||e===null||Array.isArray(e))throw Z(t,"expected an object");return e}function ke(e,t,r){let n=new Set(t),s=Object.keys(e).filter(o=>!n.has(o)),i=t.filter(o=>!Ci(e,o));if(s.length>0)throw Z(r,`unknown field ${s[0]}`);if(i.length>0)throw Z(`${r}.${i[0]}`,"is required")}function q(e,t,r=!1){if(typeof e!="string"||!r&&e.length===0)throw Z(t,r?"expected a string":"expected a non-empty string");return e}function Un(e,t){let r=q(e,t);if(!Gd.test(r))throw Z(t,"expected stable three-part SemVer");return r}function Rt(e,t,r={}){if(typeof e!="number"||!Number.isFinite(e))throw Z(t,"expected a finite number");if(r.integer&&!Number.isInteger(e))throw Z(t,"expected an integer");if(r.min!==void 0&&e<r.min)throw Z(t,`must be >= ${r.min}`);if(r.max!==void 0&&e>r.max)throw Z(t,`must be <= ${r.max}`);return e}function Wd(e,t){if(!Array.isArray(e)||e.length===0)throw Z(t,"expected a non-empty string array");return e.map((r,n)=>q(r,`${t}[${n}]`))}function Vd(e,t){let r=Te(e,t),n={};for(let[s,i]of Object.entries(r)){if(s.length===0)throw Z(t,"contains an empty key");n[s]=q(i,`${t}.${s}`)}return n}function Jd(e){let t=Te(e,"bridgePolicy");ke(t,["minimumUsdc","maxFeeBufferPct","burnGraceBlocks","mintMaxAttempts","leaseSeconds","logChunkBlocks"],"bridgePolicy");let r=q(t.minimumUsdc,"bridgePolicy.minimumUsdc"),n=Number(r);if(!Number.isFinite(n)||n<0)throw Z("bridgePolicy.minimumUsdc","expected a non-negative decimal number");return{minimumUsdc:r,maxFeeBufferPct:Rt(t.maxFeeBufferPct,"bridgePolicy.maxFeeBufferPct",{min:0,max:100}),burnGraceBlocks:Rt(t.burnGraceBlocks,"bridgePolicy.burnGraceBlocks",{integer:!0,min:1}),mintMaxAttempts:Rt(t.mintMaxAttempts,"bridgePolicy.mintMaxAttempts",{integer:!0,min:1}),leaseSeconds:Rt(t.leaseSeconds,"bridgePolicy.leaseSeconds",{integer:!0,min:1}),logChunkBlocks:Rt(t.logChunkBlocks,"bridgePolicy.logChunkBlocks",{integer:!0,min:1})}}function Di(e){if(typeof e!="object"||e===null||Object.isFrozen(e))return e;for(let t of Object.values(e))Di(t);return Object.freeze(e)}function Kd(e){let t=Te(e,"buildConfig");ke(t,["services","networks","providers","artifact","install","bridgePolicy"],"buildConfig");let r=Te(t.services,"services");ke(r,["authServerUrl","authWebappUrl","privyAppId","cryptoGatewayUrl","memberGatewayUrls","defaultRegion"],"services");let n=Te(t.networks,"networks");ke(n,["depositNetworks","paymentNetwork","paymentCurrency","paymentUsdcAddress","bridgeSource","bridgeDestination"],"networks");let s=Te(t.providers,"providers");ke(s,["tada"],"providers");let i=Te(s.tada,"providers.tada");ke(i,["memberClientId","memberClientSecret"],"providers.tada");let o=Te(t.artifact,"artifact");ke(o,["cliVersion","gitSha"],"artifact");let a=Te(t.install,"install"),c=q(a.mode,"install.mode");if(c!=="git"&&c!=="npm")throw Z("install.mode","expected git or npm");ke(a,c==="git"?["mode","repoBranch","repoUrl","expectedCliSha","minimumCliVersion"]:["mode","repoBranch","expectedCliSha","minimumCliVersion"],"install");let l=q(a.expectedCliSha,"install.expectedCliSha",c==="npm"),d=Vd(r.memberGatewayUrls,"services.memberGatewayUrls"),u=q(r.defaultRegion,"services.defaultRegion");if(!Ci(d,u))throw Z("services.defaultRegion","must name a memberGatewayUrls entry");return Di({services:{authServerUrl:q(r.authServerUrl,"services.authServerUrl"),authWebappUrl:q(r.authWebappUrl,"services.authWebappUrl"),privyAppId:q(r.privyAppId,"services.privyAppId"),cryptoGatewayUrl:q(r.cryptoGatewayUrl,"services.cryptoGatewayUrl"),memberGatewayUrls:d,defaultRegion:u},networks:{depositNetworks:Wd(n.depositNetworks,"networks.depositNetworks"),paymentNetwork:q(n.paymentNetwork,"networks.paymentNetwork"),paymentCurrency:q(n.paymentCurrency,"networks.paymentCurrency"),paymentUsdcAddress:q(n.paymentUsdcAddress,"networks.paymentUsdcAddress"),bridgeSource:q(n.bridgeSource,"networks.bridgeSource",!0),bridgeDestination:q(n.bridgeDestination,"networks.bridgeDestination",!0)},providers:{tada:{memberClientId:q(i.memberClientId,"providers.tada.memberClientId",!0),memberClientSecret:q(i.memberClientSecret,"providers.tada.memberClientSecret",!0)}},artifact:{cliVersion:Un(o.cliVersion,"artifact.cliVersion"),gitSha:q(o.gitSha,"artifact.gitSha")},install:c==="git"?{mode:"git",repoBranch:q(a.repoBranch,"install.repoBranch"),repoUrl:q(a.repoUrl,"install.repoUrl"),expectedCliSha:l,minimumCliVersion:Un(a.minimumCliVersion,"install.minimumCliVersion")}:{mode:"npm",repoBranch:q(a.repoBranch,"install.repoBranch"),expectedCliSha:l,minimumCliVersion:Un(a.minimumCliVersion,"install.minimumCliVersion")},bridgePolicy:Jd(t.bridgePolicy)})}var Yd,ar;function z(){return ar||(ar=Kd(Yd??y),ar)}var Xd={AMB_RIDE_STATE_DIR:"stateDir",AMB_RIDE_PASSPHRASE:"passphrase",AMB_RIDE_LOG_LEVEL:"logLevel",AMB_RIDE_OPENCLAW_CLI:"openclawCli"},zd=/^AMB_RIDE_RPC_URL_([A-Z0-9_]+)$/,Pi=/^[A-Z0-9]+(?:_[A-Z0-9]+)*$/;function cr(e){let t=e.toUpperCase();if(!Pi.test(t))throw new Error(`Invalid Ambient Ride RPC network: ${e}`);return`AMB_RIDE_RPC_URL_${t}`}function je(e=process.env){let t={rpcUrls:{}};for(let[r,n]of Object.entries(e)){if(!r.startsWith("AMB_RIDE_"))continue;let s=Xd[r];if(s!==void 0){n!==void 0&&(t[s]=n);continue}let i=zd.exec(r);if(i&&Pi.test(i[1])){n!==void 0&&(t.rpcUrls[i[1]]=n);continue}throw new Error(`Unknown Ambient Ride runtime environment variable: ${r}`)}return t}function Bn(e,t,r){return t.rpcUrls[e]??r[e]}function Zd(e){let t=e;for(;;){if(Fn.existsSync(Ge.join(t,"SKILL.md")))return t;let r=Ge.dirname(t);if(r===t)break;t=r}return Ge.dirname(Ge.dirname(e))}function eu(){let e=Ge.dirname(Qd(import.meta.url));return Zd(e)}function tu(e){if(!Fn.existsSync(e))return{};let t={};for(let r of Fn.readFileSync(e,"utf8").split(`
|
|
18
|
+
`)){let n=r.trim();if(!n||n.startsWith("#"))continue;let s=n.indexOf("=");s<=0||(t[n.slice(0,s).trim()]=n.slice(s+1).trim())}return t}var nt={BASE_SEPOLIA:84532,BASE:8453,ETHEREUM:1,ETHEREUM_SEPOLIA:11155111},$n={BASE_SEPOLIA:"https://sepolia.base.org",BASE:"https://mainnet.base.org",ETHEREUM:"https://eth.drpc.org",ETHEREUM_SEPOLIA:"https://sepolia.drpc.org"},ru={BASE_SEPOLIA:"0xcC1F1b0C7b505D94ee04E028eA19f81464AdefA9",ETHEREUM:"0xA4bAE3CeFA30F6A263402D388261c611663b2683"},nu={BASE_SEPOLIA:"0x3B7E10b1AEA923E13e2716b538789b6EC9F0A4c1",ETHEREUM:"0x5c23a543963A181267d5790d61eBa55b66246f0a"},Hn={ETHEREUM:{domain:0,tokenMessengerV2:"0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",messageTransmitterV2:"0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",usdc:"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",irisBaseUrl:"https://iris-api.circle.com",testnet:!1},ETHEREUM_SEPOLIA:{domain:0,tokenMessengerV2:"0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",messageTransmitterV2:"0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",usdc:"0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",irisBaseUrl:"https://iris-api-sandbox.circle.com",testnet:!0},BASE:{domain:6,tokenMessengerV2:"0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d",messageTransmitterV2:"0x81D40F21F12A8F0E3252Bccb954D722d4c464B64",usdc:"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",testnet:!1},BASE_SEPOLIA:{domain:6,tokenMessengerV2:"0x8FE6B999Dc680CcFDD5Bf7EB0974218be2542DAA",messageTransmitterV2:"0xE737e5cEBEEBa77EFE34D4aa090756590b1CE275",usdc:"0x036CbD53842c5426634e7929541eC2318f3dCF7e",testnet:!0}};function su(e){return Hn[e]}var iu=Object.entries(Hn).filter(([,e])=>e.irisBaseUrl).map(([e])=>e);function ou(e,t=je()){return Bn(e,t,$n)}function Li(e,t,r,n=je()){if(!t)return{error:`${e} is not set \u2014 this build has no USDC bridge configured.`};let s=su(t);if(!s)return{error:`${e}=${t} is not a CCTP network. Known networks: ${Object.keys(Hn).join(", ")}.`};let i=nt[t];if(i===void 0)return{error:`${e}=${t} has no chain id configured. Known networks: ${Object.keys(nt).join(", ")}.`};if(r==="source"&&!s.irisBaseUrl)return{error:`${e}=${t} is not supported as a bridge source (no Circle attestation host). Supported sources: ${iu.join(", ")}.`};let o=ou(t,n);if(!o)return{error:`No RPC URL for ${t}. Set ${cr(t)}.`};let a={name:t,chainId:i,rpcUrl:o,usdcAddress:s.usdc,cctpDomain:s.domain,tokenMessengerV2:s.tokenMessengerV2};return r==="source"?{endpoint:{...a,role:"source"},net:s}:{endpoint:{...a,role:"dest",messageTransmitterV2:s.messageTransmitterV2},net:s}}function au(e=z().networks.bridgeSource,t=z().networks.bridgeDestination,r=je()){let n=Li("bridge source",e,"source",r);if(!n.endpoint||!n.net)return{error:n.error??"Bridge source could not be resolved."};let s=Li("bridge destination",t,"dest",r);if(!s.endpoint||!s.net)return{error:s.error??"Bridge destination could not be resolved."};if(n.endpoint.name===s.endpoint.name)return{error:`Bridge source and destination are both ${n.endpoint.name} \u2014 a bridge needs two different networks.`};let i=n.net,o=s.net;if(i.testnet!==o.testnet){let a=c=>c.testnet?"testnet":"mainnet";return{error:`Bridge source ${n.endpoint.name} (${a(i)}) and destination ${s.endpoint.name} (${a(o)}) are on different network environments \u2014 mainnet vs testnet cannot be bridged. CCTP domains do not distinguish the two, so this would burn on ${a(i)} and mint on the ${a(i)} chain with domain ${o.domain}, not on ${s.endpoint.name}.`}}return{bridge:{source:n.endpoint,dest:s.endpoint,irisBaseUrl:i.irisBaseUrl}}}function R(){let e=z(),t=eu();or(Kt(t),Ge.join(F(),"state"));let r=et(),n=Xs(),s=tu(Ge.join(r,".env"));for(let[f,_]of Object.entries(s))process.env[f]===void 0&&(process.env[f]=_);let i=je();function o(f,_){if(f)return f;g({error:`Missing required environment variable: ${_} \u2014 local state is not initialised. Run \`amb install\`.`}),process.exit(1)}let c=e.networks.depositNetworks.map(f=>{let _=i.rpcUrls[f]??$n[f];_||(g({error:`Missing ${cr(f)} and no default RPC URL for network ${f}`}),process.exit(1));let h=ru[f];h||(g({error:`No v2 DepositRouter address configured for network ${f}. Mainnet \uC8FC\uC18C\uB294 Kaelie \uCE21 v2 \uBC30\uD3EC \uD6C4 \uCD94\uAC00.`}),process.exit(1));let I=nu[f];I||(g({error:`No v2 AgentDepositV2 ledger address for network ${f}.`}),process.exit(1));let E=nt[f];return E===void 0&&(g({error:`Unknown network name: ${f}. Known networks: ${Object.keys(nt).join(", ")}`}),process.exit(1)),{name:f,rpcUrl:_,chainId:E,depositContract:h,agentDepositLedger:I}}),l=e.networks.paymentNetwork,d=nt[l];d===void 0&&(g({error:`Unknown payment network name: ${l}. Known networks: ${Object.keys(nt).join(", ")}`}),process.exit(1));let u=Bn(l,i,$n);u||(g({error:`Missing ${cr(l)} and no default RPC URL for payment network ${l}`}),process.exit(1));let m=au(e.networks.bridgeSource,e.networks.bridgeDestination,i),p=e.bridgePolicy;return{appServerUrl:e.services.authServerUrl,webappUrl:e.services.authWebappUrl,privyAppId:e.services.privyAppId,dataDir:r,keysDir:n,depositNetworks:c,gatewayServerUrl:e.services.cryptoGatewayUrl,passphrase:o(i.passphrase,"AMB_RIDE_PASSPHRASE"),payCurrency:e.networks.paymentCurrency,paymentNetwork:l,paymentRpcUrl:u,paymentChainId:d,paymentUsdcAddress:e.networks.paymentUsdcAddress,cliGatewayHost:e.services.memberGatewayUrls[e.services.defaultRegion],cliGatewayHosts:e.services.memberGatewayUrls,riderCliApp:{clientId:e.providers.tada.memberClientId,clientSecret:e.providers.tada.memberClientSecret},bridge:m.bridge,bridgeConfigError:m.error,bridgeMinUsdc:p.minimumUsdc,bridgeMaxFeeBufferPct:p.maxFeeBufferPct,bridgeBurnGraceBlocks:p.burnGraceBlocks,bridgeMintMaxAttempts:p.mintMaxAttempts,bridgeLeaseSec:p.leaseSeconds,bridgeLogChunkBlocks:p.logChunkBlocks}}function Oi(e){return e.riderCliApp.clientId!==""&&e.riderCliApp.clientSecret!==""}function bt(){let e=z(),t=e.providers.tada.memberClientId,r=e.providers.tada.memberClientSecret,n=e.services.memberGatewayUrls[e.services.defaultRegion]??"";return t!==""&&r!==""&&n!==""}import du from"better-sqlite3";import dr from"fs";import ur from"path";import Se from"fs";import lr from"path";import cu from"better-sqlite3";var Ui="tada-ride-agent.db",Mi="ambient-ride.db",Bi=["","-wal","-shm","-journal"];function lu(e){let t=Se.openSync(e,"r");try{Se.fsyncSync(t)}finally{Se.closeSync(t)}}function Tt(e){return Bi.some(t=>Se.existsSync(lr.join(e,`${Ui}${t}`)))}function Fi(e){if(!Tt(e))return!1;Se.mkdirSync(e,{recursive:!0,mode:448});for(let n of Bi){let s=lr.join(e,`${Ui}${n}`),i=lr.join(e,`${Mi}${n}`);if(Se.existsSync(s)){if(Se.existsSync(i))throw new Error(`MIGRATION_CONFLICT: both database names exist (${n||"main"})`);Se.renameSync(s,i),lu(e)}}let t=lr.join(e,Mi);if(!Se.existsSync(t))throw new Error("MIGRATION_INTERRUPTED: database sidecar exists without main database");let r=new cu(t,{readonly:!0,fileMustExist:!0});try{if(r.pragma("integrity_check",{simple:!0})!=="ok")throw new Error("MIGRATION_VALIDATION_FAILED: database integrity")}finally{r.close()}return!0}var $i=je().logLevel==="debug";function Hi(){return $i}function oe(e){$i&&process.stderr.write(e)}function st(e){return e.placeId?`gid:${e.placeId}`:Number.isFinite(e.latitude)&&Number.isFinite(e.longitude)?`coord:${e.latitude.toFixed(4)},${e.longitude.toFixed(4)}`:null}var L;function b(e){let t=ur.dirname(e),r=ur.basename(t)==="state"?ur.dirname(t):t;if(dr.existsSync(`${r}.amb-migration-quiesced`))throw new Error("MIGRATION_CONFLICT: runtime state is being migrated");dr.existsSync(e)||dr.mkdirSync(e,{recursive:!0});let n=ur.join(e,"ambient-ride.db");if(!dr.existsSync(n)&&Tt(e))throw new Error("LEGACY_AMBIENT_DATABASE: run `amb install` before using the CLI");L=new du(n),L.pragma("journal_mode = WAL"),L.pragma("foreign_keys = ON"),L.exec(`
|
|
19
|
+
CREATE TABLE IF NOT EXISTS identity (
|
|
20
|
+
id INTEGER PRIMARY KEY,
|
|
21
|
+
privy_user_id TEXT NOT NULL UNIQUE,
|
|
22
|
+
email TEXT NOT NULL,
|
|
23
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
CREATE TABLE IF NOT EXISTS wallets (
|
|
27
|
+
wallet_address TEXT PRIMARY KEY,
|
|
28
|
+
wallet_type TEXT NOT NULL CHECK(wallet_type IN ('privy', 'external')),
|
|
29
|
+
label TEXT,
|
|
30
|
+
last_valid INTEGER DEFAULT 0,
|
|
31
|
+
invalid_reason TEXT,
|
|
32
|
+
last_validity_check_at TEXT,
|
|
33
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
CREATE TABLE IF NOT EXISTS privy_wallet_meta (
|
|
37
|
+
wallet_address TEXT PRIMARY KEY,
|
|
38
|
+
wallet_id TEXT NOT NULL,
|
|
39
|
+
quorum_id TEXT NOT NULL,
|
|
40
|
+
k_i_pub TEXT NOT NULL,
|
|
41
|
+
k_i_key_path TEXT NOT NULL,
|
|
42
|
+
FOREIGN KEY (wallet_address) REFERENCES wallets(wallet_address) ON DELETE CASCADE
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE TABLE IF NOT EXISTS pending_sessions (
|
|
46
|
+
session_token TEXT PRIMARY KEY,
|
|
47
|
+
k_i_pub TEXT NOT NULL,
|
|
48
|
+
k_i_key_path TEXT NOT NULL,
|
|
49
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
CREATE TABLE IF NOT EXISTS jwt_cache (
|
|
53
|
+
cache_key TEXT PRIMARY KEY,
|
|
54
|
+
token TEXT NOT NULL,
|
|
55
|
+
expires_at INTEGER NOT NULL
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
CREATE TABLE IF NOT EXISTS search_cache (
|
|
59
|
+
check_sum TEXT PRIMARY KEY,
|
|
60
|
+
raw TEXT NOT NULL,
|
|
61
|
+
expires_at INTEGER NOT NULL
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
CREATE TABLE IF NOT EXISTS payment_cache (
|
|
65
|
+
request_id TEXT PRIMARY KEY,
|
|
66
|
+
data TEXT NOT NULL,
|
|
67
|
+
expires_at INTEGER NOT NULL
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
CREATE TABLE IF NOT EXISTS ride_events (
|
|
71
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
72
|
+
type TEXT NOT NULL,
|
|
73
|
+
request_id TEXT NOT NULL,
|
|
74
|
+
timestamp TEXT NOT NULL,
|
|
75
|
+
payload TEXT NOT NULL,
|
|
76
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now'))
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
CREATE TABLE IF NOT EXISTS ride_requests (
|
|
80
|
+
request_id TEXT PRIMARY KEY,
|
|
81
|
+
wallet_address TEXT,
|
|
82
|
+
member_id TEXT,
|
|
83
|
+
region TEXT NOT NULL,
|
|
84
|
+
product_code TEXT,
|
|
85
|
+
status TEXT NOT NULL DEFAULT 'WAITPAY',
|
|
86
|
+
price TEXT,
|
|
87
|
+
pay_currency TEXT,
|
|
88
|
+
origin_name TEXT,
|
|
89
|
+
origin_addr TEXT,
|
|
90
|
+
origin_lat REAL,
|
|
91
|
+
origin_lng REAL,
|
|
92
|
+
dest_name TEXT,
|
|
93
|
+
dest_addr TEXT,
|
|
94
|
+
dest_lat REAL,
|
|
95
|
+
dest_lng REAL,
|
|
96
|
+
rider_name TEXT,
|
|
97
|
+
rider_phone TEXT,
|
|
98
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
99
|
+
status_updated_at INTEGER,
|
|
100
|
+
FOREIGN KEY (wallet_address) REFERENCES wallets(wallet_address)
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
CREATE TABLE IF NOT EXISTS phone_verifications (
|
|
104
|
+
wallet_address TEXT NOT NULL,
|
|
105
|
+
phone TEXT NOT NULL,
|
|
106
|
+
verified_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
107
|
+
PRIMARY KEY (wallet_address, phone),
|
|
108
|
+
FOREIGN KEY (wallet_address) REFERENCES wallets(wallet_address) ON DELETE CASCADE
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
CREATE TABLE IF NOT EXISTS processed_events (
|
|
112
|
+
ride_id TEXT NOT NULL,
|
|
113
|
+
event_id TEXT NOT NULL,
|
|
114
|
+
seq INTEGER NOT NULL,
|
|
115
|
+
PRIMARY KEY (ride_id, event_id)
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
CREATE TABLE IF NOT EXISTS tada_members (
|
|
119
|
+
id TEXT PRIMARY KEY,
|
|
120
|
+
phone TEXT,
|
|
121
|
+
display_name TEXT,
|
|
122
|
+
region TEXT,
|
|
123
|
+
created_at INTEGER NOT NULL,
|
|
124
|
+
last_active_at INTEGER NOT NULL
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
CREATE TABLE IF NOT EXISTS places (
|
|
128
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
129
|
+
wallet_address TEXT NOT NULL,
|
|
130
|
+
label TEXT,
|
|
131
|
+
name TEXT NOT NULL,
|
|
132
|
+
formatted_address TEXT NOT NULL,
|
|
133
|
+
latitude REAL NOT NULL,
|
|
134
|
+
longitude REAL NOT NULL,
|
|
135
|
+
google_place_id TEXT,
|
|
136
|
+
city TEXT, -- nullable (history rows have no resolve)
|
|
137
|
+
region TEXT NOT NULL, -- always filled from ride params.region (favorite via server resolve, history via ride-request)
|
|
138
|
+
ftid TEXT,
|
|
139
|
+
kg_id TEXT,
|
|
140
|
+
source_url TEXT, -- nullable (history rows have no URL)
|
|
141
|
+
is_favorite INTEGER NOT NULL DEFAULT 0,
|
|
142
|
+
hit_count INTEGER NOT NULL DEFAULT 0,
|
|
143
|
+
last_used_at INTEGER,
|
|
144
|
+
dedup_key TEXT NOT NULL,
|
|
145
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
146
|
+
updated_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
147
|
+
UNIQUE(wallet_address, label) ON CONFLICT REPLACE,
|
|
148
|
+
FOREIGN KEY (wallet_address) REFERENCES wallets(wallet_address) ON DELETE CASCADE
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
CREATE TABLE IF NOT EXISTS bridge_jobs (
|
|
152
|
+
id TEXT PRIMARY KEY, -- uuid v4, generated by the CLI
|
|
153
|
+
-- NO foreign key to wallets(wallet_address), unlike places/ride_requests:
|
|
154
|
+
-- a bridge job must survive wallet removal. Funds can be mid-flight (burned
|
|
155
|
+
-- on the source chain, not yet minted on the destination), and this row is
|
|
156
|
+
-- the only record identifying the last-resort recovery target.
|
|
157
|
+
wallet_address TEXT NOT NULL,
|
|
158
|
+
status TEXT NOT NULL, -- INITIATED | BURN_SUBMITTED | ATTESTED | MINT_SUBMITTED | COMPLETED | FAILED
|
|
159
|
+
source_chain_id INTEGER NOT NULL,
|
|
160
|
+
dest_chain_id INTEGER NOT NULL,
|
|
161
|
+
amount_raw TEXT NOT NULL, -- uint256 string (6 decimals)
|
|
162
|
+
is_fast INTEGER NOT NULL, -- 0/1
|
|
163
|
+
max_fee_raw TEXT NOT NULL,
|
|
164
|
+
from_block TEXT, -- eth_blockNumber at INITIATED; start of the fallback DepositForBurn log scan
|
|
165
|
+
burn_privy_tx_id TEXT, -- transaction_id returned by Privy sendCalls
|
|
166
|
+
burn_tx_hash TEXT, -- resolved via the user-service passthrough
|
|
167
|
+
relay_request_id TEXT, -- server mint-relay request UUID
|
|
168
|
+
cctp_nonce TEXT,
|
|
169
|
+
message TEXT,
|
|
170
|
+
attestation TEXT,
|
|
171
|
+
mint_privy_tx_id TEXT, -- transaction_id returned by sponsored sendTx
|
|
172
|
+
mint_tx_hash TEXT,
|
|
173
|
+
mint_from_block TEXT,
|
|
174
|
+
-- mint_attempts / last_revert_sig back the deterministic-revert threshold.
|
|
175
|
+
-- They accumulate across processes, so they live here rather than in memory.
|
|
176
|
+
mint_attempts INTEGER NOT NULL DEFAULT 0,
|
|
177
|
+
last_revert_sig TEXT,
|
|
178
|
+
minted_amount_raw TEXT, -- set only at COMPLETED, from MintAndWithdraw.amount
|
|
179
|
+
lease_expires_at INTEGER, -- concurrency serialization
|
|
180
|
+
failure_reason TEXT,
|
|
181
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
|
|
182
|
+
updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
CREATE TABLE IF NOT EXISTS bridge_fast_grants (
|
|
186
|
+
wallet_address TEXT PRIMARY KEY, -- as passed; requireWalletRegistered gates the form
|
|
187
|
+
max_fee_raw TEXT NOT NULL, -- 6-decimal raw, same unit as bridge_jobs.max_fee_raw
|
|
188
|
+
granted_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
CREATE TABLE IF NOT EXISTS member_login_sessions (
|
|
192
|
+
session_token TEXT PRIMARY KEY,
|
|
193
|
+
expires_at INTEGER NOT NULL,
|
|
194
|
+
created_at INTEGER NOT NULL
|
|
195
|
+
);
|
|
196
|
+
`),L.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='saved_places'").get()&&L.transaction(()=>{L.exec(`
|
|
197
|
+
INSERT INTO places (
|
|
198
|
+
id, wallet_address, label, name, formatted_address,
|
|
199
|
+
latitude, longitude, google_place_id, city, region,
|
|
200
|
+
ftid, kg_id, source_url, created_at, updated_at,
|
|
201
|
+
is_favorite, hit_count, last_used_at, dedup_key
|
|
202
|
+
)
|
|
203
|
+
SELECT
|
|
204
|
+
id, wallet_address, label, name, formatted_address,
|
|
205
|
+
latitude, longitude, google_place_id, city, region,
|
|
206
|
+
ftid, kg_id, source_url, created_at, updated_at,
|
|
207
|
+
1 AS is_favorite,
|
|
208
|
+
0 AS hit_count,
|
|
209
|
+
NULL AS last_used_at,
|
|
210
|
+
CASE
|
|
211
|
+
WHEN google_place_id IS NOT NULL
|
|
212
|
+
THEN 'gid:' || google_place_id
|
|
213
|
+
ELSE 'coord:' || printf('%.4f', latitude) || ',' || printf('%.4f', longitude)
|
|
214
|
+
END AS dedup_key
|
|
215
|
+
FROM saved_places;
|
|
216
|
+
DROP TABLE saved_places;
|
|
217
|
+
`)})(),L.prepare("PRAGMA table_info(ride_events)").all().some(h=>h.name==="read")||L.exec("ALTER TABLE ride_events ADD COLUMN read INTEGER DEFAULT 0");let o=L.prepare("PRAGMA table_info(jwt_cache)").all();o.some(h=>h.name==="kind")||L.exec("ALTER TABLE jwt_cache ADD COLUMN kind TEXT NOT NULL DEFAULT 'siwe'"),o.some(h=>h.name==="refresh_token")||L.exec("ALTER TABLE jwt_cache ADD COLUMN refresh_token TEXT"),o.some(h=>h.name==="refresh_expires_at")||L.exec("ALTER TABLE jwt_cache ADD COLUMN refresh_expires_at INTEGER"),L.prepare("PRAGMA table_info(bridge_jobs)").all().some(h=>h.name==="relay_request_id")||L.exec("ALTER TABLE bridge_jobs ADD COLUMN relay_request_id TEXT"),L.prepare("PRAGMA table_info(wallets)").all().some(h=>h.name==="is_active")&&L.exec("ALTER TABLE wallets DROP COLUMN is_active"),L.prepare("PRAGMA table_info(phone_verifications)").all().some(h=>h.name==="wallet_address")||L.exec(`DROP TABLE phone_verifications;
|
|
218
|
+
CREATE TABLE phone_verifications (
|
|
219
|
+
wallet_address TEXT NOT NULL,
|
|
220
|
+
phone TEXT NOT NULL,
|
|
221
|
+
verified_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
222
|
+
PRIMARY KEY (wallet_address, phone),
|
|
223
|
+
FOREIGN KEY (wallet_address) REFERENCES wallets(wallet_address) ON DELETE CASCADE
|
|
224
|
+
);`),L.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='active_listeners'").get()&&L.exec("DROP TABLE active_listeners");let m=L.prepare("PRAGMA table_info(tada_members)").all().find(h=>h.name==="phone");m&&m.notnull===1&&L.exec(`
|
|
225
|
+
CREATE TABLE tada_members__new (
|
|
226
|
+
id TEXT PRIMARY KEY,
|
|
227
|
+
phone TEXT,
|
|
228
|
+
display_name TEXT,
|
|
229
|
+
region TEXT,
|
|
230
|
+
created_at INTEGER NOT NULL,
|
|
231
|
+
last_active_at INTEGER NOT NULL
|
|
232
|
+
);
|
|
233
|
+
INSERT INTO tada_members__new SELECT id, phone, display_name, region, created_at, last_active_at FROM tada_members;
|
|
234
|
+
DROP TABLE tada_members;
|
|
235
|
+
ALTER TABLE tada_members__new RENAME TO tada_members;
|
|
236
|
+
`);let p=L.prepare("PRAGMA table_info(ride_requests)").all();p.length>0&&!p.some(h=>h.name==="member_id")&&L.exec(`
|
|
237
|
+
CREATE TABLE ride_requests__new (
|
|
238
|
+
request_id TEXT PRIMARY KEY,
|
|
239
|
+
wallet_address TEXT,
|
|
240
|
+
member_id TEXT,
|
|
241
|
+
region TEXT NOT NULL,
|
|
242
|
+
product_code TEXT,
|
|
243
|
+
status TEXT NOT NULL DEFAULT 'WAITPAY',
|
|
244
|
+
price TEXT,
|
|
245
|
+
pay_currency TEXT,
|
|
246
|
+
origin_name TEXT, origin_addr TEXT, origin_lat REAL, origin_lng REAL,
|
|
247
|
+
dest_name TEXT, dest_addr TEXT, dest_lat REAL, dest_lng REAL,
|
|
248
|
+
rider_name TEXT, rider_phone TEXT,
|
|
249
|
+
created_at INTEGER DEFAULT (strftime('%s','now')),
|
|
250
|
+
status_updated_at INTEGER,
|
|
251
|
+
FOREIGN KEY (wallet_address) REFERENCES wallets(wallet_address)
|
|
252
|
+
);
|
|
253
|
+
INSERT INTO ride_requests__new (
|
|
254
|
+
request_id, wallet_address, region, product_code, status, price, pay_currency,
|
|
255
|
+
origin_name, origin_addr, origin_lat, origin_lng,
|
|
256
|
+
dest_name, dest_addr, dest_lat, dest_lng, rider_name, rider_phone,
|
|
257
|
+
created_at, status_updated_at
|
|
258
|
+
) SELECT
|
|
259
|
+
request_id, wallet_address, region, product_code, status, price, pay_currency,
|
|
260
|
+
origin_name, origin_addr, origin_lat, origin_lng,
|
|
261
|
+
dest_name, dest_addr, dest_lat, dest_lng, rider_name, rider_phone,
|
|
262
|
+
created_at, status_updated_at
|
|
263
|
+
FROM ride_requests;
|
|
264
|
+
DROP TABLE ride_requests;
|
|
265
|
+
ALTER TABLE ride_requests__new RENAME TO ride_requests;
|
|
266
|
+
`),L.exec(`
|
|
267
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_places_dedup ON places(wallet_address, dedup_key);
|
|
268
|
+
CREATE INDEX IF NOT EXISTS idx_places_wallet ON places(wallet_address);
|
|
269
|
+
CREATE INDEX IF NOT EXISTS idx_places_label ON places(wallet_address, label);
|
|
270
|
+
CREATE INDEX IF NOT EXISTS idx_places_hit ON places(wallet_address, hit_count DESC, last_used_at DESC);
|
|
271
|
+
CREATE INDEX IF NOT EXISTS idx_bridge_jobs_wallet_status ON bridge_jobs (wallet_address, status);
|
|
272
|
+
`);let f=Math.floor(Date.now()/1e3),_=L.prepare(`
|
|
273
|
+
DELETE FROM jwt_cache
|
|
274
|
+
WHERE (kind != 'member' AND expires_at <= ?)
|
|
275
|
+
OR (kind = 'member' AND refresh_expires_at IS NOT NULL AND refresh_expires_at <= ?)
|
|
276
|
+
`).run(f,f);L.prepare("DELETE FROM search_cache WHERE expires_at <= ?").run(Date.now()),L.prepare("DELETE FROM payment_cache WHERE expires_at <= ?").run(Date.now()),oe(`[jwt-debug] INIT db=${n} now=${f} cleanup_removed_jwt=${_.changes} pid=${process.pid}
|
|
277
|
+
`)}function T(){if(!L)throw new Error("DB not initialized. Call initDb() first.");return L}function mr(e){T().prepare(`
|
|
278
|
+
INSERT OR REPLACE INTO wallets (wallet_address, wallet_type, label)
|
|
279
|
+
VALUES (?, 'privy', ?)
|
|
280
|
+
`).run(e.wallet_address,e.label??null),T().prepare(`
|
|
281
|
+
INSERT OR REPLACE INTO privy_wallet_meta (wallet_address, wallet_id, quorum_id, k_i_pub, k_i_key_path)
|
|
282
|
+
VALUES (?, ?, ?, ?, ?)
|
|
283
|
+
`).run(e.wallet_address,e.wallet_id,e.quorum_id,e.k_i_pub,e.k_i_key_path)}function qi(e){T().prepare(`
|
|
284
|
+
INSERT OR REPLACE INTO wallets (wallet_address, wallet_type, label)
|
|
285
|
+
VALUES (?, 'external', ?)
|
|
286
|
+
`).run(e.wallet_address,e.label??null)}function gr(){return T().prepare("SELECT * FROM wallets ORDER BY created_at ASC").all()}function fr(){return T().prepare("SELECT * FROM wallets WHERE last_valid = 1").all()}function ye(e){return T().prepare("SELECT * FROM wallets WHERE wallet_address = ?").get(e)}function ae(e){return T().prepare("SELECT * FROM privy_wallet_meta WHERE wallet_address = ?").get(e)}function Ae(e,t){T().prepare(`
|
|
287
|
+
UPDATE wallets SET last_valid = 0, invalid_reason = ?, last_validity_check_at = CURRENT_TIMESTAMP
|
|
288
|
+
WHERE wallet_address = ?
|
|
289
|
+
`).run(t,e)}function _r(e){T().prepare(`
|
|
290
|
+
UPDATE wallets SET last_valid = 1, invalid_reason = NULL, last_validity_check_at = CURRENT_TIMESTAMP
|
|
291
|
+
WHERE wallet_address = ?
|
|
292
|
+
`).run(e)}function yr(){return T().prepare("SELECT * FROM pending_sessions ORDER BY created_at DESC, rowid DESC LIMIT 1").get()}function ji(e){T().prepare(`
|
|
293
|
+
INSERT INTO pending_sessions (session_token, k_i_pub, k_i_key_path)
|
|
294
|
+
VALUES (@session_token, @k_i_pub, @k_i_key_path)
|
|
295
|
+
`).run(e)}function We(e){T().prepare("DELETE FROM pending_sessions WHERE session_token = ?").run(e)}function Gi(e,t){T().prepare("INSERT OR REPLACE INTO member_login_sessions (session_token, expires_at, created_at) VALUES (?, ?, ?)").run(e,t,Math.floor(Date.now()/1e3))}function Wi(){return T().prepare("SELECT * FROM member_login_sessions ORDER BY created_at DESC, rowid DESC LIMIT 1").get()}function hr(e){T().prepare("DELETE FROM member_login_sessions WHERE session_token = ?").run(e)}function Vi(){T().prepare("DELETE FROM member_login_sessions").run()}function Ji(e,t,r,n="siwe"){T().prepare(`
|
|
296
|
+
INSERT OR REPLACE INTO jwt_cache (cache_key, token, expires_at, kind, refresh_token, refresh_expires_at)
|
|
297
|
+
VALUES (?, ?, ?, ?, NULL, NULL)
|
|
298
|
+
`).run(e,t,r,n)}function Ki(e,t,r,n,s){T().prepare(`
|
|
299
|
+
INSERT OR REPLACE INTO jwt_cache (cache_key, token, expires_at, kind, refresh_token, refresh_expires_at)
|
|
300
|
+
VALUES (?, ?, ?, 'member', ?, ?)
|
|
301
|
+
`).run(e,t,r,n,s)}function P(e,t="siwe"){let r=Math.floor(Date.now()/1e3);return T().prepare(`
|
|
302
|
+
SELECT token FROM jwt_cache WHERE cache_key = ? AND kind = ? AND expires_at > ?
|
|
303
|
+
`).get(e,t,r)?.token??null}function wr(e){let t=Math.floor(Date.now()/1e3);return T().prepare(`
|
|
304
|
+
SELECT refresh_token, refresh_expires_at
|
|
305
|
+
FROM jwt_cache
|
|
306
|
+
WHERE cache_key = ? AND kind = 'member'
|
|
307
|
+
AND refresh_token IS NOT NULL
|
|
308
|
+
AND (refresh_expires_at IS NULL OR refresh_expires_at > ?)
|
|
309
|
+
`).get(e,t)??null}function Yi(e,t,r=18e4){let n=Date.now()+r;T().prepare(`
|
|
310
|
+
INSERT OR REPLACE INTO search_cache (check_sum, raw, expires_at)
|
|
311
|
+
VALUES (?, ?, ?)
|
|
312
|
+
`).run(e,t,n)}function Xi(e){let t=Date.now();return T().prepare(`
|
|
313
|
+
SELECT raw FROM search_cache WHERE check_sum = ? AND expires_at > ?
|
|
314
|
+
`).get(e,t)?.raw??null}function zi(e,t,r=6e5){let n=Date.now()+r;T().prepare(`
|
|
315
|
+
INSERT OR REPLACE INTO payment_cache (request_id, data, expires_at)
|
|
316
|
+
VALUES (?, ?, ?)
|
|
317
|
+
`).run(e,JSON.stringify(t),n)}function Qi(e){let t=Date.now(),r=T().prepare(`
|
|
318
|
+
SELECT data FROM payment_cache WHERE request_id = ? AND expires_at > ?
|
|
319
|
+
`).get(e,t);return r?JSON.parse(r.data):null}function Er(e){T().prepare(`
|
|
320
|
+
INSERT OR REPLACE INTO ride_requests (
|
|
321
|
+
request_id, wallet_address, member_id, region, product_code, status, price, pay_currency,
|
|
322
|
+
origin_name, origin_addr, origin_lat, origin_lng,
|
|
323
|
+
dest_name, dest_addr, dest_lat, dest_lng,
|
|
324
|
+
rider_name, rider_phone
|
|
325
|
+
) VALUES (
|
|
326
|
+
@request_id, @wallet_address, @member_id, @region, @product_code, @status, @price, @pay_currency,
|
|
327
|
+
@origin_name, @origin_addr, @origin_lat, @origin_lng,
|
|
328
|
+
@dest_name, @dest_addr, @dest_lat, @dest_lng,
|
|
329
|
+
@rider_name, @rider_phone
|
|
330
|
+
)
|
|
331
|
+
`).run({...e,wallet_address:e.wallet_address??null,member_id:e.member_id??null,status:e.status??"WAITPAY",product_code:e.product_code??null,price:e.price??null,pay_currency:e.pay_currency??null,origin_name:e.origin_name??null,origin_addr:e.origin_addr??null,origin_lat:e.origin_lat??null,origin_lng:e.origin_lng??null,dest_name:e.dest_name??null,dest_addr:e.dest_addr??null,dest_lat:e.dest_lat??null,dest_lng:e.dest_lng??null,rider_name:e.rider_name??null,rider_phone:e.rider_phone??null})}function V(e){return T().prepare("SELECT * FROM ride_requests WHERE request_id = ?").get(e)}function Ve(e,t){T().prepare(`
|
|
332
|
+
UPDATE ride_requests SET status = ?, status_updated_at = strftime('%s', 'now') WHERE request_id = ?
|
|
333
|
+
`).run(t,e)}function Rr(e,t){T().prepare(`
|
|
334
|
+
INSERT OR REPLACE INTO phone_verifications (wallet_address, phone, verified_at)
|
|
335
|
+
VALUES (?, ?, CURRENT_TIMESTAMP)
|
|
336
|
+
`).run(e,t)}function Zi(e){return T().prepare("SELECT phone FROM phone_verifications WHERE wallet_address = ? ORDER BY verified_at ASC").all(e).map(r=>r.phone)}function eo(e,t){return T().prepare("SELECT 1 FROM phone_verifications WHERE wallet_address = ? AND phone = ?").get(e,t)!==void 0}function to(e,t="siwe"){let r=Math.floor(Date.now()/1e3);return T().prepare(`
|
|
337
|
+
SELECT token, expires_at FROM jwt_cache WHERE cache_key = ? AND kind = ? AND expires_at > ?
|
|
338
|
+
`).get(e,t,r)??null}function ro(e){let t=Math.floor(Date.now()/1e3),r=T();r.prepare("DELETE FROM tada_members WHERE id <> ?").run(e.id),r.prepare(`
|
|
339
|
+
INSERT INTO tada_members (id, phone, display_name, region, created_at, last_active_at)
|
|
340
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
341
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
342
|
+
phone = excluded.phone,
|
|
343
|
+
display_name = excluded.display_name,
|
|
344
|
+
region = excluded.region,
|
|
345
|
+
last_active_at = excluded.last_active_at
|
|
346
|
+
`).run(e.id,e.phone??null,e.display_name??null,e.region??null,t,t)}function St(){return T().prepare("SELECT * FROM tada_members ORDER BY last_active_at DESC LIMIT 1").get()}function no(e){let t=T();t.prepare("DELETE FROM jwt_cache WHERE cache_key = ? AND kind = 'member'").run(e),t.prepare("DELETE FROM tada_members WHERE id = ?").run(e)}function so(e,t){return!!T().prepare("SELECT 1 FROM processed_events WHERE ride_id=? AND event_id=?").get(e,t)}function io(e,t,r){T().prepare("INSERT OR IGNORE INTO processed_events(ride_id,event_id,seq) VALUES(?,?,?)").run(e,t,r)}function oo(e){return T().prepare("SELECT MAX(seq) AS m FROM processed_events WHERE ride_id=?").get(e).m??0}function it(e){if(e==null)return null;let t=e.trim();return t.length===0?null:t.toLowerCase()}function qn(e){let t=it(e.label),r=st({placeId:e.google_place_id??void 0,latitude:e.latitude,longitude:e.longitude});if(!r)throw new Error("upsertSavedPlace: cannot compute dedup_key (missing placeId and coords)");let n=T().prepare(`
|
|
347
|
+
INSERT INTO places (
|
|
348
|
+
wallet_address, label, name, formatted_address, latitude, longitude,
|
|
349
|
+
google_place_id, city, region, ftid, kg_id, source_url,
|
|
350
|
+
is_favorite, hit_count, last_used_at, dedup_key, updated_at
|
|
351
|
+
) VALUES (
|
|
352
|
+
@wallet_address, @label, @name, @formatted_address, @latitude, @longitude,
|
|
353
|
+
@google_place_id, @city, @region, @ftid, @kg_id, @source_url,
|
|
354
|
+
1, 0, NULL, @dedup_key,
|
|
355
|
+
strftime('%s', 'now')
|
|
356
|
+
)
|
|
357
|
+
`).run({...e,label:t,google_place_id:e.google_place_id??null,ftid:e.ftid??null,kg_id:e.kg_id??null,dedup_key:r});return T().prepare("SELECT * FROM places WHERE id = ?").get(n.lastInsertRowid)}var pr="is_favorite DESC, hit_count DESC, last_used_at DESC NULLS LAST, created_at DESC";function jn(e,t={}){let r=["wallet_address = ?"],n=[e];t.favoritesOnly&&r.push("is_favorite = 1"),t.historyOnly&&r.push("is_favorite = 0");let s=`SELECT * FROM places WHERE ${r.join(" AND ")}
|
|
358
|
+
ORDER BY ${pr}`;return t.limit&&t.limit>0&&(s+=" LIMIT ?",n.push(t.limit)),T().prepare(s).all(...n)}function ao(e,t,r={}){let n=Array.from(new Set(t.map(A=>A.trim().toLowerCase()).filter(A=>A.length>0)));if(n.length===0)return{matches:jn(e,r),matched_tier:null,normalized_queries:[]};let s=T(),i=[];r.favoritesOnly&&i.push("is_favorite = 1"),r.historyOnly&&i.push("is_favorite = 0");let o=i.length>0?` AND ${i.join(" AND ")}`:"",a=!!(r.limit&&r.limit>0),c=a?" LIMIT ?":"",l=n.map(()=>"?").join(","),d=[e,...n];a&&d.push(r.limit);let u=s.prepare(`SELECT * FROM places
|
|
359
|
+
WHERE wallet_address = ? AND label IN (${l})${o}
|
|
360
|
+
ORDER BY ${pr}${c}`).all(...d);if(u.length>0)return{matches:u,matched_tier:"label",normalized_queries:n};let m=n.map(A=>`%${A}%`),p=n.map(()=>"name LIKE ? COLLATE NOCASE").join(" OR "),f=[e,...m];a&&f.push(r.limit);let _=s.prepare(`SELECT * FROM places
|
|
361
|
+
WHERE wallet_address = ? AND (${p})${o}
|
|
362
|
+
ORDER BY ${pr}${c}`).all(...f);if(_.length>0)return{matches:_,matched_tier:"name",normalized_queries:n};let h=n.map(()=>"formatted_address LIKE ? COLLATE NOCASE").join(" OR "),I=[e,...m];a&&I.push(r.limit);let E=s.prepare(`SELECT * FROM places
|
|
363
|
+
WHERE wallet_address = ? AND (${h})${o}
|
|
364
|
+
ORDER BY ${pr}${c}`).all(...I);return E.length>0?{matches:E,matched_tier:"address",normalized_queries:n}:{matches:[],matched_tier:null,normalized_queries:n}}function co(e,t){let r=T().prepare("SELECT * FROM places WHERE wallet_address = ? AND id = ?").get(e,t);return r?(T().prepare("DELETE FROM places WHERE wallet_address = ? AND id = ?").run(e,t),r):null}function lo(e,t){let r=it(t);if(!r)return null;let n=T().prepare("SELECT * FROM places WHERE wallet_address = ? AND label = ?").get(e,r);return n?(T().prepare("DELETE FROM places WHERE wallet_address = ? AND id = ?").run(e,n.id),n):null}function uo(e){let t=T(),r=t.prepare("SELECT * FROM places WHERE wallet_address = ? AND dedup_key = ?"),n=r.get(e.wallet_address,e.dedup_key);if(!n&&e.dedup_key.startsWith("gid:")){let i=`coord:${e.latitude.toFixed(4)},${e.longitude.toFixed(4)}`,o=r.get(e.wallet_address,i);if(o)if(t.prepare("SELECT id FROM places WHERE wallet_address = ? AND dedup_key = ?").get(e.wallet_address,e.dedup_key))n=r.get(e.wallet_address,e.dedup_key);else try{t.prepare(`UPDATE places SET dedup_key = ?,
|
|
365
|
+
google_place_id = COALESCE(google_place_id, ?)
|
|
366
|
+
WHERE wallet_address = ? AND id = ?`).run(e.dedup_key,e.google_place_id,e.wallet_address,o.id),n=r.get(e.wallet_address,e.dedup_key)}catch(c){if(c.code!=="SQLITE_CONSTRAINT_UNIQUE")throw c;process.stderr.write(`[place-history] coord\u2192gid upgrade race for wallet=${e.wallet_address} key=${e.dedup_key}; falling through to gid row
|
|
367
|
+
`),n=r.get(e.wallet_address,e.dedup_key)}}if(n)return t.prepare(`UPDATE places SET hit_count = hit_count + 1, last_used_at = ?
|
|
368
|
+
WHERE wallet_address = ? AND id = ?`).run(e.ts,e.wallet_address,n.id),t.prepare("SELECT * FROM places WHERE id = ?").get(n.id);let s=t.prepare(`
|
|
369
|
+
INSERT INTO places (
|
|
370
|
+
wallet_address, label, name, formatted_address, latitude, longitude,
|
|
371
|
+
google_place_id, city, region, ftid, kg_id, source_url,
|
|
372
|
+
is_favorite, hit_count, last_used_at, dedup_key, updated_at
|
|
373
|
+
) VALUES (
|
|
374
|
+
@wallet_address, NULL, @name, @formatted_address, @latitude, @longitude,
|
|
375
|
+
@google_place_id, NULL, @region, NULL, NULL, NULL,
|
|
376
|
+
0, 1, @ts, @dedup_key, strftime('%s', 'now')
|
|
377
|
+
)
|
|
378
|
+
`).run(e);return t.prepare("SELECT * FROM places WHERE id = ?").get(s.lastInsertRowid)}function Gn(e,t){return T().prepare("SELECT * FROM places WHERE wallet_address = ? AND dedup_key = ?").get(e,t)}function Wn(e,t,r){let n=T();if(!n.prepare("SELECT * FROM places WHERE wallet_address = ? AND dedup_key = ?").get(e,t))return null;let i="label"in r&&r.label!==void 0?1:0,o=i?it(r.label):null;return n.prepare(`
|
|
379
|
+
UPDATE places SET
|
|
380
|
+
is_favorite = 1,
|
|
381
|
+
label = CASE WHEN @label_provided THEN @label ELSE label END,
|
|
382
|
+
source_url = CASE WHEN @source_url_provided THEN @source_url ELSE source_url END,
|
|
383
|
+
google_place_id = COALESCE(@google_place_id, google_place_id),
|
|
384
|
+
ftid = COALESCE(@ftid, ftid),
|
|
385
|
+
kg_id = COALESCE(@kg_id, kg_id),
|
|
386
|
+
formatted_address = COALESCE(@formatted_address, formatted_address),
|
|
387
|
+
name = COALESCE(@name, name),
|
|
388
|
+
city = COALESCE(@city, city),
|
|
389
|
+
region = COALESCE(@region, region),
|
|
390
|
+
updated_at = strftime('%s', 'now')
|
|
391
|
+
WHERE wallet_address = @wallet_address AND dedup_key = @dedup_key
|
|
392
|
+
`).run({wallet_address:e,dedup_key:t,label:o,label_provided:i,source_url:r.source_url??null,source_url_provided:"source_url"in r&&r.source_url!==void 0?1:0,google_place_id:r.google_place_id??null,ftid:r.ftid??null,kg_id:r.kg_id??null,formatted_address:r.formatted_address??null,name:r.name??null,city:r.city??null,region:r.region??null}),n.prepare("SELECT * FROM places WHERE wallet_address = ? AND dedup_key = ?").get(e,t)}function At(e,t){return T().prepare("SELECT * FROM places WHERE wallet_address = ? AND id = ?").get(e,t)}function po(e,t,r){if(!At(e,t))return null;let s=r!==void 0?it(r):void 0;return T().prepare(`
|
|
393
|
+
UPDATE places SET
|
|
394
|
+
is_favorite = 1,
|
|
395
|
+
label = CASE WHEN ? THEN ? ELSE label END,
|
|
396
|
+
updated_at = strftime('%s','now')
|
|
397
|
+
WHERE wallet_address = ? AND id = ?
|
|
398
|
+
`).run(s!==void 0?1:0,s??null,e,t),T().prepare("SELECT * FROM places WHERE wallet_address = ? AND id = ?").get(e,t)}function mo(e,t){let r=it(t);if(r)return T().prepare("SELECT * FROM places WHERE wallet_address = ? AND label = ?").get(e,r)}function go(e,t){let r=At(e,t);return r?r.is_favorite===0?r:(T().prepare(`UPDATE places SET is_favorite = 0, label = NULL, updated_at = strftime('%s','now')
|
|
399
|
+
WHERE wallet_address = ? AND id = ?`).run(e,t),T().prepare("SELECT * FROM places WHERE wallet_address = ? AND id = ?").get(e,t)):null}function fo(e,t){let r=it(t);if(!r)return null;let n=T().prepare("SELECT * FROM places WHERE wallet_address = ? AND label = ?").get(e,r);return n?n.is_favorite===0?n:(T().prepare(`UPDATE places SET is_favorite = 0, label = NULL, updated_at = strftime('%s','now')
|
|
400
|
+
WHERE wallet_address = ? AND id = ?`).run(e,n.id),T().prepare("SELECT * FROM places WHERE wallet_address = ? AND id = ?").get(e,n.id)):null}var _o;function O(e){_o=e.replace(/\/$/,"")}async function de(e,t,r,n,s){let i={"Content-Type":"application/json","x-auth-client-id":"TadaRideAiAgent"};n&&(i.Authorization=`Bearer ${n}`);let o=await fetch(`${_o}${t}`,{method:e,headers:i,body:r!==void 0?JSON.stringify(r):void 0,...s?{signal:s}:{}});if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`HTTP ${o.status} ${o.statusText} on ${e} ${t}: ${a}`)}return o.json()}function yo(e){return de("POST","/quorum-wallet/session",{kIPub:e})}async function br(e){let t=await de("GET",`/quorum-wallet/session/${e}`);return{...t,status:String(t.status).toUpperCase()}}function Tr(e){return de("POST","/quorum-wallet/validate",e)}function ho(e,t){return de("POST","/quorum-wallet/cosign",e,void 0,t)}function wo(e){return de("POST","/quorum-wallet/sendtx",e)}function Eo(e){return de("POST","/quorum-wallet/sendcalls",e)}function Ro(e,t){return de("POST","/auth/siwe/nonce",e,void 0,t)}function bo(e,t){return de("POST","/auth/siwe/token",e,void 0,t)}function To(e,t){return de("POST","/auth/siwe/users",e,void 0,t)}function So(e){return de("GET",`/quorum-wallet/transaction/${e}`)}function Ao(e,t){return de("GET",`/auth/siwe/users/${e}`,void 0,void 0,t)}import ue from"fs";import xt from"path";function It(e,t,r){return`${e}?handle=${t}&pub=${encodeURIComponent(r)}`}async function Io(e,t,r=!1){let n=r?[]:fr();if(n.length>0){let m=[],p=[];for(let f of n){let _=ae(f.wallet_address);if(!_){p.push(f.wallet_address);continue}try{let h=Q(e.keysDir,_.k_i_key_path);if(!ue.existsSync(h)){p.push(f.wallet_address);continue}let I=ue.readFileSync(h),E=fe(I,t),A=E.toString("utf8"),N=Qt(A);if(ne(E),N!==_.k_i_pub){p.push(f.wallet_address);continue}m.push({wallet_address:f.wallet_address,wallet_id:_.wallet_id})}catch{p.push(f.wallet_address)}}if(m.length>0){if(p.length>0)for(let f of p)Ae(f,"Local validation failed");return{status:"ready",wallets:m}}for(let f of p)Ae(f,"Local validation failed")}let s=yr();if(s){try{let m=await br(s.session_token);if(m.status==="PENDING")return{status:"auth_pending",auth_url:It(e.webappUrl,s.session_token,s.k_i_pub),next_action:"AFTER_USER_COMPLETES_AUTH_CALL_wallet_check_auth"};if(m.status==="DONE"){let{quorumId:p,walletId:f,walletAddress:_}=m,h=e.keysDir,I=Q(h,s.k_i_key_path),E=xt.join(h,`${_}_private.enc`);return ue.renameSync(I,E),ue.rmSync(xt.join(h,`${s.session_token}_public.pem`),{force:!0}),mr({wallet_address:_,wallet_id:f,quorum_id:p,k_i_pub:s.k_i_pub,k_i_key_path:er(E)}),_r(_),We(s.session_token),{status:"ready",wallets:[{wallet_address:_,wallet_id:f}]}}}catch{}ue.rmSync(Q(e.keysDir,s.k_i_key_path),{force:!0}),ue.rmSync(xt.join(e.keysDir,`${s.session_token}_public.pem`),{force:!0}),We(s.session_token)}let{publicKeyPem:i,privateKeyPem:o}=si(),a=ii(i),{sessionToken:c}=await yo(a),l=It(e.webappUrl,c,a),d=xt.join(e.keysDir,`${c}_private.enc`),u=xt.join(e.keysDir,`${c}_public.pem`);try{ue.mkdirSync(e.keysDir,{recursive:!0,mode:448});let m=Buffer.from(o,"utf8"),p=ai(m,t);ne(m),ue.writeFileSync(d,p,{mode:384}),ue.writeFileSync(u,i,{mode:420}),ji({session_token:c,k_i_pub:a,k_i_key_path:d})}catch(m){throw ue.rmSync(d,{force:!0}),ue.rmSync(u,{force:!0}),m}return{status:"auth_required",auth_url:l,next_action:"SHOW_AUTH_URL_TO_USER_THEN_IMMEDIATELY_CALL_wallet_check_auth_WITHOUT_WAITING_FOR_USER_CONFIRMATION"}}import xo from"fs";import vo from"path";var uu=1e3;async function Vn(e,t={}){let r=yr();if(!r)return{status:"no_pending"};let{session_token:n,k_i_pub:s,k_i_key_path:i}=r,o=It(e.webappUrl,n,s),a=Date.now()+(t.waitMs??0),c;for(;c=await br(n),c.status==="PENDING";){if(Date.now()>=a)return{status:"pending",auth_url:o};await new Promise(h=>setTimeout(h,uu))}if(c.status!=="DONE")return We(n),{status:"failed",reason:c.status==="FAILED"?c.reason:`Unexpected session status: ${c.status}`};let{quorumId:l,walletId:d,walletAddress:u}=c,m=e.keysDir,p=Q(m,i),f=vo.join(m,`${u}_private.enc`),_=vo.join(m,`${n}_public.pem`);try{xo.renameSync(p,f)}catch(h){throw We(n),new Error(`Failed to rename key file: ${h}`)}return xo.rmSync(_,{force:!0}),mr({wallet_address:u,wallet_id:d,quorum_id:l,k_i_pub:s,k_i_key_path:er(f)}),_r(u),We(n),{status:"ready",wallet_address:u}}function k(e){ye(e)||(g({error:"WALLET_NOT_FOUND",message:`Wallet ${e} is not registered.`,action:"Run `amb wallet-setup --no-wait` first to create the built-in wallet."}),process.exit(1))}function $(e){let t=P(e),r=Math.floor(Date.now()/1e3);oe(`[jwt-debug] GET cache_key=${e} now=${r} found=${!!t} pid=${process.pid}
|
|
401
|
+
`),t||(g({error:"JWT_NOT_FOUND",message:`No valid JWT for wallet ${e}.`,action:"Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1))}import{createPublicClient as Ou,http as Mu,formatUnits as es,parseAbi as Uu}from"viem";import{baseSepolia as Do,base as Bu,mainnet as Fu,sepolia as $u}from"viem/chains";function No(e){let t=new Map,r=(i,o,a)=>{let c=t.get(i);if(c)return c;let l={network:i,chainId:o,rpcUrl:a,tokens:[{kind:"native"}]};return t.set(i,l),l},n=(i,o)=>{i.tokens.some(c=>c.kind==="erc20"&&c.address.toLowerCase()===o.toLowerCase())||i.tokens.push({kind:"erc20",symbol:"USDC",decimals:6,address:o})};n(r(e.paymentNetwork,e.paymentChainId,e.paymentRpcUrl),e.paymentUsdcAddress);let s=e.bridge?.source;s&&n(r(s.name,s.chainId,s.rpcUrl),s.usdcAddress);for(let i of e.depositNetworks)r(i.name,i.chainId,i.rpcUrl).tokens.push({kind:"mvl",deposit:i});return[...t.values()]}import{createPublicClient as _u,http as yu,parseAbi as hu}from"viem";import{baseSepolia as wu,base as Eu,mainnet as Ru,sepolia as bu}from"viem/chains";import{parseAbi as pu}from"viem";var ot=pu(["function mvlReserve() view returns (uint256)","function supportedTokens() view returns (address[])","function isAllowedToken(address token) view returns (bool)","function maxDeposit(address token) view returns (uint256)","function tokenDeposits(address agent, address token) view returns (uint256)","function maxPriceAge() view returns (uint256)","function isQuoteNonceUsed(address agent, uint256 nonce) view returns (bool)","function isDepositNonceUsed(address agent, uint256 nonce) view returns (bool)","function DOMAIN_SEPARATOR() view returns (bytes32)","function depositMVL(address agent, uint256 amount)","function withdraw()"]);import{parseAbi as mu}from"viem";var at=mu(["function deposits(address agent) view returns (uint256)","function isAgentActive(address agent) view returns (bool)","function nextWithdrawAt(address agent) view returns (uint256)","function minRequiredBalance() view returns (uint256)","function withdrawCooldown() view returns (uint256)","function router() view returns (address)","function mvlToken() view returns (address)","function treasury() view returns (uint256)"]);import{encodeFunctionData as Jn,parseAbi as gu}from"viem";var fu=gu(["function approve(address spender, uint256 amount) returns (bool)"]);function Kn(e,t){return Jn({abi:fu,functionName:"approve",args:[e,t]})}function Yn(e,t){return Jn({abi:ot,functionName:"depositMVL",args:[e,t]})}function Xn(){return Jn({abi:ot,functionName:"withdraw",args:[]})}var Tu={84532:wu,8453:Eu,1:Ru,11155111:bu},Qh=hu(["function allowance(address owner, address spender) view returns (uint256)"]);function Su(e){let t=Tu[e];if(!t)throw new Error(`Unsupported chainId: ${e}`);return t}function Je(e){return _u({chain:Su(e.chainId),transport:yu(e.rpcUrl)})}async function Sr(e,t){return await Je(e).readContract({address:e.agentDepositLedger,abi:at,functionName:"deposits",args:[t]})}async function Ar(e,t){return await Je(e).readContract({address:e.agentDepositLedger,abi:at,functionName:"isAgentActive",args:[t]})}async function zn(e){return(await Je(e).readContract({address:e.depositContract,abi:ot,functionName:"supportedTokens"})).filter(r=>!/^0x0{40}$/i.test(r))}async function Ir(e,t){return await Je(e).readContract({address:e.agentDepositLedger,abi:at,functionName:"nextWithdrawAt",args:[t]})}async function vt(e){return await Je(e).readContract({address:e.agentDepositLedger,abi:at,functionName:"minRequiredBalance"})}async function Ke(e){return await Je(e).readContract({address:e.agentDepositLedger,abi:at,functionName:"mvlToken"})}async function ko(e){return await Je(e).readContract({address:e.depositContract,abi:ot,functionName:"mvlReserve"})}import{createPublicClient as Au,http as Iu,parseAbi as xu,formatUnits as nw}from"viem";import{baseSepolia as vu,base as Nu,mainnet as ku,sepolia as Cu}from"viem/chains";var Du={84532:vu,8453:Nu,1:ku,11155111:Cu},Qn=xu(["function symbol() view returns (string)","function name() view returns (string)","function decimals() view returns (uint8)"]),Pu={BASE_SEPOLIA:{"0x036cbd53842c5426634e7929541ec2318f3dcf7e":{symbol:"USDC",name:"USDC",decimals:6}},BASE:{"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913":{symbol:"USDC",name:"USD Coin",decimals:6}},ETHEREUM_SEPOLIA:{"0x3cf4ccf7ec4bb094d1504d1315057a46005d5065":{symbol:"MVL",name:"Mass Vehicle Ledger Token",decimals:18}},ETHEREUM:{"0xa849eaae994fb86afa73382e9bd88c2b6b18dc71":{symbol:"MVL",name:"Mass Vehicle Ledger Token",decimals:18}}},Zn=new Map;function Lu(e,t){return`${e}:${t.toLowerCase()}`}async function Ye(e,t){let r=Lu(e.name,t),n=Zn.get(r);if(n)return n;let s=Pu[e.name]?.[t.toLowerCase()];if(s)return Zn.set(r,s),s;let i=Du[e.chainId],o=Au({chain:i,transport:Iu(e.rpcUrl)}),a=t,[c,l,d]=await Promise.all([o.readContract({address:a,abi:Qn,functionName:"symbol"}),o.readContract({address:a,abi:Qn,functionName:"name"}),o.readContract({address:a,abi:Qn,functionName:"decimals"})]),u={symbol:c,name:l,decimals:d};return Zn.set(r,u),u}var Po={84532:Do,8453:Bu,1:Fu,11155111:$u},Hu=Uu(["function balanceOf(address account) view returns (uint256)"]),qu=(e,t)=>Ou({chain:Po[e]??Do,transport:Mu(t)});function Co(e,t,r){return e.readContract({address:t,abi:Hu,functionName:"balanceOf",args:[r]})}async function Lo(e,t,r={}){let n=r.makeClient??qu,s=r.mvlTokenReader??Ke,i=r.tokenMetadata??Ye,o=No(t),a=await Promise.all(o.map(async m=>{let p;try{p=n(m.chainId,m.rpcUrl)}catch(f){return m.tokens.map(_=>({status:"rejected",network:m.network,symbol:d(_),error:f.message}))}return await Promise.all(m.tokens.map(async f=>{try{return{status:"fulfilled",row:await u(f,m,p)}}catch(_){return{status:"rejected",network:m.network,symbol:d(f),error:_.message}}}))})),c=[],l=[];for(let m of a.flat())m.status==="fulfilled"?c.push(m.row):l.push({network:m.network,symbol:m.symbol,error:m.error});return{balances:c,errors:l};function d(m){return m.kind==="erc20"?m.symbol:m.kind==="mvl"?"MVL":"native"}async function u(m,p,f){let _=p.network;if(m.kind==="native"){let A=await f.getBalance({address:e}),N=Po[p.chainId]?.nativeCurrency.symbol??"ETH";return{network:_,symbol:N,amount:es(A,18)}}if(m.kind==="erc20"){let A=await Co(f,m.address,e);return{network:_,symbol:m.symbol,amount:es(A,m.decimals),address:m.address}}let h=await s(m.deposit),I=await i(m.deposit,h),E=await Co(f,h,e);return{network:_,symbol:I.symbol,amount:es(E,I.decimals),address:h}}}import Mo from"fs";import{p256 as ju}from"@noble/curves/p256";import{sha256 as Gu}from"@noble/hashes/sha2";import{formatRequestForAuthorizationSignature as Wu}from"@privy-io/node";import Vu from"crypto";function Oo(e,t){let n=Vu.createPrivateKey(t).export({format:"jwk"}),s=Buffer.from(n.d,"base64url"),i=ju.sign(Gu(typeof e=="string"?Buffer.from(e):e),s);return Buffer.from(i.toDERRawBytes()).toString("base64")}function ct(e,t,r,n,s){let i={version:1,method:"POST",url:`https://api.privy.io/v1/wallets/${e}/rpc`,body:t,headers:{"privy-app-id":n,...s}},o=Wu(i);return Oo(o,r)}function xr(e,t){return Oo(`${e}`,t)}async function Ce(e,t,r,n){let{wallet_address:s,sign_method:i,message:o}=e;if(!ye(s))throw new Error(`Wallet not found: ${s}`);let c=ae(s);if(!c)throw new Error("External wallet: signing must be done externally");let l=Q(t.keysDir,c.k_i_key_path);if(!Mo.existsSync(l))throw new Error(`Key file not found: ${l}`);let d=Mo.readFileSync(l),u=fe(d,r),m=u.toString("utf8");if(i==="personal_sign"&&typeof o!="string")throw ne(u),new Error("personal_sign requires a string message");if(i==="eth_signTypedData_v4"&&typeof o=="string")throw ne(u),new Error("eth_signTypedData_v4 requires a typed data object");let p=i==="personal_sign"?{method:"personal_sign",params:{encoding:"utf-8",message:o}}:{method:"eth_signTypedData_v4",params:{typed_data:{domain:o.domain,message:o.message,primary_type:o.primaryType,types:o.types}}},f=ct(c.wallet_id,p,m,t.privyAppId),{walletSignature:_}=await ho({signMethod:i,message:o,walletId:c.wallet_id,kISig:f},n);return ne(u),{wallet_signature:_}}import Uo from"fs";var Ju={1:"https://etherscan.io/tx",11155111:"https://sepolia.etherscan.io/tx",8453:"https://basescan.org/tx",84532:"https://sepolia.basescan.org/tx"};function Ku(e){let[t,r=""]=e.split("."),n=r.padEnd(18,"0").slice(0,18);return"0x"+(BigInt(t)*BigInt("1000000000000000000")+BigInt(n)).toString(16)}async function De(e,t,r){let{wallet_address:n,to:s,value_eth:i,chain_id:o,data:a,sponsor:c}=e,l=Ku(i),d=ye(n);if(!d)throw new Error(`Wallet not found: ${n}`);let u=ae(n);if(!u)throw new Error("External wallet: signing must be done externally");let m=Q(t.keysDir,u.k_i_key_path);if(!Uo.existsSync(m))throw new Error(`Key file not found: ${m}`);let p=Uo.readFileSync(m),f=fe(p,r),_=f.toString("utf8");try{let h=Date.now(),I=xr(h,_),E;try{E=await Tr({walletId:u.wallet_id,quorumId:u.quorum_id,kIPub:u.k_i_pub,timestamp:h,kISig:I})}catch(Vt){throw(Vt instanceof Error?Vt.message:String(Vt)).includes("HTTP 401")&&Ae(d.wallet_address,"Unauthorized (401)"),Vt}if(!E.valid)throw Ae(d.wallet_address,E.reason??"Server returned valid:false"),new Error(`Wallet validation failed: ${E.reason??"unknown reason"}`);let A={method:"eth_sendTransaction",caip2:`eip155:${o}`,params:{transaction:{to:s,value:l,...a?{data:a}:{}}},...c?{sponsor:!0}:{}},N=`${Date.now()}:${crypto.randomUUID()}`,C=ct(u.wallet_id,A,_,t.privyAppId,{"privy-idempotency-key":N}),U=await wo({walletId:u.wallet_id,caip2:`eip155:${o}`,transaction:{to:s,value:l,...a?{data:a}:{}},idempotencyKey:N,kISig:C,...c?{sponsor:!0}:{}}),re=U.transactionId?{transactionId:U.transactionId}:{};if(U.kind==="user_operation_hash")return{kind:"user_operation_hash",user_operation_hash:U.userOperationHash,...re};let js=U.txHash,Gs=Ju[o];return{kind:"tx_hash",tx_hash:js,...Gs?{explorer_url:`${Gs}/${js}`}:{},...re}}finally{ne(f)}}var Fo=300*1e3,Xu=async e=>{let t=R();b(t.dataDir);let r=gr();w({wallets:r.map(n=>({address:n.wallet_address,type:n.wallet_type,label:n.label,valid:n.last_valid===1,created_at:n.created_at,...n.wallet_type==="privy"?{wallet_id:ae(n.wallet_address)?.wallet_id}:{}}))})};function zu(e){let t,r;process.platform==="darwin"?(t="open",r=[e]):process.platform==="win32"?(t="cmd",r=["/c","start",'""',e]):(t="xdg-open",r=[e]);try{let n=Yu(t,r,{stdio:"ignore",detached:!0});n.on("error",()=>{}),n.unref()}catch{}}var Qu=async e=>{let t=R();b(t.dataDir),O(t.appServerUrl);let r=e.includes("--force-new"),n=e.includes("--no-wait"),s=Buffer.from(t.passphrase,"utf8"),i;try{i=await Io(t,s,r)}catch(c){g({error:c.message}),process.exit(1)}if(n){w(i);return}if(i.status==="ready"){w(i);return}let o=i.auth_url;process.stderr.write(`Open this URL to authenticate:
|
|
402
|
+
${o}
|
|
403
|
+
|
|
404
|
+
`),zu(o),process.stderr.write(`Waiting for authentication to complete (up to 5 minutes)\u2026
|
|
405
|
+
`);let a;try{a=await Vn(t,{waitMs:Fo})}catch(c){g({error:c.message}),process.exit(1)}if(w(a),a.status==="ready"){process.stderr.write(`Wallet registered: ${a.wallet_address}
|
|
406
|
+
`);return}a.status==="failed"&&(process.stderr.write(`Authentication failed: ${a.reason}
|
|
407
|
+
`),process.exit(1)),a.status==="pending"&&(process.stderr.write("Timeout reached \u2014 re-run `amb wallet-setup` to resume.\n"),process.exit(1)),process.stderr.write("No pending session found \u2014 re-run `amb wallet-setup`.\n"),process.exit(1)},Zu=async e=>{let t=R();b(t.dataDir),O(t.appServerUrl);let r=e.includes("--wait")?Fo:0;try{let n=await Vn(t,{waitMs:r});w(n)}catch(n){g({error:n.message}),process.exit(1)}},ep=async e=>{let[t,r]=e;t||(console.error("error: Usage: amb wallet-add-external <wallet_address> [label]"),process.exit(1));let n=R();b(n.dataDir),qi({wallet_address:t,label:r}),w({status:"registered",wallet_address:t,label:r??null})},tp=async e=>{let[t]=e;t||(console.error("error: Usage: amb wallet-balance <wallet_address>"),process.exit(1));let r=R();b(r.dataDir),k(t);try{let n=await Lo(t,r);w({wallet_address:t,...n})}catch(n){g({error:"RPC_ERROR",message:`Failed to fetch balance: ${n.message}`}),process.exit(1)}},rp=async e=>{let[t,r,n]=e;(!t||!r||!n)&&(console.error("error: Usage: amb wallet-sign <wallet_address> <sign_method> <siwe_file|json>"),console.error("hint: sign_method: personal_sign (SIWE login), eth_signTypedData_v4 (payment/tip)"),console.error(" SIWE: amb wallet-sign 0x... personal_sign /path/to/siwe-file.json"),console.error(` Payment: amb wallet-sign 0x... eth_signTypedData_v4 '{"types":...}'`),process.exit(1));let s=R();b(s.dataDir),k(t),O(s.appServerUrl);let i=Buffer.from(s.passphrase,"utf8"),o;if(r==="personal_sign")if(Bo.existsSync(n)){let{message:a,expiresAt:c}=JSON.parse(Bo.readFileSync(n,"utf8"));Date.now()/1e3>c&&(g({error:"SIWE message expired. Run `amb siwe-request-message` again."}),process.exit(1)),o=a}else o=n.replace(/\\n/g,`
|
|
408
|
+
`);else try{o=JSON.parse(n)}catch{g({error:"eth_signTypedData_v4 requires valid JSON typed data"}),process.exit(1)}try{let a=await Ce({wallet_address:t,sign_method:r,message:o},s,i);w(a)}catch(a){g({error:a.message}),process.exit(1)}},np=async e=>{let[t,r,n,s,i]=e;(!t||!r||!n||!s)&&(console.error("error: Usage: amb wallet-send-tx <wallet_address> <to> <value_eth> <chain_id> [data]"),console.error("hint: chain_id: 84532 (BASE_SEPOLIA), 11155111 (ETH_SEPOLIA)"),process.exit(1));let o=R();b(o.dataDir),k(t),O(o.appServerUrl);let a=Buffer.from(o.passphrase,"utf8");try{let c=await De({wallet_address:t,to:r,value_eth:n,chain_id:parseInt(s,10),data:i},o,a);w(c)}catch(c){g({error:c.message}),process.exit(1)}},$o={"wallet-status":Xu,"wallet-setup":Qu,"wallet-setup-verify":Zu,"wallet-add-external":ep,"wallet-balance":tp,"wallet-sign":rp,"wallet-send-tx":np};import{encodeFunctionData as Dp,formatUnits as Pp}from"viem";import Ho from"fs";import sp from"crypto";function ip(e){let[t,r=""]=e.split("."),n=r.padEnd(18,"0").slice(0,18);return"0x"+(BigInt(t)*BigInt("1000000000000000000")+BigInt(n)).toString(16)}async function vr(e,t,r){let{wallet_address:n,chain_id:s,calls:i,sponsor:o}=e,a=ye(n);if(!a)throw new Error(`Wallet not found: ${n}`);let c=ae(n);if(!c)throw new Error("External wallet: signing must be done externally");let l=Q(t.keysDir,c.k_i_key_path);if(!Ho.existsSync(l))throw new Error(`Key file not found: ${l}`);let d=Ho.readFileSync(l),u=fe(d,r),m=u.toString("utf8");try{let p=Date.now(),f=xr(p,m),_;try{_=await Tr({walletId:c.wallet_id,quorumId:c.quorum_id,kIPub:c.k_i_pub,timestamp:p,kISig:f})}catch(C){throw(C instanceof Error?C.message:String(C)).includes("HTTP 401")&&Ae(a.wallet_address,"Unauthorized (401)"),C}if(!_.valid)throw Ae(a.wallet_address,_.reason??"Server returned valid:false"),new Error(`Wallet validation failed: ${_.reason??"unknown reason"}`);let h=i.map(C=>({to:C.to,value:ip(C.value_eth),...C.data?{data:C.data}:{}})),I={method:"wallet_sendCalls",caip2:`eip155:${s}`,chain_type:"ethereum",...o?{sponsor:!0}:{},params:{calls:h}},E=`${Date.now()}:${sp.randomUUID()}`,A=ct(c.wallet_id,I,m,t.privyAppId,{"privy-idempotency-key":E}),N=await Eo({walletId:c.wallet_id,caip2:`eip155:${s}`,calls:h,idempotencyKey:E,kISig:A,...o?{sponsor:!0}:{}});return{kind:"transaction_id",transaction_id:N.transactionId,caip2:N.caip2}}finally{ne(u)}}import bp from"crypto";import{createSiweMessage as op,parseSiweMessage as ap}from"viem/siwe";var cp=60;async function Nr(e,t,r){let n=await Ro({walletAddress:e,chainId:t},r),s=op({domain:n.domain,address:e,uri:n.uri,version:n.version,chainId:n.chainId,nonce:n.nonce,issuedAt:new Date(n.issuedAt),expirationTime:new Date(n.expirationTime),...n.scheme?{scheme:n.scheme}:{}}),i=Math.floor(new Date(n.expirationTime).getTime()/1e3);return{message:s,expiresAt:i}}async function kr(e,t,r){let n=ap(e),s=Math.floor(Date.now()/1e3),i=n.address,{flag:o}=await Ao(i,r),a=o?await bo({message:e,signature:t},r):await To({message:e,signature:t},r),{accessToken:c,expiresIn:l}=a,d=s+l-cp;return Ji(n.address,c,d),oe(`[jwt-debug] SET cache_key=${n.address} expires_at=${d} now=${s} expiresIn=${l} pid=${process.pid}
|
|
409
|
+
`),{accessToken:c,expiresIn:l}}function j(e){return P(e)}import{hashTypedData as lp,parseSignature as dp,concat as up,toHex as pp,keccak256 as mp,toBytes as gp}from"viem";var pE=mp(gp("Kernel(bytes32 hash)"));function Cr(e){return lp(e)}function Dr(e){let{kernelDomain:t,innerDigest:r}=e;return{domain:{name:t.name,version:t.version,chainId:t.chainId,verifyingContract:t.verifyingContract},types:{EIP712Domain:[{name:"name",type:"string"},{name:"version",type:"string"},{name:"chainId",type:"uint256"},{name:"verifyingContract",type:"address"}],Kernel:[{name:"hash",type:"bytes32"}]},primaryType:"Kernel",message:{hash:r}}}function Pr(e){let{r:t,s:r,yParity:n}=dp(e),s=BigInt(r)|BigInt(n)<<255n;return up(["0x00",t,pp(s,{size:32})])}async function qo(e,t,r){let n=await fetch(`${r.gatewayServerUrl.replace(/\/$/,"")}/v1/agent-deposit/price-quote`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(n.ok)return await n.json();let s="";try{let i=await n.json();(i.code||i.message)&&(s=`${i.code??""} ${i.message??""}`.trim())}catch{}throw s||(s=await n.text().catch(()=>"")||`HTTP ${n.status}`),new Error(`POST /price-quote failed (${n.status}): ${s}`)}import{createPublicClient as fp,http as _p,parseAbi as yp,BaseError as hp,ContractFunctionZeroDataError as wp,ContractFunctionRevertedError as Ep}from"viem";var Rp=yp(["function eip712Domain() view returns (bytes1 fields, string name, string version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] extensions)"]);function ts(e){return fp({transport:_p(e.rpcUrl)})}async function jo(e){return ts(e).getChainId()}async function Lr(e,t){let r=await ts(e).getCode({address:t});return!!r&&r!=="0x"}async function Or(e,t){let r=t;try{let n=await ts(e).readContract({address:r,abi:Rp,functionName:"eip712Domain"});return{name:n[1],version:n[2],chainId:Number(n[3]),verifyingContract:r}}catch(n){if(!(n instanceof hp&&!!n.walk(i=>i instanceof wp||i instanceof Ep)))throw n;return{name:"Kernel",version:"0.3.3",chainId:e.chainId,verifyingContract:r}}}function Tp(e){let{capability:t,params:r}=e;return{domain:{name:t.domainName,version:t.domainVersion,chainId:t.chainId,verifyingContract:t.tokenAddress},types:{EIP712Domain:[{name:"name",type:"string"},{name:"version",type:"string"},{name:"chainId",type:"uint256"},{name:"verifyingContract",type:"address"}],ReceiveWithAuthorization:[{name:"from",type:"address"},{name:"to",type:"address"},{name:"value",type:"uint256"},{name:"validAfter",type:"uint256"},{name:"validBefore",type:"uint256"},{name:"nonce",type:"bytes32"}]},primaryType:"ReceiveWithAuthorization",message:{from:r.from,to:r.to,value:r.value,validAfter:r.validAfter,validBefore:r.validBefore,nonce:r.nonce}}}function Sp(e,t,r){let n=r.toLowerCase();return e.find(s=>s.network===t&&s.tokenAddress.toLowerCase()===n)}function Ap(e){return Math.floor(Date.now()/1e3)+e+60}function Ip(){return"0x"+bp.randomBytes(32).toString("hex")}async function xp(e,t){try{let r=await fetch(`${t.gatewayServerUrl.replace(/\/$/,"")}/v1/agent-deposit/relay-capabilities`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});return r.ok?await r.json():null}catch{return null}}async function vp(e){let{typedData:t,isSmartAccount:r,kernelDomain:n,walletSigner:s,config:i,passphrase:o,agent:a}=e;if(r){if(!n)throw new Error("KERNEL_DOMAIN_REQUIRED: smart-account agent needs a Kernel domain");let l=Cr(t),d=Dr({kernelDomain:n,innerDigest:l}),{wallet_signature:u}=await s({wallet_address:a,sign_method:"eth_signTypedData_v4",message:d},i,o);return Pr(u)}let{wallet_signature:c}=await s({wallet_address:a,sign_method:"eth_signTypedData_v4",message:t},i,o);return c}async function Np(e,t,r){let n=await fetch(`${r.gatewayServerUrl.replace(/\/$/,"")}/v1/agent-deposit/relay`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(n.ok)return await n.json();let s="";try{let i=await n.json();(i.code||i.message)&&(s=`${i.code??""} ${i.message??""}`.trim())}catch{}throw s||(s=await n.text().catch(()=>"")||`HTTP ${n.status}`),new Error(`POST /relay failed (${n.status}): ${s}`)}function rs(e){return{request_id:e.requestId,status:e.status,tx_hash:e.txHash,error_code:e.errorCode,error_message:e.errorMessage}}async function Mr(e,t,r){let n=await fetch(`${r.gatewayServerUrl.replace(/\/$/,"")}/v1/agent-deposit/relay/${encodeURIComponent(t)}`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});if(n.ok)return await n.json();let s=await n.text().catch(()=>"");throw new Error(`GET /relay/${t} failed (${n.status}): ${s||"no body"}`)}function Go(e){return e!=null&&"unavailable"in e}async function kp(e){let{params:t,network:r,config:n,passphrase:s,jwtProvider:i,capabilitiesFetcher:o,priceQuoteFetcher:a,smartAccountProbe:c,walletSigner:l,relaySubmitter:d}=e,u=i(t.wallet_address);if(!u)throw new Error("JWT_NOT_FOUND: No cached JWT for this wallet. Call siwe_get_message + siwe_login first.");let m=await o(u,n);if(!m)return{unavailable:"PROBE_FAILED"};let p=Sp(m.capabilities,r.name,t.token);if(!p)return{unavailable:"NO_CAPABILITY"};if(p.preferred==="PERMIT")throw new Error("PERMIT_NOT_IMPLEMENTED: relay capability is PERMIT-only; v2 supports AUTHORIZATION only");let f=await a(u,{network:r.name,tokenAddress:t.token,tokenAmount:t.amount},n),_={from:t.wallet_address,to:p.depositContractAddress,value:t.amount,validAfter:"0",validBefore:Ap(m.deadlineMinBufferSec).toString(),nonce:Ip()},h=Tp({capability:p,params:_}),{isSmartAccount:I,kernelDomain:E}=await c(r,t.wallet_address),A=await vp({typedData:h,agent:t.wallet_address,isSmartAccount:I,kernelDomain:E,walletSigner:l,config:n,passphrase:s}),N=await d(u,{network:r.name,tokenAddress:t.token,params:{..._,quote:{mvlAmount:f.quote.mvlAmount,nonce:f.quote.nonce,timestamp:f.quote.timestamp,deadline:f.quote.deadline},quoteSignature:f.signature},signature:A},n);return{path:"relay",request_id:N.requestId,status:N.status,mvl_amount:f.quote.mvlAmount,tx_hash:N.txHash,error_code:N.errorCode,error_message:N.errorMessage,next_step:`Run \`amb deposit-relay-status ${t.wallet_address} ${N.requestId}\` until status is CONFIRMED or FAILED`}}var Cp=new Set(["CONFIRMED","FAILED"]);async function Wo(e,t,r,n={}){let s=n.intervalMs??3e3,i=n.timeoutMs??12e4,o=Date.now()+i,a=await Mr(e,t,r);for(;!Cp.has(a.status)&&Date.now()<o;)await new Promise(c=>setTimeout(c,s)),a=await Mr(e,t,r);return a}function Vo(e){return kp({...e,jwtProvider:j,capabilitiesFetcher:xp,priceQuoteFetcher:qo,smartAccountProbe:async(t,r)=>{let n=await Lr(t,r);return{isSmartAccount:n,kernelDomain:n?await Or(t,r):void 0}},walletSigner:Ce,relaySubmitter:Np})}async function Jo(e,t,r,n){let s=await Ke(t);if(e.token.toLowerCase()!==s.toLowerCase()){let a=await Vo({params:e,network:t,config:r,passphrase:n});if(Go(a))throw new Error(a.unavailable==="NO_CAPABILITY"?`TOKEN_NOT_DEPOSITABLE: the deposit relay has no capability for token ${e.token} on ${t.name}, so it cannot be deposited as collateral. MVL (${s}) is always accepted and needs no relay. Run \`amb deposit-tokens ${t.name}\` for the current list. Retrying will not help.`:`RELAY_UNAVAILABLE: the agent-deposit relay could not be reached for ${t.name}, so token ${e.token} cannot be deposited right now. Check the cached JWT and the gateway, then retry. Depositing MVL (${s}) directly needs no relay.`);return a}let i=BigInt(e.amount),o=await vr({wallet_address:e.wallet_address,chain_id:t.chainId,sponsor:!0,calls:[{to:e.token,value_eth:"0",data:Kn(t.depositContract,i)},{to:t.depositContract,value_eth:"0",data:Yn(e.wallet_address,i)}]},r,n);return{path:"mvl-direct",token:e.token,agent:e.wallet_address,amount:e.amount,batch_tx:o}}async function Ko(e,t,r,n){let s=await Ir(t,e.wallet_address),i=BigInt(Math.floor(Date.now()/1e3));if(s>i){let a=Number(s-i),c=Math.ceil(a/60);throw new Error(`WITHDRAW_COOLDOWN: next withdraw allowed at unix ${s.toString()} (~${c} min from now). v2 enforces a per-agent withdraw cooldown.`)}let o=await De({wallet_address:e.wallet_address,to:t.depositContract,value_eth:"0",chain_id:t.chainId,data:Xn(),sponsor:!0},r,n);return{path:"mvl-withdraw",agent:e.wallet_address,withdraw_tx:o}}function Yo(e,t,r){if(e<0n)throw new Error("requiredTokenWei: gapMvl must be >= 0");if(r<=0n)throw new Error("requiredTokenWei: mvlAmount must be > 0");return(e*t+r-1n)/r}function Xo(e,t){let r=10n**BigInt(t),n=(e*100n+r-1n)/r,s=n/100n,i=n%100n;return`${s.toString()}.${i.toString().padStart(2,"0")}`}async function zo(e,t,r){let n=r.gatewayServerUrl.replace(/\/$/,""),s=`network=${encodeURIComponent(t.network)}&tokenAddress=${encodeURIComponent(t.tokenAddress)}`,i=await fetch(`${n}/v1/agent-deposit/exchange-rate?${s}`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});if(i.ok)return await i.json();let o="";try{let a=await i.json();(a.code||a.message)&&(o=`${a.code??""} ${a.message??""}`.trim())}catch{}throw o||(o=await i.text().catch(()=>"")||`HTTP ${i.status}`),new Error(`GET /exchange-rate failed (${i.status}): ${o}`)}var Qo="note: collateral is credited in MVL (not the deposited token); withdrawals return MVL.",Lp="note: withdrawals return MVL regardless of the token originally deposited.";function ea(e){let t=e.message,r=t.match(/^([A-Z][A-Z0-9_]+):\s*(.+)$/s);r?g({error:r[1],message:r[2]}):g({error:t})}var Op="run SIWE login (siwe-request-message then siwe-submit) to compute the token amount needed to activate";async function Mp(e,t,r,n){if(r===0n)return{entries:null,reason:null};let s=j(t);if(!s)return{entries:null,reason:"NO_JWT",hint:Op};try{let i=await zn(e);if(i.length===0)return{entries:null,reason:"RATE_UNAVAILABLE"};let a=(await Promise.allSettled(i.map(async c=>{let l=await Ye(e,c),d=await zo(s,{network:e.name,tokenAddress:c},n),u=Yo(r,BigInt(d.tokenAmount),BigInt(d.mvlAmount));return{tokenAddress:c,symbol:l.symbol,tokenWei:u.toString(),display:`~${Xo(u,l.decimals)} ${l.symbol} to activate`,ratePreview:d.ratePreview}}))).flatMap(c=>c.status==="fulfilled"?[c.value]:[]);return a.length===0?{entries:null,reason:"RATE_UNAVAILABLE"}:{entries:a,reason:null}}catch{return{entries:null,reason:"RATE_UNAVAILABLE"}}}var Up=async e=>{let[t]=e;t||(console.error("error: Usage: amb deposit-status <wallet_address>"),process.exit(1));let r=R();b(r.dataDir),k(t);try{let n=await Promise.allSettled(r.depositNetworks.map(async a=>{let[c,l,d,u,m]=await Promise.all([Ar(a,t),Sr(a,t),Ir(a,t),vt(a),ko(a)]),p=u>l?u-l:0n,f=null,_=null;if(p>0n)try{let A=await Ke(a),N=await Ye(a,A);f={tokenAddress:A,symbol:N.symbol,tokenWei:p.toString(),display:`${Pp(p,N.decimals)} ${N.symbol} to activate`}}catch(A){_=`MVL activation guidance unavailable: ${A.message}`}let h=await Mp(a,t,p,r),I=[...f?[f]:[],...h.entries??[]],E=I.length>0?I:null;return{mvlError:_,network:a.name,isActive:c,deposited:l.toString(),minRequired:u.toString(),nextWithdrawAt:Number(d),mvlReserve:m.toString(),mvlGap:p.toString(),requiredToActivate:E,requiredToActivateReason:h.reason,...h.hint?{requiredToActivateHint:h.hint}:{}}})),s=[],i=[];n.forEach((a,c)=>{if(a.status==="fulfilled"){let{mvlError:l,...d}=a.value;s.push(d),l&&i.push({network:d.network,error:l})}else i.push({network:r.depositNetworks[c].name,error:a.reason?.message})});let o=s.some(a=>a.isActive);w({anyActive:o,networks:s,errors:i})}catch(n){g({error:n.message}),process.exit(1)}},Bp=async e=>{let t=e.includes("--no-wait"),r=e.filter(d=>d!=="--no-wait"),[n,s,i,o]=r;if(!n||!s||!i||!o){console.error("error: Usage: amb deposit-add <wallet_address> <network> <token_address> <amount> [--no-wait]"),console.error('hint: amount is raw units (e.g. 1000 MVL = "1000000000000000000000")');let d=z().networks.depositNetworks;console.error(d.length?` network: ${d.join(", ")}`:" network: run `amb deposit-tokens` to see configured deposit networks"),console.error(" token: run `amb deposit-tokens <network>` for depositable token addresses"),process.exit(1)}let a=R();b(a.dataDir),k(n),O(a.appServerUrl);let c=a.depositNetworks.find(d=>d.name===s);c||(g({error:"UNKNOWN_NETWORK",message:`Network "${s}" not configured. Available: ${a.depositNetworks.map(d=>d.name).join(", ")}`}),process.exit(1));let l=Buffer.from(a.passphrase,"utf8");try{let d=await Jo({wallet_address:n,token:i,amount:o},c,a,l);if(d.path!=="relay"){w(d);return}if(t){let{next_step:p,...f}=d;if(d.status==="FAILED"){w(f),process.exit(2);return}w(d.status==="CONFIRMED"?f:d),console.error(Qo);return}let u=j(n);if(!u){g({error:"JWT_NOT_FOUND",request_id:d.request_id,message:"No cached JWT for this wallet. Call siwe-request-message + siwe-submit first."}),process.exit(1);return}let m=await Wo(u,d.request_id,a);w({path:"relay",mvl_amount:d.mvl_amount,...rs(m)}),m.status==="FAILED"&&process.exit(2),console.error(Qo)}catch(d){ea(d),process.exit(1)}},Fp=async e=>{let t=e[0],r=R(),n=t?r.depositNetworks.filter(s=>s.name===t):r.depositNetworks;n.length===0&&(g({error:"UNKNOWN_NETWORK",message:`Network "${t}" not configured. Available: ${r.depositNetworks.map(s=>s.name).join(", ")}`}),process.exit(1));try{let s=await Promise.allSettled(n.map(async a=>{let c=[],l=await Ke(a),d=null;try{d=await Ye(a,l)}catch(_){c.push(`MVL metadata unavailable: ${_.message}`)}let[u,m]=await Promise.allSettled([zn(a),vt(a)]);u.status==="rejected"&&c.push(`convertible token list unavailable: ${u.reason?.message}`),m.status==="rejected"&&c.push(`minRequiredBalance unavailable: ${m.reason?.message}`);let p=await Promise.allSettled((u.status==="fulfilled"?u.value:[]).map(async _=>{let h=await Ye(a,_);return{token:_,symbol:h.symbol,name:h.name,decimals:h.decimals}})),f=[];return p.forEach((_,h)=>{if(_.status==="fulfilled")f.push(_.value);else{let I=u.status==="fulfilled"?u.value[h]:"(unknown)";c.push(`token ${I} metadata unavailable: ${_.reason?.message}`)}}),{scopedErrors:c,network:a.name,tokens:[{token:l,symbol:d?.symbol??null,name:d?.name??null,decimals:d?.decimals??null},...f],minRequiredMvl:m.status==="fulfilled"?m.value.toString():null}})),i=[],o=[];s.forEach((a,c)=>{if(a.status==="fulfilled"){let{scopedErrors:l,...d}=a.value;i.push(d);for(let u of l)o.push({network:d.network,error:u})}else o.push({network:n[c].name,error:a.reason?.message})}),w({networks:i,errors:o})}catch(s){g({error:s.message}),process.exit(1)}},$p=async e=>{let[t,r,n]=e;(!t||!r)&&(console.error("error: Usage: amb deposit-withdraw <wallet_address> <network>"),console.error("hint: v2 withdraws the full MVL ledger balance \u2014 no token arg needed."),console.error(" Subject to a per-agent withdraw cooldown enforced by the ledger."),process.exit(1)),n&&console.error(`warning: <token> positional argument ("${n}") is no longer used. v2 withdraws all MVL via router.withdraw(). Drop the token arg: \`amb deposit-withdraw <wallet_address> <network>\``);let s=R();b(s.dataDir),k(t),O(s.appServerUrl);let i=s.depositNetworks.find(a=>a.name===r);i||(g({error:"UNKNOWN_NETWORK",message:`Network "${r}" not configured. Available: ${s.depositNetworks.map(a=>a.name).join(", ")}`}),process.exit(1));let o=Buffer.from(s.passphrase,"utf8");try{let a=await Ko({wallet_address:t},i,s,o);w(a),console.error(Lp)}catch(a){ea(a),process.exit(1)}},Hp="0xfd0Bf71F60660E2f608ed56e1659C450eB113120",qp=11155111,Zo=2e5,jp=async e=>{let[t,r,n]=e;(!t||!r)&&(console.error("error: Usage: amb bridge-deposit-eth <wallet_address> <value_eth> [recipient]"),console.error("hint: omit recipient to bridge to the same address on L2"),process.exit(1));let s=n??t,i=Dp({abi:[{name:"depositETHTo",type:"function",stateMutability:"payable",inputs:[{name:"_to",type:"address"},{name:"_minGasLimit",type:"uint32"},{name:"_extraData",type:"bytes"}],outputs:[]}],functionName:"depositETHTo",args:[s,Zo,"0x"]}),o=R();b(o.dataDir),k(t),O(o.appServerUrl);let a=Buffer.from(o.passphrase,"utf8");try{let c=await De({wallet_address:t,to:Hp,value_eth:r,chain_id:qp,data:i},o,a);w({...c,l2_recipient:s,min_gas_limit:Zo})}catch(c){g({error:c.message}),process.exit(1)}},Gp=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb deposit-relay-status <wallet_address> <request_id>"),console.error('hint: <request_id> is from the deposit-add response when path="relay"'),process.exit(1));let n=R();b(n.dataDir);let s=j(t);s||(g({error:"JWT_NOT_FOUND",message:"No cached JWT for this wallet. Call siwe-request-message + siwe-submit first."}),process.exit(1));try{let i=await Mr(s,r,n);w(rs(i)),i.status==="FAILED"&&process.exit(2)}catch(i){g({error:i.message}),process.exit(1)}},ta={"deposit-status":Up,"deposit-add":Bp,"deposit-tokens":Fp,"deposit-withdraw":$p,"deposit-relay-status":Gp,"bridge-deposit-eth":jp};import{createPublicClient as Em,http as Rm,formatUnits as bm}from"viem";import{base as Tm,baseSepolia as Sm,mainnet as Am,sepolia as Im}from"viem/chains";import{randomUUID as Wp}from"crypto";var Vp=["INITIATED","BURN_SUBMITTED","ATTESTED","MINT_SUBMITTED","COMPLETED","FAILED"],Pe=["COMPLETED","FAILED"],pR=Vp.filter(e=>!Pe.includes(e)),ra=Pe.map(()=>"?").join(","),Jp={status:"status",fromBlock:"from_block",burnPrivyTxId:"burn_privy_tx_id",burnTxHash:"burn_tx_hash",relayRequestId:"relay_request_id",cctpNonce:"cctp_nonce",message:"message",attestation:"attestation",mintPrivyTxId:"mint_privy_tx_id",mintTxHash:"mint_tx_hash",mintFromBlock:"mint_from_block",mintAttempts:"mint_attempts",lastRevertSig:"last_revert_sig",mintedAmountRaw:"minted_amount_raw",failureReason:"failure_reason"};function ns(){return Math.floor(Date.now()/1e3)}function na(e){let t=T();return t.transaction(()=>{if(t.prepare(`
|
|
410
|
+
SELECT id FROM bridge_jobs
|
|
411
|
+
WHERE wallet_address = ? AND status NOT IN (${ra})
|
|
412
|
+
LIMIT 1
|
|
413
|
+
`).get(e.walletAddress,...Pe))throw new Error("BRIDGE_JOB_IN_FLIGHT");let n=Wp();return t.prepare(`
|
|
414
|
+
INSERT INTO bridge_jobs (
|
|
415
|
+
id, wallet_address, status, source_chain_id, dest_chain_id,
|
|
416
|
+
amount_raw, is_fast, max_fee_raw, from_block, mint_from_block
|
|
417
|
+
) VALUES (@id, @walletAddress, 'INITIATED', @sourceChainId, @destChainId,
|
|
418
|
+
@amountRaw, @isFast, @maxFeeRaw, @fromBlock, @mintFromBlock)
|
|
419
|
+
`).run({id:n,walletAddress:e.walletAddress,sourceChainId:e.sourceChainId,destChainId:e.destChainId,amountRaw:e.amountRaw,isFast:e.isFast?1:0,maxFeeRaw:e.maxFeeRaw,fromBlock:e.fromBlock??null,mintFromBlock:e.mintFromBlock??null}),Le(n)})()}function Le(e){return T().prepare("SELECT * FROM bridge_jobs WHERE id = ?").get(e)}function sa(e){return T().prepare(`
|
|
420
|
+
SELECT * FROM bridge_jobs
|
|
421
|
+
WHERE wallet_address = ? AND status NOT IN (${ra})
|
|
422
|
+
ORDER BY created_at DESC, rowid DESC LIMIT 1
|
|
423
|
+
`).get(e,...Pe)}function ia(e,t=20){return T().prepare(`
|
|
424
|
+
SELECT * FROM bridge_jobs WHERE wallet_address = ?
|
|
425
|
+
ORDER BY created_at DESC, rowid DESC LIMIT ?
|
|
426
|
+
`).all(e,t)}function ss(e,t){let r=Object.entries(t),n=[],s=[];for(let[i,o]of r){let a=Jp[i];if(!a)throw new Error(`updateJob: unknown field "${String(i)}"`);n.push(`${a} = ?`),s.push(o)}n.length!==0&&(n.push("updated_at = ?"),s.push(ns()),T().prepare(`UPDATE bridge_jobs SET ${n.join(", ")} WHERE id = ?`).run(...s,e))}function Ur(e,t,r=ns()){return T().prepare(`
|
|
427
|
+
UPDATE bridge_jobs SET lease_expires_at = ?, updated_at = ?
|
|
428
|
+
WHERE id = ? AND (lease_expires_at IS NULL OR lease_expires_at < ?)
|
|
429
|
+
`).run(r+t,r,e,r).changes===1}function oa(e){T().prepare("UPDATE bridge_jobs SET lease_expires_at = NULL, updated_at = ? WHERE id = ?").run(ns(),e)}function lt(e){return T().prepare("SELECT * FROM bridge_fast_grants WHERE wallet_address = ?").get(e)}function aa(e,t){return T().prepare(`
|
|
430
|
+
INSERT INTO bridge_fast_grants (wallet_address, max_fee_raw)
|
|
431
|
+
VALUES (?, ?)
|
|
432
|
+
ON CONFLICT(wallet_address) DO UPDATE SET
|
|
433
|
+
max_fee_raw = excluded.max_fee_raw,
|
|
434
|
+
granted_at = CURRENT_TIMESTAMP
|
|
435
|
+
`).run(e,t),lt(e)}function ca(e){return T().prepare("DELETE FROM bridge_fast_grants WHERE wallet_address = ?").run(e).changes>0}function is(e,t){if(t===0n)return!0;let r=lt(e);return r?t<=BigInt(r.max_fee_raw):!1}import{encodeFunctionData as as,decodeEventLog as Yp,pad as ma}from"viem";import{parseAbi as os,parseAbiItem as Kp}from"viem";var la=os(["function depositForBurn(uint256 amount, uint32 destinationDomain, bytes32 mintRecipient, address burnToken, bytes32 destinationCaller, uint256 maxFee, uint32 minFinalityThreshold)"]),da=os(["function receiveMessage(bytes message, bytes attestation) returns (bool)"]),ua=os(["function approve(address spender, uint256 amount) returns (bool)"]),pa=Kp("event MintAndWithdraw(address indexed mintRecipient, uint256 amount, address indexed mintToken, uint256 feeCollected)");function ga(e){return as({abi:la,functionName:"depositForBurn",args:[e.amount,e.destinationDomain,ma(e.mintRecipient,{size:32}),e.burnToken,ma("0x",{size:32}),e.maxFee,e.minFinalityThreshold]})}function fa(e,t){return as({abi:da,functionName:"receiveMessage",args:[e,t]})}function _a(e,t){return as({abi:ua,functionName:"approve",args:[e,t]})}function Br(e,t,r){let n=t.toLowerCase(),s=r.toLowerCase();for(let i of e.logs??[]){if(i.address?.toLowerCase()!==n)continue;let o;try{o=Yp({abi:[pa],data:i.data,topics:i.topics})}catch{continue}let a=o.args;if(a.mintRecipient?.toLowerCase()===s)return{amountRaw:a.amount,feeCollectedRaw:a.feeCollected}}return null}var ha="Run `amb bridge-usdc <wallet>` to resume",Xp={status:"status",fromBlock:"from_block",burnPrivyTxId:"burn_privy_tx_id",burnTxHash:"burn_tx_hash",cctpNonce:"cctp_nonce",message:"message",attestation:"attestation",mintPrivyTxId:"mint_privy_tx_id",mintTxHash:"mint_tx_hash",mintFromBlock:"mint_from_block",mintAttempts:"mint_attempts",lastRevertSig:"last_revert_sig",mintedAmountRaw:"minted_amount_raw",failureReason:"failure_reason"};function se(e,t){ss(e.id,t);let r={...e};for(let[n,s]of Object.entries(t)){let i=Xp[n];i&&(r[i]=s)}return r}function Fr(e,t,r=!1){return se(e,{status:"FAILED",failureReason:t}),{status:"FAILED",...r?{fundsMidFlight:!0}:{}}}function pe(e,t,r={}){return{status:e,nextStep:ha,...t?.note?{note:t.note}:{},...t?.privyStatus?{privyStatus:t.privyStatus}:{},...r}}function wa(e,t){let r=BigInt(e.amount_raw);return t>=r&&t<=r+BigInt(e.max_fee_raw)}function Xe(e,t,r){return se(e,{status:"COMPLETED",mintedAmountRaw:t}),{status:"COMPLETED",mintedAmountRaw:t,...r?{note:r}:{}}}function cs(e){return`Your USDC arrived \u2014 the CCTP message for this transfer is redeemed on the destination chain \u2014 but the exact credited amount could not be read from the logs. Run \`amb wallet-balance ${e}\` to see the balance.`}async function Nt(e,t){let r=BigInt(e.mint_from_block??e.from_block??"0"),n=await t.scanMintLogs(r,e.wallet_address);return n&&wa(e,BigInt(n.amountRaw))?n.amountRaw:null}async function Ea(e,t){if(!e.cctp_nonce)return!1;try{return await t.isNonceUsed(e.cctp_nonce)}catch{return!1}}async function Ra(e,t){if(BigInt(e.max_fee_raw)===0n)return{amountRaw:e.amount_raw};let r=await Nt(e,t);return r?{amountRaw:r}:{amountRaw:null,note:cs(e.wallet_address)}}function ba(e){let t=e instanceof Error?e.message:String(e);return/already used|nonce already/i.test(t)}async function zp(e,t,r){let n=await Nt(e,t);if(n)return Xe(e,n);let s=Le(e.id)??e,i=s.last_revert_sig===r?s.mint_attempts+1:1,o=se(e,{mintAttempts:i,lastRevertSig:r});if(i>=t.mintMaxAttempts)return Fr(o,`receiveMessage reverted ${i} times with the same signature (${r}). The burn already moved your USDC, so the funds are mid-flight and this job needs the \xA74.3 last-resort recovery: share \`amb bridge-usdc-status\` output with support.`,!0);try{let a=await t.sendMint(o);return o=se(o,{status:"MINT_SUBMITTED",mintPrivyTxId:a.transactionId,mintTxHash:null}),pe("MINT_SUBMITTED")}catch(a){if(ba(a)){let c=await Nt(o,t);if(c)return Xe(o,c)}throw a}}var ya=new Set;function Qp(e){return Pe.includes(e)}async function Ta(e,t){let r=`${e.id}:${e.burn_tx_hash??""}`;if(ya.has(r))return e;ya.add(r);try{let n=await t.registerRelay(e);if(n&&!e.relay_request_id)return ss(e.id,{relayRequestId:n}),Le(e.id)??e}catch{}return e}var J=e=>({advanced:!1,result:e}),$r=e=>({advanced:!0,job:e});async function Zp(e,t){if(e.burn_privy_tx_id){let o=await t.resolveTxStatus(e.burn_privy_tx_id);return o.kind==="FAILED"?J(Fr(e,`burn transaction failed (${o.privyStatus??"reverted"}); no USDC was burned`)):o.kind==="REPLACED"?(o.hash&&se(e,{burnTxHash:o.hash}),J(pe("INITIATED",o,{note:`burn transaction was replaced (nonce replacement); now tracking ${o.hash}`}))):o.kind==="SUCCESS"&&o.hash?$r(se(e,{status:"BURN_SUBMITTED",burnTxHash:o.hash})):J(pe("INITIATED",o))}let r=BigInt(e.from_block??"0"),n=await t.currentBlock(),s=await t.scanBurnLogs(r,n,e.wallet_address);if(s)return $r(se(e,{status:"BURN_SUBMITTED",burnTxHash:s.hash}));let i=n-r;return i<BigInt(t.graceBlocks)?J({status:"INITIATED",verdictWithheld:!0,nextStep:ha,note:`no DepositForBurn found yet, but only ${i} of ${t.graceBlocks} grace blocks have passed; withholding judgement`}):J(Fr(e,`no DepositForBurn from ${e.wallet_address} in ${i} blocks since block ${r}; the burn never went out`))}async function em(e,t){let r=e.burn_tx_hash;if(!r)return J(pe("BURN_SUBMITTED"));let n=await t.irisAttestation(e.source_chain_id,r);if(n&&n.attestation)return $r(se(e,{status:"ATTESTED",message:n.message,attestation:n.attestation,cctpNonce:n.nonce}));let s=n&&"delayReason"in n?n.delayReason:void 0,i=BigInt(e.from_block??"0");return await t.currentBlock()-i>=BigInt(t.graceBlocks)&&await t.getTransactionByHash(r)===null?J(Fr(e,`burn transaction ${r} is no longer on the canonical chain (suspected reorg) and Circle has issued no attestation; the burn did not stand, so no USDC was lost`)):J(pe("BURN_SUBMITTED",void 0,s?{note:`Circle has not attested the burn yet; Iris reports: ${s}`}:{}))}async function tm(e,t){if(await Ea(e,t)){let{amountRaw:n,note:s}=await Ra(e,t);return J(Xe(e,n,s))}let r=e.mint_from_block?e:se(e,{mintFromBlock:(await t.destBlock()).toString()});try{let n=await t.sendMint(r);return $r(se(r,{status:"MINT_SUBMITTED",mintPrivyTxId:n.transactionId,mintTxHash:null}))}catch(n){if(ba(n)){let s=await Nt(r,t);if(s)return J(Xe(r,s))}throw n}}async function rm(e,t){if(!e.mint_privy_tx_id)return J(pe("MINT_SUBMITTED"));let r=await t.resolveTxStatus(e.mint_privy_tx_id);if(r.kind==="FAILED")return J(await zp(e,t,r.privyStatus??"unknown_revert"));if(r.kind==="REPLACED")return r.hash&&se(e,{mintTxHash:r.hash}),J(pe("MINT_SUBMITTED",r,{note:`mint transaction was replaced (nonce replacement); now tracking ${r.hash}`}));if(r.kind!=="SUCCESS"||!r.hash)return J(pe("MINT_SUBMITTED",r));let n=se(e,{mintTxHash:r.hash}),s=await t.getReceipt(r.hash),i=s?Br(s,t.destTokenMessenger,n.wallet_address):null;if(i)return wa(n,i.amountRaw)?J(Xe(n,i.amountRaw.toString())):J(pe("MINT_SUBMITTED",void 0,{note:`MintAndWithdraw paid ${i.amountRaw} which is outside the expected range ${n.amount_raw}..${BigInt(n.amount_raw)+BigInt(n.max_fee_raw)}; not treating the job as complete`}));if(await Ea(n,t)){let{amountRaw:a,note:c}=await Ra(n,t);return J(Xe(n,a,c))}let o=await Nt(n,t);return J(o?Xe(n,o):pe("MINT_SUBMITTED",void 0,{note:`mint transaction ${r.hash} succeeded but no MintAndWithdraw for ${n.wallet_address} was found yet`}))}async function Sa(e,t){let r=await t.sendBurn(e),n=se(e,{burnPrivyTxId:r.transactionId});return n=await Ta(n,t),Hr(n,t)}async function Hr(e,t){let r=e;for(let n=0;n<nm;n++){r.burn_tx_hash&&!Qp(r.status)&&(r=await Ta(r,t));let s;switch(r.status){case"COMPLETED":return{status:"COMPLETED",mintedAmountRaw:r.minted_amount_raw};case"FAILED":return{status:"FAILED"};case"INITIATED":s=await Zp(r,t);break;case"BURN_SUBMITTED":s=await em(r,t);break;case"ATTESTED":s=await tm(r,t);break;case"MINT_SUBMITTED":s=await rm(r,t);break}if(!s.advanced)return s.result;r=s.job}return pe(r.status)}var nm=8;var sm=10000n;function Aa(e,t){return(e+t-1n)/t}function Ia(e,t,r,n){let s=r?1e3:2e3,i=e.find(a=>a.finalityThreshold===s);if(!i)throw new Error(`IRIS_FEE_MISSING: no fee entry for finalityThreshold ${s} (got: ${e.map(a=>a.finalityThreshold).join(", ")||"none"})`);let o=Aa(t*BigInt(i.minimumFee),sm);return Aa(o*BigInt(100+n),100n)}async function ls(e,t){let r=`${t.irisBaseUrl}/v2/burn/USDC/fees/${t.srcDomain}/${t.dstDomain}`,n=await e(r);if(!n.ok)throw new Error(`IRIS_FEE_QUERY_FAILED: ${n.status} from ${r}`);let s=await n.json();return Array.isArray(s)?s:[]}function im(e){return typeof e=="string"&&e.startsWith("0x")}function om(e,t){let r=e.decodedMessage?.destinationDomain;if(r!==void 0&&Number(r)!==t.destinationDomain)return!1;let n=e.decodedMessage?.decodedMessageBody?.amount;return!(n!==void 0&&BigInt(n)!==t.amountRaw)}async function xa(e,t){let r=`${t.irisBaseUrl}/v2/messages/${t.srcDomain}?transactionHash=${encodeURIComponent(t.transactionHash)}`,n=await e(r);if(n.status===404)return null;if(!n.ok)throw new Error(`IRIS_MESSAGE_QUERY_FAILED: ${n.status} from ${r}`);let i=(await n.json())?.messages;if(!Array.isArray(i)||i.length===0)return null;let o=i.find(a=>a.status==="complete"&&om(a,t)&&typeof a.message=="string"&&im(a.attestation));return o?{message:o.message,attestation:o.attestation,nonce:o.eventNonce??null}:null}import{decodeEventLog as am,parseAbiItem as cm,pad as lm}from"viem";var dm=cm("event DepositForBurn(address indexed burnToken, uint256 amount, address indexed depositor, bytes32 mintRecipient, uint32 destinationDomain, bytes32 destinationTokenMessenger, bytes32 destinationCaller, uint256 maxFee, uint32 indexed minFinalityThreshold, bytes hookData)"),va=128,Na=8,um=[/block range/i,/range is too large/i,/too many blocks/i,/query returned more than/i,/response size exceeded/i,/exceeds? (the )?max(imum)? (results|logs)/i];function pm(e){let t=e instanceof Error?e.message:String(e);return um.some(r=>r.test(t))}async function ka(e,t,r){let n=t.chunk??R().bridgeLogChunkBlocks,s=t.fromBlock,i=0;for(;s<=t.toBlock;){let o=s+BigInt(n)-1n;o>t.toBlock&&(o=t.toBlock);let a;try{a=await e({address:t.tokenMessenger,fromBlock:s,toBlock:o})}catch(l){if(!pm(l))throw l;if(++i>Na)throw new Error(`eth_getLogs range rejections are not converging after ${Na} halvings (blocks ${s}-${o})`);let d=Number(o-s+1n),u=Math.floor(d/2);if(u<va)throw new Error(`eth_getLogs still rejects the block range at the ${va}-block floor (blocks ${s}-${o}): ${l instanceof Error?l.message:String(l)}`);n=u;continue}i=0;let c=r(a);if(c!==null)return c;s=o+1n}return null}async function Ca(e,t,r,n){let s=t.tokenMessenger.toLowerCase(),i=r.toLowerCase(),o=lm(n.mintRecipient,{size:32}).toLowerCase();return ka(e,t,a=>{for(let c of a){if(c.address?.toLowerCase()!==s)continue;let l;try{l=am({abi:[dm],data:c.data,topics:c.topics})}catch{continue}let d=l.args;if(d.depositor?.toLowerCase()===i&&d.amount===n.amountRaw&&d.destinationDomain===n.destinationDomain&&d.mintRecipient?.toLowerCase()===o)return{transactionHash:c.transactionHash,blockNumber:BigInt(c.blockNumber),amountRaw:d.amount,mintRecipient:d.mintRecipient,destinationDomain:d.destinationDomain}}return null})}async function Da(e,t,r){return ka(e,t,n=>{for(let s of n){let i=Br({logs:[s]},t.tokenMessenger,r);if(i)return{transactionHash:s.transactionHash,blockNumber:BigInt(s.blockNumber),amountRaw:i.amountRaw,feeCollectedRaw:i.feeCollectedRaw}}return null})}var mm=["confirmed","finalized"],gm=["execution_reverted","failed","provider_error"],fm=["pending","broadcasted"];function _m(e){let t=e instanceof Error?e.message:String(e),r=/^HTTP (\d{3})\b/.exec(t);return r?Number(r[1]):void 0}async function Pa(e,t){let r;try{r=await e(t)}catch(i){let o=_m(i);if(o!==void 0&&o>=400&&o<500&&o!==404)throw i;return{kind:"PENDING",note:`transaction lookup did not resolve (${i instanceof Error?i.message:String(i)}); treating as still in flight`}}let n=r.transactionHash??void 0,s=n?{hash:n}:{};return mm.includes(r.status)?{kind:"SUCCESS",...s}:gm.includes(r.status)?{kind:"FAILED",...s}:fm.includes(r.status)?{kind:"PENDING",...s}:r.status==="replaced"?n?{kind:"REPLACED",hash:n}:{kind:"PENDING",privyStatus:"replaced",note:"Privy reported `replaced` with no transaction hash; re-querying on the next resume"}:{kind:"PENDING",privyStatus:r.status,...s,note:`unrecognised Privy status \`${r.status}\`; treating as still in flight`}}var La=[{type:"function",name:"usedNonces",stateMutability:"view",inputs:[{name:"nonce",type:"bytes32"}],outputs:[{name:"",type:"uint256"}]}];async function Oa(e,t,r){return await e(t,r)!==0n}function ym(e,t){return{walletAddress:e.wallet_address,...e.burn_privy_tx_id?{burnPrivyTxId:e.burn_privy_tx_id}:{},...e.burn_tx_hash?{burnTxHash:e.burn_tx_hash}:{},sourceNetwork:t.source.name,destNetwork:t.dest.name,srcDomain:t.source.cctpDomain,dstDomain:t.dest.cctpDomain,amountRaw:e.amount_raw,maxFeeRaw:e.max_fee_raw,isFast:e.is_fast===1}}async function Ma(e,t,r,n,s,i=1e4){let o=AbortSignal.timeout(i);async function a(){let d=await t(o);if(!d)return null;let u=await e(`${s.gatewayServerUrl.replace(/\/$/,"")}/v1/agent-cctp-relay/relay-mint`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${d}`},body:JSON.stringify(ym(r,n)),signal:o});if(!u.ok||!u.json)return null;let m=await u.json();return typeof m.requestId=="string"&&m.requestId.length>0?m.requestId:null}let c,l=new Promise(d=>{c=setTimeout(()=>d(null),i),c.unref?.()});try{return await Promise.race([a(),l])}catch{return null}finally{clearTimeout(c)}}var Fa=6,xm=5e3,vm=3*6e4,Nm=25*6e4,us=e=>`Run \`amb bridge-usdc ${e}\` to resume`,ps=e=>`note: this bridge is NOT finished (status ${e}). Your USDC has not arrived yet \u2014 do not report it as complete.`,qr=e=>`This bridge could not complete on its own and your USDC is mid-flight.
|
|
436
|
+
Nothing is lost \u2014 it can be finished manually.
|
|
437
|
+
Send the full output of \`amb bridge-usdc-status ${e}\` to support.`,km=/^\d+(\.\d{1,6})?$/;function kt(e){if(!km.test(e))throw new Error(`BRIDGE_AMOUNT_INVALID: "${e}" is not a decimal USDC amount. Expected digits with at most 6 decimal places (e.g. "50", "50.5"). No sign, no exponent, and no 7th decimal place \u2014 USDC has exactly 6, so a finer amount cannot be sent and is rejected rather than rounded.`);let[t,r=""]=e.split(".");return BigInt(t)*1000000n+BigInt(r.padEnd(Fa,"0"))}function G(e){return bm(e,Fa)}var Cm={1:Am,11155111:Im,8453:Tm,84532:Sm},Dm=[{name:"balanceOf",type:"function",stateMutability:"view",inputs:[{name:"account",type:"address"}],outputs:[{name:"",type:"uint256"}]}],Pm=(e,t)=>{let r=Em({chain:Cm[e],transport:Rm(t)});return{getBlockNumber:()=>r.getBlockNumber(),getReceipt:async n=>{try{return{logs:(await r.getTransactionReceipt({hash:n})).logs}}catch{return null}},getTransaction:async n=>{try{return{blockNumber:(await r.getTransaction({hash:n})).blockNumber??null}}catch{return null}},getLogs:async n=>(await r.getLogs({address:n.address,fromBlock:n.fromBlock,toBlock:n.toBlock})).map(i=>({address:i.address,topics:i.topics,data:i.data,blockNumber:`0x${(i.blockNumber??0n).toString(16)}`,transactionHash:i.transactionHash??""})),usedNonces:(n,s)=>r.readContract({address:n,abi:La,functionName:"usedNonces",args:[s]}),erc20Balance:(n,s)=>r.readContract({address:n,abi:Dm,functionName:"balanceOf",args:[s]})}},Ct=Pm;var dt={now:()=>Date.now(),sleep:e=>new Promise(t=>setTimeout(t,e))};function ze(e){return e instanceof Error?e.message:String(e)}function $a(e){let t=/^HTTP (\d{3})\b/.exec(ze(e));return t?Number(t[1]):void 0}var Lm=1e3,Om=8e3,Mm=5;async function Ua(e){let t=Lm;for(let r=1;;r++)try{return await e()}catch(n){if($a(n)!==429||r>=Mm)throw n;await dt.sleep(t),t=Math.min(t*2,Om)}}function Um(e){let t=ze(e);return $a(e)===403?{error:"BRIDGE_SPONSOR_NOT_ALLOWED",message:`The gas sponsor rejected this batch (403). This is an operations problem, not yours: the CCTP approve/depositForBurn rules are not deployed to the sponsor allowlist for this environment. No USDC was burned. Report it \u2014 retrying will not help. Server said: ${t}`}:/Request expired/i.test(t)?{error:"BRIDGE_CLOCK_SKEW",message:"The server rejected the request as expired. The idempotency key is generated at call time, so the only cause is this machine's clock drifting more than 5 minutes from the server's. Synchronise the system clock (e.g. enable NTP) and run the command again. Retrying without fixing the clock fails identically, so it was not retried."}:null}function Bm(e,t,r){return async n=>{let s=j(e);if(s)return s;let{message:i}=await Nr(e,t.paymentChainId,n),{wallet_signature:o}=await Ce({wallet_address:e,sign_method:"personal_sign",message:i},t,r,n),{accessToken:a}=await kr(i,o,n);return a}}function ds(e,t,r,n){let{source:s,dest:i}=t,o=Ct(s.chainId,s.rpcUrl),a=Ct(i.chainId,i.rpcUrl),c=BigInt(r.amount_raw)+BigInt(r.max_fee_raw);return{irisFees:(l,d)=>ls(fetch,{irisBaseUrl:t.irisBaseUrl,srcDomain:l,dstDomain:d}),irisAttestation:(l,d)=>xa(fetch,{irisBaseUrl:t.irisBaseUrl,srcDomain:s.cctpDomain,transactionHash:d,destinationDomain:i.cctpDomain,amountRaw:c}),resolveTxStatus:l=>Pa(So,l),sendBurn:async l=>{let d=BigInt(l.amount_raw)+BigInt(l.max_fee_raw);return{transactionId:(await Ua(()=>vr({wallet_address:l.wallet_address,chain_id:s.chainId,sponsor:!0,calls:[{to:s.usdcAddress,value_eth:"0",data:_a(s.tokenMessengerV2,d)},{to:s.tokenMessengerV2,value_eth:"0",data:ga({amount:d,destinationDomain:i.cctpDomain,mintRecipient:l.wallet_address,burnToken:s.usdcAddress,maxFee:BigInt(l.max_fee_raw),minFinalityThreshold:l.is_fast?1e3:2e3})}]},e,n))).transaction_id}},sendMint:async l=>{let{message:d,attestation:u}=l;if(!d||!u)throw new Error("BRIDGE_NOT_ATTESTED: the job has no message/attestation to submit yet");let m=await Ua(()=>De({wallet_address:l.wallet_address,to:i.messageTransmitterV2,value_eth:"0",chain_id:i.chainId,sponsor:!0,data:fa(d,u)},e,n));if(!m.transactionId)throw new Error("BRIDGE_NO_TRANSACTION_ID: the mint was submitted but the server returned no transaction id");return{transactionId:m.transactionId}},getReceipt:l=>a.getReceipt(l),currentBlock:()=>o.getBlockNumber(),destBlock:()=>a.getBlockNumber(),scanBurnLogs:async(l,d,u)=>{let m=await Ca(p=>o.getLogs(p),{fromBlock:l,toBlock:d,tokenMessenger:s.tokenMessengerV2},u,{amountRaw:c,mintRecipient:r.wallet_address,destinationDomain:i.cctpDomain});return m?{hash:m.transactionHash}:null},scanMintLogs:async(l,d)=>{let u=await Da(m=>a.getLogs(m),{fromBlock:l,toBlock:await a.getBlockNumber(),tokenMessenger:i.tokenMessengerV2},d);return u?{amountRaw:u.amountRaw.toString()}:null},getTransactionByHash:l=>o.getTransaction(l),isNonceUsed:l=>Oa((d,u)=>a.usedNonces(d,u),i.messageTransmitterV2,l),registerRelay:l=>Ma(fetch,Bm(l.wallet_address,e,n),l,t,e),now:()=>Math.floor(dt.now()/1e3),destTokenMessenger:i.tokenMessengerV2,graceBlocks:e.bridgeBurnGraceBlocks,mintMaxAttempts:e.bridgeMintMaxAttempts}}function ms(e){return Pe.includes(e)}function jr(e){return e.status==="FAILED"&&!!e.burn_tx_hash&&!e.minted_amount_raw}function Fm(e){let t,r;switch(e.status){case"INITIATED":t="BURN_CONFIRMATION_PENDING",r=["ATTESTATION_PENDING","SERVER_MINT_PENDING","MINT_CONFIRMATION_PENDING"];break;case"BURN_SUBMITTED":t="ATTESTATION_PENDING",r=["SERVER_MINT_PENDING","MINT_CONFIRMATION_PENDING"];break;case"ATTESTED":t="SERVER_MINT_PENDING",r=["MINT_CONFIRMATION_PENDING"];break;case"MINT_SUBMITTED":t="MINT_CONFIRMATION_PENDING",r=[];break;case"COMPLETED":case"FAILED":return}return{status_class:"IN_PROGRESS",current_stage:t,remaining_stages:r,completion_mode:"SERVER_RELAY_WITH_CLI_FALLBACK",status_command:`amb bridge-usdc-status ${e.wallet_address} ${e.id}`,do_not_start_new_transfer:!0,user_action_required:"CHECK_STATUS_LATER"}}function Gr(e){let t=Fm(e);return{job_id:e.id,status:e.status,wallet_address:e.wallet_address,amount_usdc:G(BigInt(e.amount_raw)),amount_raw:e.amount_raw,max_fee_raw:e.max_fee_raw,is_fast:e.is_fast===1,source_chain_id:e.source_chain_id,dest_chain_id:e.dest_chain_id,...e.burn_tx_hash?{burn_tx_hash:e.burn_tx_hash}:{},...e.mint_tx_hash?{mint_tx_hash:e.mint_tx_hash}:{},...e.mint_attempts?{mint_attempts:e.mint_attempts}:{},...e.failure_reason?{failure_reason:e.failure_reason}:{},...e.status==="COMPLETED"?e.minted_amount_raw?{minted_amount:G(BigInt(e.minted_amount_raw)),minted_amount_raw:e.minted_amount_raw}:{minted_amount:null,minted_amount_raw:null,minted_amount_unknown:!0,minted_amount_note:cs(e.wallet_address)}:{},...jr(e)?{funds_mid_flight:!0,recovery:qr(e.wallet_address)}:{},...t?{agent_guidance:t}:{}}}function $m(e,t,r){let n=Le(e.id)??e,s=Gr(n),i=ms(n.status);if(w({...s,...t.verdictWithheld?{verdict_withheld:!0}:{},...t.note?{note:t.note}:{},...t.privyStatus?{privy_status:t.privyStatus}:{},...i?{}:{next_step:us(r)}}),!i){console.error(ps(n.status));return}n.status==="FAILED"&&((jr(n)||t.fundsMidFlight)&&console.error(qr(r)),process.exit(2))}async function Ha(e,t,r,n,s){let i=await ls(fetch,{irisBaseUrl:s.irisBaseUrl,srcDomain:s.source.cctpDomain,dstDomain:s.dest.cctpDomain}),o=Ia(i,t,r,n.bridgeMaxFeeBufferPct),a=t+o,l=await Ct(s.source.chainId,s.source.rpcUrl).erc20Balance(s.source.usdcAddress,e);return{amountRaw:t,maxFeeRaw:o,requiredRaw:a,sourceBalanceRaw:l,sufficient:l>=a}}var Hm=async e=>{let t=e.includes("--fast"),r=e.includes("--wait"),n=e.filter(d=>d!=="--fast"&&d!=="--wait"),[s,i]=n;if(!s){console.error("error: Usage: amb bridge-usdc <wallet_address> [amount] [--fast] [--wait]"),console.error('hint: amount is decimal USDC (e.g. "50", "50.5") \u2014 NOT raw units like deposit-add.'),console.error(" omit amount to resume an unfinished bridge (idempotent)."),process.exit(1);return}let o=R();if(!o.bridge){g({error:"BRIDGE_NOT_CONFIGURED",message:o.bridgeConfigError??"This build has no USDC bridge configured."}),process.exit(1);return}let a=o.bridge;b(o.dataDir),k(s),O(o.appServerUrl);let c=Buffer.from(o.passphrase,"utf8"),l;try{let d,u;if(i===void 0){let p=sa(s);if(!p){g({error:"BRIDGE_NO_ACTIVE_JOB",message:`No unfinished bridge for ${s}. Pass an amount to start one, or run \`amb bridge-usdc-status\` to see past jobs.`}),process.exit(1);return}if(!Ur(p.id,o.bridgeLeaseSec)){w({...Gr(p),lease_held:!0,next_step:us(s)}),console.error(ps(p.status)),console.error("note: another process is already advancing this bridge; nothing was submitted.");return}l=p.id,d=p,u=await Hr(d,ds(o,a,d,c))}else{let p=kt(i),f=kt(o.bridgeMinUsdc);if(p<f){g({error:"BRIDGE_AMOUNT_BELOW_MIN",message:`${G(p)} USDC is below the fixed ${o.bridgeMinUsdc} USDC bridge minimum. Nothing was submitted.`,amount_usdc:G(p),min_usdc:o.bridgeMinUsdc}),process.exit(1);return}let _=await Ha(s,p,t,o,a),h=_.maxFeeRaw;if(!_.sufficient){g({error:"BRIDGE_INSUFFICIENT_USDC",message:`${G(_.sourceBalanceRaw)} USDC on ${a.source.name} is short of the ${G(_.requiredRaw)} USDC this transfer needs`+(h>0n?` (${G(p)} to receive + ${G(h)} max fee)`:"")+". Nothing was submitted.",source:a.source.name,balance_usdc:G(_.sourceBalanceRaw),required_usdc:G(_.requiredRaw),required_raw:_.requiredRaw.toString()}),process.exit(1);return}if(!is(s,h)){let C=lt(s);g({error:"BRIDGE_FEE_NOT_AUTHORIZED",message:`This transfer's fee is ${G(h)} USDC`+(C?`, above the ${G(BigInt(C.max_fee_raw))} USDC ceiling approved for this wallet`:", and this wallet has no standing fee approval")+`. Nothing was submitted. Either approve it with \`amb bridge-fast-grant ${s} <max_fee_usdc>\`, or drop \`--fast\` to send it as a Standard transfer, which costs nothing.`,max_fee_usdc:G(h),max_fee_raw:h.toString(),grant_max_fee_usdc:C?G(BigInt(C.max_fee_raw)):null}),process.exit(1);return}let E=await Ct(a.source.chainId,a.source.rpcUrl).getBlockNumber(),N=await Ct(a.dest.chainId,a.dest.rpcUrl).getBlockNumber();d=na({walletAddress:s,sourceChainId:a.source.chainId,destChainId:a.dest.chainId,amountRaw:p.toString(),isFast:t,maxFeeRaw:h.toString(),fromBlock:E.toString(),mintFromBlock:N.toString()}),Ur(d.id,o.bridgeLeaseSec),l=d.id,u=await Sa(d,ds(o,a,d,c))}if(t||r){let p=t?vm:Nm,f=dt.now()+p;for(;!ms(u.status)&&dt.now()<f;){await dt.sleep(Math.min(xm,f-dt.now()));let _=Le(d.id);if(!_)break;Ur(_.id,o.bridgeLeaseSec),u=await Hr(_,ds(o,a,_,c))}}$m(d,u,s)}catch(d){let u=Um(d);if(u)g(u);else if(ze(d)==="BRIDGE_JOB_IN_FLIGHT")g({error:"BRIDGE_JOB_IN_FLIGHT",message:`A bridge is already running for ${s}. Run \`amb bridge-usdc ${s}\` (no amount) to resume it, or \`amb bridge-usdc-status ${s}\` to inspect it.`});else{let m=ze(d),p=m.match(/^([A-Z][A-Z0-9_]+):\s*(.+)$/s);p?g({error:p[1],message:p[2]}):g({error:m})}process.exit(1)}finally{l&&oa(l)}},qm=async e=>{let[t,r]=e;if(!t){console.error("error: Usage: amb bridge-usdc-status <wallet_address> [job_id]"),process.exit(1);return}let n=R();b(n.dataDir);try{if(r){let o=Le(r);if(!o||o.wallet_address.toLowerCase()!==t.toLowerCase()){g({error:"BRIDGE_JOB_NOT_FOUND",message:`No bridge job ${r} for ${t}.`}),process.exit(1);return}w(Gr(o)),jr(o)&&console.error(qr(t));return}let s=ia(t),i=s.find(o=>!ms(o.status));w({wallet_address:t,active_job_id:i?.id??null,jobs:s.map(Gr),...i?{next_step:us(t)}:{}}),i&&console.error(ps(i.status));for(let o of s)if(jr(o)){console.error(qr(t));break}}catch(s){g({error:ze(s)}),process.exit(1)}};function Ba(e){let t=lt(e);return{wallet_address:e,granted:!!t,max_fee_usdc:t?G(BigInt(t.max_fee_raw)):null,max_fee_raw:t?t.max_fee_raw:null,granted_at:t?t.granted_at:null}}var jm=async e=>{let t=e.includes("--fast"),r=e.filter(c=>c!=="--fast"),[n,s]=r;if(!n||!s){console.error("error: Usage: amb bridge-usdc-quote <wallet_address> <amount> [--fast]"),console.error('hint: amount is decimal USDC (e.g. "90") \u2014 NOT raw units like deposit-add.'),console.error(" Starts nothing: this is a pure read."),process.exit(1);return}let i=R();if(!i.bridge){g({error:"BRIDGE_NOT_CONFIGURED",message:i.bridgeConfigError??"This build has no USDC bridge configured."}),process.exit(1);return}let o=i.bridge;b(i.dataDir),k(n);let a;try{a=kt(s)}catch(c){g({error:"BRIDGE_AMOUNT_INVALID",message:ze(c).replace(/^BRIDGE_AMOUNT_INVALID:\s*/,"")}),process.exit(1);return}try{let c=await Ha(n,a,t,i,o),l=lt(n);w({wallet_address:n,amount_usdc:G(c.amountRaw),amount_raw:c.amountRaw.toString(),mode:t?"fast":"standard",max_fee_usdc:G(c.maxFeeRaw),max_fee_raw:c.maxFeeRaw.toString(),required_usdc:G(c.requiredRaw),source:o.source.name,source_balance_usdc:G(c.sourceBalanceRaw),sufficient:c.sufficient,below_min:a<kt(i.bridgeMinUsdc),min_usdc:i.bridgeMinUsdc,preauthorized:is(n,c.maxFeeRaw),grant_max_fee_usdc:l?G(BigInt(l.max_fee_raw)):null})}catch(c){g({error:ze(c)}),process.exit(1)}},Gm=async e=>{let t=e.includes("--revoke"),r=e.filter(o=>o!=="--revoke"),[n,s]=r;if(!n){console.error("error: Usage: amb bridge-fast-grant <wallet_address> [<max_fee_usdc> | --revoke]"),console.error("hint: no argument reads the current grant; an amount sets it; --revoke clears it."),console.error(' max_fee_usdc is decimal USDC (e.g. "0.1") \u2014 the per-transfer fee ceiling'),console.error(" below which a Fast bridge proceeds without asking about the fee."),process.exit(1);return}if(t&&s!==void 0){g({error:"BRIDGE_GRANT_INVALID",message:"--revoke takes no amount. Pass an amount to set the ceiling, or --revoke alone to clear it. Nothing was changed."}),process.exit(1);return}let i=R();if(b(i.dataDir),k(n),t){ca(n),w(Ba(n));return}if(s!==void 0){let o;try{o=kt(s)}catch{g({error:"BRIDGE_GRANT_INVALID",message:`"${s}" is not a decimal USDC amount. Expected digits with at most 6 decimal places (e.g. "0.1"). Nothing was stored.`}),process.exit(1);return}aa(n,o.toString())}w(Ba(n))},qa={"bridge-usdc":Hm,"bridge-usdc-status":qm,"bridge-usdc-quote":jm,"bridge-fast-grant":Gm};var Wm={POLYGON:"https://polygonscan.com",POLYGON_AMOY:"https://amoy.polygonscan.com",BASE:"https://basescan.org",BASE_SEPOLIA:"https://sepolia.basescan.org",ETHEREUM:"https://etherscan.io",ETHEREUM_SEPOLIA:"https://sepolia.etherscan.io"};function ut(e,t){if(!e||!t)return null;let r=Wm[e];return r?`${r}/tx/${t}`:null}var Dt;function H(e){Dt=e.replace(/\/$/,"")}async function he(e,t,r,n,s,i){let o={"Content-Type":"application/json",Authorization:`Bearer ${r}`,...i},a=s?`${Dt}${t}?${new URLSearchParams(s).toString()}`:`${Dt}${t}`,c=await fetch(a,{method:e,headers:o,body:n!==void 0?JSON.stringify(n):void 0});if(!c.ok){let l=await c.text().catch(()=>"");throw new Error(`HTTP ${c.status} ${c.statusText} on ${e} ${t}: ${l}`)}return c.json()}async function ja(e,t,r){let n={"Content-Type":"application/json",Authorization:`Bearer ${e}`};r&&(n["payment-signature"]=r,n["x-signature-interpret-mode"]="general");let s=await fetch(`${Dt}/payment/ride`,{method:"POST",headers:n,body:JSON.stringify(t)});if(s.status===402){let o=s.headers.get("payment-required");if(!o)throw new Error("402 response missing payment-required header");let a=JSON.parse(Buffer.from(o,"base64").toString("utf-8"));return{status:402,accepts:a.accepts,nonce:a.nonce}}if(s.status===200||s.status===201){let o=s.headers.get("payment-response"),a=o?JSON.parse(Buffer.from(o,"base64").toString("utf-8")):{network:"",payer:"",success:!0,transaction:""};return{status:s.status,paymentResponse:a}}let i=await s.text().catch(()=>"");throw new Error(`HTTP ${s.status} ${s.statusText} on POST /payment/ride: ${i}`)}function Ga(e,t){return he("GET","/v1/phone-verification/check",e,void 0,{phone:t})}function Wa(e,t){return he("POST","/v1/phone-verification/send-code",e,{phone:t})}function Va(e,t,r){return he("POST","/v1/phone-verification/confirm",e,{phone:t,code:r})}function Ja(e,t,r){return he("POST","/v2/rides/base-search",e,{region:t,locations:r})}function Ka(e,t){return he("POST","/v2/rides/pre-request",e,t)}function Ya(e,t){return ja(e,{rideRequestId:t,type:"RIDE"}).then(r=>{if(r.status!==402)throw new Error(`Expected 402 from payment probe, got ${r.status}`);return{accepts:r.accepts,nonce:r.nonce}})}function Xa(e,t,r){return ja(e,{rideRequestId:t,type:"RIDE"},r).then(n=>{if(n.status!==200&&n.status!==201)throw new Error(`Expected 200/201 from payment submit, got ${n.status}`);return n.paymentResponse})}var Pt={SIN:"SG",BKK:"TH",DEN:"CO",HCM:"VN",HAN:"VN",PNH:"KH",REP:"KH",SHV:"KH",KPK:"KH",SEL:"KR",ADD:"ET",JKT:"ID",HKG:"HK",NYC:"NY"},Vm={singapore:"SIN",bangkok:"BKK",denver:"DEN","ho chi minh":"HCM",saigon:"HCM",hcmc:"HCM",hanoi:"HAN","phnom penh":"PNH",sihanoukville:"SHV","siem reap":"REP",kampot:"KPK",seoul:"SEL","addis ababa":"ADD",jakarta:"JKT","hong kong":"HKG","new york":"NYC","new york city":"NYC",nyc:"NYC"};function Lt(e){let t=e.toUpperCase();return Pt[t]?t:Vm[e.toLowerCase()]}var Jm={SIN:{lat:1.3521,lng:103.8198},BKK:{lat:13.7563,lng:100.5018},DEN:{lat:39.7392,lng:-104.9903},HCM:{lat:10.8231,lng:106.6297},HAN:{lat:21.0285,lng:105.8542},PNH:{lat:11.5564,lng:104.9282},REP:{lat:13.3633,lng:103.8564},SHV:{lat:10.6097,lng:103.523},KPK:{lat:10.6099,lng:104.18},SEL:{lat:37.5665,lng:126.978},ADD:{lat:9.032,lng:38.7469},JKT:{lat:-6.2088,lng:106.8456},HKG:{lat:22.3193,lng:114.1694},NYC:{lat:40.7128,lng:-74.006}};function za(e,t,r,n,s){let i=Pt[r];if(!i)throw new Error(`Unsupported city code: ${r}. Use codes like SIN, BKK, DEN.`);let o=Jm[r],a=n??o.lat,c=s??o.lng;return he("GET","/v2/places/autocomplete",e,void 0,{input:t,latitude:String(a),longitude:String(c)},{region:i})}function Qa(e,t,r){let n=Pt[r];if(!n)throw new Error(`Unsupported city code: ${r}. Use codes like SIN, BKK, DEN.`);return he("GET",`/v1/places/${encodeURIComponent(t)}`,e,void 0,{region:n,city:r},{region:n})}function Ot(e,t){return he("GET",`/v1/rides/request/${t}`,e)}function Wr(e,t,r,n){let s={};return r&&(s.reason=r),n!=null&&(s.cancelReasonType=n),he("POST",`/v2/rides/request/${t}/cancel`,e,s)}async function Vr(e,t,r,n=20){let s={limit:String(n)};r&&(s.messageTs=r);let o=(await he("GET",`/v1/chat/${e}/messages`,t,void 0,s)).messages??[],a=o.length>0?o[o.length-1].createdAt:null;return{count:o.length,messages:o,next_cursor:a}}async function Jr(e,t,r,n){let s=new FormData,i=new Blob([new Uint8Array(r)],{type:n});s.append("image",i,"image");let o=await fetch(`${Dt}/v1/chat/${t}/image`,{method:"POST",headers:{Authorization:`Bearer ${e}`},body:s});if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`HTTP ${o.status} ${o.statusText} on POST /v1/chat/${t}/image: ${a}`)}return o.json()}function Za(e,t){Yi(e,t,18e4)}function ec(e){return Xi(e)}function Kr(e,t){let r=t.expiresAt-Date.now();zi(e,t,r>0?r:6e5)}function Yr(e){let t=Qi(e);return t||null}var tc={12:"Ride Throo",14:"EV Throo"},rc={0:"Save Throo",1:"XL Throo",31:"EV Throo",13:"Premium Throo",4:"Premium XL Throo",4e4:"WAV Throo"};function Xr(e,t,r,n){return(e??"").toUpperCase()!=="NY"?n:t!==void 0&&t in tc?tc[t]:r!==void 0&&r in rc?rc[r]:n}function Km(e,t){return`${(t==="mi"?e/1609.344:e/1e3).toFixed(1)} ${t}`}function Ym(e){return`${Math.round(e/60)} min`}var nc=e=>typeof e=="number"&&Number.isFinite(e);function zr(e,t){return Array.isArray(e)?e.map(r=>{if(r==null||typeof r!="object")return r;let n=r,s={...n};return nc(n.distance)&&(s.distance_display=Km(n.distance,t)),nc(n.duration)&&(s.duration_display=Ym(n.duration)),s}):e}var Xm={minLat:15,maxLat:72,minLng:-168,maxLng:-52};function pt(e,t){if(!Number.isFinite(e)||!Number.isFinite(t))throw new Error(`Invalid coordinate for region routing: lat=${e}, lng=${t}`);let r=Xm;return e>=r.minLat&&e<=r.maxLat&&t>=r.minLng&&t<=r.maxLng?"US":"SG"}var zm=new Set(["NY"]);function we(e){return e&&zm.has(e.toUpperCase())?"US":"SG"}function Qr(e){return e==="US"?"mi":"km"}function ie(e,t,r){return(t??{})[e]??r}function ee(e,t){return ie("US",e,t)}function Qm(e){let t=j(e);if(!t)throw new Error("JWT_NOT_FOUND: No cached JWT for this wallet. Call siwe_get_message + siwe_login first.");return t}async function Zr(e){let t=Qm(e.wallet_address),r=await Ja(t,e.region,e.locations),n=JSON.stringify(r),s=r.checkSum;if(!s)throw new Error("product-search response missing checkSum field");Za(s,n);let i=r.products??[],o=[];for(let a of i){let l=(a.priceInfos??[]).find(d=>d.currencyId===e.payCurrency);if(l){let d=l.supportedCurrencyPriceInfo;o.push({product_id:a.id,product_code:a.code,product_name:Xr(e.region,a.productType,a.carType,a.title),pay_currency:e.payCurrency,price:d?.priceAfterDiscount??d?.price,price_currency:d?.currency})}}return{search_id:s,options:o,routes:zr(r.routes,Qr(we(e.region)))}}function sc(e){for(let t of e.locations){let r=t.googlePlaceId??t.placeId,n=st({placeId:r,latitude:t.latitude,longitude:t.longitude});if(!n){process.stderr.write(`[place-history] skip location (no placeId + invalid coords): name=${t.name}
|
|
438
|
+
`);continue}uo({wallet_address:e.wallet_address,name:t.name,formatted_address:t.address,latitude:t.latitude,longitude:t.longitude,google_place_id:r??null,region:e.region,dedup_key:n,ts:e.ts})}}function Zm(e){let t=j(e);if(!t)throw new Error("JWT_NOT_FOUND: No cached JWT for this wallet. Call siwe_get_message + siwe_login first.");return t}async function en(e,t){let r=Zm(e.wallet_address),n=ec(e.search_id);if(!n)throw new Error("SEARCH_CACHE_MISS: search_id not found or expired. Call ride_search again.");let o=(JSON.parse(n).products??[]).find(p=>p.code===e.product_code);if(!o)throw new Error(`PRODUCT_NOT_FOUND: product_code ${e.product_code} not found in search result.`);let c=(o.priceInfos??[]).find(p=>p.currencyId===e.pay_currency);if(!c)throw new Error(`CURRENCY_NOT_FOUND: pay_currency ${e.pay_currency} not available for product ${e.product_code}.`);let l=c.walletProviderId,d=await Ka(r,{region:e.region,productCode:e.product_code,walletProviderId:l,payCurrency:e.pay_currency,locations:e.locations,rider:e.rider,baseSearchResponse:n,userWallet:{network:t.paymentNetwork,address:e.wallet_address,name:e.wallet_address}}),u=e.locations[0],m=e.locations[e.locations.length-1];Er({request_id:d.requestId,wallet_address:e.wallet_address,region:e.region,product_code:e.product_code,price:d.price,pay_currency:e.pay_currency,origin_name:u?.name,origin_addr:u?.address,origin_lat:u?.latitude,origin_lng:u?.longitude,dest_name:m?.name,dest_addr:m?.address,dest_lat:m?.latitude,dest_lng:m?.longitude,rider_name:e.rider.name,rider_phone:e.rider.phone});try{sc({wallet_address:e.wallet_address,region:e.region,locations:e.locations,ts:Math.floor(Date.now()/1e3)})}catch(p){process.stderr.write(`[place-history] record failed: ${p.message}
|
|
439
|
+
`)}return{request_id:d.requestId,price:d.price,currency:d.currency}}var tn;function Oe(e){tn=e.replace(/\/$/,"")}async function rn(e,t,r,n,s){let i={"Content-Type":"application/json",Authorization:`Bearer ${r}`},o=s?`${tn}${t}?${new URLSearchParams(s).toString()}`:`${tn}${t}`,a=await fetch(o,{method:e,headers:i,body:n!==void 0?JSON.stringify(n):void 0});if(!a.ok){let c=await a.text().catch(()=>"");throw new Error(`HTTP ${a.status} ${a.statusText} on ${e} ${t}: ${c}`)}return a.json()}async function ic(e,t,r){let n={"Content-Type":"application/json",Authorization:`Bearer ${e}`};r&&(n["payment-signature"]=r,n["x-signature-interpret-mode"]="general");let s=await fetch(`${tn}/payment/tip`,{method:"POST",headers:n,body:JSON.stringify(t)});if(s.status===402){let o=s.headers.get("payment-required");if(!o)throw new Error("402 response missing payment-required header");let a=JSON.parse(Buffer.from(o,"base64").toString("utf-8"));return{status:402,accepts:a.accepts,nonce:a.nonce}}if(s.status===200||s.status===201){let o=s.headers.get("payment-response"),a=o?JSON.parse(Buffer.from(o,"base64").toString("utf-8")):{network:"",payer:"",success:!0,transaction:""};return{status:s.status,paymentResponse:a}}let i=await s.text().catch(()=>"");throw new Error(`HTTP ${s.status} ${s.statusText} on POST /payment/tip: ${i}`)}function oc(e,t){return rn("GET","/tip/config",e,void 0,{region:t})}function ac(e,t,r){return rn("POST",`/v2/rides/request/${t}/tip/pre-request`,e,{price:r})}function cc(e,t){return ic(e,{rideRequestId:t}).then(r=>{if(r.status!==402)throw new Error(`Expected 402 from tip payment probe, got ${r.status}`);return{accepts:r.accepts,nonce:r.nonce}})}function lc(e,t,r){return ic(e,{rideRequestId:t},r).then(n=>{if(n.status!==200&&n.status!==201)throw new Error(`Expected 200/201 from tip payment submit, got ${n.status}`);return n.paymentResponse})}async function dc(e,t=20){let r=await rn("GET","/v2/rides/request/history",e,void 0,{status:"completed",limit:String(t)});return{items:r.items??[],nextCursor:r.nextCursor??null}}function nn(e,t){return rn("GET",`/v2/ride-histories/detail/${t}`,e)}import _s from"fs";import{spawn as tg}from"child_process";import ys from"path";import me from"fs";import uc from"path";function sn(e,t){return uc.join(e,"run",`${t}.pid`)}function gs(e){try{let t=parseInt(me.readFileSync(e,"utf8").trim(),10);return Number.isNaN(t)?null:t}catch{return null}}function pc(e){try{return process.kill(e,0),!0}catch{return!1}}function mt(e,t){let r=gs(sn(e,t));return r!==null&&pc(r)}function fs(e,t){if(me.existsSync(`${e}.amb-migration-quiesced`))return!1;let r=sn(e,t);me.mkdirSync(uc.dirname(r),{recursive:!0});try{let n=me.openSync(r,"wx");return me.writeSync(n,String(process.pid)),me.closeSync(n),!0}catch{let n=gs(r);if(n===process.pid||n!==null&&pc(n))return!1;try{me.unlinkSync(r)}catch{}try{let s=me.openSync(r,"wx");return me.writeSync(s,String(process.pid)),me.closeSync(s),!0}catch{return!1}}}function on(e,t){try{gs(sn(e,t))===process.pid&&me.unlinkSync(sn(e,t))}catch{}}import{createHash as mc}from"crypto";var gc=1,eg=new Set(["FINISHED","CANCELED","USER_CANCELED","USER_CANCELED_BEFORE_CALL","USER_CANCELED_NO_FREE","DRIVER_CANCELED","DRIVER_CANCELED_RECALLABLE","COMPANY_CANCELED","COMPANY_CANCELED_RECALLABLE","EXPIRED","EXPIRED_RECALLABLE","EXPIRED_BEFORE_PAY","NOT_MATCHED","ERROR_PAYMENT","ERROR","ACCIDENT_CANCELED"]);function Ie(e){return eg.has(e)}function fc(e){let t=e.kind==="ride_status"?`${e.rideId}|ride_status|${e.status??""}`:`${e.rideId}|${e.kind}|${mc("sha256").update(e.content??"").digest("hex")}`;return"evt_"+mc("sha256").update(t).digest("hex").slice(0,24)}function rg(){let e=process.argv[1]??"",t=e?ys.resolve(e):"";if(!t||!_s.existsSync(t))throw new Error(`amb entry not found (argv[1]=${process.argv[1]}, resolved=${t})`);return t}async function xe(e,t={}){try{let r=V(e);if(!r||Ie(r.status)||mt(F(),`sup-${e}`))return!1;let n=t.spawn??tg,s=rg();return n(process.execPath,[s,"monitor-supervisor",e],{detached:!0,stdio:"ignore"}).unref?.(),!0}catch(r){try{let n=ys.join(F(),"run",`monitor-trace-${e}.log`);_s.mkdirSync(ys.dirname(n),{recursive:!0}),_s.appendFileSync(n,JSON.stringify({ts:new Date().toISOString(),pid:process.pid,event:"ensure_monitor_failed",rideId:e,error:String(r)})+`
|
|
440
|
+
`)}catch{}return!1}}import ng from"fs";import sg from"path";var hs;function yc(e){hs=e}function hc(){return hs}function wc(e){let t=[],r;for(let n=0;n<e.length;n++){let s=e[n];if(s.startsWith("--account=")){let i=s.slice(10);if(i!=="wallet"&&i!=="tada")throw new Error(`--account: expected 'wallet' or 'tada', got '${i}'`);r=i;continue}if(s==="--account"){let i=e[n+1];if(i!=="wallet"&&i!=="tada")throw new Error(`--account: expected 'wallet' or 'tada', got '${i??"(missing)"}'`);r=i,n+=1;continue}t.push(s)}return{account:r,argv:t}}function _c(){return{hasMember:!1,hasWallet:!1,member:void 0,walletAddress:void 0}}function ig(){let e=et(),t=sg.join(e,"ambient-ride.db");if(!ng.existsSync(t))return _c();try{b(e);let r=St(),n=fr(),s=n.length>0?n:gr();return{hasMember:!!r,hasWallet:s.length>0,member:r,walletAddress:s[0]?.wallet_address}}catch{return _c()}}function K(e={}){let t=e.flag??hs,r=ig();return t?{mode:t,reason:"flag",hasMember:r.hasMember,hasWallet:r.hasWallet,member:r.member,walletAddress:r.walletAddress}:r.hasMember?{mode:"tada",reason:"member-session",hasMember:!0,hasWallet:r.hasWallet,member:r.member,walletAddress:r.walletAddress}:r.hasWallet?{mode:"wallet",reason:"wallet",hasMember:!1,hasWallet:!0,member:void 0,walletAddress:r.walletAddress}:{mode:null,reason:"onboarding",hasMember:!1,hasWallet:!1,member:void 0,walletAddress:void 0}}var og=["wallet-","deposit-","siwe-","ride-pay-","bridge-"],ag=new Set([]);function Ec(e){if(og.some(t=>e.startsWith(t)))return"wallet";if(ag.has(e))return"tada"}function Rc(e,t){return e==="wallet"?"This command is wallet-only. Add '--account=wallet' to this invocation, or run 'amb logout' to fall back to crypto mode.":"This command is TADA/Throo member-only. Add '--account=tada' to this invocation, or run 'amb login' to enter member mode."}var cg={401:"unauthorized",404:"agent_session_not_found",409:"agent_session_already_decided",410:"agent_session_expired"},Y=class extends Error{constructor(r,n){super(`gateway ${r} ${n}`);this.status=r;this.code=n;this.name="GatewayError"}status;code};function ce(e,t){let r=cg[e]??"unknown";try{let n=JSON.parse(t);n&&typeof n.code=="string"&&(r=n.code)}catch{}return new Y(e,r)}var bc="";function gt(e){bc=e.replace(/\/$/,"")}async function Me(e,t,r={}){let n=(r.host??bc).replace(/\/$/,"");if(!n)throw new Error("tada gateway not initialized");let s=r.query?Object.entries(r.query).filter(([,a])=>a!==void 0&&a!=="").map(([a,c])=>`${encodeURIComponent(a)}=${encodeURIComponent(String(c))}`).join("&"):"",i={Authorization:`Bearer ${t}`};r.lang&&(i["Accept-Language"]=r.lang);let o=await fetch(`${n}${e}${s?`?${s}`:""}`,{method:"GET",headers:i});if(!o.ok){let a=await o.text().catch(()=>"");throw ce(o.status,a)}return o.json()}function Tc(e,t){return Me("/v1/cards",t,{host:e})}function Sc(e,t){if(t!=null&&t!=="")return{kind:"selected",uuid:t};let r=e.filter(s=>!s.is_expired);if(r.length===0)return{kind:"none",reason:e.length?"all_expired":"no_cards"};if(r.length===1)return{kind:"selected",uuid:r[0].id,card:r[0]};let n=r.filter(s=>s.is_default);return n.length===1?{kind:"selected",uuid:n[0].id,card:n[0]}:{kind:"ambiguous",candidates:r}}async function Mt(e,t,r){if(t)return t;let n;try{n=await e.getCards(r)}catch(i){let a=i.code==="card_api_error"?"Card service is temporarily unavailable. Please try again in a moment.":"Could not load your cards. Your TADA/Throo session may have expired \u2014 run `amb login` again.";return g({error:"CARD_LOOKUP_FAILED",message:a}),process.exit(1),null}let s=Sc(n);return s.kind==="none"?(g({error:"NO_CARD",region_host:r,message:s.reason==="all_expired"?"All your cards are expired. Add or update a card in the TADA/Throo app.":`No payment card found for this pickup region (looked up on ${r}). If a card is already saved in the TADA/Throo app, re-check the pickup coordinates before adding another.`}),process.exit(1),null):s.kind==="ambiguous"?(w({needs_card_selection:!0,cards:s.candidates.map(i=>({id:i.id,card_brand:i.card_brand,card_last4:i.card_last4,is_default:i.is_default,is_expired:i.is_expired})),next_action:"ASK_USER_TO_PICK_CARD_THEN_RERUN_WITH_payment_item_uuid"}),process.exit(0),null):s.uuid}var Ut="";function Ue(e){Ut=e.replace(/\/$/,"")}function lg(e){return`Basic ${Buffer.from(`${e.clientId}:${e.clientSecret}`).toString("base64")}`}async function ws(e,t,r,n){if(!Ut)throw new Error("tada gateway api not initialized");let s=await fetch(`${Ut}${r}`,{method:t,headers:{"Content-Type":"application/json",Authorization:lg(e)},body:n!==void 0?JSON.stringify(n):void 0});if(!s.ok){let i=await s.text().catch(()=>"");throw ce(s.status,i)}return s.json()}function Ac(e){return ws(e,"POST","/v1/agent-session")}function Ic(e,t,r){return ws(e,"POST","/v1/oauth/token",{grant_type:"agent_session",session_token:t,user_code:r})}function xc(e,t){return ws(e,"POST","/v1/oauth/token",{grant_type:"refresh_token",refresh_token:t})}async function vc(e){if(!Ut)throw new Error("tada gateway api not initialized");let t=await fetch(`${Ut}/v1/me`,{method:"GET",headers:{Authorization:`Bearer ${e}`}});if(!t.ok){let r=await t.text().catch(()=>"");throw ce(t.status,r)}return t.json()}function Nc(e){try{let t=e.split(".")[1];if(!t)return null;let r=JSON.parse(Buffer.from(t,"base64url").toString("utf8"));return typeof r.sub=="string"?r.sub:null}catch{return null}}function kc(e){try{let t=e.split(".")[1];if(!t)return null;let r=JSON.parse(Buffer.from(t,"base64url").toString("utf8"));return typeof r.exp=="number"?r.exp:null}catch{return null}}function ug(){return Math.floor(Date.now()/1e3)}async function Es(e){let t=await Ac(e);return{sessionToken:t.session_token,approvalUrl:t.approval_url,expiresAt:t.expires_at,pollIntervalSec:t.polling_interval_seconds}}async function Rs(e,t,r){let n=await Ic(e,t,r),s=Nc(n.access_token);if(!s)throw new Error("access token missing sub claim");let i=await pg(n.access_token);return ro({id:s,display_name:i?.name??void 0,phone:i?.phone??void 0}),Cc(s,n.access_token,n.expires_in,n.refresh_token),{memberId:s}}async function pg(e){try{let t=await vc(e);return{name:t.name,phone:t.phone}}catch(t){return oe(`[tada-auth] /v1/me profile fetch failed; persisting member id only: ${String(t)}
|
|
441
|
+
`),null}}function Cc(e,t,r,n){Ki(e,t,ug()+r,n,kc(n))}async function an(e){let t=St();if(!t)return null;let r=P(t.id,"member");if(r)return r;let n=wr(t.id);if(!n)return oe(`[tada-auth] no usable member refresh token (expired or pruned); re-login required
|
|
442
|
+
`),null;try{let s=await xc(e,n.refresh_token);return Cc(t.id,s.access_token,s.expires_in,s.refresh_token),s.access_token}catch(s){return oe(`[tada-auth] member token refresh failed; re-login required: ${String(s)}
|
|
443
|
+
`),null}}function Dc(){let e=St();return e?(no(e.id),!0):!1}import{EventEmitter as fg}from"events";import{io as _g}from"socket.io-client";import gg from"path";var cn=1e4;function ln(e,t){return gg.join(e,"run",`chat-${t}.sock`)}function bs(e,t){if(!Hi())return;let r={ts:new Date().toISOString(),msg:e,...t??{}};oe("[chat-debug] "+JSON.stringify(r)+`
|
|
444
|
+
`)}var dn=class extends fg{config;requestId;walletAddress;socket=null;roomId=null;readyEmitted=!1;pendingAcks=[];constructor(t,r,n){super(),this.config=t,this.requestId=r,this.walletAddress=n}connect(){try{let t=P(this.walletAddress);if(!t){this.emit("error",{message:`No valid JWT for wallet ${this.walletAddress}`});return}let r=new URL(this.config.gatewayServerUrl).origin,n=_g(`${r}/chat`,{path:"/tada-ton/chat-socket",transports:["websocket"],auth:{token:t}});this.socket=n,this.bindSocketEvents(n)}catch(t){this.emit("error",{message:`connect failed: ${t.message}`})}}dispose(){this.disconnect()}disconnect(){try{this.socket&&(this.socket.disconnect(),this.socket=null),this.roomId=null,this.readyEmitted=!1,this.drainPendingAcks()}catch(t){this.emit("error",{message:`disconnect cleanup failed: ${t.message}`})}}send(t){return this.roomId===null?Promise.resolve({ok:!1,reason:"not_joined"}):new Promise(r=>{let n=setTimeout(()=>{let s=this.pendingAcks.findIndex(i=>i.resolve===r);s>=0&&this.pendingAcks.splice(s,1),r({ok:!1,reason:"ack_timeout"})},cn);this.pendingAcks.push({resolve:r,timer:n});try{this.socket?.emit("SEND_MESSAGE",{roomId:this.roomId,content:t,type:"TEXT"})}catch(s){let i=this.pendingAcks.findIndex(o=>o.resolve===r);i>=0&&this.pendingAcks.splice(i,1),clearTimeout(n),r({ok:!1,reason:`emit_failed: ${s.message}`})}})}drainPendingAcks(){for(;this.pendingAcks.length;){let t=this.pendingAcks.shift();clearTimeout(t.timer),t.resolve({ok:!1,reason:"disconnected"})}}bindSocketEvents(t){t.on("connect",()=>{bs("socket connect",{requestId:this.requestId,socketId:t.id});try{t.emit("JOIN_ROOM",{rideRequestId:this.requestId})}catch(r){this.emit("error",{message:`JOIN_ROOM emit failed: ${r.message}`})}}),t.on("MESSAGE_SENT",r=>{try{if(r.event==="joined"){let n=this.roomId===null&&!this.readyEmitted;this.roomId=r.roomId,n&&(this.readyEmitted=!0,this.emit("ready",{roomId:this.roomId,pid:process.pid}))}else{let n=this.pendingAcks.shift();n&&(clearTimeout(n.timer),n.resolve({ok:!0}))}}catch(n){this.emit("error",{message:`MESSAGE_SENT handler failed: ${n.message}`})}}),t.on("NEW_MESSAGE",r=>{try{this.emit("message",{data:r})}catch(n){this.emit("error",{message:`NEW_MESSAGE handler failed: ${n.message}`})}}),t.on("disconnect",r=>{bs("socket disconnect",{reason:r});try{this.roomId=null,this.drainPendingAcks()}catch(n){this.emit("error",{message:`disconnect handler failed: ${n.message}`})}}),t.io.on("reconnect_attempt",r=>{try{let n=P(this.walletAddress),s=!!n;n&&t.auth&&(t.auth.token=n),bs("socket reconnect_attempt",{attempt:r,jwtRefreshed:s}),this.emit("reconnecting",{attempt:r})}catch(n){this.emit("error",{message:`reconnect_attempt handler failed: ${n.message}`})}}),t.on("error",r=>{this.emit("error",{message:r.message})}),t.on("connect_error",r=>{this.emit("error",{message:`connect_error: ${r.message}`})})}};var Be=class extends Error{code="NOT_SUPPORTED";constructor(t){super(t),this.name="NotSupportedError"}},ft=class extends Error{code="SHARE_NOT_AVAILABLE";constructor(t){super(t),this.name="ShareNotAvailableError"}};async function un(e){let{rawTypedData:t,walletAddress:r,target:n}=e;if(ae(r)==null)return{typed_data:t,isSmartAccount:!1};let s=await jo(n);if(s!==n.chainId)throw new Error(`PAYMENT_CHAIN_MISMATCH: payment RPC serves chain ${s} but the payment authorization is for chain ${n.chainId}. Check the build payment network and its AMB_RIDE_RPC_URL_<NETWORK>.`);if(!await Lr(n,r))return{typed_data:t,isSmartAccount:!1};let i=await Or(n,r),o=Cr(t);return{typed_data:Dr({kernelDomain:i,innerDigest:o}),isSmartAccount:!0}}function pn(e,t){return t?Pr(e):e}function yg(e){let t=j(e);if(!t)throw new Error("JWT_NOT_FOUND: No cached JWT for this wallet. Call siwe_get_message + siwe_login first.");return t}async function mn(e){let{request_id:t,price:r,wallet_address:n,config:s}=e,i=yg(n);await ac(i,t,r);let{accepts:o,nonce:a}=await cc(i,t);if(!o.length)throw new Error("TIP_PAY_PROBE_FAILED: Server returned no payment accepts.");let c=o[0],l=String(Math.floor(Date.now()/1e3)+c.maxTimeoutSeconds),d=c.network.split(":")[1],u=d?parseInt(d,10):NaN;if(Number.isNaN(u))throw new Error(`TIP_PAY_PREPARE: Cannot parse chainId from network "${c.network}".`);let m={domain:{name:c.extra.name,version:c.extra.version,chainId:u,verifyingContract:c.asset},types:{EIP712Domain:[{name:"name",type:"string"},{name:"version",type:"string"},{name:"chainId",type:"uint256"},{name:"verifyingContract",type:"address"}],TransferWithAuthorization:[{name:"from",type:"address"},{name:"to",type:"address"},{name:"value",type:"uint256"},{name:"validAfter",type:"uint256"},{name:"validBefore",type:"uint256"},{name:"nonce",type:"bytes32"}]},primaryType:"TransferWithAuthorization",message:{from:n,to:c.payTo,value:c.amount,validAfter:"0",validBefore:l,nonce:a}},{typed_data:p,isSmartAccount:f}=await un({rawTypedData:m,walletAddress:n,target:{rpcUrl:s.paymentRpcUrl,chainId:u}});return Kr(t,{accepts:o,nonce:a,walletAddress:n,validBefore:l,expiresAt:Date.now()+c.maxTimeoutSeconds*1e3,isSmartAccount:f}),{typed_data:p}}function hg(e){let t=j(e);if(!t)throw new Error("JWT_NOT_FOUND: No cached JWT for this wallet. Call siwe_get_message + siwe_login first.");return t}async function gn(e){let{request_id:t,signature:r}=e,n=Yr(t);if(!n)throw new Error("PAYMENT_CACHE_MISS: request_id not found or expired. Call tip_prepare again.");let{accepts:s,nonce:i,walletAddress:o,validBefore:a,isSmartAccount:c}=n,l=hg(o),d=s[0],u={x402Version:2,accepted:d,payload:{authorization:{from:o,to:d.payTo,value:d.amount,validAfter:"0",validBefore:a,nonce:i},signature:pn(r,c===!0)}},m=Buffer.from(JSON.stringify(u)).toString("base64"),p=await lc(l,t,m);return{success:p.success,tx_hash:p.transaction}}function fn(e,t){if(e?.length)return e.find(r=>!t(r)?.toLowerCase().includes("driver"))??e[0]}function X(e){return e.replace(/([\\`*_{}\[\]()<>#+!|])/g,"\\$1")}function Ts(e){return`${X(e.amount)} ${e.currency?X(e.currency):"currency unavailable"}`}function Lc(e,t){return[`${" ".repeat(t)}- ${X(e.label)}: ${Ts(e.amount)}`,...(e.children??[]).flatMap(r=>Lc(r,t+1))]}function Pc(e){let t=e.name.trim(),r=e.address.replace(/^[\s,;:–—-]+/u,"").trim();return X(t&&r&&t!==r?`${t} \u2014 ${r}`:t||r)}function wg(e){return e.replace(/\(/g,"%28").replace(/\)/g,"%29").replace(/\s/g,"%20")}function Oc(e){return new Set(e.flatMap(t=>[t.amount.currency,...Oc(t.children??[])]).filter(t=>!!t))}function Mc(e){let t=["# Ride receipt"];if(e.total&&t.push("","## Total fee charged",`**${Ts(e.total)}**`),e.fareItems.length){t.push("","## Fare breakdown");for(let n of e.fareItems)t.push(...Lc(n,0))}let r=Oc(e.fareItems);if(e.total?.currency&&r.size===1&&!r.has(e.total.currency)){let n=[...r][0];t.push("",`> Currency conversion: fare is shown in ${X(n)} and the charge in ${X(e.total.currency)}${e.exchangeRate?`; exchange rate ${X(e.exchangeRate)}`:""}.`)}if(e.transactions?.length){t.push("","## Transactions");for(let n of e.transactions){let s=n.explorerUrl?`[${X(n.hash)}](${wg(n.explorerUrl)})`:X(n.hash),i=n.network?` (${X(n.network)})`:"",o=n.amount?` \u2014 ${Ts(n.amount)}`:"";t.push(`- ${n.kind[0].toUpperCase()}${n.kind.slice(1)}${i}: ${s}${o}`)}}if(t.push("","\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500","","## Ride details"),e.pickupAt){let n=e.dropoffAt&&e.dropoffAt!==e.pickupAt?`${e.pickupAt} ~ ${e.dropoffAt}`:e.pickupAt;t.push(`- Date and time: ${X(n)}`)}return e.rideType&&t.push(`- Ride type: ${X(e.rideType)}`),e.driverName&&t.push(`- Driver: ${X(e.driverName)}`),e.passengerName&&t.push(`- Passenger: ${X(e.passengerName)}`),t.push(`- Ride ID: ${X(e.rideId)}`),e.pickup&&t.push(`- Pickup: ${Pc(e.pickup)}`),e.dropoff&&t.push(`- Destination: ${Pc(e.dropoff)}`),e.paymentMethod&&t.push(`- Payment method: ${X(e.paymentMethod)}`),`${t.join(`
|
|
445
|
+
`)}
|
|
446
|
+
`}function Uc(e,t){return{label:e.displayName,amount:{amount:String(e.fare),currency:t},...e.children?.length?{children:e.children.map(r=>Uc(r,t))}:{}}}function Eg(e){let t=fn(e.receiptBreakdown,d=>d.subtotalName),r=e.locations[0],n=e.locations.at(-1),s=e.receiptInfo?.paidNetwork??void 0,i=[],o=(d,u,m,p)=>{if(!m)return;let f=ut(u??null,m);i.push({kind:d,hash:m,...u?{network:u}:{},...f?{explorerUrl:f}:{},...p?{amount:p}:{}})};o("payment",s,e.receiptInfo?.paidTxHash,e.receiptInfo?{amount:e.receiptInfo.paidAmount,...e.receiptInfo.paidCurrency?{currency:e.receiptInfo.paidCurrency}:{}}:void 0),o("refund",e.refundReceiptInfo?.refundNetwork??s,e.refundReceiptInfo?.refundTxHash),o("tip",e.tipInfo?.tipRequest?.network??void 0,e.tipInfo?.tipRequest?.txHash,e.tipInfo?.tipRequest?.payAmount?{amount:e.tipInfo.tipRequest.payAmount,...e.tipInfo.tipRequest.payCurrency||e.tipInfo.tipCurrency?{currency:e.tipInfo.tipRequest.payCurrency??e.tipInfo.tipCurrency??void 0}:{}}:void 0);let a=["FINISHED","COMPLETED","REFUNDED"].includes(e.status.toUpperCase()),c=e.isFreeRide||["PAID","COMPLETED","FINISHED","REFUNDED","PARTIALLY_REFUNDED"].includes(e.receiptInfo?.receiptStatus?.toUpperCase()??""),l=e.receiptInfo?{amount:e.receiptInfo.paidAmount,...e.receiptInfo.paidCurrency?{currency:e.receiptInfo.paidCurrency}:{}}:e.isFreeRide?{amount:"0",...t?.currency?{currency:t.currency}:{}}:void 0;return{ready:a&&c,...l?{total:l}:{},fareItems:t?.fareItems.map(d=>Uc(d,t.currency))??[],...e.exchangeRate!=null?{exchangeRate:String(e.exchangeRate)}:{},...i.length?{transactions:i}:{},...e.rideDate?{pickupAt:e.rideDate}:{},...e.rideEndDate?{dropoffAt:e.rideEndDate}:{},...e.rideType?{rideType:e.rideType}:{},...e.driverInfo?.name?{driverName:e.driverInfo.name}:{},rideId:e.id,...r?{pickup:r}:{},...n?{dropoff:n}:{},...e.paymentMethod?{paymentMethod:e.paymentMethod}:{}}}var Bt=class{constructor(t,r,n,s=Ce){this.config=t;this.jwt=r;this.walletAddress=n;this.walletSigner=s;H(t.gatewayServerUrl),Oe(t.gatewayServerUrl),t.appServerUrl&&O(t.appServerUrl)}config;jwt;walletAddress;walletSigner;async searchRides(t){return Zr(t)}getCancelPenalty(t){throw new Be("getCancelPenalty: not supported in crypto mode (cancellation fee is reported on cancel via isFreeCancel)")}getCancelReasons(t){throw new Be("getCancelReasons: not supported in crypto mode")}getCards(t){throw new Be("Card list is only available for TADA/Throo members.")}async getTipConfig(t){let r=await oc(this.jwt,t),n=r.tipMetadata;return{enabled:!!r.enabled,minAmount:n?.minAmount??0,...n?.maxAmount!==void 0?{maxAmount:n.maxAmount}:{},currency:n?.currency??""}}async payTip(t,r){let{typed_data:n}=await mn({request_id:t,price:r.amount,wallet_address:this.walletAddress,config:this.config}),s=Buffer.from(this.config.passphrase,"utf8"),{wallet_signature:i}=await this.walletSigner({wallet_address:this.walletAddress,sign_method:"eth_signTypedData_v4",message:n},this.config,s),o=await gn({request_id:t,signature:i});return{tipStatus:o.success?"PAID":"FAILED",...o.tx_hash?{txHash:o.tx_hash}:{}}}async bookRide(t){let n=await en(t,this.config);return{rideId:n.request_id,price:n.price,currency:n.currency}}getRideStatus(t){return Ot(this.jwt,t)}async getReceipt(t){return Eg(await nn(this.jwt,t))}async getShareLink(t){let r=await Ot(this.jwt,t);if(!r.rideShareUrl)throw new ft("Share link is available after a driver is assigned.");return{url:r.rideShareUrl}}cancelRide(t,r,n){return Wr(this.jwt,t,r,n)}async sendChat(t,r){if(r.image)return Jr(this.jwt,t,r.image.buffer,r.image.mimetype);throw new Error("MvlBackend.sendChat: text-only HTTP send not implemented. Use the monitor chat-control socket (chat-monitor-client) for crypto-mode text.")}subscribeChat(t){return new dn(this.config,t,this.walletAddress)}getMessages(t,r){return Vr(t,this.jwt,r!=null?String(r):void 0)}};async function Qe(e,t,r,n,s,i){if(!e)throw new Error("TADA ride API: host required");let o=e.replace(/\/$/,""),a=await fetch(`${o}${r}`,{method:t,headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`,...i},body:s!==void 0?JSON.stringify(s):void 0});if(!a.ok){let c=await a.text().catch(()=>"");throw ce(a.status,c)}return a.json()}function Rg(e){if(e)return{name:e.name,phone:e.phone,carModel:e.car_model??"",carPlate:e.car_plate??"",carColor:e.car_color,profilePhotoUrl:e.photo_url??""}}function Bc(e){let t=[];return e.pickup&&t.push(e.pickup),e.waypoints&&t.push(...e.waypoints),e.destination&&t.push(e.destination),{id:e.ride_id,status:e.ride_status,statusMessage:"",driverInfo:Rg(e.driver),locations:t,region:e.region,netPrice:e.net_price,payStatus:e.pay_status,tipAmount:e.tip_amount,riderName:e.rider_name}}function Ft(e,t,r){return Qe(e,"GET",`/v1/rides/${encodeURIComponent(r)}`,t)}function Ss(e,t,r){return Qe(e,"POST","/v1/products/search",t,r)}function Fc(e,t,r){return Qe(e,"POST","/v1/rides",t,r)}function $c(e,t,r,n){return Qe(e,"POST",`/v1/rides/${encodeURIComponent(r)}/cancel`,t,n)}function Hc(e,t,r,n){let s=n!=null?`?cancelReasonType=${n}`:"";return Qe(e,"GET",`/v1/rides/${encodeURIComponent(r)}/cancel-penalty${s}`,t)}function qc(e,t,r,n="en"){return Qe(e,"GET",`/v1/rides/${encodeURIComponent(r)}/cancel-reasons`,t,void 0,{"Accept-Language":n})}function jc(e){return{url:e.url,urlId:e.url_id,...e.message!=null?{message:e.message}:{},...e.chat_url!=null?{chatUrl:e.chat_url}:{}}}function Gc(e,t,r,n="en"){return Qe(e,"POST",`/v1/rides/${encodeURIComponent(r)}/share`,t,void 0,{"Accept-Language":n})}var bg={SEDAN:0,VAN:1,BUSINESS_VAN:4,EXECUTIVE_LARGE:8,BLACK_SEDAN:13,SUV:31,PREMIUM_SUV:33,SUV_LARGE:34,PREMIUM_SUV_LARGE:35,TAXI_STANDARD:1e4,TAXI_STANDARD_LARGE:10001,TAXI_PREMIUM:10002,TAXI_PREMIUM_LARGE:10003,WAV:4e4,WAV_LARGE:40001},Tg={PH:10,TAXI:11,ANYTADA:12,STUDENT:13,EV:14,TADAFAST:15,TADASAVER:16};function Wc(e){if(e!=null)return bg[e]}function Vc(e){if(e!=null)return Tg[e]}function Jc(e){return e?e.split("_").map(t=>t&&t[0].toUpperCase()+t.slice(1).toLowerCase()).join(" "):""}async function Kc(e,t,r,n,s){if(!e)throw new Error("TADA tip API: host required");let i=e.replace(/\/$/,""),o=await fetch(`${i}${r}`,{method:t,headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`},body:s!==void 0?JSON.stringify(s):void 0});if(!o.ok){let a=await o.text().catch(()=>"");throw ce(o.status,a)}return o.json()}function Yc(e,t,r){return Kc(e,"GET",`/v1/rides/tip-config?region=${encodeURIComponent(r)}`,t)}function Xc(e,t,r,n){return Kc(e,"POST",`/v1/rides/${encodeURIComponent(r)}/tip`,t,n)}function zc(e){return{enabled:!!e.enabled,minAmount:e.min_amount??0,...e.max_amount&&e.max_amount>0?{maxAmount:e.max_amount}:{},currency:e.currency??"",...e.amount_presets?{presets:e.amount_presets}:{}}}function Qc(e){return{tipStatus:e.tip_status}}import{EventEmitter as Sg}from"events";async function Zc(e,t,r,n){let s=`${e}/v2/chat/${encodeURIComponent(r)}/messages`,i={Authorization:`Bearer ${t}`},o;if(n.image){let c={};n.message&&(c.message=n.message);let l=new FormData;l.append("dto",new Blob([JSON.stringify(c)],{type:"application/json"})),l.append("image",new Blob([new Uint8Array(n.image.buffer)],{type:n.image.mimetype}),"image"),o=l}else i["Content-Type"]="application/json",o=JSON.stringify({message:n.message});let a=await fetch(s,{method:"POST",headers:i,body:o});if(!a.ok)throw ce(a.status,await a.text().catch(()=>""));return a.json()}var el=10;async function As(e,t,r,n={}){let s=[];n.since!=null&&Number.isFinite(n.since)&&s.push(`since=${n.since}`),n.wait!=null&&s.push(`wait=${n.wait}`),n.limit!=null&&s.push(`limit=${n.limit}`);let i=`${e}/v2/chat/${encodeURIComponent(r)}/messages${s.length?`?${s.join("&")}`:""}`,o=n.signal??AbortSignal.timeout(((n.wait??0)+el)*1e3),a=await fetch(i,{method:"GET",headers:{Authorization:`Bearer ${t}`},signal:o});if(!a.ok)throw ce(a.status,await a.text().catch(()=>""));return a.json()}var Ag=25,Ig=2e3;function xg(e){return new Promise(t=>setTimeout(t,e))}var _n=class extends Sg{constructor(r,n,s,i={}){super();this.host=r;this.jwt=n;this.rideId=s;this.wait=i.wait??Ag,this.backoffMs=i.backoffMs??Ig,this.since=i.startSince??i.nowMs??Date.now(),this.sleepFn=i.sleepFn??xg}host;jwt;rideId;stopped=!1;started=!1;controller;since;wait;backoffMs;sleepFn;connect(){this.started||(this.started=!0,this.loop())}disconnect(){this.stopped=!0,this.controller?.abort()}dispose(){this.disconnect()}async loop(){for(;!this.stopped;){let r=new AbortController;this.controller=r;let n=setTimeout(()=>r.abort(),(this.wait+el)*1e3);try{let s=await As(this.host,this.jwt,this.rideId,{since:this.since,wait:this.wait,signal:r.signal});if(this.stopped)break;for(let i of s.messages)i.sender_role!=="RIDER"&&this.emit("message",{data:{content:i.message??(i.file_url?"[image]":""),senderType:i.sender_role,file_url:i.file_url,message_id:i.message_id,created_at:i.created_at}});this.since=s.next_since??this.since}catch(s){if(this.stopped)break;this.emit("error",{message:s.message}),await this.sleepFn(this.backoffMs)}finally{clearTimeout(n)}}}};function tl(e,t){return{label:e.display_name,amount:{amount:String(e.fare),currency:t},...e.children?.length?{children:e.children.map(r=>tl(r,t))}:{}}}function vg(e){let t=fn(e.receipt_breakdown,l=>l.subtotal_name),r=e.start_time??e.create_time,n=e.product_type??e.car_type,s=e.pickup,i=e.destination,o=["FINISHED","COMPLETED","REFUNDED"].includes(e.ride_status.toUpperCase()),a=e.net_price===0||["PAID","COMPLETED","FINISHED","REFUNDED","PARTIALLY_REFUNDED"].includes(e.pay_status?.toUpperCase()??""),c=t?.total_fare??e.net_price??e.ride_fare;return{ready:o&&a,...c!=null?{total:{amount:String(c),...e.currency?{currency:e.currency}:{}}}:{},fareItems:t?.fare_items.map(l=>tl(l,t.currency))??[],...r?{pickupAt:r}:{},...e.drop_off_time?{dropoffAt:e.drop_off_time}:{},...n?{rideType:n}:{},...e.driver?.name?{driverName:e.driver.name}:{},...e.rider_name?{passengerName:e.rider_name}:{},rideId:e.ride_id,...s?{pickup:s}:{},...i?{dropoff:i}:{},...e.payment_method?{paymentMethod:e.payment_method}:{}}}var $t=class{constructor(t,r,n,s){this.config=t;this.jwt=r;this.memberId=n;this.rideHost=s}config;jwt;memberId;rideHost;sgHost(){return ie("SG",this.config.cliGatewayHosts,this.config.cliGatewayHost)}hostForPickup(t){let r=t[0];if(!r)throw new Error("Cannot route a ride without a pickup location.");return ie(pt(r.latitude,r.longitude),this.config.cliGatewayHosts,this.config.cliGatewayHost)}followupHost(){return this.rideHost??this.sgHost()}async bookRide(t){let r=t,n=this.hostForPickup(r.locations),i=(await Ss(n,this.jwt,{locations:r.locations,payment_item_uuid:r.paymentItemUuid})).products.filter(l=>!l.na&&l.price_entry),o=r.productId!=null?i.find(l=>l.id===r.productId):i[0];if(!o||!o.price_entry)throw new Error("No bookable product for the given locations (check region / card).");let a=o.price_entry,c=await Fc(n,this.jwt,{locations:r.locations,product_id:o.id,ride_price:a.ride_price,currency:a.currency,payment_item_uuid:r.paymentItemUuid,...r.avoidToll?{avoid_toll:!0}:{},...r.riderNote?{rider_note:r.riderNote}:{}});return Er({request_id:c.ride_id,member_id:this.memberId,region:c.region??"",status:c.ride_status,price:c.net_price!=null?String(c.net_price):void 0,pay_currency:c.currency}),{rideId:c.ride_id,price:c.net_price,currency:c.currency}}async getRideStatus(t){return Bc(await Ft(this.followupHost(),this.jwt,t))}async getReceipt(t){let r=await Ft(this.followupHost(),this.jwt,t);return vg(r)}async searchRides(t){let r=t,n=this.hostForPickup(r.locations),s=await Ss(n,this.jwt,{locations:r.locations,payment_item_uuid:r.paymentItemUuid}),i=r.locations[0],o=Qr(pt(i.latitude,i.longitude));return{options:s.products.map(c=>{let l=c.na===!0||!c.price_entry;return{product_id:c.id,product_name:Xr(c.region,Vc(c.product_type),Wc(c.car_type),c.description_local??Jc(c.car_type)),price:l?null:String(c.price_entry.net_price),price_currency:l?null:c.price_entry.currency,na:l}}),routes:zr(s.routes,o)}}async cancelRide(t,r,n){return $c(this.followupHost(),this.jwt,t,{cancel_reason_type:n??null,cancel_reason:r??null})}getCancelPenalty(t,r){return Hc(this.followupHost(),this.jwt,t,r)}async getCancelReasons(t){return((await qc(this.followupHost(),this.jwt,t)).reasons??[]).map(n=>({id:n.reason_id,title:n.title}))}async getShareLink(t,r="en"){return jc(await Gc(this.followupHost(),this.jwt,t,r))}getCards(t){return Tc(t,this.jwt)}hostForRegion(t){return ie(we(t),this.config.cliGatewayHosts,this.config.cliGatewayHost)}async getTipConfig(t){return zc(await Yc(this.hostForRegion(t),this.jwt,t))}async payTip(t,r){if(!r.paymentItemUuid)throw new Error("NO_CARD: member tip requires a payment_item_uuid (card).");let n={amount:r.amount,currency:r.currency??"",payment_item_uuid:r.paymentItemUuid};return Qc(await Xc(this.followupHost(),this.jwt,t,n))}sendChat(t,r){return Zc(this.followupHost(),this.jwt,t,r)}subscribeChat(t){return new _n(this.followupHost(),this.jwt,t)}getMessages(t,r){return As(this.followupHost(),this.jwt,t,{since:r})}};async function te(e,t){if(t.wallet_address){let o=P(t.wallet_address);if(!o)throw new Error("JWT_NOT_FOUND: no valid wallet JWT. Run siwe-request-message + siwe-submit first.");return{backend:new Bt(e,o,t.wallet_address),jwt:o,identity:t.wallet_address,mode:"wallet"}}Ue(ee(e.cliGatewayHosts,e.cliGatewayHost));let r={clientId:e.riderCliApp.clientId,clientSecret:e.riderCliApp.clientSecret},n=await an(r);if(!n)throw new Error("Your TADA/Throo session expired. Run `amb login` again.");let s=t.member_id??"",i=ie(we(t.region),e.cliGatewayHosts,e.cliGatewayHost);return{backend:new $t(e,n,s,i),jwt:n,identity:s,mode:"tada"}}async function Ht(e){let t=K();if(t.mode==="wallet"){let r=t.walletAddress;if(!r)throw new Error("No registered wallet. Run wallet setup first.");let n=P(r);if(!n)throw new Error("JWT_NOT_FOUND: no valid wallet JWT. Run siwe-request-message + siwe-submit first.");return{backend:new Bt(e,n,r),jwt:n,identity:r,mode:"wallet"}}if(t.mode==="tada"){Ue(ee(e.cliGatewayHosts,e.cliGatewayHost));let r={clientId:e.riderCliApp.clientId,clientSecret:e.riderCliApp.clientSecret},n=await an(r);if(!n)throw new Error("Your TADA/Throo session expired. Run `amb login` again.");let s=t.member?.id??"";return{backend:new $t(e,n,s),jwt:n,identity:s,mode:"tada"}}throw new Error("No active account. Run `amb login` (TADA/Throo member) or wallet setup (crypto) first.")}async function Fe(e){Ue(ee(e.cliGatewayHosts,e.cliGatewayHost));let t={clientId:e.riderCliApp.clientId,clientSecret:e.riderCliApp.clientSecret},r=await an(t);if(!r)throw new Error("Your TADA/Throo session expired. Run `amb login` again.");return r}function ge(e){if(typeof e=="number")return Number.isFinite(e)?e:null;if(typeof e=="string"&&e.trim()!==""){let t=Number(e);return Number.isFinite(t)?t:null}return null}var rl=["lat_lng","locationPoint"];function Ng(e){if(e==null||typeof e!="object")return null;let t=rl.map(i=>e[i]).find(i=>i!=null&&typeof i=="object"),r=ge(e.latitude)??ge(e.lat)??ge(t?.latitude),n=ge(e.longitude)??ge(e.lng)??ge(t?.longitude);if(r==null||n==null)return null;let s={...e};for(let i of["latitude","longitude","lat","lng",...rl])delete s[i];return{...s,latitude:r,longitude:n}}function nl(e){if(!Array.isArray(e)||e.length===0)return null;let t=[];for(let r of e){let n=Ng(r);if(n==null)return null;t.push(n)}return t}var kg=new Set(["ASSIGNED","PICKUP","PICKUP_ARRIVED"]),Cg=new Set(["INUSE","INUSE_WITH_CANCEL_PENDING"]),Dg=new Set(["PAYCONFIRMED","PENDING","ASSIGNED","PICKUP","PICKUP_ARRIVED"]);function yn(e){let{status:t,mode:r,supportsReasons:n,hasReasonType:s}=e;return Ie(t)?{kind:"not_cancelable",why:`ride already ended (${t})`}:Cg.has(t)?{kind:"not_cancelable",why:`ride is in progress (${t}) \u2014 it cannot be cancelled`}:r==="crypto"&&!Dg.has(t)?{kind:"not_cancelable",why:`ride cannot be cancelled in status ${t}`}:kg.has(t)&&n&&!s?{kind:"needs_reason"}:{kind:"immediate"}}var Pg=async e=>{let t=R();if(b(t.dataDir),K().mode==="tada"){let[d,u,m,p,f]=e;(!d||!u||!m||!p)&&(console.error("error: Usage:"),console.error(" [crypto] amb ride-search <wallet_address> <region> <origin_lat> <origin_lng> <dest_lat> <dest_lng>"),console.error(" [member] amb ride-search <origin_lat> <origin_lng> <dest_lat> <dest_lng> [card_uuid]"),console.error("hint: card_uuid is optional \u2014 the default/only card is auto-selected when omitted."),process.exit(1));let _=ge(d),h=ge(u),I=ge(m),E=ge(p);if(_==null||h==null||I==null||E==null){g({error:"INVALID_COORDINATES",message:"origin/destination lat and lng must all be numbers."}),process.exit(1);return}let A;try{A=await Ht(t)}catch(U){g({error:U.message}),process.exit(1);return}let N=ie(pt(_,h),t.cliGatewayHosts,t.cliGatewayHost),C=await Mt(A.backend,f,N);if(C==null)return;try{let U=await A.backend.searchRides({locations:[{latitude:_,longitude:h},{latitude:I,longitude:E}],paymentItemUuid:C});w(U)}catch(U){g({error:U.message}),process.exit(1)}return}let[s,i,o,a,c,l]=e;(!s||!i||!o||!a||!c||!l)&&(console.error("error: Usage:"),console.error(" [crypto] amb ride-search <wallet_address> <region> <origin_lat> <origin_lng> <dest_lat> <dest_lng>"),console.error(" [member] amb ride-search <origin_lat> <origin_lng> <dest_lat> <dest_lng> <card_uuid>"),console.error("hint: region: SG (Singapore), NY (New York)"),process.exit(1)),k(s),$(s),O(t.appServerUrl),H(t.gatewayServerUrl);try{let d=await Zr({wallet_address:s,region:i,locations:[{latitude:parseFloat(o),longitude:parseFloat(a)},{latitude:parseFloat(c),longitude:parseFloat(l)}],payCurrency:t.payCurrency});w(d)}catch(d){g({error:d.message}),process.exit(1)}},Lg=async e=>{let t=R();if(b(t.dataDir),K().mode==="tada"){let c=e[0];c||(console.error("error: Usage: amb ride-request <json_params>"),console.error('hint: [member] json_params: {"locations":[...], "payment_item_uuid":"<uuid, optional \u2014 auto-selected if omitted>", "product_id":<optional>}'),process.exit(1));let l;try{l=JSON.parse(c)}catch{g({error:"INVALID_JSON",message:"Failed to parse json_params argument."}),process.exit(1);return}let d=l.locations,u=l.payment_item_uuid,m=l.product_id;if(!d||d.length===0){g({error:"MISSING_FIELD",message:"locations is required in json_params (member mode) and must contain [origin, destination]."}),process.exit(1);return}let p;try{p=await Ht(t)}catch(E){g({error:"RIDE_REQUEST_FAILED",message:E.message}),process.exit(1);return}let f=nl(d);if(f==null){g({error:"INVALID_COORDINATES",message:"Every entry in locations needs numeric coordinates (latitude/longitude, or lat/lng)."}),process.exit(1);return}let _=f[0],h=ie(pt(_.latitude,_.longitude),t.cliGatewayHosts,t.cliGatewayHost),I=await Mt(p.backend,u,h);if(I==null)return;try{let E=await p.backend.bookRide({locations:f,paymentItemUuid:I,...m!=null?{productId:m}:{}});await xe(E.rideId),w(E)}catch(E){g({error:"RIDE_REQUEST_FAILED",message:E.message}),process.exit(1)}return}let s=e[0];s||(console.error("error: Usage: amb ride-request <json_params>"),console.error("hint: see references/ride.md for the full json_params schema"),process.exit(1));let i;try{i=JSON.parse(s)}catch{g({error:"INVALID_JSON",message:"Failed to parse json_params argument."}),process.exit(1);return}let o=i.wallet_address,a=i.rider?.phone;if(!o){g({error:"MISSING_FIELD",message:"wallet_address is required in json_params."}),process.exit(1);return}if(k(o),$(o),a&&!eo(o,a)){g({error:"PHONE_NOT_VERIFIED",message:`Phone ${a} is not verified.`,action:"Run `amb phone-verify-start` + `amb phone-verify-confirm` first."}),process.exit(1);return}O(t.appServerUrl),H(t.gatewayServerUrl);try{let c=await en(i,t);await xe(c.request_id),w(c)}catch(c){let l=c.message??"";l.toLowerCase().includes("deposit")||l.toLowerCase().includes("collateral")?g({error:"INSUFFICIENT_DEPOSIT",message:l,action:"Run `amb deposit-status` to identify the network, then `amb deposit-add <wallet> <network> <token> <amount>`."}):g({error:"RIDE_REQUEST_FAILED",message:l,action:"Run `amb deposit-status` + `amb wallet-balance` to diagnose."}),process.exit(1)}};function il(e){let t=[],r;for(let n=0;n<e.length;n++){let s=e[n],i="--reason-type=";if(s==="--reason-type"||s.startsWith(i)){let o=s.startsWith(i)?s.slice(i.length):e[++n];(o==null||!/^\d+$/.test(o))&&(g({error:"INVALID_ARGUMENT",message:`--reason-type must be an integer id (got: ${o??"<missing>"}). Get valid ids from the cancel_pending reasons list.`}),process.exit(1)),r=parseInt(o,10)}else t.push(s)}return{positional:t,reasonType:r}}async function ol(e,t,r,n){let s=await e.getCancelReasons(t),i;if(n==="member")try{i=await e.getCancelPenalty(t)}catch{}w({cancel_pending:!0,ride_status:r,reasons:s,...i?.cancellation_fee!=null?{cancellation_fee:i.cancellation_fee,cancellation_fee_currency:i.cancellation_fee_currency,fee_varies_by_reason:!0}:{},message:n==="member"?"Ride is NOT cancelled yet. Show these reasons to the rider, let them pick one, then re-run with --reason-type <id>. Do not pick a reason on the rider's behalf. The fee shown is the default \u2014 it can differ by reason (re-check with `amb ride-cancel-penalty <request_id> --reason-type <id>`).":"Ride is NOT cancelled yet. Show these reasons to the rider, let them pick one, then re-run with --reason-type <id>. Do not pick a reason on the rider's behalf. The cancellation fee is reported after cancelling (isFreeCancel)."})}async function sl(e,t,r,n){return await ol(e,t,r,n),{kind:"done"}}async function Og(e,t,r,n,s,i){let o;try{o=await e.cancelRide(t,r,n)}catch(l){if(!(l instanceof Y))throw l;if(l.status===409&&l.code==="ride_cancel_not_allowed"){let d=await e.getRideStatus(t),u=yn({status:d.status,mode:s,supportsReasons:i,hasReasonType:n!=null});return u.kind==="needs_reason"?sl(e,t,d.status,s):u.kind==="not_cancelable"?{kind:"not_cancelable",why:u.why,status:d.status}:{kind:"hold",status:d.status}}if(l.status===400&&l.code==="invalid_parameter"&&n!=null&&i){let d=await e.getRideStatus(t),u=yn({status:d.status,mode:s,supportsReasons:i,hasReasonType:!1});return u.kind==="not_cancelable"?{kind:"not_cancelable",why:u.why,status:d.status}:sl(e,t,d.status,s)}return{kind:"failed",message:l.message}}let a=o,c=s==="member"?a?.ride_status??"USER_CANCELED":a?.isFreeCancel===!1?"USER_CANCELED_NO_FREE":"USER_CANCELED";return Ve(t,c),w(o),{kind:"done"}}var Mg=async e=>{let{positional:t,reasonType:r}=il(e),n=t[0];n||(console.error("error: Usage: amb ride-cancel <request_id> [wallet_address] [reason] [--reason-type <id>]"),process.exit(1));let s=R();b(s.dataDir),await xe(n);let i=V(n);if(i?.status?.startsWith("EXPIRED")&&(w({already_expired:!0,status:i.status,message:"Ride has already expired. No active ride to cancel."}),process.exit(0)),i){let d=i.wallet_address?"crypto":"member",u=d==="crypto"?t[2]:t[1],m=d==="member",p;try{let{backend:f}=await te(s,i),_=await f.getRideStatus(n),h=yn({status:_.status,mode:d,supportsReasons:m,hasReasonType:r!=null});h.kind==="not_cancelable"?p={kind:"not_cancelable",why:h.why,status:_.status}:h.kind==="needs_reason"?(await ol(f,n,_.status,d),p={kind:"done"}):p=await Og(f,n,u,r,d,m)}catch(f){p={kind:"failed",message:f.message}}if(p.kind==="done")return;if(p.kind==="not_cancelable"){g({error:"RIDE_NOT_CANCELABLE",message:p.why,status:p.status}),process.exit(1);return}if(p.kind==="hold"){g({error:"RIDE_CANCEL_FAILED",message:"the payment is being held \u2014 retry in a moment",status:p.status}),process.exit(1);return}g({error:"RIDE_CANCEL_FAILED",message:p.message}),process.exit(1);return}let o=t[1],a=t[2],c=o;if(!c){g({error:"RIDE_NOT_FOUND",message:`No local record for request_id: ${n}. Provide wallet_address as 2nd argument.`}),process.exit(1);return}k(c),$(c),O(s.appServerUrl),H(s.gatewayServerUrl);let l=P(c);if(!l){g({error:"JWT_NOT_FOUND",message:"No valid JWT. Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1);return}try{let d=await Wr(l,n,a,r);Ve(n,d.isFreeCancel===!1?"USER_CANCELED_NO_FREE":"USER_CANCELED"),w(d)}catch(d){g({error:"RIDE_CANCEL_FAILED",message:d.message}),process.exit(1)}},Ug=async e=>{let t=e[0],r=e[1];t||(console.error("error: Usage: amb ride-status <request_id> [wallet_address]"),process.exit(1));let n=R();b(n.dataDir),await xe(t);let s=V(t);if(s){try{let{backend:a}=await te(n,s),c=await a.getRideStatus(t);Ve(t,c.status),w(c)}catch(a){g({error:"RIDE_STATUS_FAILED",message:a.message}),process.exit(1)}return}let i=r;if(!i){g({error:"RIDE_NOT_FOUND",message:`No local record for request_id: ${t}. Provide wallet_address as 2nd argument.`}),process.exit(1);return}k(i),$(i),O(n.appServerUrl),H(n.gatewayServerUrl);let o=P(i);if(!o){g({error:"JWT_NOT_FOUND",message:"No valid JWT. Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1);return}try{let a=await Ot(o,t);Ve(t,a.status),w(a)}catch(a){g({error:"RIDE_STATUS_FAILED",message:a.message}),process.exit(1)}},Bg=async e=>{let{positional:t,reasonType:r}=il(e),n=t[0];if(!n){console.error("error: Usage: amb ride-cancel-penalty <request_id> [--reason-type <id>]"),process.exit(1);return}let s=R();b(s.dataDir);let i=V(n);if(!i){g({error:"RIDE_NOT_FOUND",message:`No local record for request_id: ${n}.`}),process.exit(1);return}try{let{backend:o}=await te(s,i),a=await o.getCancelPenalty(n,r);w(a)}catch(o){o instanceof Be?g({error:"NOT_SUPPORTED",message:"Cancellation fee pre-check is TADA/Throo member-mode only; in crypto mode the fee is reported on cancel (isFreeCancel)."}):g({error:"RIDE_CANCEL_PENALTY_FAILED",message:o.message??""}),process.exit(1)}},Fg=async e=>{let t="en",r=[];for(let o=0;o<e.length;o++){let a=e[o];a==="--lang"&&e[o+1]!=null?t=e[++o]:r.push(a)}let n=r[0];if(!n){console.error("error: Usage: amb ride-share <request_id> [--lang TAG]"),process.exit(1);return}let s=R();b(s.dataDir);let i=V(n);if(!i){g({error:"RIDE_NOT_FOUND",message:`No local record for request_id: ${n}.`}),process.exit(1);return}try{let{backend:o}=await te(s,i),a=await o.getShareLink(n,t);w(a)}catch(o){if(o instanceof ft){g({error:o.code,message:o.message}),process.exit(1);return}g({error:"SHARE_LINK_FAILED",message:o.message}),process.exit(1)}},$g=async e=>{let[t,r]=e,n=r?parseInt(r,10):20;t||(console.error("error: Usage: amb ride-history <wallet_address> [limit]"),console.error("hint: limit: max history entries (default 20)"),process.exit(1));let s=R();b(s.dataDir),k(t),$(t),Oe(s.gatewayServerUrl);let i=P(t);if(!i){g({error:"JWT_NOT_FOUND",message:"No valid JWT. Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1);return}try{let o=await dc(i,n);w(o)}catch(o){g({error:"RIDE_HISTORY_FAILED",message:o.message}),process.exit(1)}};function Hg(e,t){let r=V(t);return r?.region?ie(we(r.region),e.cliGatewayHosts,e.cliGatewayHost):e.cliGatewayHost}function qg(e){if(!e.ready){g({error:"RECEIPT_NOT_READY",message:"Receipt settlement is still pending. Please try again shortly."}),process.exitCode=75;return}console.log(Mc(e))}var jg=async e=>{let t=e.filter(c=>c.startsWith("--format"));if(t.length>1||t[0]&&t[0]!=="--format=markdown"||wt()==="json"){g({error:"INVALID_RECEIPT_OPTIONS",message:"ride-receipt emits Markdown; '--format=markdown' is optional and --json is not supported."}),process.exit(1);return}let r=e.filter(c=>!c.startsWith("--format"));if(r.length!==1){g({error:"INVALID_RECEIPT_USAGE",message:"Usage: amb ride-receipt <request_id>. For raw detail, use ride-history-detail."}),process.exit(1);return}let n=r[0],s=R();b(s.dataDir);let i=V(n);if(!i){g({error:"RIDE_NOT_FOUND",message:`No local record for request_id: ${n}.`}),process.exit(1);return}let o=hc(),a=i.wallet_address?"wallet":"tada";if(o&&o!==a){g({error:"RECEIPT_ACCOUNT_CONFLICT",message:`This ride belongs to the ${a} account; remove --account=${o}.`}),process.exit(1);return}try{let{backend:c}=await te(s,i);qg(await c.getReceipt(n))}catch(c){g({error:"RIDE_RECEIPT_FAILED",message:c.message}),process.exit(1)}},Gg=async e=>{if(K().mode==="tada"){let[o]=e;if(!o){console.error("error: Usage: amb ride-history-detail <request_id>"),process.exit(1);return}let a=R();b(a.dataDir);let c;try{c=await Fe(a)}catch(l){g({error:"MEMBER_SESSION_EXPIRED",message:l.message}),process.exit(1);return}try{let l=await Ft(Hg(a,o),c,o);w(l)}catch(l){g({error:"RIDE_HISTORY_DETAIL_FAILED",message:l.message}),process.exit(1)}return}let[r,n]=e;(!r||!n)&&(console.error("error: Usage: amb ride-history-detail <wallet_address> <request_id>"),process.exit(1));let s=R();b(s.dataDir),k(r),$(r),Oe(s.gatewayServerUrl);let i=P(r);if(!i){g({error:"JWT_NOT_FOUND",message:"No valid JWT. Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1);return}try{let o=await nn(i,n);if(wt()==="plain"){let a=o.receiptInfo?.paidNetwork??null,c={...o},l=ut(a,o.receiptInfo?.paidTxHash??null);if(l&&(c.receiptInfo={...o.receiptInfo,explorerUrl:l}),o.refundReceiptInfo){let u=ut(o.refundReceiptInfo.refundNetwork??a,o.refundReceiptInfo.refundTxHash);c.refundReceiptInfo=u?{...o.refundReceiptInfo,explorerUrl:u}:o.refundReceiptInfo}let d=o.tipInfo?.tipRequest?.txHash??null;if(d){let u=ut(o.tipInfo?.tipRequest?.network??null,d);u&&o.tipInfo&&(c.tipInfo={...o.tipInfo,tipRequest:{...o.tipInfo.tipRequest,explorerUrl:u}})}w(c)}else w(o)}catch(o){g({error:"RIDE_HISTORY_DETAIL_FAILED",message:o.message}),process.exit(1)}},al={"ride-search":Pg,"ride-request":Lg,"ride-cancel":Mg,"ride-status":Ug,"ride-cancel-penalty":Bg,"ride-share":Fg,"ride-history":$g,"ride-history-detail":Gg,"ride-receipt":jg};function Wg(e){let t=j(e);if(!t)throw new Error("JWT_NOT_FOUND: No cached JWT for this wallet. Call siwe_get_message + siwe_login first.");return t}async function cl(e){let{request_id:t,wallet_address:r,config:n}=e,s=Wg(r),{accepts:i,nonce:o}=await Ya(s,t),a=i[0],c=String(Math.floor(Date.now()/1e3)+a.maxTimeoutSeconds),l=parseInt(a.network.split(":")[1],10),d={domain:{name:a.extra.name,version:a.extra.version,chainId:l,verifyingContract:a.asset},types:{EIP712Domain:[{name:"name",type:"string"},{name:"version",type:"string"},{name:"chainId",type:"uint256"},{name:"verifyingContract",type:"address"}],TransferWithAuthorization:[{name:"from",type:"address"},{name:"to",type:"address"},{name:"value",type:"uint256"},{name:"validAfter",type:"uint256"},{name:"validBefore",type:"uint256"},{name:"nonce",type:"bytes32"}]},primaryType:"TransferWithAuthorization",message:{from:r,to:a.payTo,value:a.amount,validAfter:"0",validBefore:c,nonce:o}},{typed_data:u,isSmartAccount:m}=await un({rawTypedData:d,walletAddress:r,target:{rpcUrl:n.paymentRpcUrl,chainId:l}});return Kr(t,{accepts:i,nonce:o,walletAddress:r,validBefore:c,expiresAt:Date.now()+a.maxTimeoutSeconds*1e3,isSmartAccount:m}),{typed_data:u}}function Vg(e){let t=j(e);if(!t)throw new Error("JWT_NOT_FOUND: No cached JWT for this wallet. Call siwe_get_message + siwe_login first.");return t}async function ll(e){let{request_id:t,signature:r}=e,n=Yr(t);if(!n)throw new Error("PAYMENT_CACHE_MISS: request_id not found or expired. Call ride_pay_prepare again.");let{accepts:s,nonce:i,walletAddress:o,validBefore:a,isSmartAccount:c}=n,l=Vg(o),d=s[0],u={x402Version:2,accepted:d,payload:{authorization:{from:o,to:d.payTo,value:d.amount,validAfter:"0",validBefore:a,nonce:i},signature:pn(r,c===!0)}},m=Buffer.from(JSON.stringify(u)).toString("base64"),p=await Xa(l,t,m);return{success:p.success,tx_hash:p.transaction}}var Jg=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb ride-pay-prepare <request_id> <wallet_address>"),process.exit(1));let n=R();b(n.dataDir),k(r),$(r),O(n.appServerUrl),H(n.gatewayServerUrl);try{let s=await cl({request_id:t,wallet_address:r,config:n});w(s)}catch(s){g({error:s.message}),process.exit(1)}},Kg=async e=>{let[t,r,...n]=e;(!t||!r||n.length>0)&&(console.error("error: Usage: amb ride-pay-confirm <request_id> <signature>"),console.error("hint: signature: hex from `amb wallet-sign` with sign_method=eth_signTypedData_v4"),n.length>0&&console.error(`error: unexpected extra argument(s): ${n.join(" ")}`),process.exit(1)),/^0x[0-9a-fA-F]{100,}$/.test(r)||(console.error("error: signature looks too short to be a valid hex signature (got length "+r.length+")"),console.error("hint: pass the wallet_signature returned by `amb wallet-sign`, not the wallet address"),process.exit(1));let s=R();b(s.dataDir),O(s.appServerUrl),H(s.gatewayServerUrl);try{let i=await ll({request_id:t,signature:r});w(i)}catch(i){let o=i.message??"";o.toLowerCase().includes("insufficient")||o.toLowerCase().includes("balance")?g({error:"INSUFFICIENT_BALANCE",message:o,action:"Run `amb wallet-balance` and top up your wallet."}):g({error:"RIDE_PAY_CONFIRM_FAILED",message:o,action:"Check `amb wallet-balance` for wallet balance."}),process.exit(1)}},dl={"ride-pay-prepare":Jg,"ride-pay-confirm":Kg};function Yg(e){let t=[],r,n;for(let s=0;s<e.length;s++)e[s]==="--currency"&&e[s+1]!=null?r=e[++s]:e[s]==="--card"&&e[s+1]!=null?n=e[++s]:t.push(e[s]);return{positionals:t,currency:r,card:n}}var Xg=async e=>{let t=e[0];if(!t){console.error("error: Usage: amb tip-config <region>"),console.error("hint: region: SG (Singapore), NY (New York). Member mode resolves the active account; crypto uses the registered wallet."),process.exit(1);return}let r=R();b(r.dataDir);let n;try{n=await Ht(r)}catch(s){g({error:"TIP_CONFIG_FAILED",message:s.message}),process.exit(1);return}try{w(await n.backend.getTipConfig(t))}catch(s){g({error:"TIP_CONFIG_FAILED",message:s.message}),process.exit(1)}},zg=async e=>{let{positionals:t,currency:r,card:n}=Yg(e),[s,i]=t,o=i?parseFloat(i):NaN;if(!s||Number.isNaN(o)){console.error("error: Usage: amb tip <ride_id> <amount> [--currency C] [--card uuid]"),console.error("hint: amount within the tip-config min/max. Member: card auto-selected if --card omitted; crypto: pays from the wallet."),process.exit(1);return}let a=R();b(a.dataDir);let c=V(s);if(!c){g({error:"RIDE_NOT_FOUND",message:`No local record for ride_id: ${s}.`}),process.exit(1);return}let l;try{l=await te(a,c)}catch(f){g({error:"TIP_FAILED",message:f.message}),process.exit(1);return}let{backend:d,mode:u}=l,m=r,p;if(u==="tada"){let f;try{f=await d.getTipConfig(c.region)}catch(I){g({error:"TIP_CONFIG_FAILED",message:I.message}),process.exit(1);return}if(!f.enabled){g({error:"TIP_NOT_AVAILABLE",message:"Tipping is not available for this ride/region."}),process.exit(1);return}m=r??f.currency;let _=ie(we(c.region),a.cliGatewayHosts,a.cliGatewayHost),h=await Mt(d,n,_);if(h==null)return;p=h}try{let f=await d.payTip(s,{amount:o,currency:m,paymentItemUuid:p});w(f)}catch(f){if(f instanceof Y)f.code==="tip_not_available"?g({error:"TIP_NOT_AVAILABLE",message:"Tipping is not available yet. The fare may still be settling (try again in a moment), or the tip window has passed / the region is disabled."}):f.code==="tip_already_paid"?g({error:"TIP_ALREADY_PAID",message:"This ride has already been tipped."}):f.status===402?g({error:"TIP_PAYMENT_FAILED",message:"Payment failed. Check the card and try again."}):f.status===400?g({error:"TIP_AMOUNT_INVALID",message:"Tip amount is outside the allowed range. Run `amb tip-config <region>`."}):f.status===401||f.code==="unauthorized"?g({error:"SESSION_EXPIRED",message:"Your TADA/Throo session expired. Run `amb login` again."}):g({error:"TIP_FAILED",message:f.message});else{let _=f.message??"";_.toLowerCase().includes("insufficient")||_.toLowerCase().includes("balance")?g({error:"INSUFFICIENT_BALANCE",message:_,action:"Run `amb wallet-balance` and top up your wallet."}):g({error:"TIP_FAILED",message:_})}process.exit(1)}},Qg=async e=>{let[t,r,n]=e,s=r?parseFloat(r):NaN;(!t||isNaN(s)||!n)&&(console.error("error: Usage: amb tip-prepare <request_id> <price> <wallet_address>"),process.exit(1));let i=R();b(i.dataDir),k(n),$(n),H(i.gatewayServerUrl),Oe(i.gatewayServerUrl);try{let o=await mn({request_id:t,price:s,wallet_address:n,config:i});w(o)}catch(o){g({error:"TIP_PREPARE_FAILED",message:o.message}),process.exit(1)}},Zg=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb tip-confirm <request_id> <signature>"),console.error("hint: signature: hex from `amb wallet-sign` with sign_method=eth_signTypedData_v4"),process.exit(1));let n=R();b(n.dataDir),Oe(n.gatewayServerUrl);try{let s=await gn({request_id:t,signature:r});w(s)}catch(s){let i=s.message??"";i.includes("PAYMENT_CACHE_MISS")?g({error:"PAYMENT_CACHE_MISS",message:i,action:"Session expired. Run `amb tip-prepare` again."}):i.toLowerCase().includes("insufficient")||i.toLowerCase().includes("balance")?g({error:"INSUFFICIENT_BALANCE",message:i,action:"Run `amb wallet-balance` and top up your wallet."}):g({error:"TIP_CONFIRM_FAILED",message:i}),process.exit(1)}},ul={"tip-config":Xg,tip:zg,"tip-prepare":Qg,"tip-confirm":Zg};var ml=new Set(["maps.app.goo.gl","goo.gl"]),ef=new Set(["www.google.com","google.com","maps.google.com"]),pl=5;function $e(e,t){let r=new Error(t?`${e}: ${t}`:e);throw r.code=e,r}async function tf(e){let t=e;for(let r=0;r<pl;r++){let n=new URL(t);if(!ml.has(n.host))return t;let s;try{s=await fetch(t,{redirect:"manual",method:"HEAD"})}catch(i){$e("URL_RESOLVE_FAILED",i.message)}if(s.status>=300&&s.status<400){let i=s.headers.get("location");i||$e("URL_RESOLVE_FAILED","redirect status without Location header"),t=new URL(i,t).toString();continue}$e("URL_RESOLVE_FAILED",`unexpected status ${s.status} on short link`)}$e("URL_RESOLVE_FAILED",`redirect chain exceeded ${pl}`)}function rf(e){let t=e.match(/\/maps\/place\/([^/]+)/);if(!t)return;let r=decodeURIComponent(t[1].replace(/\+/g," ")),n=r.indexOf(",");return(n>=0?r.slice(0,n):r).trim()}function nf(e){let t=e.match(/!3d(-?\d+(?:\.\d+)?)!4d(-?\d+(?:\.\d+)?)/);if(t)return{lat:parseFloat(t[1]),lng:parseFloat(t[2])}}function sf(e){let t=e.match(/\/@(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?),/);if(t)return{lat:parseFloat(t[1]),lng:parseFloat(t[2])}}function of(e){let t=e.match(/!1s(0x[0-9a-fA-F]+:0x[0-9a-fA-F]+)/);return t?t[1]:void 0}async function Is(e){let t;try{t=new URL(e)}catch{$e("INVALID_URL","not a parseable URL")}if(ml.has(t.host)){let o=await tf(e);return Is(o)}ef.has(t.host)||$e("INVALID_URL",`host ${t.host} not in allowlist`);let r=t.pathname,n=nf(r)??sf(r);n||$e("COORDS_NOT_FOUND_IN_URL");let s=rf(r)??$e("INVALID_URL","no /place/<name>/ segment"),i=of(r);return{name:s,latitude:n.lat,longitude:n.lng,...i&&{ftid:i}}}var xs="";function gl(e){xs=e.replace(/\/$/,"")}function _t(e,t){let r=new Error(t?`${e}: ${t}`:e);throw r.code=e,r}async function fl(e,t){xs||_t("RESOLVE_FAILED","initSavedPlacesApi not called");let r=await fetch(`${xs}/v1/places/resolve-google`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json"},body:JSON.stringify(t)});if(r.ok)try{return await r.json()}catch(n){_t("RESOLVE_FAILED",`malformed JSON response: ${n.message}`)}r.status===404&&_t("PLACE_NOT_FOUND"),r.status===422&&_t("REGION_UNSUPPORTED"),r.status===401&&_t("JWT_EXPIRED"),_t("RESOLVE_FAILED",`status ${r.status}`)}function af(e){if(!e)return null;let t=e.match(/^0x[0-9a-fA-F]+:0x([0-9a-fA-F]+)$/);if(!t)return null;try{return BigInt("0x"+t[1])}catch{return null}}function cf(e){let t=e.replace(/-/g,"+").replace(/_/g,"/"),r=t+"=".repeat((4-t.length%4)%4),n=Buffer.from(r,"base64");if(n.length<8)throw new Error("place_id too short");let s=0n;for(let i=0;i<8;i++)s|=BigInt(n[n.length-8+i])<<BigInt(8*i);return s}async function _l(e){let t=af(e);if(t===null)return null;let s=`https://www.google.com/maps/preview/place?hl=en&gl=us&pb=${`!1m4!1s${`0x0%3A0x${t.toString(16)}`}!3m2!2m1!1i1`}`;try{let i=await fetch(s,{headers:{"User-Agent":"Mozilla/5.0"}});if(!i.ok)return null;let a=(await i.text()).match(/ChI[A-Za-z0-9_-]{15,}/g)??[];for(let c of a)try{if(cf(c)===t)return c}catch{}return null}catch{return null}}function yl(e,t,r){return Me("/v1/places",e,{query:{query:t.query,region:t.region,city:t.city,originLat:t.originLat,originLng:t.originLng,sessionToken:t.sessionToken},lang:r})}function hl(e,t,r,n){return Me(`/v1/places/${encodeURIComponent(t)}`,e,{query:{region:r.region,city:r.city},lang:n})}function wl(e,t,r){return Me("/v1/places/nearby",e,{query:{latitude:t.latitude,longitude:t.longitude,limit:t.limit},lang:r})}function El(e,t,r){return Me("/v1/places/airports",e,{query:{city:t.city},lang:r})}function Rl(e,t,r){return Me("/v1/places/reverse-geocode",e,{query:{latitude:t.latitude,longitude:t.longitude,region:t.region},lang:r})}function qt(e){let t=[],r,n,s;for(let i=0;i<e.length;i++){let o=e[i];o==="--city"&&e[i+1]!=null?r=e[++i]:o==="--session-token"&&e[i+1]!=null?n=e[++i]:o==="--lang"&&e[i+1]!=null?s=e[++i]:t.push(o)}return{positional:t,city:r,sessionToken:n,lang:s}}function jt(e){switch(e.code){case"unsupported_region":return"Region not supported (currently NY, SG). Re-run with a supported region.";case"place_not_found":return"Place not found. Check the place id or coordinates and try again.";case"invalid_parameter":return"Invalid parameter. Check the query, region, or coordinates.";case"place_api_error":return"Place search is temporarily unavailable. Please try again.";default:return e.message}}function vs(e){g({error:"NOT_SUPPORTED",message:`tada ${e} is only available for TADA/Throo members. Run 'amb login' first.`}),process.exit(1)}var lf=async e=>{if(K().mode==="tada"){let{positional:u,city:m,sessionToken:p,lang:f}=qt(e),[_,h,I,E]=u;(!_||!h)&&(console.error("error: Usage: amb place-search <query> <region> [originLat originLng] [--city CODE] [--session-token UUID] [--lang TAG]"),console.error("hint: region: NY, SG. multi-word query is quoted."),process.exit(1)),I!==void 0!=(E!==void 0)&&(g({error:"INVALID_COORDINATES",message:"originLat and originLng must be provided together."}),process.exit(1));let A=I!==void 0?parseFloat(I):void 0,N=E!==void 0?parseFloat(E):void 0;(A!==void 0&&isNaN(A)||N!==void 0&&isNaN(N))&&(g({error:"INVALID_COORDINATES",message:"originLat and originLng must be valid numbers."}),process.exit(1));let C=R();b(C.dataDir),gt(ee(C.cliGatewayHosts,C.cliGatewayHost));let U;try{U=await Fe(C)}catch(re){g({error:"MEMBER_AUTH_FAILED",message:re.message}),process.exit(1);return}try{let re=await yl(U,{query:_,region:h,city:m,originLat:A,originLng:N,sessionToken:p},f);w({...re,next_action:"PICK_A_PLACE_THEN_BUILD_RIDE_LOCATIONS",must:"Copy place_id / sub_places[].id / lat_lng / name / address verbatim from this response. Never fabricate them. A Google candidate with lat_lng:null must be resolved via place-detail before booking."})}catch(re){g({error:"PLACE_SEARCH_FAILED",message:jt(re)}),process.exit(1)}return}let t=e[0],r=e[1],n=e[2],s=e[3],i=e[4];(!t||!r||!n)&&(console.error("error: Usage: amb place-search <wallet_address> <city> <query> [latitude] [longitude]"),console.error("hint: city: SIN (Singapore), NYC (New York)"),process.exit(1));let o=Lt(r);o||(g({error:"UNKNOWN_CITY",message:`Cannot resolve city: "${r}". Use codes like SIN, BKK, DEN or full names like Singapore, Bangkok, Denver.`}),process.exit(1));let a=s!==void 0?parseFloat(s):void 0,c=i!==void 0?parseFloat(i):void 0;s!==void 0!=(i!==void 0)&&(g({error:"INVALID_COORDINATES",message:"latitude and longitude must both be provided together."}),process.exit(1)),(s!==void 0&&isNaN(a)||i!==void 0&&isNaN(c))&&(g({error:"INVALID_COORDINATES",message:"latitude and longitude must be valid numbers."}),process.exit(1));let l=R();b(l.dataDir),k(t),$(t),O(l.appServerUrl),H(l.gatewayServerUrl);let d=P(t);d||(g({error:"JWT_NOT_FOUND",message:"No valid JWT. Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1));try{let u=await za(d,n,o,a,c);u.length===0?w({results:[],next_action:"REPORT_NO_MATCHES_TO_USER"}):w({results:u,next_action:"ASK_USER_TO_PICK_ONE_RESULT",must:"placeId passed to any subsequent command (amb place-detail, amb map-session-create, amb ride-request) MUST be a value taken verbatim from results[].placeId of this response. NEVER fabricate or substitute a place ID from any external system or from your own model knowledge. If a chosen result has subPlaces, amb map-session-create + amb map-session-verify is mandatory and the user must pick a subPlace on the map \u2014 do NOT enumerate subPlaces as text."})}catch(u){g({error:"PLACE_SEARCH_FAILED",message:u.message}),process.exit(1)}},df=async e=>{if(K().mode==="tada"){let{positional:a,city:c,lang:l}=qt(e),[d,u,m]=a,p=c??m;(!d||!u)&&(console.error("error: Usage: amb place-detail <place_id> <region> [city] [--lang TAG]"),console.error("hint: region: NY, SG"),process.exit(1));let f=R();b(f.dataDir),gt(ee(f.cliGatewayHosts,f.cliGatewayHost));let _;try{_=await Fe(f)}catch(h){g({error:"MEMBER_AUTH_FAILED",message:h.message}),process.exit(1);return}try{let h=await hl(_,d,{region:u,city:p},l);w(h)}catch(h){g({error:"PLACE_DETAIL_FAILED",message:jt(h)}),process.exit(1)}return}let t=e[0],r=e[1],n=e[2];(!t||!r||!n)&&(console.error("error: Usage: amb place-detail <wallet_address> <city> <place_id>"),console.error("hint: city: SIN (Singapore), NYC (New York)"),process.exit(1));let s=Lt(r);s||(g({error:"UNKNOWN_CITY",message:`Cannot resolve city: "${r}". Use codes like SIN, BKK, DEN or full names like Singapore, Bangkok, Denver.`}),process.exit(1));let i=R();b(i.dataDir),k(t),$(t),O(i.appServerUrl),H(i.gatewayServerUrl);let o=P(t);o||(g({error:"JWT_NOT_FOUND",message:"No valid JWT. Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1));try{let a=await Qa(o,n,s);w(a)}catch(a){g({error:"PLACE_DETAIL_FAILED",message:a.message}),process.exit(1)}},uf=async e=>{let t=e[0],r=e[1],n,s=!1;for(let f=2;f<e.length;f++)e[f]==="--label"&&e[f+1]!=null?(n=e[f+1],f++):e[f]==="--force"&&(s=!0);(!t||!r)&&(console.error("error: Usage: amb place-save <wallet_address> <google_maps_url> [--label LABEL]"),process.exit(1));let i=R();b(i.dataDir),k(t),$(t),gl(i.gatewayServerUrl);let o=P(t);o||(console.error(JSON.stringify({error:"JWT_NOT_FOUND",message:"No valid JWT. Run siwe-request-message.js + siwe-submit.js first."})),process.exit(1));let a;try{a=await Is(r)}catch(f){let _=f.code??"INVALID_URL";console.error(JSON.stringify({error:_,message:f.message})),process.exit(1)}let c=await _l(a.ftid),l;try{l=await fl(o,{name:a.name,latitude:a.latitude,longitude:a.longitude,...a.ftid&&{ftid:a.ftid},...c&&{resolvedPlaceId:c}})}catch(f){let _=f.code??"RESOLVE_FAILED";console.error(JSON.stringify({error:_,message:f.message})),process.exit(1)}l.match==="approximate"&&!s&&(console.error(JSON.stringify({error:"APPROXIMATE_MATCH_NEEDS_FORCE",message:"This URL did not resolve to an exact place. Confirm the place below with the user, then re-run with --force to save it.",candidate:{name:l.name,formatted_address:l.formattedAddress},match:l.match})),process.exit(1));let d=st({placeId:l.googlePlaceId,latitude:l.location.latitude,longitude:l.location.longitude}),u,m=!1,p={wallet_address:t,label:n??null,name:l.name,formatted_address:l.formattedAddress,latitude:l.location.latitude,longitude:l.location.longitude,google_place_id:l.googlePlaceId,city:l.city,region:l.region,ftid:a.ftid??null,kg_id:null,source_url:r};d?Gn(t,d)?(u=Wn(t,d,{...n!==void 0&&{label:n},source_url:r,google_place_id:l.googlePlaceId,ftid:a.ftid??null,kg_id:null,formatted_address:l.formattedAddress,name:l.name,city:l.city,region:l.region}),m=!0):u=qn(p):u=qn(p),console.log(JSON.stringify({...u,was_promoted:m},null,2))},pf=async e=>{let t=e[0],r=[],n=!1,s=!1,i;for(let d=1;d<e.length;d++){let u=e[d];if(u==="--match"&&e[d+1]!=null)r.push(e[d+1]),d++;else if(u==="--favorites-only")n=!0;else if(u==="--history-only")s=!0;else if(u==="--limit"&&e[d+1]!=null){let m=e[d+1],p=parseInt(m,10);(!/^\d+$/.test(m)||p<=0)&&(console.log(JSON.stringify({error:"INVALID_LIMIT",value:m})),process.exit(1)),i=p,d++}else u==="--limit"&&(console.log(JSON.stringify({error:"INVALID_LIMIT",message:"--limit requires a positive integer value"})),process.exit(1))}t||(console.error("error: Usage: amb place-list <wallet_address> [--match QUERY ...] [--favorites-only|--history-only] [--limit N]"),process.exit(1)),n&&s&&(console.log(JSON.stringify({error:"CONFLICTING_FILTERS"})),process.exit(1));let o=i??50,a=R();b(a.dataDir),k(t);let c={favoritesOnly:n,historyOnly:s,limit:o},l;r.length===0?l={matches:jn(t,c),matched_tier:null,normalized_queries:[]}:l=ao(t,r,c),console.log(JSON.stringify({matches:l.matches,matched_tier:l.matched_tier,queries:l.normalized_queries,next_action:"USE_FOR_RIDE_OR_FALLBACK_TO_PLACE_SEARCH"},null,2))},mf=async e=>{let t=e[0],r=e[1];(!t||!r)&&(console.error("error: Usage: amb place-remove <wallet_address> <id_or_label>"),process.exit(1));let n=R();b(n.dataDir),k(t);let s=/^\d+$/.test(r)?parseInt(r,10):null,i=s!=null?co(t,s):lo(t,r);if(!i){console.log(JSON.stringify({removed:0,error:"NOT_FOUND"}));return}console.log(JSON.stringify({removed:1,id:i.id,label:i.label}))},gf=async e=>{let t=e[0],r=e[1],n;for(let l=2;l<e.length;l++)e[l]==="--label"&&e[l+1]!=null&&(n=e[l+1],l++);(!t||!r)&&(console.error("error: Usage: amb place-favorite <wallet_address> <id_or_dedup_key> [--label LABEL]"),process.exit(1));let s=R();b(s.dataDir),k(t);let i=/^\d+$/.test(r)?parseInt(r,10):null,o;i!=null?o=At(t,i):o=Gn(t,r),o||(console.log(JSON.stringify({error:"NOT_FOUND"})),process.exit(1));let a=o.is_favorite===1,c=i!=null?po(t,i,n):Wn(t,r,{...n!==void 0&&{label:n}});console.log(JSON.stringify({...c,was_favorite_before:a},null,2))},ff=async e=>{let t=e[0],r=e[1];(!t||!r)&&(console.error("error: Usage: amb place-unfavorite <wallet_address> <id_or_label>"),process.exit(1));let n=R();b(n.dataDir),k(t);let s=/^\d+$/.test(r)?parseInt(r,10):null,i;if(s!=null?i=At(t,s):i=mo(t,r),i||(console.log(JSON.stringify({error:"NOT_FOUND"})),process.exit(1)),i.is_favorite===0){console.log(JSON.stringify({info:"ALREADY_NOT_FAVORITE",row:i},null,2));return}let o=s!=null?go(t,s):fo(t,r);console.log(JSON.stringify({...o,was_favorite_before:!0},null,2))},_f=async e=>{K().mode!=="tada"&&vs("place-nearby");let{positional:t,lang:r}=qt(e),[n,s,i]=t;(!n||!s)&&(console.error("error: Usage: amb place-nearby <lat> <lng> [limit] [--lang TAG]"),process.exit(1));let o=parseFloat(n),a=parseFloat(s),c=i!==void 0?parseInt(i,10):void 0;(isNaN(o)||isNaN(a)||i!==void 0&&isNaN(c))&&(g({error:"INVALID_COORDINATES",message:"lat/lng (and limit) must be valid numbers."}),process.exit(1));let l=R();b(l.dataDir),gt(ee(l.cliGatewayHosts,l.cliGatewayHost));let d;try{d=await Fe(l)}catch(u){g({error:"MEMBER_AUTH_FAILED",message:u.message}),process.exit(1);return}try{w(await wl(d,{latitude:o,longitude:a,limit:c},r))}catch(u){g({error:"PLACE_NEARBY_FAILED",message:jt(u)}),process.exit(1)}},yf=async e=>{K().mode!=="tada"&&vs("place-airports");let{positional:t,lang:r}=qt(e),[n]=t;n||(console.error("error: Usage: amb place-airports <city> [--lang TAG]"),console.error("hint: city = TADA city code, e.g. NYC"),process.exit(1));let s=R();b(s.dataDir),gt(ee(s.cliGatewayHosts,s.cliGatewayHost));let i;try{i=await Fe(s)}catch(o){g({error:"MEMBER_AUTH_FAILED",message:o.message}),process.exit(1);return}try{w(await El(i,{city:n},r))}catch(o){g({error:"PLACE_AIRPORTS_FAILED",message:jt(o)}),process.exit(1)}},hf=async e=>{K().mode!=="tada"&&vs("place-reverse-geocode");let{positional:t,lang:r}=qt(e),[n,s,i]=t;(!n||!s||!i)&&(console.error("error: Usage: amb place-reverse-geocode <lat> <lng> <region> [--lang TAG]"),console.error("hint: region: NY, SG"),process.exit(1));let o=parseFloat(n),a=parseFloat(s);(isNaN(o)||isNaN(a))&&(g({error:"INVALID_COORDINATES",message:"lat/lng must be valid numbers."}),process.exit(1));let c=R();b(c.dataDir),gt(ee(c.cliGatewayHosts,c.cliGatewayHost));let l;try{l=await Fe(c)}catch(d){g({error:"MEMBER_AUTH_FAILED",message:d.message}),process.exit(1);return}try{w(await Rl(l,{latitude:o,longitude:a,region:i},r))}catch(d){g({error:"PLACE_REVERSE_GEOCODE_FAILED",message:jt(d)}),process.exit(1)}},bl={"place-search":lf,"place-detail":df,"place-save":uf,"place-list":pf,"place-remove":mf,"place-favorite":gf,"place-unfavorite":ff,"place-nearby":_f,"place-airports":yf,"place-reverse-geocode":hf};import{readFileSync as wf}from"fs";async function Ns(e,t,r){let n=r?.pollIntervalMs??1e3,s=r?.timeoutMs??5*6e4,i=r?.authUrl??"",o=Date.now()+s,a=`${e.gatewayServerUrl}/v1/map-session/${t}/result`;for(;;){let c;try{c=await(await fetch(a)).json()}catch(d){process.stderr.write(`map-session-check poll_error: ${d.message}
|
|
447
|
+
`)}let l=c?.status;if(l==="completed"&&!c?.result)return{status:"failed",reason:"completed_missing_result"};if(l==="completed"&&c?.result)return{status:"ready",result:c.result};if(l==="jwt_expired"||l==="cancelled"||l==="expired")return{status:"failed",reason:l};if(Date.now()>=o)return{status:"pending",auth_url:i};await new Promise(d=>setTimeout(d,n))}}function Sl(e){let t=[],r,n,s,i=!1;for(let o=0;o<e.length;o++)e[o]==="--subplaces"&&o+1<e.length?r=e[++o]:e[o]==="--subplaces-file"&&o+1<e.length?n=e[++o]:e[o]==="--purpose"&&o+1<e.length?s=e[++o]:e[o]==="--no-wait"?i=!0:t.push(e[o]);return{positional:t,subplacesJson:r,subplacesFile:n,purpose:s,noWait:i}}function ks(e){try{let t=JSON.parse(e);if(Array.isArray(t))return t;if(Array.isArray(t.subPlaces))return t.subPlaces}catch{}}function Al(e,t={}){let r=t.purpose?{...e,purpose:t.purpose}:e;if(wt()==="json"){w(r),e.status==="failed"&&process.exit(1);return}if(e.status==="ready"){let n=e.result.subPlaceId?` (subPlace=${e.result.subPlaceId})`:"";process.stdout.write(`\u2713 Selected: ${e.result.name}, ${e.result.address} (lat=${e.result.lat}, lng=${e.result.lng})${n}
|
|
448
|
+
`);return}if(e.status==="pending"){process.stderr.write(`\u23F3 Still waiting. Resume with: amb map-session-verify <session_id>
|
|
449
|
+
auth_url: ${e.auth_url}
|
|
450
|
+
`);return}process.stderr.write(`\u2717 Map session failed: ${e.reason}
|
|
451
|
+
`),process.exit(1)}var Tl=async e=>{let{positional:t,subplacesJson:r,subplacesFile:n,purpose:s,noWait:i}=Sl(e),o=t[0],a=t[1],c=t[2],l=t[3],d=t.slice(4).join(" ")||void 0;(!o||!a)&&(console.error("error: Usage: map-session <wallet_address> <city> [lat] [lng] [name] [--subplaces JSON | --subplaces-file PATH] [--purpose origin|destination] [--no-wait]"),console.error("hint: city: SIN (Singapore), NYC (New York)"),process.exit(1));let u=Lt(a);u||(g({error:"UNKNOWN_CITY",message:`Cannot resolve city: "${a}"`}),process.exit(1));let m=Pt[u],p=c?parseFloat(c):void 0,f=l?parseFloat(l):void 0,_=R();b(_.dataDir),k(o),$(o);let h=P(o);h||(g({error:"JWT_NOT_FOUND"}),process.exit(1)),H(_.gatewayServerUrl);let I;if(r)I=ks(r);else if(n)try{let E=wf(n,"utf-8");I=ks(E)}catch{}else if(!process.stdin.isTTY){let E=[];for await(let N of process.stdin)E.push(Buffer.from(N));let A=Buffer.concat(E).toString("utf-8").trim();A&&(I=ks(A))}try{let E=await fetch(`${_.gatewayServerUrl}/v1/map-session`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${h}`},body:JSON.stringify({initialLat:p,initialLng:f,initialName:d,city:u,region:m,subPlaces:I})});if(!E.ok){let re=await E.text();g({error:"MAP_SESSION_CREATE_FAILED",status:E.status,message:re}),process.exit(1)}let A=await E.json();if(A.mapUrl&&!A.mapUrl.startsWith("http")&&(A.mapUrl=`${_.webappUrl}${A.mapUrl}`),s&&(A.purpose=s),i){w(A);return}let{sessionId:N,mapUrl:C=""}=A;N||(g({error:"MAP_SESSION_INVALID_RESPONSE",message:"response missing sessionId"}),process.exit(1)),process.stderr.write(`Open this URL to pick a location:
|
|
452
|
+
${C}
|
|
453
|
+
|
|
454
|
+
Waiting (up to 5 minutes)\u2026
|
|
455
|
+
`);let U=await Ns(_,N,{authUrl:C});Al(U,{purpose:s})}catch(E){g({error:"MAP_SESSION_CREATE_FAILED",message:E.message}),process.exit(1)}},Ef=async e=>{let{positional:t,purpose:r}=Sl(e),n=t[0];n||(console.error("error: Usage: map-session-verify <session_id> [--purpose origin|destination]"),process.exit(1));let s=R(),i=await Ns(s,n,{});Al(i,{purpose:r})},Il={"map-session-create":e=>Tl(e.includes("--no-wait")?e:[...e,"--no-wait"]),"map-session":Tl,"map-session-verify":Ef};var Rf=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb phone-verify-start <wallet_address> <phone>"),console.error("hint: phone: E.164 format (e.g. +12125550100)"),process.exit(1));let n=R();b(n.dataDir),k(t),$(t);let s=P(t);O(n.appServerUrl),H(n.gatewayServerUrl);try{let i=await Wa(s,r);i.status==="ALREADY_VERIFIED"&&Rr(t,r),w(i)}catch(i){g({error:i.message}),process.exit(1)}},bf=async e=>{let[t,r,n]=e;(!t||!r||!n)&&(console.error("error: Usage: amb phone-verify-confirm <wallet_address> <phone> <code>"),console.error("hint: phone: E.164 format (e.g. +12125550100). code: 6-digit OTP from SMS"),process.exit(1));let s=R();b(s.dataDir),k(t),$(t);let i=P(t);O(s.appServerUrl),H(s.gatewayServerUrl);try{let o=await Va(i,r,n);o.status==="ok"&&Rr(t,r),w({status:o.status})}catch(o){g({error:o.message}),process.exit(1)}},Tf=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb phone-verify-check <wallet_address> <phone>"),console.error("hint: phone: E.164 format (e.g. +12125550100)"),process.exit(1));let n=R();b(n.dataDir),k(t),$(t);let s=P(t);O(n.appServerUrl),H(n.gatewayServerUrl);try{let i=await Ga(s,r);i.verified&&Rr(t,r),w(i)}catch(i){g({error:i.message}),process.exit(1)}},xl={"phone-verify-start":Rf,"phone-verify-confirm":bf,"phone-verify-check":Tf};import Cs from"fs";import Sf from"path";var Af=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb siwe-request-message <wallet_address> <chain_id>"),console.error("hint: chain_id: 84532 (BASE_SEPOLIA), 11155111 (ETH_SEPOLIA)"),process.exit(1));let n=R();b(n.dataDir),k(t),O(n.appServerUrl);try{let{message:s,expiresAt:i}=await Nr(t,parseInt(r,10));Cs.mkdirSync(n.dataDir,{recursive:!0});let o=`siwe-${t}-exp${i}.json`,a=Sf.join(n.dataDir,o);Cs.writeFileSync(a,JSON.stringify({message:s,walletAddress:t,expiresAt:i},null,2),{mode:384}),w({message:s,siwe_file:a,expires_at:new Date(i*1e3).toISOString()})}catch(s){g({error:s.message}),process.exit(1)}},If=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb siwe-submit <siwe_file> <signature>"),console.error("hint: siwe_file is the file path returned by `amb siwe-request-message`"),console.error(" signature is the wallet_signature from `amb wallet-sign`"),process.exit(1));let n=R();b(n.dataDir),O(n.appServerUrl);try{let{message:s,expiresAt:i}=JSON.parse(Cs.readFileSync(t,"utf8"));Date.now()/1e3>i&&(g({error:"SIWE message expired. Run `amb siwe-request-message` again."}),process.exit(1));let o=await kr(s,r);w({access_token:o.accessToken,expires_in:o.expiresIn})}catch(s){g({error:s.message}),process.exit(1)}},vl={"siwe-request-message":Af,"siwe-submit":If};import*as Ds from"fs";import*as kl from"path";import{io as vf}from"socket.io-client";import xf from"net";function Nl(e,t,r={}){let n=r.connectTimeoutMs??1e3,s=r.responseTimeoutMs??cn;return new Promise(i=>{let o=!1,a=m=>{if(!o){o=!0;try{c.destroy()}catch{}clearTimeout(l),clearTimeout(d),i(m)}},c=xf.connect(e),l=setTimeout(()=>a({ok:!1,reason:"monitor_unreachable"}),n),d=setTimeout(()=>{},0);clearTimeout(d);let u="";c.on("connect",()=>{clearTimeout(l),d=setTimeout(()=>a({ok:!1,reason:"response_timeout"}),s);try{c.write(JSON.stringify({content:t})+`
|
|
456
|
+
`)}catch(m){a({ok:!1,reason:`write_failed: ${m.message}`})}}),c.on("data",m=>{u+=m.toString();let p=u.indexOf(`
|
|
457
|
+
`);if(!(p<0))try{let f=JSON.parse(u.slice(0,p));a(f&&typeof f=="object"&&"ok"in f?f:{ok:!1,reason:"bad_response"})}catch{a({ok:!1,reason:"bad_response"})}}),c.on("error",()=>a({ok:!1,reason:"monitor_unreachable"})),c.on("end",()=>a({ok:!1,reason:"no_response"}))})}var Nf={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp"};function kf(e,t,r,n){let i=new URL(e.gatewayServerUrl).origin,o=vf(`${i}/chat`,{path:"/tada-ton/chat-socket",transports:["websocket"],auth:{token:r}});return new Promise(a=>{let c=!1,l,d;function u(p){c||(c=!0,clearTimeout(l),clearTimeout(d),o.disconnect(),a(p))}function m(p){u({ok:!1,reason:p})}o.on("connect_error",p=>{m(`socket connection failed: ${p.message}`)}),new Promise((p,f)=>{l=setTimeout(()=>f(new Error("join room timeout")),1e4),o.emit("JOIN_ROOM",{rideRequestId:t}),o.once("MESSAGE_SENT",_=>{if(clearTimeout(l),_.event!=="joined"){f(new Error(`unexpected join event: ${_.event}`));return}let h=_.roomId;d=setTimeout(()=>f(new Error("send message timeout")),1e4),o.emit("SEND_MESSAGE",{roomId:h,content:n,type:"TEXT"}),o.once("MESSAGE_SENT",()=>{clearTimeout(d),p()})})}).then(()=>{u({ok:!0})},p=>{m(p.message)})})}var Cf=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb chat-send-message <request_id> <content>"),process.exit(1));let n=R();b(n.dataDir);let s=V(t);if(s||(g({error:"ride request not found",request_id:t}),process.exit(1)),!s.wallet_address){let l;try{({backend:l}=await te(n,s))}catch(d){g({error:"AUTH_FAILED",message:d.message,request_id:t}),process.exit(1)}try{let d=await l.sendChat(t,{message:r});w(d)}catch(d){let u=d instanceof Y?d.code:"CHAT_SEND_FAILED";g({error:u,message:d.message,request_id:t}),process.exit(1)}return}let i=s.wallet_address;k(i),$(i);let o=P(i);o||(g({error:"JWT_NOT_FOUND",message:"Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1)),xe(t);let a=await Nl(ln(F(),t),r);if(a.ok){w({success:!0,via:"monitor"});return}let c=await kf(n,t,o,r);if(c.ok){w({success:!0,via:"fallback"});return}g({error:"CHAT_SEND_FAILED",primary:a.reason,fallback:c.reason}),process.exit(1)},Df=async e=>{let[t,r]=e;(!t||!r)&&(console.error("error: Usage: amb chat-send-image <request_id> <image_path>"),process.exit(1));let n=kl.extname(r).toLowerCase(),s=Nf[n];s||(g({error:"UNSUPPORTED_FORMAT",message:`Unsupported image format: "${n}". Supported: jpeg, jpg, png, gif, webp`}),process.exit(1));let i=R();b(i.dataDir);let o=V(t);if(o||(g({error:"RIDE_NOT_FOUND",message:`No local record for request_id: ${t}`}),process.exit(1)),!o.wallet_address){let d;try{d=Ds.readFileSync(r)}catch(m){g({error:"FILE_READ_FAILED",message:m.message}),process.exit(1)}let u;try{({backend:u}=await te(i,o))}catch(m){g({error:"AUTH_FAILED",message:m.message,request_id:t}),process.exit(1)}try{let m=await u.sendChat(t,{image:{buffer:d,mimetype:s}});w(m)}catch(m){let p=m instanceof Y?m.code:"CHAT_SEND_FAILED";g({error:p,message:m.message,request_id:t}),process.exit(1)}return}let a=o.wallet_address;k(a),$(a),H(i.gatewayServerUrl);let c=P(a),l;try{l=Ds.readFileSync(r)}catch(d){g({error:"FILE_READ_FAILED",message:d.message}),process.exit(1)}try{let d=await Jr(c,t,l,s);w(d)}catch(d){g({error:"CHAT_SEND_IMAGE_FAILED",message:d.message}),process.exit(1)}},Pf=async e=>{let[t,r]=e;t||(console.error("error: Usage: amb chat-get-messages <request_id> [cursor]"),process.exit(1));let n=R();b(n.dataDir);let s=V(t);if(s||(g({error:"ride request not found",request_id:t}),process.exit(1)),!s.wallet_address){let a;try{({backend:a}=await te(n,s))}catch(c){g({error:"AUTH_FAILED",message:c.message,request_id:t}),process.exit(1)}try{w(await a.getMessages(t,r?Number(r):void 0))}catch(c){let l=c instanceof Y?c.code:"CHAT_GET_FAILED";g({error:l,message:c.message,request_id:t}),process.exit(1)}return}let i=s.wallet_address;k(i),$(i),H(n.gatewayServerUrl);let o=P(i);o||(g({error:"JWT_NOT_FOUND",message:"Run `amb siwe-request-message` + `amb siwe-submit` first."}),process.exit(1));try{let a=await Vr(t,o,r);w(a)}catch(a){g({error:"CHAT_GET_FAILED",message:a.message}),process.exit(1)}},Cl={"chat-send-message":Cf,"chat-send-image":Df,"chat-get-messages":Pf};import*as v from"fs";import*as M from"path";import*as hn from"os";import*as Pl from"crypto";import{spawnSync as Lf}from"child_process";import Of from"better-sqlite3";function Mf(e){if(!v.existsSync(e))return{};let t={};for(let r of v.readFileSync(e,"utf8").split(`
|
|
458
|
+
`)){let n=r.trim();if(!n||n.startsWith("#"))continue;let s=n.indexOf("=");s<=0||(t[n.slice(0,s).trim()]=n.slice(s+1).trim())}return t}var Uf;function Dl(e){try{return process.kill(e,0),!0}catch{return!1}}function Ps(e){let t=M.join(e,"run"),r=`${e}.amb-migration-quiesced`;if(v.existsSync(r)){let i=Number.parseInt(v.readFileSync(r,"utf8").trim(),10);if(Number.isSafeInteger(i)&&i>1&&Dl(i))throw new Error("MIGRATION_CONFLICT: legacy quiescence is held");v.unlinkSync(r)}let n=v.openSync(r,"wx",384);v.writeFileSync(n,`${process.pid}
|
|
459
|
+
`),v.closeSync(n);let s;try{if(v.existsSync(t))for(let a of v.readdirSync(t)){if(!/^(?:mon|sup|relay)-.+\.pid$/.test(a))continue;let c=Number.parseInt(v.readFileSync(M.join(t,a),"utf8").trim(),10);if(Number.isSafeInteger(c)&&c>1&&c!==process.pid&&Dl(c))throw new Error(`MIGRATION_CONFLICT: legacy writer pid ${c} is still active`)}let o=[M.join(e,"state","data","tada-ride-agent.db"),M.join(e,"state","data","ambient-ride.db"),M.join(e,"data","tada-ride-agent.db"),M.join(e,"data","ambient-ride.db")].find(a=>v.existsSync(a));o&&(s=new Of(o,{fileMustExist:!0,timeout:0}),s.pragma("wal_checkpoint(TRUNCATE)"),s.exec("BEGIN EXCLUSIVE"))}catch(i){try{s?.close()}catch{}try{v.unlinkSync(r)}catch{}throw i}return{release(){try{s?.inTransaction&&s.exec("ROLLBACK"),s?.close()}catch{}finally{s=void 0}try{v.unlinkSync(r)}catch{}}}}function Bf(e,t){return{legacyRoot:t,ambientRoot:e,stagingRoot:`${e}.migrating`,backupRoot:`${t}.bak`,lockPath:`${e}-migration.lock`}}function Ff(e){let t={},r=[];for(let n of e){if(!v.existsSync(n))continue;let s=JSON.parse(v.readFileSync(n,"utf8")),i=s.env;if(i&&typeof i=="object"&&!Array.isArray(i)){for(let[a,c]of Object.entries(i))if(typeof c=="string"&&/^(?:TADA_AGENT_|TADA_OPENCLAW_)/.test(a)){if(t[a]!==void 0&&t[a]!==c)throw new Error(`MIGRATION_CONFLICT: managed settings disagree for ${a}`);t[a]=c}}let o=s.skills?.entries?.["tada-ride"]?.env;if(o&&typeof o=="object"&&!Array.isArray(o)){for(let[a,c]of Object.entries(o))if(typeof c=="string"&&/^(?:TADA_AGENT_|TADA_OPENCLAW_)/.test(a)){if(t[a]!==void 0&&t[a]!==c)throw new Error(`MIGRATION_CONFLICT: managed settings disagree for ${a}`);t[a]=c}}r.push({path:n,json:s})}return{legacy:t,documents:r}}function $f(e,t,r){let n=[];for(let o of e.documents){let a=o.json.env,c=o.json.skills?.entries?.["tada-ride"]?.env;for(let d of[a,c]){if(!d||typeof d!="object"||Array.isArray(d))continue;let u=d,m=Object.fromEntries(Object.entries(u).filter(([f,_])=>typeof _=="string"&&/^(?:TADA_AGENT_|TADA_OPENCLAW_)/.test(f))),p=He(m,t);for(let f of Object.keys(m))delete u[f];Object.assign(u,p.env)}let l=v.statSync(o.path);n.push({path:o.path,content:`${JSON.stringify(o.json,null,2)}
|
|
460
|
+
`,original:v.readFileSync(o.path),mode:l.mode&511})}let s=r,i=[];try{for(let o of n)s(o.path,o.content,o.mode),i.push(o)}catch(o){for(let a of i.reverse())s(a.path,a.original,a.mode);throw o}}function Hf(e,t,r){let n=`${e}.amb-migration-${process.pid}.tmp`,s;try{s=v.openSync(n,"wx",r),v.writeFileSync(s,t),v.fsyncSync(s),v.closeSync(s),s=void 0,v.renameSync(n,e);let i=v.openSync(M.dirname(e),"r");try{v.fsyncSync(i)}finally{v.closeSync(i)}}catch(i){s!==void 0&&v.closeSync(s);try{v.unlinkSync(n)}catch{}throw i}}function qf(e={}){let t=e.ambientRoot??F(),r=e.legacyRoot??M.join(M.dirname(t),".tada"),n=e.skillDir??M.resolve(M.dirname(new URL(import.meta.url).pathname),".."),s=Kt(n),i=v.existsSync(s)?(e.quiesceLegacy??Ps)(s):void 0,o=M.join(t,"state","data");if(Tt(o)){let C=(e.quiesceLegacy??Ps)(t);try{Fi(o)}finally{C.release()}}let a=Bf(t,r),c=z().networks.paymentNetwork,l=e.managedSettingsPaths??[M.join(hn.homedir(),".claude","settings.json"),M.join(hn.homedir(),".openclaw","openclaw.json")],d=Ff(l),u=e.environmentSnapshot??{...process.env},m;try{or(s,M.join(r,"state")),m=(e.migrate??Mn)({paths:a,oldDataDir:M.join(r,"state","data"),oldKeysDir:M.join(r,"state","keys"),paymentNetwork:c,installManagedCli:e.installManagedCli??(()=>{}),smoke:e.smoke??(()=>{}),quiesceLegacy:()=>(e.quiesceLegacy??Ps)(r),managedLegacyLink:e.managedLegacyLink??M.join(hn.homedir(),".local","bin","tada"),expectedLegacyLinkTarget:e.expectedLegacyLinkTarget??M.join(r,"cli","tada"),transformEnv:(C,U)=>He([{name:"legacy-state",env:C},{name:"managed-settings",env:d.legacy},{name:"process-env",env:u}],U)})}finally{i?.release()}$f(d,c,e.replaceManagedFile??Hf);let p=M.join(t,"state","data"),f=M.join(t,"state","keys"),_=M.join(p,".installed"),h=M.join(p,".env");if(v.existsSync(_)){let C=JSON.parse(v.readFileSync(_,"utf8"));return{status:"already_installed",migration:m,output:{status:"already_installed",installed_at:C.installed_at,skill_dir:n,state_dir:t,data_dir:p,keys_dir:f,migration:m}}}v.mkdirSync(p,{recursive:!0,mode:448}),v.mkdirSync(f,{recursive:!0,mode:448});let I={...He(u,c).env,...He(d.legacy,c).env,...Mf(h)},E=I.AMB_RIDE_PASSPHRASE??Pl.randomBytes(32).toString("hex"),A=I.AMB_RIDE_PASSPHRASE?"preserved from existing .env":"newly generated",N={...I,AMB_RIDE_PASSPHRASE:E};return v.writeFileSync(h,["# Ambient Ride - auto-generated by amb install",`# Generated at: ${new Date().toISOString()}`,`# Source: ${A}`,...Object.entries(N).sort(([C],[U])=>C.localeCompare(U)).map(([C,U])=>`${C}=${U}`),""].join(`
|
|
461
|
+
`),{mode:384}),v.writeFileSync(_,JSON.stringify({installed_at:new Date().toISOString(),skill_dir:n,layout:"self-contained"},null,2),{mode:384}),{status:"installed",migration:m,output:{status:"installed",skill_dir:n,state_dir:t,data_dir:p,keys_dir:f,env_file:h,passphrase_source:A,migration:m,next_steps:["No platform config edits needed \u2014 the skill is self-contained.","Run `amb whoami` to see onboarding state."]}}}var jf=async e=>{process.env.SUDO_UID&&(w({status:"skipped_sudo",message:"Skipped agent state init because this ran under sudo. If `amb install` has already been run as your own user, there is nothing to do; otherwise run it as your own user (on a root-only machine, `sudo env -u SUDO_UID amb install`)."}),process.exit(0));try{let r=qf(Uf??{installManagedCli(){let n=process.argv[1];if(!n||M.basename(n)!=="amb")throw new Error("MIGRATION_INTERRUPTED: Ambient CLI entrypoint is not verified");v.accessSync(n,v.constants.R_OK)},smoke(n){let s=Lf(process.execPath,[process.argv[1],"--json","whoami"],{encoding:"utf8",env:{...process.env,AMB_RIDE_STATE_DIR:n}});if(s.error||s.status!==0)throw new Error("MIGRATION_INTERRUPTED: Ambient runtime smoke failed")}});w(r.output),process.exit(0)}catch(t){let r=String(t),n=r.includes("BACKUP_EXISTS")?"MIGRATION_BACKUP_EXISTS":r.includes("VALIDATION")?"MIGRATION_VALIDATION_FAILED":r.includes("CONFLICT")?"MIGRATION_CONFLICT":"MIGRATION_INTERRUPTED";process.stderr.write(`${JSON.stringify({error:n,message:n})}
|
|
462
|
+
`),process.exit(1)}},Gf=async e=>{let[t]=e;t||(console.error("error: Usage: amb setup-check <wallet_address>"),process.exit(1));let r=R();try{b(r.dataDir)}catch{w({wallet:{registered:!1,address:null,type:null,is_active:!1},jwt:null,phone:null,deposit:null,ready_for_ride:!1}),process.exit(0)}let n=ye(t),s=n?{registered:!0,address:n.wallet_address,type:n.wallet_type}:{registered:!1,address:null,type:null};n||(w({wallet:s,jwt:null,phone:null,deposit:null,ready_for_ride:!1}),process.exit(0));let i=to(n.wallet_address),o=i?{valid:!0,expires_at:i.expires_at}:{valid:!1,expires_at:null};i||(w({wallet:s,jwt:o,phone:null,deposit:null,ready_for_ride:!1}),process.exit(0));let a=Zi(n.wallet_address),c={verified:a.length>0,numbers:a};a.length===0&&(w({wallet:s,jwt:o,phone:c,deposit:null,ready_for_ride:!1}),process.exit(0));let l=null,d=!1;try{l=(await Promise.allSettled(r.depositNetworks.map(async p=>{let[f,_,h]=await Promise.all([Ar(p,n.wallet_address),Sr(p,n.wallet_address),vt(p)]);return{network:p.name,isActive:f,deposited:_.toString(),minRequired:h.toString()}}))).filter(p=>p.status==="fulfilled").map(p=>p.value),d=l.some(p=>p.isActive)}catch{l=null,d=!1}w({wallet:s,jwt:o,phone:c,deposit:l,ready_for_ride:d})},Ll={install:jf,"setup-check":Gf};import zf from"fs";import wn from"fs";import Vf from"path";import Wf from"path";function Ls(e,t){return Wf.join(e,"events",`${t}.jsonl`)}function Os(e,t){let r=fc({rideId:t.rideId,kind:t.kind,status:t.status,content:t.content});if(so(t.rideId,r))return null;let n=oo(t.rideId)+1,s={v:gc,seq:n,eventId:r,rideId:t.rideId,kind:t.kind,terminal:t.terminal,ts:t.ts,payload:t.payload},i=Ls(e,t.rideId);return wn.mkdirSync(Vf.dirname(i),{recursive:!0}),wn.appendFileSync(i,JSON.stringify(s)+`
|
|
463
|
+
`),io(t.rideId,r,n),s}function Ol(e,t,r){let n=Ls(e,t);if(!wn.existsSync(n))return[];let s=[];for(let i of wn.readFileSync(n,"utf8").split(`
|
|
464
|
+
`))if(i)try{let o=JSON.parse(i);o.seq>r&&s.push(o)}catch{process.stderr.write(`[event-log] skipping unparseable line in ${t}.jsonl
|
|
465
|
+
`)}return s}var Jf={WAITPAY:"waiting for payment",PAYCONFIRMED:"payment confirmed, matching a driver",PENDING:"searching for a driver",MATCHED:"a driver candidate was found",CONFIRMED:"the match was confirmed",ASSIGNED:"a driver is assigned and on the way",PICKUP:"the driver is on the way to the pickup point",PICKUP_ARRIVED:"the driver has arrived at the pickup point",INUSE:"the ride is in progress",FINISHED:"the ride completed successfully",NOT_MATCHED:"no driver was found",CANCELED:"the ride was cancelled",ACCIDENT_CANCELED:"the ride was cancelled due to an accident",USER_CANCELED:"cancelled by the rider",USER_CANCELED_BEFORE_CALL:"cancelled by the rider before matching",USER_CANCELED_NO_FREE:"cancelled by the rider (cancellation fee charged)",DRIVER_CANCELED:"cancelled by the driver",DRIVER_CANCELED_RECALLABLE:"cancelled by the driver (can re-request)",COMPANY_CANCELED:"cancelled by the operator",COMPANY_CANCELED_RECALLABLE:"cancelled by the operator (can re-request)",EXPIRED:"the request expired (no driver found)",EXPIRED_RECALLABLE:"the request expired (can re-request)",EXPIRED_BEFORE_PAY:"the payment window expired",ERROR_PAYMENT:"a payment error occurred",ERROR:"an unexpected error occurred"};function Ml(e,t){let r=t?.trim();return r&&r.length>0?r:Jf[e]??e}var Kf={KRW:"\u20A9",USD:"$",SGD:"S$",VND:"\u20AB"};function En(e,t){let r=e.toLocaleString("en-US"),n=Kf[t];return n?`${n}${r}`:`${r} ${t}`}function Yf(e){if(!e)return"";let t=[];e.carPlate&&t.push(`plate ${e.carPlate}`);let r=[e.carColor,e.carModel].filter(Boolean).join(" ").trim();return r&&t.push(r),e.name&&t.push(`driver ${e.name}`),t.length?`, ${t.join(", ")}`:""}function Xf(e){let t=typeof e=="string"?e.toUpperCase():"";return t==="NY"?"Throo":t===""?"TADA/Throo":"TADA"}function Ul(e){let t=Xf(e.payload.region);if(e.kind==="driver_chat"){let a=e.payload.content??"";return`${t} ride: driver sent "${a}". Tell the user briefly; their next short reply may be for the driver. No unrelated questions.`}let r=e.payload.statusMessage,n=String(e.payload.status??""),s=Ml(n,r);if(e.terminal){let a=e.payload.tip,c=`${t} ride ${e.rideId} ${s}. Tell the user the final outcome briefly.`;if(n==="FINISHED"&&(c+=" Ask whether they would like to see their receipt. Fetch it only if they explicitly ask."),a){let l=a.maxAmount?`from ${En(a.minAmount,a.currency)} up to ${En(a.maxAmount,a.currency)}`:`from ${En(a.minAmount,a.currency)}`;c+=` Tips ${l} are welcome. You may offer the tip together with that receipt question.`}return c}let i=e.payload.etaMin,o=Yf(e.payload.driver);return`${t} ride ${e.rideId} is ${s}${o}${i?`, pickup ~${i}min`:""}. Tell the user briefly. No unrelated questions.`}function Qf(e){try{return parseInt(zf.readFileSync(e,"utf8").trim(),10)||0}catch{return 0}}var Zf=e=>new Promise(t=>setTimeout(t,e));async function e_(e){let t=e.write??(n=>process.stdout.write(n+`
|
|
466
|
+
`)),r=e.pollMs??2e3;for(;;){let n=Qf(e.cursorFile),s=Ol(e.root,e.rideId,n);for(let i of s){let o=e.prompt?{...i,prompt:Ul(i)}:i;if(t(JSON.stringify(o)),i.terminal)return 0}if(!e.follow)return 0;await Zf(r)}}var t_=async e=>{let t=o=>{let a=e.indexOf(o);return a>=0?e[a+1]:void 0},r=t("--ride"),n=t("--cursor-file");(!r||!n)&&(console.error(JSON.stringify({error:"USAGE",message:"amb events --ride <id> --cursor-file <f> [--follow] [--prompt]"})),process.exit(1));let s=R();b(s.dataDir),await xe(r);let i=await e_({root:F(),rideId:r,cursorFile:n,follow:e.includes("--follow"),prompt:e.includes("--prompt")});process.exit(i)},Bl={events:t_};import Bs from"fs";import Fs from"path";import{spawn as o_}from"child_process";var Fl=[2e3,5e3,15e3,3e4];function $l(e){if(e.exitCode===0)return{action:"stop",code:0};if(e.exitCode===1)return{action:"stop",code:1};let t=Math.min(e.restartCount,Fl.length-1);return{action:"restart",delayMs:Fl[t]}}import r_ from"net";import Ms from"fs";import n_ from"path";function Hl(e){let t=e.trace??(()=>{});try{Ms.mkdirSync(n_.dirname(e.sockPath),{recursive:!0})}catch{}try{Ms.unlinkSync(e.sockPath)}catch{}let r=r_.createServer(n=>{let s="";n.on("data",i=>{s+=i.toString();let o=s.indexOf(`
|
|
467
|
+
`);if(o<0)return;let a=s.slice(0,o);s="",(async()=>{let c;try{let l=JSON.parse(a);typeof l?.content!="string"?c={ok:!1,reason:"bad_request"}:(t("chat_control_req",{len:l.content.length}),c=await e.send(l.content))}catch(l){c={ok:!1,reason:`bad_request: ${l.message}`}}try{n.write(JSON.stringify(c)+`
|
|
468
|
+
`)}catch{}n.end()})()}),n.on("error",()=>{})});return r.on("error",n=>t("chat_control_server_error",{error:String(n)})),r.listen(e.sockPath,()=>t("chat_control_listen",{sockPath:e.sockPath})),{close(){try{r.close()}catch{}try{Ms.unlinkSync(e.sockPath)}catch{}}}}var s_=5e3,ql=3e4,jl=3,Gl=Symbol("timed_out");function i_(e){let t,r=new Promise(n=>{t=setTimeout(()=>n(Gl),s_)});return Promise.race([e,r]).finally(()=>clearTimeout(t))}function Wl(e){let t=e.trace??(()=>{}),r=e.now??Date.now,n=null,s=null,i=null,o=0,a=null,c=(l,d)=>{l!==null&&l!==s||(o+=1,a=r(),s=null,i=null,t("share_fetch_failed",{attempt:o,reason:d}),o>=jl&&t("share_fetch_gave_up"))};return async l=>{if(e.mode!=="tada"||l.rideShareUrl||!l.driverInfo)return l;if(Ie(l.status))return n?{...l,rideShareUrl:n}:l;if(n)return{...l,rideShareUrl:n};if(s&&i!==null&&r()-i>=ql)return c(s,"abandoned"),t("share_fetch_abandoned",{status:l.status}),l;if(!s){if(o>=jl||a!==null&&r()-a<ql)return l;let m=e.backend.getShareLink(e.rideId).then(p=>p.url);m.then(p=>{n=p,s=null,i=null},()=>{}),s=m,i=r()}let d=s,u;try{u=await i_(d)}catch(m){return c(d,String(m.message??m)),l}return u===Gl?(t("share_fetch_slow",{status:l.status}),l):(n=u,s=null,i=null,{...l,rideShareUrl:n})}}async function a_(e){let t=e.trace??(()=>{}),r=await e.fetchStatus();if(t("poll_result",{status:r.status,hasDriverInfo:!!r.driverInfo,changed:r.status!==e.lastStatusRef.status,raw:r}),r.status===e.lastStatusRef.status)return{terminal:!1};e.lastStatusRef.status=r.status;let n=Ie(r.status),s={status:r.status};if(e.region&&(s.region=e.region),r.driverInfo&&(s.driver=r.driverInfo),r.driverInfo&&r.rideShareUrl&&(s.rideShareUrl=r.rideShareUrl),r.rideId&&(s.upstreamRideId=r.rideId),r.statusMessage&&r.statusMessage.trim()&&(s.statusMessage=r.statusMessage),n&&r.status==="FINISHED"&&e.tipLookup)try{let i=await e.tipLookup();i&&(s.tip=i)}catch{}return Os(e.root,{rideId:e.rideId,kind:"ride_status",status:r.status,terminal:n,ts:new Date().toISOString(),payload:s}),Ve(e.rideId,r.status),t("event_appended",{status:r.status,terminal:n}),{terminal:n}}function c_(e,t,r,n){let s;return e.on("ready",()=>{if(!s)try{s=Hl({sockPath:ln(r,t),send:i=>e.send(i),trace:n}),n("chat_control_attached",{rideId:t})}catch(i){n("chat_control_attach_failed",{error:String(i)})}}),{close(){try{s?.close()}catch{}}}}function l_(e){return Fs.join(F(),"run",`monitor-trace-${e}.log`)}function Jl(e){let t=l_(e);return(r,n)=>{try{Bs.mkdirSync(Fs.dirname(t),{recursive:!0}),Bs.appendFileSync(t,JSON.stringify({ts:new Date().toISOString(),pid:process.pid,event:r,rideId:e,...n??{}})+`
|
|
469
|
+
`)}catch{}}}var Us=5e3,Vl=e=>new Promise(t=>setTimeout(t,e));function d_(e,t){let r=(async()=>{try{let n=await e.getTipConfig(t);return n.enabled?{minAmount:n.minAmount,currency:n.currency,...n.maxAmount!==void 0?{maxAmount:n.maxAmount}:{}}:null}catch{return null}})();return async()=>{let n=new Promise(s=>setTimeout(()=>s(null),5e3));return await Promise.race([r,n])}}var u_=async e=>{let t=e[0];t||(console.error(JSON.stringify({error:"USAGE",message:"amb monitor <request_id>"})),process.exit(1));let r=Jl(t),n=R();b(n.dataDir);let s=V(t);if(!s){r("exit",{reason:"ride_not_found"}),console.error(JSON.stringify({error:"RIDE_NOT_FOUND",message:`No local record for request_id: ${t}`})),process.exit(1);return}let i;try{i=await te(n,s)}catch(E){r("backend_resolve_failed",{msg:E.message}),console.error(JSON.stringify({error:"BACKEND_UNAVAILABLE",message:E.message})),process.exit(1);return}let{backend:o,mode:a}=i;r("startup",{rideId:t,mode:a,gatewayServerUrl:n.gatewayServerUrl,dataDir:n.dataDir,pollIntervalMs:Us});let c=d_(o,s.region),l=Wl({backend:o,mode:a,rideId:t,trace:r}),d="mon-"+t;if(!fs(F(),d)){r("already_running",{lockName:d}),process.exit(0);return}r("lock_acquired",{lockName:d});let u=null,m,p={status:s.status??""},f=0,_=!1,h=E=>{if(!_){_=!0;try{u?.dispose()}catch{}m?.close(),on(F(),d),r("lock_released",{reason:E})}},I=(E,A)=>{throw h(A),r("exit",{code:E,reason:A}),process.exit(E),new Error("unreachable")};process.on("SIGTERM",()=>{r("signal",{signal:"SIGTERM"}),I(2,"SIGTERM")}),process.on("SIGINT",()=>{r("signal",{signal:"SIGINT"}),I(2,"SIGINT")}),process.on("uncaughtException",E=>{r("crash",{source:"uncaughtException",error:String(E),stack:E?.stack}),h("uncaughtException"),r("exit",{code:1,reason:"uncaughtException"}),process.exit(1)}),process.on("unhandledRejection",E=>{let A=E instanceof Error?E:new Error(String(E));r("crash",{source:"unhandledRejection",error:String(A),stack:A.stack}),h("unhandledRejection"),r("exit",{code:1,reason:"unhandledRejection"}),process.exit(1)});try{for(;;){f+=1,r("poll_begin",{iteration:f,lastStatus:p.status}),a==="wallet"&&(P(s.wallet_address)||(process.stderr.write(JSON.stringify({error:"JWT_EXPIRED"})+`
|
|
470
|
+
`),r("jwt_missing",{iteration:f}),I(1,"jwt_missing")));let E;try{E=await p_({backend:o,rideId:t,lastStatusRef:p,trace:r,tipLookup:c,region:s.region,shareEnricher:l})}catch(A){let N=A.message;N.includes("401")&&(process.stderr.write(JSON.stringify({error:"JWT_EXPIRED",message:N})+`
|
|
471
|
+
`),r("jwt_expired",{iteration:f,message:N}),I(1,"jwt_expired")),process.stderr.write(`poll_error: ${N}
|
|
472
|
+
`),r("poll_error",{iteration:f,message:N}),await Vl(Us);continue}E.driverInfo&&!u&&(r("chat_client_start",{rideId:t}),u=o.subscribeChat(t),u.on("message",A=>{let N=A.data,C=N?.content??"",U=N?.senderType,re=U==="DRIVER"?"driver":U==="SYSTEM"?"system":"unknown";r("chat_msg_raw",{raw:N,senderType:U,sender:re}),Os(F(),{rideId:t,kind:"driver_chat",terminal:!1,ts:new Date().toISOString(),content:C,payload:{content:C,sender:re,region:s.region}})}),u.on("error",A=>{r("chat_error",{message:A.message})}),u.connect(),m=c_(u,t,F(),r)),Ie(E.status)&&(r("terminal",{status:E.status}),I(0,"terminal")),await Vl(Us)}}catch(E){if(_)throw E;let A=E;r("crash",{source:"poll_loop",error:String(A),stack:A?.stack}),h("crash"),r("exit",{code:1,reason:"crash"}),process.exit(1)}};async function p_(e){let t;if(await a_({root:F(),rideId:e.rideId,region:e.region,fetchStatus:async()=>{let r=await e.backend.getRideStatus(e.rideId),n=e.shareEnricher?await e.shareEnricher(r):r;return t=n,n},lastStatusRef:e.lastStatusRef,trace:e.trace,tipLookup:e.tipLookup??void 0}),!t)throw new Error("fetchStatus did not run");return t}function m_(){let e=process.argv[1]??"",t=e?Fs.resolve(e):"";if(!t||!Bs.existsSync(t))throw new Error(`amb entry not found (argv[1]=${process.argv[1]}, resolved=${t})`);return t}function g_(e){let t=Jl(e);return(r,n)=>t(r,{role:"supervisor",...n??{}})}var f_=async e=>{let t=e[0];if(!t){console.error(JSON.stringify({error:"USAGE",message:"amb monitor-supervisor <request_id>"})),process.exit(1);return}let r=g_(t);r("sup_start",{rideId:t,pid:process.pid});let n="sup-"+t;if(!fs(F(),n)){r("sup_already_running",{lockName:n}),process.exit(0);return}r("sup_lock_acquired",{lockName:n});let s;try{s=m_(),r("sup_entry_resolved",{ambEntry:s})}catch(p){r("sup_entry_unresolved",{error:String(p)}),on(F(),n),r("sup_stop",{code:2,reason:"entry_unresolved"}),process.exit(2);return}let i,o=!1,a=!1,c=p=>{a||(a=!0,on(F(),n),r("sup_lock_released",{reason:p}))},l=()=>{if(i&&i.exitCode===null&&i.signalCode===null)try{i.kill("SIGTERM")}catch{}},d=async p=>{if(o)return;o=!0,r("sup_signal",{signal:p});let f=!!(i&&i.exitCode===null&&i.signalCode===null);l(),f&&await new Promise(_=>{let h=!1,I=()=>{h||(h=!0,_())};i.once("exit",I),setTimeout(I,25e3).unref()}),c(p),r("sup_stop",{code:2,reason:p}),process.exit(2)};process.on("SIGTERM",()=>{d("SIGTERM")}),process.on("SIGINT",()=>{d("SIGINT")});let u=(p,f)=>{if(o)return;o=!0;let _=f instanceof Error?f:new Error(String(f));r("sup_crash",{source:p,error:String(_),stack:_.stack}),l(),c("crash"),r("sup_stop",{code:1,reason:p}),process.exit(1)};process.on("uncaughtException",p=>u("uncaughtException",p)),process.on("unhandledRejection",p=>u("unhandledRejection",p));let m=0;await new Promise(()=>{let p=()=>{i=o_(process.execPath,[s,"monitor",t],{detached:!1,stdio:"ignore"}),r("sup_spawn",{childPid:i.pid,attempt:m}),i.on("error",f=>{o||(r("sup_child_error",{error:f.message}),c("child_spawn_error"),r("sup_stop",{code:2,reason:"child_spawn_error"}),process.exit(2))}),i.on("exit",(f,_)=>{if(o)return;r("sup_child_exit",{exitCode:f,signal:_,restartCount:m});let h=$l({exitCode:f,signal:_,restartCount:m});if(r("sup_decision",{...h}),h.action==="stop"){c("terminal"),r("sup_stop",{code:h.code}),process.exit(h.code);return}m++,r("sup_restart",{delayMs:h.delayMs,restartCount:m}),setTimeout(p,h.delayMs)})};p()})},Kl={monitor:u_,"monitor-supervisor":f_};function __(e){let t=P(e,"member")!==null,r=wr(e)!==null;return t||r?"active":"needs_login"}var y_="Your TADA/Throo session isn't active \u2014 run 'amb login' to sign in again.",h_=async e=>{let t=K(),r=bt();if(t.mode===null){w({mode:null,reason:"onboarding",identity:null,member_available:r,hint:"No credentials registered. Run 'amb install' to set up, then 'amb login' (TADA/Throo member) or 'amb wallet-setup' (crypto)."});return}if(t.mode==="tada"&&t.member){let i=__(t.member.id),o=i==="active",a={mode:"tada",reason:t.reason,identity:{kind:"tada",member_id:t.member.id,phone:t.member.phone,display_name:t.member.display_name,region:t.member.region},member_available:r,authenticated:o,auth_state:i,...o?{}:{hint:y_}};w(a);return}if(t.mode==="wallet"&&t.walletAddress){let i={mode:"wallet",reason:t.reason,identity:{kind:"wallet",wallet_address:t.walletAddress},member_available:r};w(i);return}let n=t.mode==="tada"?"Run 'amb login' first.":"Run 'amb wallet-setup' first.",s={mode:t.mode,reason:t.reason,identity:null,member_available:r,hint:`--account=${t.mode} was forced but no ${t.mode==="tada"?"TADA/Throo member":"wallet"} credentials are registered. ${n}`};w(s)},Yl={whoami:h_};import w_ from"os";import Xl from"path";import E_ from"fs";import zl from"qrcode";async function $s(e){return zl.toString(e,{type:"terminal",small:!0,errorCorrectionLevel:"L"})}async function Ql(e){let t=E_.mkdtempSync(Xl.join(w_.tmpdir(),"tada-login-")),r=Xl.join(t,"qr.png");return await zl.toFile(r,e,{type:"png",margin:4,width:320}),r}import b_ from"readline/promises";async function T_(){let e=b_.createInterface({input:process.stdin,output:process.stdout});try{return(await e.question("Enter the 4-digit code shown in the TADA/Throo app: ")).trim()}finally{e.close()}}async function S_(){if(!bt()){w({status:"unavailable",message:"Member login is not available in this build (missing RIDER_CLI_APP credentials or gateway host)."});return}let e=R();b(e.dataDir);let t={clientId:e.riderCliApp.clientId,clientSecret:e.riderCliApp.clientSecret};Ue(ee(e.cliGatewayHosts,e.cliGatewayHost));try{let r=await Es(t),n=Date.parse(r.expiresAt),s=Number.isFinite(n)?Math.floor(n/1e3):Math.floor(Date.now()/1e3)+600;Vi(),Gi(r.sessionToken,s);let i={status:"auth_required",approval_url:r.approvalUrl,expires_at:r.expiresAt};try{let o=await $s(r.approvalUrl);process.stderr.write(`Scan this QR to approve the login in the TADA/Throo app:
|
|
473
|
+
${o}
|
|
474
|
+
`),i.qr_image_path=await Ql(r.approvalUrl)}catch{}w(i)}catch{w({status:"error",message:"Failed to start login session."})}}var A_=async e=>{if(e.includes("--no-wait"))return S_();let r=R();if(!Oi(r)){console.log("Member login is not available in this build (missing RIDER_CLI_APP credentials).");return}b(r.dataDir);let n={clientId:r.riderCliApp.clientId,clientSecret:r.riderCliApp.clientSecret};Ue(ee(r.cliGatewayHosts,r.cliGatewayHost));let s=await Es(n),i="";try{i=`
|
|
475
|
+
${await $s(s.approvalUrl)}`}catch{}console.log(`Approve this login in the TADA/Throo app:${i} ${s.approvalUrl}
|
|
476
|
+
Then enter the 4-digit code the app shows.`);let o=e.indexOf("--code"),a=o>=0?e[o+1]:void 0,c=!1;for(let l=1;l<=5;l++){let d=a??await T_();try{let u=await Rs(n,s.sessionToken,d);console.log(`Logged in as TADA/Throo member ${u.memberId}.`);return}catch(u){if(u instanceof Y&&u.code==="invalid_user_code"){if(c=!0,console.log(`Code did not match (${l}/5)${l===4?" \u2014 last attempt!":""}.`),a!==void 0)return;continue}if(u instanceof Y&&u.code==="invalid_grant"){console.log(c?"Too many incorrect codes \u2014 the login session was destroyed. Please run `amb login` again.":"This login session is no longer valid (it may have expired, been denied, or already been used). Please run `amb login` again.");return}console.log("Login failed. Please run `amb login` again.");return}}console.log("Too many incorrect codes \u2014 the login session was destroyed. Please run `amb login` again.")},I_=async()=>{let e=R();b(e.dataDir),console.log(Dc()?"Logged out of TADA/Throo member account.":"No active TADA/Throo member session.")},x_=async e=>{let t=e.indexOf("--code"),r=t>=0?e[t+1]:void 0;if(r||(console.error("error: Usage: amb login-verify --code <4-digit-code>"),process.exit(1)),!bt()){w({status:"unavailable",message:"Member login is not available in this build (missing RIDER_CLI_APP credentials or gateway host)."});return}let n=R();b(n.dataDir);let s={clientId:n.riderCliApp.clientId,clientSecret:n.riderCliApp.clientSecret};Ue(ee(n.cliGatewayHosts,n.cliGatewayHost));let i=Wi(),o=Math.floor(Date.now()/1e3);if(!i||i.expires_at<=o){i&&hr(i.session_token),w({status:"session_expired",message:"No active login session. Run `amb login --no-wait` to start over."});return}try{let a=await Rs(s,i.session_token,r);hr(i.session_token),w({status:"logged_in",member_id:a.memberId})}catch(a){if(a instanceof Y&&a.code==="invalid_user_code"){w({status:"invalid_code",message:"The 4-digit code did not match. Re-read the code in the TADA/Throo app and run `amb login-verify --code <code>` again."});return}if(a instanceof Y&&(a.code==="invalid_grant"||a.code==="agent_session_not_found"||a.code==="agent_session_already_decided"||a.code==="agent_session_expired")){hr(i.session_token),w({status:"session_expired",message:"The login session is no longer valid (expired, denied, or too many wrong codes). Run `amb login --no-wait` to start over."});return}w({status:"error",message:"Login failed. Run `amb login --no-wait` to start over."})}},Zl={login:A_,logout:I_,"login-verify":x_};import yt from"fs";import ht from"path";var v_="proprietary (see LICENSE)";function Rn(){let{cliVersion:e,gitSha:t}=z().artifact;return{version:e,git_sha:t,license:v_}}function ed(e){return`version: ${e.version}
|
|
477
|
+
git_sha: ${e.git_sha}
|
|
478
|
+
license: ${e.license}`}import N_ from"fs";import Ee from"path";function td(e,t){return[{arcname:`events-${e}.jsonl`,rel:Ee.join("events",`${e}.jsonl`)},{arcname:`monitor-trace-${e}.log`,rel:Ee.join("run",`monitor-trace-${e}.log`)},{arcname:`relay-error-${e}.log`,rel:Ee.join("run",`relay-error-${e}.log`)},{arcname:`relay-trace-${e}.jsonl`,rel:Ee.join("run",`relay-trace-${e}.jsonl`)},{arcname:`mon-${e}.pid`,rel:Ee.join("run",`mon-${e}.pid`)},{arcname:`sup-${e}.pid`,rel:Ee.join("run",`sup-${e}.pid`)},{arcname:`relay-${e}.pid`,rel:Ee.join("run",`relay-${e}.pid`)},{arcname:`cursor-openclaw-${e}`,rel:Ee.join("cursors",`openclaw-${e}`)},{arcname:`cursor-stdout-${e}`,rel:Ee.join("cursors",`stdout-${e}`)}].map(({arcname:n,rel:s})=>{let i=Ee.join(t,s);return{arcname:n,absPath:i,present:N_.existsSync(i)}})}function rd(e){let t=e.artifacts.filter(a=>a.present).map(a=>a.arcname),r=e.artifacts.filter(a=>!a.present).map(a=>a.arcname),n={ride_id:e.rideId,created_at:e.createdAt,version:e.version,ride_row:e.rideRow,liveness:e.liveness,collected:t,missing:r},s=e.rideRow===null?["_No local DB record for this request_id \u2014 confirm it is the id from `amb ride request` output (the local request_id, not the upstream ride id)._"]:["```json",JSON.stringify(e.rideRow,null,2),"```"],i=a=>a.length?a.map(c=>`- ${c}`):["- (none)"];return{markdown:[`# amb debug \u2014 ${e.rideId}`,"",`- created_at: ${e.createdAt}`,`- version: ${e.version.version} / git_sha ${e.version.git_sha} / branch ${e.version.branch} / install ${e.version.install_mode}`,"","## ride_requests row","",...s,"","## liveness (locks held-alive at collection time)","",`- monitor (mon-): ${e.liveness.monitor}`,`- supervisor (sup-): ${e.liveness.supervisor}`,`- relay (relay-): ${e.liveness.relay}`,"> The relay lock is held only by the OpenClaw delivery flavor; the stdout flavor (Claude Code / Hermes / codex) runs without it, so `relay: false` there is normal, not a failure.","","## collected","",...i(t),"","## missing","",...i(r),""].join(`
|
|
479
|
+
`),manifest:n}}import{spawn as k_}from"child_process";import C_ from"path";var Gt=class extends Error{constructor(){super("zip binary not found on PATH"),this.name="ZipUnavailableError"}},Wt=class extends Error{constructor(t){super(`zip exited ${t}`),this.name="ZipFailedError"}},D_=(e,t,r)=>new Promise((n,s)=>{let i=k_(e,t,{cwd:r,stdio:"ignore"});i.on("error",s),i.on("exit",o=>n(o??1))});async function nd(e){if(!C_.isAbsolute(e.outZipPath))throw new Error("outZipPath must be absolute");let t=e.run??D_,r;try{r=await t("zip",["-r","-q",e.outZipPath,"."],e.stagingDir)}catch(n){throw n.code==="ENOENT"?new Gt:n}if(r!==0)throw new Wt(r)}function sd(e){let t=r=>String(r).padStart(2,"0");return`${e.getFullYear()}${t(e.getMonth()+1)}${t(e.getDate())}-${t(e.getHours())}${t(e.getMinutes())}${t(e.getSeconds())}`}var P_=async e=>{let t=e[0];if(!t||t.startsWith("--")){g({error:"USAGE",message:"amb debug <request_id>"}),process.exit(1);return}if(/[/\\]/.test(t)||t.includes("..")||t.includes("\0")){g({error:"INVALID_RIDE_ID",message:'rideId must not contain path separators or ".."'}),process.exit(1);return}b(et());let r=F(),n=new Date,s=ht.join(r,"debug"),i=ht.join(s,`.staging-${sd(n)}-${process.pid}`);yt.mkdirSync(i,{recursive:!0});let o=ht.join(s,`tada-debug-${t.slice(0,8)}-${sd(n)}.zip`);try{let a=td(t,r);for(let u of a)if(u.present)try{yt.copyFileSync(u.absPath,ht.join(i,u.arcname))}catch{u.present=!1}let c=Rn(),l=z(),d=rd({rideId:t,createdAt:n.toISOString(),version:{version:c.version,git_sha:c.git_sha,branch:l.install.repoBranch,install_mode:l.install.mode},rideRow:V(t)??null,liveness:{monitor:mt(r,`mon-${t}`),supervisor:mt(r,`sup-${t}`),relay:mt(r,`relay-${t}`)},artifacts:a.map(u=>({arcname:u.arcname,present:u.present}))});yt.writeFileSync(ht.join(i,"summary.md"),d.markdown),yt.writeFileSync(ht.join(i,"manifest.json"),JSON.stringify(d.manifest,null,2)),await nd({stagingDir:i,outZipPath:o}),yt.rmSync(i,{recursive:!0,force:!0}),w({ok:!0,zipPath:o,rideId:t,collected:a.filter(u=>u.present).map(u=>u.arcname),missing:a.filter(u=>!u.present).map(u=>u.arcname)})}catch(a){if(yt.rmSync(i,{recursive:!0,force:!0}),a instanceof Gt){g({error:"ZIP_UNAVAILABLE",message:"zip binary not found on PATH"}),process.exit(1);return}if(a instanceof Wt){g({error:"ZIP_FAILED",message:a.message}),process.exit(1);return}g({error:"DEBUG_FAILED",message:a.message}),process.exit(1);return}},id={debug:P_};function od(){let e=z().install.repoBranch;return e==="dev"||e==="staging"}var ad={...$o,...ta,...qa,...al,...dl,...ul,...bl,...Il,...xl,...vl,...Cl,...Ll,...Bl,...Kl,...Yl,...Zl,...od()?id:{}},L_=new Set(["wallet-add-external"]);function cd(e){return ad[e]}function Hs(){return Object.keys(ad).filter(e=>!L_.has(e)).sort()}var{json:ld,argv:O_}=Ks(process.argv.slice(2));Js(ld?"json":"plain");var qs;try{let e=wc(O_);yc(e.account),qs=e.argv}catch(e){g({error:"USAGE",message:e.message}),process.exit(1)}var ve=qs[0];if(ve==="--version"||ve==="-v"){let e=Rn();console.log(ld?JSON.stringify(e):ed(e)),process.exit(0)}(!ve||ve==="--help"||ve==="-h")&&(g({error:"USAGE",message:"amb <command> [args]",commands:Hs()}),process.exit(ve?0:1));var dd=cd(ve);dd||(g({error:"UNKNOWN_COMMAND",message:`Unknown command: ${ve}`,commands:Hs()}),process.exit(1));var bn=Ec(ve);if(bn){let e=K();e.mode&&e.mode!==bn&&(g({error:"MODE_MISMATCH",message:Rc(bn,e.mode),required_mode:bn,active_mode:e.mode,active_reason:e.reason}),process.exit(1))}try{await dd(qs.slice(1))}catch(e){g({error:"CLI_ERROR",message:e.message??String(e)}),process.exit(1)}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ambprotocol/ride-cli",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"amb": "amb"
|
|
7
|
+
},
|
|
8
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18.0.0"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"postinstall": "node -e \"try{require('child_process').execFileSync(process.execPath,['./amb','install'],{stdio:'inherit'})}catch(e){console.error('AMB_INSTALL_FAILED: state init skipped during postinstall - run: amb install ('+(e&&e.message||e)+')')}\""
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@noble/curves": "^1.x",
|
|
17
|
+
"@noble/hashes": "^1.x",
|
|
18
|
+
"@privy-io/node": "^0.10.1",
|
|
19
|
+
"better-sqlite3": "^12.x",
|
|
20
|
+
"qrcode": "^1.5.4",
|
|
21
|
+
"socket.io-client": "^4.8.3",
|
|
22
|
+
"viem": "^2.x"
|
|
23
|
+
}
|
|
24
|
+
}
|