@agentprojectcontext/apx 1.33.0 → 1.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/apc-context/SKILL.md +2 -5
- package/skills/apx/SKILL.md +49 -61
- package/src/core/agent/a2a/reply.js +48 -0
- package/src/core/agent/build-agent-system.js +4 -3
- package/src/core/agent/channels/voice-context.js +98 -0
- package/src/core/agent/memory.js +2 -1
- package/src/core/agent/prompt-builder.js +2 -1
- package/src/core/agent/prompts/modes/code-build.md +1 -0
- package/src/core/agent/prompts/modes/code-plan.md +1 -0
- package/src/core/agent/prompts/modes/index.js +28 -0
- package/src/core/agent/skills/loader.js +22 -18
- package/src/core/agent/stream/turn-accumulator.js +73 -0
- package/src/core/agent/suggestions.js +37 -0
- package/src/core/agent/tools/handlers/add-project.js +5 -2
- package/src/core/agent/tools/handlers/call-runtime.js +3 -2
- package/src/core/agent/tools/handlers/transcribe-audio.js +1 -1
- package/src/core/agent/tools/helpers.js +2 -2
- package/src/core/agent/tools/names.js +138 -0
- package/src/core/agent/tools/registry-bridge.js +6 -14
- package/src/core/agent/tools/registry.js +68 -65
- package/src/core/apc/context-copy.js +27 -0
- package/src/core/apc/notes.js +19 -0
- package/src/core/apc/parser.js +13 -6
- package/src/core/apc/paths.js +87 -0
- package/src/core/apc/scaffold.js +82 -74
- package/src/core/apc/skill-sync.js +13 -1
- package/src/core/channels/telegram/dispatch.js +595 -0
- package/src/core/channels/telegram/helpers.js +130 -0
- package/src/core/config/index.js +3 -2
- package/src/core/config/redact.js +95 -0
- package/src/core/constants/channels.js +2 -0
- package/src/core/constants/code-modes.js +10 -0
- package/src/core/constants/index.js +1 -0
- package/src/core/deck/manifest.js +186 -0
- package/src/core/engines/catalog.js +83 -0
- package/src/core/engines/gemini.js +28 -11
- package/src/core/engines/index.js +11 -1
- package/src/core/{tools → http-tools}/browser.js +0 -1
- package/src/core/{tools → http-tools}/fetch.js +0 -1
- package/src/core/{tools → http-tools}/glob.js +0 -1
- package/src/core/{tools → http-tools}/grep.js +0 -1
- package/src/core/{tools → http-tools}/registry.js +0 -1
- package/src/core/{tools → http-tools}/search.js +0 -1
- package/src/core/i18n/en.js +9 -0
- package/src/core/i18n/es.js +12 -0
- package/src/core/i18n/index.js +54 -0
- package/src/core/i18n/pt.js +9 -0
- package/src/core/identity/telegram.js +2 -1
- package/src/core/mcp/runner.js +272 -14
- package/src/core/mcp/sources.js +3 -2
- package/src/core/routines/index.js +16 -0
- package/src/{host/daemon/routines.js → core/routines/runner.js} +36 -103
- package/src/core/runtime-skills/apc-context/SKILL.md +159 -0
- package/src/core/runtime-skills/apx/SKILL.md +95 -0
- package/src/core/runtime-skills/apx-mcp/SKILL.md +116 -0
- package/src/core/runtime-skills/{claude-code.md → claude-code/SKILL.md} +1 -0
- package/src/core/runtime-skills/{codex-cli.md → codex-cli/SKILL.md} +1 -0
- package/src/core/runtime-skills/{opencode-cli.md → opencode-cli/SKILL.md} +1 -0
- package/src/core/runtime-skills/{openrouter.md → openrouter/SKILL.md} +1 -0
- package/src/{host/daemon/env-detect.js → core/runtimes/detect.js} +1 -1
- package/src/core/stores/code-sessions.js +50 -2
- package/src/core/stores/routine-memory.js +1 -1
- package/src/core/stores/sessions-search.js +121 -0
- package/src/core/stores/sessions.js +38 -0
- package/src/core/vars/index.js +14 -0
- package/src/core/vars/interpolate.js +86 -0
- package/src/core/vars/sources.js +151 -0
- package/src/core/voice/audio-decode.js +38 -0
- package/src/core/voice/transcription.js +225 -0
- package/src/host/daemon/api/admin-config.js +5 -82
- package/src/host/daemon/api/agents.js +5 -5
- package/src/host/daemon/api/code.js +17 -169
- package/src/host/daemon/api/config.js +3 -4
- package/src/host/daemon/api/conversations.js +8 -29
- package/src/host/daemon/api/deck.js +37 -404
- package/src/host/daemon/api/engines.js +1 -50
- package/src/host/daemon/api/exec.js +1 -1
- package/src/host/daemon/api/mcps.js +32 -0
- package/src/host/daemon/api/routines.js +1 -1
- package/src/host/daemon/api/runtimes.js +4 -3
- package/src/host/daemon/api/sessions-search.js +24 -140
- package/src/host/daemon/api/sessions.js +12 -30
- package/src/host/daemon/api/shared.js +2 -1
- package/src/host/daemon/api/telegram.js +1 -11
- package/src/host/daemon/api/tools.js +6 -6
- package/src/host/daemon/api/transcribe.js +2 -2
- package/src/host/daemon/api/vars.js +137 -0
- package/src/host/daemon/api/voice.js +13 -290
- package/src/host/daemon/api.js +2 -0
- package/src/host/daemon/db.js +6 -6
- package/src/host/daemon/deck-exec.js +148 -0
- package/src/host/daemon/index.js +3 -3
- package/src/host/daemon/plugins/telegram/index.js +24 -687
- package/src/host/daemon/routines-scheduler.js +64 -0
- package/src/host/daemon/smoke.js +3 -2
- package/src/host/daemon/whisper-server.js +225 -0
- package/src/interfaces/cli/commands/agent.js +3 -2
- package/src/interfaces/cli/commands/command.js +2 -3
- package/src/interfaces/cli/commands/messages.js +6 -2
- package/src/interfaces/cli/commands/pair.js +5 -4
- package/src/interfaces/cli/commands/search.js +1 -1
- package/src/interfaces/cli/commands/sessions.js +3 -2
- package/src/interfaces/cli/commands/skills.js +36 -55
- package/src/interfaces/web/dist/assets/index-DdmSRtsz.css +1 -0
- package/src/interfaces/web/dist/assets/index-M4FspaCH.js +613 -0
- package/src/interfaces/web/dist/assets/index-M4FspaCH.js.map +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/package-lock.json +182 -182
- package/src/interfaces/web/src/components/ModelCombobox.tsx +44 -8
- package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +1 -1
- package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +76 -0
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +16 -3
- package/src/interfaces/web/src/components/chat/MessageList.tsx +23 -1
- package/src/interfaces/web/src/components/chat/ModelPicker.tsx +3 -1
- package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +4 -4
- package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +3 -2
- package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +3 -2
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +3 -2
- package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +2 -1
- package/src/interfaces/web/src/components/deck/WidgetRow.tsx +2 -1
- package/src/interfaces/web/src/components/inputs/KeyValueList.tsx +93 -0
- package/src/interfaces/web/src/components/inputs/VarTokenInput.tsx +449 -0
- package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +2 -1
- package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +2 -2
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +5 -4
- package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +3 -2
- package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +3 -2
- package/src/interfaces/web/src/components/ui/chat-input.tsx +5 -4
- package/src/interfaces/web/src/components/ui/sidebar.tsx +3 -2
- package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +2 -1
- package/src/interfaces/web/src/constants/index.ts +1 -1
- package/src/interfaces/web/src/i18n/en.ts +174 -7
- package/src/interfaces/web/src/i18n/es.ts +179 -15
- package/src/interfaces/web/src/lib/api/mcps.ts +25 -0
- package/src/interfaces/web/src/lib/api/vars.ts +38 -0
- package/src/interfaces/web/src/lib/api.ts +1 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -31
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +4 -3
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +7 -6
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +4 -3
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/project/ConfigTab.tsx +132 -1
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +549 -104
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/VarsTab.tsx +300 -0
- package/src/interfaces/web/src/types/daemon.ts +5 -0
- package/src/host/daemon/transcription.js +0 -538
- package/src/host/daemon/whisper-transcribe.py +0 -73
- package/src/interfaces/web/dist/assets/index-7dVT2O1S.css +0 -1
- package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js +0 -602
- package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js.map +0 -1
- /package/src/{host/daemon → core/apc}/projects-helpers.js +0 -0
- /package/src/{host/daemon/plugins → core/channels}/telegram/ask.js +0 -0
- /package/src/{host/daemon/plugins → core/channels}/telegram/media.js +0 -0
- /package/src/core/{tools → http-tools}/index.js +0 -0
- /package/{skills → src/core/runtime-skills}/apx-agency-agents/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-agent/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-mcp-builder/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-project/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-routine/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-runtime/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-sessions/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-skill-builder/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-task/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-telegram/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-voice/SKILL.md +0 -0
- /package/src/{host/daemon/compact.js → core/stores/conversations-compactor.js} +0 -0
- /package/src/{host/daemon → core/stores}/conversations.js +0 -0
- /package/src/{host/daemon → core/util}/thinking.js +0 -0
|
@@ -7,6 +7,7 @@ import { useToast } from "../../components/Toast";
|
|
|
7
7
|
import { DaemonCard } from "../../components/deck/DaemonCard";
|
|
8
8
|
import { DesktopGroup } from "../../components/deck/DesktopGroup";
|
|
9
9
|
import type { DeckWidget } from "../../lib/api/deck";
|
|
10
|
+
import { t } from "../../i18n";
|
|
10
11
|
|
|
11
12
|
// Deck module — configure the companion "Deck" app: enable/disable widgets,
|
|
12
13
|
// view desktops, inspect the daemon manifest.
|
|
@@ -81,7 +82,7 @@ export function DeckScreen() {
|
|
|
81
82
|
|
|
82
83
|
{/* Widgets section */}
|
|
83
84
|
<Section
|
|
84
|
-
title="
|
|
85
|
+
title={t("deck_screen.widgets_title")}
|
|
85
86
|
description={
|
|
86
87
|
isLoading
|
|
87
88
|
? "Cargando manifest…"
|
|
@@ -90,7 +91,7 @@ export function DeckScreen() {
|
|
|
90
91
|
: `${widgets.length} widgets · ${enabledCount} externos habilitados`
|
|
91
92
|
}
|
|
92
93
|
action={
|
|
93
|
-
<Button size="sm" variant="ghost" onClick={() => mutate()} disabled={isLoading} title="
|
|
94
|
+
<Button size="sm" variant="ghost" onClick={() => mutate()} disabled={isLoading} title={t("deck_screen.reload_manifest")} aria-label={t("deck_screen.reload_manifest")}>
|
|
94
95
|
<RefreshCw size={14} className={isLoading ? "animate-spin" : ""} />
|
|
95
96
|
</Button>
|
|
96
97
|
}
|
|
@@ -132,7 +133,7 @@ export function DeckScreen() {
|
|
|
132
133
|
|
|
133
134
|
{/* Active project + stats (read-only context) */}
|
|
134
135
|
{data?.apx && (
|
|
135
|
-
<Section title="
|
|
136
|
+
<Section title={t("deck_screen.context_title")} description="Información que el Deck ve del daemon.">
|
|
136
137
|
<div className="space-y-2 text-sm" data-testid="deck-apx-context">
|
|
137
138
|
<div className="flex items-center gap-2">
|
|
138
139
|
<span className="text-muted-fg">Proyecto activo:</span>
|
|
@@ -7,6 +7,7 @@ import { UiSelect } from "../../components/UiSelect";
|
|
|
7
7
|
import { useToast } from "../../components/Toast";
|
|
8
8
|
import { useGlobalConfig } from "../../hooks/useGlobalConfig";
|
|
9
9
|
import { Desktop, fetchDesktopMessages, type GlobalMessage } from "../../lib/api/desktop";
|
|
10
|
+
import { t } from "../../i18n";
|
|
10
11
|
|
|
11
12
|
const DEFAULT_SHORTCUT = "CommandOrControl+G";
|
|
12
13
|
const POSITION_OPTS = [
|
|
@@ -100,7 +101,7 @@ export function DesktopScreen() {
|
|
|
100
101
|
<div className="grid gap-6 xl:grid-cols-[1fr_1fr]">
|
|
101
102
|
{/* ── LEFT: configuration + status ─────────────────────────────── */}
|
|
102
103
|
<div className="space-y-6">
|
|
103
|
-
<Section title="
|
|
104
|
+
<Section title={t("desktop_screen.status_title")} description="La ventana se lanza desde la terminal o por autostart.">
|
|
104
105
|
{stLoading ? <Loading /> : (
|
|
105
106
|
<div className="flex items-center gap-2 text-sm">
|
|
106
107
|
<StatusDot ok={running} />
|
|
@@ -120,7 +121,7 @@ export function DesktopScreen() {
|
|
|
120
121
|
</Section>
|
|
121
122
|
|
|
122
123
|
<Section
|
|
123
|
-
title="
|
|
124
|
+
title={t("desktop_screen.autostart_title")}
|
|
124
125
|
description="Lanza la ventana al iniciar sesión del usuario. Equivalente a `apx desktop install` (no requiere sudo)."
|
|
125
126
|
>
|
|
126
127
|
{!autostart ? <Loading /> : (
|
|
@@ -137,7 +138,7 @@ export function DesktopScreen() {
|
|
|
137
138
|
</Section>
|
|
138
139
|
|
|
139
140
|
<Section
|
|
140
|
-
title="
|
|
141
|
+
title={t("desktop_screen.shortcut_title")}
|
|
141
142
|
description="Botón de acceso rápido global que muestra/oculta la ventana y arranca a escuchar."
|
|
142
143
|
>
|
|
143
144
|
{cfgLoading ? <Loading /> : (
|
|
@@ -169,7 +170,7 @@ export function DesktopScreen() {
|
|
|
169
170
|
)}
|
|
170
171
|
</Section>
|
|
171
172
|
|
|
172
|
-
<Section title="
|
|
173
|
+
<Section title={t("desktop_screen.appearance_title")} description="Tema y posición de la ventana en la pantalla.">
|
|
173
174
|
{cfgLoading ? <Loading /> : (
|
|
174
175
|
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
|
175
176
|
<Field label="Tema" hint="Reiniciá la ventana para aplicar.">
|
|
@@ -193,7 +194,7 @@ export function DesktopScreen() {
|
|
|
193
194
|
</Section>
|
|
194
195
|
|
|
195
196
|
<Section
|
|
196
|
-
title="
|
|
197
|
+
title={t("desktop_screen.activation_title")}
|
|
197
198
|
description="El plugin del daemon procesa los mensajes. STT se configura en Voces."
|
|
198
199
|
>
|
|
199
200
|
{cfgLoading ? <Loading /> : (
|
|
@@ -216,7 +217,7 @@ export function DesktopScreen() {
|
|
|
216
217
|
{/* ── RIGHT: last conversation preview ─────────────────────────── */}
|
|
217
218
|
<div>
|
|
218
219
|
<Section
|
|
219
|
-
title="
|
|
220
|
+
title={t("desktop_screen.last_conv_title")}
|
|
220
221
|
description="Lo último charlado con el agente desde la ventana flotante."
|
|
221
222
|
action={
|
|
222
223
|
<button
|
|
@@ -9,6 +9,7 @@ import { VoiceProviderModal, type VoiceProviderSave } from "../../components/voi
|
|
|
9
9
|
import { VoiceTestCard } from "../../components/voice/VoiceTestCard";
|
|
10
10
|
import { VoiceSttCard } from "../../components/voice/VoiceSttCard";
|
|
11
11
|
import { Voice, type TranscriptionConfig, type TtsMode, type VoiceTtsConfig } from "../../lib/api/voice";
|
|
12
|
+
import { t } from "../../i18n";
|
|
12
13
|
|
|
13
14
|
// Voices module — configure TTS/STT providers, pick the default engine, and
|
|
14
15
|
// test playback. TTS provider availability comes from the daemon
|
|
@@ -123,7 +124,7 @@ export function VoiceScreen() {
|
|
|
123
124
|
<div className="grid gap-6 xl:grid-cols-2">
|
|
124
125
|
{/* Left: TTS providers */}
|
|
125
126
|
<Section
|
|
126
|
-
title="
|
|
127
|
+
title={t("voice_screen.providers_title")}
|
|
127
128
|
description="Motores de síntesis. El estado lo reporta el daemon en vivo. Elegí cuál usar por defecto."
|
|
128
129
|
>
|
|
129
130
|
{provLoading || cfgLoading ? (
|
|
@@ -148,12 +149,12 @@ export function VoiceScreen() {
|
|
|
148
149
|
|
|
149
150
|
{/* Right: test + STT */}
|
|
150
151
|
<div className="space-y-6">
|
|
151
|
-
<Section title="
|
|
152
|
+
<Section title={t("voice_screen.test_title")} description='Elegí con qué motor sintetizar y, si aplica, cómo querés que hable.'>
|
|
152
153
|
<VoiceTestCard engines={engines} defaultProvider={configuredProvider} mode={mode} />
|
|
153
154
|
</Section>
|
|
154
155
|
|
|
155
156
|
<Section
|
|
156
|
-
title="
|
|
157
|
+
title={t("voice_screen.stt_title")}
|
|
157
158
|
description="Motor de voz a texto que usan el deck, Telegram y la CLI al escuchar."
|
|
158
159
|
>
|
|
159
160
|
{cfgLoading ? <Loading /> : <VoiceSttCard config={transcriptionCfg} onPatch={patchStt} />}
|
|
@@ -128,7 +128,7 @@ export function AgentDetailScreen({ pid }: { pid: string }) {
|
|
|
128
128
|
<Stat label="Heartbeats" value={myRoutines.length} icon={Heart} />
|
|
129
129
|
</div>
|
|
130
130
|
<div className="grid gap-3 sm:grid-cols-2">
|
|
131
|
-
<Section title="
|
|
131
|
+
<Section title={t("agent_detail_extra.skills_title")} description="">
|
|
132
132
|
<div className="flex flex-wrap gap-1">
|
|
133
133
|
{a.skills?.map((s) => <Badge key={s} tone="info"><Sparkles size={10} /> {s}</Badge>)}
|
|
134
134
|
{a.tools?.map((t) => <Badge key={t}><Wrench size={10} /> {t}</Badge>)}
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { useNavigate } from "react-router-dom";
|
|
1
3
|
import useSWR from "swr";
|
|
4
|
+
import { RefreshCw, Trash2 } from "lucide-react";
|
|
2
5
|
import { Projects } from "../../lib/api";
|
|
3
6
|
import { Section } from "../../components/Section";
|
|
4
|
-
import { Empty, Loading } from "../../components/ui";
|
|
7
|
+
import { Button, Dialog, Empty, Loading } from "../../components/ui";
|
|
5
8
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../components/ui/tabs";
|
|
6
9
|
import { ConfigTabsEditor } from "../../components/config/ConfigTabsEditor";
|
|
7
10
|
import { APC_PROJECT_SECTIONS, PROJECT_OVERRIDE_SECTIONS } from "../../components/config/project-config-sections";
|
|
8
11
|
import { useToast } from "../../components/Toast";
|
|
12
|
+
import { useProject } from "../../hooks/useProjects";
|
|
9
13
|
import { flattenObject } from "../../lib/config-values";
|
|
10
14
|
import { isSecretMarker } from "../../lib/secrets";
|
|
11
15
|
import { t } from "../../i18n";
|
|
12
16
|
|
|
13
17
|
export function ConfigTab({ pid }: { pid: string }) {
|
|
14
18
|
const toast = useToast();
|
|
19
|
+
const navigate = useNavigate();
|
|
20
|
+
const { project, mutate: mutateProject } = useProject(pid);
|
|
15
21
|
const cfg = useSWR(`/projects/${pid}/config`, () => Projects.config.show(pid));
|
|
22
|
+
const isBase = String(pid) === "0";
|
|
16
23
|
|
|
17
24
|
if (cfg.isLoading) return <Loading />;
|
|
18
25
|
if (!cfg.data) return <Empty>{t("project.config.no_data")}</Empty>;
|
|
@@ -81,10 +88,134 @@ export function ConfigTab({ pid }: { pid: string }) {
|
|
|
81
88
|
</TabsContent>
|
|
82
89
|
</Tabs>
|
|
83
90
|
</Section>
|
|
91
|
+
|
|
92
|
+
{!isBase && project ? (
|
|
93
|
+
<DangerZone
|
|
94
|
+
pid={pid}
|
|
95
|
+
label={project.name || project.path}
|
|
96
|
+
onRebuilt={() => cfg.mutate()}
|
|
97
|
+
onUnregistered={() => { mutateProject(); navigate("/"); }}
|
|
98
|
+
/>
|
|
99
|
+
) : null}
|
|
84
100
|
</div>
|
|
85
101
|
);
|
|
86
102
|
}
|
|
87
103
|
|
|
104
|
+
function DangerZone({
|
|
105
|
+
pid,
|
|
106
|
+
label,
|
|
107
|
+
onRebuilt,
|
|
108
|
+
onUnregistered,
|
|
109
|
+
}: {
|
|
110
|
+
pid: string;
|
|
111
|
+
label: string;
|
|
112
|
+
onRebuilt: () => void;
|
|
113
|
+
onUnregistered: () => void;
|
|
114
|
+
}) {
|
|
115
|
+
const toast = useToast();
|
|
116
|
+
const [busy, setBusy] = useState<"rebuild" | "unregister" | null>(null);
|
|
117
|
+
const [confirm, setConfirm] = useState<"rebuild" | "unregister" | null>(null);
|
|
118
|
+
|
|
119
|
+
const runRebuild = async () => {
|
|
120
|
+
setBusy("rebuild");
|
|
121
|
+
try {
|
|
122
|
+
await Projects.rebuild(pid);
|
|
123
|
+
toast.success(t("project.rebuild_done"));
|
|
124
|
+
onRebuilt();
|
|
125
|
+
} catch (e) {
|
|
126
|
+
toast.error((e as Error).message);
|
|
127
|
+
} finally {
|
|
128
|
+
setBusy(null);
|
|
129
|
+
setConfirm(null);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const runUnregister = async () => {
|
|
134
|
+
setBusy("unregister");
|
|
135
|
+
try {
|
|
136
|
+
await Projects.remove(pid);
|
|
137
|
+
toast.success(t("project.unregistered"));
|
|
138
|
+
onUnregistered();
|
|
139
|
+
} catch (e) {
|
|
140
|
+
toast.error((e as Error).message);
|
|
141
|
+
} finally {
|
|
142
|
+
setBusy(null);
|
|
143
|
+
setConfirm(null);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<>
|
|
149
|
+
<Section
|
|
150
|
+
title={t("project.danger.title")}
|
|
151
|
+
description={t("project.danger.subtitle")}
|
|
152
|
+
>
|
|
153
|
+
<div className="space-y-3">
|
|
154
|
+
<div className="flex items-start justify-between gap-3 rounded-md border border-border bg-muted/30 px-3 py-3">
|
|
155
|
+
<div className="min-w-0">
|
|
156
|
+
<div className="text-sm font-medium">{t("project.rebuild")}</div>
|
|
157
|
+
<div className="text-xs text-muted-fg">{t("project.danger.rebuild_desc")}</div>
|
|
158
|
+
</div>
|
|
159
|
+
<Button size="sm" variant="secondary" onClick={() => setConfirm("rebuild")}>
|
|
160
|
+
<RefreshCw size={13} /> {t("project.rebuild")}
|
|
161
|
+
</Button>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<div className="flex items-start justify-between gap-3 rounded-md border border-red-500/40 bg-red-500/5 px-3 py-3">
|
|
165
|
+
<div className="min-w-0">
|
|
166
|
+
<div className="text-sm font-medium">{t("admin.unregister")}</div>
|
|
167
|
+
<div className="text-xs text-muted-fg">{t("project.danger.unregister_desc")}</div>
|
|
168
|
+
</div>
|
|
169
|
+
<Button size="sm" variant="destructive" onClick={() => setConfirm("unregister")}>
|
|
170
|
+
<Trash2 size={13} /> {t("admin.unregister")}
|
|
171
|
+
</Button>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</Section>
|
|
175
|
+
|
|
176
|
+
<Dialog
|
|
177
|
+
open={confirm === "rebuild"}
|
|
178
|
+
onClose={() => (busy ? null : setConfirm(null))}
|
|
179
|
+
title={t("project.danger.rebuild_confirm_title")}
|
|
180
|
+
description={t("project.danger.rebuild_confirm_desc", { label })}
|
|
181
|
+
size="sm"
|
|
182
|
+
footer={
|
|
183
|
+
<>
|
|
184
|
+
<Button variant="ghost" onClick={() => setConfirm(null)} disabled={busy !== null}>
|
|
185
|
+
{t("common.cancel")}
|
|
186
|
+
</Button>
|
|
187
|
+
<Button variant="primary" onClick={runRebuild} loading={busy === "rebuild"}>
|
|
188
|
+
{t("project.rebuild")}
|
|
189
|
+
</Button>
|
|
190
|
+
</>
|
|
191
|
+
}
|
|
192
|
+
>
|
|
193
|
+
<p className="text-sm text-muted-fg">{t("project.danger.rebuild_long")}</p>
|
|
194
|
+
</Dialog>
|
|
195
|
+
|
|
196
|
+
<Dialog
|
|
197
|
+
open={confirm === "unregister"}
|
|
198
|
+
onClose={() => (busy ? null : setConfirm(null))}
|
|
199
|
+
title={t("project.danger.unregister_confirm_title")}
|
|
200
|
+
description={t("project.unregister_confirm", { label })}
|
|
201
|
+
size="sm"
|
|
202
|
+
footer={
|
|
203
|
+
<>
|
|
204
|
+
<Button variant="ghost" onClick={() => setConfirm(null)} disabled={busy !== null}>
|
|
205
|
+
{t("common.cancel")}
|
|
206
|
+
</Button>
|
|
207
|
+
<Button variant="destructive" onClick={runUnregister} loading={busy === "unregister"}>
|
|
208
|
+
{t("admin.unregister")}
|
|
209
|
+
</Button>
|
|
210
|
+
</>
|
|
211
|
+
}
|
|
212
|
+
>
|
|
213
|
+
<p className="text-sm text-muted-fg">{t("project.danger.unregister_long")}</p>
|
|
214
|
+
</Dialog>
|
|
215
|
+
</>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
88
219
|
function cleanSet(set: Record<string, unknown>) {
|
|
89
220
|
const out: Record<string, unknown> = {};
|
|
90
221
|
for (const [key, value] of Object.entries(flattenObject(set))) {
|