@burdenoff/microfe-vibecontrols 2026.531.6 → 2026.531.7
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/dist/components/agents/RemoteTaskPermissionsCard.js +111 -0
- package/dist/components/agents/RemoteTaskPermissionsCard.js.map +1 -0
- package/dist/components/agents/index.js +1 -0
- package/dist/components/sessions/TerminalPanel.js +4 -4
- package/dist/components/sessions/TerminalPanel.js.map +1 -1
- package/dist/pages/AgentDetailsPage.js +349 -341
- package/dist/pages/AgentDetailsPage.js.map +1 -1
- package/dist/pages/SessionDetailsPage.js +72 -71
- package/dist/pages/SessionDetailsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
|
+
import { deleteAgentConfigValue as t, getAgentConfigValue as n, setAgentConfigValue as r } from "../../services/aiApi.js";
|
|
3
|
+
import { useCallback as i, useEffect as a, useState as o } from "react";
|
|
4
|
+
import { Loader2 as s, ShieldAlert as c } from "lucide-react";
|
|
5
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
6
|
+
import { Card as d, Switch as f } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
//#region src/components/agents/RemoteTaskPermissionsCard.tsx
|
|
8
|
+
function p(e) {
|
|
9
|
+
return [{
|
|
10
|
+
key: "security:vibecontrols_allow_remote_shell_task",
|
|
11
|
+
title: e("agentDetails.remoteShellTask.title", "Allow remote shell + script tasks"),
|
|
12
|
+
description: e("agentDetails.remoteShellTask.description", "Enable VibeDeck buttons, scheduled jobs, and the UI to run shell commands and scripts on this agent over its tunnel. Required for any UI-triggered \"execute action\" that runs a command."),
|
|
13
|
+
warning: e("agentDetails.remoteShellTask.warning", "Anyone with workspace access can execute commands on the host machine. Audit log records every invocation; reviewers can revert this flag at any time.")
|
|
14
|
+
}, {
|
|
15
|
+
key: "security:vibecontrols_allow_remote_file_task",
|
|
16
|
+
title: e("agentDetails.remoteFileTask.title", "Allow remote file writes + deletes"),
|
|
17
|
+
description: e("agentDetails.remoteFileTask.description", "Enable UI-triggered file_operation write/delete tasks on this agent. Reads are always allowed; this gate covers mutations only."),
|
|
18
|
+
warning: e("agentDetails.remoteFileTask.warning", "Writes and deletes are confined to the cwd / homedir / tmpdir allowlist via resolveSafePath + isSensitivePath, but a malicious caller could still corrupt files in those scopes.")
|
|
19
|
+
}];
|
|
20
|
+
}
|
|
21
|
+
function m(e) {
|
|
22
|
+
if (!e) return !1;
|
|
23
|
+
let t = e.trim().toLowerCase();
|
|
24
|
+
return t === "1" || t === "true" || t === "yes";
|
|
25
|
+
}
|
|
26
|
+
function h({ agent: h }) {
|
|
27
|
+
let g = e(), _ = p(g), [v, y] = o(!0), [b, x] = o(null), [S, C] = o({}), [w, T] = o(null), E = i(async () => {
|
|
28
|
+
y(!0), T(null);
|
|
29
|
+
try {
|
|
30
|
+
let e = await Promise.all(_.map(async (e) => [e.key, m(await n(h, e.key))]));
|
|
31
|
+
C(Object.fromEntries(e));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
T(e instanceof Error ? e.message : g("agentDetails.remoteTaskPermissions.loadFailed", "Failed to load permissions"));
|
|
34
|
+
} finally {
|
|
35
|
+
y(!1);
|
|
36
|
+
}
|
|
37
|
+
}, [h, g]);
|
|
38
|
+
a(() => {
|
|
39
|
+
E();
|
|
40
|
+
}, [E]);
|
|
41
|
+
let D = i(async (e, n) => {
|
|
42
|
+
x(e), T(null);
|
|
43
|
+
try {
|
|
44
|
+
n ? await r(h, e, "1") : await t(h, e), C((t) => ({
|
|
45
|
+
...t,
|
|
46
|
+
[e]: n
|
|
47
|
+
}));
|
|
48
|
+
} catch (e) {
|
|
49
|
+
T(e instanceof Error ? e.message : g("agentDetails.remoteTaskPermissions.saveFailed", "Failed to save permission"));
|
|
50
|
+
} finally {
|
|
51
|
+
x(null);
|
|
52
|
+
}
|
|
53
|
+
}, [h, g]);
|
|
54
|
+
return /* @__PURE__ */ u(d, { children: [/* @__PURE__ */ u("div", {
|
|
55
|
+
className: "p-4 border-b border-border-default",
|
|
56
|
+
children: [/* @__PURE__ */ u("h3", {
|
|
57
|
+
className: "font-semibold text-text-primary flex items-center gap-2",
|
|
58
|
+
children: [/* @__PURE__ */ l(c, { className: "size-5" }), g("agentDetails.remoteTaskPermissions.title", "Remote Task Permissions")]
|
|
59
|
+
}), /* @__PURE__ */ l("p", {
|
|
60
|
+
className: "mt-1 text-xs text-text-secondary",
|
|
61
|
+
children: g("agentDetails.remoteTaskPermissions.subtitle", "These gates default to deny so an agent on a public tunnel can’t be coerced into shell execution. Toggle them on per-agent if you want VibeDeck buttons / scheduled jobs to run shell or file-mutation tasks here.")
|
|
62
|
+
})]
|
|
63
|
+
}), /* @__PURE__ */ u("div", {
|
|
64
|
+
className: "p-4 space-y-4",
|
|
65
|
+
children: [w ? /* @__PURE__ */ l("div", {
|
|
66
|
+
className: "rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-sm text-status-error-text",
|
|
67
|
+
children: w
|
|
68
|
+
}) : null, v ? /* @__PURE__ */ u("div", {
|
|
69
|
+
className: "flex items-center gap-2 text-sm text-text-secondary",
|
|
70
|
+
children: [/* @__PURE__ */ l(s, { className: "size-4 animate-spin" }), g("agentDetails.remoteTaskPermissions.loading", "Loading…")]
|
|
71
|
+
}) : _.map((e) => {
|
|
72
|
+
let t = S[e.key] ?? !1, n = b === e.key;
|
|
73
|
+
return /* @__PURE__ */ u("div", {
|
|
74
|
+
className: "flex items-start gap-3 rounded-lg border border-border-default bg-bg-primary p-3",
|
|
75
|
+
children: [/* @__PURE__ */ u("div", {
|
|
76
|
+
className: "flex-1 min-w-0 space-y-1",
|
|
77
|
+
children: [
|
|
78
|
+
/* @__PURE__ */ l("p", {
|
|
79
|
+
className: "text-sm font-medium text-text-primary",
|
|
80
|
+
children: e.title
|
|
81
|
+
}),
|
|
82
|
+
/* @__PURE__ */ l("p", {
|
|
83
|
+
className: "text-xs text-text-secondary",
|
|
84
|
+
children: e.description
|
|
85
|
+
}),
|
|
86
|
+
e.warning ? /* @__PURE__ */ l("p", {
|
|
87
|
+
className: "text-xs text-status-warning-text",
|
|
88
|
+
children: e.warning
|
|
89
|
+
}) : null,
|
|
90
|
+
/* @__PURE__ */ l("p", {
|
|
91
|
+
className: "text-[11px] font-mono text-text-tertiary",
|
|
92
|
+
children: e.key
|
|
93
|
+
})
|
|
94
|
+
]
|
|
95
|
+
}), /* @__PURE__ */ u("div", {
|
|
96
|
+
className: "flex items-center gap-2 flex-shrink-0",
|
|
97
|
+
children: [n ? /* @__PURE__ */ l(s, { className: "size-4 animate-spin text-text-secondary" }) : null, /* @__PURE__ */ l(f, {
|
|
98
|
+
checked: t,
|
|
99
|
+
onCheckedChange: (t) => void D(e.key, t),
|
|
100
|
+
disabled: n,
|
|
101
|
+
"aria-label": e.title
|
|
102
|
+
})]
|
|
103
|
+
})]
|
|
104
|
+
}, e.key);
|
|
105
|
+
})]
|
|
106
|
+
})] });
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { h as RemoteTaskPermissionsCard };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=RemoteTaskPermissionsCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RemoteTaskPermissionsCard.js","names":[],"sources":["../../../src/components/agents/RemoteTaskPermissionsCard.tsx"],"sourcesContent":["/**\n * RemoteTaskPermissionsCard\n *\n * Toggles the per-agent \"allow remote shell tasks\" and \"allow remote\n * file-mutation tasks\" gates that the agent enforces in\n * `core/request-security.ts:denyNonLocalMutation`. The agent's task\n * plugin (POST /api/profiles/<n>/tasks/) refuses non-loopback callers by\n * default because remote shell execution + file writes are high-blast\n * radius; this card lets operators opt in per-agent without having to\n * SSH in and restart the daemon with the matching env var\n * (VIBECONTROLS_ALLOW_REMOTE_SHELL_TASK / _FILE_TASK).\n *\n * Storage: writes to the agent's encrypted config bag under\n * `security:vibecontrols_allow_remote_shell_task` and\n * `security:vibecontrols_allow_remote_file_task`. The same keys are\n * read by the agent's gate via `db.getConfig(...)`. Persisted across\n * agent restarts.\n */\nimport { useCallback, useEffect, useState } from 'react';\nimport { ShieldAlert, Loader2 } from 'lucide-react';\n\nimport { Card } from '@burdenoff/fe-libs/ui';\nimport { Switch } from '@burdenoff/fe-libs/ui';\n\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n deleteAgentConfigValue,\n getAgentConfigValue,\n setAgentConfigValue,\n type AgentRef,\n} from '@/services/aiApi';\n\ninterface RemoteTaskPermissionsCardProps {\n agent: AgentRef;\n}\n\ninterface GateDef {\n key: string;\n title: string;\n description: string;\n warning?: string;\n}\n\nfunction buildGates(tr: ReturnType<typeof useTr>): GateDef[] {\n return [\n {\n key: 'security:vibecontrols_allow_remote_shell_task',\n title: tr(\n 'agentDetails.remoteShellTask.title',\n 'Allow remote shell + script tasks'\n ),\n description: tr(\n 'agentDetails.remoteShellTask.description',\n 'Enable VibeDeck buttons, scheduled jobs, and the UI to run shell commands and scripts on this agent over its tunnel. Required for any UI-triggered \"execute action\" that runs a command.'\n ),\n warning: tr(\n 'agentDetails.remoteShellTask.warning',\n 'Anyone with workspace access can execute commands on the host machine. Audit log records every invocation; reviewers can revert this flag at any time.'\n ),\n },\n {\n key: 'security:vibecontrols_allow_remote_file_task',\n title: tr(\n 'agentDetails.remoteFileTask.title',\n 'Allow remote file writes + deletes'\n ),\n description: tr(\n 'agentDetails.remoteFileTask.description',\n 'Enable UI-triggered file_operation write/delete tasks on this agent. Reads are always allowed; this gate covers mutations only.'\n ),\n warning: tr(\n 'agentDetails.remoteFileTask.warning',\n 'Writes and deletes are confined to the cwd / homedir / tmpdir allowlist via resolveSafePath + isSensitivePath, but a malicious caller could still corrupt files in those scopes.'\n ),\n },\n ];\n}\n\nfunction isTruthy(raw: string | null | undefined): boolean {\n if (!raw) return false;\n const v = raw.trim().toLowerCase();\n return v === '1' || v === 'true' || v === 'yes';\n}\n\nexport function RemoteTaskPermissionsCard({ agent }: RemoteTaskPermissionsCardProps) {\n const tr = useTr();\n const gates = buildGates(tr);\n const [loading, setLoading] = useState(true);\n const [saving, setSaving] = useState<string | null>(null);\n const [values, setValues] = useState<Record<string, boolean>>({});\n const [error, setError] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const entries = await Promise.all(\n gates.map(async (g) => [g.key, isTruthy(await getAgentConfigValue(agent, g.key))] as const)\n );\n setValues(Object.fromEntries(entries));\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr('agentDetails.remoteTaskPermissions.loadFailed', 'Failed to load permissions')\n );\n } finally {\n setLoading(false);\n }\n // gates is stable across renders (derived from tr); intentionally not in deps.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [agent, tr]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const toggle = useCallback(\n async (key: string, next: boolean) => {\n setSaving(key);\n setError(null);\n try {\n if (next) {\n await setAgentConfigValue(agent, key, '1');\n } else {\n await deleteAgentConfigValue(agent, key);\n }\n setValues((prev) => ({ ...prev, [key]: next }));\n } catch (err) {\n setError(\n err instanceof Error\n ? err.message\n : tr(\n 'agentDetails.remoteTaskPermissions.saveFailed',\n 'Failed to save permission'\n )\n );\n } finally {\n setSaving(null);\n }\n },\n [agent, tr]\n );\n\n return (\n <Card>\n <div className=\"p-4 border-b border-border-default\">\n <h3 className=\"font-semibold text-text-primary flex items-center gap-2\">\n <ShieldAlert className=\"size-5\" />\n {tr(\n 'agentDetails.remoteTaskPermissions.title',\n 'Remote Task Permissions'\n )}\n </h3>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tr(\n 'agentDetails.remoteTaskPermissions.subtitle',\n 'These gates default to deny so an agent on a public tunnel can’t be coerced into shell execution. Toggle them on per-agent if you want VibeDeck buttons / scheduled jobs to run shell or file-mutation tasks here.'\n )}\n </p>\n </div>\n <div className=\"p-4 space-y-4\">\n {error ? (\n <div className=\"rounded-md border border-status-error-border bg-status-error-bg px-3 py-2 text-sm text-status-error-text\">\n {error}\n </div>\n ) : null}\n {loading ? (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <Loader2 className=\"size-4 animate-spin\" />\n {tr('agentDetails.remoteTaskPermissions.loading', 'Loading…')}\n </div>\n ) : (\n gates.map((g) => {\n const current = values[g.key] ?? false;\n const busy = saving === g.key;\n return (\n <div\n key={g.key}\n className=\"flex items-start gap-3 rounded-lg border border-border-default bg-bg-primary p-3\"\n >\n <div className=\"flex-1 min-w-0 space-y-1\">\n <p className=\"text-sm font-medium text-text-primary\">{g.title}</p>\n <p className=\"text-xs text-text-secondary\">{g.description}</p>\n {g.warning ? (\n <p className=\"text-xs text-status-warning-text\">{g.warning}</p>\n ) : null}\n <p className=\"text-[11px] font-mono text-text-tertiary\">{g.key}</p>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n {busy ? (\n <Loader2 className=\"size-4 animate-spin text-text-secondary\" />\n ) : null}\n <Switch\n checked={current}\n onCheckedChange={(checked) => void toggle(g.key, checked)}\n disabled={busy}\n aria-label={g.title}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </Card>\n );\n}\n"],"mappings":"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"}
|
|
@@ -351,7 +351,7 @@ function _({ tabs: _, activeTabId: me, onTabSelect: he, onTabClose: ge, onOpenEx
|
|
|
351
351
|
children: e.loading ? k("vibecontrols.sessions.terminal.startingTerminal", "Starting terminal...") : k("vibecontrols.sessions.terminal.connecting", "Connecting...")
|
|
352
352
|
}), /* @__PURE__ */ h("p", {
|
|
353
353
|
className: "text-text-secondary text-xs",
|
|
354
|
-
children: e.loading ? k("vibecontrols.sessions.terminal.
|
|
354
|
+
children: e.loading ? k("vibecontrols.sessions.terminal.settingUpWebTerminal", "Setting up session + web terminal") : k("vibecontrols.sessions.terminal.establishingConnection", "Establishing connection")
|
|
355
355
|
})] })]
|
|
356
356
|
})
|
|
357
357
|
}),
|
|
@@ -397,7 +397,7 @@ function _({ tabs: _, activeTabId: me, onTabSelect: he, onTabClose: ge, onOpenEx
|
|
|
397
397
|
}),
|
|
398
398
|
/* @__PURE__ */ h("p", {
|
|
399
399
|
className: "text-text-secondary text-xs mt-1",
|
|
400
|
-
children: "Setting up
|
|
400
|
+
children: "Setting up session + web terminal"
|
|
401
401
|
})
|
|
402
402
|
]
|
|
403
403
|
})
|
|
@@ -837,7 +837,7 @@ function _({ tabs: _, activeTabId: me, onTabSelect: he, onTabClose: ge, onOpenEx
|
|
|
837
837
|
children: e.loading ? k("vibecontrols.sessions.terminal.startingTerminal", "Starting terminal...") : k("vibecontrols.sessions.terminal.connecting", "Connecting...")
|
|
838
838
|
}), /* @__PURE__ */ h("p", {
|
|
839
839
|
className: "text-text-secondary text-xs",
|
|
840
|
-
children: e.loading ? k("vibecontrols.sessions.terminal.
|
|
840
|
+
children: e.loading ? k("vibecontrols.sessions.terminal.settingUpWebTerminal", "Setting up session + web terminal") : k("vibecontrols.sessions.terminal.establishingConnection", "Establishing connection")
|
|
841
841
|
})] })]
|
|
842
842
|
})
|
|
843
843
|
}),
|
|
@@ -890,7 +890,7 @@ function _({ tabs: _, activeTabId: me, onTabSelect: he, onTabClose: ge, onOpenEx
|
|
|
890
890
|
}),
|
|
891
891
|
/* @__PURE__ */ h("p", {
|
|
892
892
|
className: "text-text-secondary text-xs mt-1",
|
|
893
|
-
children: "Setting up
|
|
893
|
+
children: "Setting up session + web terminal"
|
|
894
894
|
})
|
|
895
895
|
]
|
|
896
896
|
})
|