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 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 stubs = /* @__PURE__ */ new Map();
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 importDshPackage("@deepseek-ai/dsh-native-command");
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 cached3;
1563
+ var cached4;
1443
1564
  async function loadDefineTool() {
1444
- if (cached3 !== void 0) return cached3;
1565
+ if (cached4 !== void 0) return cached4;
1445
1566
  try {
1446
- const mod = await importDshPackage("@deepseek-ai/dsh-tools");
1567
+ const mod = await importDshPackage4("@deepseek-ai/dsh-tools");
1447
1568
  if (typeof mod?.defineTool === "function") {
1448
- cached3 = mod.defineTool;
1449
- return cached3;
1569
+ cached4 = mod.defineTool;
1570
+ return cached4;
1450
1571
  }
1451
1572
  } catch {
1452
1573
  }
1453
- cached3 = fallbackDefineTool;
1454
- return cached3;
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 detail = manager.assetDetail(args.asset_id);
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(args.asset_id, revisionId);
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: args.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.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"