@@ -48,15 +48,37 @@ export type ResolvedAuth =
4848// ---------------------------------------------------------------------------
4949
5050const DEFAULT_TIMEOUT_MS = 15_000 ;
51+ const MAX_ERROR_MESSAGE_LENGTH = 300 ;
52+ const SERVICE_ACCOUNT_TOKEN_RE = / o p s _ [ A - Z a - z 0 - 9 _ - ] + / g;
5153type OnePasswordSdkModule = typeof import ( "@1password/sdk" ) ;
5254
55+ const formatCause = ( cause : unknown ) : string => {
56+ // oxlint-disable-next-line executor/no-unknown-error-message -- boundary: normalizing untyped op-js/SDK throwables into OnePasswordError.message
57+ const maybeMessage = ( cause as { readonly message ?: unknown } | null | undefined ) ?. message ;
58+ const raw =
59+ // oxlint-disable-next-line executor/no-unknown-error-message -- boundary: last-resort stringification of a non-Error throwable
60+ typeof maybeMessage === "string" && maybeMessage . length > 0 ? maybeMessage : String ( cause ) ;
61+ return raw
62+ . replace ( SERVICE_ACCOUNT_TOKEN_RE , "[redacted 1Password token]" )
63+ . replace ( / \s + / g, " " )
64+ . trim ( ) ;
65+ } ;
66+
67+ const messageWithCause = ( prefix : string , cause : unknown ) : string => {
68+ const causeMessage = formatCause ( cause ) ;
69+ const message = causeMessage ? `${ prefix } : ${ causeMessage } ` : prefix ;
70+ return message . length > MAX_ERROR_MESSAGE_LENGTH
71+ ? `${ message . slice ( 0 , MAX_ERROR_MESSAGE_LENGTH - 3 ) } ...`
72+ : message ;
73+ } ;
74+
5375const loadOnePasswordSdk = ( ) : Effect . Effect < OnePasswordSdkModule , OnePasswordError > =>
5476 Effect . tryPromise ( {
5577 try : ( ) => import ( "@1password/sdk" ) ,
56- catch : ( ) =>
78+ catch : ( cause ) =>
5779 new OnePasswordError ( {
5880 operation : "sdk module load" ,
59- message : "Failed to load 1Password SDK" ,
81+ message : messageWithCause ( "Failed to load 1Password SDK" , cause ) ,
6082 } ) ,
6183 } ) ;
6284
@@ -99,20 +121,20 @@ export const makeNativeSdkService = (
99121 integrationName : "Executor" ,
100122 integrationVersion : "0.0.0" ,
101123 } ) ,
102- catch : ( ) =>
124+ catch : ( cause ) =>
103125 new OnePasswordError ( {
104126 operation : "client setup" ,
105- message : "Failed to set up 1Password client" ,
127+ message : messageWithCause ( "Failed to set up 1Password client" , cause ) ,
106128 } ) ,
107129 } ) . pipe ( timeoutWithOnePasswordError ( "client setup" , timeoutMs ) ) ;
108130
109131 const wrap = < A > ( fn : ( ) => Promise < A > , operation : string ) : Effect . Effect < A , OnePasswordError > =>
110132 Effect . tryPromise ( {
111133 try : fn ,
112- catch : ( ) =>
134+ catch : ( cause ) =>
113135 new OnePasswordError ( {
114136 operation,
115- message : `1Password SDK ${ operation } failed` ,
137+ message : messageWithCause ( `1Password SDK ${ operation } failed` , cause ) ,
116138 } ) ,
117139 } ) . pipe (
118140 timeoutWithOnePasswordError ( operation , timeoutMs ) ,
@@ -158,10 +180,10 @@ export const makeCliService = (
158180 }
159181 return fn ( ) ;
160182 } ,
161- catch : ( ) =>
183+ catch : ( cause ) =>
162184 new OnePasswordError ( {
163185 operation,
164- message : `1Password CLI ${ operation } failed` ,
186+ message : messageWithCause ( `1Password CLI ${ operation } failed` , cause ) ,
165187 } ) ,
166188 } ) ,
167189 )
@@ -186,6 +208,24 @@ export const makeCliService = (
186208// Smart factory — tries CLI first (avoids IPC hang), falls back to SDK
187209// ---------------------------------------------------------------------------
188210
211+ const isCliUnavailable = ( error : OnePasswordError ) : boolean => {
212+ // oxlint-disable-next-line executor/no-unknown-error-message -- boundary: OnePasswordError carries a typed `message`
213+ const message = error . message . toLowerCase ( ) ;
214+ return (
215+ message . includes ( "enoent" ) ||
216+ message . includes ( "not found" ) ||
217+ message . includes ( "command not found" ) ||
218+ message . includes ( "not installed" ) ||
219+ message . includes ( "no such file" ) ||
220+ message . includes ( "spawn op" )
221+ ) ;
222+ } ;
223+
224+ const chooseFallbackError = (
225+ cliError : OnePasswordError ,
226+ sdkError : OnePasswordError ,
227+ ) : OnePasswordError => ( isCliUnavailable ( cliError ) ? sdkError : cliError ) ;
228+
189229export const makeOnePasswordService = (
190230 auth : ResolvedAuth ,
191231 options ?: { readonly preferSdk ?: boolean ; readonly timeoutMs ?: number } ,
@@ -196,11 +236,33 @@ export const makeOnePasswordService = (
196236 return makeNativeSdkService ( auth , timeoutMs ) ;
197237 }
198238
199- // Default: prefer CLI to avoid the IPC hang bug
200- return makeCliService ( auth ) . pipe (
201- Effect . catch ( ( cliError : OnePasswordError ) =>
202- // CLI unavailable (e.g. `op` not installed) — fall back to SDK
203- makeNativeSdkService ( auth , timeoutMs ) . pipe ( Effect . mapError ( ( ) => cliError ) ) ,
204- ) ,
205- ) ;
239+ return Effect . gen ( function * ( ) {
240+ const cliService = yield * makeCliService ( auth ) ;
241+ const sdkService = yield * Effect . cached ( makeNativeSdkService ( auth , timeoutMs ) ) ;
242+
243+ const withSdkFallback = < A > (
244+ cliEffect : Effect . Effect < A , OnePasswordError > ,
245+ sdkEffect : ( service : OnePasswordService ) => Effect . Effect < A , OnePasswordError > ,
246+ ) : Effect . Effect < A , OnePasswordError > =>
247+ cliEffect . pipe (
248+ Effect . catch ( ( cliError : OnePasswordError ) =>
249+ sdkService . pipe (
250+ Effect . flatMap ( sdkEffect ) ,
251+ Effect . mapError ( ( sdkError : OnePasswordError ) =>
252+ chooseFallbackError ( cliError , sdkError ) ,
253+ ) ,
254+ ) ,
255+ ) ,
256+ ) ;
257+
258+ return OnePasswordServiceTag . of ( {
259+ resolveSecret : ( uri ) =>
260+ withSdkFallback ( cliService . resolveSecret ( uri ) , ( service ) => service . resolveSecret ( uri ) ) ,
261+
262+ listVaults : ( ) => withSdkFallback ( cliService . listVaults ( ) , ( service ) => service . listVaults ( ) ) ,
263+
264+ listItems : ( vaultId ) =>
265+ withSdkFallback ( cliService . listItems ( vaultId ) , ( service ) => service . listItems ( vaultId ) ) ,
266+ } ) ;
267+ } ) . pipe ( Effect . withSpan ( "onepassword.make_service" ) ) ;
206268} ;
0 commit comments