dsh-plugin-library 0.1.1 → 0.1.3
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/dist/client.js +17 -1
- package/dist/dsh-package.mjs +116 -0
- package/dist/host.js +141 -13
- package/package.json +2 -1
package/dist/client.js
CHANGED
|
@@ -1092,7 +1092,9 @@ window.__ModuleLoader__.load({
|
|
|
1092
1092
|
/** The applications registered for this file, OS preference order first. */
|
|
1093
1093
|
fileApplications: (assetId) => hostCall(FEATURE, "file-applications", { assetId }),
|
|
1094
1094
|
/** Re-read the (possibly externally edited) original into a new revision. */
|
|
1095
|
-
importExternal: (assetId) => hostCall(FEATURE, "import-external", { assetId })
|
|
1095
|
+
importExternal: (assetId) => hostCall(FEATURE, "import-external", { assetId }),
|
|
1096
|
+
/** The original converted to PDF by the installation's LibreOffice engine (pptx / docx / xlsx …). */
|
|
1097
|
+
previewPdf: (assetId) => hostCall(FEATURE, "preview-pdf", { assetId })
|
|
1096
1098
|
};
|
|
1097
1099
|
var KIND_LABELS = {
|
|
1098
1100
|
markdown: "Markdown",
|
|
@@ -1502,6 +1504,7 @@ window.__ModuleLoader__.load({
|
|
|
1502
1504
|
avif: "image/avif",
|
|
1503
1505
|
ico: "image/x-icon"
|
|
1504
1506
|
};
|
|
1507
|
+
var OFFICE_EXTENSIONS = /* @__PURE__ */ new Set(["pptx", "ppt", "docx", "doc", "xlsx", "xls"]);
|
|
1505
1508
|
function previewKindOf(name2, kind, mediaType) {
|
|
1506
1509
|
const extension = extensionOf(name2);
|
|
1507
1510
|
const type = mediaType.toLowerCase();
|
|
@@ -1509,6 +1512,7 @@ window.__ModuleLoader__.load({
|
|
|
1509
1512
|
if (kind === "html" || extension === "html" || extension === "htm" || type === "text/html") return "html";
|
|
1510
1513
|
if (kind === "csv" || extension === "csv" || type === "text/csv") return "csv";
|
|
1511
1514
|
if (type.startsWith("image/") || IMAGE_TYPES[extension] !== void 0) return "image";
|
|
1515
|
+
if (OFFICE_EXTENSIONS.has(extension)) return "office-pdf";
|
|
1512
1516
|
return void 0;
|
|
1513
1517
|
}
|
|
1514
1518
|
function isolatedHtml(source) {
|
|
@@ -1713,6 +1717,17 @@ window.__ModuleLoader__.load({
|
|
|
1713
1717
|
setPreview({ kind: "csv", bytes: decodeBase64(original.base64) });
|
|
1714
1718
|
return;
|
|
1715
1719
|
}
|
|
1720
|
+
if (kind === "office-pdf") {
|
|
1721
|
+
const converted = await management.previewPdf(asset.id);
|
|
1722
|
+
if (!live()) return;
|
|
1723
|
+
const url2 = URL.createObjectURL(originalBlob(
|
|
1724
|
+
{ name: `${asset.id}-preview.pdf`, mediaType: "application/pdf", byteLength: 0, base64: converted.base64 },
|
|
1725
|
+
"application/pdf"
|
|
1726
|
+
));
|
|
1727
|
+
previewUrlRef.current = url2;
|
|
1728
|
+
setPreview({ kind: "office-pdf", url: url2 });
|
|
1729
|
+
return;
|
|
1730
|
+
}
|
|
1716
1731
|
const name2 = original.name.length > 0 ? original.name : node.name;
|
|
1717
1732
|
const extension = extensionOf(name2);
|
|
1718
1733
|
const type = kind === "pdf" ? "application/pdf" : original.mediaType.startsWith("image/") ? original.mediaType : IMAGE_TYPES[extension] ?? original.mediaType;
|
|
@@ -2102,6 +2117,7 @@ window.__ModuleLoader__.load({
|
|
|
2102
2117
|
}
|
|
2103
2118
|
if (preview !== void 0) {
|
|
2104
2119
|
if (preview.kind === "csv") return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CsvGrid, { bytes: preview.bytes });
|
|
2120
|
+
if (preview.kind === "office-pdf") return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("iframe", { className: "wd-library-pdf", title: node.name, src: preview.url });
|
|
2105
2121
|
return preview.kind === "pdf" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("iframe", { className: "wd-library-pdf", title: node.name, src: preview.url }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: preview.url, alt: node.name, style: { maxWidth: "100%", borderRadius: 10, border: "1px solid var(--dsw-alias-border-l2)" } });
|
|
2106
2122
|
}
|
|
2107
2123
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "wd-library-state", children: "\u539F\u4EF6\u8BFB\u53D6\u5931\u8D25\uFF0C\u4E0A\u65B9\u63D0\u793A\u8BF4\u660E\u4E86\u539F\u56E0\uFF1B\u53EF\u4EE5\u7528\u4E0A\u65B9\u7684\u300C\u4E0B\u8F7D\u300D\u628A\u5B83\u53D6\u56DE\u3002" });
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reaching an official Harness package from a third-party Host half.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: `install_bundle` links a workspace directory into the
|
|
5
|
+
* profile, and Node resolves a plugin's bare-specifier imports from that
|
|
6
|
+
* directory's **realpath** — the user's source tree, far outside the dsh
|
|
7
|
+
* installation. So `import '@deepseek-ai/dsh-mcp-client'` fails with
|
|
8
|
+
* ERR_MODULE_NOT_FOUND even though the package is right there in the running
|
|
9
|
+
* app. Verified on 0.2.0-rc.2; see docs/PORTING-NOTES.md §9.
|
|
10
|
+
*
|
|
11
|
+
* The anchor order mirrors app-boot's own `resolveBundleDir`: the installation
|
|
12
|
+
* first, the profile second, so an in-box package is never shadowed by a
|
|
13
|
+
* profile-local copy. Resolution through the same anchor also means the import
|
|
14
|
+
* lands on the same absolute file the official rows use, so Node's module cache
|
|
15
|
+
* hands back the same instance rather than a second one.
|
|
16
|
+
*
|
|
17
|
+
* @module shared/dsh-package
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { createRequire } from 'node:module'
|
|
21
|
+
import { isAbsolute, join } from 'node:path'
|
|
22
|
+
import { pathToFileURL } from 'node:url'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Candidate resolution anchors, most authoritative first.
|
|
26
|
+
* @param {{ argv?: readonly string[], cwd?: string }} [options] - overrides for tests.
|
|
27
|
+
* @returns {string[]} absolute anchors suitable for `createRequire`.
|
|
28
|
+
*/
|
|
29
|
+
export function dshResolutionAnchors({ argv = process.argv, cwd = process.cwd() } = {}) {
|
|
30
|
+
const anchors = []
|
|
31
|
+
// The script the host process was started with lives inside the installation:
|
|
32
|
+
// <dsh home>/node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js
|
|
33
|
+
if (typeof argv[1] === 'string' && isAbsolute(argv[1])) anchors.push(argv[1])
|
|
34
|
+
// The desktop host is additionally handed the dsh home as its first argument.
|
|
35
|
+
if (typeof argv[2] === 'string' && isAbsolute(argv[2])) anchors.push(join(argv[2], 'package.json'))
|
|
36
|
+
// Profile-local packages last, so they can never shadow the installation.
|
|
37
|
+
anchors.push(join(cwd, 'package.json'))
|
|
38
|
+
return [...new Set(anchors)]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Test seam: specifiers answered from memory instead of the installation.
|
|
43
|
+
*
|
|
44
|
+
* Offline verification runs a Host half's `apply()` outside DSH, where the
|
|
45
|
+
* installation does not exist. A feature that needs `storageDomain` or the MCP
|
|
46
|
+
* client would otherwise be unverifiable until it is installed — exactly the
|
|
47
|
+
* situation shared/host/route.mjs and the rest of the harness exist to avoid.
|
|
48
|
+
*
|
|
49
|
+
* This only substitutes *resolution*; the real anchor walk stays the default and
|
|
50
|
+
* is covered directly by tests/dsh-package.test.mjs.
|
|
51
|
+
*
|
|
52
|
+
* The map lives on `globalThis` under a symbol: the published bundle ships this
|
|
53
|
+
* file as its own module (`dist/dsh-package.mjs`), so a test process holds two
|
|
54
|
+
* instances of it — the repo copy and the bundle copy. Module-level state would
|
|
55
|
+
* split them, and stubs registered through the repo copy would silently stop
|
|
56
|
+
* resolving inside the bundle. One shared registry keeps every instance honest.
|
|
57
|
+
* @type {Map<string, Record<string, unknown>>}
|
|
58
|
+
*/
|
|
59
|
+
const STUBS_KEY = Symbol.for('dsh-skills:dsh-package-stubs')
|
|
60
|
+
const stubs = (globalThis[STUBS_KEY] ??= new Map())
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Answer one specifier from memory for the rest of this process.
|
|
64
|
+
* @param {string} specifier - package name to stub.
|
|
65
|
+
* @param {Record<string, unknown>} module - the module namespace to return.
|
|
66
|
+
* @returns {void}
|
|
67
|
+
*/
|
|
68
|
+
export function setDshPackageStub(specifier, module) {
|
|
69
|
+
stubs.set(specifier, module)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Remove every stub. */
|
|
73
|
+
export function clearDshPackageStubs() {
|
|
74
|
+
stubs.clear()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Resolve an official package to its entry file.
|
|
79
|
+
* @param {string} specifier - the package name to resolve.
|
|
80
|
+
* @param {{ argv?: readonly string[], cwd?: string }} [options] - overrides for tests.
|
|
81
|
+
* @returns {string} absolute path of the resolved entry file.
|
|
82
|
+
* @throws {Error} with the anchors and codes tried when nothing resolves.
|
|
83
|
+
*/
|
|
84
|
+
export function resolveDshPackage(specifier, options = {}) {
|
|
85
|
+
if (stubs.has(specifier)) return `stub:${specifier}`
|
|
86
|
+
const tried = []
|
|
87
|
+
for (const anchor of dshResolutionAnchors(options)) {
|
|
88
|
+
try {
|
|
89
|
+
return createRequire(anchor).resolve(specifier)
|
|
90
|
+
} catch (error) {
|
|
91
|
+
tried.push(`${anchor} (${error?.code ?? error?.name ?? 'error'})`)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
throw new Error(
|
|
95
|
+
`dsh-skills: cannot resolve ${JSON.stringify(specifier)} from the dsh installation or the profile.\n`
|
|
96
|
+
+ `Tried: ${tried.join(', ')}\n`
|
|
97
|
+
+ 'A Host half must not import an official package by bare specifier: Node resolves that from\n'
|
|
98
|
+
+ "this plugin's realpath, which is outside the installation. Use importDshPackage() instead.",
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Import an official package from the installation.
|
|
104
|
+
*
|
|
105
|
+
* Resolving to the same absolute file the official rows use means a second
|
|
106
|
+
* module instance is never created, which matters for plugins that coordinate
|
|
107
|
+
* through module-level state (for example mcp-client's `serverName` reservations).
|
|
108
|
+
* @param {string} specifier - the package name to import.
|
|
109
|
+
* @param {{ argv?: readonly string[], cwd?: string }} [options] - overrides for tests.
|
|
110
|
+
* @returns {Promise<Record<string, unknown>>} the imported module namespace.
|
|
111
|
+
*/
|
|
112
|
+
export async function importDshPackage(specifier, options) {
|
|
113
|
+
const stub = stubs.get(specifier)
|
|
114
|
+
if (stub !== undefined) return stub
|
|
115
|
+
return import(pathToFileURL(resolveDshPackage(specifier, options)).href)
|
|
116
|
+
}
|
package/dist/host.js
CHANGED
|
@@ -60,7 +60,8 @@ function dshResolutionAnchors({ argv = process.argv, cwd = process.cwd() } = {})
|
|
|
60
60
|
anchors.push(join(cwd, "package.json"));
|
|
61
61
|
return [...new Set(anchors)];
|
|
62
62
|
}
|
|
63
|
-
var
|
|
63
|
+
var STUBS_KEY = /* @__PURE__ */ Symbol.for("dsh-skills:dsh-package-stubs");
|
|
64
|
+
var stubs = globalThis[STUBS_KEY] ??= /* @__PURE__ */ new Map();
|
|
64
65
|
function resolveDshPackage(specifier, options = {}) {
|
|
65
66
|
if (stubs.has(specifier)) return `stub:${specifier}`;
|
|
66
67
|
const tried = [];
|
|
@@ -264,11 +265,12 @@ function deriveText(kind, bytes) {
|
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
// src/native-open.js
|
|
268
|
+
import { importDshPackage as importDshPackage2 } from "./dsh-package.mjs";
|
|
267
269
|
var cached2;
|
|
268
270
|
async function loadNativeCommand() {
|
|
269
271
|
if (cached2 !== void 0) return cached2;
|
|
270
272
|
try {
|
|
271
|
-
const mod = await
|
|
273
|
+
const mod = await importDshPackage2("@deepseek-ai/dsh-native-command");
|
|
272
274
|
if (typeof mod?.openNativeAssociatedPath === "function") {
|
|
273
275
|
cached2 = mod;
|
|
274
276
|
return cached2;
|
|
@@ -317,6 +319,43 @@ async function fileApplications(absolutePath2) {
|
|
|
317
319
|
return Array.isArray(rows) ? rows : [];
|
|
318
320
|
}
|
|
319
321
|
|
|
322
|
+
// src/office-preview.js
|
|
323
|
+
import { importDshPackage as importDshPackage3 } from "./dsh-package.mjs";
|
|
324
|
+
var cached3;
|
|
325
|
+
async function loadKit() {
|
|
326
|
+
if (cached3 !== void 0) return cached3;
|
|
327
|
+
try {
|
|
328
|
+
const mod = await importDshPackage3("@deepseek-ai/libreoffice-kit");
|
|
329
|
+
if (typeof mod?.createConverter === "function") {
|
|
330
|
+
cached3 = mod;
|
|
331
|
+
return cached3;
|
|
332
|
+
}
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
return void 0;
|
|
336
|
+
}
|
|
337
|
+
var CONVERTIBLE = /* @__PURE__ */ new Set([".pptx", ".ppt", ".docx", ".doc", ".xlsx", ".xls"]);
|
|
338
|
+
function isConvertible(name2) {
|
|
339
|
+
const dot = name2.lastIndexOf(".");
|
|
340
|
+
if (dot < 0) return false;
|
|
341
|
+
return CONVERTIBLE.has(name2.slice(dot).toLowerCase());
|
|
342
|
+
}
|
|
343
|
+
async function convertToPdf(inputPath, outputPath) {
|
|
344
|
+
const kit = await loadKit();
|
|
345
|
+
if (kit === void 0) {
|
|
346
|
+
const error = new Error("\u5F53\u524D DSH \u5B89\u88C5\u6CA1\u6709\u5185\u7F6E LibreOffice\uFF0C\u65E0\u6CD5\u8F6C\u6362\u9884\u89C8\u3002");
|
|
347
|
+
error.code = "library/preview-unavailable";
|
|
348
|
+
throw error;
|
|
349
|
+
}
|
|
350
|
+
const converter = await kit.createConverter();
|
|
351
|
+
try {
|
|
352
|
+
const result = await converter.render({ inputPath, outputPath });
|
|
353
|
+
return { engine: result?.engine ?? "native" };
|
|
354
|
+
} finally {
|
|
355
|
+
await converter[Symbol.asyncDispose]?.();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
320
359
|
// src/manager.js
|
|
321
360
|
var MAX_BYTES = 50 * 1024 * 1024;
|
|
322
361
|
var MAX_TOTAL_BYTES = 5 * 1024 * 1024 * 1024;
|
|
@@ -581,6 +620,38 @@ var LibraryManager = class {
|
|
|
581
620
|
return this.storeBytes({ name: name2, parentId: node.parentId, bytes, source: asset.source ?? "import" });
|
|
582
621
|
});
|
|
583
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* The PDF preview of one revision's original, converted on demand and cached
|
|
625
|
+
* beside the revision's own files.
|
|
626
|
+
*
|
|
627
|
+
* Office originals (pptx / docx / xlsx …) cannot be shown by a browser, but
|
|
628
|
+
* the DSH installation ships a full LibreOffice engine — the same one its own
|
|
629
|
+
* document preview drives. This converts the original to PDF once, stores the
|
|
630
|
+
* result as `<revision-dir>/preview.pdf`, and every later read of the same
|
|
631
|
+
* revision reuses it. 「导入新版本」 mints a new revision id, so the cache
|
|
632
|
+
* invalidates itself.
|
|
633
|
+
* @param {string} assetId - the asset.
|
|
634
|
+
* @param {string} [revisionId] - a pinned revision; defaults to the current one.
|
|
635
|
+
* @returns {Promise<{ base64: string, cached: boolean, mediaType: string }>} the converted PDF.
|
|
636
|
+
*/
|
|
637
|
+
async previewPdf(assetId, revisionId, convert) {
|
|
638
|
+
const { revision } = this.resolveStored(assetId, revisionId);
|
|
639
|
+
if (revision.conversionStatus !== void 0 && revision.conversionStatus === "unavailable" && false) {
|
|
640
|
+
}
|
|
641
|
+
const absolutePath2 = this.safePath(revision.originalRelativePath);
|
|
642
|
+
const pdfPath = this.safePath(join2(dirname(revision.originalRelativePath), "preview.pdf"));
|
|
643
|
+
try {
|
|
644
|
+
const pdf2 = await readFile(pdfPath);
|
|
645
|
+
return { base64: Buffer.from(pdf2).toString("base64"), cached: true, mediaType: "application/pdf" };
|
|
646
|
+
} catch {
|
|
647
|
+
}
|
|
648
|
+
if (!isConvertible(revision.originalRelativePath)) {
|
|
649
|
+
throw new FeatureError2("library/not-convertible", "\u8BE5\u683C\u5F0F\u4E0D\u652F\u6301\u8F6C\u6362\u4E3A PDF \u9884\u89C8\u3002");
|
|
650
|
+
}
|
|
651
|
+
await convert(absolutePath2, pdfPath);
|
|
652
|
+
const pdf = await readFile(pdfPath);
|
|
653
|
+
return { base64: Buffer.from(pdf).toString("base64"), cached: false, mediaType: "application/pdf" };
|
|
654
|
+
}
|
|
584
655
|
// ---- write surface -------------------------------------------------------
|
|
585
656
|
/** Create one folder. */
|
|
586
657
|
createFolder(input) {
|
|
@@ -1155,7 +1226,12 @@ var LibraryManager = class {
|
|
|
1155
1226
|
byteLength: bytes.byteLength,
|
|
1156
1227
|
base64: Buffer.from(bytes).toString("base64")
|
|
1157
1228
|
};
|
|
1158
|
-
}
|
|
1229
|
+
},
|
|
1230
|
+
"preview-pdf": async (payload) => this.previewPdf(
|
|
1231
|
+
requireString(payload.assetId, "assetId"),
|
|
1232
|
+
optionalId(payload.revisionId),
|
|
1233
|
+
(inputPath, outputPath) => convertToPdf(inputPath, outputPath)
|
|
1234
|
+
)
|
|
1159
1235
|
};
|
|
1160
1236
|
}
|
|
1161
1237
|
// ---- internals -----------------------------------------------------------
|
|
@@ -1339,6 +1415,50 @@ var LibraryManager = class {
|
|
|
1339
1415
|
if (asset.status === "removed") throw new FeatureError2("library/not-found", "\u8D44\u6599\u5DF2\u79FB\u9664\u3002");
|
|
1340
1416
|
return asset;
|
|
1341
1417
|
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Resolve whatever identifier a caller supplied into a live asset id.
|
|
1420
|
+
*
|
|
1421
|
+
* The model-facing tools address a document by `asset_id`, but the text a
|
|
1422
|
+
* model actually reads (the search results) leads with the *name*. A model
|
|
1423
|
+
* that passes the name used to get `library/not-found` — "资料不存在或已移除"
|
|
1424
|
+
* — for a document that was perfectly readable, which reads as data loss.
|
|
1425
|
+
* So a name is accepted too, with two deliberate refusals:
|
|
1426
|
+
*
|
|
1427
|
+
* - **Ambiguous** (two live documents share a name): refused with every
|
|
1428
|
+
* candidate's id listed. Silently picking one would read the wrong document.
|
|
1429
|
+
* - **Unknown**: refused with a message that names both possibilities and
|
|
1430
|
+
* points at `library_search`, instead of the bare "not found" that hints
|
|
1431
|
+
* nothing about what was wrong with the identifier.
|
|
1432
|
+
* @param {string} identifier - an asset id, or a document name.
|
|
1433
|
+
* @returns {string} the live asset id.
|
|
1434
|
+
* @throws {FeatureError} `library/invalid-request`, `library/ambiguous` or `library/not-found`.
|
|
1435
|
+
*/
|
|
1436
|
+
resolveAssetId(identifier) {
|
|
1437
|
+
const text = typeof identifier === "string" ? identifier.trim() : "";
|
|
1438
|
+
if (text.length === 0) {
|
|
1439
|
+
throw new FeatureError2("library/invalid-request", "\u9700\u8981 asset_id\uFF1A\u7528 library_search \u641C\u5230\u8D44\u6599\u540E\uFF0C\u628A\u5B83\u8FD4\u56DE\u7684 asset_id \u539F\u6837\u4F20\u8FDB\u6765\u3002");
|
|
1440
|
+
}
|
|
1441
|
+
const state = this.load();
|
|
1442
|
+
const direct = state.assets[text];
|
|
1443
|
+
const directNode = direct === void 0 ? void 0 : state.nodes[text];
|
|
1444
|
+
if (direct !== void 0 && direct.status !== "removed" && directNode !== void 0 && directNode.removedAt === void 0) {
|
|
1445
|
+
return text;
|
|
1446
|
+
}
|
|
1447
|
+
const live = Object.values(state.assets).filter((asset) => asset.status !== "removed").map((asset) => ({ asset, node: state.nodes[asset.id] })).filter((row) => row.node !== void 0 && row.node.removedAt === void 0 && row.node.kind === "asset");
|
|
1448
|
+
const exact = live.filter((row) => row.node.name === text);
|
|
1449
|
+
const matches = exact.length > 0 ? exact : live.filter((row) => row.node.name.toLocaleLowerCase() === text.toLocaleLowerCase());
|
|
1450
|
+
if (matches.length === 1) return matches[0].asset.id;
|
|
1451
|
+
if (matches.length > 1) {
|
|
1452
|
+
throw new FeatureError2(
|
|
1453
|
+
"library/ambiguous",
|
|
1454
|
+
`\u6709 ${matches.length} \u4EFD\u8D44\u6599\u53EB\u300C${text}\u300D\uFF0C\u65E0\u6CD5\u786E\u5B9A\u8BFB\u54EA\u4E00\u4EFD\uFF1B\u8BF7\u628A asset_id \u6362\u6210\u5176\u4E2D\u4E00\u4E2A\uFF1A` + matches.map((row) => `${row.node.name}\uFF08${row.asset.id}\uFF09`).join("\u3001")
|
|
1455
|
+
);
|
|
1456
|
+
}
|
|
1457
|
+
throw new FeatureError2(
|
|
1458
|
+
"library/not-found",
|
|
1459
|
+
`\u6CA1\u627E\u5230\u8D44\u6599\u300C${text}\u300D\uFF1A\u5B83\u65E2\u4E0D\u662F asset_id\uFF0C\u4E5F\u4E0D\u662F\u73B0\u6709\u8D44\u6599\u7684\u540D\u79F0\u3002\u8D44\u6599\u5728\u8D44\u6599\u5E93\u91CC\u3001\u4E0D\u5728\u5DE5\u4F5C\u533A\u6587\u4EF6\u7CFB\u7EDF\u91CC\uFF0C\u5148\u7528 library_search \u641C\u7D22\uFF0C\u518D\u628A\u8FD4\u56DE\u7684 asset_id \u539F\u6837\u4F20\u7ED9\u672C\u5DE5\u5177\u3002`
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1342
1462
|
/** An asset record a read is allowed to resolve: present and enabled. */
|
|
1343
1463
|
requireReadableAsset(state, assetId) {
|
|
1344
1464
|
const asset = this.requireAsset(state, assetId);
|
|
@@ -1391,6 +1511,7 @@ var LibraryManager = class {
|
|
|
1391
1511
|
};
|
|
1392
1512
|
|
|
1393
1513
|
// src/tools.js
|
|
1514
|
+
import { importDshPackage as importDshPackage4 } from "./dsh-package.mjs";
|
|
1394
1515
|
function compileValue(node) {
|
|
1395
1516
|
if (node === null || typeof node !== "object") throw new Error("dsh-plugin-library: schema node must be an object");
|
|
1396
1517
|
if (node.type === "object") {
|
|
@@ -1439,19 +1560,19 @@ function fallbackDefineTool(options) {
|
|
|
1439
1560
|
execute: options.execute
|
|
1440
1561
|
};
|
|
1441
1562
|
}
|
|
1442
|
-
var
|
|
1563
|
+
var cached4;
|
|
1443
1564
|
async function loadDefineTool() {
|
|
1444
|
-
if (
|
|
1565
|
+
if (cached4 !== void 0) return cached4;
|
|
1445
1566
|
try {
|
|
1446
|
-
const mod = await
|
|
1567
|
+
const mod = await importDshPackage4("@deepseek-ai/dsh-tools");
|
|
1447
1568
|
if (typeof mod?.defineTool === "function") {
|
|
1448
|
-
|
|
1449
|
-
return
|
|
1569
|
+
cached4 = mod.defineTool;
|
|
1570
|
+
return cached4;
|
|
1450
1571
|
}
|
|
1451
1572
|
} catch {
|
|
1452
1573
|
}
|
|
1453
|
-
|
|
1454
|
-
return
|
|
1574
|
+
cached4 = fallbackDefineTool;
|
|
1575
|
+
return cached4;
|
|
1455
1576
|
}
|
|
1456
1577
|
function hitForModel(hit) {
|
|
1457
1578
|
return {
|
|
@@ -1509,7 +1630,13 @@ async function registerLibraryTools(ctx, manager) {
|
|
|
1509
1630
|
},
|
|
1510
1631
|
render: (_args, value) => [{
|
|
1511
1632
|
type: "text",
|
|
1633
|
+
// The id is printed here, not just carried in the structured payload:
|
|
1634
|
+
// this rendered text is what the model actually reads, and the read /
|
|
1635
|
+
// edit tools address a document by `asset_id`. Leaving it out (v0.1.x)
|
|
1636
|
+
// made models pass the *name* instead and get "资料不存在或已移除" for a
|
|
1637
|
+
// document that was perfectly readable.
|
|
1512
1638
|
text: value.hits.length > 0 ? value.hits.map((hit) => `${hit.name} (${hit.folder_path} \xB7 ${hit.kind} \xB7 \u4FEE\u8BA2 ${hit.revision_number} \xB7 ${hit.conversion_status})
|
|
1639
|
+
asset_id: ${hit.asset_id}
|
|
1513
1640
|
${hit.excerpt}`).join("\n\n") : "\u8D44\u6599\u5E93\u91CC\u6CA1\u6709\u5339\u914D\u7684\u8D44\u6599\u3002"
|
|
1514
1641
|
}]
|
|
1515
1642
|
},
|
|
@@ -1549,15 +1676,16 @@ ${hit.excerpt}`).join("\n\n") : "\u8D44\u6599\u5E93\u91CC\u6CA1\u6709\u5339\u914
|
|
|
1549
1676
|
if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1 || limit > 2e4) {
|
|
1550
1677
|
throw new Error("library/invalid-page");
|
|
1551
1678
|
}
|
|
1552
|
-
const
|
|
1679
|
+
const assetId = manager.resolveAssetId(args.asset_id);
|
|
1680
|
+
const detail = manager.assetDetail(assetId);
|
|
1553
1681
|
const revisionId = args.revision_id ?? detail.asset.currentRevisionId;
|
|
1554
1682
|
const revision = detail.revisions.find((row) => row.id === revisionId);
|
|
1555
|
-
const complete = await manager.readText(
|
|
1683
|
+
const complete = await manager.readText(assetId, revisionId);
|
|
1556
1684
|
const content = complete.slice(offset, offset + limit);
|
|
1557
1685
|
const next = offset + content.length;
|
|
1558
1686
|
const truncated = next < complete.length;
|
|
1559
1687
|
return {
|
|
1560
|
-
asset_id:
|
|
1688
|
+
asset_id: assetId,
|
|
1561
1689
|
revision_id: revisionId,
|
|
1562
1690
|
conversion_status: revision?.conversionStatus ?? "ready",
|
|
1563
1691
|
content,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-library",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "本地文件资料库:内容寻址的不可变修订、派生的可检索正文、文件夹与模型工具。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/host.js",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"files": [
|
|
31
31
|
"dist/host.js",
|
|
32
32
|
"dist/client.js",
|
|
33
|
+
"dist/dsh-package.mjs",
|
|
33
34
|
"cordis.patch.yml",
|
|
34
35
|
"locale/*.json",
|
|
35
36
|
"README.md"
|