@monkey-mini-app/dsh-mini-app 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 +6 -0
- package/lib/client.d.cts +35 -0
- package/lib/client.js +5462 -0
- package/lib/index.d.ts +140 -0
- package/lib/index.js +1094 -0
- package/package.json +90 -0
- package/skills/monkey-mini-app/SKILL.md +209 -0
- package/skills/monkey-mini-app/references/catalog.md +99 -0
- package/skills/monkey-mini-app/references/contracts/activity-feed.md +24 -0
- package/skills/monkey-mini-app/references/contracts/app-shell.md +28 -0
- package/skills/monkey-mini-app/references/contracts/attachment-gallery.md +22 -0
- package/skills/monkey-mini-app/references/contracts/autocomplete.md +16 -0
- package/skills/monkey-mini-app/references/contracts/cascader.md +25 -0
- package/skills/monkey-mini-app/references/contracts/code-block.md +15 -0
- package/skills/monkey-mini-app/references/contracts/code-editor.md +17 -0
- package/skills/monkey-mini-app/references/contracts/color-picker.md +14 -0
- package/skills/monkey-mini-app/references/contracts/comment-thread.md +24 -0
- package/skills/monkey-mini-app/references/contracts/confirm-dialog.md +19 -0
- package/skills/monkey-mini-app/references/contracts/copyable.md +13 -0
- package/skills/monkey-mini-app/references/contracts/currency-input.md +15 -0
- package/skills/monkey-mini-app/references/contracts/data-grid.md +87 -0
- package/skills/monkey-mini-app/references/contracts/date-picker.md +17 -0
- package/skills/monkey-mini-app/references/contracts/date-range-picker.md +26 -0
- package/skills/monkey-mini-app/references/contracts/date-time-picker.md +18 -0
- package/skills/monkey-mini-app/references/contracts/date-time-range-picker.md +24 -0
- package/skills/monkey-mini-app/references/contracts/description-list.md +13 -0
- package/skills/monkey-mini-app/references/contracts/detail-panel.md +27 -0
- package/skills/monkey-mini-app/references/contracts/diff-viewer.md +16 -0
- package/skills/monkey-mini-app/references/contracts/donut-chart.md +31 -0
- package/skills/monkey-mini-app/references/contracts/duration-input.md +14 -0
- package/skills/monkey-mini-app/references/contracts/env-badge.md +13 -0
- package/skills/monkey-mini-app/references/contracts/event-calendar.md +50 -0
- package/skills/monkey-mini-app/references/contracts/file-dropzone.md +14 -0
- package/skills/monkey-mini-app/references/contracts/file-tree.md +23 -0
- package/skills/monkey-mini-app/references/contracts/filter-bar.md +26 -0
- package/skills/monkey-mini-app/references/contracts/gantt.md +24 -0
- package/skills/monkey-mini-app/references/contracts/gauge.md +14 -0
- package/skills/monkey-mini-app/references/contracts/jira-wiki.md +53 -0
- package/skills/monkey-mini-app/references/contracts/jql-input.md +37 -0
- package/skills/monkey-mini-app/references/contracts/json-viewer.md +13 -0
- package/skills/monkey-mini-app/references/contracts/kanban-issue-panel.md +45 -0
- package/skills/monkey-mini-app/references/contracts/kanban.md +64 -0
- package/skills/monkey-mini-app/references/contracts/log-viewer.md +13 -0
- package/skills/monkey-mini-app/references/contracts/markdown-editor.md +24 -0
- package/skills/monkey-mini-app/references/contracts/markdown.md +14 -0
- package/skills/monkey-mini-app/references/contracts/mini-calendar.md +15 -0
- package/skills/monkey-mini-app/references/contracts/notification-center.md +23 -0
- package/skills/monkey-mini-app/references/contracts/number-field.md +17 -0
- package/skills/monkey-mini-app/references/contracts/page-header.md +25 -0
- package/skills/monkey-mini-app/references/contracts/password-field.md +14 -0
- package/skills/monkey-mini-app/references/contracts/phone-input.md +14 -0
- package/skills/monkey-mini-app/references/contracts/progress-ring.md +13 -0
- package/skills/monkey-mini-app/references/contracts/radar-chart.md +23 -0
- package/skills/monkey-mini-app/references/contracts/rating.md +15 -0
- package/skills/monkey-mini-app/references/contracts/relative-date-picker.md +24 -0
- package/skills/monkey-mini-app/references/contracts/request-inspector.md +16 -0
- package/skills/monkey-mini-app/references/contracts/rich-text-editor.md +15 -0
- package/skills/monkey-mini-app/references/contracts/run-timeline.md +24 -0
- package/skills/monkey-mini-app/references/contracts/scrollspy.md +13 -0
- package/skills/monkey-mini-app/references/contracts/search-input.md +17 -0
- package/skills/monkey-mini-app/references/contracts/severity-chip.md +13 -0
- package/skills/monkey-mini-app/references/contracts/slider-range.md +16 -0
- package/skills/monkey-mini-app/references/contracts/sortable-list.md +23 -0
- package/skills/monkey-mini-app/references/contracts/sparkline.md +13 -0
- package/skills/monkey-mini-app/references/contracts/stacked-bar-chart.md +23 -0
- package/skills/monkey-mini-app/references/contracts/stat-card.md +27 -0
- package/skills/monkey-mini-app/references/contracts/status-badge.md +24 -0
- package/skills/monkey-mini-app/references/contracts/stepper.md +26 -0
- package/skills/monkey-mini-app/references/contracts/tag-input.md +15 -0
- package/skills/monkey-mini-app/references/contracts/terminal.md +13 -0
- package/skills/monkey-mini-app/references/contracts/test-step-list.md +23 -0
- package/skills/monkey-mini-app/references/contracts/time-picker.md +18 -0
- package/skills/monkey-mini-app/references/contracts/time-range-picker.md +23 -0
- package/skills/monkey-mini-app/references/contracts/time-wheel.md +29 -0
- package/skills/monkey-mini-app/references/contracts/timeline.md +24 -0
- package/skills/monkey-mini-app/references/contracts/timezone-select.md +27 -0
- package/skills/monkey-mini-app/references/contracts/transfer.md +24 -0
- package/skills/monkey-mini-app/references/contracts/tree-view.md +23 -0
- package/skills/monkey-mini-app/references/contracts/trend-card.md +26 -0
- package/skills/monkey-mini-app/references/contracts/user-picker.md +24 -0
- package/skills/monkey-mini-app/references/ctx.md +137 -0
- package/skills/monkey-mini-app/references/history.md +10 -0
- package/skills/monkey-mini-app/references/icons.md +83 -0
- package/skills/monkey-mini-app/references/llm-json.md +69 -0
- package/skills/monkey-mini-app/references/loader.md +30 -0
- package/skills/monkey-mini-app/references/test.md +30 -0
- package/skills/monkey-mini-app/references/tools.md +21 -0
- package/skills/monkey-mini-app/references/troubleshoot.md +12 -0
- package/skills/monkey-mini-app/templates/README.md +24 -0
- package/skills/monkey-mini-app/templates/agentrun/main.api.ts +78 -0
- package/skills/monkey-mini-app/templates/agentrun/manifest.json +9 -0
- package/skills/monkey-mini-app/templates/agentrun/ui.tsx +125 -0
- package/skills/monkey-mini-app/templates/insights/lib/sample.ts +17 -0
- package/skills/monkey-mini-app/templates/insights/main.api.ts +101 -0
- package/skills/monkey-mini-app/templates/insights/manifest.json +9 -0
- package/skills/monkey-mini-app/templates/insights/ui.tsx +138 -0
- package/skills/monkey-mini-app/templates/jira/main.api.ts +115 -0
- package/skills/monkey-mini-app/templates/jira/manifest.json +9 -0
- package/skills/monkey-mini-app/templates/jira/ui.tsx +318 -0
- package/skills/monkey-mini-app/templates/minimal/main.api.ts +16 -0
- package/skills/monkey-mini-app/templates/minimal/manifest.json +8 -0
- package/skills/monkey-mini-app/templates/minimal/ui.tsx +74 -0
- package/skills/monkey-mini-app/templates/monitor/main.api.ts +91 -0
- package/skills/monkey-mini-app/templates/monitor/manifest.json +9 -0
- package/skills/monkey-mini-app/templates/monitor/ui.tsx +176 -0
- package/skills/monkey-mini-app/templates/review/main.api.ts +68 -0
- package/skills/monkey-mini-app/templates/review/manifest.json +9 -0
- package/skills/monkey-mini-app/templates/review/ui.tsx +159 -0
- package/skills/monkey-mini-app/templates/todo/main.api.ts +83 -0
- package/skills/monkey-mini-app/templates/todo/manifest.json +8 -0
- package/skills/monkey-mini-app/templates/todo/ui.tsx +177 -0
- package/tsup.config.ts +68 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createHost } from "@monkey-mini-app/host";
|
|
3
|
+
|
|
4
|
+
// src/apply-config.ts
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import {
|
|
8
|
+
asAbsolutePath,
|
|
9
|
+
bootstrapHostConfig,
|
|
10
|
+
createHostI18n,
|
|
11
|
+
HostConfigError,
|
|
12
|
+
loadHostConfig,
|
|
13
|
+
WorkspacePaths
|
|
14
|
+
} from "@monkey-mini-app/host";
|
|
15
|
+
function expandHome(p) {
|
|
16
|
+
if (p === "~") return homedir();
|
|
17
|
+
if (p.startsWith("~/") || p.startsWith("~\\")) return path.join(homedir(), p.slice(2));
|
|
18
|
+
return p;
|
|
19
|
+
}
|
|
20
|
+
function toRuntimeRoot(input) {
|
|
21
|
+
const resolved = path.resolve(expandHome(input));
|
|
22
|
+
try {
|
|
23
|
+
return asAbsolutePath(resolved);
|
|
24
|
+
} catch (cause) {
|
|
25
|
+
throw new HostConfigError("host config runtimeRoot must be an absolute path", { cause });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function resolveRuntimeRoot(config = {}) {
|
|
29
|
+
if (typeof config.runtimeRoot === "string" && config.runtimeRoot.length > 0) {
|
|
30
|
+
return toRuntimeRoot(config.runtimeRoot);
|
|
31
|
+
}
|
|
32
|
+
return bootstrapHostConfig({}).runtimeRoot;
|
|
33
|
+
}
|
|
34
|
+
function missingConfigError(file, cause) {
|
|
35
|
+
const i18n = createHostI18n("zh-CN");
|
|
36
|
+
const message = `${i18n.t("config.missingFile", { path: file })} ${i18n.t("config.missingHint")}`;
|
|
37
|
+
return new HostConfigError(message, { code: "HOST_CONFIG_NOT_FOUND", cause });
|
|
38
|
+
}
|
|
39
|
+
function loadPluginHostConfig(config = {}) {
|
|
40
|
+
const runtimeRoot = resolveRuntimeRoot(config);
|
|
41
|
+
const paths = new WorkspacePaths(runtimeRoot);
|
|
42
|
+
try {
|
|
43
|
+
return loadHostConfig(paths);
|
|
44
|
+
} catch (cause) {
|
|
45
|
+
if (cause instanceof HostConfigError && cause.code === "HOST_CONFIG_NOT_FOUND") {
|
|
46
|
+
throw missingConfigError(paths.hostConfigFile(), cause);
|
|
47
|
+
}
|
|
48
|
+
throw cause;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/capabilities.ts
|
|
53
|
+
import { execFile } from "child_process";
|
|
54
|
+
import { promisify } from "util";
|
|
55
|
+
|
|
56
|
+
// src/agent-one-shot.ts
|
|
57
|
+
import { randomUUID } from "crypto";
|
|
58
|
+
import { rm } from "fs/promises";
|
|
59
|
+
import {
|
|
60
|
+
effectiveSignal,
|
|
61
|
+
isMiniAppToolName,
|
|
62
|
+
resolveAgentCwd
|
|
63
|
+
} from "@monkey-mini-app/host";
|
|
64
|
+
|
|
65
|
+
// src/ctx.ts
|
|
66
|
+
function isRecord(value) {
|
|
67
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
68
|
+
}
|
|
69
|
+
function getService(ctx, name2) {
|
|
70
|
+
try {
|
|
71
|
+
if (typeof ctx.get === "function") {
|
|
72
|
+
const viaGet = ctx.get(name2);
|
|
73
|
+
if (viaGet !== void 0) return viaGet;
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const direct = ctx[name2];
|
|
79
|
+
if (direct !== void 0) return direct;
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
function toolsOf(ctx) {
|
|
85
|
+
const tools = getService(ctx, "tools");
|
|
86
|
+
return isRecord(tools) ? tools : void 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/agent-event-project.ts
|
|
90
|
+
function projectTurnEndReason(raw) {
|
|
91
|
+
if (typeof raw === "string" && raw.trim()) {
|
|
92
|
+
return { kind: raw.trim() };
|
|
93
|
+
}
|
|
94
|
+
if (!isRecord(raw)) return void 0;
|
|
95
|
+
const kind = typeof raw.kind === "string" ? raw.kind : "";
|
|
96
|
+
if (!kind) return void 0;
|
|
97
|
+
const out = { kind };
|
|
98
|
+
if (raw.error !== void 0) out.error = raw.error;
|
|
99
|
+
if (raw.reason !== void 0) out.reason = raw.reason;
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
function contentBlocksToText(blocks) {
|
|
103
|
+
if (!Array.isArray(blocks)) return "";
|
|
104
|
+
const parts = [];
|
|
105
|
+
for (const block of blocks) {
|
|
106
|
+
if (!isRecord(block)) continue;
|
|
107
|
+
if (block.type === "text" && typeof block.text === "string") parts.push(block.text);
|
|
108
|
+
}
|
|
109
|
+
return parts.join("");
|
|
110
|
+
}
|
|
111
|
+
function parseToolArgs(raw) {
|
|
112
|
+
if (typeof raw !== "string") return raw;
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(raw);
|
|
115
|
+
} catch {
|
|
116
|
+
return raw;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function emitSafe(onEvent, event) {
|
|
120
|
+
if (!onEvent) return;
|
|
121
|
+
try {
|
|
122
|
+
onEvent(event);
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function createSessionEventProjector(onEvent) {
|
|
127
|
+
const callNames = /* @__PURE__ */ new Map();
|
|
128
|
+
return {
|
|
129
|
+
push(raw) {
|
|
130
|
+
if (!onEvent || !isRecord(raw)) return;
|
|
131
|
+
const type = typeof raw.type === "string" ? raw.type : "";
|
|
132
|
+
const data = isRecord(raw.data) ? raw.data : raw;
|
|
133
|
+
if (type === "turn/start" && typeof data.turn === "number") {
|
|
134
|
+
emitSafe(onEvent, { type: "turn", phase: "start", turn: data.turn });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (type === "turn/end" && typeof data.turn === "number") {
|
|
138
|
+
const reason = projectTurnEndReason(data.reason);
|
|
139
|
+
emitSafe(onEvent, {
|
|
140
|
+
type: "turn",
|
|
141
|
+
phase: "end",
|
|
142
|
+
turn: data.turn,
|
|
143
|
+
...reason ? { reason } : {}
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (type === "assistant/chunk") {
|
|
148
|
+
const chunk = isRecord(data.chunk) ? data.chunk : null;
|
|
149
|
+
if (chunk && chunk.type === "text-delta" && typeof chunk.text === "string" && chunk.text) {
|
|
150
|
+
emitSafe(onEvent, { type: "text-delta", text: chunk.text });
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (type === "tool/call") {
|
|
155
|
+
const name2 = typeof data.name === "string" ? data.name : "";
|
|
156
|
+
const callId = data.callId != null ? String(data.callId) : "";
|
|
157
|
+
if (callId && name2) callNames.set(callId, name2);
|
|
158
|
+
emitSafe(onEvent, {
|
|
159
|
+
type: "tool",
|
|
160
|
+
phase: "start",
|
|
161
|
+
name: name2,
|
|
162
|
+
args: parseToolArgs(data.arguments)
|
|
163
|
+
});
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (type === "tool/result") {
|
|
167
|
+
const message = isRecord(data.message) ? data.message : null;
|
|
168
|
+
const callId = message && Array.isArray(message.content) && isRecord(message.content[0]) ? String(message.content[0].callId ?? "") : "";
|
|
169
|
+
const name2 = callId && callNames.get(callId) || "";
|
|
170
|
+
if (callId) callNames.delete(callId);
|
|
171
|
+
const resultText = message ? contentBlocksToText(message.content) : "";
|
|
172
|
+
emitSafe(onEvent, {
|
|
173
|
+
type: "tool",
|
|
174
|
+
phase: "end",
|
|
175
|
+
name: name2,
|
|
176
|
+
result: resultText || data.error || void 0
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function emitAgentEvent(onEvent, event) {
|
|
183
|
+
emitSafe(onEvent, event);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/llm-route.ts
|
|
187
|
+
var DSH_LLM_FALLBACK = {
|
|
188
|
+
provider: "deepseek-official",
|
|
189
|
+
model: "deepseek-v4-flash"
|
|
190
|
+
};
|
|
191
|
+
function pickLlmRoute(value) {
|
|
192
|
+
if (!isRecord(value)) return null;
|
|
193
|
+
const provider = typeof value.provider === "string" ? value.provider.trim() : "";
|
|
194
|
+
const model = typeof value.model === "string" ? value.model.trim() : "";
|
|
195
|
+
if (!provider || !model) return null;
|
|
196
|
+
return { provider, model };
|
|
197
|
+
}
|
|
198
|
+
function resolveLlmRoute(ctx, opts, hostLlm) {
|
|
199
|
+
const fromOpts = pickLlmRoute({ provider: opts?.provider, model: opts?.model });
|
|
200
|
+
if (fromOpts) return fromOpts;
|
|
201
|
+
const fromHost = pickLlmRoute(hostLlm);
|
|
202
|
+
if (fromHost) return fromHost;
|
|
203
|
+
const cfg = getService(ctx, "config");
|
|
204
|
+
const fromCfg = isRecord(cfg) ? pickLlmRoute(cfg.llm) : null;
|
|
205
|
+
if (fromCfg) return fromCfg;
|
|
206
|
+
const adm = getService(ctx, "agentDefaultModel");
|
|
207
|
+
if (isRecord(adm) && typeof adm.currentSelection === "function") {
|
|
208
|
+
try {
|
|
209
|
+
const fromAdm = pickLlmRoute(adm.currentSelection());
|
|
210
|
+
if (fromAdm) return fromAdm;
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return { provider: DSH_LLM_FALLBACK.provider, model: DSH_LLM_FALLBACK.model };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/llm-stream.ts
|
|
218
|
+
function stringifyToolResult(value) {
|
|
219
|
+
if (typeof value === "string") return value;
|
|
220
|
+
try {
|
|
221
|
+
return JSON.stringify(value, null, 2);
|
|
222
|
+
} catch {
|
|
223
|
+
return String(value);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function withJsonInstruction(prompt, opts) {
|
|
227
|
+
if (!opts || opts.schema == null) {
|
|
228
|
+
return { prompt, system: opts?.system };
|
|
229
|
+
}
|
|
230
|
+
const schemaText = JSON.stringify(opts.schema);
|
|
231
|
+
const system = [
|
|
232
|
+
opts.system ?? "",
|
|
233
|
+
"Return ONLY a JSON object matching this JSON Schema. No markdown fences, no preamble.",
|
|
234
|
+
schemaText
|
|
235
|
+
].filter(Boolean).join("\n\n");
|
|
236
|
+
return { prompt, system };
|
|
237
|
+
}
|
|
238
|
+
function coerceSchemaJson(text, opts) {
|
|
239
|
+
if (!opts || opts.schema == null) return text;
|
|
240
|
+
const t = String(text || "").trim();
|
|
241
|
+
const fence = t.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
242
|
+
const body = (fence?.[1] ?? t).trim();
|
|
243
|
+
if (body.startsWith("{") || body.startsWith("[")) return body;
|
|
244
|
+
const startObj = body.indexOf("{");
|
|
245
|
+
const endObj = body.lastIndexOf("}");
|
|
246
|
+
if (startObj >= 0 && endObj > startObj) return body.slice(startObj, endObj + 1);
|
|
247
|
+
const startArr = body.indexOf("[");
|
|
248
|
+
const endArr = body.lastIndexOf("]");
|
|
249
|
+
if (startArr >= 0 && endArr > startArr) return body.slice(startArr, endArr + 1);
|
|
250
|
+
return body;
|
|
251
|
+
}
|
|
252
|
+
function asChunk(value) {
|
|
253
|
+
if (typeof value !== "object" || value === null) return null;
|
|
254
|
+
return value;
|
|
255
|
+
}
|
|
256
|
+
async function collectLlmStream(llmSvc, prompt, route, opts) {
|
|
257
|
+
const { prompt: text, system } = withJsonInstruction(prompt, opts);
|
|
258
|
+
const req = {
|
|
259
|
+
provider: route.provider,
|
|
260
|
+
model: route.model,
|
|
261
|
+
messages: [{ role: "user", content: [{ type: "text", text }] }],
|
|
262
|
+
system,
|
|
263
|
+
maxTokens: opts?.maxTokens ?? 1024
|
|
264
|
+
};
|
|
265
|
+
const acc = [];
|
|
266
|
+
let sawDelta = false;
|
|
267
|
+
for await (const raw of llmSvc.stream(req)) {
|
|
268
|
+
const chunk = asChunk(raw);
|
|
269
|
+
if (!chunk) continue;
|
|
270
|
+
const type = chunk.type;
|
|
271
|
+
if (type === "text-delta" && typeof chunk.text === "string") {
|
|
272
|
+
sawDelta = true;
|
|
273
|
+
acc.push(chunk.text);
|
|
274
|
+
} else if (type === "block-end" && chunk.block && typeof chunk.block.text === "string") {
|
|
275
|
+
if (!sawDelta) acc.push(chunk.block.text);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const out = acc.join("");
|
|
279
|
+
if (!out) throw new Error("llm stream empty");
|
|
280
|
+
return coerceSchemaJson(out, opts);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/agent-one-shot.ts
|
|
284
|
+
var helpersPromise = null;
|
|
285
|
+
var helpersLoadError;
|
|
286
|
+
function loadDshHelpers() {
|
|
287
|
+
if (!helpersPromise) {
|
|
288
|
+
helpersPromise = (async () => {
|
|
289
|
+
try {
|
|
290
|
+
const [sessionMod, llmMod, subagentMod] = await Promise.all([
|
|
291
|
+
import(
|
|
292
|
+
/* @vite-ignore */
|
|
293
|
+
"@deepseek-ai/dsh-session"
|
|
294
|
+
),
|
|
295
|
+
import(
|
|
296
|
+
/* @vite-ignore */
|
|
297
|
+
"@deepseek-ai/dsh-llm"
|
|
298
|
+
),
|
|
299
|
+
import(
|
|
300
|
+
/* @vite-ignore */
|
|
301
|
+
"@deepseek-ai/dsh-subagent"
|
|
302
|
+
)
|
|
303
|
+
]);
|
|
304
|
+
const SessionId = sessionMod.SessionId;
|
|
305
|
+
const createUserMessage = llmMod.createUserMessage;
|
|
306
|
+
const finalAssistantOutput = subagentMod.finalAssistantOutput;
|
|
307
|
+
if (!SessionId || !createUserMessage || !finalAssistantOutput) {
|
|
308
|
+
helpersLoadError = "exports missing SessionId / createUserMessage / finalAssistantOutput";
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
helpersLoadError = void 0;
|
|
312
|
+
return { SessionId, createUserMessage, finalAssistantOutput };
|
|
313
|
+
} catch (cause) {
|
|
314
|
+
helpersLoadError = cause instanceof Error ? cause.message : String(cause);
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
})();
|
|
318
|
+
}
|
|
319
|
+
return helpersPromise.then((helpers) => {
|
|
320
|
+
if (!helpers) helpersPromise = null;
|
|
321
|
+
return helpers;
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function contentBlocksToText2(blocks) {
|
|
325
|
+
if (!Array.isArray(blocks)) return "";
|
|
326
|
+
const parts = [];
|
|
327
|
+
for (const block of blocks) {
|
|
328
|
+
if (!isRecord(block)) continue;
|
|
329
|
+
if (block.type === "text" && typeof block.text === "string") parts.push(block.text);
|
|
330
|
+
}
|
|
331
|
+
return parts.join("");
|
|
332
|
+
}
|
|
333
|
+
function heuristicFinalText(events) {
|
|
334
|
+
let last = "";
|
|
335
|
+
for (const ev of events) {
|
|
336
|
+
if (!isRecord(ev)) continue;
|
|
337
|
+
const data = isRecord(ev.data) ? ev.data : ev;
|
|
338
|
+
const msg = isRecord(data.message) ? data.message : isRecord(data) ? data : null;
|
|
339
|
+
if (!msg || msg.role !== "assistant") continue;
|
|
340
|
+
const text = contentBlocksToText2(msg.content);
|
|
341
|
+
if (text.trim()) last = text;
|
|
342
|
+
}
|
|
343
|
+
return last;
|
|
344
|
+
}
|
|
345
|
+
function readTurnEndSummaries(events) {
|
|
346
|
+
const out = [];
|
|
347
|
+
for (const ev of events) {
|
|
348
|
+
if (!isRecord(ev) || ev.type !== "turn/end") continue;
|
|
349
|
+
const data = isRecord(ev.data) ? ev.data : ev;
|
|
350
|
+
const reason = data.reason;
|
|
351
|
+
const kind = isRecord(reason) && typeof reason.kind === "string" ? reason.kind : typeof reason === "string" ? reason : "unknown";
|
|
352
|
+
const turn = typeof data.turn === "number" ? data.turn : "?";
|
|
353
|
+
let detail;
|
|
354
|
+
if (isRecord(reason) && isRecord(reason.error)) {
|
|
355
|
+
const code = typeof reason.error.code === "string" ? reason.error.code : "";
|
|
356
|
+
const message = typeof reason.error.message === "string" ? reason.error.message : "";
|
|
357
|
+
detail = [code, message].filter(Boolean).join(": ") || void 0;
|
|
358
|
+
} else if (isRecord(reason) && reason.reason !== void 0) {
|
|
359
|
+
detail = typeof reason.reason === "string" ? reason.reason : JSON.stringify(reason.reason);
|
|
360
|
+
}
|
|
361
|
+
out.push({ turn, kind, detail });
|
|
362
|
+
}
|
|
363
|
+
return out;
|
|
364
|
+
}
|
|
365
|
+
function summarizeSessionEvents(events) {
|
|
366
|
+
const types = [];
|
|
367
|
+
for (const ev of events) {
|
|
368
|
+
if (!isRecord(ev)) continue;
|
|
369
|
+
types.push(typeof ev.type === "string" ? ev.type : "?");
|
|
370
|
+
}
|
|
371
|
+
const turnEnds = readTurnEndSummaries(events).map((t) => {
|
|
372
|
+
const base = `t${t.turn}:${t.kind}`;
|
|
373
|
+
return t.detail ? `${base}(${t.detail.slice(0, 160)})` : base;
|
|
374
|
+
});
|
|
375
|
+
const typeSeq = types.length ? types.join(" \u2192 ") : "(no events)";
|
|
376
|
+
const ends = turnEnds.length ? turnEnds.join(", ") : "(no turn/end)";
|
|
377
|
+
return `events=[${typeSeq}]; turnEnds=[${ends}]`;
|
|
378
|
+
}
|
|
379
|
+
function primaryTurnFailureMessage(events) {
|
|
380
|
+
const ends = readTurnEndSummaries(events);
|
|
381
|
+
for (let i = ends.length - 1; i >= 0; i--) {
|
|
382
|
+
const t = ends[i];
|
|
383
|
+
if (t.kind === "error") {
|
|
384
|
+
return t.detail ? `agent: turn ${t.turn} failed: ${t.detail}` : `agent: turn ${t.turn} failed`;
|
|
385
|
+
}
|
|
386
|
+
if (t.kind === "blocked") {
|
|
387
|
+
return `agent: turn ${t.turn} blocked (pre-step rejected)`;
|
|
388
|
+
}
|
|
389
|
+
if (t.kind === "aborted") {
|
|
390
|
+
return t.detail ? `agent: turn ${t.turn} aborted: ${t.detail}` : `agent: turn ${t.turn} aborted`;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return void 0;
|
|
394
|
+
}
|
|
395
|
+
function asAgentsService(value) {
|
|
396
|
+
if (!isRecord(value) || typeof value.create !== "function") return null;
|
|
397
|
+
return value;
|
|
398
|
+
}
|
|
399
|
+
function buildGoalText(goal, opts) {
|
|
400
|
+
const { prompt, system } = withJsonInstruction(goal, opts);
|
|
401
|
+
if (!system) return prompt;
|
|
402
|
+
return `${system}
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
${prompt}`;
|
|
407
|
+
}
|
|
408
|
+
function onEventOf(opts) {
|
|
409
|
+
return typeof opts?.onEvent === "function" ? opts.onEvent : void 0;
|
|
410
|
+
}
|
|
411
|
+
async function composeOneShotSetup(agentCtx) {
|
|
412
|
+
const presets = getService(agentCtx, "agentPresets");
|
|
413
|
+
if (isRecord(presets) && typeof presets.mount === "function") {
|
|
414
|
+
await presets.mount(agentCtx);
|
|
415
|
+
} else {
|
|
416
|
+
console.warn(
|
|
417
|
+
"[monkey-mini-app] agentPresets.mount unavailable; ctx.agent one-shot will lack preset tools (bash/read)"
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
const tools = toolsOf(agentCtx) ?? (isRecord(agentCtx) ? agentCtx.tools : void 0);
|
|
421
|
+
if (tools && typeof tools.restrict === "function") {
|
|
422
|
+
const rows = [];
|
|
423
|
+
if (typeof tools.schemas === "function") {
|
|
424
|
+
const list = tools.schemas();
|
|
425
|
+
if (Array.isArray(list)) rows.push(...list);
|
|
426
|
+
} else if (typeof tools.list === "function") {
|
|
427
|
+
const list = tools.list();
|
|
428
|
+
if (Array.isArray(list)) rows.push(...list);
|
|
429
|
+
}
|
|
430
|
+
for (const row of rows) {
|
|
431
|
+
const name2 = isRecord(row) && typeof row.name === "string" ? row.name : "";
|
|
432
|
+
if (!isMiniAppToolName(name2)) continue;
|
|
433
|
+
try {
|
|
434
|
+
tools.restrict({ deny: [name2] });
|
|
435
|
+
} catch {
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
commit() {
|
|
441
|
+
const agent = isRecord(agentCtx) ? agentCtx.agent : void 0;
|
|
442
|
+
const sessionRec = isRecord(agent) && isRecord(agent.session) ? agent.session : void 0;
|
|
443
|
+
const append = sessionRec && typeof sessionRec.append === "function" ? sessionRec.append : void 0;
|
|
444
|
+
if (!append) return;
|
|
445
|
+
try {
|
|
446
|
+
append("approval/policy", { policy: "never", source: "delegation" });
|
|
447
|
+
} catch {
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
append("sandbox/mode", { mode: "danger-full-access", source: "delegation" });
|
|
451
|
+
} catch {
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
append("permission/preset", {
|
|
455
|
+
preset: "danger-full-access",
|
|
456
|
+
source: "delegation"
|
|
457
|
+
});
|
|
458
|
+
} catch {
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function drainSessionEvents(events, cursor, projector) {
|
|
464
|
+
while (cursor.i < events.length) {
|
|
465
|
+
projector.push(events[cursor.i]);
|
|
466
|
+
cursor.i += 1;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
async function runDshAgentOneShot(dsh, goal, opts, deps) {
|
|
470
|
+
const callCtx = deps?.callCtx;
|
|
471
|
+
const agents = asAgentsService(getService(dsh, "agents"));
|
|
472
|
+
if (!agents) {
|
|
473
|
+
throw new Error(
|
|
474
|
+
"agent: dsh agents factory unbound (ctx.agents.create). Ensure @deepseek-ai/dsh-agent-loop is loaded."
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
const helpers = await (deps?.loadHelpers ?? loadDshHelpers)();
|
|
478
|
+
if (!helpers) {
|
|
479
|
+
const detail = helpersLoadError ? `: ${helpersLoadError}` : "";
|
|
480
|
+
throw new Error(
|
|
481
|
+
`agent: dsh SDK helpers unavailable (@deepseek-ai/dsh-session / dsh-llm / dsh-subagent)${detail}`
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
const userOnEvent = onEventOf(opts);
|
|
485
|
+
const route = resolveLlmRoute(dsh, opts, callCtx?.hostLlm);
|
|
486
|
+
if (!route.provider.trim() || !route.model.trim()) {
|
|
487
|
+
throw new Error(
|
|
488
|
+
`agent: unresolved model route (need provider+model for prompt assembly; got provider=${JSON.stringify(route.provider)} model=${JSON.stringify(route.model)})`
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
const maxTokens = opts?.maxTokens;
|
|
492
|
+
const maxIterations = typeof opts?.maxIterations === "number" && Number.isFinite(opts.maxIterations) && opts.maxIterations > 0 ? Math.floor(opts.maxIterations) : void 0;
|
|
493
|
+
const signal = effectiveSignal(
|
|
494
|
+
opts?.signal instanceof AbortSignal ? opts.signal : void 0,
|
|
495
|
+
callCtx
|
|
496
|
+
);
|
|
497
|
+
const sessionId = helpers.SessionId(randomUUID());
|
|
498
|
+
const goalText = buildGoalText(String(goal || ""), opts);
|
|
499
|
+
const cwd = resolveAgentCwd(
|
|
500
|
+
{ cwdType: opts?.cwdType, cwd: opts?.cwd },
|
|
501
|
+
{ appDir: callCtx?.appDir }
|
|
502
|
+
);
|
|
503
|
+
const cleanupTemp = opts?.cwdType === "temp" ? cwd : void 0;
|
|
504
|
+
const handle = await agents.create({
|
|
505
|
+
sessionId,
|
|
506
|
+
// Absolute cwd required for {{cwd}} in deployment:persona; origin marks ephemeral isolation.
|
|
507
|
+
meta: { origin: "subagent", cwd },
|
|
508
|
+
agentOptions: {
|
|
509
|
+
provider: route.provider,
|
|
510
|
+
model: route.model,
|
|
511
|
+
...maxTokens != null ? { maxTokens } : {}
|
|
512
|
+
},
|
|
513
|
+
// Headless: never ask (no UI), open sandbox, hide mini_app_* from this agent.
|
|
514
|
+
setup: (agentCtx) => composeOneShotSetup(agentCtx),
|
|
515
|
+
...signal ? { signal } : {}
|
|
516
|
+
});
|
|
517
|
+
try {
|
|
518
|
+
const approval = getService(dsh, "approval");
|
|
519
|
+
if (isRecord(approval) && typeof approval.setPolicy === "function" && isRecord(handle.agent)) {
|
|
520
|
+
approval.setPolicy(handle.agent, "never");
|
|
521
|
+
}
|
|
522
|
+
} catch {
|
|
523
|
+
}
|
|
524
|
+
const onAbort = () => {
|
|
525
|
+
try {
|
|
526
|
+
handle.agent.cancel("host", { keepInbox: false });
|
|
527
|
+
} catch {
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
let turnEnds = 0;
|
|
531
|
+
let hitIterationLimit = false;
|
|
532
|
+
const onEvent = userOnEvent || maxIterations ? (ev) => {
|
|
533
|
+
if (ev.type === "turn" && ev.phase === "end" && maxIterations) {
|
|
534
|
+
turnEnds += 1;
|
|
535
|
+
if (turnEnds >= maxIterations && !hitIterationLimit) {
|
|
536
|
+
hitIterationLimit = true;
|
|
537
|
+
try {
|
|
538
|
+
handle.agent.cancel("host", { keepInbox: false });
|
|
539
|
+
} catch {
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
userOnEvent?.(ev);
|
|
544
|
+
} : void 0;
|
|
545
|
+
const projector = createSessionEventProjector(onEvent);
|
|
546
|
+
const cursor = { i: 0 };
|
|
547
|
+
let pollTimer;
|
|
548
|
+
try {
|
|
549
|
+
if (signal?.aborted) {
|
|
550
|
+
throw new Error("cancelled");
|
|
551
|
+
}
|
|
552
|
+
if (signal) signal.addEventListener("abort", onAbort, { once: true });
|
|
553
|
+
emitAgentEvent(onEvent, { type: "status", status: "running" });
|
|
554
|
+
const message = helpers.createUserMessage({
|
|
555
|
+
content: [{ type: "text", text: goalText }],
|
|
556
|
+
source: { kind: "plugin", plugin: "monkey-mini-app" }
|
|
557
|
+
});
|
|
558
|
+
handle.agent.followup(message);
|
|
559
|
+
if (onEvent) {
|
|
560
|
+
const pollMs = deps?.pollMs ?? 40;
|
|
561
|
+
pollTimer = setInterval(() => {
|
|
562
|
+
try {
|
|
563
|
+
drainSessionEvents(handle.agent.session.events, cursor, projector);
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
}, pollMs);
|
|
567
|
+
}
|
|
568
|
+
await handle.agent.whenIdle();
|
|
569
|
+
if (pollTimer) {
|
|
570
|
+
clearInterval(pollTimer);
|
|
571
|
+
pollTimer = void 0;
|
|
572
|
+
}
|
|
573
|
+
drainSessionEvents(handle.agent.session.events, cursor, projector);
|
|
574
|
+
emitAgentEvent(onEvent, { type: "status", status: "idle" });
|
|
575
|
+
if (signal?.aborted) {
|
|
576
|
+
throw new Error("cancelled");
|
|
577
|
+
}
|
|
578
|
+
const events = handle.agent.session.events;
|
|
579
|
+
const blocks = helpers.finalAssistantOutput(events);
|
|
580
|
+
const text = contentBlocksToText2(blocks) || heuristicFinalText(events);
|
|
581
|
+
if (!text) {
|
|
582
|
+
const diag = summarizeSessionEvents(events);
|
|
583
|
+
const turnFailure = primaryTurnFailureMessage(events);
|
|
584
|
+
if (turnFailure) {
|
|
585
|
+
throw new Error(`${turnFailure}; ${diag}`);
|
|
586
|
+
}
|
|
587
|
+
throw new Error(
|
|
588
|
+
hitIterationLimit ? `agent: empty result after maxIterations=${maxIterations}; ${diag}` : `agent: empty result (no assistant output); ${diag}`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
const finalText = coerceSchemaJson(text, opts);
|
|
592
|
+
emitAgentEvent(onEvent, { type: "done", text: finalText });
|
|
593
|
+
return finalText;
|
|
594
|
+
} catch (cause) {
|
|
595
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
596
|
+
emitAgentEvent(onEvent, { type: "error", message });
|
|
597
|
+
throw cause;
|
|
598
|
+
} finally {
|
|
599
|
+
if (pollTimer) clearInterval(pollTimer);
|
|
600
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
601
|
+
try {
|
|
602
|
+
await handle.dispose();
|
|
603
|
+
} catch {
|
|
604
|
+
}
|
|
605
|
+
if (cleanupTemp) {
|
|
606
|
+
try {
|
|
607
|
+
await rm(cleanupTemp, { recursive: true, force: true });
|
|
608
|
+
} catch {
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// src/capabilities.ts
|
|
615
|
+
var execFileAsync = promisify(execFile);
|
|
616
|
+
function listDshTools(ctx) {
|
|
617
|
+
try {
|
|
618
|
+
const t = toolsOf(ctx);
|
|
619
|
+
if (typeof t?.schemas === "function") {
|
|
620
|
+
const list = t.schemas();
|
|
621
|
+
if (Array.isArray(list)) {
|
|
622
|
+
return list.map((x) => ({
|
|
623
|
+
name: String(x.name || ""),
|
|
624
|
+
description: String(x.description || ""),
|
|
625
|
+
parameters: x.parameters ?? { type: "object", properties: {} }
|
|
626
|
+
}));
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return [];
|
|
630
|
+
} catch {
|
|
631
|
+
return [];
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
var DshCapabilities = class {
|
|
635
|
+
constructor(dsh) {
|
|
636
|
+
this.dsh = dsh;
|
|
637
|
+
}
|
|
638
|
+
dsh;
|
|
639
|
+
async bash(_ctx, command) {
|
|
640
|
+
try {
|
|
641
|
+
const { stdout, stderr } = await execFileAsync("bash", ["-c", command], {
|
|
642
|
+
timeout: 12e4,
|
|
643
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
644
|
+
env: process.env
|
|
645
|
+
});
|
|
646
|
+
return {
|
|
647
|
+
stdout: String(stdout ?? ""),
|
|
648
|
+
stderr: String(stderr ?? ""),
|
|
649
|
+
exitCode: 0
|
|
650
|
+
};
|
|
651
|
+
} catch (cause) {
|
|
652
|
+
const err = cause;
|
|
653
|
+
const exitCode = typeof err.code === "number" ? err.code : 1;
|
|
654
|
+
return {
|
|
655
|
+
stdout: String(err.stdout ?? ""),
|
|
656
|
+
stderr: String(err.stderr ?? (cause instanceof Error ? cause.message : String(cause))),
|
|
657
|
+
exitCode
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
async llm(ctx, prompt, opts) {
|
|
662
|
+
const llmSvc = getService(this.dsh, "llm") || getService(this.dsh, "model") || getService(this.dsh, "chat");
|
|
663
|
+
const route = resolveLlmRoute(this.dsh, opts, ctx.hostLlm);
|
|
664
|
+
if (isRecord(llmSvc) && typeof llmSvc.stream === "function") {
|
|
665
|
+
return collectLlmStream(llmSvc, prompt, route, opts);
|
|
666
|
+
}
|
|
667
|
+
if (typeof llmSvc === "function") {
|
|
668
|
+
return stringifyToolResult(
|
|
669
|
+
await llmSvc(prompt, opts)
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
throw new Error("llm: no dsh model service bound (ctx.llm)");
|
|
673
|
+
}
|
|
674
|
+
async agent(ctx, goal, opts) {
|
|
675
|
+
const agents = getService(this.dsh, "agents") || getService(this.dsh, "subagents");
|
|
676
|
+
if (isRecord(agents) && typeof agents.run === "function") {
|
|
677
|
+
return stringifyToolResult(
|
|
678
|
+
await agents.run(goal, opts)
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
if (isRecord(agents) && typeof agents.spawn === "function") {
|
|
682
|
+
return stringifyToolResult(
|
|
683
|
+
await agents.spawn({ goal, ...opts || {} })
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
return runDshAgentOneShot(this.dsh, goal, opts, { callCtx: ctx });
|
|
687
|
+
}
|
|
688
|
+
async tool(_ctx, name2, args) {
|
|
689
|
+
const tools = toolsOf(this.dsh);
|
|
690
|
+
if (!tools) throw new Error("tool: no dsh tools service");
|
|
691
|
+
const t = typeof tools.get === "function" ? tools.get(name2) : void 0;
|
|
692
|
+
if (isRecord(t) && typeof t.execute === "function") {
|
|
693
|
+
return stringifyToolResult(
|
|
694
|
+
await t.execute(args || {})
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
throw new Error(`tool not found: ${name2}`);
|
|
698
|
+
}
|
|
699
|
+
async mcp(_ctx, name2, args) {
|
|
700
|
+
const mcpSvc = getService(this.dsh, "mcp");
|
|
701
|
+
if (!mcpSvc) throw new Error("mcp: no dsh mcp service");
|
|
702
|
+
const call = isRecord(mcpSvc) ? typeof mcpSvc.call === "function" ? mcpSvc.call : mcpSvc.execute : void 0;
|
|
703
|
+
if (typeof call !== "function") throw new Error("mcp: service has no call");
|
|
704
|
+
return stringifyToolResult(await call(name2, args));
|
|
705
|
+
}
|
|
706
|
+
credentials(_ctx) {
|
|
707
|
+
const cred = getService(this.dsh, "credentials");
|
|
708
|
+
if (!cred) return {};
|
|
709
|
+
if (isRecord(cred) && typeof cred.getAll === "function") {
|
|
710
|
+
const all = cred.getAll() || {};
|
|
711
|
+
return { ...all };
|
|
712
|
+
}
|
|
713
|
+
if (isRecord(cred)) {
|
|
714
|
+
const out = {};
|
|
715
|
+
for (const [k, v] of Object.entries(cred)) {
|
|
716
|
+
if (typeof v === "string") out[k] = v;
|
|
717
|
+
}
|
|
718
|
+
return out;
|
|
719
|
+
}
|
|
720
|
+
return {};
|
|
721
|
+
}
|
|
722
|
+
config(_ctx) {
|
|
723
|
+
const cfg = getService(this.dsh, "config");
|
|
724
|
+
return isRecord(cfg) ? { ...cfg } : {};
|
|
725
|
+
}
|
|
726
|
+
listTools(_ctx) {
|
|
727
|
+
return listDshTools(this.dsh);
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
// src/lifecycle.ts
|
|
732
|
+
import { cpSync, mkdirSync } from "fs";
|
|
733
|
+
import { homedir as homedir2 } from "os";
|
|
734
|
+
import path3 from "path";
|
|
735
|
+
|
|
736
|
+
// src/skills.ts
|
|
737
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
738
|
+
import path2 from "path";
|
|
739
|
+
import { fileURLToPath } from "url";
|
|
740
|
+
function resolveSkillDir() {
|
|
741
|
+
const here = path2.dirname(fileURLToPath(import.meta.url));
|
|
742
|
+
const candidates = [
|
|
743
|
+
path2.join(here, "..", "skills", "monkey-mini-app"),
|
|
744
|
+
path2.join(here, "..", "..", "skills", "monkey-mini-app")
|
|
745
|
+
];
|
|
746
|
+
for (const c of candidates) {
|
|
747
|
+
if (existsSync(path2.join(c, "SKILL.md"))) return c;
|
|
748
|
+
}
|
|
749
|
+
return candidates[0];
|
|
750
|
+
}
|
|
751
|
+
function getSkillDir() {
|
|
752
|
+
return resolveSkillDir();
|
|
753
|
+
}
|
|
754
|
+
function getSkillMarkdown() {
|
|
755
|
+
return readFileSync(path2.join(resolveSkillDir(), "SKILL.md"), "utf8");
|
|
756
|
+
}
|
|
757
|
+
function getTemplateFiles(name2) {
|
|
758
|
+
const base = path2.join(resolveSkillDir(), "templates", name2);
|
|
759
|
+
if (!existsSync(base)) throw new Error(`template not found: ${name2} under ${resolveSkillDir()}`);
|
|
760
|
+
const out = {};
|
|
761
|
+
const walk = (dir, prefix) => {
|
|
762
|
+
for (const ent of readdirSync(dir, { withFileTypes: true })) {
|
|
763
|
+
const rel = prefix ? `${prefix}/${ent.name}` : ent.name;
|
|
764
|
+
const full = path2.join(dir, ent.name);
|
|
765
|
+
if (ent.isDirectory()) walk(full, rel);
|
|
766
|
+
else out[rel] = readFileSync(full, "utf8");
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
walk(base, "");
|
|
770
|
+
return out;
|
|
771
|
+
}
|
|
772
|
+
function getSkeletonTemplateFiles() {
|
|
773
|
+
return getTemplateFiles("minimal");
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// src/lifecycle.ts
|
|
777
|
+
function snapshotDshTools(tools) {
|
|
778
|
+
if (!tools) return [];
|
|
779
|
+
const rows = [];
|
|
780
|
+
const seen = /* @__PURE__ */ new Set();
|
|
781
|
+
const push = (raw, fallbackName) => {
|
|
782
|
+
const t = isRecord(raw) ? raw : {};
|
|
783
|
+
const name2 = String(t.name || t.title || t.id || fallbackName || "");
|
|
784
|
+
if (!name2 || seen.has(name2)) return;
|
|
785
|
+
seen.add(name2);
|
|
786
|
+
rows.push({ name: name2, description: String(t.description || "") });
|
|
787
|
+
};
|
|
788
|
+
const listed = isRecord(tools) && typeof tools.list === "function" ? tools.list() : void 0;
|
|
789
|
+
if (Array.isArray(listed)) {
|
|
790
|
+
for (const item of listed) push(item);
|
|
791
|
+
} else if (isRecord(tools)) {
|
|
792
|
+
for (const key of Object.keys(tools)) {
|
|
793
|
+
push(tools[key], key);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return rows;
|
|
797
|
+
}
|
|
798
|
+
async function loadDefineTool() {
|
|
799
|
+
try {
|
|
800
|
+
const mod = await import(
|
|
801
|
+
/* @vite-ignore */
|
|
802
|
+
"@deepseek-ai/dsh-tools"
|
|
803
|
+
);
|
|
804
|
+
return typeof mod.defineTool === "function" ? mod.defineTool : null;
|
|
805
|
+
} catch {
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
function toDshValueSchema(field, fallbackDesc) {
|
|
810
|
+
const type = typeof field.type === "string" ? field.type : "string";
|
|
811
|
+
const description = typeof field.description === "string" ? field.description : fallbackDesc !== void 0 ? fallbackDesc : void 0;
|
|
812
|
+
if (type === "object") {
|
|
813
|
+
const props = isRecord(field.properties) ? field.properties : {};
|
|
814
|
+
const req = new Set(Array.isArray(field.required) ? field.required.map(String) : []);
|
|
815
|
+
const properties = {};
|
|
816
|
+
for (const [k, raw] of Object.entries(props)) {
|
|
817
|
+
const child = toDshValueSchema(isRecord(raw) ? raw : {}, k);
|
|
818
|
+
if (req.has(k)) child.required = true;
|
|
819
|
+
properties[k] = child;
|
|
820
|
+
}
|
|
821
|
+
const out2 = {
|
|
822
|
+
type: "object",
|
|
823
|
+
additionalProperties: typeof field.additionalProperties === "boolean" ? field.additionalProperties : true
|
|
824
|
+
};
|
|
825
|
+
if (description) out2.description = description;
|
|
826
|
+
if (Object.keys(properties).length > 0) out2.properties = properties;
|
|
827
|
+
return out2;
|
|
828
|
+
}
|
|
829
|
+
if (type === "array") {
|
|
830
|
+
const out2 = {
|
|
831
|
+
type: "array",
|
|
832
|
+
items: isRecord(field.items) ? toDshValueSchema(field.items) : { type: "string" }
|
|
833
|
+
};
|
|
834
|
+
if (description) out2.description = description;
|
|
835
|
+
return out2;
|
|
836
|
+
}
|
|
837
|
+
const out = { type };
|
|
838
|
+
if (description) out.description = description;
|
|
839
|
+
if (Array.isArray(field.enum)) out.enum = field.enum;
|
|
840
|
+
return out;
|
|
841
|
+
}
|
|
842
|
+
function toolParameters(toolDef) {
|
|
843
|
+
const schema = isRecord(toolDef.inputSchema) ? toolDef.inputSchema : {};
|
|
844
|
+
const props = isRecord(schema.properties) ? schema.properties : {};
|
|
845
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required.map(String) : []);
|
|
846
|
+
const parameters = {};
|
|
847
|
+
for (const [key, raw] of Object.entries(props)) {
|
|
848
|
+
const field = isRecord(raw) ? raw : {};
|
|
849
|
+
const param = toDshValueSchema(field, key);
|
|
850
|
+
if (required.has(key)) param.required = true;
|
|
851
|
+
parameters[key] = param;
|
|
852
|
+
}
|
|
853
|
+
return parameters;
|
|
854
|
+
}
|
|
855
|
+
function registerTools(ctx, tools, defineTool) {
|
|
856
|
+
const disposers = [];
|
|
857
|
+
const toolsSvc = toolsOf(ctx);
|
|
858
|
+
if (!toolsSvc?.register) {
|
|
859
|
+
console.warn("[monkey-mini-app] ctx.tools missing; inject=['tools'] required for model-facing tools");
|
|
860
|
+
return disposers;
|
|
861
|
+
}
|
|
862
|
+
for (const toolDef of tools) {
|
|
863
|
+
const parameters = toolParameters(toolDef);
|
|
864
|
+
const execute = async (args, toolCtx) => {
|
|
865
|
+
return toolDef.execute ? await toolDef.execute(args ?? {}, toolCtx?.signal) : { ok: false, error: "no execute" };
|
|
866
|
+
};
|
|
867
|
+
const built = defineTool ? defineTool({
|
|
868
|
+
name: toolDef.name,
|
|
869
|
+
description: toolDef.description,
|
|
870
|
+
parameters,
|
|
871
|
+
output: {
|
|
872
|
+
schema: { type: "object", additionalProperties: true },
|
|
873
|
+
render: (_a, v) => [
|
|
874
|
+
{ type: "text", text: typeof v === "string" ? v : JSON.stringify(v, null, 2) }
|
|
875
|
+
]
|
|
876
|
+
},
|
|
877
|
+
execute
|
|
878
|
+
}) : {
|
|
879
|
+
name: toolDef.name,
|
|
880
|
+
description: toolDef.description,
|
|
881
|
+
parameters,
|
|
882
|
+
output: {
|
|
883
|
+
schema: { type: "object", additionalProperties: true },
|
|
884
|
+
render: (_a, v) => [
|
|
885
|
+
{ type: "text", text: typeof v === "string" ? v : JSON.stringify(v, null, 2) }
|
|
886
|
+
]
|
|
887
|
+
},
|
|
888
|
+
execute
|
|
889
|
+
};
|
|
890
|
+
try {
|
|
891
|
+
const ret = toolsSvc.register(built);
|
|
892
|
+
if (typeof ret === "function") disposers.push(ret);
|
|
893
|
+
} catch (e) {
|
|
894
|
+
console.warn(`[monkey-mini-app] tools.register failed for ${toolDef.name}:`, e);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return disposers;
|
|
898
|
+
}
|
|
899
|
+
function registerListCtxTools(ctx, defineTool = null) {
|
|
900
|
+
const toolsSvc = toolsOf(ctx);
|
|
901
|
+
if (!toolsSvc?.register) return null;
|
|
902
|
+
try {
|
|
903
|
+
const def = {
|
|
904
|
+
name: "mini_app_list_ctx_tools",
|
|
905
|
+
description: "List live dsh tools for ctx.tool(name, args) inside main.api.ts. Call ONLY when the app will use ctx.tool. Skip for storage/bash/llm-only apps.",
|
|
906
|
+
parameters: {},
|
|
907
|
+
output: {
|
|
908
|
+
schema: { type: "object", additionalProperties: true },
|
|
909
|
+
render: (_a, v) => [
|
|
910
|
+
{ type: "text", text: typeof v === "string" ? v : JSON.stringify(v, null, 2) }
|
|
911
|
+
]
|
|
912
|
+
},
|
|
913
|
+
async execute() {
|
|
914
|
+
const listed = snapshotDshTools(toolsOf(ctx));
|
|
915
|
+
return {
|
|
916
|
+
count: listed.length,
|
|
917
|
+
note: "These names are for ctx.tool(name, args) inside main.api.ts. Prefer storage/bash/llm when possible.",
|
|
918
|
+
tools: listed
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
const built = defineTool ? defineTool(def) : def;
|
|
923
|
+
const ret = toolsSvc.register(built);
|
|
924
|
+
return typeof ret === "function" ? ret : null;
|
|
925
|
+
} catch (e) {
|
|
926
|
+
console.warn("[monkey-mini-app] register mini_app_list_ctx_tools failed", e);
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
function installSkillDir(src, dest) {
|
|
931
|
+
mkdirSync(path3.dirname(dest), { recursive: true });
|
|
932
|
+
cpSync(src, dest, { recursive: true });
|
|
933
|
+
}
|
|
934
|
+
function defaultSkillDest() {
|
|
935
|
+
return path3.join(homedir2(), ".dsh", "skills", "monkey-mini-app");
|
|
936
|
+
}
|
|
937
|
+
var DshLifecycle = class {
|
|
938
|
+
constructor(ctx, options = {}) {
|
|
939
|
+
this.ctx = ctx;
|
|
940
|
+
this.options = options;
|
|
941
|
+
}
|
|
942
|
+
ctx;
|
|
943
|
+
options;
|
|
944
|
+
disposers = [];
|
|
945
|
+
async attach(_ctx, services) {
|
|
946
|
+
const defineTool = await loadDefineTool();
|
|
947
|
+
this.disposers.push(...registerTools(this.ctx, services.tools.definitions(), defineTool));
|
|
948
|
+
const extra = registerListCtxTools(this.ctx, defineTool);
|
|
949
|
+
if (extra) this.disposers.push(extra);
|
|
950
|
+
try {
|
|
951
|
+
installSkillDir(getSkillDir(), this.options.skillDest ?? defaultSkillDest());
|
|
952
|
+
} catch (e) {
|
|
953
|
+
console.warn("[monkey-mini-app] skill install skipped:", e);
|
|
954
|
+
}
|
|
955
|
+
if (typeof this.ctx.provide === "function") {
|
|
956
|
+
this.ctx.provide("monkeyMiniApp", {
|
|
957
|
+
tools: services.tools,
|
|
958
|
+
paths: services.paths,
|
|
959
|
+
config: services.config,
|
|
960
|
+
runtimeRoot: services.paths.root,
|
|
961
|
+
skillDir: getSkillDir(),
|
|
962
|
+
skillMarkdown: getSkillMarkdown()
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
async detach() {
|
|
967
|
+
const list = this.disposers.splice(0, this.disposers.length);
|
|
968
|
+
for (const d of list) {
|
|
969
|
+
try {
|
|
970
|
+
d();
|
|
971
|
+
} catch {
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
onHostPortChanged(_port) {
|
|
976
|
+
}
|
|
977
|
+
log(level, message, meta) {
|
|
978
|
+
const line = `[monkey-mini-app] ${message}`;
|
|
979
|
+
if (level === "error") console.error(line, meta ?? "");
|
|
980
|
+
else if (level === "warn") console.warn(line, meta ?? "");
|
|
981
|
+
else console.log(line, meta ?? "");
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
// src/theme-resource.ts
|
|
986
|
+
import fs from "fs";
|
|
987
|
+
import path4 from "path";
|
|
988
|
+
import {
|
|
989
|
+
cssVars,
|
|
990
|
+
parseThemeCss,
|
|
991
|
+
runnerThemeCss,
|
|
992
|
+
themeLabelFromCss
|
|
993
|
+
} from "@monkey-mini-app/panel/themes";
|
|
994
|
+
function customThemesDir(runtimeRoot) {
|
|
995
|
+
return path4.join(path4.resolve(runtimeRoot, ".."), "themes");
|
|
996
|
+
}
|
|
997
|
+
function loadCustomPalettes(runtimeRoot) {
|
|
998
|
+
const dir = customThemesDir(runtimeRoot);
|
|
999
|
+
const out = [];
|
|
1000
|
+
try {
|
|
1001
|
+
if (!fs.existsSync(dir)) return out;
|
|
1002
|
+
const names = fs.readdirSync(dir).filter((n) => /^theme-.+\.css$/.test(n));
|
|
1003
|
+
for (const n of names) {
|
|
1004
|
+
const id = n.replace(/^theme-/, "").replace(/\.css$/, "");
|
|
1005
|
+
const css = fs.readFileSync(path4.join(dir, n), "utf8");
|
|
1006
|
+
const tokens = parseThemeCss(css, id);
|
|
1007
|
+
if (!tokens) continue;
|
|
1008
|
+
out.push({
|
|
1009
|
+
id,
|
|
1010
|
+
label: themeLabelFromCss(css, id),
|
|
1011
|
+
swatch: tokens.dark.primary,
|
|
1012
|
+
tokens
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
} catch {
|
|
1016
|
+
}
|
|
1017
|
+
return out;
|
|
1018
|
+
}
|
|
1019
|
+
function customPaletteCss(runtimeRoot) {
|
|
1020
|
+
return loadCustomPalettes(runtimeRoot).map(
|
|
1021
|
+
(c) => `html[data-theme="light"][data-palette="${c.id}"]{${cssVars(c.tokens.light)}}html[data-theme="dark"][data-palette="${c.id}"]{${cssVars(c.tokens.dark)}}`
|
|
1022
|
+
).join("\n ");
|
|
1023
|
+
}
|
|
1024
|
+
var DshThemeResource = class {
|
|
1025
|
+
constructor(runtimeRoot) {
|
|
1026
|
+
this.runtimeRoot = runtimeRoot;
|
|
1027
|
+
}
|
|
1028
|
+
runtimeRoot;
|
|
1029
|
+
runnerCss() {
|
|
1030
|
+
return runnerThemeCss() + customPaletteCss(this.runtimeRoot);
|
|
1031
|
+
}
|
|
1032
|
+
listCustomPalettes() {
|
|
1033
|
+
return loadCustomPalettes(this.runtimeRoot).map((c) => ({
|
|
1034
|
+
id: c.id,
|
|
1035
|
+
label: c.label,
|
|
1036
|
+
swatch: c.swatch,
|
|
1037
|
+
custom: true,
|
|
1038
|
+
tokens: c.tokens
|
|
1039
|
+
}));
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
// src/index.ts
|
|
1044
|
+
var packageName = "@monkey-mini-app/dsh-mini-app";
|
|
1045
|
+
var name = "monkey-mini-app";
|
|
1046
|
+
var inject = ["tools"];
|
|
1047
|
+
async function apply(ctx, config = {}) {
|
|
1048
|
+
const hostConfig = loadPluginHostConfig(config);
|
|
1049
|
+
const themes = new DshThemeResource(hostConfig.runtimeRoot);
|
|
1050
|
+
const host = createHost(new DshCapabilities(ctx), new DshLifecycle(ctx), {
|
|
1051
|
+
config: hostConfig,
|
|
1052
|
+
themes
|
|
1053
|
+
});
|
|
1054
|
+
let port;
|
|
1055
|
+
try {
|
|
1056
|
+
({ port } = await host.apply(ctx));
|
|
1057
|
+
} catch (cause) {
|
|
1058
|
+
const msg = cause instanceof Error ? cause.message : String(cause);
|
|
1059
|
+
console.error(
|
|
1060
|
+
`[monkey-mini-app] apps host failed to start on ${hostConfig.hostPort}: ${msg}
|
|
1061
|
+
Free the port, or edit ${hostConfig.runtimeRoot}/host.json (hostPort) and restart.`
|
|
1062
|
+
);
|
|
1063
|
+
await host.stop().catch(() => {
|
|
1064
|
+
});
|
|
1065
|
+
return () => {
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
console.log(`[monkey-mini-app] apps host http://127.0.0.1:${port}`);
|
|
1069
|
+
console.log(`[monkey-mini-app] loaded \xB7 runtimeRoot=${hostConfig.runtimeRoot} \xB7 skill=${getSkillDir()}`);
|
|
1070
|
+
return () => {
|
|
1071
|
+
void host.stop();
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
export {
|
|
1075
|
+
DSH_LLM_FALLBACK,
|
|
1076
|
+
DshCapabilities,
|
|
1077
|
+
DshLifecycle,
|
|
1078
|
+
DshThemeResource,
|
|
1079
|
+
apply,
|
|
1080
|
+
coerceSchemaJson,
|
|
1081
|
+
collectLlmStream,
|
|
1082
|
+
getSkeletonTemplateFiles,
|
|
1083
|
+
getSkillDir,
|
|
1084
|
+
getSkillMarkdown,
|
|
1085
|
+
getTemplateFiles,
|
|
1086
|
+
inject,
|
|
1087
|
+
loadPluginHostConfig,
|
|
1088
|
+
name,
|
|
1089
|
+
packageName,
|
|
1090
|
+
resolveLlmRoute,
|
|
1091
|
+
resolveRuntimeRoot,
|
|
1092
|
+
runDshAgentOneShot,
|
|
1093
|
+
withJsonInstruction
|
|
1094
|
+
};
|