#!/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; log.debug("secretResponse", secretResponse); 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; log.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", "direct-output"], collect: ["id"], alias: { d: "direct-output", }, }); if (flags.help) { console.log( "export RUN_ENV=ALIBABA_CLOUD or `echo ALIBABA_CLOUD > ~/.config/envs/RUN_ENV` runs on Alibaba Cloud", ); console.log( "openclaw-secret.ts --id ID1 [--id ID2] [--direct-output|-d]", ); 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); let openClawInput; if (flags.id) { openClawInput = { protocolVersion: 1, provider: "n/a", ids: flags.id, } as OpenClawSecretInput; } else { const input = new TextDecoder().decode(await stdinToArrayBuffer()); 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); } if (flags["direct-output"]) { if (openClawInput.ids.length != 1) { console.error( `--direct-output requires only one id`, openClawInput.ids, ); exit(1); } console.log(await getSecretValue(isOnAlibabaCloud, openClawInput.ids[0])); exit(0); } 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));