@a9i5k4/dsh-literature 0.3.4 → 0.3.6

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 CHANGED
@@ -757,7 +757,7 @@ var UA, FetchFailure;
757
757
  var init_net = __esm({
758
758
  "src/node/net.js"() {
759
759
  init_log();
760
- UA = "dsh-literature/0.3.4 (+https://github.com/deepseek-ai/deepseek-harness)";
760
+ UA = "dsh-literature/0.3.6 (+https://github.com/deepseek-ai/deepseek-harness)";
761
761
  FetchFailure = class extends Error {
762
762
  constructor(message, { code = "network", status = 0, retryable = true, cause } = {}) {
763
763
  super(message, { cause });
@@ -2421,6 +2421,8 @@ function titleFromFilename(name2) {
2421
2421
  if (doi) return { kind: "doi", value: doi[1].replace(/_/g, "/") };
2422
2422
  const arxiv = /\b(\d{4}\.\d{4,5})(v\d+)?\b/.exec(t);
2423
2423
  if (arxiv) return { kind: "arxiv", value: arxiv[1] + (arxiv[2] ?? "") };
2424
+ const springer = /^([a-z]\d{4,6}-\d{2,4}-\d{2,6}(?:-[a-z0-9]{1,10})?)(?:\s*\(\d+\))?\s*$/i.exec(t.trim());
2425
+ if (springer) return { kind: "doi", value: `10.1007/${springer[1].toLowerCase()}` };
2424
2426
  t = t.replace(/^\[\d+\]\s*/, "").replace(/\s*\(?\d{4}[a-z]?\)?\s*$/i, "").replace(/[_\-]+/g, " ").replace(/^\d+[\s.]+/, "").trim();
2425
2427
  if (!t) return null;
2426
2428
  return { kind: "title", value: t };
@@ -2825,6 +2827,7 @@ __export(pipeline_exports, {
2825
2827
  log: () => log,
2826
2828
  normalizeDoi: () => normalizeDoi,
2827
2829
  previewConflict: () => previewConflict,
2830
+ reidentifyItem: () => reidentifyItem,
2828
2831
  resolveItem: () => resolveItem,
2829
2832
  retryItem: () => retryItem,
2830
2833
  saveItem: () => saveItem,
@@ -3275,6 +3278,33 @@ async function importDroppedPdf(buffer, { filename = "dropped.pdf" } = {}) {
3275
3278
  emitItem(saved);
3276
3279
  return saved;
3277
3280
  }
3281
+ async function reidentifyItem(key) {
3282
+ const item = await getItem(key);
3283
+ if (!item) throw failure("not_found", "\u6761\u76EE\u4E0D\u5B58\u5728");
3284
+ const name2 = item.pdf?.filename ?? item.sourceFile ?? (item.rawValue ? `${item.rawValue}.pdf` : "");
3285
+ const { titleFromFilename: titleFromFilename2 } = await Promise.resolve().then(() => (init_importer(), importer_exports));
3286
+ const hit = titleFromFilename2(name2);
3287
+ if (!hit || hit.kind === "title") {
3288
+ throw failure("network", "\u65E0\u6CD5\u4ECE\u6587\u4EF6\u540D\u8BC6\u522B\u51FA DOI / arXiv \u6807\u8BC6\u7B26\uFF0C\u8BF7\u624B\u52A8\u7C98\u8D34\u6807\u8BC6\u7B26\u540E\u91CD\u8BD5");
3289
+ }
3290
+ let next = await patchItem(key, {
3291
+ kind: hit.kind,
3292
+ rawValue: hit.value,
3293
+ display: hit.value,
3294
+ doi: hit.kind === "doi" ? hit.value : "",
3295
+ arxiv: hit.kind === "arxiv" ? hit.value : "",
3296
+ isbn: "",
3297
+ record: null,
3298
+ error: null
3299
+ });
3300
+ emitItem(next);
3301
+ try {
3302
+ next = await resolveItem(key);
3303
+ } catch {
3304
+ }
3305
+ emitItem(next);
3306
+ return next;
3307
+ }
3278
3308
  async function importDroppedCourseware(buffer, { filename = "deck.pptx" } = {}) {
3279
3309
  if (!buffer || buffer.length < 4 || buffer.subarray(0, 2).toString("latin1") !== "PK") {
3280
3310
  throw failure("network", "\u62D6\u5165\u7684\u6587\u4EF6\u4E0D\u662F\u6709\u6548\u7684 .pptx\uFF08\u65E7\u7248 .ppt \u8BF7\u5148\u53E6\u5B58\u4E3A .pptx \u6216\u5BFC\u51FA PDF\uFF09");
@@ -36262,7 +36292,7 @@ init_health();
36262
36292
  init_local_api();
36263
36293
  init_connector();
36264
36294
  init_log();
36265
- import { readFile as readFile5 } from "node:fs/promises";
36295
+ import { readFile as readFile5, open as openFile, stat as statFile } from "node:fs/promises";
36266
36296
  import { resolve as resolve5 } from "node:path";
36267
36297
  var PREFIX2 = "/api/dsh-literature";
36268
36298
  function pathOf(req) {
@@ -36311,30 +36341,44 @@ async function servePdf(req, res, key) {
36311
36341
  writeJson(res, 400, { error: "invalid key", code: "invalidKey" });
36312
36342
  return;
36313
36343
  }
36314
- let buffer;
36344
+ let size = 0;
36315
36345
  try {
36316
- buffer = await readFile5(path);
36346
+ size = (await statFile(path)).size;
36317
36347
  } catch {
36318
36348
  writeJson(res, 404, { error: "pdf not downloaded yet", code: "pdfNotDownloaded" });
36319
36349
  return;
36320
36350
  }
36321
- const range = parseRange(req.headers.range, buffer.length);
36351
+ const range = parseRange(req.headers.range, size);
36322
36352
  if (range?.invalid) {
36323
- res.writeHead(416, { "content-range": `bytes */${buffer.length}`, "content-length": 0 });
36353
+ res.writeHead(416, { "content-range": `bytes */${size}`, "content-length": 0 });
36324
36354
  res.end();
36325
36355
  return;
36326
36356
  }
36327
36357
  if (range) {
36328
- const slice = buffer.subarray(range.start, range.end + 1);
36358
+ const len = range.end - range.start + 1;
36359
+ const slice = Buffer.alloc(len);
36360
+ const fh = await openFile(path, "r");
36361
+ try {
36362
+ await fh.read(slice, 0, len, range.start);
36363
+ } finally {
36364
+ await fh.close();
36365
+ }
36329
36366
  res.writeHead(206, {
36330
36367
  "content-type": "application/pdf",
36331
36368
  "content-length": slice.length,
36332
- "content-range": `bytes ${range.start}-${range.end}/${buffer.length}`,
36369
+ "content-range": `bytes ${range.start}-${range.end}/${size}`,
36333
36370
  "accept-ranges": "bytes"
36334
36371
  });
36335
36372
  res.end(req.method === "HEAD" ? void 0 : slice);
36336
36373
  return;
36337
36374
  }
36375
+ let buffer;
36376
+ try {
36377
+ buffer = await readFile5(path);
36378
+ } catch {
36379
+ writeJson(res, 404, { error: "pdf not downloaded yet", code: "pdfNotDownloaded" });
36380
+ return;
36381
+ }
36338
36382
  res.writeHead(200, {
36339
36383
  "content-type": "application/pdf",
36340
36384
  "content-length": buffer.length,
@@ -36406,7 +36450,7 @@ async function readJsonBody2(req) {
36406
36450
  const { readJsonBody: read } = await Promise.resolve().then(() => (init_http(), http_exports));
36407
36451
  return read(req);
36408
36452
  }
36409
- async function handler(req, res, ctx2) {
36453
+ async function handler(req, res, ctx2, { activation } = {}) {
36410
36454
  if (!isLoopbackRequest(req)) {
36411
36455
  writeJson(res, 403, { error: "forbidden: loopback-only", code: "forbidden" });
36412
36456
  return;
@@ -36485,6 +36529,12 @@ async function handler(req, res, ctx2) {
36485
36529
  writeJson(res, 200, { item });
36486
36530
  return;
36487
36531
  }
36532
+ if (head === "reidentify" && methodOk(req, "POST")) {
36533
+ const body = await readJsonBody2(req);
36534
+ const item = await reidentifyItem(String(body?.key ?? ""));
36535
+ writeJson(res, 200, { item });
36536
+ return;
36537
+ }
36488
36538
  if (head === "save" && methodOk(req, "POST")) {
36489
36539
  const body = await readJsonBody2(req);
36490
36540
  const item = await saveItem(String(body?.key ?? ""), { mode: body?.mode, tags: body?.tags });
@@ -36687,8 +36737,8 @@ async function handler(req, res, ctx2) {
36687
36737
  else res.end();
36688
36738
  }
36689
36739
  }
36690
- function registerRoutes(ctx2, { activation: activation2 } = {}) {
36691
- const disposers = [ctx2.webServer.register({ kind: "prefix", path: PREFIX2, handler: (req, res) => handler(req, res, ctx2) })];
36740
+ function registerRoutes(ctx2, { activation } = {}) {
36741
+ const disposers = [ctx2.webServer.register({ kind: "prefix", path: PREFIX2, handler: (req, res) => handler(req, res, ctx2, { activation }) })];
36692
36742
  if (typeof ctx2.on === "function") {
36693
36743
  disposers.push(
36694
36744
  ctx2.on("session/event", (session) => {
@@ -37307,14 +37357,14 @@ function textFromSessionEvent(event) {
37307
37357
  }
37308
37358
  return "";
37309
37359
  }
37310
- function registerSessionHook(ctx2, { activation: activation2, detectIntent: detectIntent2 } = {}) {
37360
+ function registerSessionHook(ctx2, { activation, detectIntent: detectIntent2 } = {}) {
37311
37361
  const disposers = [];
37312
37362
  disposers.push(
37313
37363
  ctx2.on("session/event", (session, event) => {
37314
37364
  const text = textFromSessionEvent(event);
37315
- if (activation2 && detectIntent2 && event?.type === "user/message") {
37365
+ if (activation && detectIntent2 && event?.type === "user/message") {
37316
37366
  const verdict = detectIntent2(textFromSessionEvent(event));
37317
- if (verdict.active) activation2.activate(verdict.reason);
37367
+ if (verdict.active) activation.activate(verdict.reason);
37318
37368
  }
37319
37369
  if (!text || text.length < 20) return;
37320
37370
  if (!extractIdentifiers(text).length) return;
@@ -37508,12 +37558,12 @@ function apply(ctx2, config) {
37508
37558
  const ready = (async () => {
37509
37559
  await ensureDirs();
37510
37560
  await init();
37511
- const activation2 = createActivation();
37512
- activation2.activate("always-on");
37561
+ const activation = createActivation();
37562
+ activation.activate("always-on");
37513
37563
  const toolsCtl = registerTools(ctx2);
37514
37564
  toolsCtl.mount();
37515
- disposers.push(activation2.onTransition(({ active }) => active ? toolsCtl.mount() : toolsCtl.unmount()));
37516
- disposers.push(() => activation2.dispose());
37565
+ disposers.push(activation.onTransition(({ active }) => active ? toolsCtl.mount() : toolsCtl.unmount()));
37566
+ disposers.push(() => activation.dispose());
37517
37567
  try {
37518
37568
  disposers.push(
37519
37569
  ctx2.systemPrompt.section({
@@ -37533,9 +37583,9 @@ function apply(ctx2, config) {
37533
37583
  } catch (e) {
37534
37584
  error("systemPrompt unavailable:", e.message);
37535
37585
  }
37536
- disposers.push(...registerRoutes(ctx2, { activation: activation2 }));
37586
+ disposers.push(...registerRoutes(ctx2, { activation }));
37537
37587
  try {
37538
- disposers.push(...registerSessionHook(ctx2, { activation: activation2, detectIntent }));
37588
+ disposers.push(...registerSessionHook(ctx2, { activation, detectIntent }));
37539
37589
  } catch (e) {
37540
37590
  error("session hook unavailable:", e.message);
37541
37591
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a9i5k4/dsh-literature",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "DSH Literature 文献侧窗:在 DeepSeek Harness 侧边栏识别 DOI/arXiv/标题、抓取元数据与全文、写入本地文献库或导出目录,并提供内置 PDF 阅读器(缩放/翻页/目录/搜索/高亮/笔记)。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",