diff --git a/plugins/template-function-1password/src/index.ts b/plugins/template-function-1password/src/index.ts index 170a2cfd..af11dd02 100644 --- a/plugins/template-function-1password/src/index.ts +++ b/plugins/template-function-1password/src/index.ts @@ -12,14 +12,16 @@ interface CacheEntry { expiresAt: number; } +type Result = { error: unknown } | T; + const CACHE_TTL_MS = 60 * 1000; // 1 minute cache TTL const _cache: Record> = {}; async function op( args: CallTemplateFunctionArgs, -): Promise<{ client?: Client; clientHash?: string; error?: unknown }> { - let authMethod: string | DesktopAuth | null = null; - let hash: string | null = null; +): Promise> { + let authMethod: string | DesktopAuth; + let hash: string; switch (args.values.authMethod) { case 'desktop': { const account = args.values.token; @@ -37,18 +39,20 @@ async function op( authMethod = token; break; } + default: + return { error: 'Invalid authentication method' }; } - if (hash == null || authMethod == null) return { error: 'Invalid authentication method' }; - - try { - _clients[hash] ??= await createClient({ - auth: authMethod, - integrationName: 'Yaak 1Password Plugin', - integrationVersion: 'v1.0.0', - }); - } catch (e) { - return { error: e }; + if (!_clients[hash]) { + try { + _clients[hash] = await createClient({ + auth: authMethod, + integrationName: 'Yaak 1Password Plugin', + integrationVersion: 'v1.0.0', + }); + } catch (e) { + return { error: e }; + } } return { client: _clients[hash], clientHash: hash }; @@ -59,47 +63,35 @@ async function getValue( vaultId?: JsonPrimitive, itemId?: JsonPrimitive, fieldId?: JsonPrimitive, -): Promise<{ value?: string; error?: unknown }> { - const { client, error, clientHash } = await op(args); - if (!client || !clientHash) return { error }; +): Promise> { + const res = await op(args); + if ('error' in res) return { error: res.error }; + const clientHash = res.clientHash; + const client = res.client; - if (vaultId && typeof vaultId === 'string') { - const vaultCacheKey = `${clientHash}:vault:${vaultId}`; - const cachedVault = getCached(vaultCacheKey); - if (!cachedVault) { - try { - const vault = await client.vaults.getOverview(vaultId); - setCache(vaultCacheKey, vault); - } catch { - return { error: `Vault ${vaultId} not found` }; - } - } - } else { + if (!vaultId || typeof vaultId !== 'string') { return { error: 'No vault specified' }; } - - if (itemId && typeof itemId === 'string') { - const itemCacheKey = `${clientHash}:item:${vaultId}:${itemId}`; - try { - const item = - getCached>>(itemCacheKey) ?? - setCache(itemCacheKey, await client.items.get(vaultId, itemId)); - if (fieldId && typeof fieldId === 'string') { - const field = item.fields.find((f) => f.id === fieldId); - if (field) { - return { value: field.value }; - } else { - return { error: `Field ${fieldId} not found in item ${itemId} in vault ${vaultId}` }; - } - } - } catch { - return { error: `Item ${itemId} not found in vault ${vaultId}` }; - } - } else { + if (!itemId || typeof itemId !== 'string') { return { error: 'No item specified' }; } + if (!fieldId || typeof fieldId !== 'string') { + return { error: 'No field specified' }; + } - return {}; + try { + const cacheKey = `${clientHash}:item:${vaultId}:${itemId}:${fieldId}`; + let value = getCached(cacheKey); + + if (!value) { + value = await client.secrets.resolve(`op://${vaultId}/${itemId}/${fieldId}`); + setCache(cacheKey, value); + } + + return { value }; + } catch (e) { + return { error: e }; + } } export const plugin: PluginDefinition = { @@ -161,8 +153,10 @@ export const plugin: PluginDefinition = { type: 'select', options: [], async dynamic(_ctx, args) { - const { client, clientHash } = await op(args); - if (client == null || clientHash == null) return { hidden: true }; + const res = await op(args); + if ('error' in res) return { hidden: true }; + const clientHash = res.clientHash; + const client = res.client; const cacheKey = `${clientHash}:vaults`; const cachedVaults = @@ -170,11 +164,22 @@ export const plugin: PluginDefinition = { const vaults = cachedVaults ?? setCache(cacheKey, await client.vaults.list({ decryptDetails: true })); + return { - options: vaults.map((vault) => ({ - label: `${vault.title} (${vault.activeItemCount} Items)`, - value: vault.id, - })), + options: vaults.map((vault) => { + let title = vault.id; + if ('title' in vault) { + title = vault.title; + } else if ('name' in vault) { + // The SDK returns 'name' instead of 'title' but the bindings still use 'title' + title = (vault as { name: string }).name; + } + + return { + label: `${title} (${vault.activeItemCount} Items)`, + value: vault.id, + }; + }), }; }, }, @@ -184,8 +189,11 @@ export const plugin: PluginDefinition = { type: 'select', options: [], async dynamic(_ctx, args) { - const { client, clientHash } = await op(args); - if (client == null || clientHash == null) return { hidden: true }; + const res = await op(args); + if ('error' in res) return { hidden: true }; + const clientHash = res.clientHash; + const client = res.client; + const vaultId = args.values.vault; if (typeof vaultId !== 'string') return { hidden: true }; @@ -212,8 +220,11 @@ export const plugin: PluginDefinition = { type: 'select', options: [], async dynamic(_ctx, args) { - const { client, clientHash } = await op(args); - if (client == null || clientHash == null) return { hidden: true }; + const res = await op(args); + if ('error' in res) return { hidden: true }; + const clientHash = res.clientHash; + const client = res.client; + const vaultId = args.values.vault; const itemId = args.values.item; if (typeof vaultId !== 'string' || typeof itemId !== 'string') { @@ -239,12 +250,12 @@ export const plugin: PluginDefinition = { const vaultId = args.values.vault; const itemId = args.values.item; const fieldId = args.values.field; - const { value, error } = await getValue(args, vaultId, itemId, fieldId); - if (error) { - throw error; + const res = await getValue(args, vaultId, itemId, fieldId); + if ('error' in res) { + throw res.error; } - return value ?? ''; + return res.value; }, }, ],