feat: KMS client + sync all package versions to 1.8.0

- Add shared/kms.ts: lightweight KMS client (list, get, create, update, delete secrets)
- Add KMS_API_URL + kmsApiUrl to shared config with runtime override support
- Sync versions: VS Code 1.7.27→1.8.0, JetBrains 1.7.18→1.8.0, DXT 1.7.27→1.8.0, Tools 1.7.27→1.8.0
- KMS uses IAM bearer tokens — no separate credentials needed
This commit is contained in:
Hanzo Dev
2026-03-11 15:48:20 -07:00
parent 48c37ffcc1
commit b8427412c8
6 changed files with 179 additions and 4 deletions
+7
View File
@@ -39,6 +39,11 @@ export const STORAGE_KEYS = {
/** Cloud API base — override via storage key 'hanzo_config_api_base' */
export const API_BASE_URL = 'https://api.hanzo.ai';
// ── KMS (Secrets Management) ──────────────────────────────────────────
/** KMS API base — override via storage key 'hanzo_config_kms_url' */
export const KMS_API_URL = 'https://kms.hanzo.ai';
// ── Local Provider Defaults ────────────────────────────────────────────
export const LOCAL_PROVIDER_DEFAULTS = {
@@ -79,6 +84,7 @@ export interface RuntimeConfig {
iamLoginUrl: string;
iamApiUrl: string;
apiBaseUrl: string;
kmsApiUrl: string;
clientId: string;
scopes: string;
redirectUri: string;
@@ -89,6 +95,7 @@ const CONFIG_STORAGE_MAP: Record<keyof RuntimeConfig, { key: string; fallback: s
iamLoginUrl: { key: 'hanzo_config_iam_login_url', fallback: IAM_LOGIN_URL },
iamApiUrl: { key: 'hanzo_config_iam_api_url', fallback: IAM_API_URL },
apiBaseUrl: { key: 'hanzo_config_api_base', fallback: API_BASE_URL },
kmsApiUrl: { key: 'hanzo_config_kms_url', fallback: KMS_API_URL },
clientId: { key: 'hanzo_config_client_id', fallback: DEFAULT_CLIENT_ID },
scopes: { key: 'hanzo_config_scopes', fallback: DEFAULT_SCOPES },
redirectUri: { key: 'hanzo_config_redirect_uri', fallback: DEFAULT_REDIRECT_URI },
+168
View File
@@ -0,0 +1,168 @@
/**
* KMS (Key Management / Secrets) client — shared across all browser targets.
*
* Lightweight fetch-based client for Hanzo KMS (kms.hanzo.ai).
* Uses IAM bearer tokens for auth — no separate KMS credentials needed.
*
* API surface mirrors @hanzo/kms-sdk SecretsApi:
* GET /api/v3/secrets/raw → list
* GET /api/v3/secrets/raw/:name → get
* POST /api/v3/secrets/raw/:name → create
* PATCH /api/v3/secrets/raw/:name → update
* DELETE /api/v3/secrets/raw/:name → delete
*/
import { KMS_API_URL } from './config.js';
// ── Types ───────────────────────────────────────────────────────────────
export interface KmsSecret {
secretKey: string;
secretValue?: string;
version?: number;
type?: string;
environment?: string;
secretPath?: string;
}
export interface KmsListParams {
workspaceId: string;
environment: string;
secretPath?: string;
}
export interface KmsSecretParams extends KmsListParams {
secretName: string;
}
// ── Helpers ─────────────────────────────────────────────────────────────
function kmsHeaders(token: string): Record<string, string> {
return {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
};
}
function kmsUrl(path: string, baseUrl?: string): string {
return `${baseUrl || KMS_API_URL}${path}`;
}
function buildQuery(params: Record<string, string | undefined>): string {
const entries = Object.entries(params).filter(([, v]) => v !== undefined) as [string, string][];
if (!entries.length) return '';
return '?' + new URLSearchParams(entries).toString();
}
// ── API Functions ───────────────────────────────────────────────────────
/** List secrets in a workspace/environment */
export async function kmsListSecrets(
token: string,
params: KmsListParams,
baseUrl?: string,
): Promise<KmsSecret[]> {
const query = buildQuery({
workspaceId: params.workspaceId,
environment: params.environment,
secretPath: params.secretPath,
});
const resp = await fetch(kmsUrl(`/api/v3/secrets/raw${query}`, baseUrl), {
headers: kmsHeaders(token),
});
if (!resp.ok) throw new Error(`KMS list failed: ${resp.status} ${await resp.text()}`);
const data = await resp.json();
return data.secrets || data;
}
/** Get a single secret by name */
export async function kmsGetSecret(
token: string,
params: KmsSecretParams,
baseUrl?: string,
): Promise<KmsSecret> {
const query = buildQuery({
workspaceId: params.workspaceId,
environment: params.environment,
secretPath: params.secretPath,
});
const resp = await fetch(
kmsUrl(`/api/v3/secrets/raw/${encodeURIComponent(params.secretName)}${query}`, baseUrl),
{ headers: kmsHeaders(token) },
);
if (!resp.ok) throw new Error(`KMS get failed: ${resp.status} ${await resp.text()}`);
const data = await resp.json();
return data.secret || data;
}
/** Create a new secret */
export async function kmsCreateSecret(
token: string,
params: KmsSecretParams,
value: string,
baseUrl?: string,
): Promise<KmsSecret> {
const resp = await fetch(
kmsUrl(`/api/v3/secrets/raw/${encodeURIComponent(params.secretName)}`, baseUrl),
{
method: 'POST',
headers: kmsHeaders(token),
body: JSON.stringify({
workspaceId: params.workspaceId,
environment: params.environment,
secretPath: params.secretPath,
secretValue: value,
type: 'shared',
}),
},
);
if (!resp.ok) throw new Error(`KMS create failed: ${resp.status} ${await resp.text()}`);
const data = await resp.json();
return data.secret || data;
}
/** Update an existing secret */
export async function kmsUpdateSecret(
token: string,
params: KmsSecretParams,
value: string,
baseUrl?: string,
): Promise<KmsSecret> {
const resp = await fetch(
kmsUrl(`/api/v3/secrets/raw/${encodeURIComponent(params.secretName)}`, baseUrl),
{
method: 'PATCH',
headers: kmsHeaders(token),
body: JSON.stringify({
workspaceId: params.workspaceId,
environment: params.environment,
secretPath: params.secretPath,
secretValue: value,
}),
},
);
if (!resp.ok) throw new Error(`KMS update failed: ${resp.status} ${await resp.text()}`);
const data = await resp.json();
return data.secret || data;
}
/** Delete a secret */
export async function kmsDeleteSecret(
token: string,
params: KmsSecretParams,
baseUrl?: string,
): Promise<void> {
const resp = await fetch(
kmsUrl(`/api/v3/secrets/raw/${encodeURIComponent(params.secretName)}`, baseUrl),
{
method: 'DELETE',
headers: kmsHeaders(token),
body: JSON.stringify({
workspaceId: params.workspaceId,
environment: params.environment,
secretPath: params.secretPath,
}),
},
);
if (!resp.ok) throw new Error(`KMS delete failed: ${resp.status} ${await resp.text()}`);
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hanzo/dxt",
"version": "1.7.27",
"version": "1.8.0",
"description": "Hanzo AI DXT Bundle",
"main": "dist/index.js",
"scripts": {
+1 -1
View File
@@ -4,7 +4,7 @@ pluginGroup = ai.hanzo
pluginName = Hanzo AI
pluginRepositoryUrl = https://github.com/hanzoai/hanzo-ai-jetbrains
# SemVer format -> https://semver.org
pluginVersion = 1.7.18
pluginVersion = 1.8.0
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
pluginSinceBuild = 233
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@hanzo/cli-tools",
"version": "1.7.27",
"version": "1.8.0",
"description": "Hanzo AI CLI Tools Integration",
"main": "dist/index.js",
"scripts": {
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "hanzo-ide",
"displayName": "Hanzo AI",
"description": "The ultimate meta AI development platform. Manage and run all LLMs and CLI tools (Claude, Codex, Gemini, OpenHands, Aider) in one unified interface. Features MCP integration, async execution, git worktrees, and universal context sync.",
"version": "1.7.27",
"version": "1.8.0",
"publisher": "hanzo-ai",
"private": false,
"license": "MIT",