diff --git a/apps/app/src/app/components/mcp-auth-modal.tsx b/apps/app/src/app/components/mcp-auth-modal.tsx
index 8d0b04a1b..2ffde2846 100644
--- a/apps/app/src/app/components/mcp-auth-modal.tsx
+++ b/apps/app/src/app/components/mcp-auth-modal.tsx
@@ -168,7 +168,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
statusPoll = window.setInterval(async () => {
if (Date.now() - startedAt >= MCP_AUTH_TIMEOUT_MS) {
stopStatusPolling();
- setError(translate("mcp.auth.request_timed_out"));
+ setError("Request timed out.");
return;
}
@@ -694,7 +694,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {
{translate("mcp.auth.already_connected")}
+Already Connected
{translate("mcp.auth.already_connected_description", { server: serverName() })}
@@ -804,7 +804,7 @@ export default function McpAuthModal(props: McpAuthModalProps) {{translate("mcp.auth.step1_title")}
+Opening your browser
{translate("mcp.auth.step1_description", { server: serverName() })}
@@ -863,7 +863,7 @@ export default function McpAuthModal(props: McpAuthModalProps) { 2{translate("mcp.auth.step2_title")}
+Authorize OpenWork
{translate("mcp.auth.step2_description")}
@@ -875,7 +875,7 @@ export default function McpAuthModal(props: McpAuthModalProps) { 3{translate("mcp.auth.step3_title")}
+Return here when you're done
{translate("mcp.auth.step3_description")}
diff --git a/apps/app/src/app/components/question-modal.tsx b/apps/app/src/app/components/question-modal.tsx index 94448f56b..4b3b070bd 100644 --- a/apps/app/src/app/components/question-modal.tsx +++ b/apps/app/src/app/components/question-modal.tsx @@ -4,7 +4,6 @@ import type { QuestionInfo } from "@opencode-ai/sdk/v2/client"; import { Check, ChevronRight, HelpCircle } from "lucide-solid"; import Button from "./button"; -import { t } from "../../i18n"; export type QuestionModalProps = { open: boolean; @@ -139,10 +138,10 @@ export default function QuestionModal(props: QuestionModalProps) {- {t("scheduled.page_description")} + Schedule recurring tasks for this worker, monitor what is already registered, and start from a reusable template.
{props.schedulerInstalled - ? t("scheduled.reload_activate_hint") - : t("scheduled.install_scheduler_hint")} + ? "OpenCode loads plugins at startup. Reload OpenWork to activate opencode-scheduler for this workspace." + : "Automations run through the opencode-scheduler plugin today. Add it to this workspace to unlock local scheduling."}
{sourceDescription()}
- {t("scheduled.quick_start_templates_desc")} + Start from a proven recurring workflow, then tailor the prompt before you prepare it in chat.
- {t("scheduled.delete_confirm_desc", undefined, { source: sourceLabel().toLowerCase() })} + This removes the schedule and deletes the job definition from {sourceLabel().toLowerCase()}.
- {t("scheduled.create_desc")} + The form is ready for direct writes. For now, OpenWork prepares the scheduler command in chat for you.
@@ -336,9 +335,9 @@ export default function ConfigView(props: ConfigViewProps) {
- {t("config.server_sharing_title")}
+ OpenWork server sharing
- {t("config.server_sharing_desc")}
+ Share these details with a trusted device. Keep the server on the same network for the fastest setup.
@@ -349,15 +348,15 @@ export default function ConfigView(props: ConfigViewProps) {
- {t("config.server_url_label")}
- {hostConnectUrl() || t("config.starting_server")}
+ OpenWork Server URL
+ {hostConnectUrl() || "Starting server…"}
{!hostRemoteAccessEnabled()
- ? t("config.remote_access_off_hint")
+ ? "Remote access is off. Use Share workspace to enable it before connecting from another machine."
: hostConnectUrlUsesMdns()
- ? t("config.mdns_hint")
- : t("config.local_ip_hint")}
+ ? ".local names are easier to remember but may not resolve on all networks."
+ : "Use your local IP on the same Wi-Fi for the fastest connection."}
@@ -367,13 +366,13 @@ export default function ConfigView(props: ConfigViewProps) {
onClick={() => handleCopy(hostConnectUrl(), "host-url")}
disabled={!hostConnectUrl()}
>
- {copyingField() === "host-url" ? t("config.copied") : t("config.copy")}
+ {copyingField() === "host-url" ? "Copied" : "Copy"}
- {t("config.collaborator_token_label")}
+ Collaborator token
{clientTokenVisible()
? hostInfo()?.clientToken || "—"
@@ -383,8 +382,8 @@ export default function ConfigView(props: ConfigViewProps) {
{hostRemoteAccessEnabled()
- ? t("config.collaborator_token_remote_hint")
- : t("config.collaborator_token_disabled_hint")}
+ ? "Routine remote access for phones or laptops connecting to this server."
+ : "Stored in advance for remote sharing, but remote access is currently disabled."}
@@ -394,7 +393,7 @@ export default function ConfigView(props: ConfigViewProps) {
onClick={() => setClientTokenVisible((prev) => !prev)}
disabled={!hostInfo()?.clientToken}
>
- {clientTokenVisible() ? t("common.hide") : t("common.show")}
+ {clientTokenVisible() ? "Hide" : "Show"}
handleCopy(hostInfo()?.clientToken ?? "", "client-token")}
disabled={!hostInfo()?.clientToken}
>
- {copyingField() === "client-token" ? t("config.copied") : t("config.copy")}
+ {copyingField() === "client-token" ? "Copied" : "Copy"}
- {t("config.owner_token_label")}
+ Owner token
{ownerTokenVisible()
? hostInfo()?.ownerToken || "—"
@@ -419,8 +418,8 @@ export default function ConfigView(props: ConfigViewProps) {
{hostRemoteAccessEnabled()
- ? t("config.owner_token_remote_hint")
- : t("config.owner_token_disabled_hint")}
+ ? "Use this when a remote client needs to answer permission prompts or take owner-only actions."
+ : "Only relevant after you enable remote access for this worker."}
@@ -430,7 +429,7 @@ export default function ConfigView(props: ConfigViewProps) {
onClick={() => setOwnerTokenVisible((prev) => !prev)}
disabled={!hostInfo()?.ownerToken}
>
- {ownerTokenVisible() ? t("common.hide") : t("common.show")}
+ {ownerTokenVisible() ? "Hide" : "Show"}
handleCopy(hostInfo()?.ownerToken ?? "", "owner-token")}
disabled={!hostInfo()?.ownerToken}
>
- {copyingField() === "owner-token" ? t("config.copied") : t("config.copy")}
+ {copyingField() === "owner-token" ? "Copied" : "Copy"}
- {t("config.host_admin_token_label")}
+ Host admin token
{hostTokenVisible()
? hostInfo()?.hostToken || "—"
@@ -453,7 +452,7 @@ export default function ConfigView(props: ConfigViewProps) {
? "••••••••••••"
: "—"}
- {t("config.host_admin_token_hint")}
+ Internal host-only token for approvals CLI and admin APIs. Do not use this in the remote app connect flow.
setHostTokenVisible((prev) => !prev)}
disabled={!hostInfo()?.hostToken}
>
- {hostTokenVisible() ? t("common.hide") : t("common.show")}
+ {hostTokenVisible() ? "Hide" : "Show"}
handleCopy(hostInfo()?.hostToken ?? "", "host-token")}
disabled={!hostInfo()?.hostToken}
>
- {copyingField() === "host-token" ? t("config.copied") : t("config.copy")}
+ {copyingField() === "host-token" ? "Copied" : "Copy"}
- {t("config.server_sharing_menu_hint")}
+ For per-workspace sharing links, use Share... in the workspace menu.
@@ -485,9 +484,9 @@ export default function ConfigView(props: ConfigViewProps) {
- {t("config.server_section_title")}
+ OpenWork server
- {t("config.server_section_desc")}
+ Connect to an OpenWork server. Use the URL plus a collaborator or owner token from your server admin.
{openworkStatusLabel()}
@@ -495,22 +494,22 @@ export default function ConfigView(props: ConfigViewProps) {
setOpenworkUrl(event.currentTarget.value)}
placeholder="http://127.0.0.1:"
- hint={t("config.server_url_hint")}
+ hint="Use the URL shared by your OpenWork server. Local desktop workers reuse a persistent high port in the 48000-51000 range."
disabled={props.busy}
/>
- {t("config.resolved_worker_url")}{resolvedWorkspaceUrl() || t("config.not_set")}
- {t("config.worker_id")}{resolvedWorkspaceId() || t("config.unavailable")}
+ Resolved worker URL: {resolvedWorkspaceUrl() || "Not set"}
+ Worker ID: {resolvedWorkspaceId() || "Unavailable"}
@@ -545,27 +544,27 @@ export default function ConfigView(props: ConfigViewProps) {
const ok = await props.testOpenworkServerConnection(next);
setOpenworkTestState(ok ? "success" : "error");
setOpenworkTestMessage(
- ok ? t("config.connection_successful") : t("config.connection_failed"),
+ ok ? "Connection successful." : "Connection failed. Check the host URL and token.",
);
} catch (error) {
- const message = error instanceof Error ? error.message : t("config.connection_failed_check");
+ const message = error instanceof Error ? error.message : "Connection failed.";
setOpenworkTestState("error");
setOpenworkTestMessage(message);
}
}}
disabled={props.busy || openworkTestState() === "testing"}
>
- {openworkTestState() === "testing" ? t("config.testing") : t("config.test_connection")}
+ {openworkTestState() === "testing" ? "Testing..." : "Test connection"}
props.updateOpenworkServerSettings(buildOpenworkSettings())}
disabled={props.busy || !hasOpenworkChanges()}
>
- {t("common.save")}
+ Save
- {t("common.reset")}
+ Reset
@@ -581,25 +580,25 @@ export default function ConfigView(props: ConfigViewProps) {
role="status"
aria-live="polite"
>
- {openworkTestState() === "testing" ? t("config.testing_connection") : openworkTestMessage() ?? t("config.connection_status_updated")}
+ {openworkTestState() === "testing" ? "Testing connection..." : openworkTestMessage() ?? "Connection status updated."}
-
- {t("config.server_needed_hint")}
+
+ OpenWork server connection needed to sync skills, plugins, and commands.
- {t("config.messaging_identities_title")}
+ Messaging identities
- {t("config.messaging_identities_desc")}
+ Manage Telegram/Slack identities and routing in the Identities tab.
- {t("config.desktop_only_hint")}
+ Some config features (local server sharing + messaging bridge) require the desktop app.
diff --git a/apps/app/src/app/pages/extensions.tsx b/apps/app/src/app/pages/extensions.tsx
index 3fd5fa489..34ef3a157 100644
--- a/apps/app/src/app/pages/extensions.tsx
+++ b/apps/app/src/app/pages/extensions.tsx
@@ -7,7 +7,6 @@ import McpView from "../connections/mcp-view";
import { useConnections } from "../connections/provider";
import { useExtensions } from "../extensions/provider";
import PluginsView, { type PluginsViewProps } from "./plugins";
-import { t } from "../../i18n";
export type ExtensionsSection = "all" | "mcp" | "plugins";
@@ -65,9 +64,9 @@ export default function ExtensionsView(props: ExtensionsViewProps) {
- {t("extensions.title")}
+ Extensions
- {t("extensions.subtitle")}
+ Apps (MCP) and OpenCode plugins live in one place.
@@ -75,7 +74,7 @@ export default function ExtensionsView(props: ExtensionsViewProps) {
- {connectedAppsCount()} {connectedAppsCount() === 1 ? t("extensions.app_count_one") : t("extensions.app_count_many")}
+ {connectedAppsCount()} app{connectedAppsCount() === 1 ? "" : "s"} connected
@@ -83,7 +82,7 @@ export default function ExtensionsView(props: ExtensionsViewProps) {
- {pluginCount()} {pluginCount() === 1 ? t("extensions.plugin_count_one") : t("extensions.plugin_count_many")}
+ {pluginCount()} plugin{pluginCount() === 1 ? "" : "s"}
@@ -98,7 +97,7 @@ export default function ExtensionsView(props: ExtensionsViewProps) {
aria-pressed={section() === "all"}
onClick={() => selectSection("all")}
>
- {t("extensions.filter_all")}
+ All
selectSection("mcp")}
>
- {t("extensions.filter_apps")}
+ Apps
selectSection("plugins")}
>
- {t("extensions.filter_plugins")}
+ Plugins
- {t("common.refresh")}
+ Refresh
@@ -129,7 +128,7 @@ export default function ExtensionsView(props: ExtensionsViewProps) {
- {t("extensions.apps_mcp_header")}
+ Apps (MCP)
- {t("extensions.plugins_opencode_header")}
+ Plugins (OpenCode)
props.openworkServerStatus === "connected" && Boolean(openworkServerClient()));
const scopedWorkspaceReady = createMemo(() => Boolean(workspaceId()));
- const defaultRoutingDirectory = createMemo(() => props.selectedWorkspaceRoot.trim() || t("identities.not_set"));
+ const defaultRoutingDirectory = createMemo(() => props.selectedWorkspaceRoot.trim() || "Not set");
let lastResetKey = "";
const statusLabel = createMemo(() => {
- if (healthError()) return t("identities.health_unavailable");
+ if (healthError()) return "Unavailable";
const snapshot = health();
- if (!snapshot) return t("identities.health_unknown");
- return snapshot.ok ? t("identities.health_running") : t("identities.health_offline");
+ if (!snapshot) return "Unknown";
+ return snapshot.ok ? "Running" : "Offline";
});
const isWorkerOnline = createMemo(() => {
@@ -268,13 +266,13 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
const ts = lastActivityAt();
if (!ts) return "\u2014";
const elapsedMs = Math.max(0, Date.now() - ts);
- if (elapsedMs < 60_000) return t("identities.just_now");
+ if (elapsedMs < 60_000) return "Just now";
const minutes = Math.floor(elapsedMs / 60_000);
- if (minutes < 60) return t("identities.minutes_ago", undefined, { minutes });
+ if (minutes < 60) return `${minutes}m ago`;
const hours = Math.floor(minutes / 60);
- if (hours < 24) return t("identities.hours_ago", undefined, { hours });
+ if (hours < 24) return `${hours}h ago`;
const days = Math.floor(hours / 24);
- return t("identities.days_ago", undefined, { days });
+ return `${days}d ago`;
});
const workspaceAgentStatus = createMemo(() => {
@@ -304,7 +302,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
const id = workspaceId();
if (!id) {
resetAgentState();
- setAgentError(t("identities.agent_worker_scope_unavailable"));
+ setAgentError("Worker scope unavailable.");
return;
}
const client = openworkServerClient();
@@ -353,7 +351,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
setAgentContent(OPENCODE_ROUTER_AGENT_FILE_TEMPLATE);
setAgentDraft(OPENCODE_ROUTER_AGENT_FILE_TEMPLATE);
setAgentBaseUpdatedAt(typeof result.updatedAt === "number" ? result.updatedAt : null);
- setAgentStatus(t("identities.agent_created"));
+ setAgentStatus("Created default messaging agent file.");
} catch (error) {
setAgentError(formatRequestError(error));
} finally {
@@ -381,10 +379,10 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
setAgentExists(true);
setAgentContent(agentDraft());
setAgentBaseUpdatedAt(typeof result.updatedAt === "number" ? result.updatedAt : null);
- setAgentStatus(t("identities.agent_saved"));
+ setAgentStatus("Saved messaging behavior.");
} catch (error) {
if (error instanceof OpenworkServerError && error.status === 409) {
- setAgentError(t("identities.agent_file_changed"));
+ setAgentError("File changed remotely. Reload and save again.");
} else {
setAgentError(formatRequestError(error));
}
@@ -416,7 +414,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
...(sendAutoBind() ? { autoBind: true } : {}),
});
setSendResult(result);
- const base = t("identities.dispatched_messages", undefined, { sent: result.sent, attempted: result.attempted });
+ const base = `Dispatched ${result.sent}/${result.attempted} messages.`;
setSendStatus(result.reason?.trim() ? `${base} ${result.reason.trim()}` : base);
} catch (error) {
setSendError(formatRequestError(error));
@@ -445,9 +443,9 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
setTelegramBotUsername(null);
setTelegramPairingCode(null);
setSlackIdentities([]);
- setHealthError(t("identities.worker_scope_unavailable_detail"));
- setTelegramIdentitiesError(t("identities.worker_scope_unavailable"));
- setSlackIdentitiesError(t("identities.worker_scope_unavailable"));
+ setHealthError("Worker scope unavailable. Reconnect using a worker URL or switch to a known worker.");
+ setTelegramIdentitiesError("Worker scope unavailable.");
+ setSlackIdentitiesError("Worker scope unavailable.");
resetAgentState();
setSendStatus(null);
setSendError(null);
@@ -493,7 +491,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
const message =
(healthRes.json && typeof (healthRes.json as any).message === "string")
? String((healthRes.json as any).message)
- : t("identities.health_unavailable_status", undefined, { status: healthRes.status });
+ : `OpenCodeRouter health unavailable (${healthRes.status})`;
setHealthError(message);
}
setMessagingRestartRequired(true);
@@ -507,14 +505,14 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
} else {
setTelegramIdentities([]);
setTelegramPairingCode(null);
- setTelegramIdentitiesError(t("identities.telegram_unavailable"));
+ setTelegramIdentitiesError("Telegram identities unavailable.");
}
if (isOpenCodeRouterIdentities(slackRes)) {
setSlackIdentities(slackRes.items ?? []);
} else {
setSlackIdentities([]);
- setSlackIdentitiesError(t("identities.slack_unavailable"));
+ setSlackIdentitiesError("Slack identities unavailable.");
}
if (!agentDirty() && !agentSaving()) {
@@ -544,13 +542,13 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
const ok = await props.reconnectOpenworkServer();
if (!ok) {
- setReconnectError(t("identities.reconnect_failed"));
+ setReconnectError("Reconnect failed. Check OpenWork URL/token and try again.");
return;
}
- setReconnectStatus(t("identities.reconnected_refreshing"));
+ setReconnectStatus("Reconnected. Refreshing worker state...");
await refreshAll({ force: true });
- setReconnectStatus(t("identities.reconnected"));
+ setReconnectStatus("Reconnected.");
};
const enableMessagingModule = async () => {
@@ -577,7 +575,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
setMessagingRiskOpen(false);
setMessagingRestartAction("enable");
setMessagingRestartPromptOpen(true);
- setMessagingStatus(t("identities.messaging_enabled_restart"));
+ setMessagingStatus("Messaging enabled. Restart this worker to apply before configuring channels.");
await refreshAll({ force: true });
} catch (error) {
setMessagingError(formatRequestError(error));
@@ -610,7 +608,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
setMessagingRestartRequired(true);
setMessagingRestartAction("disable");
setMessagingRestartPromptOpen(true);
- setMessagingStatus(t("identities.messaging_disabled_restart"));
+ setMessagingStatus("Messaging disabled. Restart this worker to stop the messaging sidecar.");
await refreshAll({ force: true });
} catch (error) {
setMessagingError(formatRequestError(error));
@@ -627,14 +625,14 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
try {
const ok = await props.restartLocalServer();
if (!ok) {
- setMessagingError(t("identities.restart_failed"));
+ setMessagingError("Restart failed. Please restart the worker from Settings and try again.");
return;
}
setMessagingRestartPromptOpen(false);
setMessagingRestartRequired(false);
- setMessagingStatus(t("identities.worker_restarted_refreshing"));
+ setMessagingStatus("Worker restarted. Refreshing messaging status...");
await refreshAll({ force: true });
- setMessagingStatus(t("identities.worker_restarted"));
+ setMessagingStatus("Worker restarted.");
} catch (error) {
setMessagingError(formatRequestError(error));
} finally {
@@ -666,7 +664,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
const pairingCode = typeof result.telegram?.pairingCode === "string" ? result.telegram.pairingCode.trim() : "";
if (access === "private" && pairingCode) {
setTelegramPairingCode(pairingCode);
- setTelegramStatus(t("identities.telegram_private_saved_pair", undefined, { code: pairingCode }));
+ setTelegramStatus(`Private bot saved. Pair via /pair ${pairingCode}`);
} else {
setTelegramPairingCode(null);
}
@@ -675,15 +673,15 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
const normalized = String(username).trim().replace(/^@+/, "");
setTelegramBotUsername(normalized || null);
if (access !== "private" || !pairingCode) {
- setTelegramStatus(t("identities.telegram_saved_username", undefined, { username: normalized || String(username) }));
+ setTelegramStatus(`Saved (@${normalized || String(username)})`);
}
} else {
if (access !== "private" || !pairingCode) {
- setTelegramStatus(result.applied === false ? t("identities.telegram_saved_pending") : t("identities.telegram_saved"));
+ setTelegramStatus(result.applied === false ? "Saved (pending apply)." : "Saved.");
}
}
} else {
- setTelegramError(t("identities.telegram_save_failed"));
+ setTelegramError("Failed to save.");
}
if (typeof result.applyError === "string" && result.applyError.trim()) {
setTelegramError(result.applyError.trim());
@@ -714,9 +712,9 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
if (result.ok) {
setTelegramBotUsername(null);
setTelegramPairingCode(null);
- setTelegramStatus(result.applied === false ? t("identities.telegram_deleted_pending") : t("identities.telegram_deleted"));
+ setTelegramStatus(result.applied === false ? "Deleted (pending apply)." : "Deleted.");
} else {
- setTelegramError(t("identities.telegram_delete_failed"));
+ setTelegramError("Failed to delete.");
}
if (typeof result.applyError === "string" && result.applyError.trim()) {
setTelegramError(result.applyError.trim());
@@ -734,9 +732,9 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
if (!code) return;
try {
await navigator.clipboard.writeText(code);
- setTelegramStatus(t("identities.pairing_code_copied"));
+ setTelegramStatus("Pairing code copied.");
} catch {
- setTelegramError(t("identities.pairing_code_copy_failed"));
+ setTelegramError("Could not copy pairing code. Copy it manually.");
}
};
@@ -758,9 +756,9 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
try {
const result = await client.upsertOpenCodeRouterSlackIdentity(id, { botToken, appToken, enabled: slackEnabled() });
if (result.ok) {
- setSlackStatus(result.applied === false ? t("identities.telegram_saved_pending") : t("identities.telegram_saved"));
+ setSlackStatus(result.applied === false ? "Saved (pending apply)." : "Saved.");
} else {
- setSlackError(t("identities.telegram_save_failed"));
+ setSlackError("Failed to save.");
}
if (typeof result.applyError === "string" && result.applyError.trim()) {
setSlackError(result.applyError.trim());
@@ -790,9 +788,9 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
try {
const result = await client.deleteOpenCodeRouterSlackIdentity(id, identityId);
if (result.ok) {
- setSlackStatus(result.applied === false ? t("identities.telegram_deleted_pending") : t("identities.telegram_deleted"));
+ setSlackStatus(result.applied === false ? "Deleted (pending apply)." : "Deleted.");
} else {
- setSlackError(t("identities.telegram_delete_failed"));
+ setSlackError("Failed to delete.");
}
if (typeof result.applyError === "string" && result.applyError.trim()) {
setSlackError(result.applyError.trim());
@@ -857,7 +855,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
- {t("identities.title")}
+ Messaging channels
- {t("identities.repair_reconnect")}
+ Repair & reconnect
- {t("common.refresh")}
+ Refresh
- {t("identities.subtitle")}
+ Let people reach your worker through messaging apps. Connect a channel and
+ your worker will automatically read and respond to messages.
- {t("identities.workspace_scope_prefix")} {scopedOpenworkBaseUrl().trim() || props.openworkServerUrl.trim() || t("identities.not_set")}
+ Workspace scope: {scopedOpenworkBaseUrl().trim() || props.openworkServerUrl.trim() || "Not set"}
{(value) => {value()}}
@@ -905,9 +904,9 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
{/* ---- Not connected to server ---- */}
- {t("identities.connect_server_title")}
+ Connect to an OpenWork server
- {t("identities.connect_server_desc")}
+ Identities are available when you are connected to an OpenWork host (openwork).
@@ -915,7 +914,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
- {t("identities.workspace_id_required")}
+ Workspace ID is required to manage identities. Reconnect with a workspace URL (for example: /w/<workspace-id>) or select a workspace mapped on this host.
@@ -930,7 +929,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
}`}
onClick={() => setActiveTab("general")}
>
- {t("identities.tab_general")}
+ General
setActiveTab("advanced")}
>
- {t("settings.tab_advanced")}
+ Advanced
setMessagingDisableConfirmOpen(true)}
>
- {t("identities.disable_messaging")}
+ Disable messaging
- {t("identities.messaging_disabled_title")}
+ Messaging is disabled by default
- {t("identities.messaging_disabled_risk")}
+ Messaging bots can execute actions against your local worker. If exposed publicly, they may allow access
+ to files, credentials, and API keys available to this worker.
- {t("identities.messaging_disabled_hint")}
+ Enable messaging only if you understand the risk and plan to secure access (for example, private Telegram
+ pairing).
setMessagingRiskOpen(true)}
>
- {messagingSaving() ? t("identities.enabling") : t("identities.enable_messaging")}
+ {messagingSaving() ? "Enabling..." : "Enable messaging"}
@@ -980,7 +981,8 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
- {t("identities.messaging_sidecar_not_running")}
+ Messaging is enabled in this workspace, but the messaging sidecar is not running yet. Restart this worker,
+ then return to Messaging settings to connect Telegram or Slack.
void restartMessagingWorker()}
>
- {messagingRestartBusy() ? t("identities.restarting") : t("identities.restart_worker")}
+ {messagingRestartBusy() ? "Restarting..." : "Restart worker"}
@@ -1007,7 +1009,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
- {isWorkerOnline() ? t("identities.worker_online") : healthError() ? t("identities.worker_unavailable") : t("identities.worker_offline")}
+ {isWorkerOnline() ? "Worker online" : healthError() ? "Worker unavailable" : "Worker offline"}
0}
/>
0}
/>
@@ -1051,7 +1053,7 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
{/* ---- Available channels ---- */}
- {t("identities.available_channels")}
+ Available channels
@@ -1075,12 +1077,12 @@ export default function IdentitiesView(props: IdentitiesViewProps) {
Telegram
- {t("identities.connected_badge")}
+ Connected
- {t("identities.telegram_desc")}
+ Connect a Telegram bot in public mode (open inbox) or private mode (pairing code required).
- {item.enabled ? t("identities.enabled_label") : t("identities.disabled_label")} · {item.running ? t("identities.running_label") : t("identities.stopped_label")} · {item.access === "private" ? t("identities.private_label") : t("identities.public_label")}
+ {item.enabled ? "Enabled" : "Disabled"} · {item.running ? "Running" : "Stopped"} · {item.access === "private" ? "Private" : "Public"}
/newbot.
+ Open @BotFather and run /newbot.
/pair <code>.
+ Choose Public for open inbox or Private to require /pair <code>.
/pair {code()}.
+ In Telegram, open the chat that should control this worker and send /pair {code()}.
- {t("identities.slack_intro")} + Connect your Slack workspace to let team members interact with this worker in channels and DMs.
- {t("identities.message_routing_desc")} + Control which conversations go to which workspace folder. Messages are + routed to the worker's default folder unless you set up rules here.
/dir <path> {t("identities.routing_override_suffix")}
+ Advanced: reply with /dir <path> in Slack/Telegram to override the directory for a specific chat (limited to this workspace root).
@agent <id> to route via a specific OpenCode agent.
- {t("session.loading_detail")} + Pulling in the latest messages for this task.
{props.safeStringify(props.activePermission?.metadata)}
@@ -3812,7 +3812,7 @@ export default function SessionView(props: SessionViewProps) {
}
disabled={props.permissionReplyBusy}
>
- {t("session.deny")}
+ Deny
- {t("session.allow_once")}
+ Once
- {t("session.allow_for_session")}
+ Allow for session
{runtimeDebugReportJson()}
@@ -2661,10 +2689,11 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.sandbox_probe_title")}
+ Sandbox probe
- {translate("settings.sandbox_probe_desc")}
+ Runs a temporary Docker sandbox startup check and
+ captures inspect/log output.
{sandboxProbeBusy()
- ? translate("settings.running_probe")
- : translate("settings.run_sandbox_probe")}
+ ? "Running probe..."
+ : "Run sandbox probe"}
{(result) => (
- {t("settings.sandbox_run_id", undefined, { id: result().runId ?? "—" })}
+ Run ID:{" "}
+ {result().runId}
- {t("settings.sandbox_result", undefined, { status: result().ready ? translate("settings.sandbox_ready") : translate("settings.sandbox_error") })}
+ Result: {result().ready ? "ready" : "error"}
{(err) => {err()}}
@@ -2708,7 +2738,8 @@ export default function SettingsView(props: SettingsViewProps) {
)}
- {translate("settings.sandbox_export_hint")}
+ Use Export in Runtime debug report above to
+ save this probe output with logs.
@@ -2716,7 +2747,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.startup_title")}
+ Startup
@@ -2744,7 +2775,7 @@ export default function SettingsView(props: SettingsViewProps) {
onClick={props.stopHost}
disabled={props.busy}
>
- {translate("settings.switch")}
+ Switch
@@ -2753,7 +2784,7 @@ export default function SettingsView(props: SettingsViewProps) {
class="w-full justify-between group"
onClick={props.onResetStartupPreference}
>
- {translate("settings.reset_startup_pref")}
+ Reset startup preference
- {translate("settings.startup_reset_hint")}
+ This clears your saved preference and shows the connection
+ choice on next launch.
@@ -2773,20 +2805,21 @@ export default function SettingsView(props: SettingsViewProps) {
>
- {translate("settings.engine_title")}
+ Engine
- {translate("settings.engine_desc")}
+ Choose how OpenCode runs locally.
- {translate("settings.startup_remote_warning")}
+ Startup preference is currently remote. Engine settings
+ are saved now and apply the next time you run locally.
- {translate("settings.engine_source_debug")}
+ Engine source
props.setEngineSource("sidecar")}
disabled={props.busy}
>
- {translate("settings.engine_bundled")}
+ Bundled (recommended)
props.setEngineSource("path")}
disabled={props.busy}
>
- {translate("settings.engine_system_path")}
+ System install (PATH)
props.setEngineSource("custom")}
disabled={props.busy}
>
- {translate("settings.engine_custom_binary")}
+ Custom binary
- {translate("settings.engine_bundled_hint")}
+ Bundled engine is the most reliable option. Use System
+ install only if you manage OpenCode yourself.
@@ -2842,7 +2876,7 @@ export default function SettingsView(props: SettingsViewProps) {
>
- {translate("settings.custom_binary_label")}
+ Custom OpenCode binary
- {translate("settings.choose")}
+ Choose
- {translate("settings.clear")}
+ Clear
- {translate("settings.custom_binary_hint")}
+ Use this to point OpenWork at a local OpenCode build
+ (e.g. your fork). Applies next time the engine starts
+ or reloads.
- {translate("settings.engine_runtime_label")}
+ Engine runtime
props.setEngineRuntime("direct")}
disabled={props.busy}
>
- {translate("settings.runtime_direct")}
+ Direct (OpenCode)
- {translate("settings.runtime_orchestrator")}
+ OpenWork Orchestrator
- {translate("settings.runtime_applies_hint")}
+ Applies the next time the engine starts or reloads.
@@ -2921,18 +2957,18 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.reset_recovery_title")}
+ Reset & Recovery
- {translate("settings.reset_recovery_desc")}
+ Clear data or restart the setup flow.
- {translate("settings.reset_onboarding_title")}
+ Reset onboarding
- {translate("settings.reset_onboarding_description")}
+ Clears OpenWork preferences and restarts the app.
- {translate("settings.reset_button")}
+ Reset
- {translate("settings.reset_app_data_title")}
+ Reset app data
- {translate("settings.reset_app_data_description")}
+ More aggressive. Clears OpenWork cache + app data.
- {translate("settings.reset_button")}
+ Reset
- {translate("settings.reset_requires_confirm")}
+ Requires typing{" "}
+ RESET and will
+ restart the app.
- {translate("settings.devtools_title")}
+ Devtools
- {translate("settings.devtools_desc")}
+ Sidecar health, capabilities, and audit trail.
- {translate("settings.service_restarts_title")}
+ Service restarts
- {translate("settings.service_restarts_desc")}
+ Restart specific host services without leaving this
+ screen.
@@ -3013,8 +3052,8 @@ export default function SettingsView(props: SettingsViewProps) {
class={`w-3.5 h-3.5 mr-1.5 ${openworkRestartBusy() ? "animate-spin" : ""}`}
/>
{openworkRestartBusy()
- ? translate("settings.restarting")
- : translate("settings.restart_orchestrator")}
+ ? "Restarting..."
+ : "Restart orchestrator"}
{opencodeRestarting()
- ? translate("settings.restarting")
- : translate("settings.restart_opencode")}
+ ? "Restarting..."
+ : "Restart OpenCode"}
{openworkServerRestarting()
- ? translate("settings.restarting")
- : translate("settings.restart_openwork_server")}
+ ? "Restarting..."
+ : "Restart OpenWork server"}
{opencodeRouterRestarting()
- ? translate("settings.restarting")
- : translate("settings.restart_opencode_router")}
+ ? "Restarting..."
+ : "Restart OpenCodeRouter"}
@@ -3086,30 +3125,30 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.versions_title")}
+ Versions
- {translate("settings.versions_desc")}
+ Sidecar + desktop build info.
- {t("settings.debug_desktop_app", undefined, { version: appVersionLabel() })}
+ Desktop app: {appVersionLabel()}
- {t("settings.debug_commit", undefined, { commit: appCommitLabel() })}
+ Commit: {appCommitLabel()}
- {t("settings.debug_orchestrator_version", undefined, { version: orchestratorVersionLabel() })}
+ Orchestrator: {orchestratorVersionLabel()}
- {t("settings.debug_opencode_version", undefined, { version: opencodeVersionLabel() })}
+ OpenCode: {opencodeVersionLabel()}
- {t("settings.debug_openwork_server_version", undefined, { version: openworkServerVersionLabel() })}
+ OpenWork server: {openworkServerVersionLabel()}
- {t("settings.debug_opencode_router_version", undefined, { version: opencodeRouterVersionLabel() })}
+ OpenCodeRouter: {opencodeRouterVersionLabel()}
@@ -3118,10 +3157,10 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.opencode_sdk_title")}
+ OpenCode engine
- {translate("settings.opencode_engine_sidecar_desc")}
+ Local execution sidecar.
- {props.engineInfo?.baseUrl ?? translate("settings.base_url_unavailable")}
+ {props.engineInfo?.baseUrl ?? "Base URL unavailable"}
{props.engineInfo?.projectDir ??
- translate("settings.no_project_directory")}
+ "No project directory"}
- {t("settings.diag_pid", undefined, { pid: String(props.engineInfo?.pid ?? "—") })}
+ PID: {props.engineInfo?.pid ?? "—"}
- {translate("settings.last_stdout")}
+ Last stdout
{engineStdout()}
@@ -3153,7 +3192,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.last_stderr")}
+ Last stderr
{engineStderr()}
@@ -3166,10 +3205,10 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.orchestrator_daemon_title")}
+ Orchestrator daemon
- {translate("settings.orchestrator_daemon_layer_desc")}
+ Workspace orchestration layer.
{props.orchestratorStatus?.dataDir ??
- translate("settings.data_dir_unavailable")}
+ "Data directory unavailable"}
- {t("settings.diag_daemon_url", undefined, { url: props.orchestratorStatus?.daemon?.baseUrl ?? "—" })}
+ Daemon:{" "}
+ {props.orchestratorStatus?.daemon?.baseUrl ?? "—"}
- {t("settings.diag_opencode_url", undefined, { url: props.orchestratorStatus?.opencode?.baseUrl ?? "—" })}
+ OpenCode:{" "}
+ {props.orchestratorStatus?.opencode?.baseUrl ?? "—"}
- {t("settings.diag_version", undefined, { version: props.orchestratorStatus?.cliVersion ?? "—" })}
+ Version: {props.orchestratorStatus?.cliVersion ?? "—"}
- {t("settings.diag_sidecar", undefined, { info: orchestratorSidecarSummary() })}
+ Sidecar: {orchestratorSidecarSummary()}
- {t("settings.diag_opencode_binary", undefined, { binary: formatOrchestratorBinary(props.orchestratorStatus?.binaries?.opencode ?? null) })}
+ Opencode binary:{" "}
+ {formatOrchestratorBinary(
+ props.orchestratorStatus?.binaries?.opencode ??
+ null,
+ )}
- {t("settings.diag_runtime_workspace", undefined, { id: props.orchestratorStatus?.activeId ?? "—" })}
+ Runtime workspace:{" "}
+ {props.orchestratorStatus?.activeId ?? "—"}
- {translate("settings.last_error")}
+ Last error
{props.orchestratorStatus?.lastError}
@@ -3221,10 +3267,10 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.opencode_sdk_title")}
+ OpenCode SDK
- {translate("settings.opencode_sdk_desc")}
+ UI connection diagnostics.
{props.opencodeConnectStatus?.baseUrl ??
- translate("settings.opencode_url_unavailable")}
+ "Base URL unavailable"}
{props.opencodeConnectStatus?.directory ??
- translate("settings.no_worker_directory")}
+ "No project directory"}
- {t("settings.diag_last_attempt", undefined, { time: opencodeConnectTimestamp() ?? "—" })}
+ Last attempt: {opencodeConnectTimestamp() ?? "—"}
- {t("settings.diag_reason", undefined, { reason: props.opencodeConnectStatus?.reason ?? "" })}
+ Reason: {props.opencodeConnectStatus?.reason}
@@ -3255,29 +3301,41 @@ export default function SettingsView(props: SettingsViewProps) {
- {t("settings.diag_healthy_ms", undefined, { ms: String(Math.round(metrics().healthyMs as number)) })}
+ Healthy:{" "}
+ {Math.round(metrics().healthyMs as number)}ms
- {t("settings.diag_load_sessions_ms", undefined, { ms: String(Math.round(metrics().loadSessionsMs as number)) })}
+ Load sessions:{" "}
+ {Math.round(
+ metrics().loadSessionsMs as number,
+ )}
+ ms
- {t("settings.diag_pending_permissions_ms", undefined, { ms: String(Math.round(metrics().pendingPermissionsMs as number)) })}
+ Pending permissions:{" "}
+ {Math.round(
+ metrics().pendingPermissionsMs as number,
+ )}
+ ms
- {t("settings.diag_providers_ms", undefined, { ms: String(Math.round(metrics().providersMs as number)) })}
+ Providers:{" "}
+ {Math.round(metrics().providersMs as number)}
+ ms
- {t("settings.diag_total_ms", undefined, { ms: String(Math.round(metrics().totalMs as number)) })}
+ Total:{" "}
+ {Math.round(metrics().totalMs as number)}ms
@@ -3287,7 +3345,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.last_error")}
+ Last error
{props.opencodeConnectStatus?.error}
@@ -3300,10 +3358,10 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.openwork_server_label")}
+ OpenWork server
- {translate("settings.openwork_config_sidecar_desc")}
+ Config and approvals sidecar.
{(props.openworkServerHostInfo?.baseUrl ??
props.openworkServerUrl) ||
- translate("settings.base_url_unavailable")}
+ "Base URL unavailable"}
- {t("settings.diag_pid", undefined, { pid: String(props.openworkServerHostInfo?.pid ?? "—") })}
+ PID: {props.openworkServerHostInfo?.pid ?? "—"}
- {translate("settings.last_stdout")}
+ Last stdout
{openworkStdout()}
@@ -3333,7 +3391,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.last_stderr")}
+ Last stderr
{openworkStderr()}
@@ -3346,10 +3404,10 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.opencode_router_sidecar")}
+ OpenCodeRouter sidecar
- {translate("settings.messaging_bridge_service")}
+ Messaging bridge service.
{props.opencodeRouterInfo?.opencodeUrl?.trim() ||
- translate("settings.opencode_url_unavailable")}
+ "OpenCode URL unavailable"}
{props.opencodeRouterInfo?.workspacePath?.trim() ||
- translate("settings.no_worker_directory")}
+ "No worker directory"}
- {t("settings.diag_health_port", undefined, { port: String(props.opencodeRouterInfo?.healthPort ?? "—") })}
+ Health port:{" "}
+ {props.opencodeRouterInfo?.healthPort ?? "—"}
- {t("settings.diag_pid", undefined, { pid: String(props.opencodeRouterInfo?.pid ?? "—") })}
+ PID: {props.opencodeRouterInfo?.pid ?? "—"}
@@ -3387,8 +3446,8 @@ export default function SettingsView(props: SettingsViewProps) {
class={`w-3.5 h-3.5 mr-1.5 ${opencodeRouterRestarting() ? "animate-spin" : ""}`}
/>
{opencodeRouterRestarting()
- ? translate("settings.restarting")
- : translate("settings.restart_opencode_router")}
+ ? "Restarting..."
+ : "Restart"}
- {translate("settings.stop_local_server")}
+ Stop
@@ -3409,7 +3468,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.last_stdout")}
+ Last stdout
{opencodeRouterStdout()}
@@ -3417,7 +3476,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.last_stderr")}
+ Last stderr
{opencodeRouterStderr()}
@@ -3430,7 +3489,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.openwork_diagnostics_title")}
+ OpenWork server diagnostics
{props.openworkServerDiagnostics?.version ?? "—"}
@@ -3440,32 +3499,33 @@ export default function SettingsView(props: SettingsViewProps) {
when={props.openworkServerDiagnostics}
fallback={
- {translate("settings.diagnostics_unavailable")}
+ Diagnostics unavailable.
}
>
{(diag) => (
- {t("settings.diag_started", undefined, { time: formatUptime(diag().uptimeMs) })}
+ Started: {formatUptime(diag().uptimeMs)}
- {t("settings.diag_read_only", undefined, { value: diag().readOnly ? "true" : "false" })}
+ Read-only: {diag().readOnly ? "true" : "false"}
- {t("settings.diag_approval", undefined, { mode: diag().approval.mode, ms: String(diag().approval.timeoutMs) })}
+ Approval: {diag().approval.mode} (
+ {diag().approval.timeoutMs}ms)
- {t("settings.diag_workspaces", undefined, { count: String(diag().workspaceCount) })}
+ Workspaces: {diag().workspaceCount}
- {t("settings.diag_selected_workspace", undefined, { id: diag().selectedWorkspaceId ?? "—" })}
+ Selected workspace: {diag().selectedWorkspaceId ?? "—"}
- {t("settings.diag_runtime_workspace", undefined, { id: diag().activeWorkspaceId ?? "—" })}
+ Runtime workspace: {diag().activeWorkspaceId ?? "—"}
- {t("settings.diag_config_path", undefined, { path: diag().server.configPath ?? translate("settings.diag_default") })}
+ Config path: {diag().server.configPath ?? "default"}
- {t("settings.diag_token_source", undefined, { source: diag().tokenSource.client })}
+ Token source: {diag().tokenSource.client}
- {t("settings.diag_host_token_source", undefined, { source: diag().tokenSource.host })}
+ Host token source: {diag().tokenSource.host}
)}
@@ -3475,67 +3535,65 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.capabilities_title")}
+ OpenWork server capabilities
{props.runtimeWorkspaceId
- ? t("settings.worker_id_label", undefined, { id: props.runtimeWorkspaceId })
- : translate("settings.worker_unresolved")}
+ ? `Worker ${props.runtimeWorkspaceId}`
+ : "Worker unresolved"}
- {translate("settings.capabilities_unavailable")}
+ Capabilities unavailable. Connect with a client token.
}
>
{(caps) => (
- {t("settings.cap_skills", undefined, { value: formatCapability(caps().skills) })}
- {t("settings.cap_plugins", undefined, { value: formatCapability(caps().plugins) })}
- {t("settings.cap_mcp", undefined, { value: formatCapability(caps().mcp) })}
- {t("settings.cap_commands", undefined, { value: formatCapability(caps().commands) })}
- {t("settings.cap_config", undefined, { value: formatCapability(caps().config) })}
+ Skills: {formatCapability(caps().skills)}
+ Plugins: {formatCapability(caps().plugins)}
+ MCP: {formatCapability(caps().mcp)}
- {t("settings.cap_proxy", undefined, {
- value: caps().proxy?.opencodeRouter
- ? translate("settings.enabled")
- : translate("settings.disabled")
- })}
+ Commands: {formatCapability(caps().commands)}
+ Config: {formatCapability(caps().config)}
- {t("settings.cap_browser_tools", undefined, {
- value: (() => {
- const browser = caps().toolProviders?.browser;
- if (!browser?.enabled) return translate("settings.disabled");
- return `${browser.mode} · ${browser.placement}`;
- })()
- })}
+ Proxy (OpenCodeRouter):{" "}
+ {caps().proxy?.opencodeRouter
+ ? "enabled"
+ : "disabled"}
- {t("settings.cap_file_tools", undefined, {
- value: (() => {
- const files = caps().toolProviders?.files;
- if (!files) return translate("config.unavailable");
- const parts = [
- files.injection ? translate("settings.cap_inbox_on") : translate("settings.cap_inbox_off"),
- files.outbox ? translate("settings.cap_outbox_on") : translate("settings.cap_outbox_off"),
- ];
- return parts.join(" · ");
- })()
- })}
+ Browser tools:{" "}
+ {(() => {
+ const browser = caps().toolProviders?.browser;
+ if (!browser?.enabled) return "disabled";
+ return `${browser.mode} · ${browser.placement}`;
+ })()}
- {t("settings.cap_sandbox", undefined, {
- value: (() => {
- const sandbox = caps().sandbox;
- return sandbox
- ? `${sandbox.backend} (${sandbox.enabled ? translate("settings.on") : translate("settings.off")})`
- : translate("config.unavailable");
- })()
- })}
+ File tools:{" "}
+ {(() => {
+ const files = caps().toolProviders?.files;
+ if (!files) return "Unavailable";
+ const parts = [
+ files.injection ? "inbox on" : "inbox off",
+ files.outbox ? "outbox on" : "outbox off",
+ ];
+ return parts.join(" · ");
+ })()}
+
+
+ Sandbox:{" "}
+ {(() => {
+ const sandbox = caps().sandbox;
+ return sandbox
+ ? `${sandbox.backend} (${sandbox.enabled ? "on" : "off"})`
+ : "Unavailable";
+ })()}
)}
@@ -3545,14 +3603,14 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.pending_permissions")}
+ Pending permissions
{props.safeStringify(props.pendingPermissions)}
- {translate("settings.recent_events")}
+ Recent events
{props.safeStringify(props.events)}
@@ -3562,7 +3620,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.workspace_debug_events_label")}
+ Workspace debug events
- {translate("settings.clear")}
+ Clear
@@ -3581,7 +3639,7 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.audit_log_title")}
+ Audit log
0}
fallback={
- {translate("settings.no_audit_entries")}
+ No audit entries yet.
}
>
@@ -3632,12 +3690,12 @@ export default function SettingsView(props: SettingsViewProps) {
- {translate("settings.reset_openwork_title")}
+ Reset OpenWork + OpenCode state
- {opencodeDevModeEnabled()
- ? translate("settings.reset_openwork_desc_dev")
- : translate("settings.reset_openwork_desc_prod")}
+ This is irreversible and deletes all local OpenWork data for the current app mode. {opencodeDevModeEnabled()
+ ? "With dev mode active, it only clears the isolated OpenCode dev state inside openwork-dev-data."
+ : "With production mode active, it only clears the standard OpenCode config, auth, cache, data, and state paths."}
{opencodeDevModeEnabled()
- ? translate("settings.dev_mode_badge")
- : translate("settings.production_mode_badge")}
+ ? "Dev mode"
+ : "Production mode"}
- {translate("settings.quit_hint")}
+ OpenWork quits immediately after cleanup so the next launch starts from a blank local state for this mode.
@@ -3666,11 +3724,11 @@ export default function SettingsView(props: SettingsViewProps) {
>
{nukeConfigBusy()
- ? translate("settings.removing_local_state")
- : translate("settings.delete_local_config")}
+ ? "Removing local state..."
+ : "Delete local config and quit"}
- {translate("settings.nuke_hint")}
+ Use this only when you want to fully reset the desktop app and its OpenCode runtime state.
diff --git a/apps/app/src/app/pages/skills.tsx b/apps/app/src/app/pages/skills.tsx
index 9c2b68aa4..f12e234a9 100644
--- a/apps/app/src/app/pages/skills.tsx
+++ b/apps/app/src/app/pages/skills.tsx
@@ -237,7 +237,7 @@ export default function SkillsView(props: SkillsViewProps) {
});
});
- const maskError = (value: unknown) => (value instanceof Error ? value.message : translate("common.something_went_wrong"));
+ const maskError = (value: unknown) => (value instanceof Error ? value.message : "Something went wrong");
const showToast = (title: string, tone: AppStatusToastTone = "info") => {
statusToasts.showToast({ title, tone });
};
@@ -245,7 +245,7 @@ export default function SkillsView(props: SkillsViewProps) {
const hubRepoKey = (repo: HubSkillRepo) => `${repo.owner}/${repo.repo}@${repo.ref}`;
const defaultHubRepoKey = "different-ai/openwork-hub@main";
- const activeHubRepoLabel = createMemo(() => (extensions.hubRepo() ? hubRepoKey(extensions.hubRepo()!) : translate("skills.no_hub_repo_label")));
+ const activeHubRepoLabel = createMemo(() => (extensions.hubRepo() ? hubRepoKey(extensions.hubRepo()!) : "No hub repo selected"));
const hasDefaultHubRepo = createMemo(() => extensions.hubRepos().some((repo) => hubRepoKey(repo) === defaultHubRepoKey));
@@ -273,7 +273,7 @@ export default function SkillsView(props: SkillsViewProps) {
const repo = customRepoName().trim();
const ref = customRepoRef().trim() || "main";
if (!owner || !repo) {
- setCustomRepoError(translate("skills.owner_repo_required"));
+ setCustomRepoError("Owner and repo are required.");
return;
}
extensions.addHubRepo({ owner, repo, ref });
@@ -387,7 +387,7 @@ export default function SkillsView(props: SkillsViewProps) {
const installFromHub = async (skill: HubSkillCard) => {
if (props.busy || installingHubSkill()) return;
setInstallingHubSkill(skill.name);
- showToast(`${translate("skills.installing_prefix")} ${skill.name}…`);
+ showToast(`Installing ${skill.name}…`);
try {
const result = await extensions.installHubSkill(skill.name);
showToast(result.message, "success");
@@ -488,7 +488,7 @@ export default function SkillsView(props: SkillsViewProps) {
try {
const skill = await extensions.readSkill(target.name);
- if (!skill) throw new Error(translate("skills.skill_load_failed"));
+ if (!skill) throw new Error("Failed to load skill");
const payload: SkillBundleV1 = {
schemaVersion: 1,
@@ -508,7 +508,7 @@ export default function SkillsView(props: SkillsViewProps) {
setShareUrl(result.url);
try {
await navigator.clipboard.writeText(result.url);
- showToast(translate("skills.link_copied"), "success");
+ showToast("Link copied", "success");
} catch {
// ignore
}
@@ -524,9 +524,9 @@ export default function SkillsView(props: SkillsViewProps) {
if (!url) return;
try {
await navigator.clipboard.writeText(url);
- showToast(translate("skills.link_copied"), "success");
+ showToast("Link copied", "success");
} catch {
- setShareError(translate("skills.copy_link_failed"));
+ setShareError("Failed to copy link");
}
};
@@ -540,12 +540,12 @@ export default function SkillsView(props: SkillsViewProps) {
try {
const result = await extensions.readSkill(skill.name);
if (!result) {
- setSelectedError(translate("skills.skill_load_failed"));
+ setSelectedError("Failed to load skill.");
return;
}
setSelectedContent(result.content);
} catch (e) {
- setSelectedError(e instanceof Error ? e.message : translate("skills.skill_load_failed"));
+ setSelectedError(e instanceof Error ? e.message : "Failed to load skill.");
} finally {
setSelectedLoading(false);
}
@@ -574,7 +574,7 @@ export default function SkillsView(props: SkillsViewProps) {
);
setSelectedDirty(false);
} catch (e) {
- setSelectedError(e instanceof Error ? e.message : translate("skills.save_failed"));
+ setSelectedError(e instanceof Error ? e.message : "Failed to save skill.");
}
};
@@ -619,7 +619,7 @@ export default function SkillsView(props: SkillsViewProps) {
{translate("skills.title")}
- {translate("skills.worker_profile_desc")}
+ Skills are the core abilities of this worker. Discover them from Hub, manage what is installed, and create new ones directly in chat.
@@ -631,7 +631,7 @@ export default function SkillsView(props: SkillsViewProps) {
class={pillSecondaryClass}
>
- {translate("skills.import_local_skill")}
+ Import local skill
- {translate("skills.reveal_folder")}
+ Reveal folder
- {translate("skills.create_in_chat")}
+ Create skill in chat
@@ -691,7 +691,7 @@ export default function SkillsView(props: SkillsViewProps) {
class={pillSecondaryClass}
>
- {translate("common.refresh")}
+ Refresh
@@ -722,10 +722,10 @@ export default function SkillsView(props: SkillsViewProps) {
{translate("skills.installed")}
- {translate("skills.installed_desc")}
+ Installed skills live on this worker and can be edited or shared.
- {t("skills.shown_count", currentLocale(), { count: filteredSkills().length })}
+ {filteredSkills().length} shown
OpenWork
- {translate("skills.no_description")}}>
+ No description yet.}>
{skill.description}
@@ -773,7 +773,7 @@ export default function SkillsView(props: SkillsViewProps) {
- {translate("skills.installed_status")}
+ Installed
- {translate("skills.share_title")}
+ Share
- {translate("common.edit")}
+ Edit
- {translate("common.remove")}
+ Remove
@@ -961,9 +961,9 @@ export default function SkillsView(props: SkillsViewProps) {
- {translate("skills.available_from_hub")}
+ Available from Hub
- {translate("skills.hub_desc")}
+ Browse shared skills from GitHub-backed hubs and add them to this worker.
@@ -977,34 +977,34 @@ export default function SkillsView(props: SkillsViewProps) {
disabled={props.busy || hasDefaultHubRepo()}
>
- {translate("skills.add_openwork_hub")}
+ Add OpenWork Hub
- {translate("skills.add_git_repo")}
+ Add git repo
void extensions.refreshHubSkills({ force: true })}
disabled={props.busy}
class={pillSecondaryClass}
- title={translate("skills.refresh_hub_title")}
+ title="Refresh hub catalog"
>
- {translate("skills.refresh_hub")}
+ Refresh hub
- {translate("skills.source_label")}: {activeHubRepoLabel()}
+ Source: {activeHubRepoLabel()}
@@ -1031,7 +1031,7 @@ export default function SkillsView(props: SkillsViewProps) {
void extensions.refreshHubSkills({ force: true });
}}
disabled={props.busy}
- title={translate("skills.remove_saved_repo")}
+ title="Remove saved repo"
>
×
@@ -1052,7 +1052,7 @@ export default function SkillsView(props: SkillsViewProps) {
when={filteredHubSkills().length}
fallback={
- {extensions.hubRepo() ? translate("skills.no_hub_skills") : translate("skills.no_hub_repo_selected")}
+ {extensions.hubRepo() ? "No hub skills available." : "No hub repo selected. Add a GitHub repo to browse skills."}
}
>
@@ -1069,7 +1069,7 @@ export default function SkillsView(props: SkillsViewProps) {
{skill.name}
{t("skills.from_repo", currentLocale(), { owner: skill.source.owner, repo: skill.source.repo })}}
+ fallback={From {skill.source.owner}/{skill.source.repo}
}
>
{skill.description}
@@ -1078,8 +1078,8 @@ export default function SkillsView(props: SkillsViewProps) {
{skill.source.owner}/{skill.source.repo}
-
- {t("skills.trigger_label", currentLocale(), { trigger: skill.trigger ?? "" })}
+
+ Trigger: {skill.trigger}
@@ -1087,7 +1087,7 @@ export default function SkillsView(props: SkillsViewProps) {
- {translate("skills.hub_label")}
+ Hub
- {installingHubSkill() === skill.name ? translate("skills.installing") : translate("common.add")}
+ {installingHubSkill() === skill.name ? "Installing" : "Add skill"}
@@ -1135,14 +1135,14 @@ export default function SkillsView(props: SkillsViewProps) {
disabled={!selectedDirty() || props.busy}
onClick={() => void saveSelectedSkill()}
>
- {translate("common.save")}
+ Save
- {translate("common.close")}
+ Close
@@ -1155,7 +1155,7 @@ export default function SkillsView(props: SkillsViewProps) {
{translate("skills.loading")} }
+ fallback={Loading…}
>