scientify 1.3.0 → 1.4.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/README.md +38 -14
- package/README.zh.md +38 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -2
- package/dist/index.js.map +1 -1
- package/dist/src/services/auto-updater.d.ts +15 -0
- package/dist/src/services/auto-updater.d.ts.map +1 -0
- package/dist/src/services/auto-updater.js +188 -0
- package/dist/src/services/auto-updater.js.map +1 -0
- package/dist/src/tools/arxiv-download.d.ts +25 -0
- package/dist/src/tools/arxiv-download.d.ts.map +1 -0
- package/dist/src/tools/arxiv-download.js +179 -0
- package/dist/src/tools/arxiv-download.js.map +1 -0
- package/dist/src/tools/{arxiv-tool.d.ts → arxiv-search.d.ts} +11 -8
- package/dist/src/tools/arxiv-search.d.ts.map +1 -0
- package/dist/src/tools/arxiv-search.js +140 -0
- package/dist/src/tools/arxiv-search.js.map +1 -0
- package/dist/src/tools/github-search-tool.d.ts +5 -1
- package/dist/src/tools/github-search-tool.d.ts.map +1 -1
- package/dist/src/tools/github-search-tool.js +10 -30
- package/dist/src/tools/github-search-tool.js.map +1 -1
- package/dist/src/tools/result.d.ts +37 -0
- package/dist/src/tools/result.d.ts.map +1 -0
- package/dist/src/tools/result.js +39 -0
- package/dist/src/tools/result.js.map +1 -0
- package/dist/src/tools/workspace.d.ts +32 -0
- package/dist/src/tools/workspace.d.ts.map +1 -0
- package/dist/src/tools/workspace.js +69 -0
- package/dist/src/tools/workspace.js.map +1 -0
- package/openclaw.plugin.json +22 -1
- package/package.json +13 -2
- package/skills/_shared/workspace-spec.md +15 -5
- package/skills/idea-generation/SKILL.md +2 -0
- package/skills/install-scientify/SKILL.md +17 -17
- package/skills/literature-survey/SKILL.md +86 -214
- package/skills/research-experiment/SKILL.md +114 -0
- package/skills/research-implement/SKILL.md +166 -0
- package/skills/research-pipeline/SKILL.md +104 -166
- package/skills/research-plan/SKILL.md +121 -0
- package/skills/research-review/SKILL.md +110 -0
- package/skills/research-survey/SKILL.md +140 -0
- package/skills/write-review-paper/SKILL.md +2 -0
- package/dist/src/tools/arxiv-tool.d.ts.map +0 -1
- package/dist/src/tools/arxiv-tool.js +0 -258
- package/dist/src/tools/arxiv-tool.js.map +0 -1
- package/skills/research-pipeline/references/prompts/implement.md +0 -135
- package/skills/research-pipeline/references/prompts/plan.md +0 -142
- package/skills/research-pipeline/references/prompts/review.md +0 -118
- package/skills/research-pipeline/references/prompts/survey.md +0 -105
- package/skills/research-pipeline/references/workspace-spec.md +0 -5
package/README.md
CHANGED
|
@@ -12,10 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
| Skill | Description |
|
|
14
14
|
|-------|-------------|
|
|
15
|
-
| **
|
|
16
|
-
| **research-
|
|
17
|
-
| **
|
|
18
|
-
| **
|
|
15
|
+
| **research-pipeline** | Orchestrator for end-to-end ML research. Spawns sub-agents for each phase, verifies outputs between steps. |
|
|
16
|
+
| **research-survey** | Deep analysis of downloaded papers: extract formulas, map to code, produce method comparison table. |
|
|
17
|
+
| **research-plan** | Create structured 4-part implementation plan (Dataset/Model/Training/Testing) from survey results. |
|
|
18
|
+
| **research-implement** | Implement ML code from plan, run 2-epoch validation with `uv` venv isolation, verify real results. |
|
|
19
|
+
| **research-review** | Review implementation against plan and survey. Iterates fix-rerun-review up to 3 times. |
|
|
20
|
+
| **research-experiment** | Full training run + ablation experiments + result analysis. Requires review PASS. |
|
|
21
|
+
| **literature-survey** | Comprehensive literature survey: search → filter → download → cluster → report. |
|
|
22
|
+
| **idea-generation** | Generate innovative research ideas from a topic. Searches arXiv/GitHub, downloads papers, outputs 5 ideas. |
|
|
19
23
|
|
|
20
24
|
### Commands (Direct, no LLM)
|
|
21
25
|
|
|
@@ -32,7 +36,8 @@
|
|
|
32
36
|
|
|
33
37
|
| Tool | Description |
|
|
34
38
|
|------|-------------|
|
|
35
|
-
| **arxiv_search** | Search arXiv.org API
|
|
39
|
+
| **arxiv_search** | Search arXiv.org API for papers. Returns metadata only (title, authors, abstract, arxiv_id). No side effects. |
|
|
40
|
+
| **arxiv_download** | Download arXiv papers by ID. Tries .tex source first, falls back to PDF. Requires absolute `output_dir` path. |
|
|
36
41
|
| **github_search** | Search GitHub repositories by keyword, filter by language, sort by stars/updated |
|
|
37
42
|
|
|
38
43
|
---
|
|
@@ -214,17 +219,36 @@ Agent: [Reading selected_idea.md and related papers]
|
|
|
214
219
|
|
|
215
220
|
```
|
|
216
221
|
~/.openclaw/workspace/projects/
|
|
217
|
-
├── .active
|
|
218
|
-
├── nlp-summarization/
|
|
219
|
-
│ ├── project.json
|
|
220
|
-
│ ├── task.json
|
|
221
|
-
│ ├──
|
|
222
|
-
│ ├──
|
|
223
|
-
│
|
|
224
|
-
│
|
|
222
|
+
├── .active # Current project ID
|
|
223
|
+
├── nlp-summarization/ # Project A
|
|
224
|
+
│ ├── project.json # Metadata
|
|
225
|
+
│ ├── task.json # Task definition
|
|
226
|
+
│ ├── survey/
|
|
227
|
+
│ │ ├── search_terms.json # Search terms used
|
|
228
|
+
│ │ └── report.md # Final survey report
|
|
229
|
+
│ ├── papers/
|
|
230
|
+
│ │ ├── _downloads/ # Raw downloaded files
|
|
231
|
+
│ │ ├── _meta/ # Paper metadata JSON files
|
|
232
|
+
│ │ │ └── {arxiv_id}.json
|
|
233
|
+
│ │ └── {direction}/ # Clustered papers by research direction
|
|
234
|
+
│ ├── repos/ # Cloned repos
|
|
235
|
+
│ ├── notes/ # /research-survey: per-paper analysis
|
|
236
|
+
│ │ └── paper_{arxiv_id}.md
|
|
237
|
+
│ ├── survey_res.md # /research-survey: method comparison
|
|
238
|
+
│ ├── plan_res.md # /research-plan: implementation plan
|
|
239
|
+
│ ├── project/ # /research-implement: ML code
|
|
240
|
+
│ │ ├── model/
|
|
241
|
+
│ │ ├── data/
|
|
242
|
+
│ │ ├── run.py
|
|
243
|
+
│ │ └── requirements.txt
|
|
244
|
+
│ ├── ml_res.md # /research-implement: execution report
|
|
245
|
+
│ ├── iterations/ # /research-review: judge reports
|
|
246
|
+
│ │ └── judge_v*.md
|
|
247
|
+
│ ├── experiment_res.md # /research-experiment: final results
|
|
248
|
+
│ └── ideas/ # Generated ideas
|
|
225
249
|
│ ├── idea_1.md
|
|
226
250
|
│ ├── idea_2.md
|
|
227
|
-
│ └── selected_idea.md
|
|
251
|
+
│ └── selected_idea.md # Best idea
|
|
228
252
|
└── another-project/
|
|
229
253
|
```
|
|
230
254
|
|
package/README.zh.md
CHANGED
|
@@ -12,10 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
| Skill | 描述 |
|
|
14
14
|
|-------|------|
|
|
15
|
-
| **
|
|
16
|
-
| **research-
|
|
17
|
-
| **
|
|
18
|
-
| **
|
|
15
|
+
| **research-pipeline** | 端到端 ML 研究编排器。通过 sessions_spawn 逐阶段派发子 agent,验证产出后推进。 |
|
|
16
|
+
| **research-survey** | 深度分析已下载论文:提取公式、映射代码、生成核心方法对比表。 |
|
|
17
|
+
| **research-plan** | 从调研结果制定四部分实现计划(数据集/模型/训练/测试)。 |
|
|
18
|
+
| **research-implement** | 按计划实现 ML 代码,使用 `uv` 虚拟环境隔离,2 epoch 验证,确保真实结果。 |
|
|
19
|
+
| **research-review** | 对照计划和调研审查实现代码,最多迭代修复 3 轮。 |
|
|
20
|
+
| **research-experiment** | 完整训练 + 消融实验 + 结果分析。需要 review PASS。 |
|
|
21
|
+
| **literature-survey** | 文献综述:搜索 → 筛选 → 下载 → 聚类 → 报告。 |
|
|
22
|
+
| **idea-generation** | 从研究主题生成创新想法。搜索 arXiv/GitHub、下载论文,输出 5 个研究想法。 |
|
|
19
23
|
|
|
20
24
|
### Commands (直接执行,不经 LLM)
|
|
21
25
|
|
|
@@ -32,8 +36,9 @@
|
|
|
32
36
|
|
|
33
37
|
| Tool | 描述 |
|
|
34
38
|
|------|------|
|
|
35
|
-
| **arxiv_search** | 搜索 arXiv API
|
|
36
|
-
| **
|
|
39
|
+
| **arxiv_search** | 搜索 arXiv API,返回论文元数据(标题、作者、摘要、ID)。 |
|
|
40
|
+
| **arxiv_download** | 按 ID 下载 arXiv 论文,优先 .tex 源文件,回退到 PDF。内置速率限制。 |
|
|
41
|
+
| **github_search** | 搜索 GitHub 仓库,支持关键词、语言过滤、按 stars/更新时间排序。 |
|
|
37
42
|
|
|
38
43
|
---
|
|
39
44
|
|
|
@@ -215,17 +220,35 @@ Agent: [读取 selected_idea.md 和相关论文]
|
|
|
215
220
|
|
|
216
221
|
```
|
|
217
222
|
~/.openclaw/workspace/projects/
|
|
218
|
-
├── .active
|
|
219
|
-
├── nlp-summarization/
|
|
220
|
-
│ ├── project.json
|
|
221
|
-
│ ├── task.json
|
|
222
|
-
│ ├──
|
|
223
|
-
│ ├──
|
|
224
|
-
│
|
|
225
|
-
│
|
|
223
|
+
├── .active # 当前项目 ID
|
|
224
|
+
├── nlp-summarization/ # 项目 A
|
|
225
|
+
│ ├── project.json # 元数据
|
|
226
|
+
│ ├── task.json # 任务定义
|
|
227
|
+
│ ├── survey/ # /literature-survey 产出
|
|
228
|
+
│ │ ├── search_terms.json
|
|
229
|
+
│ │ └── report.md
|
|
230
|
+
│ ├── papers/ # 下载的论文
|
|
231
|
+
│ │ ├── _downloads/ # 原始文件
|
|
232
|
+
│ │ ├── _meta/ # 元数据 JSON
|
|
233
|
+
│ │ └── {direction}/ # 按方向聚类
|
|
234
|
+
│ ├── repos/ # 克隆的仓库
|
|
235
|
+
│ ├── notes/ # /research-survey: 逐篇深度笔记
|
|
236
|
+
│ │ └── paper_{arxiv_id}.md
|
|
237
|
+
│ ├── survey_res.md # /research-survey: 方法对比
|
|
238
|
+
│ ├── plan_res.md # /research-plan: 实现计划
|
|
239
|
+
│ ├── project/ # /research-implement: ML 代码
|
|
240
|
+
│ │ ├── model/
|
|
241
|
+
│ │ ├── data/
|
|
242
|
+
│ │ ├── run.py
|
|
243
|
+
│ │ └── requirements.txt
|
|
244
|
+
│ ├── ml_res.md # /research-implement: 执行报告
|
|
245
|
+
│ ├── iterations/ # /research-review: 审查报告
|
|
246
|
+
│ │ └── judge_v*.md
|
|
247
|
+
│ ├── experiment_res.md # /research-experiment: 最终结果
|
|
248
|
+
│ └── ideas/ # 生成的想法
|
|
226
249
|
│ ├── idea_1.md
|
|
227
250
|
│ ├── idea_2.md
|
|
228
|
-
│ └── selected_idea.md
|
|
251
|
+
│ └── selected_idea.md # 最佳想法
|
|
229
252
|
└── another-project/
|
|
230
253
|
```
|
|
231
254
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAiBlD,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,GAAG,EAAE,iBAAiB,QA0EtD"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import { handleResearchStatus, handlePapers, handleIdeas, handleProjects, handleProjectSwitch, handleProjectDelete, } from "./src/commands.js";
|
|
2
|
-
import {
|
|
2
|
+
import { createArxivSearchTool } from "./src/tools/arxiv-search.js";
|
|
3
|
+
import { createArxivDownloadTool } from "./src/tools/arxiv-download.js";
|
|
3
4
|
import { createGithubSearchTool } from "./src/tools/github-search-tool.js";
|
|
5
|
+
import { createAutoUpdaterService } from "./src/services/auto-updater.js";
|
|
6
|
+
// Default: check every hour
|
|
7
|
+
const UPDATE_CHECK_INTERVAL_MS = 60 * 60 * 1000;
|
|
4
8
|
export default function register(api) {
|
|
5
9
|
// Register tools
|
|
6
|
-
api.registerTool(
|
|
10
|
+
api.registerTool(createArxivSearchTool());
|
|
11
|
+
api.registerTool(createArxivDownloadTool());
|
|
7
12
|
api.registerTool(createGithubSearchTool());
|
|
13
|
+
// Register auto-updater service (silent updates)
|
|
14
|
+
const pluginConfig = api.pluginConfig;
|
|
15
|
+
const autoUpdateEnabled = pluginConfig?.autoUpdate !== false; // enabled by default
|
|
16
|
+
if (autoUpdateEnabled) {
|
|
17
|
+
api.registerService(createAutoUpdaterService({
|
|
18
|
+
packageName: "scientify",
|
|
19
|
+
checkIntervalMs: UPDATE_CHECK_INTERVAL_MS,
|
|
20
|
+
logger: {
|
|
21
|
+
info: (msg) => api.logger.info(msg),
|
|
22
|
+
warn: (msg) => api.logger.warn(msg),
|
|
23
|
+
debug: (msg) => api.logger.debug?.(msg),
|
|
24
|
+
},
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
8
27
|
// Register chat commands (bypass LLM)
|
|
9
28
|
api.registerCommand({
|
|
10
29
|
name: "research-status",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface AutoUpdaterOptions {
|
|
2
|
+
packageName: string;
|
|
3
|
+
checkIntervalMs: number;
|
|
4
|
+
logger: {
|
|
5
|
+
info: (msg: string) => void;
|
|
6
|
+
warn: (msg: string) => void;
|
|
7
|
+
debug?: (msg: string) => void;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export declare function createAutoUpdaterService(options: AutoUpdaterOptions): {
|
|
11
|
+
id: string;
|
|
12
|
+
start: () => Promise<void>;
|
|
13
|
+
stop: () => void;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=auto-updater.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-updater.d.ts","sourceRoot":"","sources":["../../../src/services/auto-updater.ts"],"names":[],"mappings":"AAmJA,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE;QACN,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KAC/B,CAAC;CACH;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,kBAAkB;;;;EA4DnE"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
import { readFileSync, existsSync, lstatSync } from "fs";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join } from "path";
|
|
5
|
+
/**
|
|
6
|
+
* Get the plugin root directory (where package.json lives).
|
|
7
|
+
*/
|
|
8
|
+
function getPluginRoot() {
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
// From dist/src/services/ → go up 3 levels to plugin root
|
|
12
|
+
// Check for package.json at each level to find the correct root
|
|
13
|
+
let root = __dirname;
|
|
14
|
+
for (let i = 0; i < 5; i++) {
|
|
15
|
+
if (existsSync(join(root, "package.json"))) {
|
|
16
|
+
return root;
|
|
17
|
+
}
|
|
18
|
+
root = dirname(root);
|
|
19
|
+
}
|
|
20
|
+
// Fallback: assume dist/src/services structure
|
|
21
|
+
return join(__dirname, "../../..");
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Check if running in development mode.
|
|
25
|
+
* Detection methods (no main package dependency):
|
|
26
|
+
* 1. SCIENTIFY_DEV=1 environment variable
|
|
27
|
+
* 2. tsconfig.json exists in plugin root (source checkout)
|
|
28
|
+
* 3. Plugin directory is a symlink (npm link / pnpm link)
|
|
29
|
+
* 4. Version contains "-dev" suffix
|
|
30
|
+
* 5. NODE_ENV=development
|
|
31
|
+
*/
|
|
32
|
+
function isDevMode() {
|
|
33
|
+
// 1. Explicit env var
|
|
34
|
+
const scientifyDev = process.env.SCIENTIFY_DEV;
|
|
35
|
+
if (scientifyDev === "1" || scientifyDev === "true") {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
// 2. NODE_ENV
|
|
39
|
+
if (process.env.NODE_ENV === "development") {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const pluginRoot = getPluginRoot();
|
|
43
|
+
// 3. tsconfig.json exists (source checkout, not installed from npm)
|
|
44
|
+
if (existsSync(join(pluginRoot, "tsconfig.json"))) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
// 4. Plugin directory is a symlink (npm/pnpm link)
|
|
48
|
+
try {
|
|
49
|
+
if (lstatSync(pluginRoot).isSymbolicLink()) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// ignore
|
|
55
|
+
}
|
|
56
|
+
// 5. Version contains "-dev"
|
|
57
|
+
try {
|
|
58
|
+
const pkg = JSON.parse(readFileSync(join(pluginRoot, "package.json"), "utf-8"));
|
|
59
|
+
if (pkg.version && pkg.version.includes("-dev")) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// ignore
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
// Get current version from package.json
|
|
69
|
+
function getCurrentVersion() {
|
|
70
|
+
try {
|
|
71
|
+
const packagePath = join(getPluginRoot(), "package.json");
|
|
72
|
+
const pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
|
|
73
|
+
return pkg.version;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return "0.0.0";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Check npm registry for latest version
|
|
80
|
+
async function getLatestVersion(packageName) {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
exec(`npm view ${packageName} version`, { timeout: 10000 }, (error, stdout) => {
|
|
83
|
+
if (error) {
|
|
84
|
+
resolve(null);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
resolve(stdout.trim());
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// Compare semver versions: returns true if latest > current
|
|
92
|
+
function isNewerVersion(current, latest) {
|
|
93
|
+
const currentParts = current.split(".").map(Number);
|
|
94
|
+
const latestParts = latest.split(".").map(Number);
|
|
95
|
+
for (let i = 0; i < 3; i++) {
|
|
96
|
+
const c = currentParts[i] || 0;
|
|
97
|
+
const l = latestParts[i] || 0;
|
|
98
|
+
if (l > c)
|
|
99
|
+
return true;
|
|
100
|
+
if (l < c)
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Get the OpenClaw extensions directory.
|
|
107
|
+
*/
|
|
108
|
+
function getExtensionsDir() {
|
|
109
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
110
|
+
return join(home, ".openclaw", "extensions");
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Run the update using npm pack + extract approach.
|
|
114
|
+
* This avoids dependency on `openclaw` CLI being in PATH.
|
|
115
|
+
*/
|
|
116
|
+
async function runUpdate(packageName, logger) {
|
|
117
|
+
const extensionsDir = getExtensionsDir();
|
|
118
|
+
const targetDir = join(extensionsDir, packageName);
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
// Use npm pack to download the tarball, then extract
|
|
121
|
+
const cmd = `cd "${extensionsDir}" && npm pack ${packageName} --pack-destination . && tar -xzf ${packageName}-*.tgz && rm -rf "${targetDir}" && mv package "${targetDir}" && rm ${packageName}-*.tgz`;
|
|
122
|
+
exec(cmd, { timeout: 120000 }, (error, stdout, stderr) => {
|
|
123
|
+
if (error) {
|
|
124
|
+
logger.warn(`Scientify auto-update failed: ${stderr || error.message}`);
|
|
125
|
+
resolve(false);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
logger.info(`Scientify updated. Restart gateway to apply.`);
|
|
129
|
+
resolve(true);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export function createAutoUpdaterService(options) {
|
|
134
|
+
const { packageName, checkIntervalMs, logger } = options;
|
|
135
|
+
let intervalId = null;
|
|
136
|
+
let isChecking = false;
|
|
137
|
+
const checkAndUpdate = async () => {
|
|
138
|
+
if (isChecking)
|
|
139
|
+
return;
|
|
140
|
+
isChecking = true;
|
|
141
|
+
try {
|
|
142
|
+
const currentVersion = getCurrentVersion();
|
|
143
|
+
const latestVersion = await getLatestVersion(packageName);
|
|
144
|
+
if (!latestVersion) {
|
|
145
|
+
logger.debug?.(`Scientify update check: could not fetch latest version`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (isNewerVersion(currentVersion, latestVersion)) {
|
|
149
|
+
logger.info(`Scientify update available: ${currentVersion} → ${latestVersion}`);
|
|
150
|
+
await runUpdate(packageName, logger);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
logger.debug?.(`Scientify is up to date (${currentVersion})`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
logger.warn(`Scientify update check error: ${err}`);
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
isChecking = false;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
id: "scientify-auto-updater",
|
|
165
|
+
start: async () => {
|
|
166
|
+
// Skip auto-updates in development mode
|
|
167
|
+
if (isDevMode()) {
|
|
168
|
+
logger.debug?.("Scientify auto-updater skipped (dev mode)");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
logger.debug?.("Scientify auto-updater service started");
|
|
172
|
+
// Check once on startup (with a small delay to not block gateway start)
|
|
173
|
+
setTimeout(() => {
|
|
174
|
+
checkAndUpdate();
|
|
175
|
+
}, 5000);
|
|
176
|
+
// Then check periodically
|
|
177
|
+
intervalId = setInterval(checkAndUpdate, checkIntervalMs);
|
|
178
|
+
},
|
|
179
|
+
stop: () => {
|
|
180
|
+
if (intervalId) {
|
|
181
|
+
clearInterval(intervalId);
|
|
182
|
+
intervalId = null;
|
|
183
|
+
}
|
|
184
|
+
logger.debug?.("Scientify auto-updater service stopped");
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=auto-updater.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-updater.js","sourceRoot":"","sources":["../../../src/services/auto-updater.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const ArxivDownloadSchema: import("@sinclair/typebox").TObject<{
|
|
2
|
+
arxiv_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
|
|
3
|
+
output_dir: import("@sinclair/typebox").TString;
|
|
4
|
+
}>;
|
|
5
|
+
/**
|
|
6
|
+
* arxiv_download: Download papers by arxiv_id. Requires explicit output_dir.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createArxivDownloadTool(): {
|
|
9
|
+
label: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
parameters: import("@sinclair/typebox").TObject<{
|
|
13
|
+
arxiv_ids: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
|
|
14
|
+
output_dir: import("@sinclair/typebox").TString;
|
|
15
|
+
}>;
|
|
16
|
+
execute: (_toolCallId: string, rawArgs: unknown) => Promise<{
|
|
17
|
+
type: "tool_result";
|
|
18
|
+
content: {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
isError?: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=arxiv-download.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arxiv-download.d.ts","sourceRoot":"","sources":["../../../src/tools/arxiv-download.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,mBAAmB;;;EAO9B,CAAC;AAoIH;;GAEG;AACH,wBAAgB,uBAAuB;;;;;;;;2BAMN,MAAM,WAAW,OAAO;;;;;;;;EA4DxD"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import * as tar from "tar";
|
|
5
|
+
import { Result } from "./result.js";
|
|
6
|
+
// Rate limit: arXiv recommends ~3 seconds between requests
|
|
7
|
+
const RATE_LIMIT_MS = 3000;
|
|
8
|
+
/** Non-blocking delay that yields to event loop */
|
|
9
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
export const ArxivDownloadSchema = Type.Object({
|
|
11
|
+
arxiv_ids: Type.Array(Type.String(), {
|
|
12
|
+
description: "List of arXiv IDs to download (e.g. ['2401.12345', '2312.00001']).",
|
|
13
|
+
}),
|
|
14
|
+
output_dir: Type.String({
|
|
15
|
+
description: "Directory to save files. MUST be an absolute path.",
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
function readStringParam(params, key, opts) {
|
|
19
|
+
const value = params[key];
|
|
20
|
+
if (value === undefined || value === null) {
|
|
21
|
+
if (opts?.required) {
|
|
22
|
+
throw new Error(`Missing required parameter: ${key}`);
|
|
23
|
+
}
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return String(value);
|
|
27
|
+
}
|
|
28
|
+
function readArrayParam(params, key) {
|
|
29
|
+
const value = params[key];
|
|
30
|
+
if (Array.isArray(value))
|
|
31
|
+
return value.map(String);
|
|
32
|
+
if (typeof value === "string") {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(value);
|
|
35
|
+
if (Array.isArray(parsed))
|
|
36
|
+
return parsed.map(String);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return [value];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
async function downloadTexSource(arxivId, outputDir, logger) {
|
|
45
|
+
const log = (msg) => logger?.debug?.(`[arxiv:${arxivId}] ${msg}`);
|
|
46
|
+
const paperDir = path.join(outputDir, arxivId);
|
|
47
|
+
await fs.promises.mkdir(paperDir, { recursive: true });
|
|
48
|
+
const srcUrl = `https://arxiv.org/src/${arxivId}`;
|
|
49
|
+
const tarPath = path.join(paperDir, "source.tar.gz");
|
|
50
|
+
try {
|
|
51
|
+
log(`Fetching source from ${srcUrl}`);
|
|
52
|
+
const response = await fetch(srcUrl);
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const reason = `Source download failed: HTTP ${response.status} ${response.statusText}`;
|
|
55
|
+
log(reason);
|
|
56
|
+
const result = await downloadPdf(arxivId, outputDir, logger);
|
|
57
|
+
return { ...result, fallbackReason: reason };
|
|
58
|
+
}
|
|
59
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
60
|
+
log(`Downloaded ${buffer.length} bytes`);
|
|
61
|
+
await fs.promises.writeFile(tarPath, buffer);
|
|
62
|
+
const isTarGz = buffer[0] === 0x1f && buffer[1] === 0x8b;
|
|
63
|
+
log(`Format check: ${isTarGz ? "tar.gz" : "single file"}`);
|
|
64
|
+
if (isTarGz) {
|
|
65
|
+
await tar.x({ file: tarPath, cwd: paperDir });
|
|
66
|
+
await fs.promises.unlink(tarPath);
|
|
67
|
+
const files = await findTexFiles(paperDir);
|
|
68
|
+
log(`Found ${files.length} .tex files: ${files.join(", ")}`);
|
|
69
|
+
if (files.length === 0) {
|
|
70
|
+
const reason = "No .tex files found in source archive";
|
|
71
|
+
log(reason);
|
|
72
|
+
const result = await downloadPdf(arxivId, outputDir, logger);
|
|
73
|
+
return { ...result, fallbackReason: reason };
|
|
74
|
+
}
|
|
75
|
+
return { success: true, format: "tex", path: paperDir, files };
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const texPath = path.join(paperDir, "main.tex");
|
|
79
|
+
await fs.promises.rename(tarPath, texPath);
|
|
80
|
+
log("Saved as single main.tex file");
|
|
81
|
+
return { success: true, format: "tex", path: paperDir, files: ["main.tex"] };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
const reason = `Source extraction error: ${error instanceof Error ? error.message : String(error)}`;
|
|
86
|
+
log(reason);
|
|
87
|
+
const result = await downloadPdf(arxivId, outputDir, logger);
|
|
88
|
+
return { ...result, fallbackReason: reason };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function downloadPdf(arxivId, outputDir, logger) {
|
|
92
|
+
const log = (msg) => logger?.debug?.(`[arxiv:${arxivId}] ${msg}`);
|
|
93
|
+
try {
|
|
94
|
+
const pdfUrl = `https://arxiv.org/pdf/${arxivId}.pdf`;
|
|
95
|
+
log(`Downloading PDF: ${pdfUrl}`);
|
|
96
|
+
const response = await fetch(pdfUrl);
|
|
97
|
+
if (!response.ok) {
|
|
98
|
+
log(`PDF download failed: HTTP ${response.status}`);
|
|
99
|
+
return { success: false, format: "pdf", path: "", files: [], error: `PDF download failed: ${response.status}` };
|
|
100
|
+
}
|
|
101
|
+
const pdfPath = path.join(outputDir, `${arxivId}.pdf`);
|
|
102
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
103
|
+
await fs.promises.writeFile(pdfPath, buffer);
|
|
104
|
+
log(`PDF saved: ${pdfPath} (${buffer.length} bytes)`);
|
|
105
|
+
return { success: true, format: "pdf", path: pdfPath, files: [`${arxivId}.pdf`] };
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
log(`PDF download error: ${error}`);
|
|
109
|
+
return { success: false, format: "pdf", path: "", files: [], error: String(error) };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function findTexFiles(dir) {
|
|
113
|
+
const files = [];
|
|
114
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
115
|
+
for (const entry of entries) {
|
|
116
|
+
const fullPath = path.join(dir, entry.name);
|
|
117
|
+
if (entry.isDirectory()) {
|
|
118
|
+
const subFiles = await findTexFiles(fullPath);
|
|
119
|
+
files.push(...subFiles.map((f) => path.join(entry.name, f)));
|
|
120
|
+
}
|
|
121
|
+
else if (entry.name.endsWith(".tex")) {
|
|
122
|
+
files.push(entry.name);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return files;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* arxiv_download: Download papers by arxiv_id. Requires explicit output_dir.
|
|
129
|
+
*/
|
|
130
|
+
export function createArxivDownloadTool() {
|
|
131
|
+
return {
|
|
132
|
+
label: "ArXiv Download",
|
|
133
|
+
name: "arxiv_download",
|
|
134
|
+
description: "Download arXiv papers by ID. Downloads .tex source (with PDF fallback). Requires explicit output_dir (absolute path).",
|
|
135
|
+
parameters: ArxivDownloadSchema,
|
|
136
|
+
execute: async (_toolCallId, rawArgs) => {
|
|
137
|
+
const params = rawArgs;
|
|
138
|
+
const arxivIds = readArrayParam(params, "arxiv_ids");
|
|
139
|
+
const outputDir = readStringParam(params, "output_dir", { required: true });
|
|
140
|
+
if (arxivIds.length === 0) {
|
|
141
|
+
return Result.err("invalid_params", "arxiv_ids must be a non-empty array");
|
|
142
|
+
}
|
|
143
|
+
if (!path.isAbsolute(outputDir)) {
|
|
144
|
+
return Result.err("invalid_params", `output_dir must be an absolute path, got: ${outputDir}`);
|
|
145
|
+
}
|
|
146
|
+
await fs.promises.mkdir(outputDir, { recursive: true });
|
|
147
|
+
const logger = { debug: (msg) => console.log(`[arxiv-download] ${msg}`) };
|
|
148
|
+
const downloads = [];
|
|
149
|
+
for (let i = 0; i < arxivIds.length; i++) {
|
|
150
|
+
const arxivId = arxivIds[i];
|
|
151
|
+
// Rate limit: wait before each request (except first)
|
|
152
|
+
if (i > 0) {
|
|
153
|
+
await delay(RATE_LIMIT_MS);
|
|
154
|
+
}
|
|
155
|
+
// Always try .tex first, fallback to PDF automatically
|
|
156
|
+
const result = await downloadTexSource(arxivId, outputDir, logger);
|
|
157
|
+
downloads.push({
|
|
158
|
+
arxiv_id: arxivId,
|
|
159
|
+
success: result.success,
|
|
160
|
+
format: result.format,
|
|
161
|
+
path: result.path,
|
|
162
|
+
files: result.files,
|
|
163
|
+
error: result.error,
|
|
164
|
+
fallback_reason: result.fallbackReason,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const successful = downloads.filter((d) => d.success).length;
|
|
168
|
+
const failed = downloads.filter((d) => !d.success).length;
|
|
169
|
+
return Result.ok({
|
|
170
|
+
output_dir: outputDir,
|
|
171
|
+
total: arxivIds.length,
|
|
172
|
+
successful,
|
|
173
|
+
failed,
|
|
174
|
+
downloads,
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=arxiv-download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arxiv-download.js","sourceRoot":"","sources":["../../../src/tools/arxiv-download.ts"],"names":[],"mappings":"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"}
|