#!/usr/bin/env -S deno run -A import { args, execCommand, exit, fetchDataWithTimeout, getEnv, log, stdinToArrayBuffer, } from "https://script.hatter.ink/@50/deno-commons-mod.ts"; import {parseArgs} from "jsr:@std/cli/parse-args"; // specification: https://docs.openclaw.ai/gateway/secrets // input: // { "protocolVersion": 1, "provider": "vault", "ids": ["providers/openai/apiKey"] } // output: // { "protocolVersion": 1, "values": { "providers/openai/apiKey": "" } } // or with error: // { // "protocolVersion": 1, // "values": {}, // "errors": { "providers/openai/apiKey": { "message": "not found" } } // } interface OpenClawSecretInput { protocolVersion: number; provider: string; ids: string[]; } interface OpenClawSecretOutput { protocolVersion: number; values: Record; errors: Record>; } interface GetSecretResponse { status: number; message: string; data: { created: number; modified: number; name: string; creatorKeyId: string; grantedKeyIds: string[]; comment: string; value: string; version: number; }; } async function getSecretValueViaAlibabaCloudHttps( key: string, ): Promise { const pkcs7Response = await fetchDataWithTimeout( "http://100.100.100.200/latest/dynamic/instance-identity/pkcs7", ); if (pkcs7Response.status != 200) { throw new Error("Get PKCS#7 failed: ${pkcs7Response.status}`)"); } const pkcs7 = await pkcs7Response.text(); const httpSecretResponse = await fetchDataWithTimeout( "https://hatter.ink/secret/get.json?name=" + encodeURIComponent(key), { headers: { "Authorization": `PKCS7 ${pkcs7}`, }, }, ); if (httpSecretResponse.status != 200) { throw new Error(`Get secret failed: ${httpSecretResponse.status}`); } const secretResponse = await httpSecretResponse .json() as GetSecretResponse; if (secretResponse.status != 200) { throw new Error(`Get secret failed: ${secretResponse.status}`); } return secretResponse.data.value; } async function getSecretValueViaHatterCli(key: string): Promise { const output = await execCommand("hatter", [ "secret", "get", "--name", key, ]); const secretResponse = output.getStdoutAsJson() as GetSecretResponse; console.debug("secretResponse", secretResponse); if (secretResponse.status != 200) { throw new Error(`Get secret failed: ${secretResponse.status}`); } return secretResponse.data.value; } async function getSecretValue( isOnAlibabaCloud: boolean, key: string, ): Promise { if (isOnAlibabaCloud) { return await getSecretValueViaAlibabaCloudHttps(key); } else { return await getSecretValueViaHatterCli(key); } } const flags = parseArgs(args(), { boolean: ["help"], }); if (flags.help) { console.log( "export RUN_ENV=ALIBABA_CLOUD or `echo ALIBABA_CLOUD > ~/.config/envs/RUN_ENV` runs on Alibaba Cloud", ); console.log( 'echo \'{"protocolVersion": 1, "provider": "vault", "ids": ["providers/openai/apiKey"]}\' | openclaw-secret.ts', ); exit(0); } // RUN_ENV values ALIBABA_CLOUD, LOCAL const runEnv = getEnv("RUN_ENV"); const isOnAlibabaCloud = runEnv == "ALIBABA_CLOUD"; log.debug("isOnAlibabaCloud", isOnAlibabaCloud); const input = new TextDecoder().decode(await stdinToArrayBuffer()); const openClawInput = JSON.parse(input) as OpenClawSecretInput; const values = {} as Record; const errors = {} as Record>; if (openClawInput.protocolVersion !== 1) { console.error( `Invalid OpenClaw protocol version: ${openClawInput.protocolVersion}`, ); exit(1); } for (const id of openClawInput.ids) { try { values[id] = await getSecretValue(isOnAlibabaCloud, id); } catch (e) { errors[id] = { message: e.message }; } } const output = { protocolVersion: 1, values, errors, } as OpenClawSecretOutput; console.log(JSON.stringify(output, null, 2));