Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ jobs:
- name: Smoke test container
run: |
set -euo pipefail
docker run --rm -d \
docker rm -f liveagent-gateway-smoke >/dev/null 2>&1 || true
docker run -d \
--name liveagent-gateway-smoke \
-p 18080:8080 \
-e LIVEAGENT_GATEWAY_TOKEN=ci-token \
Expand All @@ -91,7 +92,8 @@ jobs:
fi
sleep 1
done
docker logs liveagent-gateway-smoke
echo "Gateway Docker smoke test failed; container logs:"
docker logs liveagent-gateway-smoke || true
exit 1

webui:
Expand Down
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,16 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates \
&& rm -rf /var/lib/apt/lists/*

RUN useradd --system --uid 10001 --home-dir /nonexistent --shell /usr/sbin/nologin liveagent
RUN useradd --system --uid 10001 --user-group --home-dir /nonexistent --shell /usr/sbin/nologin liveagent \
&& install -d -o liveagent -g liveagent -m 0700 /var/lib/liveagent

COPY --from=gateway-builder /out/liveagent-gateway /usr/local/bin/liveagent-gateway

USER liveagent

ENV PORT=8080
ENV LIVEAGENT_GATEWAY_GRPC_ADDR=:50051
ENV LIVEAGENT_GATEWAY_CHAT_EVENT_STORE=/var/lib/liveagent/gateway-chat.sqlite3

EXPOSE 8080 50051

Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ gateway-docker-smoke: gateway-docker-build
@set -e; \
name="liveagent-gateway-smoke"; \
docker rm -f "$$name" >/dev/null 2>&1 || true; \
docker run --rm -d --name "$$name" -p 18080:8080 -e LIVEAGENT_GATEWAY_TOKEN=$(DEV_GATEWAY_TOKEN) $(GATEWAY_DOCKER_IMAGE) >/dev/null; \
docker run -d --name "$$name" -p 18080:8080 -e LIVEAGENT_GATEWAY_TOKEN=$(DEV_GATEWAY_TOKEN) $(GATEWAY_DOCKER_IMAGE) >/dev/null; \
trap 'docker rm -f "$$name" >/dev/null 2>&1 || true' EXIT; \
for _ in $$(seq 1 30); do \
if curl -fsS http://127.0.0.1:18080/healthz | grep -q '"ok":true'; then \
Expand All @@ -152,7 +152,8 @@ gateway-docker-smoke: gateway-docker-build
fi; \
sleep 1; \
done; \
docker logs "$$name"; \
echo "Gateway Docker smoke test failed; container logs:"; \
docker logs "$$name" || true; \
exit 1

build-linux: proto webui
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,19 +237,21 @@ export function SshTunnelPanel(props: SshTunnelPanelProps) {
const visibleSessions = scope === "project" ? projectSshSessions : sshSessions;
const canCreateInScope = scope === "project";
const createHosts = canCreateInScope ? associatedHosts : [];
const hasCreateHosts = createHosts.length > 0;
const canShowCreateButton = canCreateInScope && hasCreateHosts;
const selectedCreateHostId = createHosts.some((host) => host.id === createHostId)
? createHostId
: (createHosts[0]?.id ?? "");
const selectedCreateHost = createHosts.find((host) => host.id === selectedCreateHostId) ?? null;
const selectedHostMessage = selectedCreateHost ? hostStatusMessage(selectedCreateHost, t) : "";
const canCreate = Boolean(
canCreateInScope && selectedCreateHost && !selectedHostMessage && !creating,
canShowCreateButton && selectedCreateHost && !selectedHostMessage && !creating,
);

useEffect(() => {
if (canCreateInScope || view !== "create") return;
if (canShowCreateButton || view !== "create") return;
setView("list");
}, [canCreateInScope, view]);
}, [canShowCreateButton, view]);

useEffect(() => {
if (!active) return;
Expand Down Expand Up @@ -524,7 +526,6 @@ export function SshTunnelPanel(props: SshTunnelPanelProps) {
scope === "project"
? t("projectTools.sshTunnelProjectEmptyHint")
: t("projectTools.sshTunnelAllEmptyHint");
const hasCreateHosts = createHosts.length > 0;
const visibleSessionCount = visibleSessions.length;
const connectedSessionCount = visibleSessions.filter(sshSessionConnected).length;
const statusText =
Expand Down Expand Up @@ -816,7 +817,7 @@ export function SshTunnelPanel(props: SshTunnelPanelProps) {
{statusText}
</div>
</div>
{canCreateInScope ? (
{canShowCreateButton ? (
<button
type="button"
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-transparent text-muted-foreground transition-colors hover:border-border/60 hover:bg-muted/60 hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
Expand Down Expand Up @@ -889,7 +890,7 @@ export function SshTunnelPanel(props: SshTunnelPanelProps) {

{visibleSessionCount === 0 ? (
<div className="flex min-h-full items-center justify-center">
<div className="flex flex-col items-center justify-center gap-1.5 rounded-lg border border-dashed border-border/70 bg-background/40 px-4 py-8 text-center">
<div className="flex flex-col items-center justify-center gap-1.5 rounded-lg bg-background/40 px-4 py-8 text-center">
<div className="mb-1.5 flex h-12 w-12 items-center justify-center rounded-xl border border-border/50 bg-background/80 text-muted-foreground/70 shadow-[inset_0_1px_0_hsl(0_0%_100%_/_0.6),0_1px_3px_hsl(0_0%_0%_/_0.05)] dark:shadow-none">
<Key className="h-5 w-5" />
</div>
Expand All @@ -898,14 +899,13 @@ export function SshTunnelPanel(props: SshTunnelPanelProps) {
{emptyHint}
</div>
<div className="mt-2 flex flex-wrap items-center justify-center gap-2">
{canCreateInScope ? (
{canShowCreateButton ? (
<Button
type="button"
variant="default"
size="sm"
className="h-7 rounded-lg px-2.5 text-xs"
onClick={() => setView("create")}
disabled={!hasCreateHosts}
>
{t("projectTools.newSshTunnel")}
</Button>
Expand Down
52 changes: 42 additions & 10 deletions crates/agent-gateway/web/src/i18n/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,10 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.navRemote": "Remote",
"settings.navSkills": "Skills",
"settings.navMemory": "记忆",
"settings.groupGeneral": "通用",
"settings.groupIntelligence": "智能",
"settings.groupAutomation": "自动化",
"settings.groupConnectivity": "连接",
"settings.backToChat": "返回对话",
"settings.title": "设置",

Expand Down Expand Up @@ -937,19 +941,14 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.agentsNamePlaceholder": "例如:代码审查助手",
"settings.agentsDescription": "描述",
"settings.agentsDescriptionPlaceholder": "简要说明这个全局提示词模板的用途",
"settings.agentsTags": "标签",
"settings.agentsTagsPlaceholder": "例如:代码审查, 写作, 全局提示",
"settings.agentsPrompt": "Prompt",
"settings.agentsPromptPlaceholder": "输入完整的全局提示词内容...",
"settings.agentsCount": "个模板",
"settings.agentsActive": "已激活",
"settings.agentsActiveLabel": "激活中",
"settings.agentsNoTemplates": "还没有配置任何全局提示词模板",
"settings.agentsNoTemplatesHint": "创建全局提示词模板,在对话中快速复用常见的 Agent 指令",
"settings.agentsNoDescription": "暂无描述",
"settings.agentsNoTags": "无标签",
"settings.agentsShowPrompt": "查看 Prompt",
"settings.agentsHidePrompt": "收起 Prompt",
"settings.agentsReady": "可以保存",
"settings.agentsRequired": "名称和 Prompt 为必填项",

Expand Down Expand Up @@ -1286,6 +1285,23 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.skillsHubInstallStatusFailed": "读取技能安装状态失败",
"settings.skillsHubDeleteFailed": "删除技能失败",
"settings.skillsHubDetailLoadFailed": "加载技能详情失败",
"settings.skillsInstalledPreviewOpen": "查看技能详情",
"settings.skillsInstalledPreviewTitle": "已安装技能",
"settings.skillsInstalledPreviewSelected": "已选中",
"settings.skillsInstalledPreviewUnselected": "未选中",
"settings.skillsInstalledPreviewBuiltIn": "内置",
"settings.skillsInstalledPreviewName": "技能名称",
"settings.skillsInstalledPreviewDescription": "技能描述",
"settings.skillsInstalledPreviewNoDescription": "暂无描述",
"settings.skillsInstalledPreviewDetails": "信息",
"settings.skillsInstalledPreviewBaseDir": "目录",
"settings.skillsInstalledPreviewSkillFile": "技能文件",
"settings.skillsInstalledPreviewSource": "来源",
"settings.skillsInstalledPreviewPublished": "发布",
"settings.skillsInstalledPreviewFilePreview": "文件预览",
"settings.skillsInstalledPreviewUnavailable": "文件预览暂不可用。",
"settings.skillsInstalledPreviewEmpty": "这个技能文件没有可预览内容。",
"settings.skillsInstalledPreviewTruncated": "仅显示前 {count} 行。",
"settings.skillsSelected": "已选中",
"settings.skillsEnable": "启用技能",
"settings.skillsAlwaysOn": "内置",
Expand Down Expand Up @@ -2075,6 +2091,10 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.navRemote": "Remote",
"settings.navSkills": "Skills",
"settings.navMemory": "Memory",
"settings.groupGeneral": "General",
"settings.groupIntelligence": "Intelligence",
"settings.groupAutomation": "Automation",
"settings.groupConnectivity": "Connectivity",
"settings.backToChat": "Back to Chat",
"settings.title": "Settings",

Expand Down Expand Up @@ -2384,8 +2404,6 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.agentsDescription": "Description",
"settings.agentsDescriptionPlaceholder":
"Briefly describe what this global prompt template is for",
"settings.agentsTags": "Tags",
"settings.agentsTagsPlaceholder": "e.g. review, writing, global prompt",
"settings.agentsPrompt": "Prompt",
"settings.agentsPromptPlaceholder": "Enter the full global prompt content...",
"settings.agentsCount": "templates",
Expand All @@ -2394,10 +2412,7 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.agentsNoTemplates": "No global prompt templates yet",
"settings.agentsNoTemplatesHint":
"Create reusable prompt templates to quickly apply common Agent instructions in your chats",
"settings.agentsNoDescription": "No description",
"settings.agentsNoTags": "No tags",
"settings.agentsShowPrompt": "View Prompt",
"settings.agentsHidePrompt": "Hide Prompt",
"settings.agentsReady": "Ready to save",
"settings.agentsRequired": "Name and Prompt are required",

Expand Down Expand Up @@ -2747,6 +2762,23 @@ export const translations: Record<Locale, Record<string, string>> = {
"settings.skillsHubInstallStatusFailed": "Failed to read Skill install status",
"settings.skillsHubDeleteFailed": "Failed to delete Skill",
"settings.skillsHubDetailLoadFailed": "Failed to load Skill details",
"settings.skillsInstalledPreviewOpen": "View Skill details",
"settings.skillsInstalledPreviewTitle": "Installed Skill",
"settings.skillsInstalledPreviewSelected": "Selected",
"settings.skillsInstalledPreviewUnselected": "Not selected",
"settings.skillsInstalledPreviewBuiltIn": "Built-in",
"settings.skillsInstalledPreviewName": "Skill name",
"settings.skillsInstalledPreviewDescription": "Description",
"settings.skillsInstalledPreviewNoDescription": "No description",
"settings.skillsInstalledPreviewDetails": "Details",
"settings.skillsInstalledPreviewBaseDir": "Directory",
"settings.skillsInstalledPreviewSkillFile": "Skill file",
"settings.skillsInstalledPreviewSource": "Source",
"settings.skillsInstalledPreviewPublished": "Published",
"settings.skillsInstalledPreviewFilePreview": "File preview",
"settings.skillsInstalledPreviewUnavailable": "File preview is unavailable.",
"settings.skillsInstalledPreviewEmpty": "This Skill file has no previewable content.",
"settings.skillsInstalledPreviewTruncated": "Showing the first {count} lines only.",
"settings.skillsSelected": "Selected",
"settings.skillsEnable": "Enable Skills",
"settings.skillsAlwaysOn": "Built-in",
Expand Down
11 changes: 0 additions & 11 deletions crates/agent-gateway/web/src/lib/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ export type AgentPromptTemplate = {
id: string;
name: string;
description: string;
tags: string[];
prompt: string;
enabled: boolean;
};
Expand Down Expand Up @@ -934,15 +933,6 @@ function normalizeStringArray(input: unknown): string[] {
.filter(Boolean);
}

function normalizeOrderedUniqueStrings(input: unknown): string[] {
const out: string[] = [];
for (const value of normalizeStringArray(input)) {
if (out.includes(value)) continue;
out.push(value);
}
return out;
}

function normalizeOptionalText(input: unknown): string {
return typeof input === "string" ? input.trim() : "";
}
Expand Down Expand Up @@ -1298,7 +1288,6 @@ export function normalizeAgentPromptTemplate(input: unknown): AgentPromptTemplat
id: typeof obj.id === "string" && obj.id.trim() ? obj.id.trim() : crypto.randomUUID(),
name: typeof obj.name === "string" && obj.name.trim() ? obj.name.trim() : "未命名模板",
description: normalizeOptionalText(obj.description),
tags: normalizeOrderedUniqueStrings(obj.tags),
prompt: normalizeOptionalText(obj.prompt),
enabled: obj.enabled === true,
};
Expand Down
Loading
Loading