mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-11 17:46:41 -05:00
Official 1Password Template Function (#305)
This commit is contained in:
20
plugins/template-function-1password/package.json
Normal file
20
plugins/template-function-1password/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@yaak/template-function-1password",
|
||||
"displayName": "1Password Template Functions",
|
||||
"description": "Template function for accessing 1Password secrets",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"scripts": {
|
||||
"build": "run-p build:*",
|
||||
"build:1-build": "yaakcli build",
|
||||
"build:2-cpywasm": "cpx '../../node_modules/@1password/sdk-core/nodejs/core_bg.*' build/",
|
||||
"dev": "yaakcli dev",
|
||||
"lint": "tsc --noEmit && eslint . --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@1password/sdk": "^0.4.0-beta.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cpx": "^1.5.0"
|
||||
}
|
||||
}
|
||||
123
plugins/template-function-1password/src/index.ts
Normal file
123
plugins/template-function-1password/src/index.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { Client } from '@1password/sdk';
|
||||
import { createClient } from '@1password/sdk';
|
||||
import type { CallTemplateFunctionArgs } from '@yaakapp-internal/plugins';
|
||||
import type { PluginDefinition } from '@yaakapp/api';
|
||||
import crypto from 'crypto';
|
||||
|
||||
const _clients: Record<string, Client> = {};
|
||||
|
||||
async function op(args: CallTemplateFunctionArgs): Promise<Client | null> {
|
||||
const token = args.values.token;
|
||||
if (typeof token !== 'string') return null;
|
||||
|
||||
const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
|
||||
try {
|
||||
_clients[tokenHash] ??= await createClient({
|
||||
auth: token,
|
||||
integrationName: 'Yaak 1Password Plugin',
|
||||
integrationVersion: 'v1.0.0',
|
||||
});
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return _clients[tokenHash];
|
||||
}
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [
|
||||
{
|
||||
name: '1password.item',
|
||||
description: 'Get a secret',
|
||||
args: [
|
||||
{
|
||||
name: 'token',
|
||||
type: 'text',
|
||||
label: '1Password Service Account Token',
|
||||
description: '',
|
||||
defaultValue: '${[ONEPASSWORD_TOKEN]}',
|
||||
password: true,
|
||||
},
|
||||
{
|
||||
name: 'vault',
|
||||
label: 'Vault',
|
||||
type: 'select',
|
||||
options: [],
|
||||
async dynamic(_ctx, args) {
|
||||
const client = await op(args);
|
||||
if (client == null) return { hidden: true };
|
||||
// Fetches a secret.
|
||||
const vaults = await client.vaults.list({ decryptDetails: true });
|
||||
return {
|
||||
options: vaults.map((vault) => ({
|
||||
label: `${vault.title} (${vault.activeItemCount} Items)`,
|
||||
value: vault.id,
|
||||
})),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'item',
|
||||
label: 'Item',
|
||||
type: 'select',
|
||||
options: [],
|
||||
async dynamic(_ctx, args) {
|
||||
const client = await op(args);
|
||||
if (client == null) return { hidden: true };
|
||||
const vaultId = args.values.vault;
|
||||
if (typeof vaultId !== 'string') return { hidden: true };
|
||||
|
||||
const items = await client.items.list(vaultId);
|
||||
return {
|
||||
options: items.map((item) => ({
|
||||
label: item.title + ' ' + item.category,
|
||||
value: item.id,
|
||||
})),
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'field',
|
||||
label: 'Field',
|
||||
type: 'select',
|
||||
options: [],
|
||||
async dynamic(_ctx, args) {
|
||||
const client = await op(args);
|
||||
if (client == null) return { hidden: true };
|
||||
const vaultId = args.values.vault;
|
||||
const itemId = args.values.item;
|
||||
if (typeof vaultId !== 'string' || typeof itemId !== 'string') {
|
||||
return { hidden: true };
|
||||
}
|
||||
|
||||
const item = await client.items.get(vaultId, itemId);
|
||||
|
||||
return {
|
||||
options: item.fields.map((field) => ({ label: field.title, value: field.id })),
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
async onRender(_ctx, args) {
|
||||
const client = await op(args);
|
||||
if (client == null) throw new Error('Invalid token');
|
||||
const vaultId = args.values.vault;
|
||||
const itemId = args.values.item;
|
||||
const fieldId = args.values.field;
|
||||
if (
|
||||
typeof vaultId !== 'string' ||
|
||||
typeof itemId !== 'string' ||
|
||||
typeof fieldId !== 'string'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const item = await client.items.get(vaultId, itemId);
|
||||
const field = item.fields.find((f) => f.id === fieldId);
|
||||
if (field == null) {
|
||||
throw new Error('Field not found: ' + fieldId);
|
||||
}
|
||||
return field.value ?? '';
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
3
plugins/template-function-1password/tsconfig.json
Normal file
3
plugins/template-function-1password/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { AnyModel, HttpUrlParameter } from '@yaakapp-internal/models';
|
||||
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
@@ -36,6 +37,21 @@ export const plugin: PluginDefinition = {
|
||||
name: 'header',
|
||||
label: 'Header Name',
|
||||
type: 'text',
|
||||
async dynamic(ctx, args) {
|
||||
if (typeof args.values.requestId !== 'string') return null;
|
||||
|
||||
const request = await ctx.httpRequest.getById({ id: args.values.requestId });
|
||||
if (request == null) return null;
|
||||
|
||||
const validHeaders = request.headers.filter(h => h.enabled !== false && h.name);
|
||||
return {
|
||||
placeholder: validHeaders[0]?.name,
|
||||
completionOptions: validHeaders.map<GenericCompletionOption>((h) => ({
|
||||
label: h.name,
|
||||
type: 'constant',
|
||||
})),
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||
import type { JSONPathResult } from '../../template-function-json';
|
||||
import { filterJSONPath } from '../../template-function-json';
|
||||
import type { XPathResult } from '../../template-function-xml';
|
||||
@@ -68,7 +69,22 @@ export const plugin: PluginDefinition = {
|
||||
type: 'text',
|
||||
name: 'header',
|
||||
label: 'Header Name',
|
||||
placeholder: 'Content-Type',
|
||||
async dynamic(ctx, args) {
|
||||
const response = await getResponse(ctx, {
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
ttl: String(args.values.ttl || ''),
|
||||
});
|
||||
|
||||
return {
|
||||
placeholder: response?.headers[0]?.name,
|
||||
completionOptions: response?.headers.map<GenericCompletionOption>((h) => ({
|
||||
label: h.name,
|
||||
type: 'constant',
|
||||
})),
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
|
||||
Reference in New Issue
Block a user