@prettier-ai/dsh-client-ui-permission-presets 0.1.2-alpha.1
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +91 -0
- package/README.zh.md +91 -0
- package/lib/client.js +471 -0
- package/lib/index.js +11 -0
- package/lib/invariant.js +24 -0
- package/lib/types/client/PermissionRow.d.ts +35 -0
- package/lib/types/client/index.d.ts +27 -0
- package/lib/types/client/locales.d.ts +46 -0
- package/lib/types/client/presentation.d.ts +16 -0
- package/lib/types/client/settings-store.d.ts +74 -0
- package/lib/types/index.d.ts +9 -0
- package/lib/types/invariant.d.ts +16 -0
- package/package.json +87 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/client/ui-permission-presets/README.md
|
|
5
|
+
README.md: fc7328bd0029ea86b369d3457c43aa4e24db1243
|
|
6
|
+
README.zh.md: 523ea66a0f7fa3eeddb0f8fe1a901d03f9720c3d
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Permission preset surfaces for the Web GUI: the General-settings default row and the /permission picker for the current session; for users and maintainers of permission policy."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-client-ui-permission-presets
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
This package provides permission preset surfaces for two lifetimes in the Web GUI: a General-settings row chooses the default for later sessions without switching the current session. A picker on the host `/permission` command switches the current session through one flat preset list with the active value marked. Kebab-case names render as title-case labels, and `danger-full-access` is presented as `Full access`. Choosing full access requires an explicit risk acknowledgement before either surface writes it. Both surfaces read one host-computed projection and write through one path, so the pushed projection frame is the single confirmation both follow.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Mount this plugin alongside the settings and commands packages; the permission row then appears in General settings, and the `/permission` picker replaces the bare command invocation. The current-session picker is available exactly while the projection key is present; a permission-less composition shows neither picker nor Settings row.
|
|
29
|
+
|
|
30
|
+
### The picker
|
|
31
|
+
|
|
32
|
+
A pick submits the `/permission <preset>` command line. The argued path (`/permission <preset>` typed directly) still switches directly; the decoration replaces only the bare invocation. Unknown kebab-case preset names render in title case, and `custom` is display state, never a target.
|
|
33
|
+
|
|
34
|
+
### The Settings row
|
|
35
|
+
|
|
36
|
+
The row derives its options from the host's dynamic `defaultPreset` enum and writes one settings mutation. The value applies only when a later session is created; changing it never switches or rewrites the current session.
|
|
37
|
+
|
|
38
|
+
-----
|
|
39
|
+
|
|
40
|
+
<a id="understand-the-implementation"></a>
|
|
41
|
+
## Understand the implementation
|
|
42
|
+
|
|
43
|
+
<details>
|
|
44
|
+
<summary>Implementation internals — click to expand</summary>
|
|
45
|
+
|
|
46
|
+
The General row reads the explicitly exposed `permission` Settings descriptor through `ctx.settingsScope` and writes one `settings.mutate` path operation with the descriptor revision; its observable rides the slot system's `hooks` compartment, so the renderer owns React hook binding, and a push invalidation refetches the descriptor. The value is read only when a later session is created. The current-session surface is a popupSelect decoration hung on the host `/permission` command (`ctx.commandUi.decorate`): the host command keeps its slash-menu row, argued path, and durable lifecycle logging, while the decoration replaces only the bare invocation with the picker. Options and the active mark read the session's `permissions` projection — the same host-computed select the composer chip renders. The full-access option carries a `confirmation` payload the shared popup shell renders as the in-page risk gate.
|
|
47
|
+
|
|
48
|
+
</details>
|
|
49
|
+
|
|
50
|
+
-----
|
|
51
|
+
|
|
52
|
+
<a id="further-exploration"></a>
|
|
53
|
+
## Further Exploration
|
|
54
|
+
|
|
55
|
+
Read these pages when the permission surface is not enough. They move from the browser surfaces to the host policy and the command shell.
|
|
56
|
+
|
|
57
|
+
- [dsh-permission-presets](../../interaction/permission-presets/README.md) — the host-side permission preset policy these surfaces write.
|
|
58
|
+
- [ui-commands](../ui-commands/README.md) — the popupSelect shell the `/permission` decoration registers into.
|
|
59
|
+
- [ui-conversation](../ui-conversation/README.md) — the composer chip that renders the same permissions projection.
|
|
60
|
+
- [Client package map](../README.md) — adjacent browser UI packages.
|
|
61
|
+
|
|
62
|
+
-----
|
|
63
|
+
|
|
64
|
+
<a id="model-experience"></a>
|
|
65
|
+
## Model Experience
|
|
66
|
+
|
|
67
|
+
Indirectly, through the permission facts its two surfaces write: the Settings row causes a future session to start with whole-value knob events, while the `/permission` picker appends the same facts when it switches the current session; those events select the sandbox mode and approval policy later tool calls resolve.
|
|
68
|
+
|
|
69
|
+
#### KV Cache effect
|
|
70
|
+
|
|
71
|
+
No direct invalidation; the knob consumers own any request-prefix changes.
|
|
72
|
+
|
|
73
|
+
## Known Limitations and Deferred Work
|
|
74
|
+
|
|
75
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
These limits define the current permission surfaces. They are current package constraints, not a general policy comparison or a task backlog.
|
|
79
|
+
|
|
80
|
+
- **The Settings row is Web-only** — non-Web clients may still switch the current session through `/permission`, but do not receive this browser contribution.
|
|
81
|
+
- **Preset descriptions come from the host** — localized built-in labels may therefore appear beside a description written in another language.
|
|
82
|
+
|
|
83
|
+
<a id="dev-note"></a>
|
|
84
|
+
### Dev Note
|
|
85
|
+
|
|
86
|
+
<details>
|
|
87
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
88
|
+
|
|
89
|
+
None.
|
|
90
|
+
|
|
91
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Web GUI 的权限预设表面:通用设置中的默认行与切换当前会话的 /permission 选择器;供权限策略的用户与维护者阅读。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-client-ui-permission-presets
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
本包为 Web GUI 中两种生命周期提供权限预设表面:通用设置中的一行选择之后创建会话所用的默认值,但不会切换当前会话。挂在宿主 `/permission` 命令上的选择器通过一张扁平预设列表切换当前会话,并标记 active 值。Kebab-case 名称渲染为 Title Case 标签,`danger-full-access` 显示为 `Full access`。选择完全权限时,该行或选择器写入前必须先显式确认风险。两个表面读取同一份宿主计算的投影、经同一条路径写入,因此推送的投影帧是两者共同跟随的唯一确认。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
与设置与命令包一起挂载本插件;权限行随即出现在通用设置中,`/permission` 选择器替换裸命令调用。当前会话选择器恰在投影 key 存在时可用;无权限组合既不显示选择器,也不显示设置行。
|
|
29
|
+
|
|
30
|
+
### 选择器
|
|
31
|
+
|
|
32
|
+
选中即提交 `/permission <preset>` 命令行。带参路径(直接键入 `/permission <preset>`)仍直接切换;装饰只替换裸调用。未知 kebab-case 预设名渲染为 Title Case 标签,`custom` 只是显示状态,绝非目标。
|
|
33
|
+
|
|
34
|
+
### 设置行
|
|
35
|
+
|
|
36
|
+
该行从宿主动态的 `defaultPreset` enum 推导选项,写入一条设置变更操作。该值只在之后创建会话时生效;改变它绝不会切换或改写当前会话。
|
|
37
|
+
|
|
38
|
+
-----
|
|
39
|
+
|
|
40
|
+
<a id="understand-the-implementation"></a>
|
|
41
|
+
## 理解实现
|
|
42
|
+
|
|
43
|
+
<details>
|
|
44
|
+
<summary>实现细节——点击展开</summary>
|
|
45
|
+
|
|
46
|
+
通用行经 `ctx.settingsScope` 读取显式暴露的 `permission` Settings 描述符,并携带描述符 revision 写入一条 `settings.mutate` 路径操作;其 observable 经槽位系统的 `hooks` 格传递,因此 React 钩子绑定归渲染器,推送失效通知会重新获取描述符。该值只在之后创建会话时读取。当前会话表面是挂在宿主 `/permission` 命令上的 popupSelect 装饰(`ctx.commandUi.decorate`):宿主命令保留斜杠菜单行、带参路径与持久生命周期记账,装饰只把裸调用替换为选择器。选项与 active 标记读取会话的 `permissions` 投影——与 composer chip 渲染的同一份宿主计算 select。完全权限选项携带 `confirmation` 载荷,由共享弹窗外壳渲染为页内风险门。
|
|
47
|
+
|
|
48
|
+
</details>
|
|
49
|
+
|
|
50
|
+
-----
|
|
51
|
+
|
|
52
|
+
<a id="further-exploration"></a>
|
|
53
|
+
## 进一步探索
|
|
54
|
+
|
|
55
|
+
当权限面不够用时阅读以下页面。它们从浏览器表面进入宿主策略与命令外壳。
|
|
56
|
+
|
|
57
|
+
- [dsh-permission-presets](../../interaction/permission-presets/README.zh.md)——这些表面写入的宿主侧权限预设策略。
|
|
58
|
+
- [ui-commands](../ui-commands/README.zh.md)——`/permission` 装饰注册进的 popupSelect 外壳。
|
|
59
|
+
- [ui-conversation](../ui-conversation/README.zh.md)——渲染同一份权限投影的 composer chip。
|
|
60
|
+
- [客户端包映射](../README.zh.md)——相邻的浏览器 UI 包。
|
|
61
|
+
|
|
62
|
+
-----
|
|
63
|
+
|
|
64
|
+
<a id="model-experience"></a>
|
|
65
|
+
## 模型体验
|
|
66
|
+
|
|
67
|
+
间接影响。它的两个表面写入权限事实:设置行使未来会话带着全量值旋钮事件启动,而 `/permission` 选择器切换当前会话时追加相同的事实;这些事件决定后续工具调用解析到的沙箱模式与审批策略。
|
|
68
|
+
|
|
69
|
+
#### KV Cache 影响
|
|
70
|
+
|
|
71
|
+
无直接失效;请求前缀的变化由旋钮消费方自行承担。
|
|
72
|
+
|
|
73
|
+
## 已知限制与延期工作
|
|
74
|
+
|
|
75
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
这些限制界定了当前权限表面。它们是当前包约束,不是通用策略对比或任务积压。
|
|
79
|
+
|
|
80
|
+
- **设置行仅限 Web**——非 Web 客户端仍可经 `/permission` 切换当前会话,但不会获得这项浏览器贡献。
|
|
81
|
+
- **预设描述来自宿主**——本地化的内置标签旁边可能显示另一种语言编写的描述。
|
|
82
|
+
|
|
83
|
+
<a id="dev-note"></a>
|
|
84
|
+
### 开发备注
|
|
85
|
+
|
|
86
|
+
<details>
|
|
87
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
88
|
+
|
|
89
|
+
无。
|
|
90
|
+
|
|
91
|
+
</details>
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@prettier-ai/dsh-client-ui-permission-presets",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
|
+
let react = require("react");
|
|
9
|
+
let _prettier_ai_dsh_client_ui_primitives = require("@prettier-ai/dsh-client-ui-primitives");
|
|
10
|
+
let _prettier_ai_dsh_client_store = require("@prettier-ai/dsh-client-store");
|
|
11
|
+
//#region lib/types/client/presentation.js
|
|
12
|
+
/** Machine value of the preset that requires an explicit GUI risk gate. */
|
|
13
|
+
const FULL_ACCESS_PRESET = "danger-full-access";
|
|
14
|
+
/**
|
|
15
|
+
* Convert conventional kebab-case preset names into user-facing title case.
|
|
16
|
+
* @param name - host-supplied preset label or key.
|
|
17
|
+
* @returns the title-cased conventional key, or a non-kebab label unchanged.
|
|
18
|
+
*/
|
|
19
|
+
function displayPresetName(name) {
|
|
20
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(name)) return name;
|
|
21
|
+
return name.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Render a permission preset under its product label.
|
|
25
|
+
* @param value - preset machine value.
|
|
26
|
+
* @param name - host-supplied preset name.
|
|
27
|
+
* @returns the Full access product label or the conventional display name.
|
|
28
|
+
*/
|
|
29
|
+
function displayPermissionPreset(value, name) {
|
|
30
|
+
return value === "danger-full-access" ? "Full access" : displayPresetName(name);
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region \0dsh-css:/home/runner/work/dsh-publisher/dsh-publisher/upstream/packages/client/ui-permission-presets/src/client/PermissionRow.module.css.mjs
|
|
34
|
+
const css = ".kR5lJq_row{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.kR5lJq_rowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.kR5lJq_title{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.kR5lJq_desc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.kR5lJq_selector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.kR5lJq_selector:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.kR5lJq_selector:disabled{cursor:default}.kR5lJq_chevron{flex:none}";
|
|
35
|
+
const tagId = "@prettier-ai/dsh-client-ui-permission-presets/PermissionRow.module.css";
|
|
36
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
37
|
+
const tag = document.createElement("style");
|
|
38
|
+
tag.dataset.plugin = "@prettier-ai/dsh-client-ui-permission-presets";
|
|
39
|
+
tag.dataset.pluginCss = tagId;
|
|
40
|
+
tag.textContent = css;
|
|
41
|
+
document.head.appendChild(tag);
|
|
42
|
+
}
|
|
43
|
+
var PermissionRow_module_css_default = {
|
|
44
|
+
"chevron": "kR5lJq_chevron",
|
|
45
|
+
"desc": "kR5lJq_desc",
|
|
46
|
+
"row": "kR5lJq_row",
|
|
47
|
+
"rowText": "kR5lJq_rowText",
|
|
48
|
+
"selector": "kR5lJq_selector",
|
|
49
|
+
"title": "kR5lJq_title"
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region lib/types/client/PermissionRow.js
|
|
53
|
+
/**
|
|
54
|
+
* Permission preference row: the default preset for subsequently created
|
|
55
|
+
* sessions. Current-session switches remain on the composer `/permission`
|
|
56
|
+
* control.
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* Render the new-session Permission default selector.
|
|
60
|
+
* @param props - composed slot props.
|
|
61
|
+
* @returns the row, or null when the host does not expose permission settings.
|
|
62
|
+
*/
|
|
63
|
+
function PermissionRow({ load, select, usePermission, t }) {
|
|
64
|
+
const state = usePermission((snapshot) => snapshot);
|
|
65
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
66
|
+
const [confirmingFullAccess, setConfirmingFullAccess] = (0, react.useState)(false);
|
|
67
|
+
const [acknowledged, setAcknowledged] = (0, react.useState)(false);
|
|
68
|
+
(0, react.useEffect)(() => {
|
|
69
|
+
load();
|
|
70
|
+
}, [load]);
|
|
71
|
+
(0, react.useEffect)(() => {
|
|
72
|
+
if (state.writable && state.status !== "unavailable") return;
|
|
73
|
+
setOpen(false);
|
|
74
|
+
setAcknowledged(false);
|
|
75
|
+
setConfirmingFullAccess(false);
|
|
76
|
+
}, [state.status, state.writable]);
|
|
77
|
+
if (state.status === "unavailable") return null;
|
|
78
|
+
const selected = state.options.find((option) => option.id === state.currentValue);
|
|
79
|
+
const busy = state.status === "loading" || state.status === "saving" || confirmingFullAccess;
|
|
80
|
+
const label = selected?.label ?? (busy ? t("loading") : t("unavailable"));
|
|
81
|
+
const description = state.error ?? t("description");
|
|
82
|
+
return (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
83
|
+
className: PermissionRow_module_css_default.row,
|
|
84
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
85
|
+
className: PermissionRow_module_css_default.rowText,
|
|
86
|
+
children: [(0, react_jsx_runtime.jsx)("div", {
|
|
87
|
+
className: PermissionRow_module_css_default.title,
|
|
88
|
+
children: t("title")
|
|
89
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
90
|
+
className: PermissionRow_module_css_default.desc,
|
|
91
|
+
role: state.error === null ? void 0 : "alert",
|
|
92
|
+
children: description
|
|
93
|
+
})]
|
|
94
|
+
}), (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.Menu, {
|
|
95
|
+
open,
|
|
96
|
+
onClose: () => {
|
|
97
|
+
setOpen(false);
|
|
98
|
+
},
|
|
99
|
+
items: state.options.map((option) => ({
|
|
100
|
+
id: option.id,
|
|
101
|
+
label: option.label
|
|
102
|
+
})),
|
|
103
|
+
selectedId: state.currentValue,
|
|
104
|
+
onSelect: (id) => {
|
|
105
|
+
setOpen(false);
|
|
106
|
+
if (id === state.currentValue) return;
|
|
107
|
+
if (id === "danger-full-access") {
|
|
108
|
+
setAcknowledged(false);
|
|
109
|
+
setConfirmingFullAccess(true);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
select(id);
|
|
113
|
+
},
|
|
114
|
+
align: "end",
|
|
115
|
+
portal: true,
|
|
116
|
+
anchor: (0, react_jsx_runtime.jsxs)("button", {
|
|
117
|
+
type: "button",
|
|
118
|
+
className: PermissionRow_module_css_default.selector,
|
|
119
|
+
"aria-haspopup": "menu",
|
|
120
|
+
"aria-expanded": open,
|
|
121
|
+
disabled: busy || !state.writable || state.options.length === 0,
|
|
122
|
+
onClick: () => {
|
|
123
|
+
setOpen((value) => !value);
|
|
124
|
+
},
|
|
125
|
+
children: [label, (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: PermissionRow_module_css_default.chevron })]
|
|
126
|
+
})
|
|
127
|
+
})]
|
|
128
|
+
}), (0, react_jsx_runtime.jsx)(_prettier_ai_dsh_client_ui_primitives.RiskConfirmation, {
|
|
129
|
+
open: confirmingFullAccess,
|
|
130
|
+
title: t("confirm.title"),
|
|
131
|
+
description: t("confirm.description"),
|
|
132
|
+
acknowledgeLabel: t("confirm.acknowledge"),
|
|
133
|
+
cancelLabel: t("confirm.cancel"),
|
|
134
|
+
closeLabel: t("close"),
|
|
135
|
+
confirmLabel: t("confirm.enable"),
|
|
136
|
+
acknowledged,
|
|
137
|
+
disabled: !state.writable || state.status === "saving",
|
|
138
|
+
onAcknowledgedChange: setAcknowledged,
|
|
139
|
+
onCancel: () => {
|
|
140
|
+
setAcknowledged(false);
|
|
141
|
+
setConfirmingFullAccess(false);
|
|
142
|
+
},
|
|
143
|
+
onConfirm: () => {
|
|
144
|
+
setAcknowledged(false);
|
|
145
|
+
setConfirmingFullAccess(false);
|
|
146
|
+
select(FULL_ACCESS_PRESET);
|
|
147
|
+
}
|
|
148
|
+
})] });
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region lib/types/client/locales.js
|
|
152
|
+
/** `settings.permission` namespace dictionaries (the Permission row's copy). */
|
|
153
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
154
|
+
const zh = {
|
|
155
|
+
"title": "权限",
|
|
156
|
+
"description": "选择新会话的默认权限模式",
|
|
157
|
+
"loading": "加载中",
|
|
158
|
+
"unavailable": "不可用",
|
|
159
|
+
"confirm.title": "确认启用 Full access?",
|
|
160
|
+
"confirm.description": "启用 Full access 后,新会话将减少确认步骤,并且可以直接执行更多操作,包括敏感操作、文件修改或外部命令。仅建议在你信任后续任务时使用。",
|
|
161
|
+
"confirm.acknowledge": "我已了解风险,并愿意继续",
|
|
162
|
+
"confirm.cancel": "取消",
|
|
163
|
+
"confirm.enable": "启用 Full access"
|
|
164
|
+
};
|
|
165
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
166
|
+
const en = {
|
|
167
|
+
"title": "Permission",
|
|
168
|
+
"description": "Choose the default permission mode for new sessions",
|
|
169
|
+
"loading": "Loading",
|
|
170
|
+
"unavailable": "Unavailable",
|
|
171
|
+
"confirm.title": "Enable Full access?",
|
|
172
|
+
"confirm.description": "Full access lets new sessions reduce confirmation steps and perform more actions directly, including sensitive operations, file changes, or external commands. Only use it when you trust subsequent tasks.",
|
|
173
|
+
"confirm.acknowledge": "I understand the risks and want to continue",
|
|
174
|
+
"confirm.cancel": "Cancel",
|
|
175
|
+
"confirm.enable": "Enable Full access"
|
|
176
|
+
};
|
|
177
|
+
/** Simplified Chinese dictionary for the current-session popup gate. */
|
|
178
|
+
const accessZh = {
|
|
179
|
+
"confirm.title": "确认启用 Full access?",
|
|
180
|
+
"confirm.description": "启用 Full access 后,agent 将减少确认步骤,并且可以直接执行更多操作,包括敏感操作、文件修改或外部命令。仅建议在你信任当前任务时使用。",
|
|
181
|
+
"confirm.acknowledge": "我已了解风险,并愿意继续",
|
|
182
|
+
"confirm.cancel": "取消",
|
|
183
|
+
"confirm.enable": "启用 Full access"
|
|
184
|
+
};
|
|
185
|
+
/** English dictionary for the current-session popup gate. */
|
|
186
|
+
const accessEn = {
|
|
187
|
+
"confirm.title": "Enable Full access?",
|
|
188
|
+
"confirm.description": "Full access reduces confirmation steps and lets the agent perform more actions directly, including sensitive operations, file changes, or external commands. Only use it when you trust the current task.",
|
|
189
|
+
"confirm.acknowledge": "I understand the risks and want to continue",
|
|
190
|
+
"confirm.cancel": "Cancel",
|
|
191
|
+
"confirm.enable": "Enable Full access"
|
|
192
|
+
};
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region lib/types/client/settings-store.js
|
|
195
|
+
/**
|
|
196
|
+
* Permission default-settings controller. The permission descriptor comes
|
|
197
|
+
* from the shared describe mirror (the dynamic preset enum lives in the
|
|
198
|
+
* namespace schema, which per-namespace scopes do not carry); writes target
|
|
199
|
+
* only `defaultPreset`, carry the descriptor revision, and fold their answer
|
|
200
|
+
* back into the mirror.
|
|
201
|
+
*/
|
|
202
|
+
/** Permission's settings namespace on the host wire. */
|
|
203
|
+
const PERMISSION_SETTINGS_NS = "permission";
|
|
204
|
+
/**
|
|
205
|
+
* Read the dynamic preset enum encoded by the host's `defaultPreset` schema.
|
|
206
|
+
* @param view - permission namespace descriptor.
|
|
207
|
+
* @param schema - settings schema operations.
|
|
208
|
+
* @returns current value and selectable options.
|
|
209
|
+
*/
|
|
210
|
+
function permissionDefaultOf(view, schema) {
|
|
211
|
+
const value = view.value?.defaultPreset;
|
|
212
|
+
if (typeof value !== "string") throw new Error("permission settings has no defaultPreset value");
|
|
213
|
+
const node = schema.nodeAtPath(schema.rehydrate(view.schema), ["defaultPreset"]);
|
|
214
|
+
if (node === void 0) throw new Error("permission settings schema has no defaultPreset field");
|
|
215
|
+
const options = (node.type === "union" ? node.list ?? [] : [node]).flatMap((candidate) => {
|
|
216
|
+
const choice = candidate;
|
|
217
|
+
if (choice.type !== "const" || typeof choice.value !== "string") return [];
|
|
218
|
+
const described = choice.meta?.description;
|
|
219
|
+
return [{
|
|
220
|
+
id: choice.value,
|
|
221
|
+
label: typeof described === "string" && described.length > 0 ? displayPermissionPreset(choice.value, described) : displayPermissionPreset(choice.value, choice.value)
|
|
222
|
+
}];
|
|
223
|
+
});
|
|
224
|
+
if (options.length === 0 || !options.some((option) => option.id === value)) throw new Error("permission settings schema does not advertise its current preset");
|
|
225
|
+
return {
|
|
226
|
+
currentValue: value,
|
|
227
|
+
options
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/** Controller deriving the row from the shared mirror and writing the default through it. */
|
|
231
|
+
var PermissionPresetSettingsController = class {
|
|
232
|
+
describeFace;
|
|
233
|
+
api;
|
|
234
|
+
schema;
|
|
235
|
+
/** Row snapshot consumed through a bound selector hook. */
|
|
236
|
+
store = (0, _prettier_ai_dsh_client_store.createSnapshotStore)({
|
|
237
|
+
status: "idle",
|
|
238
|
+
error: null,
|
|
239
|
+
writable: false,
|
|
240
|
+
currentValue: "",
|
|
241
|
+
options: [],
|
|
242
|
+
revision: 0
|
|
243
|
+
});
|
|
244
|
+
following;
|
|
245
|
+
saving = false;
|
|
246
|
+
disposed = false;
|
|
247
|
+
/**
|
|
248
|
+
* @param describeFace - the shared mirror's read/fold face (descriptor and schema source).
|
|
249
|
+
* @param api - settings wire face for the `defaultPreset` write.
|
|
250
|
+
* @param schema - settings-owned schema operations.
|
|
251
|
+
*/
|
|
252
|
+
constructor(describeFace, api, schema) {
|
|
253
|
+
this.describeFace = describeFace;
|
|
254
|
+
this.api = api;
|
|
255
|
+
this.schema = schema;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Begin following the mirror (idempotent) and reflect its current answer.
|
|
259
|
+
* @returns settlement once the snapshot reflects the mirror.
|
|
260
|
+
*/
|
|
261
|
+
async load() {
|
|
262
|
+
if (this.disposed) return;
|
|
263
|
+
this.following ??= this.describeFace.subscribe(() => {
|
|
264
|
+
this.derive();
|
|
265
|
+
});
|
|
266
|
+
this.store.update((state) => {
|
|
267
|
+
state.status = "loading";
|
|
268
|
+
state.error = null;
|
|
269
|
+
});
|
|
270
|
+
await this.describeFace.ensure();
|
|
271
|
+
this.derive();
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Persist one preset as the default for subsequently created sessions.
|
|
275
|
+
* A selection made while one is already saving is ignored — the row's
|
|
276
|
+
* control is disabled during the save, so this only drops programmatic
|
|
277
|
+
* double-submits rather than user intent.
|
|
278
|
+
* @param preset - advertised preset key.
|
|
279
|
+
* @returns nothing; {@link store} carries success or failure.
|
|
280
|
+
*/
|
|
281
|
+
async select(preset) {
|
|
282
|
+
const state = this.store.getSnapshot();
|
|
283
|
+
const view = this.describeFace.getSnapshot().view?.namespaces.find((entry) => entry.ns === PERMISSION_SETTINGS_NS);
|
|
284
|
+
if (view === void 0 || !state.writable || this.saving) return;
|
|
285
|
+
this.saving = true;
|
|
286
|
+
this.store.update((draft) => {
|
|
287
|
+
draft.status = "saving";
|
|
288
|
+
draft.error = null;
|
|
289
|
+
});
|
|
290
|
+
try {
|
|
291
|
+
const response = await this.api.settings.mutate(PERMISSION_SETTINGS_NS, [{
|
|
292
|
+
op: "set",
|
|
293
|
+
path: ["defaultPreset"],
|
|
294
|
+
value: preset
|
|
295
|
+
}], view.revision);
|
|
296
|
+
if (!response.ok) throw new Error(response.error.message);
|
|
297
|
+
this.saving = false;
|
|
298
|
+
if (this.disposed) return;
|
|
299
|
+
this.describeFace.acceptView(response.value);
|
|
300
|
+
} catch (error) {
|
|
301
|
+
this.saving = false;
|
|
302
|
+
if (this.disposed) return;
|
|
303
|
+
this.fail(error);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/** Stop following the mirror; later publishes leave the snapshot alone. */
|
|
307
|
+
dispose() {
|
|
308
|
+
this.disposed = true;
|
|
309
|
+
this.following?.();
|
|
310
|
+
this.following = void 0;
|
|
311
|
+
}
|
|
312
|
+
derive() {
|
|
313
|
+
if (this.disposed || this.saving) return;
|
|
314
|
+
const mirrored = this.describeFace.getSnapshot();
|
|
315
|
+
if (mirrored.status === "unavailable") {
|
|
316
|
+
this.store.update((state) => {
|
|
317
|
+
state.status = "unavailable";
|
|
318
|
+
state.writable = false;
|
|
319
|
+
state.currentValue = "";
|
|
320
|
+
state.options = [];
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
if (mirrored.view === void 0) {
|
|
325
|
+
if (mirrored.error !== null) this.fail(new Error(mirrored.error));
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const view = mirrored.view.namespaces.find((entry) => entry.ns === PERMISSION_SETTINGS_NS);
|
|
329
|
+
if (view === void 0) {
|
|
330
|
+
this.store.update((state) => {
|
|
331
|
+
state.status = "unavailable";
|
|
332
|
+
state.writable = false;
|
|
333
|
+
state.currentValue = "";
|
|
334
|
+
state.options = [];
|
|
335
|
+
});
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
try {
|
|
339
|
+
const resolved = permissionDefaultOf(view, this.schema);
|
|
340
|
+
const { writable } = mirrored.view;
|
|
341
|
+
this.store.update((state) => {
|
|
342
|
+
state.status = "ready";
|
|
343
|
+
state.error = null;
|
|
344
|
+
state.writable = writable;
|
|
345
|
+
state.currentValue = resolved.currentValue;
|
|
346
|
+
state.options = resolved.options;
|
|
347
|
+
state.revision = view.revision;
|
|
348
|
+
});
|
|
349
|
+
} catch (error) {
|
|
350
|
+
this.fail(error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
fail(error) {
|
|
354
|
+
this.store.update((state) => {
|
|
355
|
+
state.status = "error";
|
|
356
|
+
state.error = error instanceof Error ? error.message : String(error);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region lib/types/client/index.js
|
|
362
|
+
/** Required services (cordis fiber inject). */
|
|
363
|
+
const inject = [
|
|
364
|
+
"commandUi",
|
|
365
|
+
"sessions",
|
|
366
|
+
"slots",
|
|
367
|
+
"locale",
|
|
368
|
+
"remote",
|
|
369
|
+
"remote.settings",
|
|
370
|
+
"settingsScope",
|
|
371
|
+
"settingsSchema"
|
|
372
|
+
];
|
|
373
|
+
const ACCESS_NS = "permission.access";
|
|
374
|
+
/** Read one session's current permissions projection value (undefined = capability absent). */
|
|
375
|
+
function selectOf(session) {
|
|
376
|
+
return session?.projections.faceOf("permissions").getSnapshot();
|
|
377
|
+
}
|
|
378
|
+
/** Flatten the projection select into popup rows; `custom` is display state, never a target. */
|
|
379
|
+
function optionsOf(value, t) {
|
|
380
|
+
return value.options.filter((option) => option.value !== "custom").map((option) => ({
|
|
381
|
+
id: option.value,
|
|
382
|
+
label: displayPermissionPreset(option.value, option.name),
|
|
383
|
+
...option.description !== void 0 ? { detail: option.description } : {},
|
|
384
|
+
...option.value === value.currentValue ? { active: true } : {},
|
|
385
|
+
...option.value === "danger-full-access" ? { confirmation: {
|
|
386
|
+
title: t("confirm.title"),
|
|
387
|
+
description: t("confirm.description"),
|
|
388
|
+
acknowledgeLabel: t("confirm.acknowledge"),
|
|
389
|
+
cancelLabel: t("confirm.cancel"),
|
|
390
|
+
confirmLabel: t("confirm.enable")
|
|
391
|
+
} } : {}
|
|
392
|
+
}));
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Client plugin body: register the /permission popup picker over the
|
|
396
|
+
* permissions projection.
|
|
397
|
+
* @param ctx - client root context.
|
|
398
|
+
*/
|
|
399
|
+
function apply(ctx) {
|
|
400
|
+
const command = ctx.get("commandUi");
|
|
401
|
+
const sessions = ctx.sessions;
|
|
402
|
+
ctx.effect(() => {
|
|
403
|
+
const disposers = [ctx.locale.register(ACCESS_NS, "zh", {
|
|
404
|
+
"confirm.title": accessZh["confirm.title"],
|
|
405
|
+
"confirm.description": accessZh["confirm.description"],
|
|
406
|
+
"confirm.acknowledge": accessZh["confirm.acknowledge"],
|
|
407
|
+
"confirm.cancel": accessZh["confirm.cancel"],
|
|
408
|
+
"confirm.enable": accessZh["confirm.enable"]
|
|
409
|
+
}), ctx.locale.register(ACCESS_NS, "en", {
|
|
410
|
+
"confirm.title": accessEn["confirm.title"],
|
|
411
|
+
"confirm.description": accessEn["confirm.description"],
|
|
412
|
+
"confirm.acknowledge": accessEn["confirm.acknowledge"],
|
|
413
|
+
"confirm.cancel": accessEn["confirm.cancel"],
|
|
414
|
+
"confirm.enable": accessEn["confirm.enable"]
|
|
415
|
+
})];
|
|
416
|
+
return () => {
|
|
417
|
+
for (const dispose of disposers) dispose();
|
|
418
|
+
};
|
|
419
|
+
}, "ui-permission: Full access confirmation dictionaries");
|
|
420
|
+
const t = ctx.locale.bind(ACCESS_NS);
|
|
421
|
+
const sessionFor = (session) => sessions.binding(session.sessionId)?.session;
|
|
422
|
+
ctx.effect(() => ctx.locale.register("settings.permission", {
|
|
423
|
+
zh,
|
|
424
|
+
en
|
|
425
|
+
}), "ui-permission: settings row dictionaries");
|
|
426
|
+
const controller = new PermissionPresetSettingsController(ctx.settingsScope.describe(), { settings: ctx.remote.settings }, ctx.settingsSchema);
|
|
427
|
+
const load = () => controller.load();
|
|
428
|
+
const select = (preset) => controller.select(preset);
|
|
429
|
+
const injected = () => ({
|
|
430
|
+
hooks: { permission: controller.store },
|
|
431
|
+
load,
|
|
432
|
+
select
|
|
433
|
+
});
|
|
434
|
+
ctx.effect(() => () => {
|
|
435
|
+
controller.dispose();
|
|
436
|
+
}, "ui-permission: settings row directory");
|
|
437
|
+
ctx.slots.inject("settings.general.item", () => ctx.slots.register({
|
|
438
|
+
name: "settings.general.item",
|
|
439
|
+
id: "permission",
|
|
440
|
+
order: -20,
|
|
441
|
+
locale: "settings.permission",
|
|
442
|
+
inject: injected
|
|
443
|
+
}, PermissionRow));
|
|
444
|
+
ctx.effect(() => command.decorate({
|
|
445
|
+
name: "permission",
|
|
446
|
+
available: (session) => selectOf(sessionFor(session)) !== void 0,
|
|
447
|
+
ui: {
|
|
448
|
+
kind: "popupSelect",
|
|
449
|
+
options: (session) => {
|
|
450
|
+
const value = selectOf(sessionFor(session));
|
|
451
|
+
if (value === void 0) throw new Error("permission presets are not available on this host");
|
|
452
|
+
return Promise.resolve(optionsOf(value, t));
|
|
453
|
+
},
|
|
454
|
+
onSelect: async (option, session) => {
|
|
455
|
+
const live = sessionFor(session);
|
|
456
|
+
if (live === void 0) throw new Error("this session is not materialized yet");
|
|
457
|
+
const result = await live.command(`/permission ${option.id}`);
|
|
458
|
+
if (!result.ok) throw new Error(`permission switch failed: ${result.error.code}: ${result.error.message}`);
|
|
459
|
+
if (!result.value.matched) throw new Error("the host offers no /permission command");
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}), "ui-permission: /permission decoration");
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
465
|
+
exports.apply = apply;
|
|
466
|
+
exports.inject = inject;
|
|
467
|
+
return module.exports;
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
//# sourceMappingURL=client.js.map
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region lib/types/index.js
|
|
2
|
+
/**
|
|
3
|
+
* Permission surfaces plugin, node half. The empty apply exists so the plugin
|
|
4
|
+
* appears in the host cordis.yml / Loader; the browser half ships the
|
|
5
|
+
* new-session Settings row and current-session command picker through
|
|
6
|
+
* exports["./client"], discovered from the package.json dsh.client declaration.
|
|
7
|
+
*/
|
|
8
|
+
/** Host plugin body — no host-side behavior for this surface plugin. */
|
|
9
|
+
function apply() {}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { apply };
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/**
|
|
3
|
+
* Package-owned invariant companion for `@prettier-ai/dsh-client-ui-permission-presets`.
|
|
4
|
+
* @module @prettier-ai/dsh-client-ui-permission-presets/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = "@prettier-ai/dsh-client-ui-permission-presets";
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = "client-ui-permission-presets-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ["invariants"];
|
|
11
|
+
/**
|
|
12
|
+
* No runtime invariant: the command and slot contribution lifecycles are
|
|
13
|
+
* proven by the HMR-safety spec, while the browser-only Settings controller
|
|
14
|
+
* owns no host events or cross-plugin mutable state.
|
|
15
|
+
*/
|
|
16
|
+
const install = () => {};
|
|
17
|
+
/**
|
|
18
|
+
* Register this package's invariant companion.
|
|
19
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
20
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
21
|
+
*/
|
|
22
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
23
|
+
//#endregion
|
|
24
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission preference row: the default preset for subsequently created
|
|
3
|
+
* sessions. Current-session switches remain on the composer `/permission`
|
|
4
|
+
* control.
|
|
5
|
+
*/
|
|
6
|
+
import type { SnapshotStore } from '@prettier-ai/dsh-client-store';
|
|
7
|
+
import type { InjectFace, PropsLocale, PropsRuntime } from '@prettier-ai/dsh-client-ui-slots';
|
|
8
|
+
import type { PermissionSettingsState } from './settings-store.ts';
|
|
9
|
+
import type { PermissionSettingsKey } from './locales.ts';
|
|
10
|
+
/** Registration-side business face for the host-backed preference. */
|
|
11
|
+
export interface PermissionRowInjected {
|
|
12
|
+
hooks: {
|
|
13
|
+
/** Permission settings snapshot bound by the renderer as usePermission. */
|
|
14
|
+
permission: SnapshotStore<PermissionSettingsState>;
|
|
15
|
+
};
|
|
16
|
+
/** Load the descriptor when the row first renders. */
|
|
17
|
+
load: () => Promise<void>;
|
|
18
|
+
/** Persist one advertised preset. */
|
|
19
|
+
select: (preset: string) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
/** Full component props. */
|
|
22
|
+
export type PermissionRowProps = PropsRuntime<'settings.general.item'> & PropsLocale<'settings.permission'> & InjectFace<PermissionRowInjected>;
|
|
23
|
+
/**
|
|
24
|
+
* Render the new-session Permission default selector.
|
|
25
|
+
* @param props - composed slot props.
|
|
26
|
+
* @returns the row, or null when the host does not expose permission settings.
|
|
27
|
+
*/
|
|
28
|
+
export declare function PermissionRow({ load, select, usePermission, t }: PermissionRowProps): import("react").JSX.Element | null;
|
|
29
|
+
declare module '@prettier-ai/dsh-client-ui-slots' {
|
|
30
|
+
interface LocaleNamespaceMap {
|
|
31
|
+
/** Permission row copy. */
|
|
32
|
+
'settings.permission': PermissionSettingsKey;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=PermissionRow.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission preset plugin, browser half — a popupSelect DECORATION hung on
|
|
3
|
+
* the host `/permission` command: one flat list of presets, current value
|
|
4
|
+
* marked active, a pick executes the switch. The decoration owns only the
|
|
5
|
+
* bare invocation; the host command keeps its catalog row, the argued path
|
|
6
|
+
* (`/permission <preset>` still switches directly), and the lifecycle
|
|
7
|
+
* logging. Options and the active mark read the session's `permissions`
|
|
8
|
+
* projection (the same host-computed select the composer chip renders); a
|
|
9
|
+
* pick submits the `/permission <preset>` command line, so both surfaces
|
|
10
|
+
* write through one path and the pushed projection frame is the one
|
|
11
|
+
* confirmation. The Full access row carries the same explicit risk gate as
|
|
12
|
+
* the composer chip; the shared popup shell owns the modal mechanics.
|
|
13
|
+
* The General-settings row separately writes the default preset for sessions
|
|
14
|
+
* created later through the host Settings API.
|
|
15
|
+
*/
|
|
16
|
+
import type { Context as ClientContext } from '@prettier-ai/cordis';
|
|
17
|
+
export type { PermissionRowInjected, PermissionRowProps } from './PermissionRow.tsx';
|
|
18
|
+
export type { PermissionDefaultOption, PermissionSettingsState, } from './settings-store.ts';
|
|
19
|
+
/** Required services (cordis fiber inject). */
|
|
20
|
+
export declare const inject: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Client plugin body: register the /permission popup picker over the
|
|
23
|
+
* permissions projection.
|
|
24
|
+
* @param ctx - client root context.
|
|
25
|
+
*/
|
|
26
|
+
export declare function apply(ctx: ClientContext): void;
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** `settings.permission` namespace dictionaries (the Permission row's copy). */
|
|
2
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
|
+
export declare const zh: {
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
loading: string;
|
|
7
|
+
unavailable: string;
|
|
8
|
+
'confirm.title': string;
|
|
9
|
+
'confirm.description': string;
|
|
10
|
+
'confirm.acknowledge': string;
|
|
11
|
+
'confirm.cancel': string;
|
|
12
|
+
'confirm.enable': string;
|
|
13
|
+
};
|
|
14
|
+
/** The settings.permission namespace key union. */
|
|
15
|
+
export type PermissionSettingsKey = keyof typeof zh;
|
|
16
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
17
|
+
export declare const en: {
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
loading: string;
|
|
21
|
+
unavailable: string;
|
|
22
|
+
'confirm.title': string;
|
|
23
|
+
'confirm.description': string;
|
|
24
|
+
'confirm.acknowledge': string;
|
|
25
|
+
'confirm.cancel': string;
|
|
26
|
+
'confirm.enable': string;
|
|
27
|
+
};
|
|
28
|
+
/** Simplified Chinese dictionary for the current-session popup gate. */
|
|
29
|
+
export declare const accessZh: {
|
|
30
|
+
'confirm.title': string;
|
|
31
|
+
'confirm.description': string;
|
|
32
|
+
'confirm.acknowledge': string;
|
|
33
|
+
'confirm.cancel': string;
|
|
34
|
+
'confirm.enable': string;
|
|
35
|
+
};
|
|
36
|
+
/** Current-session popup-gate key union. */
|
|
37
|
+
export type PermissionAccessKey = keyof typeof accessZh;
|
|
38
|
+
/** English dictionary for the current-session popup gate. */
|
|
39
|
+
export declare const accessEn: {
|
|
40
|
+
'confirm.title': string;
|
|
41
|
+
'confirm.description': string;
|
|
42
|
+
'confirm.acknowledge': string;
|
|
43
|
+
'confirm.cancel': string;
|
|
44
|
+
'confirm.enable': string;
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Machine value of the preset that requires an explicit GUI risk gate. */
|
|
2
|
+
export declare const FULL_ACCESS_PRESET = "danger-full-access";
|
|
3
|
+
/**
|
|
4
|
+
* Convert conventional kebab-case preset names into user-facing title case.
|
|
5
|
+
* @param name - host-supplied preset label or key.
|
|
6
|
+
* @returns the title-cased conventional key, or a non-kebab label unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export declare function displayPresetName(name: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Render a permission preset under its product label.
|
|
11
|
+
* @param value - preset machine value.
|
|
12
|
+
* @param name - host-supplied preset name.
|
|
13
|
+
* @returns the Full access product label or the conventional display name.
|
|
14
|
+
*/
|
|
15
|
+
export declare function displayPermissionPreset(value: string, name: string): string;
|
|
16
|
+
//# sourceMappingURL=presentation.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission default-settings controller. The permission descriptor comes
|
|
3
|
+
* from the shared describe mirror (the dynamic preset enum lives in the
|
|
4
|
+
* namespace schema, which per-namespace scopes do not carry); writes target
|
|
5
|
+
* only `defaultPreset`, carry the descriptor revision, and fold their answer
|
|
6
|
+
* back into the mirror.
|
|
7
|
+
*/
|
|
8
|
+
import type { SettingsNamespaceView } from '@prettier-ai/dsh-api-remotes/client';
|
|
9
|
+
import { type SnapshotStore } from '@prettier-ai/dsh-client-store';
|
|
10
|
+
import type { SettingsDescribeFace, SettingsSchemaService, SettingsWireFace } from '@prettier-ai/dsh-client-ui-settings/client';
|
|
11
|
+
/** Permission's settings namespace on the host wire. */
|
|
12
|
+
export declare const PERMISSION_SETTINGS_NS = "permission";
|
|
13
|
+
/** One selectable new-session default. */
|
|
14
|
+
export interface PermissionDefaultOption {
|
|
15
|
+
/** Preset key written to Settings. */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Host-supplied label or a title-cased preset key. */
|
|
18
|
+
label: string;
|
|
19
|
+
}
|
|
20
|
+
/** Permission settings-row snapshot. */
|
|
21
|
+
export interface PermissionSettingsState {
|
|
22
|
+
status: 'idle' | 'loading' | 'ready' | 'saving' | 'unavailable' | 'error';
|
|
23
|
+
error: string | null;
|
|
24
|
+
writable: boolean;
|
|
25
|
+
currentValue: string;
|
|
26
|
+
options: readonly PermissionDefaultOption[];
|
|
27
|
+
revision: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Read the dynamic preset enum encoded by the host's `defaultPreset` schema.
|
|
31
|
+
* @param view - permission namespace descriptor.
|
|
32
|
+
* @param schema - settings schema operations.
|
|
33
|
+
* @returns current value and selectable options.
|
|
34
|
+
*/
|
|
35
|
+
export declare function permissionDefaultOf(view: SettingsNamespaceView, schema: SettingsSchemaService): {
|
|
36
|
+
currentValue: string;
|
|
37
|
+
options: PermissionDefaultOption[];
|
|
38
|
+
};
|
|
39
|
+
/** Controller deriving the row from the shared mirror and writing the default through it. */
|
|
40
|
+
export declare class PermissionPresetSettingsController {
|
|
41
|
+
private readonly describeFace;
|
|
42
|
+
private readonly api;
|
|
43
|
+
private readonly schema;
|
|
44
|
+
/** Row snapshot consumed through a bound selector hook. */
|
|
45
|
+
readonly store: SnapshotStore<PermissionSettingsState>;
|
|
46
|
+
private following;
|
|
47
|
+
private saving;
|
|
48
|
+
private disposed;
|
|
49
|
+
/**
|
|
50
|
+
* @param describeFace - the shared mirror's read/fold face (descriptor and schema source).
|
|
51
|
+
* @param api - settings wire face for the `defaultPreset` write.
|
|
52
|
+
* @param schema - settings-owned schema operations.
|
|
53
|
+
*/
|
|
54
|
+
constructor(describeFace: SettingsDescribeFace, api: SettingsWireFace, schema: SettingsSchemaService);
|
|
55
|
+
/**
|
|
56
|
+
* Begin following the mirror (idempotent) and reflect its current answer.
|
|
57
|
+
* @returns settlement once the snapshot reflects the mirror.
|
|
58
|
+
*/
|
|
59
|
+
load(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Persist one preset as the default for subsequently created sessions.
|
|
62
|
+
* A selection made while one is already saving is ignored — the row's
|
|
63
|
+
* control is disabled during the save, so this only drops programmatic
|
|
64
|
+
* double-submits rather than user intent.
|
|
65
|
+
* @param preset - advertised preset key.
|
|
66
|
+
* @returns nothing; {@link store} carries success or failure.
|
|
67
|
+
*/
|
|
68
|
+
select(preset: string): Promise<void>;
|
|
69
|
+
/** Stop following the mirror; later publishes leave the snapshot alone. */
|
|
70
|
+
dispose(): void;
|
|
71
|
+
private derive;
|
|
72
|
+
private fail;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=settings-store.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission surfaces plugin, node half. The empty apply exists so the plugin
|
|
3
|
+
* appears in the host cordis.yml / Loader; the browser half ships the
|
|
4
|
+
* new-session Settings row and current-session command picker through
|
|
5
|
+
* exports["./client"], discovered from the package.json dsh.client declaration.
|
|
6
|
+
*/
|
|
7
|
+
/** Host plugin body — no host-side behavior for this surface plugin. */
|
|
8
|
+
export declare function apply(): void;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@prettier-ai/dsh-client-ui-permission-presets`.
|
|
3
|
+
* @module @prettier-ai/dsh-client-ui-permission-presets/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@prettier-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "client-ui-permission-presets-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prettier-ai/dsh-client-ui-permission-presets",
|
|
3
|
+
"description": "Permission surfaces: a new-session default in General settings and a current-session /permission popup over the permissions projection",
|
|
4
|
+
"version": "0.1.2-alpha.1",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/client/ui-permission-presets"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./client": {
|
|
26
|
+
"types": "./lib/types/client/index.d.ts",
|
|
27
|
+
"default": "./lib/client.js"
|
|
28
|
+
},
|
|
29
|
+
"./src/*": "./src/*",
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"dsh": {
|
|
33
|
+
"client": {
|
|
34
|
+
"inject": [
|
|
35
|
+
"@prettier-ai/dsh-api-session-controller",
|
|
36
|
+
"@prettier-ai/dsh-client-locale",
|
|
37
|
+
"@prettier-ai/dsh-client-ui-commands",
|
|
38
|
+
"@prettier-ai/dsh-api-remotes",
|
|
39
|
+
"@prettier-ai/dsh-client-ui-settings"
|
|
40
|
+
],
|
|
41
|
+
"platform": "web"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@prettier-ai/dsh-api-remotes": "^0.1.2-alpha.1",
|
|
47
|
+
"@prettier-ai/dsh-api-session-controller": "^0.1.2-alpha.1",
|
|
48
|
+
"@prettier-ai/dsh-client-ui-commands": "^0.1.2-alpha.1",
|
|
49
|
+
"@prettier-ai/cordis": "^4.0.1",
|
|
50
|
+
"@prettier-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1",
|
|
51
|
+
"@prettier-ai/dsh-client-ui-settings": "^0.1.2-alpha.1",
|
|
52
|
+
"@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
|
|
53
|
+
"@prettier-ai/dsh-permission-presets": "^0.1.2-alpha.1",
|
|
54
|
+
"@prettier-ai/dsh-client-locale": "^0.1.2-alpha.1",
|
|
55
|
+
"@prettier-ai/dsh-client-ui-session": "^0.1.2-alpha.1",
|
|
56
|
+
"@prettier-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/react": "~18.3.1",
|
|
60
|
+
"react": "^18.2.0",
|
|
61
|
+
"@prettier-ai/cordis": "^4.0.1",
|
|
62
|
+
"@prettier-ai/dsh-api-session-controller": "^0.1.2-alpha.1",
|
|
63
|
+
"@prettier-ai/dsh-api-remotes": "^0.1.2-alpha.1",
|
|
64
|
+
"@prettier-ai/dsh-client-locale": "^0.1.2-alpha.1",
|
|
65
|
+
"@prettier-ai/dsh-client-store": "^0.1.2-alpha.1",
|
|
66
|
+
"@prettier-ai/dsh-client-test-runtime": "^0.1.2-alpha.1",
|
|
67
|
+
"@prettier-ai/dsh-client-ui-commands": "^0.1.2-alpha.1",
|
|
68
|
+
"@prettier-ai/dsh-client-ui-settings": "^0.1.2-alpha.1",
|
|
69
|
+
"@prettier-ai/dsh-client-ui-primitives": "^0.1.2-alpha.1",
|
|
70
|
+
"@prettier-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.1",
|
|
71
|
+
"@prettier-ai/dsh-client-ui-slots": "^0.1.2-alpha.1",
|
|
72
|
+
"@prettier-ai/dsh-invariants": "^0.1.2-alpha.1",
|
|
73
|
+
"@prettier-ai/dsh-client-ui-renderer": "^0.1.2-alpha.1",
|
|
74
|
+
"@prettier-ai/dsh-permission-presets": "^0.1.2-alpha.1",
|
|
75
|
+
"@prettier-ai/dsh-client-ui-session": "^0.1.2-alpha.1"
|
|
76
|
+
},
|
|
77
|
+
"files": [
|
|
78
|
+
"lib/index.js",
|
|
79
|
+
"lib/invariant.js",
|
|
80
|
+
"lib/client.js",
|
|
81
|
+
"lib/types/**/*.d.ts"
|
|
82
|
+
],
|
|
83
|
+
"scripts": {
|
|
84
|
+
"bundle": "tsdown",
|
|
85
|
+
"watch": "tsdown --watch"
|
|
86
|
+
}
|
|
87
|
+
}
|