fix(console): mirror secure agent setup commands

This commit is contained in:
ZacharyZhang-NY committed 2026-07-19 18:00:33 -04:00
1 parent 78cb95e288
commit 99dc0fe122
9 files changed
+286 -59

No files matched your search

@@ -30,8 +30,16 @@ export default function ConnectStep({
const [language, setLanguage] = useState<SdkLanguage>('node');
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL ?? 'http://localhost:8787';
const instructions = useMemo(
() => buildInstallInstructions(language, { integration, credentials, token, baseUrl }),
[baseUrl, credentials, integration, language, token],
() => buildInstallInstructions(language, {
integration,
identity: {
agentId: credentials.agentId,
kid: credentials.kid,
orgId: credentials.orgId,
},
baseUrl,
}),
[baseUrl, credentials.agentId, credentials.kid, credentials.orgId, integration, language],
);
return (
@@ -63,18 +71,50 @@ export default function ConnectStep({
<h3 id="setup-command-label" className="section-label">
{integration.mode === 'adapter' ? t('agentRegistration.install') : t('agentRegistration.configure')}
</h3>
<CopyControl value={instructions.setup} />
<CopyControl
value={instructions.setup}
ariaLabel={t('agentRegistration.copySetup')}
/>
</div>
<pre className="p-4 bg-ink text-[#EAEAE5] font-mono text-[10px] leading-relaxed whitespace-pre-wrap break-all overflow-x-auto">
{instructions.setup}
</pre>
</section>
<div>
<div
className="border-y border-border"
role="group"
aria-label={t('agentRegistration.steps.credentials')}
>
<div className="flex items-center justify-between py-2 border-b border-border">
<span className="section-label">{t('agentRegistration.privateKey')}</span>
<CopyControl
value={credentials.privateKey}
ariaLabel={t('agentRegistration.copyPrivateKey')}
/>
</div>
<div className="flex items-center justify-between py-2">
<span className="section-label">{t('agentRegistration.apiToken')}</span>
<CopyControl
value={token}
ariaLabel={t('agentRegistration.copyApiToken')}
/>
</div>
</div>
<p className="mt-2 font-mono text-[10px] text-ink-dim">
{t(`agentRegistration.secretDelivery.${instructions.secretDelivery}`)}
</p>
</div>
{instructions.usage && (
<section aria-labelledby="record-operation-label">
<div className="flex items-center justify-between mb-2">
<h3 id="record-operation-label" className="section-label">{t('agentRegistration.record')}</h3>
<CopyControl value={instructions.usage} />
<CopyControl
value={instructions.usage}
ariaLabel={t('agentRegistration.copyUsage')}
/>
</div>
<pre className="p-4 bg-ink text-[#EAEAE5] font-mono text-[10px] leading-relaxed whitespace-pre-wrap break-all overflow-x-auto">
{instructions.usage}
@@ -86,7 +126,10 @@ export default function ConnectStep({
<section aria-labelledby="verify-command-label">
<div className="flex items-center justify-between mb-2">
<h3 id="verify-command-label" className="section-label">{t('agentRegistration.verify')}</h3>
<CopyControl value={instructions.verify} />
<CopyControl
value={instructions.verify}
ariaLabel={t('agentRegistration.copyVerify')}
/>
</div>
<code className="block px-3 py-2 border-y border-border font-mono text-[11px] text-ink">
{instructions.verify}
@@ -6,9 +6,10 @@ import { useTranslation } from 'react-i18next';
interface CopyControlProps {
readonly value: string;
readonly className?: string;
readonly ariaLabel?: string;
}
export default function CopyControl({ value, className = '' }: CopyControlProps) {
export default function CopyControl({ value, className = '', ariaLabel }: CopyControlProps) {
const { t } = useTranslation();
const [status, setStatus] = useState<'idle' | 'copied' | 'failed'>('idle');
@@ -26,6 +27,7 @@ export default function CopyControl({ value, className = '' }: CopyControlProps)
<button
type="button"
onClick={copy}
aria-label={ariaLabel}
className="font-mono text-[10px] uppercase tracking-wider text-ink-dim hover:text-ink"
>
{status === 'copied' ? t('common.copied') : t('common.copy')}
@@ -83,7 +83,10 @@ export default function CredentialsStep({
<h3 id="private-key-label" className="section-label text-red-700">
{t('agentRegistration.privateKeySaveNow')}
</h3>
<CopyControl value={credentials.privateKey} />
<CopyControl
value={credentials.privateKey}
ariaLabel={t('agentRegistration.copyPrivateKey')}
/>
</div>
<code className="block p-3 bg-ink text-[#EAEAE5] font-mono text-[10px] break-all select-all">
{credentials.privateKey}
@@ -104,7 +107,10 @@ export default function CredentialsStep({
<span className="font-mono text-[10px] text-ink-dim">
{t('agentRegistration.tokenIssued')}
</span>
<CopyControl value={token} />
<CopyControl
value={token}
ariaLabel={t('agentRegistration.copyApiToken')}
/>
</div>
<code className="block p-3 bg-ink text-[#EAEAE5] font-mono text-[10px] break-all select-all">
{token}
@@ -1,13 +1,18 @@
import type { IntegrationCatalogItem } from './integrations';
import type { AgentCredentials } from './credentials';
export const SDK_LANGUAGES = ['node', 'python', 'go'] as const;
export type SdkLanguage = (typeof SDK_LANGUAGES)[number];
type SecretDelivery = 'hidden-prompts' | 'environment';
interface AgentInstallIdentity {
readonly agentId: string;
readonly kid: string;
readonly orgId: string;
}
interface InstructionInput {
readonly integration: IntegrationCatalogItem;
readonly credentials: AgentCredentials;
readonly token: string;
readonly identity: AgentInstallIdentity;
readonly baseUrl: string;
}
@@ -16,37 +21,39 @@ export interface InstallInstructions {
readonly usage?: string;
readonly verify?: string;
readonly postInstall?: readonly string[];
readonly secretDelivery: SecretDelivery;
}
function quoted(value: string): string {
return `"${value.replaceAll('"', '\\"')}"`;
function shellQuoted(value: string, label: string): string {
if (!value || /['\u0000-\u001f\u007f]/u.test(value)) {
throw new Error(`${label} cannot be represented in the generated shell command.`);
}
return `'${value}'`;
}
function adapterCommand(
language: SdkLanguage,
{ integration, credentials, token, baseUrl }: InstructionInput,
{ integration, identity, baseUrl }: InstructionInput,
): string {
const values = {
agent: integration.id,
orgId: quoted(credentials.orgId),
agentId: quoted(credentials.agentId),
privateKey: quoted(credentials.privateKey),
kid: quoted(credentials.kid),
token: quoted(token),
baseUrl: quoted(baseUrl),
orgId: shellQuoted(identity.orgId, 'Organization ID'),
agentId: shellQuoted(identity.agentId, 'Agent ID'),
kid: shellQuoted(identity.kid, 'Key ID'),
baseUrl: shellQuoted(baseUrl, 'API base URL'),
};
if (language === 'go') {
return [
'go install github.com/Elydora-Infrastructure/Elydora-Go-SDK/cmd/elydora@latest',
`elydora install --agent ${values.agent} --org-id ${values.orgId} --agent-id ${values.agentId} --private-key ${values.privateKey} --kid ${values.kid} --token ${values.token} --base-url ${values.baseUrl}`,
`elydora install --agent ${values.agent} --org-id ${values.orgId} --agent-id ${values.agentId} --kid ${values.kid} --base-url ${values.baseUrl}`,
].join('\n');
}
const executable = language === 'node'
? 'npx @elydora/sdk install'
: 'python -m pip install elydora\nelydora install';
return `${executable} --agent ${values.agent} --org_id ${values.orgId} --agent_id ${values.agentId} --private_key ${values.privateKey} --kid ${values.kid} --token ${values.token} --base_url ${values.baseUrl}`;
return `${executable} --agent ${values.agent} --org_id ${values.orgId} --agent_id ${values.agentId} --kid ${values.kid} --base_url ${values.baseUrl}`;
}
function postInstallSteps(integration: IntegrationCatalogItem): readonly string[] | undefined {
@@ -63,31 +70,47 @@ function postInstallSteps(integration: IntegrationCatalogItem): readonly string[
}
function sdkSetup(language: SdkLanguage, input: InstructionInput): string {
const { credentials, token, baseUrl } = input;
const { identity, baseUrl } = input;
if (language === 'python') {
return `python -m pip install elydora
import os
from elydora import ElydoraClient
def require_env(name: str) -> str:
value = os.environ.get(name)
if not value:
raise RuntimeError(f"{name} is required")
return value
client = ElydoraClient(
org_id=${JSON.stringify(credentials.orgId)},
agent_id=${JSON.stringify(credentials.agentId)},
private_key=${JSON.stringify(credentials.privateKey)},
org_id=${JSON.stringify(identity.orgId)},
agent_id=${JSON.stringify(identity.agentId)},
private_key=require_env("ELYDORA_PRIVATE_KEY"),
base_url=${JSON.stringify(baseUrl)},
token=${JSON.stringify(token)},
token=require_env("ELYDORA_API_TOKEN"),
)
client.set_kid(${JSON.stringify(credentials.kid)})`;
client.set_kid(${JSON.stringify(identity.kid)})`;
}
if (language === 'go') {
return `go get github.com/Elydora-Infrastructure/Elydora-Go-SDK
requireEnv := func(name string) string {
value, ok := os.LookupEnv(name)
if !ok || value == "" {
panic(name + " is required")
}
return value
}
client, err := elydora.NewClient(&elydora.Config{
OrgID: ${JSON.stringify(credentials.orgId)},
AgentID: ${JSON.stringify(credentials.agentId)},
PrivateKey: ${JSON.stringify(credentials.privateKey)},
OrgID: ${JSON.stringify(identity.orgId)},
AgentID: ${JSON.stringify(identity.agentId)},
PrivateKey: requireEnv("ELYDORA_PRIVATE_KEY"),
BaseURL: ${JSON.stringify(baseUrl)},
Token: ${JSON.stringify(token)},
Token: requireEnv("ELYDORA_API_TOKEN"),
})
if err != nil {
panic(err)
@@ -98,14 +121,20 @@ if err != nil {
import { ElydoraClient } from '@elydora/sdk';
const privateKey = process.env.ELYDORA_PRIVATE_KEY;
const token = process.env.ELYDORA_API_TOKEN;
if (!privateKey || !token) {
throw new Error('ELYDORA_PRIVATE_KEY and ELYDORA_API_TOKEN are required');
}
const client = new ElydoraClient({
orgId: ${JSON.stringify(credentials.orgId)},
agentId: ${JSON.stringify(credentials.agentId)},
privateKey: ${JSON.stringify(credentials.privateKey)},
kid: ${JSON.stringify(credentials.kid)},
orgId: ${JSON.stringify(identity.orgId)},
agentId: ${JSON.stringify(identity.agentId)},
privateKey,
kid: ${JSON.stringify(identity.kid)},
baseUrl: ${JSON.stringify(baseUrl)},
});
client.setToken(${JSON.stringify(token)});`;
client.setToken(token);`;
}
function sdkUsage(language: SdkLanguage): string {
@@ -153,11 +182,13 @@ export function buildInstallInstructions(
setup: adapterCommand(language, input),
verify: language === 'node' ? 'npx @elydora/sdk status' : 'elydora status',
postInstall: postInstallSteps(input.integration),
secretDelivery: 'hidden-prompts',
};
}
return {
setup: sdkSetup(language, input),
usage: sdkUsage(language),
secretDelivery: 'environment',
};
}
+11 -1
View File
@@ -307,6 +307,7 @@
"submitting": "Registering...",
"submit": "Register Agent",
"failedToRegister": "Failed to register agent.",
"privateKey": "Private Key",
"privateKeySaveNow": "Private Key — Save This Now",
"privateKeyWarning": "This will not be shown again. Store it as an environment variable or in a secrets manager.",
"apiToken": "API Token",
@@ -333,6 +334,11 @@
"sdk": "Custom SDK"
},
"integration": "Integration",
"copySetup": "Copy setup command",
"copyUsage": "Copy operation example",
"copyVerify": "Copy verification command",
"copyPrivateKey": "Copy private key",
"copyApiToken": "Copy API token",
"copyFailed": "Copy failed.",
"invalidExpiration": "Select a valid expiration.",
"tokenIssued": "Issued once. Store it securely.",
@@ -340,7 +346,11 @@
"record": "Record an operation",
"verify": "Verify",
"postInstall": "Post-install steps",
"sdkLanguage": "SDK language"
"sdkLanguage": "SDK language",
"secretDelivery": {
"hidden-prompts": "The CLI requests both values with terminal echo disabled.",
"environment": "Set ELYDORA_PRIVATE_KEY and ELYDORA_API_TOKEN in the runtime environment."
}
},
"chain": {
"title": "Chain Visualization",
+11 -1
View File
@@ -307,6 +307,7 @@
"submitting": "注册中...",
"submit": "注册代理",
"failedToRegister": "注册代理失败。",
"privateKey": "私钥",
"privateKeySaveNow": "私钥 — 请立即保存",
"privateKeyWarning": "此私钥不会再次显示。请将其存储为环境变量或保存在密钥管理器中。",
"apiToken": "API 令牌",
@@ -333,6 +334,11 @@
"sdk": "自定义 SDK"
},
"integration": "集成",
"copySetup": "复制安装命令",
"copyUsage": "复制操作示例",
"copyVerify": "复制验证命令",
"copyPrivateKey": "复制私钥",
"copyApiToken": "复制 API 令牌",
"copyFailed": "复制失败。",
"invalidExpiration": "请选择有效期限。",
"tokenIssued": "仅显示一次,请安全保存。",
@@ -340,7 +346,11 @@
"record": "记录操作",
"verify": "验证",
"postInstall": "安装后步骤",
"sdkLanguage": "SDK 语言"
"sdkLanguage": "SDK 语言",
"secretDelivery": {
"hidden-prompts": "CLI 会在关闭终端回显后请求这两项凭据。",
"environment": "请在运行环境中设置 ELYDORA_PRIVATE_KEY 与 ELYDORA_API_TOKEN。"
}
},
"chain": {
"title": "链可视化",