dsh-agent-crew-desktop-ui 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.i18n.yaml +37 -0
- package/README.md +92 -2
- package/README.zh.md +93 -0
- package/lib/client.js +743 -0
- package/lib/index.js +6 -0
- package/lib/types/client/DesktopTab.d.ts +20 -0
- package/lib/types/client/desktop-model.d.ts +59 -0
- package/lib/types/client/frame.d.ts +28 -0
- package/lib/types/client/index.d.ts +21 -0
- package/lib/types/client/locales.d.ts +34 -0
- package/lib/types/index.d.ts +4 -0
- package/locale/de.json +6 -0
- package/locale/en.json +6 -0
- package/locale/es.json +6 -0
- package/locale/fr.json +6 -0
- package/locale/ja.json +6 -0
- package/locale/pt.json +6 -0
- package/locale/ru.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +68 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tushar
|
|
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,37 @@
|
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/experimental/crew-desktop-ui/README.md
|
|
5
|
+
/:
|
|
6
|
+
en: fd84da234a63d5ac
|
|
7
|
+
zh: 5bc3ca2b5adc0c9e
|
|
8
|
+
/deepseek-ai-dsh-crew-desktop-ui:
|
|
9
|
+
en: d9282c8f0ed1e841
|
|
10
|
+
zh: 9fa2fad28e1494ba
|
|
11
|
+
/deepseek-ai-dsh-crew-desktop-ui/summary:
|
|
12
|
+
en: bb704a49986ef54f
|
|
13
|
+
zh: 6fee0ba186f3a14a
|
|
14
|
+
/deepseek-ai-dsh-crew-desktop-ui/table-of-contents:
|
|
15
|
+
en: d152484eb41ac6b4
|
|
16
|
+
zh: b00dddf4c88f0a45
|
|
17
|
+
/deepseek-ai-dsh-crew-desktop-ui/use-this-package:
|
|
18
|
+
en: 22a5fe3d507185ba
|
|
19
|
+
zh: 1c697ad3fbd72143
|
|
20
|
+
/deepseek-ai-dsh-crew-desktop-ui/understand-the-implementation:
|
|
21
|
+
en: b558c66fc9af80cc
|
|
22
|
+
zh: 3c6d1ca637bd0716
|
|
23
|
+
/deepseek-ai-dsh-crew-desktop-ui/further-exploration:
|
|
24
|
+
en: 9ba2bb6048edd305
|
|
25
|
+
zh: 865d444094927932
|
|
26
|
+
/deepseek-ai-dsh-crew-desktop-ui/model-experience:
|
|
27
|
+
en: 03c852ffd232fc8f
|
|
28
|
+
zh: 624575ba9df489ce
|
|
29
|
+
/deepseek-ai-dsh-crew-desktop-ui/model-experience/kv-cache-effect:
|
|
30
|
+
en: ca75c51c89c2c9b0
|
|
31
|
+
zh: 477b768cc9dcda1a
|
|
32
|
+
/deepseek-ai-dsh-crew-desktop-ui/known-limitations-and-deferred-work:
|
|
33
|
+
en: ff61a9db8e974b46
|
|
34
|
+
zh: d2359dfa404afa96
|
|
35
|
+
/deepseek-ai-dsh-crew-desktop-ui/known-limitations-and-deferred-work/dev-note:
|
|
36
|
+
en: 1b1046904f84d318
|
|
37
|
+
zh: d2b3b084ce7576fa
|
package/README.md
CHANGED
|
@@ -1,3 +1,93 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "Browser half of crew desktop: the right-panel Desktop tab with the latest tool-result screenshot, where computer use runs, and Pause, Take over and Resume."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
6
|
+
# dsh-agent-crew-desktop-ui
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
This client plugin adds a **Desktop** tab to the right panel. It shows the newest screenshot any tool returned in the Session, labelled with the tool and time, where computer use runs, and whether the agent is working. **Pause** stops the current turn; **Take over** stops it and shows that you are in control until you resume or send a message. The screen updates after each agent action and is not a live stream.
|
|
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
|
+
The [crew-desktop](../crew-desktop/README.md) bundle mounts this row. Open **Desktop** from the right panel's guide in any Session.
|
|
29
|
+
|
|
30
|
+
| Control | Effect |
|
|
31
|
+
|---|---|
|
|
32
|
+
| **Pause** | Stops the agent's current turn through the Session's cancel; enabled while the agent works |
|
|
33
|
+
| **Take over** | Stops a running turn, then shows **You're in control** |
|
|
34
|
+
| **Resume** | Clears the in-control notice; the agent continues when you send it a message |
|
|
35
|
+
|
|
36
|
+
A new turn, for example after you send a message, also ends the in-control notice. If stopping fails, the tab says so and the agent stays in control.
|
|
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 tab reads the Session's loaded event window. The frame is the last `image` block of the newest `tool/result`, named after the matching `tool/call`; offloaded images are skipped. Its bytes come from the Session's `readAttachment` and are shown as a data URL. The model keeps one Desktop state per Session binding, follows its sources only while the tab is open, and reads the location from `crewDesktop.where()` once.
|
|
47
|
+
|
|
48
|
+
| Source | Contents |
|
|
49
|
+
|---|---|
|
|
50
|
+
| [`src/client/frame.ts`](src/client/frame.ts) | Frame selection and image encoding |
|
|
51
|
+
| [`src/client/desktop-model.ts`](src/client/desktop-model.ts) | Per-Session state, Pause, Take over and Resume |
|
|
52
|
+
| [`src/client/DesktopTab.tsx`](src/client/DesktopTab.tsx) | Tab body |
|
|
53
|
+
| [`src/client/index.ts`](src/client/index.ts) | Tab type, Remote and slot registration |
|
|
54
|
+
| [`src/client/locales.ts`](src/client/locales.ts) | Copy |
|
|
55
|
+
| [`tests/`](tests/) | Frame selection, states, labels and stopping |
|
|
56
|
+
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
-----
|
|
60
|
+
|
|
61
|
+
<a id="further-exploration"></a>
|
|
62
|
+
## Further Exploration
|
|
63
|
+
|
|
64
|
+
- [crew-desktop](../crew-desktop/README.md) — the bundle and its Remote.
|
|
65
|
+
- [ui-sidebar-right](../../client/ui-sidebar-right/README.md) — right-panel tabs.
|
|
66
|
+
- [session-controller](../../api/session-controller/README.md) — cancel and attachments.
|
|
67
|
+
|
|
68
|
+
<a id="model-experience"></a>
|
|
69
|
+
## Model Experience
|
|
70
|
+
|
|
71
|
+
None, as the package is a browser tab that registers nothing model-facing.
|
|
72
|
+
|
|
73
|
+
#### KV Cache effect
|
|
74
|
+
|
|
75
|
+
None; this package neither assembles nor sends a provider request.
|
|
76
|
+
|
|
77
|
+
## Known Limitations and Deferred Work
|
|
78
|
+
|
|
79
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
80
|
+
|
|
81
|
+
- Frames come only from images tools return, so the tab shows nothing until an agent takes a screenshot; a continuous stream (for example over VNC) is deferred.
|
|
82
|
+
- Only the loaded part of the Session is searched; an older screenshot beyond it is not shown.
|
|
83
|
+
- Take over does not lock the computer; it stops the agent and marks that you are in control.
|
|
84
|
+
|
|
85
|
+
<a id="dev-note"></a>
|
|
86
|
+
### Dev Note
|
|
87
|
+
|
|
88
|
+
<details>
|
|
89
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
90
|
+
|
|
91
|
+
Tests drive the tab with a hand-built event window and Session.
|
|
92
|
+
|
|
93
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "crew 桌面的浏览器端:右侧面板的桌面标签页,显示最新的工具结果截图、电脑使用的运行位置,以及暂停、接管和恢复。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dsh-agent-crew-desktop-ui
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
本客户端插件在右侧面板添加 **桌面** 标签页。它显示 Session 中任意工具返回的最新截图,并标注工具和时间,同时显示电脑使用的运行位置以及智能体是否在工作。**暂停** 停止当前轮次;**接管** 停止它并显示由你控制,直到你恢复或发送消息。画面在每次智能体操作后更新,不是实时画面。
|
|
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
|
+
[crew-desktop](../crew-desktop/README.zh.md) bundle 挂载此行。在任意 Session 中从右侧面板的引导页打开 **桌面**。
|
|
29
|
+
|
|
30
|
+
| 控件 | 作用 |
|
|
31
|
+
|---|---|
|
|
32
|
+
| **暂停** | 通过 Session 的取消停止智能体当前轮次;智能体工作时可用 |
|
|
33
|
+
| **接管** | 停止正在运行的轮次,然后显示 **由你控制** |
|
|
34
|
+
| **恢复** | 清除由你控制的提示;你发送消息后智能体继续 |
|
|
35
|
+
|
|
36
|
+
新的轮次(例如你发送消息后)也会结束由你控制的提示。如果停止失败,标签页会说明,智能体继续控制。
|
|
37
|
+
|
|
38
|
+
-----
|
|
39
|
+
|
|
40
|
+
<a id="understand-the-implementation"></a>
|
|
41
|
+
## 实现原理
|
|
42
|
+
|
|
43
|
+
<details>
|
|
44
|
+
<summary>实现细节——点击展开</summary>
|
|
45
|
+
|
|
46
|
+
标签页读取 Session 已加载的事件窗口。画面是最新 `tool/result` 中最后一个 `image` 块,以对应的 `tool/call` 命名;已卸载的图片会被跳过。图片字节来自 Session 的 `readAttachment`,以 data URL 显示。模型为每个 Session 绑定保留一份桌面状态,只在标签页打开时跟随其来源,并只读取一次 `crewDesktop.where()` 的位置。
|
|
47
|
+
|
|
48
|
+
| 源文件 | 内容 |
|
|
49
|
+
|---|---|
|
|
50
|
+
| [`src/client/frame.ts`](src/client/frame.ts) | 画面选择与图片编码 |
|
|
51
|
+
| [`src/client/desktop-model.ts`](src/client/desktop-model.ts) | 每个 Session 的状态、暂停、接管与恢复 |
|
|
52
|
+
| [`src/client/DesktopTab.tsx`](src/client/DesktopTab.tsx) | 标签页内容 |
|
|
53
|
+
| [`src/client/index.ts`](src/client/index.ts) | 标签页类型、Remote 与插槽注册 |
|
|
54
|
+
| [`src/client/locales.ts`](src/client/locales.ts) | 文案 |
|
|
55
|
+
| [`tests/`](tests/) | 画面选择、状态、标签与停止 |
|
|
56
|
+
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
-----
|
|
60
|
+
|
|
61
|
+
<a id="further-exploration"></a>
|
|
62
|
+
## 延伸阅读
|
|
63
|
+
|
|
64
|
+
- [crew-desktop](../crew-desktop/README.zh.md) — bundle 及其 Remote。
|
|
65
|
+
- [ui-sidebar-right](../../client/ui-sidebar-right/README.zh.md) — 右侧面板标签页。
|
|
66
|
+
- [session-controller](../../api/session-controller/README.zh.md) — 取消与附件。
|
|
67
|
+
|
|
68
|
+
<a id="model-experience"></a>
|
|
69
|
+
## 模型体验
|
|
70
|
+
|
|
71
|
+
无,本包是浏览器标签页,不注册任何面向模型的内容。
|
|
72
|
+
|
|
73
|
+
#### KV Cache 影响
|
|
74
|
+
|
|
75
|
+
无;本包既不组装也不发送模型提供方请求。
|
|
76
|
+
|
|
77
|
+
## 已知限制与后续工作
|
|
78
|
+
|
|
79
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
80
|
+
|
|
81
|
+
- 画面只来自工具返回的图片,因此在智能体截图之前标签页没有内容;连续画面(例如通过 VNC)留待以后。
|
|
82
|
+
- 只搜索 Session 已加载的部分;更早的截图不会显示。
|
|
83
|
+
- 接管不会锁定电脑;它停止智能体并标明由你控制。
|
|
84
|
+
|
|
85
|
+
<a id="dev-note"></a>
|
|
86
|
+
### 开发备注
|
|
87
|
+
|
|
88
|
+
<details>
|
|
89
|
+
<summary>维护者工作背景——点击展开</summary>
|
|
90
|
+
|
|
91
|
+
测试用手工构造的事件窗口和 Session 驱动标签页。
|
|
92
|
+
|
|
93
|
+
</details>
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-agent-crew-desktop-ui",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let zod = require("zod");
|
|
8
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
let react = require("react");
|
|
10
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
11
|
+
//#region ../../util/brand/src/index.ts
|
|
12
|
+
/**
|
|
13
|
+
* Apply a compile-time string brand without changing the value.
|
|
14
|
+
* @param value - string admitted by the domain that owns the target brand.
|
|
15
|
+
* @returns the same string with the requested compile-time brand.
|
|
16
|
+
*/
|
|
17
|
+
function brandString(value) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region ../../core/session/src/types.ts
|
|
22
|
+
/**
|
|
23
|
+
* Brand a string as a {@link SessionId}.
|
|
24
|
+
* @param id - the raw session id string.
|
|
25
|
+
* @returns the same string with the session-id brand.
|
|
26
|
+
*/
|
|
27
|
+
function SessionId(id) {
|
|
28
|
+
return brandString(id);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region ../../typert/protocol/src/remote-info.ts
|
|
32
|
+
/**
|
|
33
|
+
* Retain one generated definition per endpoint without constructing parsers or projections.
|
|
34
|
+
* The class instance builds its shared descriptor/method index on first access;
|
|
35
|
+
* input schemas and JSON Schema projections are cached only when requested.
|
|
36
|
+
* @param spec - compiler-produced method facts and Zod factories.
|
|
37
|
+
* @param zod - the generated artifact's Zod runtime for named-input parsers.
|
|
38
|
+
* @returns a lazy, mountable info instance with no registration or transport effects.
|
|
39
|
+
*/
|
|
40
|
+
function createTypertRemoteInfo(spec, zod) {
|
|
41
|
+
return new RemoteInfo(spec, zod);
|
|
42
|
+
}
|
|
43
|
+
var RemoteInfo = class {
|
|
44
|
+
spec;
|
|
45
|
+
zod;
|
|
46
|
+
index;
|
|
47
|
+
constructor(spec, zod) {
|
|
48
|
+
this.spec = spec;
|
|
49
|
+
this.zod = zod;
|
|
50
|
+
}
|
|
51
|
+
get package() {
|
|
52
|
+
return this.spec.package;
|
|
53
|
+
}
|
|
54
|
+
get descriptors() {
|
|
55
|
+
return this.resolve().descriptors;
|
|
56
|
+
}
|
|
57
|
+
get methods() {
|
|
58
|
+
return this.resolve().methods;
|
|
59
|
+
}
|
|
60
|
+
resolve() {
|
|
61
|
+
if (this.index !== void 0) return this.index;
|
|
62
|
+
const zod = this.zod;
|
|
63
|
+
const descriptors = [];
|
|
64
|
+
const methods = Object.create(null);
|
|
65
|
+
for (const [endpoint, definition] of Object.entries(this.spec.methods)) {
|
|
66
|
+
const separator = endpoint.indexOf("/");
|
|
67
|
+
const namespace = endpoint.slice(0, separator);
|
|
68
|
+
const method = endpoint.slice(separator + 1);
|
|
69
|
+
const parameters = definition.parameters.map((parameter) => ({
|
|
70
|
+
...parameter,
|
|
71
|
+
wire: parameter.wire ?? parameter.name
|
|
72
|
+
}));
|
|
73
|
+
const descriptor = {
|
|
74
|
+
...definition,
|
|
75
|
+
id: `${this.spec.package}#${endpoint}`,
|
|
76
|
+
namespace,
|
|
77
|
+
method,
|
|
78
|
+
parameters,
|
|
79
|
+
result: definition.result.description === void 0 ? definition.result : new ResultValue(definition.result)
|
|
80
|
+
};
|
|
81
|
+
if (definition.invocation.kind === "direct") {
|
|
82
|
+
const inputs = [];
|
|
83
|
+
methods[endpoint] = Object.assign(descriptor, {
|
|
84
|
+
input(options) {
|
|
85
|
+
const strict = options?.strict === true;
|
|
86
|
+
return inputs[strict ? 1 : 0] ??= new NamedInput(parameters, strict, zod);
|
|
87
|
+
},
|
|
88
|
+
invoke(remote, input, signal) {
|
|
89
|
+
const named = input;
|
|
90
|
+
const values = parameters.map((parameter) => named[parameter.wire]);
|
|
91
|
+
if (definition.cancellation !== void 0) values.push(signal);
|
|
92
|
+
const receiver = Reflect.get(remote, namespace);
|
|
93
|
+
return Reflect.get(receiver, method).apply(receiver, values);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
descriptors.push(descriptor);
|
|
98
|
+
}
|
|
99
|
+
return this.index = {
|
|
100
|
+
descriptors,
|
|
101
|
+
methods
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
function optional(parameter) {
|
|
106
|
+
return parameter.source === "json" && parameter.acceptsUndefined === true;
|
|
107
|
+
}
|
|
108
|
+
var NamedInput = class {
|
|
109
|
+
parameters;
|
|
110
|
+
strict;
|
|
111
|
+
zod;
|
|
112
|
+
schema;
|
|
113
|
+
projection;
|
|
114
|
+
constructor(parameters, strict, zod) {
|
|
115
|
+
this.parameters = parameters;
|
|
116
|
+
this.strict = strict;
|
|
117
|
+
this.zod = zod;
|
|
118
|
+
}
|
|
119
|
+
codec(parameter) {
|
|
120
|
+
return this.strict ? parameter.strictCodec : parameter.codec;
|
|
121
|
+
}
|
|
122
|
+
create = () => this.schema ??= this.zod.strictObject(Object.fromEntries(this.parameters.map((parameter) => {
|
|
123
|
+
const value = this.codec(parameter).create();
|
|
124
|
+
return [parameter.wire, optional(parameter) ? value.optional() : value.nonoptional()];
|
|
125
|
+
})));
|
|
126
|
+
get jsonSchema() {
|
|
127
|
+
return this.project().jsonSchema;
|
|
128
|
+
}
|
|
129
|
+
get jsonSchemaError() {
|
|
130
|
+
return this.project().jsonSchemaError;
|
|
131
|
+
}
|
|
132
|
+
project() {
|
|
133
|
+
if (this.projection !== void 0) return this.projection;
|
|
134
|
+
const unavailable = this.parameters.find((parameter) => this.codec(parameter).jsonSchema === void 0);
|
|
135
|
+
if (unavailable !== void 0) return this.projection = { jsonSchemaError: this.codec(unavailable).jsonSchemaError ?? `No JSON Schema for parameter ${unavailable.wire}` };
|
|
136
|
+
return this.projection = { jsonSchema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: Object.fromEntries(this.parameters.map((parameter) => [parameter.wire, {
|
|
139
|
+
...this.codec(parameter).jsonSchema,
|
|
140
|
+
...parameter.description === void 0 ? {} : { description: parameter.description }
|
|
141
|
+
}])),
|
|
142
|
+
required: this.parameters.filter((parameter) => !optional(parameter)).map((parameter) => parameter.wire),
|
|
143
|
+
additionalProperties: false
|
|
144
|
+
} };
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var ResultValue = class {
|
|
148
|
+
spec;
|
|
149
|
+
projection;
|
|
150
|
+
constructor(spec) {
|
|
151
|
+
this.spec = spec;
|
|
152
|
+
}
|
|
153
|
+
get mode() {
|
|
154
|
+
return this.spec.mode;
|
|
155
|
+
}
|
|
156
|
+
get typeSymbol() {
|
|
157
|
+
return this.spec.typeSymbol;
|
|
158
|
+
}
|
|
159
|
+
get create() {
|
|
160
|
+
return this.spec.create;
|
|
161
|
+
}
|
|
162
|
+
get decode() {
|
|
163
|
+
return this.spec.decode;
|
|
164
|
+
}
|
|
165
|
+
get encode() {
|
|
166
|
+
return this.spec.encode;
|
|
167
|
+
}
|
|
168
|
+
get description() {
|
|
169
|
+
return this.spec.description;
|
|
170
|
+
}
|
|
171
|
+
get jsonSchemaError() {
|
|
172
|
+
return this.spec.jsonSchemaError;
|
|
173
|
+
}
|
|
174
|
+
get jsonSchema() {
|
|
175
|
+
if (this.projection === void 0) {
|
|
176
|
+
const schema = this.spec.jsonSchema;
|
|
177
|
+
this.projection = { value: schema === void 0 ? void 0 : {
|
|
178
|
+
...schema,
|
|
179
|
+
description: this.spec.description
|
|
180
|
+
} };
|
|
181
|
+
}
|
|
182
|
+
return this.projection.value;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
//#endregion
|
|
186
|
+
//#region ../crew-desktop/lib/typert.remote-client.js
|
|
187
|
+
let _deepseek_ai_dsh_crew_desktop_crewDesktop_where_result$schema$value;
|
|
188
|
+
const _deepseek_ai_dsh_crew_desktop_crewDesktop_where_result$schema = () => _deepseek_ai_dsh_crew_desktop_crewDesktop_where_result$schema$value ??= zod.z.object({
|
|
189
|
+
"provider": zod.z.string().readonly().optional().describe("Registered computer-use provider, absent while none is mounted."),
|
|
190
|
+
"host": zod.z.string().readonly().describe("Host machine name."),
|
|
191
|
+
"platform": zod.z.string().readonly().describe("Host operating system, as Node reports it.")
|
|
192
|
+
});
|
|
193
|
+
const TYPERT_REMOTE = createTypertRemoteInfo({
|
|
194
|
+
package: "dsh-agent-crew-desktop",
|
|
195
|
+
methods: { "crewDesktop/where": {
|
|
196
|
+
service: "crewDesktop",
|
|
197
|
+
description: "Where computer use runs.",
|
|
198
|
+
invocation: { kind: "direct" },
|
|
199
|
+
parameters: [],
|
|
200
|
+
result: {
|
|
201
|
+
mode: "strict",
|
|
202
|
+
typeSymbol: "dsh-agent-crew-desktop/types#DesktopLocation",
|
|
203
|
+
create: _deepseek_ai_dsh_crew_desktop_crewDesktop_where_result$schema,
|
|
204
|
+
description: "the provider name, when one is registered, and the host machine.",
|
|
205
|
+
jsonSchema: {
|
|
206
|
+
"type": "object",
|
|
207
|
+
"properties": {
|
|
208
|
+
"provider": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"description": "Registered computer-use provider, absent while none is mounted."
|
|
211
|
+
},
|
|
212
|
+
"host": {
|
|
213
|
+
"type": "string",
|
|
214
|
+
"description": "Host machine name."
|
|
215
|
+
},
|
|
216
|
+
"platform": {
|
|
217
|
+
"type": "string",
|
|
218
|
+
"description": "Host operating system, as Node reports it."
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
"required": ["host", "platform"],
|
|
222
|
+
"additionalProperties": true
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
sourceLocation: {
|
|
226
|
+
"file": "packages/experimental/crew-desktop/src/index.ts",
|
|
227
|
+
"line": 42,
|
|
228
|
+
"column": 3
|
|
229
|
+
}
|
|
230
|
+
} }
|
|
231
|
+
}, zod.z);
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region lib/types/client/frame.js
|
|
234
|
+
/**
|
|
235
|
+
* Find the newest tool-result image.
|
|
236
|
+
* @param entries - the Session's loaded event window.
|
|
237
|
+
* @returns the latest frame, or undefined when no tool has returned an image.
|
|
238
|
+
*/
|
|
239
|
+
function latestFrame(entries) {
|
|
240
|
+
const tools = /* @__PURE__ */ new Map();
|
|
241
|
+
let latest;
|
|
242
|
+
for (const entry of entries) {
|
|
243
|
+
if (entry.type !== "event") continue;
|
|
244
|
+
const event = entry.event;
|
|
245
|
+
if (event.type === "tool/call") tools.set(event.data.callId, event.data.name);
|
|
246
|
+
else if (event.type === "tool/result") {
|
|
247
|
+
const image = event.data.message.content.findLast((block) => block.type === "image");
|
|
248
|
+
if (image?.type === "image" && image.offloaded !== true) latest = {
|
|
249
|
+
attachment: image.attachment,
|
|
250
|
+
tool: tools.get(event.data.message.toolCallId) ?? "tool",
|
|
251
|
+
time: event.time,
|
|
252
|
+
seq: event.seq
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return latest;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Encode image bytes as a data URL the tab can show without another request.
|
|
260
|
+
* @param bytes - encoded image.
|
|
261
|
+
* @param mediaType - its verified media type.
|
|
262
|
+
* @returns a `data:` URL.
|
|
263
|
+
*/
|
|
264
|
+
function dataUrl(bytes, mediaType) {
|
|
265
|
+
let binary = "";
|
|
266
|
+
for (let index = 0; index < bytes.length; index += 32768) binary += String.fromCharCode(...bytes.subarray(index, index + 32768));
|
|
267
|
+
return `data:${mediaType};base64,${btoa(binary)}`;
|
|
268
|
+
}
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region lib/types/client/desktop-model.js
|
|
271
|
+
/** Observable Desktop state for one Session; it follows its sources only while the tab is open. */
|
|
272
|
+
var DesktopModel = class {
|
|
273
|
+
events;
|
|
274
|
+
session;
|
|
275
|
+
where;
|
|
276
|
+
state = {
|
|
277
|
+
frame: void 0,
|
|
278
|
+
image: void 0,
|
|
279
|
+
imageFailed: false,
|
|
280
|
+
running: false,
|
|
281
|
+
takenOver: false,
|
|
282
|
+
location: void 0,
|
|
283
|
+
locationFailed: false,
|
|
284
|
+
stopFailed: false
|
|
285
|
+
};
|
|
286
|
+
listeners = /* @__PURE__ */ new Set();
|
|
287
|
+
detach;
|
|
288
|
+
revision = -1;
|
|
289
|
+
locating = false;
|
|
290
|
+
/** Current state. */
|
|
291
|
+
source = {
|
|
292
|
+
getSnapshot: () => this.state,
|
|
293
|
+
subscribe: (listener) => {
|
|
294
|
+
this.listeners.add(listener);
|
|
295
|
+
if (this.listeners.size === 1) this.attach();
|
|
296
|
+
return () => {
|
|
297
|
+
this.listeners.delete(listener);
|
|
298
|
+
if (this.listeners.size === 0) {
|
|
299
|
+
this.detach?.();
|
|
300
|
+
this.detach = void 0;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* @param events - the Session's event window.
|
|
307
|
+
* @param session - the Session, for running state, images and stopping.
|
|
308
|
+
* @param where - reads where computer use runs; undefined when the reply was lost.
|
|
309
|
+
*/
|
|
310
|
+
constructor(events, session, where) {
|
|
311
|
+
this.events = events;
|
|
312
|
+
this.session = session;
|
|
313
|
+
this.where = where;
|
|
314
|
+
}
|
|
315
|
+
publish(next) {
|
|
316
|
+
this.state = {
|
|
317
|
+
...this.state,
|
|
318
|
+
...next
|
|
319
|
+
};
|
|
320
|
+
for (const listener of [...this.listeners]) listener();
|
|
321
|
+
}
|
|
322
|
+
attach() {
|
|
323
|
+
const offEvents = this.events.subscribe(() => {
|
|
324
|
+
this.readFrame();
|
|
325
|
+
});
|
|
326
|
+
const offSession = this.session.subscribe(() => {
|
|
327
|
+
this.readRunning();
|
|
328
|
+
});
|
|
329
|
+
this.detach = () => {
|
|
330
|
+
offEvents();
|
|
331
|
+
offSession();
|
|
332
|
+
};
|
|
333
|
+
this.readFrame();
|
|
334
|
+
this.readRunning();
|
|
335
|
+
if (this.locating) return;
|
|
336
|
+
this.locating = true;
|
|
337
|
+
this.where().then((location) => {
|
|
338
|
+
this.publish({
|
|
339
|
+
location,
|
|
340
|
+
locationFailed: location === void 0
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
readRunning() {
|
|
345
|
+
const running = this.session.getSnapshot().running;
|
|
346
|
+
if (running === this.state.running) return;
|
|
347
|
+
this.publish(running ? {
|
|
348
|
+
running,
|
|
349
|
+
takenOver: false
|
|
350
|
+
} : { running });
|
|
351
|
+
}
|
|
352
|
+
readFrame() {
|
|
353
|
+
const window = this.events.getSnapshot();
|
|
354
|
+
if (window.revision === this.revision) return;
|
|
355
|
+
this.revision = window.revision;
|
|
356
|
+
const frame = latestFrame(window.entries);
|
|
357
|
+
if (frame?.seq === this.state.frame?.seq) return;
|
|
358
|
+
this.publish({
|
|
359
|
+
frame,
|
|
360
|
+
image: void 0,
|
|
361
|
+
imageFailed: false
|
|
362
|
+
});
|
|
363
|
+
if (frame === void 0) return;
|
|
364
|
+
this.session.readAttachment(frame.attachment).then((result) => {
|
|
365
|
+
if (this.state.frame?.seq !== frame.seq) return;
|
|
366
|
+
this.publish(result.ok ? { image: dataUrl(result.value.data, frame.attachment.mediaType) } : { imageFailed: true });
|
|
367
|
+
}, () => {
|
|
368
|
+
if (this.state.frame?.seq === frame.seq) this.publish({ imageFailed: true });
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
async stop() {
|
|
372
|
+
const ok = (await this.session.cancel().catch(() => void 0))?.ok === true;
|
|
373
|
+
this.publish({ stopFailed: !ok });
|
|
374
|
+
return ok;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Stop the agent's current turn.
|
|
378
|
+
* @returns whether the Host accepted the stop.
|
|
379
|
+
*/
|
|
380
|
+
pause() {
|
|
381
|
+
return this.stop();
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Stop the agent and mark that the user is in control.
|
|
385
|
+
* @returns whether the agent was stopped.
|
|
386
|
+
*/
|
|
387
|
+
async takeOver() {
|
|
388
|
+
if (this.state.running && !await this.stop()) return false;
|
|
389
|
+
this.publish({
|
|
390
|
+
takenOver: true,
|
|
391
|
+
stopFailed: false
|
|
392
|
+
});
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
/** Hand control back; the agent continues when the user sends it a message. */
|
|
396
|
+
resume() {
|
|
397
|
+
this.publish({ takenOver: false });
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region \0dsh-css:/Users/tushar/Developer/deepseek-harness-fork/packages/experimental/crew-desktop-ui/src/client/DesktopTab.module.css.mjs
|
|
402
|
+
const css = "[data-mode]{--c:var(--dsw-alias-state-idle-primary)}[data-mode=working]{--c:var(--dsw-alias-state-business-primary)}[data-mode=taken]{--c:var(--dsw-alias-state-warn-primary)}.zK5j4a_tab{background:var(--dsw-alias-bg-base);height:100%;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;padding:16px;font-size:13px;display:flex;overflow-y:auto}.zK5j4a_head{align-items:flex-start;gap:12px;display:flex}.zK5j4a_headText{flex:1;min-width:0}.zK5j4a_title{letter-spacing:-.01em;margin:0;font-size:17px;font-weight:650;line-height:1.2}.zK5j4a_status{color:var(--dsw-alias-label-tertiary);margin:2px 0 0;font-size:12px}.zK5j4a_pill{background:color-mix(in srgb, var(--c) 14%, transparent);white-space:nowrap;border-radius:999px;flex:none;align-items:center;gap:6px;padding:2px 8px;font-size:11px;font-weight:550;display:inline-flex}.zK5j4a_pill:before{content:\"\";background:var(--c);border-radius:50%;width:6px;height:6px}.zK5j4a_pill[data-mode=working]:before{animation:1.6s ease-in-out infinite zK5j4a_pulse}@keyframes zK5j4a_pulse{50%{opacity:.35}}.zK5j4a_where{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);align-items:flex-start;gap:8px;margin:0;padding:8px 10px;font-size:12px;display:flex}.zK5j4a_where[data-off]{color:var(--dsw-alias-label-tertiary)}.zK5j4a_whereIcon{color:var(--dsw-alias-label-tertiary);flex:none;margin-top:1px}.zK5j4a_banner{border-radius:var(--dsw-radius-md);background:color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);color:var(--dsw-alias-label-primary);margin:0;padding:10px 12px;font-size:12px}.zK5j4a_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px}.zK5j4a_screen{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);justify-content:center;align-items:center;min-height:180px;margin:0;padding:6px;transition:border-color .2s,box-shadow .2s;display:flex;position:relative}.zK5j4a_screen[data-mode=taken]{border-color:var(--c);box-shadow:0 0 0 3px color-mix(in srgb, var(--c) 18%, transparent)}.zK5j4a_screen img{border-radius:6px;max-width:100%;height:auto;display:block}.zK5j4a_badge{color:#fff;max-width:calc(100% - 28px);font-family:var(--dsw-font-mono,ui-monospace, monospace);text-overflow:ellipsis;white-space:nowrap;backdrop-filter:blur(6px);background:#14161ab8;border-radius:999px;padding:3px 8px;font-size:11px;position:absolute;bottom:14px;right:14px;overflow:hidden}.zK5j4a_empty{max-width:30ch;color:var(--dsw-alias-label-tertiary);text-align:center;flex-direction:column;align-items:center;gap:8px;margin:0;padding:16px;font-size:12px;display:flex}.zK5j4a_caption,.zK5j4a_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:11px}.zK5j4a_caption{margin-top:-6px}.zK5j4a_hint{text-align:center}.zK5j4a_actions{grid-template-columns:1fr 1fr;gap:8px;margin-top:auto;display:grid}.zK5j4a_wide{grid-column:1/-1}@media (prefers-reduced-motion:reduce){.zK5j4a_pill:before{animation:none!important}.zK5j4a_screen{transition:none}}";
|
|
403
|
+
const tagId = "dsh-agent-crew-desktop-ui/DesktopTab.module.css";
|
|
404
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
405
|
+
const tag = document.createElement("style");
|
|
406
|
+
tag.dataset.plugin = "dsh-agent-crew-desktop-ui";
|
|
407
|
+
tag.dataset.pluginCss = tagId;
|
|
408
|
+
tag.textContent = css;
|
|
409
|
+
document.head.appendChild(tag);
|
|
410
|
+
}
|
|
411
|
+
var DesktopTab_module_css_default = {
|
|
412
|
+
"actions": "zK5j4a_actions",
|
|
413
|
+
"badge": "zK5j4a_badge",
|
|
414
|
+
"banner": "zK5j4a_banner",
|
|
415
|
+
"caption": "zK5j4a_caption",
|
|
416
|
+
"empty": "zK5j4a_empty",
|
|
417
|
+
"error": "zK5j4a_error",
|
|
418
|
+
"head": "zK5j4a_head",
|
|
419
|
+
"headText": "zK5j4a_headText",
|
|
420
|
+
"hint": "zK5j4a_hint",
|
|
421
|
+
"pill": "zK5j4a_pill",
|
|
422
|
+
"pulse": "zK5j4a_pulse",
|
|
423
|
+
"screen": "zK5j4a_screen",
|
|
424
|
+
"status": "zK5j4a_status",
|
|
425
|
+
"tab": "zK5j4a_tab",
|
|
426
|
+
"title": "zK5j4a_title",
|
|
427
|
+
"where": "zK5j4a_where",
|
|
428
|
+
"whereIcon": "zK5j4a_whereIcon",
|
|
429
|
+
"wide": "zK5j4a_wide"
|
|
430
|
+
};
|
|
431
|
+
//#endregion
|
|
432
|
+
//#region lib/types/client/DesktopTab.js
|
|
433
|
+
/** Right-panel Desktop tab: the latest screen an agent saw, where computer use runs, and Pause, Take over and Resume. */
|
|
434
|
+
function when(time) {
|
|
435
|
+
return new Date(time).toLocaleString(void 0, {
|
|
436
|
+
month: "short",
|
|
437
|
+
day: "numeric",
|
|
438
|
+
hour: "numeric",
|
|
439
|
+
minute: "2-digit",
|
|
440
|
+
second: "2-digit"
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
function Monitor() {
|
|
444
|
+
return (0, react_jsx_runtime.jsxs)("svg", {
|
|
445
|
+
width: "16",
|
|
446
|
+
height: "16",
|
|
447
|
+
viewBox: "0 0 16 16",
|
|
448
|
+
"aria-hidden": "true",
|
|
449
|
+
className: DesktopTab_module_css_default.whereIcon,
|
|
450
|
+
children: [(0, react_jsx_runtime.jsx)("rect", {
|
|
451
|
+
x: "1.5",
|
|
452
|
+
y: "2.5",
|
|
453
|
+
width: "13",
|
|
454
|
+
height: "9",
|
|
455
|
+
rx: "1.5",
|
|
456
|
+
fill: "none",
|
|
457
|
+
stroke: "currentColor",
|
|
458
|
+
strokeWidth: "1.4"
|
|
459
|
+
}), (0, react_jsx_runtime.jsx)("path", {
|
|
460
|
+
d: "M5.5 14h5M8 11.5V14",
|
|
461
|
+
stroke: "currentColor",
|
|
462
|
+
strokeWidth: "1.4",
|
|
463
|
+
strokeLinecap: "round"
|
|
464
|
+
})]
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Render the Desktop tab. Nothing stops the agent without a click.
|
|
469
|
+
* @param props - Desktop state, actions and copy.
|
|
470
|
+
* @returns the tab body.
|
|
471
|
+
*/
|
|
472
|
+
function DesktopTab({ useDesktop, pause, takeOver, resume, t }) {
|
|
473
|
+
const state = useDesktop((value) => value);
|
|
474
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
475
|
+
const run = (action) => {
|
|
476
|
+
setBusy(true);
|
|
477
|
+
action().finally(() => {
|
|
478
|
+
setBusy(false);
|
|
479
|
+
});
|
|
480
|
+
};
|
|
481
|
+
const { frame, location } = state;
|
|
482
|
+
const where = state.locationFailed ? t("where.failed") : location === void 0 ? t("where.loading") : location.provider === void 0 ? t("where.none", { host: location.host }) : t("where.provider", {
|
|
483
|
+
provider: location.provider,
|
|
484
|
+
host: location.host
|
|
485
|
+
});
|
|
486
|
+
const mode = state.takenOver ? "taken" : state.running ? "working" : "idle";
|
|
487
|
+
return (0, react_jsx_runtime.jsxs)("section", {
|
|
488
|
+
className: DesktopTab_module_css_default.tab,
|
|
489
|
+
"aria-label": t("tab.title"),
|
|
490
|
+
children: [
|
|
491
|
+
(0, react_jsx_runtime.jsxs)("header", {
|
|
492
|
+
className: DesktopTab_module_css_default.head,
|
|
493
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
494
|
+
className: DesktopTab_module_css_default.headText,
|
|
495
|
+
children: [(0, react_jsx_runtime.jsx)("h2", {
|
|
496
|
+
className: DesktopTab_module_css_default.title,
|
|
497
|
+
children: t("tab.title")
|
|
498
|
+
}), !state.takenOver && (0, react_jsx_runtime.jsx)("p", {
|
|
499
|
+
className: DesktopTab_module_css_default.status,
|
|
500
|
+
role: "status",
|
|
501
|
+
children: state.running ? t("status.running") : t("status.idle")
|
|
502
|
+
})]
|
|
503
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
504
|
+
className: DesktopTab_module_css_default.pill,
|
|
505
|
+
"data-mode": mode,
|
|
506
|
+
"aria-hidden": "true",
|
|
507
|
+
children: t(`pill.${mode}`)
|
|
508
|
+
})]
|
|
509
|
+
}),
|
|
510
|
+
(0, react_jsx_runtime.jsxs)("p", {
|
|
511
|
+
className: DesktopTab_module_css_default.where,
|
|
512
|
+
"data-off": state.locationFailed || location?.provider === void 0 || void 0,
|
|
513
|
+
children: [(0, react_jsx_runtime.jsx)(Monitor, {}), where]
|
|
514
|
+
}),
|
|
515
|
+
state.takenOver && (0, react_jsx_runtime.jsx)("p", {
|
|
516
|
+
className: DesktopTab_module_css_default.banner,
|
|
517
|
+
role: "status",
|
|
518
|
+
children: t("status.takenOver")
|
|
519
|
+
}),
|
|
520
|
+
state.stopFailed && (0, react_jsx_runtime.jsx)("p", {
|
|
521
|
+
className: DesktopTab_module_css_default.error,
|
|
522
|
+
role: "alert",
|
|
523
|
+
children: t("action.failed")
|
|
524
|
+
}),
|
|
525
|
+
(0, react_jsx_runtime.jsx)("figure", {
|
|
526
|
+
className: DesktopTab_module_css_default.screen,
|
|
527
|
+
"data-mode": mode,
|
|
528
|
+
children: frame === void 0 ? (0, react_jsx_runtime.jsxs)("p", {
|
|
529
|
+
className: DesktopTab_module_css_default.empty,
|
|
530
|
+
children: [(0, react_jsx_runtime.jsx)(Monitor, {}), t("frame.empty")]
|
|
531
|
+
}) : state.image !== void 0 ? (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)("img", {
|
|
532
|
+
src: state.image,
|
|
533
|
+
alt: t("frame.alt", {
|
|
534
|
+
tool: frame.tool,
|
|
535
|
+
time: when(frame.time)
|
|
536
|
+
}),
|
|
537
|
+
width: frame.attachment.width,
|
|
538
|
+
height: frame.attachment.height
|
|
539
|
+
}), (0, react_jsx_runtime.jsxs)("figcaption", {
|
|
540
|
+
className: DesktopTab_module_css_default.badge,
|
|
541
|
+
"aria-hidden": "true",
|
|
542
|
+
children: [
|
|
543
|
+
frame.tool,
|
|
544
|
+
" · ",
|
|
545
|
+
when(frame.time)
|
|
546
|
+
]
|
|
547
|
+
})] }) : (0, react_jsx_runtime.jsx)("p", {
|
|
548
|
+
className: state.imageFailed ? DesktopTab_module_css_default.error : DesktopTab_module_css_default.empty,
|
|
549
|
+
children: t(state.imageFailed ? "frame.failed" : "frame.loading")
|
|
550
|
+
})
|
|
551
|
+
}),
|
|
552
|
+
frame !== void 0 && (0, react_jsx_runtime.jsx)("p", {
|
|
553
|
+
className: DesktopTab_module_css_default.caption,
|
|
554
|
+
children: t("frame.note")
|
|
555
|
+
}),
|
|
556
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
557
|
+
className: DesktopTab_module_css_default.actions,
|
|
558
|
+
children: state.takenOver ? (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
559
|
+
variant: "primary",
|
|
560
|
+
className: DesktopTab_module_css_default.wide,
|
|
561
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlayOutlineRegular, { size: 14 }),
|
|
562
|
+
"aria-label": t("action.resumeLabel"),
|
|
563
|
+
onClick: resume,
|
|
564
|
+
children: t("action.resume")
|
|
565
|
+
}) : (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
566
|
+
variant: "outline",
|
|
567
|
+
icon: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPauseOutlineRegular, { size: 14 }),
|
|
568
|
+
disabled: busy || !state.running,
|
|
569
|
+
"aria-label": t("action.pauseLabel"),
|
|
570
|
+
onClick: () => {
|
|
571
|
+
run(pause);
|
|
572
|
+
},
|
|
573
|
+
children: t("action.pause")
|
|
574
|
+
}), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
575
|
+
variant: "primary",
|
|
576
|
+
disabled: busy,
|
|
577
|
+
"aria-label": t("action.takeOverLabel"),
|
|
578
|
+
onClick: () => {
|
|
579
|
+
run(takeOver);
|
|
580
|
+
},
|
|
581
|
+
children: t("action.takeOver")
|
|
582
|
+
})] })
|
|
583
|
+
}),
|
|
584
|
+
(0, react_jsx_runtime.jsx)("p", {
|
|
585
|
+
className: DesktopTab_module_css_default.hint,
|
|
586
|
+
children: t(state.takenOver ? "hint.resume" : "hint.stop")
|
|
587
|
+
})
|
|
588
|
+
]
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region lib/types/client/locales.js
|
|
593
|
+
/** Desktop tab copy. */
|
|
594
|
+
const en = {
|
|
595
|
+
"tab.title": "Desktop",
|
|
596
|
+
"tab.description": "See the screen an agent is using, and pause it or take over",
|
|
597
|
+
"where.provider": "Computer use: {provider} on {host}",
|
|
598
|
+
"where.loading": "Checking where computer use runs…",
|
|
599
|
+
"where.none": "No computer-use provider is on, so agents can’t use this computer ({host}). Turn on Computer Use from Plugins to allow it.",
|
|
600
|
+
"where.failed": "Couldn’t reach dsh to check where computer use runs.",
|
|
601
|
+
"status.running": "The agent is working",
|
|
602
|
+
"status.idle": "The agent is not running",
|
|
603
|
+
"status.takenOver": "You’re in control. The agent stays stopped until you resume or send it a message.",
|
|
604
|
+
"frame.alt": "Screen from {tool} at {time}",
|
|
605
|
+
"frame.note": "Updates after each agent action. It is not a live stream.",
|
|
606
|
+
"pill.working": "Working",
|
|
607
|
+
"pill.idle": "Idle",
|
|
608
|
+
"pill.taken": "You’re in control",
|
|
609
|
+
"hint.stop": "Pause stops the current turn. Take over also keeps the agent stopped.",
|
|
610
|
+
"hint.resume": "Resume hands control back. The agent continues on your next message.",
|
|
611
|
+
"frame.empty": "No screen yet. When an agent takes a screenshot while using the computer, it appears here.",
|
|
612
|
+
"frame.loading": "Loading the latest screen…",
|
|
613
|
+
"frame.failed": "Couldn’t load the latest screen.",
|
|
614
|
+
"action.pause": "Pause",
|
|
615
|
+
"action.pauseLabel": "Pause the agent’s current turn",
|
|
616
|
+
"action.takeOver": "Take over",
|
|
617
|
+
"action.takeOverLabel": "Stop the agent and take control of the computer",
|
|
618
|
+
"action.resume": "Resume",
|
|
619
|
+
"action.resumeLabel": "Hand control back to the agent",
|
|
620
|
+
"action.failed": "Couldn’t stop the agent. Try again."
|
|
621
|
+
};
|
|
622
|
+
/** Chinese copy. */
|
|
623
|
+
const zh = {
|
|
624
|
+
"tab.title": "桌面",
|
|
625
|
+
"tab.description": "查看智能体正在使用的屏幕,并暂停或接管",
|
|
626
|
+
"where.provider": "电脑使用:{provider},运行于 {host}",
|
|
627
|
+
"where.loading": "正在检查电脑使用的运行位置…",
|
|
628
|
+
"where.none": "没有开启电脑使用提供方,智能体无法使用这台电脑({host})。可在插件中开启电脑使用。",
|
|
629
|
+
"where.failed": "无法连接 dsh 检查电脑使用的运行位置。",
|
|
630
|
+
"status.running": "智能体正在工作",
|
|
631
|
+
"status.idle": "智能体未运行",
|
|
632
|
+
"status.takenOver": "由你控制。在你恢复或给它发送消息之前,智能体保持停止。",
|
|
633
|
+
"frame.alt": "来自 {tool} 的屏幕,{time}",
|
|
634
|
+
"frame.note": "每次智能体操作后更新,不是实时画面。",
|
|
635
|
+
"pill.working": "工作中",
|
|
636
|
+
"pill.idle": "空闲",
|
|
637
|
+
"pill.taken": "由你控制",
|
|
638
|
+
"hint.stop": "暂停会停止当前轮次。接管还会让智能体保持停止。",
|
|
639
|
+
"hint.resume": "恢复即交还控制权。智能体会在你下一条消息时继续。",
|
|
640
|
+
"frame.empty": "还没有屏幕画面。智能体使用电脑时截取的屏幕会显示在这里。",
|
|
641
|
+
"frame.loading": "正在加载最新屏幕…",
|
|
642
|
+
"frame.failed": "无法加载最新屏幕。",
|
|
643
|
+
"action.pause": "暂停",
|
|
644
|
+
"action.pauseLabel": "暂停智能体当前的轮次",
|
|
645
|
+
"action.takeOver": "接管",
|
|
646
|
+
"action.takeOverLabel": "停止智能体并接管电脑",
|
|
647
|
+
"action.resume": "恢复",
|
|
648
|
+
"action.resumeLabel": "把控制权交还给智能体",
|
|
649
|
+
"action.failed": "无法停止智能体,请重试。"
|
|
650
|
+
};
|
|
651
|
+
//#endregion
|
|
652
|
+
//#region lib/types/client/index.js
|
|
653
|
+
/** Registries the tab and the Remote need. */
|
|
654
|
+
const inject = [
|
|
655
|
+
"locale",
|
|
656
|
+
"sessions",
|
|
657
|
+
"slots",
|
|
658
|
+
"sidebarRightTabs",
|
|
659
|
+
"remote"
|
|
660
|
+
];
|
|
661
|
+
/** Desktop tab implementation identity and keyed Slot dispatch key. */
|
|
662
|
+
const DESKTOP_TAB_ID = "dsh-agent-crew-desktop-ui";
|
|
663
|
+
/**
|
|
664
|
+
* Register the tab type, mount the Remote, then the tab body for the same lifetime.
|
|
665
|
+
* @param ctx - Client plugin context.
|
|
666
|
+
* @returns disposer removing the tab before the Remote.
|
|
667
|
+
*/
|
|
668
|
+
async function apply(ctx) {
|
|
669
|
+
ctx.effect(() => ctx.locale.register("crewDesktop", {
|
|
670
|
+
en,
|
|
671
|
+
zh
|
|
672
|
+
}), "crew-desktop-ui: locale");
|
|
673
|
+
const t = ctx.locale.bind("crewDesktop");
|
|
674
|
+
ctx.effect(() => ctx.sidebarRightTabs.register({
|
|
675
|
+
id: DESKTOP_TAB_ID,
|
|
676
|
+
kind: "crew-desktop",
|
|
677
|
+
title: () => t("tab.title"),
|
|
678
|
+
guide: [{
|
|
679
|
+
id: "desktop",
|
|
680
|
+
order: 62,
|
|
681
|
+
title: () => t("tab.title"),
|
|
682
|
+
description: () => t("tab.description")
|
|
683
|
+
}]
|
|
684
|
+
}), "crew-desktop-ui: tab type");
|
|
685
|
+
const unmount = await ctx.remote.$mount(TYPERT_REMOTE);
|
|
686
|
+
const ui = ctx.inject(["remote.crewDesktop"], (inner) => {
|
|
687
|
+
const where = async () => {
|
|
688
|
+
try {
|
|
689
|
+
const result = await inner.remote.crewDesktop.where();
|
|
690
|
+
return result.ok ? result.value : void 0;
|
|
691
|
+
} catch (_error) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
const models = /* @__PURE__ */ new WeakMap();
|
|
696
|
+
inner.slots.inject("sidebar.right.pane.tab", () => inner.slots.register({
|
|
697
|
+
name: "sidebar.right.pane.tab",
|
|
698
|
+
key: DESKTOP_TAB_ID,
|
|
699
|
+
locale: "crewDesktop",
|
|
700
|
+
inject: (key) => {
|
|
701
|
+
const sessionId = SessionId(key);
|
|
702
|
+
const binding = inner.sessions.binding(sessionId);
|
|
703
|
+
if (binding === void 0) throw new Error(`crew-desktop-ui: unknown Session ${sessionId}`);
|
|
704
|
+
let model = models.get(binding);
|
|
705
|
+
if (model === void 0) {
|
|
706
|
+
model = new DesktopModel(binding.eventSource, binding.session, where);
|
|
707
|
+
models.set(binding, model);
|
|
708
|
+
}
|
|
709
|
+
const current = model;
|
|
710
|
+
return {
|
|
711
|
+
hooks: { desktop: current.source },
|
|
712
|
+
pause: () => current.pause(),
|
|
713
|
+
takeOver: () => current.takeOver(),
|
|
714
|
+
resume: () => {
|
|
715
|
+
current.resume();
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
}, DesktopTab));
|
|
720
|
+
});
|
|
721
|
+
try {
|
|
722
|
+
await ui;
|
|
723
|
+
} catch (error) {
|
|
724
|
+
await ui.dispose();
|
|
725
|
+
await unmount();
|
|
726
|
+
throw error;
|
|
727
|
+
}
|
|
728
|
+
return async () => {
|
|
729
|
+
await ui.dispose();
|
|
730
|
+
await unmount();
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
//#endregion
|
|
734
|
+
exports.DESKTOP_TAB_ID = DESKTOP_TAB_ID;
|
|
735
|
+
exports.DesktopModel = DesktopModel;
|
|
736
|
+
exports.apply = apply;
|
|
737
|
+
exports.inject = inject;
|
|
738
|
+
exports.latestFrame = latestFrame;
|
|
739
|
+
return module.exports;
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
//# sourceMappingURL=client.js.map
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { HostObservable, InjectFace, PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
import type { DesktopState } from './desktop-model.ts';
|
|
3
|
+
/** What the tab reads and does. */
|
|
4
|
+
export interface DesktopTabInjected {
|
|
5
|
+
hooks: {
|
|
6
|
+
desktop: HostObservable<DesktopState>;
|
|
7
|
+
};
|
|
8
|
+
readonly pause: () => Promise<boolean>;
|
|
9
|
+
readonly takeOver: () => Promise<boolean>;
|
|
10
|
+
readonly resume: () => void;
|
|
11
|
+
}
|
|
12
|
+
type Props = InjectFace<DesktopTabInjected> & PropsLocale<'crewDesktop'>;
|
|
13
|
+
/**
|
|
14
|
+
* Render the Desktop tab. Nothing stops the agent without a click.
|
|
15
|
+
* @param props - Desktop state, actions and copy.
|
|
16
|
+
* @returns the tab body.
|
|
17
|
+
*/
|
|
18
|
+
export declare function DesktopTab({ useDesktop, pause, takeOver, resume, t }: Props): import("react").JSX.Element;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=DesktopTab.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Per-Session Desktop state: the latest frame and its image, whether the agent runs, and whether the user took over. */
|
|
2
|
+
import type { HostObservable } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
+
import type { SessionEventSource, SessionFace } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
4
|
+
import type { DesktopLocation } from 'dsh-agent-crew-desktop/types';
|
|
5
|
+
import { type DesktopFrame } from './frame.ts';
|
|
6
|
+
/** What the Desktop tab shows. */
|
|
7
|
+
export interface DesktopState {
|
|
8
|
+
readonly frame: DesktopFrame | undefined;
|
|
9
|
+
/** Loaded image of `frame`, as a data URL. */
|
|
10
|
+
readonly image: string | undefined;
|
|
11
|
+
readonly imageFailed: boolean;
|
|
12
|
+
readonly running: boolean;
|
|
13
|
+
/** The user took over: the agent was stopped and stays stopped until they resume. */
|
|
14
|
+
readonly takenOver: boolean;
|
|
15
|
+
readonly location: DesktopLocation | undefined;
|
|
16
|
+
/** Whether reading the location failed. */
|
|
17
|
+
readonly locationFailed: boolean;
|
|
18
|
+
/** The last Pause or Take over could not stop the agent. */
|
|
19
|
+
readonly stopFailed: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** The Session calls the model needs. */
|
|
22
|
+
export type DesktopSession = Pick<SessionFace, 'getSnapshot' | 'subscribe' | 'cancel' | 'readAttachment'>;
|
|
23
|
+
/** Observable Desktop state for one Session; it follows its sources only while the tab is open. */
|
|
24
|
+
export declare class DesktopModel {
|
|
25
|
+
private readonly events;
|
|
26
|
+
private readonly session;
|
|
27
|
+
private readonly where;
|
|
28
|
+
private state;
|
|
29
|
+
private readonly listeners;
|
|
30
|
+
private detach;
|
|
31
|
+
private revision;
|
|
32
|
+
private locating;
|
|
33
|
+
/** Current state. */
|
|
34
|
+
readonly source: HostObservable<DesktopState>;
|
|
35
|
+
/**
|
|
36
|
+
* @param events - the Session's event window.
|
|
37
|
+
* @param session - the Session, for running state, images and stopping.
|
|
38
|
+
* @param where - reads where computer use runs; undefined when the reply was lost.
|
|
39
|
+
*/
|
|
40
|
+
constructor(events: SessionEventSource, session: DesktopSession, where: () => Promise<DesktopLocation | undefined>);
|
|
41
|
+
private publish;
|
|
42
|
+
private attach;
|
|
43
|
+
private readRunning;
|
|
44
|
+
private readFrame;
|
|
45
|
+
private stop;
|
|
46
|
+
/**
|
|
47
|
+
* Stop the agent's current turn.
|
|
48
|
+
* @returns whether the Host accepted the stop.
|
|
49
|
+
*/
|
|
50
|
+
pause(): Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Stop the agent and mark that the user is in control.
|
|
53
|
+
* @returns whether the agent was stopped.
|
|
54
|
+
*/
|
|
55
|
+
takeOver(): Promise<boolean>;
|
|
56
|
+
/** Hand control back; the agent continues when the user sends it a message. */
|
|
57
|
+
resume(): void;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=desktop-model.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Pick the screen an agent saw last: the newest image any tool returned in the loaded part of the Session. */
|
|
2
|
+
import type { SessionEventLikeEntry } from '@deepseek-ai/dsh-api-session-controller/client';
|
|
3
|
+
import type { ImageBlock } from '@deepseek-ai/dsh-llm';
|
|
4
|
+
/** One screenshot frame and where it came from. */
|
|
5
|
+
export interface DesktopFrame {
|
|
6
|
+
/** Stored image to load. */
|
|
7
|
+
readonly attachment: ImageBlock['attachment'];
|
|
8
|
+
/** Tool that returned it, such as a computer-use tool or `run_code`. */
|
|
9
|
+
readonly tool: string;
|
|
10
|
+
/** When the result was recorded, in epoch milliseconds. */
|
|
11
|
+
readonly time: number;
|
|
12
|
+
/** Session sequence of the result. */
|
|
13
|
+
readonly seq: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Find the newest tool-result image.
|
|
17
|
+
* @param entries - the Session's loaded event window.
|
|
18
|
+
* @returns the latest frame, or undefined when no tool has returned an image.
|
|
19
|
+
*/
|
|
20
|
+
export declare function latestFrame(entries: readonly SessionEventLikeEntry[]): DesktopFrame | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Encode image bytes as a data URL the tab can show without another request.
|
|
23
|
+
* @param bytes - encoded image.
|
|
24
|
+
* @param mediaType - its verified media type.
|
|
25
|
+
* @returns a `data:` URL.
|
|
26
|
+
*/
|
|
27
|
+
export declare function dataUrl(bytes: Uint8Array, mediaType: string): string;
|
|
28
|
+
//# sourceMappingURL=frame.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Mount the crew desktop Remote and register the right-panel Desktop tab. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import { type DesktopKey } from './locales.ts';
|
|
4
|
+
export { latestFrame, type DesktopFrame } from './frame.ts';
|
|
5
|
+
export { DesktopModel, type DesktopState } from './desktop-model.ts';
|
|
6
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
7
|
+
interface LocaleNamespaceMap {
|
|
8
|
+
crewDesktop: DesktopKey;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Registries the tab and the Remote need. */
|
|
12
|
+
export declare const inject: string[];
|
|
13
|
+
/** Desktop tab implementation identity and keyed Slot dispatch key. */
|
|
14
|
+
export declare const DESKTOP_TAB_ID = "dsh-agent-crew-desktop-ui";
|
|
15
|
+
/**
|
|
16
|
+
* Register the tab type, mount the Remote, then the tab body for the same lifetime.
|
|
17
|
+
* @param ctx - Client plugin context.
|
|
18
|
+
* @returns disposer removing the tab before the Remote.
|
|
19
|
+
*/
|
|
20
|
+
export declare function apply(ctx: Context): Promise<() => Promise<void>>;
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Desktop tab copy. */
|
|
2
|
+
export declare const en: {
|
|
3
|
+
'tab.title': string;
|
|
4
|
+
'tab.description': string;
|
|
5
|
+
'where.provider': string;
|
|
6
|
+
'where.loading': string;
|
|
7
|
+
'where.none': string;
|
|
8
|
+
'where.failed': string;
|
|
9
|
+
'status.running': string;
|
|
10
|
+
'status.idle': string;
|
|
11
|
+
'status.takenOver': string;
|
|
12
|
+
'frame.alt': string;
|
|
13
|
+
'frame.note': string;
|
|
14
|
+
'pill.working': string;
|
|
15
|
+
'pill.idle': string;
|
|
16
|
+
'pill.taken': string;
|
|
17
|
+
'hint.stop': string;
|
|
18
|
+
'hint.resume': string;
|
|
19
|
+
'frame.empty': string;
|
|
20
|
+
'frame.loading': string;
|
|
21
|
+
'frame.failed': string;
|
|
22
|
+
'action.pause': string;
|
|
23
|
+
'action.pauseLabel': string;
|
|
24
|
+
'action.takeOver': string;
|
|
25
|
+
'action.takeOverLabel': string;
|
|
26
|
+
'action.resume': string;
|
|
27
|
+
'action.resumeLabel': string;
|
|
28
|
+
'action.failed': string;
|
|
29
|
+
};
|
|
30
|
+
/** Keys shared by every dictionary. */
|
|
31
|
+
export type DesktopKey = keyof typeof en;
|
|
32
|
+
/** Chinese copy. */
|
|
33
|
+
export declare const zh: Record<DesktopKey, string>;
|
|
34
|
+
//# sourceMappingURL=locales.d.ts.map
|
package/locale/de.json
ADDED
package/locale/en.json
ADDED
package/locale/es.json
ADDED
package/locale/fr.json
ADDED
package/locale/ja.json
ADDED
package/locale/pt.json
ADDED
package/locale/ru.json
ADDED
package/locale/zh.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-agent-crew-desktop-ui",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"description": "Browser half of crew desktop: the right-panel Desktop tab with the latest screenshot, Pause and Take over",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/tusharcora/dsh-agent-crew.git",
|
|
8
|
+
"directory": "packages/crew-desktop-ui"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "lib/index.js",
|
|
12
|
+
"types": "lib/types/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./lib/types/index.d.ts",
|
|
16
|
+
"default": "./lib/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./src/*": "./src/*",
|
|
19
|
+
"./package.json": "./package.json",
|
|
20
|
+
"./client": {
|
|
21
|
+
"types": "./lib/types/client/index.d.ts",
|
|
22
|
+
"default": "./lib/client.js"
|
|
23
|
+
},
|
|
24
|
+
"./locale/*.json": "./locale/*.json"
|
|
25
|
+
},
|
|
26
|
+
"dsh": {
|
|
27
|
+
"client": {
|
|
28
|
+
"platform": "web",
|
|
29
|
+
"inject": [
|
|
30
|
+
"@deepseek-ai/dsh-api-remotes",
|
|
31
|
+
"@deepseek-ai/dsh-api-session-controller",
|
|
32
|
+
"@deepseek-ai/dsh-client-locale",
|
|
33
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
34
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"@deepseek-ai/cordis": "~4.1.0"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"locale/*.json",
|
|
44
|
+
"lib/index.js",
|
|
45
|
+
"lib/client.js",
|
|
46
|
+
"lib/types/**/*.d.ts",
|
|
47
|
+
"LICENSE"
|
|
48
|
+
],
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"@deepseek-ai/cordis": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"dsh",
|
|
56
|
+
"dsh-plugin",
|
|
57
|
+
"deepseek-harness",
|
|
58
|
+
"deepseek-harness-plugin",
|
|
59
|
+
"deepseek",
|
|
60
|
+
"dsh-agent-crew"
|
|
61
|
+
],
|
|
62
|
+
"author": "Tushar (https://github.com/tusharcora)",
|
|
63
|
+
"homepage": "https://github.com/tusharcora/dsh-agent-crew#readme",
|
|
64
|
+
"bugs": {
|
|
65
|
+
"url": "https://github.com/tusharcora/dsh-agent-crew/issues"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"dsh": ">=0.2.2-rc.1 <0.3.0"
|
|
69
|
+
}
|
|
70
|
+
}
|