@guguo/dsh-offline-store 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/cordis.patch.yml +5 -0
- package/lib/client.js +173 -0
- package/lib/index.js +339 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +75 -0
- package/lib/typert.remote-client.d.ts +24 -0
- package/lib/typert.remote-client.js +69 -0
- package/lib/types/client/Panel.d.ts +15 -0
- package/lib/types/client/Panel.js +65 -0
- package/lib/types/client/index.d.ts +5 -0
- package/lib/types/client/index.js +12 -0
- package/lib/types/index.d.ts +22 -0
- package/lib/types/index.js +315 -0
- package/lib/types/types.d.ts +21 -0
- package/lib/types/types.js +3 -0
- package/package.json +91 -0
package/cordis.patch.yml
ADDED
package/lib/client.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@guguo/dsh-offline-store",
|
|
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
|
+
//#region \0dsh-css:D:\deepseek-harness\packages\extensions\offline-store\src\client\panel.module.css.mjs
|
|
10
|
+
const css = "._95sPDG_entry{cursor:pointer;background:0 0;border:none;border-radius:6px;flex:none;justify-content:center;align-items:center;width:28px;height:28px;padding:0;font-size:16px;display:inline-flex}._95sPDG_entry:hover{background:var(--dsh-bg-hover,#8080801f)}._95sPDG_panel{background:var(--dsh-panel-bg,#fff);width:360px;max-width:calc(100vw - 24px);max-height:72vh;color:var(--dsh-fg,inherit);border:1px solid var(--dsh-border,#80808059);z-index:999;pointer-events:auto;border-radius:12px;flex-direction:column;font-size:13px;display:flex;position:fixed;top:72px;left:12px;box-shadow:0 12px 40px #00000040}._95sPDG_head{border-bottom:1px solid var(--dsh-border,#80808040);justify-content:space-between;align-items:center;padding:10px 14px;font-weight:600;display:flex}._95sPDG_close{cursor:pointer;background:0 0;border:none;font-size:15px}._95sPDG_list{padding:8px;overflow-y:auto}._95sPDG_item{border-radius:8px;align-items:flex-start;gap:10px;padding:10px;display:flex}._95sPDG_item:hover{background:var(--dsh-bg-hover,#80808014)}._95sPDG_body{flex:1;min-width:0}._95sPDG_title{align-items:center;gap:6px;font-weight:600;display:flex}._95sPDG_type{color:#4a6cf7;background:#648cff29;border-radius:999px;padding:1px 6px;font-size:10px}._95sPDG_typePlugin{color:#c77700;background:#ffa03c29}._95sPDG_typePreset{color:#1e8e4e;background:#50c8782e}._95sPDG_desc{color:var(--dsh-fg-muted,#0000008c);margin-top:2px;font-size:12px}._95sPDG_btn{border:1px solid var(--dsh-border,#80808059);cursor:pointer;white-space:nowrap;background:0 0;border-radius:8px;flex-shrink:0;padding:4px 12px;font-size:12px}._95sPDG_btn:hover:not(:disabled){background:#648cff1f}._95sPDG_btn:disabled{opacity:.6;cursor:default}._95sPDG_done{color:#1e8e4e;border-color:#50c87880}._95sPDG_status,._95sPDG_statusOk,._95sPDG_statusErr{border-top:1px solid var(--dsh-border,#80808040);white-space:pre-wrap;max-height:88px;color:var(--dsh-fg-muted,#0009);padding:8px 14px;font-size:12px;overflow-y:auto}._95sPDG_statusOk{color:#1e8e4e}._95sPDG_statusErr{color:#d0453c}";
|
|
11
|
+
const tagId = "@guguo/dsh-offline-store/panel.module.css";
|
|
12
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
13
|
+
const tag = document.createElement("style");
|
|
14
|
+
tag.dataset.plugin = "@guguo/dsh-offline-store";
|
|
15
|
+
tag.dataset.pluginCss = tagId;
|
|
16
|
+
tag.textContent = css;
|
|
17
|
+
document.head.appendChild(tag);
|
|
18
|
+
}
|
|
19
|
+
var panel_module_css_default = {
|
|
20
|
+
"body": "_95sPDG_body",
|
|
21
|
+
"btn": "_95sPDG_btn",
|
|
22
|
+
"close": "_95sPDG_close",
|
|
23
|
+
"desc": "_95sPDG_desc",
|
|
24
|
+
"done": "_95sPDG_done",
|
|
25
|
+
"entry": "_95sPDG_entry",
|
|
26
|
+
"head": "_95sPDG_head",
|
|
27
|
+
"item": "_95sPDG_item",
|
|
28
|
+
"list": "_95sPDG_list",
|
|
29
|
+
"panel": "_95sPDG_panel",
|
|
30
|
+
"status": "_95sPDG_status",
|
|
31
|
+
"statusErr": "_95sPDG_statusErr",
|
|
32
|
+
"statusOk": "_95sPDG_statusOk",
|
|
33
|
+
"title": "_95sPDG_title",
|
|
34
|
+
"type": "_95sPDG_type",
|
|
35
|
+
"typePlugin": "_95sPDG_typePlugin",
|
|
36
|
+
"typePreset": "_95sPDG_typePreset"
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region lib/types/client/Panel.js
|
|
40
|
+
/** 商店 UI 组件:入口按钮 + 面板(模块级共享开关状态)。 */
|
|
41
|
+
const panelState = {
|
|
42
|
+
open: false,
|
|
43
|
+
listeners: /* @__PURE__ */ new Set()
|
|
44
|
+
};
|
|
45
|
+
const setPanelOpen = (v) => {
|
|
46
|
+
panelState.open = v;
|
|
47
|
+
panelState.listeners.forEach((fn) => fn(v));
|
|
48
|
+
};
|
|
49
|
+
const subscribePanel = (fn) => {
|
|
50
|
+
panelState.listeners.add(fn);
|
|
51
|
+
return () => {
|
|
52
|
+
panelState.listeners.delete(fn);
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/** 输入框左端的 📦 入口。 */
|
|
56
|
+
function StoreEntry() {
|
|
57
|
+
return (0, react_jsx_runtime.jsx)("button", {
|
|
58
|
+
type: "button",
|
|
59
|
+
className: panel_module_css_default.entry,
|
|
60
|
+
title: "离线应用商店",
|
|
61
|
+
"aria-label": "离线应用商店",
|
|
62
|
+
onClick: () => setPanelOpen(!panelState.open),
|
|
63
|
+
children: (0, react_jsx_runtime.jsx)("span", {
|
|
64
|
+
"aria-hidden": "true",
|
|
65
|
+
children: "📦"
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** 面板工厂:闭包持有 remote,供入口按钮与面板共享开关状态。 */
|
|
70
|
+
function makePanel(remote) {
|
|
71
|
+
return function Panel() {
|
|
72
|
+
const [open, setOpen] = (0, react.useState)(panelState.open);
|
|
73
|
+
const [items, setItems] = (0, react.useState)(null);
|
|
74
|
+
const [busy, setBusy] = (0, react.useState)(null);
|
|
75
|
+
const [status, setStatus] = (0, react.useState)("");
|
|
76
|
+
(0, react.useEffect)(() => subscribePanel(setOpen), []);
|
|
77
|
+
const refresh = (0, react.useCallback)(() => {
|
|
78
|
+
remote.list().then((res) => {
|
|
79
|
+
setItems(res.items ?? []);
|
|
80
|
+
if (res.error !== void 0) setStatus("加载提示: " + res.error);
|
|
81
|
+
}).catch((e) => {
|
|
82
|
+
setStatus("加载失败: " + (e instanceof Error ? e.message : String(e)));
|
|
83
|
+
});
|
|
84
|
+
}, [remote]);
|
|
85
|
+
(0, react.useEffect)(() => {
|
|
86
|
+
if (!open) return;
|
|
87
|
+
setStatus("");
|
|
88
|
+
refresh();
|
|
89
|
+
}, [open, refresh]);
|
|
90
|
+
const install = (id) => {
|
|
91
|
+
if (busy !== null) return;
|
|
92
|
+
setBusy(id);
|
|
93
|
+
setStatus("正在安装 " + id + " …");
|
|
94
|
+
remote.install({ id }).then((res) => {
|
|
95
|
+
setStatus(res.ok ? "✅ " + res.message : "❌ " + res.message);
|
|
96
|
+
if (res.ok) refresh();
|
|
97
|
+
}).catch((e) => {
|
|
98
|
+
setStatus("❌ 安装失败: " + (e instanceof Error ? e.message : String(e)));
|
|
99
|
+
}).finally(() => setBusy(null));
|
|
100
|
+
};
|
|
101
|
+
if (!open) return null;
|
|
102
|
+
return (0, react_jsx_runtime.jsxs)("div", {
|
|
103
|
+
className: panel_module_css_default.panel,
|
|
104
|
+
children: [
|
|
105
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
106
|
+
className: panel_module_css_default.head,
|
|
107
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: "📦 离线应用商店" }), (0, react_jsx_runtime.jsx)("button", {
|
|
108
|
+
type: "button",
|
|
109
|
+
className: panel_module_css_default.close,
|
|
110
|
+
onClick: () => setPanelOpen(false),
|
|
111
|
+
"aria-label": "关闭",
|
|
112
|
+
children: "✕"
|
|
113
|
+
})]
|
|
114
|
+
}),
|
|
115
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
116
|
+
className: panel_module_css_default.list,
|
|
117
|
+
children: items === null ? (0, react_jsx_runtime.jsx)("div", { children: "加载中…" }) : items.length === 0 ? (0, react_jsx_runtime.jsx)("div", { children: "(暂无可用条目)" }) : items.map((it) => (0, react_jsx_runtime.jsxs)("div", {
|
|
118
|
+
className: panel_module_css_default.item,
|
|
119
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
120
|
+
className: panel_module_css_default.body,
|
|
121
|
+
children: [(0, react_jsx_runtime.jsxs)("div", {
|
|
122
|
+
className: panel_module_css_default.title,
|
|
123
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: it.name }), (0, react_jsx_runtime.jsx)("span", {
|
|
124
|
+
className: `${panel_module_css_default.type} ${it.type === "plugin" ? panel_module_css_default.typePlugin : it.type === "preset" ? panel_module_css_default.typePreset : ""}`,
|
|
125
|
+
children: it.type === "skill" ? "技能" : it.type === "plugin" ? "插件" : "预设"
|
|
126
|
+
})]
|
|
127
|
+
}), (0, react_jsx_runtime.jsx)("div", {
|
|
128
|
+
className: panel_module_css_default.desc,
|
|
129
|
+
children: it.description
|
|
130
|
+
})]
|
|
131
|
+
}), (0, react_jsx_runtime.jsx)("button", {
|
|
132
|
+
type: "button",
|
|
133
|
+
className: `${panel_module_css_default.btn}${it.installed ? " " + panel_module_css_default.done : ""}`,
|
|
134
|
+
disabled: busy !== null || it.installed,
|
|
135
|
+
onClick: () => install(it.id),
|
|
136
|
+
children: it.installed ? "已安装" : "安装"
|
|
137
|
+
})]
|
|
138
|
+
}, it.id))
|
|
139
|
+
}),
|
|
140
|
+
status !== "" && (0, react_jsx_runtime.jsx)("div", {
|
|
141
|
+
className: status.startsWith("✅") ? panel_module_css_default.statusOk : status.startsWith("❌") ? panel_module_css_default.statusErr : panel_module_css_default.status,
|
|
142
|
+
children: status
|
|
143
|
+
})
|
|
144
|
+
]
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
//#region lib/types/client/index.js
|
|
150
|
+
/** 离线应用商店 Client 半:输入框左端入口 + 左上浮层面板。 */
|
|
151
|
+
const inject = ["slots", "remote"];
|
|
152
|
+
function apply(ctx) {
|
|
153
|
+
const remote = ctx.remote;
|
|
154
|
+
const Panel = makePanel(remote);
|
|
155
|
+
ctx.slots.inject("conversation.input.left", () => ctx.slots.register({
|
|
156
|
+
name: "conversation.input.left",
|
|
157
|
+
id: "offline-store-entry",
|
|
158
|
+
order: 100
|
|
159
|
+
}, StoreEntry));
|
|
160
|
+
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
161
|
+
name: "shell.overlay",
|
|
162
|
+
id: "offline-store-panel",
|
|
163
|
+
order: 100
|
|
164
|
+
}, Panel));
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
exports.apply = apply;
|
|
168
|
+
exports.inject = inject;
|
|
169
|
+
return module.exports;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
//# sourceMappingURL=client.js.map
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
2
|
+
//#region lib/types/index.js
|
|
3
|
+
/** 离线应用商店 Host 半:Remote 控制器,暴露 catalog/install 给浏览器端。 */
|
|
4
|
+
var __runInitializers = function(thisArg, initializers, value) {
|
|
5
|
+
var useValue = arguments.length > 2;
|
|
6
|
+
for (var i = 0; i < initializers.length; i++) value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
7
|
+
return useValue ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
10
|
+
function accept(f) {
|
|
11
|
+
if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected");
|
|
12
|
+
return f;
|
|
13
|
+
}
|
|
14
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
15
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
16
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
17
|
+
var _, done = false;
|
|
18
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
19
|
+
var context = {};
|
|
20
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
21
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
22
|
+
context.addInitializer = function(f) {
|
|
23
|
+
if (done) throw new TypeError("Cannot add initializers after decoration has completed");
|
|
24
|
+
extraInitializers.push(accept(f || null));
|
|
25
|
+
};
|
|
26
|
+
var result = (0, decorators[i])(kind === "accessor" ? {
|
|
27
|
+
get: descriptor.get,
|
|
28
|
+
set: descriptor.set
|
|
29
|
+
} : descriptor[key], context);
|
|
30
|
+
if (kind === "accessor") {
|
|
31
|
+
if (result === void 0) continue;
|
|
32
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
33
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
34
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
35
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
36
|
+
} else if (_ = accept(result)) if (kind === "field") initializers.unshift(_);
|
|
37
|
+
else descriptor[key] = _;
|
|
38
|
+
}
|
|
39
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
40
|
+
done = true;
|
|
41
|
+
};
|
|
42
|
+
/** 内置演示条目:技能 / 插件 / 预设各一个。 */
|
|
43
|
+
const EMBEDDED = [
|
|
44
|
+
{
|
|
45
|
+
id: "skill-offline-demo",
|
|
46
|
+
type: "skill",
|
|
47
|
+
name: "离线演示技能",
|
|
48
|
+
description: "演示条目:安装后写入用户级技能目录,所有会话可见、重启保留。",
|
|
49
|
+
version: "1.0.0",
|
|
50
|
+
installed: false
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "plugin-str-replace",
|
|
54
|
+
type: "plugin",
|
|
55
|
+
name: "字符串替换编辑器",
|
|
56
|
+
description: "演示条目:安装后把插件行追加到本地预设 offline-store,下次会话生效。",
|
|
57
|
+
version: "1.0.0",
|
|
58
|
+
installed: false
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "preset-minimal-demo",
|
|
62
|
+
type: "preset",
|
|
63
|
+
name: "极简模式(演示预设)",
|
|
64
|
+
description: "演示条目:把「极简模式」复制为本地预设 minimal-demo,新建会话时可选择。",
|
|
65
|
+
version: "1.0.0",
|
|
66
|
+
installed: false
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
/** 商店宿主预设 id(插件条目的写入目标)。 */
|
|
70
|
+
const HOST_PRESET = "offline-store";
|
|
71
|
+
/** 带内嵌演示技能的 SKILL.md 内容(离线演示条目专用)。 */
|
|
72
|
+
const DEMO_SKILL_CONTENT = [
|
|
73
|
+
"---",
|
|
74
|
+
"name: offline-demo",
|
|
75
|
+
"description: 离线商店演示技能:安装后立即出现在技能目录中,用于验证离线技能安装链路。",
|
|
76
|
+
"---",
|
|
77
|
+
"",
|
|
78
|
+
"# 离线演示技能",
|
|
79
|
+
"",
|
|
80
|
+
"这是一个通过「离线应用商店」安装的技能。",
|
|
81
|
+
"",
|
|
82
|
+
"## 用法",
|
|
83
|
+
"",
|
|
84
|
+
"1. 点击输入框左端的「📦 应用商店」图标打开面板",
|
|
85
|
+
"2. 找到本技能并点击「安装」",
|
|
86
|
+
"3. 安装后它会出现在技能目录中,且重启后依然存在。",
|
|
87
|
+
""
|
|
88
|
+
].join("\n");
|
|
89
|
+
/** Remote-only 服务:目录 + 安装。客户端经 ctx.remote.offlineStore 调用。 */
|
|
90
|
+
let OfflineStoreGateway = (() => {
|
|
91
|
+
let _classSuper = TypertRemoteService;
|
|
92
|
+
let _instanceExtraInitializers = [];
|
|
93
|
+
let _list_decorators;
|
|
94
|
+
let _install_decorators;
|
|
95
|
+
return class OfflineStoreGateway extends _classSuper {
|
|
96
|
+
static {
|
|
97
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
98
|
+
_list_decorators = [Remote("list")];
|
|
99
|
+
_install_decorators = [Remote("install")];
|
|
100
|
+
__esDecorate(this, null, _list_decorators, {
|
|
101
|
+
kind: "method",
|
|
102
|
+
name: "list",
|
|
103
|
+
static: false,
|
|
104
|
+
private: false,
|
|
105
|
+
access: {
|
|
106
|
+
has: (obj) => "list" in obj,
|
|
107
|
+
get: (obj) => obj.list
|
|
108
|
+
},
|
|
109
|
+
metadata: _metadata
|
|
110
|
+
}, null, _instanceExtraInitializers);
|
|
111
|
+
__esDecorate(this, null, _install_decorators, {
|
|
112
|
+
kind: "method",
|
|
113
|
+
name: "install",
|
|
114
|
+
static: false,
|
|
115
|
+
private: false,
|
|
116
|
+
access: {
|
|
117
|
+
has: (obj) => "install" in obj,
|
|
118
|
+
get: (obj) => obj.install
|
|
119
|
+
},
|
|
120
|
+
metadata: _metadata
|
|
121
|
+
}, null, _instanceExtraInitializers);
|
|
122
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, {
|
|
123
|
+
enumerable: true,
|
|
124
|
+
configurable: true,
|
|
125
|
+
writable: true,
|
|
126
|
+
value: _metadata
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
static inject = ["fs", "agentPresets"];
|
|
130
|
+
constructor(ctx) {
|
|
131
|
+
super(ctx, "offlineStore");
|
|
132
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
133
|
+
}
|
|
134
|
+
async list() {
|
|
135
|
+
try {
|
|
136
|
+
const items = [];
|
|
137
|
+
for (const it of EMBEDDED) items.push({
|
|
138
|
+
...it,
|
|
139
|
+
installed: await this.isInstalled(it.id)
|
|
140
|
+
});
|
|
141
|
+
for (const bundle of await this.listBundles()) items.push({
|
|
142
|
+
id: bundle.id,
|
|
143
|
+
type: "skill",
|
|
144
|
+
name: bundle.name,
|
|
145
|
+
description: bundle.description,
|
|
146
|
+
version: bundle.version,
|
|
147
|
+
installed: await this.skillInstalled(bundle.skillName)
|
|
148
|
+
});
|
|
149
|
+
return { items };
|
|
150
|
+
} catch (error) {
|
|
151
|
+
return {
|
|
152
|
+
items: [],
|
|
153
|
+
error: errorMessage(error)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async install(args) {
|
|
158
|
+
const id = args.id;
|
|
159
|
+
const embedded = EMBEDDED.find((x) => x.id === id);
|
|
160
|
+
const bundle = (await this.listBundles()).find((b) => b.id === id);
|
|
161
|
+
const kind = embedded?.type ?? (bundle !== void 0 ? "skill" : void 0);
|
|
162
|
+
if (kind === void 0) return {
|
|
163
|
+
ok: false,
|
|
164
|
+
message: "未找到条目: " + id
|
|
165
|
+
};
|
|
166
|
+
try {
|
|
167
|
+
if (kind === "skill") {
|
|
168
|
+
const content = bundle === void 0 ? DEMO_SKILL_CONTENT : await this.readText(bundle.path);
|
|
169
|
+
if (content === void 0) return {
|
|
170
|
+
ok: false,
|
|
171
|
+
message: "技能内容缺失"
|
|
172
|
+
};
|
|
173
|
+
const home = await this.resolveHome();
|
|
174
|
+
const target = await this.fs().resolve(`${home}/skills/${bundle?.skillName ?? "offline-demo"}/SKILL.md`);
|
|
175
|
+
await this.fs().writeText(target, content, void 0, void 0, fullAccess(`${home}/skills`));
|
|
176
|
+
return {
|
|
177
|
+
ok: true,
|
|
178
|
+
message: "技能已安装:写入用户级技能目录,立即生效且重启保留"
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (kind === "plugin") {
|
|
182
|
+
let comp;
|
|
183
|
+
try {
|
|
184
|
+
comp = await this.ctx.agentPresets.read(HOST_PRESET);
|
|
185
|
+
} catch {
|
|
186
|
+
await this.ctx.agentPresets.copy("cordis", HOST_PRESET, "离线商店预设");
|
|
187
|
+
comp = await this.ctx.agentPresets.read(HOST_PRESET);
|
|
188
|
+
}
|
|
189
|
+
if (comp.includes("tool-str-replace-editor")) return {
|
|
190
|
+
ok: true,
|
|
191
|
+
message: "插件已在预设 offline-store 中"
|
|
192
|
+
};
|
|
193
|
+
const home = await this.resolveHome();
|
|
194
|
+
const row = " - id: tool-str-replace-editor\n name: '@deepseek-ai/dsh-tool-str-replace-editor'\n";
|
|
195
|
+
const target = await this.fs().resolve(`${home}/.agent-presets/${HOST_PRESET}/agent.cordis.yml`);
|
|
196
|
+
await this.fs().writeText(target, comp.replace(/\s*$/, "\n") + row, void 0, void 0, fullAccess(`${home}/.agent-presets/${HOST_PRESET}`));
|
|
197
|
+
return {
|
|
198
|
+
ok: true,
|
|
199
|
+
message: "插件已写入预设 offline-store,下次会话生效"
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
if (kind === "preset") {
|
|
203
|
+
if (!(await this.ctx.agentPresets.list()).some((p) => p.id === "minimal-demo")) await this.ctx.agentPresets.copy("minimal", "minimal-demo", "极简模式演示");
|
|
204
|
+
return {
|
|
205
|
+
ok: true,
|
|
206
|
+
message: "预设已复制为本地预设 minimal-demo,新建会话时可选择"
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
ok: false,
|
|
211
|
+
message: "未知条目类型"
|
|
212
|
+
};
|
|
213
|
+
} catch (error) {
|
|
214
|
+
return {
|
|
215
|
+
ok: false,
|
|
216
|
+
message: errorMessage(error)
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
fs() {
|
|
221
|
+
return this.ctx.get("fs");
|
|
222
|
+
}
|
|
223
|
+
/** 自动探测 DSH_HOME:从用户预设路径逐级剥离 .agent-presets 目录。 */
|
|
224
|
+
async resolveHome() {
|
|
225
|
+
let anchor = (await this.ctx.agentPresets.list()).find((p) => p.trust === "user");
|
|
226
|
+
if (anchor === void 0) {
|
|
227
|
+
await this.ctx.agentPresets.copy("cordis", HOST_PRESET, "离线商店预设");
|
|
228
|
+
anchor = (await this.ctx.agentPresets.list()).find((p) => p.id === HOST_PRESET);
|
|
229
|
+
if (anchor === void 0) throw new Error("无法创建商店宿主预设 offline-store");
|
|
230
|
+
}
|
|
231
|
+
return anchor.path.replace(/[\\/][^\\/]+$/, "").replace(/[\\/][^\\/]+$/, "").replace(/[\\/]\.agent-presets$/, "");
|
|
232
|
+
}
|
|
233
|
+
async skillInstalled(name) {
|
|
234
|
+
const home = await this.resolveHome();
|
|
235
|
+
return await this.fileExists(`${home}/skills/${name}/SKILL.md`);
|
|
236
|
+
}
|
|
237
|
+
async fileExists(path) {
|
|
238
|
+
try {
|
|
239
|
+
const target = await this.fs().resolve(path);
|
|
240
|
+
return await this.fs().stat(target) !== void 0;
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
async readText(path) {
|
|
246
|
+
try {
|
|
247
|
+
return await this.fs().readText(await this.fs().resolve(path));
|
|
248
|
+
} catch {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async isInstalled(embeddedId) {
|
|
253
|
+
if (embeddedId === "skill-offline-demo") return await this.skillInstalled("offline-demo");
|
|
254
|
+
if (embeddedId === "plugin-str-replace") try {
|
|
255
|
+
return (await this.ctx.agentPresets.read(HOST_PRESET)).includes("tool-str-replace-editor");
|
|
256
|
+
} catch {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
if (embeddedId === "preset-minimal-demo") return (await this.ctx.agentPresets.list()).some((p) => p.id === "minimal-demo");
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
async listBundles() {
|
|
263
|
+
const out = [];
|
|
264
|
+
let entries;
|
|
265
|
+
try {
|
|
266
|
+
const home = await this.resolveHome();
|
|
267
|
+
const dir = await this.fs().resolve(`${home}/skills-store`);
|
|
268
|
+
if (await this.fs().stat(dir) === void 0) return out;
|
|
269
|
+
entries = await this.fs().listDir(dir);
|
|
270
|
+
} catch {
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
for (const entry of entries) {
|
|
274
|
+
if (entry.type !== "directory") continue;
|
|
275
|
+
const skillPath = `${entry.target.displayPath}/SKILL.md`;
|
|
276
|
+
const text = await this.readText(skillPath);
|
|
277
|
+
if (text === void 0) continue;
|
|
278
|
+
const meta = parseFrontmatter(text);
|
|
279
|
+
if (meta === null || meta.name === void 0 || meta.description === void 0) continue;
|
|
280
|
+
out.push({
|
|
281
|
+
id: "bundle-" + entry.name,
|
|
282
|
+
name: meta.name,
|
|
283
|
+
description: meta.description,
|
|
284
|
+
version: "1.0.0",
|
|
285
|
+
skillName: entry.name,
|
|
286
|
+
path: skillPath
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
})();
|
|
293
|
+
function fullAccess(root) {
|
|
294
|
+
return {
|
|
295
|
+
mode: "danger-full-access",
|
|
296
|
+
workspaceRoot: root
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/** 从 SKILL.md 的 YAML frontmatter 里提取 name/description(最小实现)。 */
|
|
300
|
+
function parseFrontmatter(text) {
|
|
301
|
+
const lines = text.split("\n");
|
|
302
|
+
const first = lines[0];
|
|
303
|
+
if (first === void 0 || lines.length < 3 || first.trim() !== "---") return null;
|
|
304
|
+
let end = -1;
|
|
305
|
+
for (let i = 1; i < lines.length; i++) {
|
|
306
|
+
const line = lines[i];
|
|
307
|
+
if (line !== void 0 && line.trim() === "---") {
|
|
308
|
+
end = i;
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (end < 0) return null;
|
|
313
|
+
const data = {};
|
|
314
|
+
for (let i = 1; i < end; i++) {
|
|
315
|
+
const line = lines[i];
|
|
316
|
+
if (line === void 0) continue;
|
|
317
|
+
const m = /^([A-Za-z0-9-]+):\s*(.*)$/.exec(line);
|
|
318
|
+
if (m === null) continue;
|
|
319
|
+
const key = m[1];
|
|
320
|
+
if (key === void 0) continue;
|
|
321
|
+
const raw = m[2];
|
|
322
|
+
if (raw === void 0) continue;
|
|
323
|
+
let v = raw.trim();
|
|
324
|
+
if (v.startsWith("\"") && v.endsWith("\"") || v.startsWith("'") && v.endsWith("'")) v = v.slice(1, -1);
|
|
325
|
+
data[key] = v;
|
|
326
|
+
}
|
|
327
|
+
const name = data.name;
|
|
328
|
+
const description = data.description;
|
|
329
|
+
if (name === void 0 || description === void 0) return null;
|
|
330
|
+
return {
|
|
331
|
+
name,
|
|
332
|
+
description
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function errorMessage(error) {
|
|
336
|
+
return error instanceof Error ? error.message : String(error);
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
339
|
+
export { OfflineStoreGateway, OfflineStoreGateway as default };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* Generated by @deepseek-ai/dsh-typert-generator from FaceModel — do not edit. */
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
const _guguo_dsh_offline_store_offlineStore_install_parameter_0$schema = z.object({
|
|
5
|
+
'id': z.string(),
|
|
6
|
+
})
|
|
7
|
+
const _guguo_dsh_offline_store_offlineStore_install_result$schema = z.object({
|
|
8
|
+
'ok': z.boolean(),
|
|
9
|
+
'message': z.string(),
|
|
10
|
+
})
|
|
11
|
+
const _guguo_dsh_offline_store_offlineStore_list_result$schema = z.object({
|
|
12
|
+
'items': z.array(z.object({
|
|
13
|
+
'id': z.string(),
|
|
14
|
+
'type': z.union([z.literal("skill"), z.literal("plugin"), z.literal("preset")]),
|
|
15
|
+
'name': z.string(),
|
|
16
|
+
'description': z.string(),
|
|
17
|
+
'version': z.string(),
|
|
18
|
+
'installed': z.boolean(),
|
|
19
|
+
})),
|
|
20
|
+
'error': z.string().optional(),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export const TYPERT = {
|
|
24
|
+
package: '@guguo/dsh-offline-store',
|
|
25
|
+
face: 'host',
|
|
26
|
+
schemas: [
|
|
27
|
+
],
|
|
28
|
+
invocations: [
|
|
29
|
+
{
|
|
30
|
+
id: '@guguo/dsh-offline-store#offlineStore/install',
|
|
31
|
+
service: 'offlineStore',
|
|
32
|
+
namespace: 'offlineStore',
|
|
33
|
+
method: 'install',
|
|
34
|
+
invocation: { kind: 'direct' },
|
|
35
|
+
parameters: [
|
|
36
|
+
{
|
|
37
|
+
name: 'args',
|
|
38
|
+
wire: 'args',
|
|
39
|
+
source: 'json',
|
|
40
|
+
codec: {
|
|
41
|
+
mode: 'strict',
|
|
42
|
+
typeSymbol: '@guguo/dsh-offline-store/types#InstallArgs',
|
|
43
|
+
schema: _guguo_dsh_offline_store_offlineStore_install_parameter_0$schema,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
result: {
|
|
48
|
+
mode: 'strict',
|
|
49
|
+
typeSymbol: '@guguo/dsh-offline-store/types#InstallResult',
|
|
50
|
+
schema: _guguo_dsh_offline_store_offlineStore_install_result$schema,
|
|
51
|
+
},
|
|
52
|
+
sourceLocation: {"file":"packages/extensions/offline-store/src/index.ts","line":93,"column":9},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: '@guguo/dsh-offline-store#offlineStore/list',
|
|
56
|
+
service: 'offlineStore',
|
|
57
|
+
namespace: 'offlineStore',
|
|
58
|
+
method: 'list',
|
|
59
|
+
invocation: { kind: 'direct' },
|
|
60
|
+
parameters: [
|
|
61
|
+
],
|
|
62
|
+
result: {
|
|
63
|
+
mode: 'strict',
|
|
64
|
+
typeSymbol: '@guguo/dsh-offline-store/types#CatalogView',
|
|
65
|
+
schema: _guguo_dsh_offline_store_offlineStore_list_result$schema,
|
|
66
|
+
},
|
|
67
|
+
sourceLocation: {"file":"packages/extensions/offline-store/src/index.ts","line":70,"column":9},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
model: {
|
|
71
|
+
"services": [],
|
|
72
|
+
"events": [],
|
|
73
|
+
"objects": []
|
|
74
|
+
},
|
|
75
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
|
|
2
|
+
import type {
|
|
3
|
+
RemoteResult,
|
|
4
|
+
TypertRemoteContribution,
|
|
5
|
+
} from '@deepseek-ai/dsh-typert-protocol'
|
|
6
|
+
import type { CatalogView, InstallArgs, InstallResult } from '@guguo/dsh-offline-store/types'
|
|
7
|
+
|
|
8
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
9
|
+
interface TypertRemoteNamespace$6f66666c696e6553746f7265 {
|
|
10
|
+
install: (args: InstallArgs) => Promise<RemoteResult<InstallResult>>
|
|
11
|
+
list: () => Promise<RemoteResult<CatalogView>>
|
|
12
|
+
}
|
|
13
|
+
interface TypertRemoteMap {
|
|
14
|
+
'offlineStore/install': (args: InstallArgs) => Promise<RemoteResult<InstallResult>>
|
|
15
|
+
'offlineStore/list': () => Promise<RemoteResult<CatalogView>>
|
|
16
|
+
}
|
|
17
|
+
interface TypertRemoteNamespaceMap {
|
|
18
|
+
'offlineStore': TypertRemoteNamespace$6f66666c696e6553746f7265
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export declare const TYPERT_REMOTE: TypertRemoteContribution
|
|
23
|
+
export default TYPERT_REMOTE
|
|
24
|
+
//# sourceMappingURL=typert.remote-client.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
const _guguo_dsh_offline_store_offlineStore_install_parameter_0$schema = z.object({
|
|
5
|
+
'id': z.string(),
|
|
6
|
+
})
|
|
7
|
+
const _guguo_dsh_offline_store_offlineStore_install_result$schema = z.object({
|
|
8
|
+
'ok': z.boolean(),
|
|
9
|
+
'message': z.string(),
|
|
10
|
+
})
|
|
11
|
+
const _guguo_dsh_offline_store_offlineStore_list_result$schema = z.object({
|
|
12
|
+
'items': z.array(z.object({
|
|
13
|
+
'id': z.string(),
|
|
14
|
+
'type': z.union([z.literal("skill"), z.literal("plugin"), z.literal("preset")]),
|
|
15
|
+
'name': z.string(),
|
|
16
|
+
'description': z.string(),
|
|
17
|
+
'version': z.string(),
|
|
18
|
+
'installed': z.boolean(),
|
|
19
|
+
})),
|
|
20
|
+
'error': z.string().optional(),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export const TYPERT_REMOTE = {
|
|
24
|
+
package: '@guguo/dsh-offline-store',
|
|
25
|
+
descriptors: [
|
|
26
|
+
{
|
|
27
|
+
id: '@guguo/dsh-offline-store#offlineStore/install',
|
|
28
|
+
service: 'offlineStore',
|
|
29
|
+
namespace: 'offlineStore',
|
|
30
|
+
method: 'install',
|
|
31
|
+
invocation: { kind: 'direct' },
|
|
32
|
+
parameters: [
|
|
33
|
+
{
|
|
34
|
+
name: 'args',
|
|
35
|
+
wire: 'args',
|
|
36
|
+
source: 'json',
|
|
37
|
+
codec: {
|
|
38
|
+
mode: 'strict',
|
|
39
|
+
typeSymbol: '@guguo/dsh-offline-store/types#InstallArgs',
|
|
40
|
+
schema: _guguo_dsh_offline_store_offlineStore_install_parameter_0$schema,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
result: {
|
|
45
|
+
mode: 'strict',
|
|
46
|
+
typeSymbol: '@guguo/dsh-offline-store/types#InstallResult',
|
|
47
|
+
schema: _guguo_dsh_offline_store_offlineStore_install_result$schema,
|
|
48
|
+
},
|
|
49
|
+
sourceLocation: {"file":"packages/extensions/offline-store/src/index.ts","line":93,"column":9},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: '@guguo/dsh-offline-store#offlineStore/list',
|
|
53
|
+
service: 'offlineStore',
|
|
54
|
+
namespace: 'offlineStore',
|
|
55
|
+
method: 'list',
|
|
56
|
+
invocation: { kind: 'direct' },
|
|
57
|
+
parameters: [
|
|
58
|
+
],
|
|
59
|
+
result: {
|
|
60
|
+
mode: 'strict',
|
|
61
|
+
typeSymbol: '@guguo/dsh-offline-store/types#CatalogView',
|
|
62
|
+
schema: _guguo_dsh_offline_store_offlineStore_list_result$schema,
|
|
63
|
+
},
|
|
64
|
+
sourceLocation: {"file":"packages/extensions/offline-store/src/index.ts","line":70,"column":9},
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default TYPERT_REMOTE
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** 商店 UI 组件:入口按钮 + 面板(模块级共享开关状态)。 */
|
|
2
|
+
import { type ReactElement } from 'react';
|
|
3
|
+
import type { CatalogView, InstallResult } from '../types.ts';
|
|
4
|
+
/** Host Remote 的最小接口桥。 */
|
|
5
|
+
export interface OfflineStoreRemote {
|
|
6
|
+
list(): Promise<CatalogView>;
|
|
7
|
+
install(args: {
|
|
8
|
+
id: string;
|
|
9
|
+
}): Promise<InstallResult>;
|
|
10
|
+
}
|
|
11
|
+
/** 输入框左端的 📦 入口。 */
|
|
12
|
+
export declare function StoreEntry(): ReactElement;
|
|
13
|
+
/** 面板工厂:闭包持有 remote,供入口按钮与面板共享开关状态。 */
|
|
14
|
+
export declare function makePanel(remote: OfflineStoreRemote): () => ReactElement | null;
|
|
15
|
+
//# sourceMappingURL=Panel.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/** 商店 UI 组件:入口按钮 + 面板(模块级共享开关状态)。 */
|
|
3
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
4
|
+
import css from './panel.module.css';
|
|
5
|
+
const panelState = { open: false, listeners: new Set() };
|
|
6
|
+
const setPanelOpen = (v) => {
|
|
7
|
+
panelState.open = v;
|
|
8
|
+
panelState.listeners.forEach((fn) => fn(v));
|
|
9
|
+
};
|
|
10
|
+
const subscribePanel = (fn) => {
|
|
11
|
+
panelState.listeners.add(fn);
|
|
12
|
+
return () => {
|
|
13
|
+
panelState.listeners.delete(fn);
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
/** 输入框左端的 📦 入口。 */
|
|
17
|
+
export function StoreEntry() {
|
|
18
|
+
return (_jsx("button", { type: "button", className: css.entry, title: "\u79BB\u7EBF\u5E94\u7528\u5546\u5E97", "aria-label": "\u79BB\u7EBF\u5E94\u7528\u5546\u5E97", onClick: () => setPanelOpen(!panelState.open), children: _jsx("span", { "aria-hidden": "true", children: "\uD83D\uDCE6" }) }));
|
|
19
|
+
}
|
|
20
|
+
/** 面板工厂:闭包持有 remote,供入口按钮与面板共享开关状态。 */
|
|
21
|
+
export function makePanel(remote) {
|
|
22
|
+
return function Panel() {
|
|
23
|
+
const [open, setOpen] = useState(panelState.open);
|
|
24
|
+
const [items, setItems] = useState(null);
|
|
25
|
+
const [busy, setBusy] = useState(null);
|
|
26
|
+
const [status, setStatus] = useState('');
|
|
27
|
+
useEffect(() => subscribePanel(setOpen), []);
|
|
28
|
+
const refresh = useCallback(() => {
|
|
29
|
+
remote.list().then((res) => {
|
|
30
|
+
setItems(res.items ?? []);
|
|
31
|
+
if (res.error !== undefined)
|
|
32
|
+
setStatus('加载提示: ' + res.error);
|
|
33
|
+
}).catch((e) => {
|
|
34
|
+
setStatus('加载失败: ' + (e instanceof Error ? e.message : String(e)));
|
|
35
|
+
});
|
|
36
|
+
}, [remote]);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!open)
|
|
39
|
+
return;
|
|
40
|
+
setStatus('');
|
|
41
|
+
refresh();
|
|
42
|
+
}, [open, refresh]);
|
|
43
|
+
const install = (id) => {
|
|
44
|
+
if (busy !== null)
|
|
45
|
+
return;
|
|
46
|
+
setBusy(id);
|
|
47
|
+
setStatus('正在安装 ' + id + ' …');
|
|
48
|
+
remote.install({ id }).then((res) => {
|
|
49
|
+
setStatus(res.ok ? '✅ ' + res.message : '❌ ' + res.message);
|
|
50
|
+
if (res.ok)
|
|
51
|
+
refresh();
|
|
52
|
+
}).catch((e) => {
|
|
53
|
+
setStatus('❌ 安装失败: ' + (e instanceof Error ? e.message : String(e)));
|
|
54
|
+
}).finally(() => setBusy(null));
|
|
55
|
+
};
|
|
56
|
+
if (!open)
|
|
57
|
+
return null;
|
|
58
|
+
return (_jsxs("div", { className: css.panel, children: [_jsxs("div", { className: css.head, children: [_jsx("span", { children: "\uD83D\uDCE6 \u79BB\u7EBF\u5E94\u7528\u5546\u5E97" }), _jsx("button", { type: "button", className: css.close, onClick: () => setPanelOpen(false), "aria-label": "\u5173\u95ED", children: "\u2715" })] }), _jsx("div", { className: css.list, children: items === null
|
|
59
|
+
? _jsx("div", { children: "\u52A0\u8F7D\u4E2D\u2026" })
|
|
60
|
+
: items.length === 0
|
|
61
|
+
? _jsx("div", { children: "\uFF08\u6682\u65E0\u53EF\u7528\u6761\u76EE\uFF09" })
|
|
62
|
+
: items.map((it) => (_jsxs("div", { className: css.item, children: [_jsxs("div", { className: css.body, children: [_jsxs("div", { className: css.title, children: [_jsx("span", { children: it.name }), _jsx("span", { className: `${css.type} ${it.type === 'plugin' ? css.typePlugin : it.type === 'preset' ? css.typePreset : ''}`, children: it.type === 'skill' ? '技能' : it.type === 'plugin' ? '插件' : '预设' })] }), _jsx("div", { className: css.desc, children: it.description })] }), _jsx("button", { type: "button", className: `${css.btn}${it.installed ? ' ' + css.done : ''}`, disabled: busy !== null || it.installed, onClick: () => install(it.id), children: it.installed ? '已安装' : '安装' })] }, it.id))) }), status !== '' && (_jsx("div", { className: status.startsWith('✅') ? css.statusOk : status.startsWith('❌') ? css.statusErr : css.status, children: status }))] }));
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=Panel.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** 离线应用商店 Client 半:输入框左端入口 + 左上浮层面板。 */
|
|
2
|
+
import { makePanel, StoreEntry } from "./Panel.js";
|
|
3
|
+
export const inject = ['slots', 'remote'];
|
|
4
|
+
export function apply(ctx) {
|
|
5
|
+
// 运行时 ctx.remote.offlineStore 由 Host 的 TypertRemoteService 生成;
|
|
6
|
+
// 此处用最小接口桥接,正式类型由构建期 typert 生成物提供。
|
|
7
|
+
const remote = ctx.remote;
|
|
8
|
+
const Panel = makePanel(remote);
|
|
9
|
+
ctx.slots.inject('conversation.input.left', () => ctx.slots.register({ name: 'conversation.input.left', id: 'offline-store-entry', order: 100 }, StoreEntry));
|
|
10
|
+
ctx.slots.inject('shell.overlay', () => ctx.slots.register({ name: 'shell.overlay', id: 'offline-store-panel', order: 100 }, Panel));
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** 离线应用商店 Host 半:Remote 控制器,暴露 catalog/install 给浏览器端。 */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
4
|
+
import type { CatalogView, InstallArgs, InstallResult } from './types.ts';
|
|
5
|
+
export type * from './types.ts';
|
|
6
|
+
/** Remote-only 服务:目录 + 安装。客户端经 ctx.remote.offlineStore 调用。 */
|
|
7
|
+
export declare class OfflineStoreGateway extends TypertRemoteService {
|
|
8
|
+
static inject: string[];
|
|
9
|
+
constructor(ctx: Context);
|
|
10
|
+
list(): Promise<CatalogView>;
|
|
11
|
+
install(args: InstallArgs): Promise<InstallResult>;
|
|
12
|
+
private fs;
|
|
13
|
+
/** 自动探测 DSH_HOME:从用户预设路径逐级剥离 .agent-presets 目录。 */
|
|
14
|
+
private resolveHome;
|
|
15
|
+
private skillInstalled;
|
|
16
|
+
private fileExists;
|
|
17
|
+
private readText;
|
|
18
|
+
private isInstalled;
|
|
19
|
+
private listBundles;
|
|
20
|
+
}
|
|
21
|
+
export default OfflineStoreGateway;
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/** 离线应用商店 Host 半:Remote 控制器,暴露 catalog/install 给浏览器端。 */
|
|
2
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
3
|
+
var useValue = arguments.length > 2;
|
|
4
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
5
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
6
|
+
}
|
|
7
|
+
return useValue ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
10
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
11
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
12
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
13
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
14
|
+
var _, done = false;
|
|
15
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
16
|
+
var context = {};
|
|
17
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
18
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
19
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
20
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
21
|
+
if (kind === "accessor") {
|
|
22
|
+
if (result === void 0) continue;
|
|
23
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
24
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
25
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
26
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
27
|
+
}
|
|
28
|
+
else if (_ = accept(result)) {
|
|
29
|
+
if (kind === "field") initializers.unshift(_);
|
|
30
|
+
else descriptor[key] = _;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
34
|
+
done = true;
|
|
35
|
+
};
|
|
36
|
+
import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol';
|
|
37
|
+
/** 内置演示条目:技能 / 插件 / 预设各一个。 */
|
|
38
|
+
const EMBEDDED = [
|
|
39
|
+
{
|
|
40
|
+
id: 'skill-offline-demo',
|
|
41
|
+
type: 'skill',
|
|
42
|
+
name: '离线演示技能',
|
|
43
|
+
description: '演示条目:安装后写入用户级技能目录,所有会话可见、重启保留。',
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
installed: false,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'plugin-str-replace',
|
|
49
|
+
type: 'plugin',
|
|
50
|
+
name: '字符串替换编辑器',
|
|
51
|
+
description: '演示条目:安装后把插件行追加到本地预设 offline-store,下次会话生效。',
|
|
52
|
+
version: '1.0.0',
|
|
53
|
+
installed: false,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'preset-minimal-demo',
|
|
57
|
+
type: 'preset',
|
|
58
|
+
name: '极简模式(演示预设)',
|
|
59
|
+
description: '演示条目:把「极简模式」复制为本地预设 minimal-demo,新建会话时可选择。',
|
|
60
|
+
version: '1.0.0',
|
|
61
|
+
installed: false,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
/** 商店宿主预设 id(插件条目的写入目标)。 */
|
|
65
|
+
const HOST_PRESET = 'offline-store';
|
|
66
|
+
/** 带内嵌演示技能的 SKILL.md 内容(离线演示条目专用)。 */
|
|
67
|
+
const DEMO_SKILL_CONTENT = [
|
|
68
|
+
'---',
|
|
69
|
+
'name: offline-demo',
|
|
70
|
+
'description: 离线商店演示技能:安装后立即出现在技能目录中,用于验证离线技能安装链路。',
|
|
71
|
+
'---',
|
|
72
|
+
'',
|
|
73
|
+
'# 离线演示技能',
|
|
74
|
+
'',
|
|
75
|
+
'这是一个通过「离线应用商店」安装的技能。',
|
|
76
|
+
'',
|
|
77
|
+
'## 用法',
|
|
78
|
+
'',
|
|
79
|
+
'1. 点击输入框左端的「📦 应用商店」图标打开面板',
|
|
80
|
+
'2. 找到本技能并点击「安装」',
|
|
81
|
+
'3. 安装后它会出现在技能目录中,且重启后依然存在。',
|
|
82
|
+
'',
|
|
83
|
+
].join('\n');
|
|
84
|
+
/** Remote-only 服务:目录 + 安装。客户端经 ctx.remote.offlineStore 调用。 */
|
|
85
|
+
let OfflineStoreGateway = (() => {
|
|
86
|
+
let _classSuper = TypertRemoteService;
|
|
87
|
+
let _instanceExtraInitializers = [];
|
|
88
|
+
let _list_decorators;
|
|
89
|
+
let _install_decorators;
|
|
90
|
+
return class OfflineStoreGateway extends _classSuper {
|
|
91
|
+
static {
|
|
92
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
93
|
+
_list_decorators = [Remote('list')];
|
|
94
|
+
_install_decorators = [Remote('install')];
|
|
95
|
+
__esDecorate(this, null, _list_decorators, { kind: "method", name: "list", static: false, private: false, access: { has: obj => "list" in obj, get: obj => obj.list }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
96
|
+
__esDecorate(this, null, _install_decorators, { kind: "method", name: "install", static: false, private: false, access: { has: obj => "install" in obj, get: obj => obj.install }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
97
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
98
|
+
}
|
|
99
|
+
static inject = ['fs', 'agentPresets'];
|
|
100
|
+
constructor(ctx) {
|
|
101
|
+
super(ctx, 'offlineStore');
|
|
102
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
103
|
+
}
|
|
104
|
+
async list() {
|
|
105
|
+
try {
|
|
106
|
+
const items = [];
|
|
107
|
+
for (const it of EMBEDDED) {
|
|
108
|
+
items.push({ ...it, installed: await this.isInstalled(it.id) });
|
|
109
|
+
}
|
|
110
|
+
for (const bundle of await this.listBundles()) {
|
|
111
|
+
items.push({
|
|
112
|
+
id: bundle.id,
|
|
113
|
+
type: 'skill',
|
|
114
|
+
name: bundle.name,
|
|
115
|
+
description: bundle.description,
|
|
116
|
+
version: bundle.version,
|
|
117
|
+
installed: await this.skillInstalled(bundle.skillName),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return { items };
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return { items: [], error: errorMessage(error) };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async install(args) {
|
|
127
|
+
const id = args.id;
|
|
128
|
+
const embedded = EMBEDDED.find((x) => x.id === id);
|
|
129
|
+
const bundle = (await this.listBundles()).find((b) => b.id === id);
|
|
130
|
+
const kind = embedded?.type ?? (bundle !== undefined ? 'skill' : undefined);
|
|
131
|
+
if (kind === undefined)
|
|
132
|
+
return { ok: false, message: '未找到条目: ' + id };
|
|
133
|
+
try {
|
|
134
|
+
if (kind === 'skill') {
|
|
135
|
+
const content = bundle === undefined ? DEMO_SKILL_CONTENT : await this.readText(bundle.path);
|
|
136
|
+
if (content === undefined)
|
|
137
|
+
return { ok: false, message: '技能内容缺失' };
|
|
138
|
+
const home = await this.resolveHome();
|
|
139
|
+
const target = await this.fs().resolve(`${home}/skills/${bundle?.skillName ?? 'offline-demo'}/SKILL.md`);
|
|
140
|
+
await this.fs().writeText(target, content, undefined, undefined, fullAccess(`${home}/skills`));
|
|
141
|
+
return { ok: true, message: '技能已安装:写入用户级技能目录,立即生效且重启保留' };
|
|
142
|
+
}
|
|
143
|
+
if (kind === 'plugin') {
|
|
144
|
+
let comp;
|
|
145
|
+
try {
|
|
146
|
+
comp = await this.ctx.agentPresets.read(HOST_PRESET);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
await this.ctx.agentPresets.copy('cordis', HOST_PRESET, '离线商店预设');
|
|
150
|
+
comp = await this.ctx.agentPresets.read(HOST_PRESET);
|
|
151
|
+
}
|
|
152
|
+
if (comp.includes('tool-str-replace-editor')) {
|
|
153
|
+
return { ok: true, message: '插件已在预设 ' + HOST_PRESET + ' 中' };
|
|
154
|
+
}
|
|
155
|
+
const home = await this.resolveHome();
|
|
156
|
+
const row = " - id: tool-str-replace-editor\n name: '@deepseek-ai/dsh-tool-str-replace-editor'\n";
|
|
157
|
+
const target = await this.fs().resolve(`${home}/.agent-presets/${HOST_PRESET}/agent.cordis.yml`);
|
|
158
|
+
await this.fs().writeText(target, comp.replace(/\s*$/, '\n') + row, undefined, undefined, fullAccess(`${home}/.agent-presets/${HOST_PRESET}`));
|
|
159
|
+
return { ok: true, message: '插件已写入预设 ' + HOST_PRESET + ',下次会话生效' };
|
|
160
|
+
}
|
|
161
|
+
if (kind === 'preset') {
|
|
162
|
+
const all = await this.ctx.agentPresets.list();
|
|
163
|
+
if (!all.some((p) => p.id === 'minimal-demo')) {
|
|
164
|
+
await this.ctx.agentPresets.copy('minimal', 'minimal-demo', '极简模式演示');
|
|
165
|
+
}
|
|
166
|
+
return { ok: true, message: '预设已复制为本地预设 minimal-demo,新建会话时可选择' };
|
|
167
|
+
}
|
|
168
|
+
return { ok: false, message: '未知条目类型' };
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
return { ok: false, message: errorMessage(error) };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// ── 内部工具 ──────────────────────────────────────────────
|
|
175
|
+
fs() {
|
|
176
|
+
return this.ctx.get('fs');
|
|
177
|
+
}
|
|
178
|
+
/** 自动探测 DSH_HOME:从用户预设路径逐级剥离 .agent-presets 目录。 */
|
|
179
|
+
async resolveHome() {
|
|
180
|
+
const all = await this.ctx.agentPresets.list();
|
|
181
|
+
let anchor = all.find((p) => p.trust === 'user');
|
|
182
|
+
if (anchor === undefined) {
|
|
183
|
+
await this.ctx.agentPresets.copy('cordis', HOST_PRESET, '离线商店预设');
|
|
184
|
+
anchor = (await this.ctx.agentPresets.list()).find((p) => p.id === HOST_PRESET);
|
|
185
|
+
if (anchor === undefined)
|
|
186
|
+
throw new Error('无法创建商店宿主预设 ' + HOST_PRESET);
|
|
187
|
+
}
|
|
188
|
+
const dir = anchor.path.replace(/[\\/][^\\/]+$/, '');
|
|
189
|
+
const presetRoot = dir.replace(/[\\/][^\\/]+$/, '');
|
|
190
|
+
return presetRoot.replace(/[\\/]\.agent-presets$/, '');
|
|
191
|
+
}
|
|
192
|
+
async skillInstalled(name) {
|
|
193
|
+
const home = await this.resolveHome();
|
|
194
|
+
return await this.fileExists(`${home}/skills/${name}/SKILL.md`);
|
|
195
|
+
}
|
|
196
|
+
async fileExists(path) {
|
|
197
|
+
try {
|
|
198
|
+
const target = await this.fs().resolve(path);
|
|
199
|
+
return (await this.fs().stat(target)) !== undefined;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async readText(path) {
|
|
206
|
+
try {
|
|
207
|
+
return await this.fs().readText(await this.fs().resolve(path));
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async isInstalled(embeddedId) {
|
|
214
|
+
if (embeddedId === 'skill-offline-demo')
|
|
215
|
+
return await this.skillInstalled('offline-demo');
|
|
216
|
+
if (embeddedId === 'plugin-str-replace') {
|
|
217
|
+
try {
|
|
218
|
+
return (await this.ctx.agentPresets.read(HOST_PRESET)).includes('tool-str-replace-editor');
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (embeddedId === 'preset-minimal-demo') {
|
|
225
|
+
return (await this.ctx.agentPresets.list()).some((p) => p.id === 'minimal-demo');
|
|
226
|
+
}
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
async listBundles() {
|
|
230
|
+
const out = [];
|
|
231
|
+
let entries;
|
|
232
|
+
try {
|
|
233
|
+
const home = await this.resolveHome();
|
|
234
|
+
const dir = await this.fs().resolve(`${home}/skills-store`);
|
|
235
|
+
if ((await this.fs().stat(dir)) === undefined)
|
|
236
|
+
return out;
|
|
237
|
+
entries = await this.fs().listDir(dir);
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return out;
|
|
241
|
+
}
|
|
242
|
+
for (const entry of entries) {
|
|
243
|
+
if (entry.type !== 'directory')
|
|
244
|
+
continue;
|
|
245
|
+
const skillPath = `${entry.target.displayPath}/SKILL.md`;
|
|
246
|
+
const text = await this.readText(skillPath);
|
|
247
|
+
if (text === undefined)
|
|
248
|
+
continue;
|
|
249
|
+
const meta = parseFrontmatter(text);
|
|
250
|
+
if (meta === null || meta.name === undefined || meta.description === undefined)
|
|
251
|
+
continue;
|
|
252
|
+
out.push({
|
|
253
|
+
id: 'bundle-' + entry.name,
|
|
254
|
+
name: meta.name,
|
|
255
|
+
description: meta.description,
|
|
256
|
+
version: '1.0.0',
|
|
257
|
+
skillName: entry.name,
|
|
258
|
+
path: skillPath,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
})();
|
|
265
|
+
export { OfflineStoreGateway };
|
|
266
|
+
export default OfflineStoreGateway;
|
|
267
|
+
// ── 模块级辅助 ──────────────────────────────────────────────
|
|
268
|
+
function fullAccess(root) {
|
|
269
|
+
return { mode: 'danger-full-access', workspaceRoot: root };
|
|
270
|
+
}
|
|
271
|
+
/** 从 SKILL.md 的 YAML frontmatter 里提取 name/description(最小实现)。 */
|
|
272
|
+
function parseFrontmatter(text) {
|
|
273
|
+
const lines = text.split('\n');
|
|
274
|
+
const first = lines[0];
|
|
275
|
+
if (first === undefined || lines.length < 3 || first.trim() !== '---')
|
|
276
|
+
return null;
|
|
277
|
+
let end = -1;
|
|
278
|
+
for (let i = 1; i < lines.length; i++) {
|
|
279
|
+
const line = lines[i];
|
|
280
|
+
if (line !== undefined && line.trim() === '---') {
|
|
281
|
+
end = i;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (end < 0)
|
|
286
|
+
return null;
|
|
287
|
+
const data = {};
|
|
288
|
+
for (let i = 1; i < end; i++) {
|
|
289
|
+
const line = lines[i];
|
|
290
|
+
if (line === undefined)
|
|
291
|
+
continue;
|
|
292
|
+
const m = /^([A-Za-z0-9-]+):\s*(.*)$/.exec(line);
|
|
293
|
+
if (m === null)
|
|
294
|
+
continue;
|
|
295
|
+
const key = m[1];
|
|
296
|
+
if (key === undefined)
|
|
297
|
+
continue;
|
|
298
|
+
const raw = m[2];
|
|
299
|
+
if (raw === undefined)
|
|
300
|
+
continue;
|
|
301
|
+
let v = raw.trim();
|
|
302
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'")))
|
|
303
|
+
v = v.slice(1, -1);
|
|
304
|
+
data[key] = v;
|
|
305
|
+
}
|
|
306
|
+
const name = data.name;
|
|
307
|
+
const description = data.description;
|
|
308
|
+
if (name === undefined || description === undefined)
|
|
309
|
+
return null;
|
|
310
|
+
return { name, description };
|
|
311
|
+
}
|
|
312
|
+
function errorMessage(error) {
|
|
313
|
+
return error instanceof Error ? error.message : String(error);
|
|
314
|
+
}
|
|
315
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** 离线商店的公共契约类型(Host Remote 服务与 Client 共享)。 */
|
|
2
|
+
export interface CatalogItem {
|
|
3
|
+
id: string;
|
|
4
|
+
type: 'skill' | 'plugin' | 'preset';
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
version: string;
|
|
8
|
+
installed: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface CatalogView {
|
|
11
|
+
items: CatalogItem[];
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface InstallArgs {
|
|
15
|
+
id: string;
|
|
16
|
+
}
|
|
17
|
+
export interface InstallResult {
|
|
18
|
+
ok: boolean;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=types.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@guguo/dsh-offline-store",
|
|
3
|
+
"description": "离线应用商店:内置技能/插件/预设目录,输入框左端入口,点击即安装(离线可用)。一个包同时是 Cordis 插件与 profile bundle。",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "lib/index.js",
|
|
10
|
+
"types": "lib/types/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./lib/types/index.d.ts",
|
|
14
|
+
"default": "./lib/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./types": {
|
|
17
|
+
"types": "./lib/types/types.d.ts",
|
|
18
|
+
"default": "./lib/types/types.js"
|
|
19
|
+
},
|
|
20
|
+
"./typert": {
|
|
21
|
+
"types": "./lib/typert.host.d.ts",
|
|
22
|
+
"default": "./lib/typert.host.js"
|
|
23
|
+
},
|
|
24
|
+
"./remote": {
|
|
25
|
+
"types": "./lib/typert.remote-client.d.ts",
|
|
26
|
+
"default": "./lib/typert.remote-client.js"
|
|
27
|
+
},
|
|
28
|
+
"./client": {
|
|
29
|
+
"types": "./lib/types/client/index.d.ts",
|
|
30
|
+
"default": "./lib/client.js"
|
|
31
|
+
},
|
|
32
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
33
|
+
"./src/*": "./src/*",
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"lib/index.js",
|
|
38
|
+
"lib/types/**/*.js",
|
|
39
|
+
"lib/types/**/*.d.ts",
|
|
40
|
+
"lib/typert.host.js",
|
|
41
|
+
"lib/typert.host.d.ts",
|
|
42
|
+
"lib/typert.remote-client.js",
|
|
43
|
+
"lib/typert.remote-client.d.ts",
|
|
44
|
+
"lib/client.js",
|
|
45
|
+
"cordis.patch.yml"
|
|
46
|
+
],
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"dsh": {
|
|
49
|
+
"bundle": {
|
|
50
|
+
"patch": "./cordis.patch.yml"
|
|
51
|
+
},
|
|
52
|
+
"client": {
|
|
53
|
+
"platform": "web"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"bundle": "tsdown"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"zod": "^4.4.3"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
64
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6",
|
|
65
|
+
"@deepseek-ai/dsh-fs": "^0.0.1-rc.1",
|
|
66
|
+
"@deepseek-ai/dsh-agent-presets": "^0.0.1-rc.1",
|
|
67
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.0.1-rc.1",
|
|
68
|
+
"@deepseek-ai/dsh-client-ui-layout": "^0.0.1-rc.1",
|
|
69
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.0-rc.8",
|
|
70
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
|
|
71
|
+
"@deepseek-ai/dsh-api-remotes": "^0.0.1-rc.1"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"@deepseek-ai/dsh-agent-presets": {
|
|
75
|
+
"optional": true
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
80
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6",
|
|
81
|
+
"@deepseek-ai/dsh-fs": "^0.0.1-rc.1",
|
|
82
|
+
"@deepseek-ai/dsh-agent-presets": "^0.0.1-rc.1",
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.0.1-rc.1",
|
|
84
|
+
"@deepseek-ai/dsh-client-ui-layout": "^0.0.1-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.0-rc.8",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
|
|
87
|
+
"@deepseek-ai/dsh-api-remotes": "^0.0.1-rc.1",
|
|
88
|
+
"@types/react": "~18.3.1",
|
|
89
|
+
"react": "^18.2.0"
|
|
90
|
+
}
|
|
91
|
+
}
|