@sparkelf/dsh-task-bus 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js ADDED
@@ -0,0 +1,485 @@
1
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { basename, dirname, isAbsolute, join } from "node:path";
3
+ import { credentialRef } from "@deepseek-ai/dsh-credentials";
4
+ import { createUserMessage } from "@deepseek-ai/dsh-llm";
5
+ import { defineTool } from "@deepseek-ai/dsh-tools";
6
+ //#region src/index.ts
7
+ const name = "task-bus";
8
+ const inject = [
9
+ "webServer",
10
+ "tools",
11
+ "sessionController",
12
+ "credentials",
13
+ "attachments"
14
+ ];
15
+ const TASK_BUS_ROUTE = "/dsh-task-bus/api";
16
+ const TASK_CENTER_PATH = "/api/task-center";
17
+ const DATAOPS_CREDENTIAL_REF = "DATAOPS_ACCESS_TOKEN";
18
+ const resolveBase = () => {
19
+ const base = process.env.DATAOPS_AI_WORKSPACE_BACKEND_BASE_URL?.trim().replace(/\/+$/u, "");
20
+ if (!base) throw new Error("DataOps Task Center is not configured in this workspace");
21
+ return base;
22
+ };
23
+ /** Make approval links usable inside the DataOps iframe, where `/tasks` is not this origin. */
24
+ const absoluteApprovalLinks = (payload) => {
25
+ const origin = process.env.DATAOPS_PUBLIC_APP_ORIGIN?.trim().replace(/\/+$/u, "");
26
+ if (!origin || payload === null || typeof payload !== "object") return payload;
27
+ const fix = (value) => {
28
+ if (value === null || typeof value !== "object") return;
29
+ const item = value;
30
+ if (item.source !== void 0 && typeof item.source.deepLink === "string" && item.source.deepLink.startsWith("/")) item.source.deepLink = origin + item.source.deepLink;
31
+ };
32
+ const record = payload;
33
+ fix(record.item);
34
+ for (const item of record.items ?? []) fix(item);
35
+ return payload;
36
+ };
37
+ const createTaskCenterClient = (host) => {
38
+ const base = resolveBase();
39
+ return async (path, method = "GET", body) => {
40
+ const token = (await host.credentials.resolve(credentialRef(DATAOPS_CREDENTIAL_REF)))?.value?.trim();
41
+ if (!token) throw new Error("DataOps Task Center credential is not configured");
42
+ const response = await fetch(base + TASK_CENTER_PATH + path, {
43
+ method,
44
+ headers: {
45
+ "content-type": "application/json",
46
+ authorization: `Bearer ${token}`
47
+ },
48
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
49
+ });
50
+ const text = await response.text();
51
+ const payload = text ? JSON.parse(text) : null;
52
+ if (!response.ok) {
53
+ const message = payload !== null && typeof payload === "object" && "message" in payload ? String(payload.message) : `Task Center request failed (${response.status})`;
54
+ throw new Error(message);
55
+ }
56
+ return payload;
57
+ };
58
+ };
59
+ const readJsonBody = async (request) => {
60
+ if (request.on === void 0) return void 0;
61
+ let body = "";
62
+ for await (const chunk of request) body += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
63
+ return body.trim() ? JSON.parse(body) : void 0;
64
+ };
65
+ const writeJson = (response, status, body) => {
66
+ response.writeHead(status, { "content-type": "application/json; charset=utf-8" });
67
+ response.end(JSON.stringify(body));
68
+ };
69
+ const taskListPath = (search) => {
70
+ const params = new URLSearchParams(search);
71
+ if (!params.has("status")) params.set("status", "all");
72
+ return "/items?" + params.toString();
73
+ };
74
+ const genericListPath = (search) => {
75
+ const params = new URLSearchParams(search);
76
+ if (!params.has("scope")) params.set("scope", "mine");
77
+ if (!params.has("pageSize")) params.set("pageSize", "20");
78
+ return "/generic?" + params.toString();
79
+ };
80
+ const open = {
81
+ type: "object",
82
+ additionalProperties: true
83
+ };
84
+ /** Where this workspace stores the connector's own enabled state. */
85
+ const CONNECTOR_STATE_PATH = join(process.env.DSH_HOME?.trim() || "/workspace/.dataops/dsh", "task-bus", "connector-state.json");
86
+ const DEFAULT_CONNECTOR_STATE = {
87
+ enabled: false,
88
+ capabilities: { "approval.push": true }
89
+ };
90
+ /** Read the workspace connector state; a missing/corrupt file is a safe disabled state. */
91
+ async function readConnectorState(path = CONNECTOR_STATE_PATH) {
92
+ try {
93
+ const text = await readFile(path, "utf8");
94
+ const value = JSON.parse(text);
95
+ return {
96
+ enabled: value.enabled === true,
97
+ capabilities: {
98
+ ...DEFAULT_CONNECTOR_STATE.capabilities,
99
+ ...typeof value.capabilities === "object" && value.capabilities !== null ? value.capabilities : {}
100
+ }
101
+ };
102
+ } catch {
103
+ return {
104
+ ...DEFAULT_CONNECTOR_STATE,
105
+ capabilities: { ...DEFAULT_CONNECTOR_STATE.capabilities }
106
+ };
107
+ }
108
+ }
109
+ /** Persist the connector state atomically; the host and UI read the same file. */
110
+ async function writeConnectorState(next, path = CONNECTOR_STATE_PATH) {
111
+ await mkdir(dirname(path), { recursive: true });
112
+ const temporary = path + ".tmp-" + process.pid.toString();
113
+ await writeFile(temporary, JSON.stringify(next, null, 2), "utf8");
114
+ await rename(temporary, path);
115
+ }
116
+ const renderJson = (_args, value) => [{
117
+ type: "text",
118
+ text: JSON.stringify(value, null, 2)
119
+ }];
120
+ const tool = (name, description, parameters, execute) => defineTool({
121
+ name,
122
+ description,
123
+ parameters,
124
+ output: {
125
+ schema: open,
126
+ render: renderJson
127
+ },
128
+ execute: (async (args) => execute(args))
129
+ });
130
+ const apply = (ctx) => {
131
+ const host = ctx;
132
+ const client = createTaskCenterClient(host);
133
+ ctx.effect(() => host.webServer.register({
134
+ kind: "prefix",
135
+ path: TASK_BUS_ROUTE,
136
+ handler: async (rawRequest, rawResponse) => {
137
+ const request = rawRequest;
138
+ const response = rawResponse;
139
+ const url = new URL(request.url ?? "/", "http://dsh.internal");
140
+ const route = url.pathname.slice(17).replace(/^\/+/u, "");
141
+ try {
142
+ if (route === "connector") {
143
+ if (request.method === "GET") {
144
+ writeJson(response, 200, await readConnectorState());
145
+ syncTools().catch(() => {
146
+ disposeTools();
147
+ });
148
+ return;
149
+ }
150
+ if (request.method === "POST") {
151
+ const body = await readJsonBody(request);
152
+ if (typeof body?.enabled !== "boolean") throw new Error("connector state requires enabled:boolean");
153
+ const current = await readConnectorState();
154
+ const capabilities = typeof body.capabilities === "object" && body.capabilities !== null ? {
155
+ ...current.capabilities,
156
+ ...body.capabilities
157
+ } : current.capabilities;
158
+ await writeConnectorState({
159
+ enabled: body.enabled,
160
+ capabilities
161
+ });
162
+ await syncTools();
163
+ writeJson(response, 200, await readConnectorState());
164
+ return;
165
+ }
166
+ writeJson(response, 405, { error: "connector state supports GET and POST" });
167
+ return;
168
+ }
169
+ if (request.method === "GET" && route === "health") {
170
+ resolveBase();
171
+ writeJson(response, 200, {
172
+ ok: true,
173
+ source: "dataops_approval"
174
+ });
175
+ syncTools();
176
+ return;
177
+ }
178
+ if (request.method === "GET" && route === "overview") {
179
+ writeJson(response, 200, absoluteApprovalLinks(await client("/overview")));
180
+ syncTools();
181
+ return;
182
+ }
183
+ if (request.method === "GET" && route === "attachment-materialize") {
184
+ const attachmentId = url.searchParams.get("attachmentId") ?? "";
185
+ const name = url.searchParams.get("name") ?? "attachment";
186
+ const bytes = Number(url.searchParams.get("bytes") ?? "0");
187
+ if (!attachmentId.startsWith("sha256:") || !Number.isSafeInteger(bytes) || bytes <= 0) throw new Error("attachment-materialize requires a sha256 attachmentId and byte count");
188
+ const attachments = ctx.attachments;
189
+ if (attachments === void 0) throw new Error("attachment store is unavailable");
190
+ const chunks = [];
191
+ for await (const chunk of attachments.readFileStream({
192
+ attachmentId,
193
+ name,
194
+ bytes
195
+ })) chunks.push(chunk);
196
+ const safeName = basename(name).replace(/[^\p{L}\p{N}._-]+/gu, "_") || "attachment";
197
+ const requestedCwd = url.searchParams.get("cwd")?.trim() ?? "";
198
+ const root = requestedCwd !== "" && isAbsolute(requestedCwd) ? requestedCwd : process.cwd();
199
+ const directory = join(root, ".dsh-attachment-preview");
200
+ await mkdir(directory, { recursive: true });
201
+ const relativePath = ".dsh-attachment-preview/" + attachmentId.slice(7, 19) + "-" + safeName;
202
+ await writeFile(join(root, relativePath), Buffer.concat(chunks));
203
+ writeJson(response, 200, {
204
+ path: relativePath,
205
+ absolutePath: join(root, relativePath),
206
+ name: safeName
207
+ });
208
+ return;
209
+ }
210
+ if (request.method === "GET" && route === "count") {
211
+ writeJson(response, 200, await client("/count"));
212
+ return;
213
+ }
214
+ if (request.method === "GET" && route === "attachment") {
215
+ const taskId = url.searchParams.get("taskId")?.trim() ?? "";
216
+ const attachmentId = url.searchParams.get("attachmentId")?.trim() ?? "";
217
+ if (!/^\d+$/u.test(taskId) || !/^[0-9a-fA-F-]{36}$/u.test(attachmentId)) throw new Error("attachment requires numeric taskId and UUID attachmentId");
218
+ const token = (await host.credentials.resolve(credentialRef(DATAOPS_CREDENTIAL_REF)))?.value?.trim();
219
+ if (!token) throw new Error("DataOps Task Center credential is not configured");
220
+ const upstream = await fetch(resolveBase() + "/api/tasks/" + encodeURIComponent(taskId) + "/attachments/" + encodeURIComponent(attachmentId) + "/download", { headers: { authorization: `Bearer ${token}` } });
221
+ if (!upstream.ok) {
222
+ const text = await upstream.text();
223
+ throw new Error(text || `Attachment download failed (${upstream.status})`);
224
+ }
225
+ const buffer = Buffer.from(await upstream.arrayBuffer());
226
+ response.writeHead(upstream.status, {
227
+ "content-type": upstream.headers.get("content-type") ?? "application/octet-stream",
228
+ "content-disposition": upstream.headers.get("content-disposition") ?? "attachment",
229
+ "content-length": String(buffer.length),
230
+ "cache-control": "private, no-store"
231
+ });
232
+ response.end(buffer);
233
+ return;
234
+ }
235
+ if (request.method === "GET" && route === "items") {
236
+ writeJson(response, 200, absoluteApprovalLinks(await client(taskListPath(url.search))));
237
+ return;
238
+ }
239
+ if (request.method === "GET" && route === "generic") {
240
+ writeJson(response, 200, await client(genericListPath(url.search)));
241
+ return;
242
+ }
243
+ if (request.method === "POST" && route === "ai-accept") {
244
+ const body = await readJsonBody(request);
245
+ const id = typeof body?.id === "string" ? body.id : "";
246
+ if (!id) throw new Error("ai-accept requires a task id");
247
+ const task = id.startsWith("approval:") ? await client(`/items/${encodeURIComponent(id)}`) : id.startsWith("task:") ? await client(`/generic/${encodeURIComponent(id.slice(5))}`) : null;
248
+ if (task === null) throw new Error("ai-accept requires an approval:<id> or task:<uuid> id");
249
+ const card = task.item;
250
+ const title = card?.title ?? id;
251
+ const sessionId = `task-bus-${crypto.randomUUID()}`;
252
+ await host.sessionController.create({
253
+ sessionId,
254
+ ...process.env.DATAOPS_AI_WORKSPACE_MOUNT_PATH === void 0 ? {} : { cwd: process.env.DATAOPS_AI_WORKSPACE_MOUNT_PATH }
255
+ });
256
+ const resolved = await host.sessionController.resolveAgent(sessionId);
257
+ const agent = "agent" in resolved ? resolved.agent : void 0;
258
+ if (agent === void 0) throw new Error("无法为任务绑定 AI 会话");
259
+ const prompt = String(body?.prompt ?? "") || `这是 DataOps 任务「${title}」(${id}),来源:${card?.source?.label ?? card?.sourceType ?? "Task Center"}。请先阅读任务上下文,整理材料并给出办理建议;不要替审批人通过、驳回或提交审批。`;
260
+ agent.followup(createUserMessage({
261
+ source: {
262
+ kind: "task-bus",
263
+ taskId: id
264
+ },
265
+ content: [{
266
+ type: "text",
267
+ text: prompt
268
+ }]
269
+ }));
270
+ writeJson(response, 201, {
271
+ sessionId,
272
+ taskId: id
273
+ });
274
+ return;
275
+ }
276
+ const transition = /^generic\/([^/]+)\/transition$/u.exec(route);
277
+ if (request.method === "POST" && transition !== null) {
278
+ writeJson(response, 200, await client(`/generic/${encodeURIComponent(transition[1])}/transition`, "POST", await readJsonBody(request)));
279
+ return;
280
+ }
281
+ if (request.method === "POST" && route === "generic") {
282
+ writeJson(response, 201, await client("/generic", "POST", await readJsonBody(request)));
283
+ return;
284
+ }
285
+ const detail = /^generic\/([^/]+)$/u.exec(route);
286
+ if (request.method === "GET" && detail !== null) {
287
+ writeJson(response, 200, await client(`/generic/${encodeURIComponent(detail[1])}`));
288
+ return;
289
+ }
290
+ const approval = /^items\/([^/]+)$/u.exec(route);
291
+ if (request.method === "GET" && approval !== null) {
292
+ writeJson(response, 200, absoluteApprovalLinks(await client(`/items/${encodeURIComponent(decodeURIComponent(approval[1]))}`)));
293
+ return;
294
+ }
295
+ writeJson(response, 404, { error: "Unknown Task Bus route" });
296
+ } catch (error) {
297
+ writeJson(response, 502, { error: error instanceof Error ? error.message : String(error) });
298
+ }
299
+ }
300
+ }), "task-bus: browser API");
301
+ /**
302
+ * Tool definitions are static; only their registration is permission-dependent.
303
+ *
304
+ * This is the plugin-side disclosure boundary: the model never sees a tool the
305
+ * current DataOps principal cannot use. Authorization itself remains at the
306
+ * DataOps Task Center API; this layer only adjusts what the connector contributes.
307
+ */
308
+ const toolDefinitions = [
309
+ {
310
+ permission: "workflow:task:read",
311
+ definition: tool("task_center_list", "List DataOps Task Center cards visible to the current workspace user, including approval tasks and generic Task Bus tasks. Approval cards are read-only projections of DataOps tasks.", {
312
+ status: {
313
+ type: "string",
314
+ enum: [
315
+ "all",
316
+ "pending",
317
+ "done"
318
+ ]
319
+ },
320
+ scope: {
321
+ type: "string",
322
+ enum: [
323
+ "mine",
324
+ "created",
325
+ "all"
326
+ ]
327
+ },
328
+ limit: { type: "number" }
329
+ }, async (args) => {
330
+ const status = typeof args.status === "string" ? args.status : "all";
331
+ const limit = typeof args.limit === "number" ? Math.max(1, Math.min(50, args.limit)) : 20;
332
+ const result = absoluteApprovalLinks(await client("/overview"));
333
+ const items = (result.items ?? []).filter((item) => {
334
+ if (status === "pending") return item.status !== "completed" && item.status !== "cancelled";
335
+ if (status === "done") return item.status === "completed" || item.status === "cancelled";
336
+ return true;
337
+ }).slice(0, limit);
338
+ return {
339
+ ...result,
340
+ items,
341
+ total: items.length
342
+ };
343
+ })
344
+ },
345
+ {
346
+ permission: "workflow:task:read",
347
+ definition: tool("task_center_get", "Read one Task Center card by its stable id. Use approval:<id> for a DataOps approval task or task:<uuid> for a generic Task Bus task.", { id: {
348
+ type: "string",
349
+ required: true
350
+ } }, async (args) => {
351
+ const id = String(args.id ?? "");
352
+ if (id.startsWith("approval:")) return client(`/items/${encodeURIComponent(id)}`);
353
+ if (id.startsWith("task:")) return client(`/generic/${encodeURIComponent(id.slice(5))}`);
354
+ throw new Error("Task Center id must start with approval: or task:");
355
+ })
356
+ },
357
+ {
358
+ permission: "workflow:task:create",
359
+ definition: tool("task_center_create", [
360
+ "Create a generic Task Center card ONLY when the user explicitly asks you to create a task/card in this turn.",
361
+ "Never call this tool proactively, speculatively, as a side effect of another request, or merely because work could be tracked.",
362
+ "If the user did not explicitly ask for a new card, do not call it; ask for confirmation instead.",
363
+ "This never creates or mutates a DataOps approval task."
364
+ ].join(" "), {
365
+ title: {
366
+ type: "string",
367
+ required: true
368
+ },
369
+ description: { type: "string" },
370
+ source_type: { type: "string" },
371
+ source_id: { type: "string" },
372
+ assignee_user_id: { type: "string" },
373
+ priority: { type: "string" },
374
+ user_requested: {
375
+ type: "boolean",
376
+ required: true,
377
+ description: "Must be true only when the user explicitly requested creation of this card in the current turn."
378
+ }
379
+ }, async (args) => {
380
+ if (args.user_requested !== true) throw new Error("task_center_create requires an explicit user request; do not create Task Center cards proactively");
381
+ return client("/generic", "POST", {
382
+ title: String(args.title ?? ""),
383
+ description: typeof args.description === "string" ? args.description : void 0,
384
+ sourceType: typeof args.source_type === "string" ? args.source_type : "agent",
385
+ sourceId: typeof args.source_id === "string" ? args.source_id : void 0,
386
+ assigneeUserId: typeof args.assignee_user_id === "string" ? args.assignee_user_id : void 0,
387
+ priority: typeof args.priority === "string" ? args.priority : void 0
388
+ });
389
+ })
390
+ },
391
+ {
392
+ permission: "workflow:task:execute",
393
+ definition: tool("task_center_transition", "Advance a generic Task Bus task. Approval tasks must be handled through the existing DataOps approval UI and APIs; this tool refuses approval ids.", {
394
+ id: {
395
+ type: "string",
396
+ required: true
397
+ },
398
+ action: {
399
+ type: "string",
400
+ required: true,
401
+ enum: [
402
+ "accept",
403
+ "start",
404
+ "submit",
405
+ "complete",
406
+ "cancel",
407
+ "block",
408
+ "unblock"
409
+ ]
410
+ },
411
+ comment: { type: "string" }
412
+ }, async (args) => {
413
+ const id = String(args.id ?? "");
414
+ if (!id.startsWith("task:")) throw new Error("Only generic task:<uuid> cards can be transitioned here");
415
+ return client(`/generic/${encodeURIComponent(id.slice(5))}/transition`, "POST", {
416
+ action: String(args.action ?? ""),
417
+ comment: typeof args.comment === "string" ? args.comment : void 0
418
+ });
419
+ })
420
+ }
421
+ ];
422
+ const toolDisposers = /* @__PURE__ */ new Map();
423
+ let syncGeneration = 0;
424
+ const disposeTools = () => {
425
+ for (const dispose of toolDisposers.values()) dispose();
426
+ toolDisposers.clear();
427
+ };
428
+ /** Read the current principal's DataOps permission keys without caching them per tool call. */
429
+ const loadPermissionKeys = async () => {
430
+ try {
431
+ const token = (await host.credentials.resolve(credentialRef(DATAOPS_CREDENTIAL_REF)))?.value?.trim();
432
+ if (!token) return null;
433
+ const response = await fetch(resolveBase() + "/api/ai/review/context", { headers: {
434
+ authorization: `Bearer ${token}`,
435
+ accept: "application/json"
436
+ } });
437
+ if (!response.ok) return null;
438
+ const body = await response.json();
439
+ const values = Array.isArray(body.permissions) ? body.permissions : Array.isArray(body.permissionKeys) ? body.permissionKeys : null;
440
+ return values === null ? null : new Set(values.filter((item) => typeof item === "string"));
441
+ } catch {
442
+ return null;
443
+ }
444
+ };
445
+ /**
446
+ * Reconcile connector state, principal permissions and the registered tool set.
447
+ *
448
+ * Called at plugin start, after every browser API request, and after a connector
449
+ * enable/disable change. The tool execution path never repeats this work.
450
+ */
451
+ const syncTools = async () => {
452
+ const generation = ++syncGeneration;
453
+ const state = await readConnectorState();
454
+ if (!state.enabled || state.capabilities["approval.push"] === false) {
455
+ disposeTools();
456
+ return;
457
+ }
458
+ const permissions = await loadPermissionKeys();
459
+ if (permissions === null) {
460
+ disposeTools();
461
+ return;
462
+ }
463
+ if (generation !== syncGeneration) return;
464
+ for (const item of toolDefinitions) {
465
+ const existing = toolDisposers.get(item.definition.name);
466
+ if (permissions.has(item.permission)) {
467
+ if (existing === void 0) toolDisposers.set(item.definition.name, host.tools.register(item.definition));
468
+ } else if (existing !== void 0) {
469
+ existing();
470
+ toolDisposers.delete(item.definition.name);
471
+ }
472
+ }
473
+ };
474
+ ctx.effect(() => {
475
+ syncTools().catch(() => {
476
+ disposeTools();
477
+ });
478
+ return () => {
479
+ syncGeneration += 1;
480
+ disposeTools();
481
+ };
482
+ }, "task-bus: permission-aware tool disclosure");
483
+ };
484
+ //#endregion
485
+ export { CONNECTOR_STATE_PATH, TASK_BUS_ROUTE, apply, inject, name, readConnectorState, writeConnectorState };
package/package.json CHANGED
@@ -1,6 +1,89 @@
1
1
  {
2
2
  "name": "@sparkelf/dsh-task-bus",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "DataOps Task Center and Task Bus for the official DSH Web shell.",
5
+ "type": "module",
6
+ "main": "./lib/index.js",
7
+ "types": "./lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./package.json": "./package.json",
14
+ "./client": {
15
+ "default": "./lib/client.js"
16
+ }
17
+ },
18
+ "dsh": {
19
+ "bundle": {
20
+ "patch": "./cordis.patch.yml"
21
+ },
22
+ "client": {
23
+ "inject": [
24
+ "@deepseek-ai/dsh-client-ui-layout",
25
+ "@deepseek-ai/dsh-client-ui-slots",
26
+ "@deepseek-ai/dsh-client-locale",
27
+ "@deepseek-ai/dsh-client-ui-primitives"
28
+ ],
29
+ "platform": "web"
30
+ }
31
+ },
32
+ "files": [
33
+ "lib/index.js",
34
+ "lib/index.d.ts",
35
+ "lib/client.js",
36
+ "lib/client.js.map",
37
+ "cordis.patch.yml",
38
+ "README.md"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsdown",
42
+ "typecheck": "tsc --noEmit -p tsconfig.json"
43
+ },
44
+ "engines": {
45
+ "node": "^22.19.0 || >=24.0.0"
46
+ },
47
+ "license": "MIT",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/SparkElf/dsh-plugins-plus.git",
51
+ "directory": "packages/task-bus"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "peerDependencies": {
57
+ "@deepseek-ai/cordis": "^4.0.4",
58
+ "@deepseek-ai/dsh-client-locale": ">=0.1.7-rc.2",
59
+ "@deepseek-ai/dsh-client-ui-layout": ">=0.2.1-alpha.2",
60
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.2",
61
+ "@deepseek-ai/dsh-host-webserver": ">=0.1.7-rc.2",
62
+ "@deepseek-ai/dsh-tools": ">=0.1.7-rc.2",
63
+ "react": ">=18.2.0 <20",
64
+ "@deepseek-ai/dsh-llm": ">=0.1.7-rc.2",
65
+ "@deepseek-ai/dsh-client-ui-sidebar": ">=0.2.1-alpha.2",
66
+ "@deepseek-ai/dsh-credentials": ">=0.1.7-rc.2",
67
+ "@deepseek-ai/dsh-client-ui-conversation": ">=0.1.7-rc.2",
68
+ "@deepseek-ai/dsh-client-ui-primitives": ">=0.2.0-rc.2"
69
+ },
70
+ "devDependencies": {
71
+ "@deepseek-ai/cordis": "4.0.4",
72
+ "@deepseek-ai/dsh-client-locale": "0.1.7-rc.2",
73
+ "@deepseek-ai/dsh-client-ui-layout": "0.2.1-alpha.2",
74
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2",
75
+ "@deepseek-ai/dsh-host-webserver": "0.1.7-rc.2",
76
+ "@deepseek-ai/dsh-tools": "0.1.7-rc.2",
77
+ "@types/node": "^22.0.0",
78
+ "@types/react": "~18.3.1",
79
+ "@types/react-dom": "^18.3.0",
80
+ "react": "^18.2.0",
81
+ "react-dom": "^18.2.0",
82
+ "tsdown": "^0.22.2",
83
+ "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
84
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.2.1-alpha.2",
85
+ "@deepseek-ai/dsh-credentials": "0.1.7-rc.2",
86
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.2",
87
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2"
88
+ }
89
+ }