open-research 0.1.0 → 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/README.md +27 -1
- package/dist/cli.js +87 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,11 +4,37 @@ Local-first research CLI agent. Discover papers, synthesize notes, run analysis,
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
+
Requires Node.js 20+.
|
|
8
|
+
|
|
9
|
+
**curl**
|
|
10
|
+
```bash
|
|
11
|
+
curl -fsSL https://raw.githubusercontent.com/gangj277/open-research/main/install.sh | bash
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**npm**
|
|
7
15
|
```bash
|
|
8
16
|
npm install -g open-research
|
|
9
17
|
```
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
**bun**
|
|
20
|
+
```bash
|
|
21
|
+
bun install -g open-research
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**pnpm**
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add -g open-research
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**yarn**
|
|
30
|
+
```bash
|
|
31
|
+
yarn global add open-research
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**npx** (no install, runs latest)
|
|
35
|
+
```bash
|
|
36
|
+
npx open-research
|
|
37
|
+
```
|
|
12
38
|
|
|
13
39
|
## Quick Start
|
|
14
40
|
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import React4 from "react";
|
|
5
|
-
import
|
|
5
|
+
import path17 from "path";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { render } from "ink";
|
|
8
8
|
|
|
@@ -848,7 +848,7 @@ async function ensureOpenResearchConfig(options) {
|
|
|
848
848
|
}
|
|
849
849
|
|
|
850
850
|
// src/tui/app.tsx
|
|
851
|
-
import
|
|
851
|
+
import path16 from "path";
|
|
852
852
|
import {
|
|
853
853
|
startTransition,
|
|
854
854
|
useDeferredValue,
|
|
@@ -5033,6 +5033,81 @@ function ConfigScreen({ items, onUpdate, onClose }) {
|
|
|
5033
5033
|
] });
|
|
5034
5034
|
}
|
|
5035
5035
|
|
|
5036
|
+
// src/lib/cli/update-check.ts
|
|
5037
|
+
import fs16 from "fs/promises";
|
|
5038
|
+
import path15 from "path";
|
|
5039
|
+
import os4 from "os";
|
|
5040
|
+
var PACKAGE_NAME = "open-research";
|
|
5041
|
+
var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
5042
|
+
var STATE_FILE = path15.join(os4.homedir(), ".open-research", "update-check.json");
|
|
5043
|
+
async function readState() {
|
|
5044
|
+
try {
|
|
5045
|
+
const raw = await fs16.readFile(STATE_FILE, "utf8");
|
|
5046
|
+
return JSON.parse(raw);
|
|
5047
|
+
} catch {
|
|
5048
|
+
return { lastCheck: 0, latestVersion: null };
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
async function writeState(state) {
|
|
5052
|
+
await fs16.mkdir(path15.dirname(STATE_FILE), { recursive: true });
|
|
5053
|
+
await fs16.writeFile(STATE_FILE, JSON.stringify(state), "utf8");
|
|
5054
|
+
}
|
|
5055
|
+
function getCurrentVersion() {
|
|
5056
|
+
try {
|
|
5057
|
+
const pkgPath = new URL("../../../package.json", import.meta.url);
|
|
5058
|
+
return process.env.npm_package_version ?? "0.0.0";
|
|
5059
|
+
} catch {
|
|
5060
|
+
return "0.0.0";
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
async function fetchLatestVersion() {
|
|
5064
|
+
try {
|
|
5065
|
+
const controller = new AbortController();
|
|
5066
|
+
const timer = setTimeout(() => controller.abort(), 5e3);
|
|
5067
|
+
const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
|
|
5068
|
+
signal: controller.signal
|
|
5069
|
+
});
|
|
5070
|
+
clearTimeout(timer);
|
|
5071
|
+
if (!res.ok) return null;
|
|
5072
|
+
const data = await res.json();
|
|
5073
|
+
return data.version ?? null;
|
|
5074
|
+
} catch {
|
|
5075
|
+
return null;
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
function isNewer(latest, current) {
|
|
5079
|
+
const l = latest.split(".").map(Number);
|
|
5080
|
+
const c = current.split(".").map(Number);
|
|
5081
|
+
for (let i = 0; i < 3; i++) {
|
|
5082
|
+
if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
|
|
5083
|
+
if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
|
|
5084
|
+
}
|
|
5085
|
+
return false;
|
|
5086
|
+
}
|
|
5087
|
+
async function checkForUpdate() {
|
|
5088
|
+
try {
|
|
5089
|
+
const state = await readState();
|
|
5090
|
+
const now = Date.now();
|
|
5091
|
+
if (now - state.lastCheck < CHECK_INTERVAL_MS && state.latestVersion) {
|
|
5092
|
+
const current2 = getCurrentVersion();
|
|
5093
|
+
if (isNewer(state.latestVersion, current2)) {
|
|
5094
|
+
return `Update available: ${current2} \u2192 ${state.latestVersion}. Run: npm update -g open-research`;
|
|
5095
|
+
}
|
|
5096
|
+
return null;
|
|
5097
|
+
}
|
|
5098
|
+
const latest = await fetchLatestVersion();
|
|
5099
|
+
await writeState({ lastCheck: now, latestVersion: latest });
|
|
5100
|
+
if (!latest) return null;
|
|
5101
|
+
const current = getCurrentVersion();
|
|
5102
|
+
if (isNewer(latest, current)) {
|
|
5103
|
+
return `Update available: ${current} \u2192 ${latest}. Run: npm update -g open-research`;
|
|
5104
|
+
}
|
|
5105
|
+
return null;
|
|
5106
|
+
} catch {
|
|
5107
|
+
return null;
|
|
5108
|
+
}
|
|
5109
|
+
}
|
|
5110
|
+
|
|
5036
5111
|
// src/tui/commands.ts
|
|
5037
5112
|
var SLASH_COMMANDS = [
|
|
5038
5113
|
{ name: "auth", aliases: ["/connect", "/login"], description: "Connect your OpenAI account via browser OAuth", category: "auth" },
|
|
@@ -5605,6 +5680,9 @@ function App({
|
|
|
5605
5680
|
setTheme(cfg.theme);
|
|
5606
5681
|
const auth2 = await loadStoredAuth({ homeDir });
|
|
5607
5682
|
setAuthStatus(auth2 ? "connected" : "missing");
|
|
5683
|
+
checkForUpdate().then((msg) => {
|
|
5684
|
+
if (msg) addSystemMessage(msg);
|
|
5685
|
+
});
|
|
5608
5686
|
})();
|
|
5609
5687
|
}, [homeDir]);
|
|
5610
5688
|
useEffect2(() => {
|
|
@@ -6510,7 +6588,7 @@ function App({
|
|
|
6510
6588
|
statusParts,
|
|
6511
6589
|
statusColor,
|
|
6512
6590
|
tokenDisplay,
|
|
6513
|
-
workspaceName: hasWorkspace ?
|
|
6591
|
+
workspaceName: hasWorkspace ? path16.basename(workspacePath) : process.cwd(),
|
|
6514
6592
|
mode: agentMode,
|
|
6515
6593
|
planningStatus: planningState.status
|
|
6516
6594
|
}
|
|
@@ -6522,7 +6600,7 @@ function App({
|
|
|
6522
6600
|
var program = new Command();
|
|
6523
6601
|
program.name("open-research").description("Local-first research CLI powered by ChatGPT/Codex auth.").argument("[workspacePath]", "Optional workspace path to open").action(async (workspacePath) => {
|
|
6524
6602
|
await ensureOpenResearchConfig();
|
|
6525
|
-
const target = workspacePath ?
|
|
6603
|
+
const target = workspacePath ? path17.resolve(workspacePath) : process.cwd();
|
|
6526
6604
|
const project = await loadWorkspaceProject(target);
|
|
6527
6605
|
const auth2 = await loadStoredAuth();
|
|
6528
6606
|
render(
|
|
@@ -6544,7 +6622,7 @@ program.name("open-research").description("Local-first research CLI powered by C
|
|
|
6544
6622
|
});
|
|
6545
6623
|
program.command("init").argument("[workspacePath]").description("Initialize an Open Research workspace.").action(async (workspacePath) => {
|
|
6546
6624
|
await ensureOpenResearchConfig();
|
|
6547
|
-
const target =
|
|
6625
|
+
const target = path17.resolve(workspacePath ?? process.cwd());
|
|
6548
6626
|
const project = await initWorkspace({ workspaceDir: target });
|
|
6549
6627
|
console.log(`Initialized workspace: ${target}`);
|
|
6550
6628
|
console.log(`Title: ${project.title}`);
|
|
@@ -6613,8 +6691,8 @@ skills.command("create").argument("[name]").description("Scaffold a new user ski
|
|
|
6613
6691
|
});
|
|
6614
6692
|
skills.command("edit").argument("<name>").description("Open a user skill in $EDITOR.").action(async (name) => {
|
|
6615
6693
|
await ensureOpenResearchConfig();
|
|
6616
|
-
const skillDir =
|
|
6617
|
-
openInEditor(
|
|
6694
|
+
const skillDir = path17.join(getOpenResearchSkillsDir(), name);
|
|
6695
|
+
openInEditor(path17.join(skillDir, "SKILL.md"));
|
|
6618
6696
|
const validation = await validateSkillDirectory({ skillDir });
|
|
6619
6697
|
if (!validation.ok) {
|
|
6620
6698
|
console.error(validation.errors.join("\n"));
|
|
@@ -6625,9 +6703,9 @@ skills.command("edit").argument("<name>").description("Open a user skill in $EDI
|
|
|
6625
6703
|
});
|
|
6626
6704
|
skills.command("validate").argument("[nameOrPath]").description("Validate one user skill.").action(async (nameOrPath) => {
|
|
6627
6705
|
await ensureOpenResearchConfig();
|
|
6628
|
-
const skillDir = nameOrPath ?
|
|
6706
|
+
const skillDir = nameOrPath ? path17.isAbsolute(nameOrPath) ? nameOrPath : path17.join(getOpenResearchSkillsDir(), nameOrPath) : getOpenResearchSkillsDir();
|
|
6629
6707
|
const stat = await import("fs/promises").then(
|
|
6630
|
-
(
|
|
6708
|
+
(fs17) => fs17.stat(skillDir).catch(() => null)
|
|
6631
6709
|
);
|
|
6632
6710
|
if (!stat) {
|
|
6633
6711
|
throw new Error(`Skill path not found: ${skillDir}`);
|
package/package.json
CHANGED