open-research 0.1.17 → 0.1.19
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/cli.js +165 -37
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -811,7 +811,7 @@ function formatDateTime(value) {
|
|
|
811
811
|
}
|
|
812
812
|
|
|
813
813
|
// src/lib/cli/version.ts
|
|
814
|
-
var PACKAGE_VERSION = "0.1.
|
|
814
|
+
var PACKAGE_VERSION = "0.1.19";
|
|
815
815
|
function getPackageVersion() {
|
|
816
816
|
return PACKAGE_VERSION;
|
|
817
817
|
}
|
|
@@ -827,7 +827,11 @@ var openResearchConfigSchema = z.object({
|
|
|
827
827
|
editPolicy: z.literal("mixed")
|
|
828
828
|
}),
|
|
829
829
|
theme: z.enum(themeValues).default("dark"),
|
|
830
|
-
lastWorkspace: z.string().nullable()
|
|
830
|
+
lastWorkspace: z.string().nullable(),
|
|
831
|
+
apiKeys: z.object({
|
|
832
|
+
semanticScholar: z.string().optional(),
|
|
833
|
+
openAlex: z.string().optional()
|
|
834
|
+
}).optional()
|
|
831
835
|
});
|
|
832
836
|
var DEFAULT_OPEN_RESEARCH_CONFIG = {
|
|
833
837
|
version: 1,
|
|
@@ -837,8 +841,15 @@ var DEFAULT_OPEN_RESEARCH_CONFIG = {
|
|
|
837
841
|
editPolicy: "mixed"
|
|
838
842
|
},
|
|
839
843
|
theme: "dark",
|
|
840
|
-
lastWorkspace: null
|
|
844
|
+
lastWorkspace: null,
|
|
845
|
+
apiKeys: {}
|
|
841
846
|
};
|
|
847
|
+
function getSemanticScholarApiKey(config) {
|
|
848
|
+
return config?.apiKeys?.semanticScholar || process.env.SEMANTIC_SCHOLAR_API_KEY;
|
|
849
|
+
}
|
|
850
|
+
function getOpenAlexApiKey(config) {
|
|
851
|
+
return config?.apiKeys?.openAlex || process.env.OPENALEX_API_KEY;
|
|
852
|
+
}
|
|
842
853
|
async function loadOpenResearchConfig(options) {
|
|
843
854
|
const configFile = getOpenResearchConfigFile(options);
|
|
844
855
|
const config = await readJsonFile(configFile, null);
|
|
@@ -1488,7 +1499,7 @@ function TextInput({
|
|
|
1488
1499
|
} else if (key.rightArrow) {
|
|
1489
1500
|
if (showCursor) nextCursor++;
|
|
1490
1501
|
} else if (!key.ctrl && !key.meta) {
|
|
1491
|
-
const clean = input2.replace(/\x1b\[[?>=!]*[0-9;]*[a-zA-Z]/g, "").replace(/[\x00-\x08\x0e-\x1f]/g, "");
|
|
1502
|
+
const clean = input2.replace(/\x1b\[[?>=!]*[0-9;]*[a-zA-Z~]/g, "").replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)?/g, "").replace(/\[20[01]~/g, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "");
|
|
1492
1503
|
if (clean) {
|
|
1493
1504
|
nextValue = originalValue.slice(0, cursorOffset) + clean + originalValue.slice(cursorOffset);
|
|
1494
1505
|
nextCursor += clean.length;
|
|
@@ -4370,11 +4381,14 @@ async function executeSearchExternalSources(args, ctx) {
|
|
|
4370
4381
|
}
|
|
4371
4382
|
const primary = searches[0];
|
|
4372
4383
|
const variations = searches.slice(1).map((item) => item.query);
|
|
4384
|
+
const config = await loadOpenResearchConfig().catch(() => null);
|
|
4373
4385
|
const results = await discoverScholarlySources({
|
|
4374
4386
|
query: primary.query,
|
|
4375
4387
|
queryVariations: variations,
|
|
4376
4388
|
numResults: args.num_results ?? 8,
|
|
4377
|
-
filters: ctx.searchFilters
|
|
4389
|
+
filters: ctx.searchFilters,
|
|
4390
|
+
semanticScholarApiKey: getSemanticScholarApiKey(config),
|
|
4391
|
+
openAlexApiKey: getOpenAlexApiKey(config)
|
|
4378
4392
|
});
|
|
4379
4393
|
const summary = results.map((result, index) => `${index + 1}. ${result.title} [${result.provider}] ${result.url}`).join("\n");
|
|
4380
4394
|
return {
|
|
@@ -5668,6 +5682,9 @@ function timeAgo(dateStr) {
|
|
|
5668
5682
|
if (days < 30) return `${days}d ago`;
|
|
5669
5683
|
return new Date(dateStr).toLocaleDateString();
|
|
5670
5684
|
}
|
|
5685
|
+
function truncate2(text, max) {
|
|
5686
|
+
return text.length > max ? text.slice(0, max) + "\u2026" : text;
|
|
5687
|
+
}
|
|
5671
5688
|
function SessionPicker({ sessions, onSelect, onCancel }) {
|
|
5672
5689
|
const [filter, setFilter] = useState3("");
|
|
5673
5690
|
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
@@ -5711,43 +5728,67 @@ function SessionPicker({ sessions, onSelect, onCancel }) {
|
|
|
5711
5728
|
setSelectedIndex(0);
|
|
5712
5729
|
}
|
|
5713
5730
|
});
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
/* @__PURE__ */ jsx3(
|
|
5731
|
+
const width = Math.max(60, (process.stdout.columns ?? 80) - 6);
|
|
5732
|
+
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
|
|
5733
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "/resume" }),
|
|
5734
|
+
/* @__PURE__ */ jsxs2(Text3, { color: "gray", dimColor: true, children: [
|
|
5735
|
+
sessions.length,
|
|
5736
|
+
" session",
|
|
5737
|
+
sessions.length !== 1 ? "s" : "",
|
|
5738
|
+
" in this workspace"
|
|
5739
|
+
] }),
|
|
5740
|
+
/* @__PURE__ */ jsxs2(
|
|
5717
5741
|
Box3,
|
|
5718
5742
|
{
|
|
5719
|
-
borderStyle: "
|
|
5743
|
+
borderStyle: "round",
|
|
5720
5744
|
borderColor: filter ? "cyan" : "gray",
|
|
5721
5745
|
paddingX: 1,
|
|
5722
5746
|
marginTop: 1,
|
|
5723
5747
|
marginBottom: 1,
|
|
5724
|
-
children:
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
filtered.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: "gray", children: filter ? "No matching sessions." : "No sessions found." }) : /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: filtered.slice(0, 15).map((session, idx) => {
|
|
5728
|
-
const isSelected = idx === clampedIndex;
|
|
5729
|
-
const indicator = isSelected ? "\u203A" : " ";
|
|
5730
|
-
const preview = session.preview || "(empty session)";
|
|
5731
|
-
const age = timeAgo(session.lastActivity);
|
|
5732
|
-
const turns = `${session.turnCount} turn${session.turnCount !== 1 ? "s" : ""}`;
|
|
5733
|
-
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginBottom: isSelected ? 1 : 0, children: [
|
|
5734
|
-
/* @__PURE__ */ jsxs2(Box3, { children: [
|
|
5735
|
-
/* @__PURE__ */ jsxs2(Text3, { color: isSelected ? "cyan" : "gray", children: [
|
|
5736
|
-
indicator,
|
|
5748
|
+
children: [
|
|
5749
|
+
/* @__PURE__ */ jsxs2(Text3, { color: "gray", children: [
|
|
5750
|
+
"\u2315",
|
|
5737
5751
|
" "
|
|
5738
5752
|
] }),
|
|
5739
|
-
/* @__PURE__ */ jsx3(Text3, {
|
|
5740
|
-
]
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5753
|
+
/* @__PURE__ */ jsx3(Text3, { children: filter || /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Search sessions..." }) })
|
|
5754
|
+
]
|
|
5755
|
+
}
|
|
5756
|
+
),
|
|
5757
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx3(Text3, { color: "gray", children: filter ? "No matching sessions." : "No sessions found." }) : /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", children: [
|
|
5758
|
+
filtered.slice(0, 12).map((session, idx) => {
|
|
5759
|
+
const isSelected = idx === clampedIndex;
|
|
5760
|
+
const preview = session.preview || "(empty session)";
|
|
5761
|
+
const age = timeAgo(session.lastActivity);
|
|
5762
|
+
const turns = `${session.turnCount} turn${session.turnCount !== 1 ? "s" : ""}`;
|
|
5763
|
+
const meta = `${age} \xB7 ${turns}`;
|
|
5764
|
+
if (isSelected) {
|
|
5765
|
+
const line = ` \u203A ${truncate2(preview, width - meta.length - 8)}`;
|
|
5766
|
+
return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", marginBottom: 0, children: [
|
|
5767
|
+
/* @__PURE__ */ jsxs2(Box3, { children: [
|
|
5768
|
+
/* @__PURE__ */ jsx3(Text3, { inverse: true, bold: true, children: line }),
|
|
5769
|
+
/* @__PURE__ */ jsx3(Text3, { inverse: true, dimColor: true, children: ` ${meta} ` })
|
|
5770
|
+
] }),
|
|
5771
|
+
/* @__PURE__ */ jsx3(Box3, { marginLeft: 3, children: /* @__PURE__ */ jsxs2(Text3, { color: "gray", dimColor: true, children: [
|
|
5772
|
+
"id: ",
|
|
5773
|
+
session.id.slice(0, 8),
|
|
5774
|
+
" \\u00B7 started ",
|
|
5775
|
+
new Date(session.startedAt).toLocaleString()
|
|
5776
|
+
] }) })
|
|
5777
|
+
] }, session.id);
|
|
5778
|
+
}
|
|
5779
|
+
return /* @__PURE__ */ jsxs2(Box3, { children: [
|
|
5780
|
+
/* @__PURE__ */ jsx3(Text3, { color: "gray", children: " " }),
|
|
5781
|
+
/* @__PURE__ */ jsx3(Text3, { children: truncate2(preview, width - meta.length - 8) }),
|
|
5782
|
+
/* @__PURE__ */ jsx3(Text3, { color: "gray", dimColor: true, children: ` ${meta}` })
|
|
5783
|
+
] }, session.id);
|
|
5784
|
+
}),
|
|
5785
|
+
filtered.length > 12 && /* @__PURE__ */ jsxs2(Text3, { color: "gray", dimColor: true, children: [
|
|
5786
|
+
" \\u2193 ",
|
|
5787
|
+
filtered.length - 12,
|
|
5788
|
+
" more below"
|
|
5789
|
+
] })
|
|
5790
|
+
] }),
|
|
5791
|
+
/* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { color: "gray", dimColor: true, children: "\\u2191/\\u2193 to select \\u00B7 Enter resume \\u00B7 Type to filter \\u00B7 Esc cancel" }) })
|
|
5751
5792
|
] });
|
|
5752
5793
|
}
|
|
5753
5794
|
|
|
@@ -6309,6 +6350,7 @@ var SLASH_COMMANDS = [
|
|
|
6309
6350
|
{ name: "btw", aliases: ["/aside"], description: "Ask a side question without affecting the main conversation", category: "session" },
|
|
6310
6351
|
{ name: "export", aliases: [], description: "Export conversation as markdown to a file", category: "session" },
|
|
6311
6352
|
{ name: "diff", aliases: ["/changes"], description: "Show files the agent has changed in this session", category: "workspace" },
|
|
6353
|
+
{ name: "api-keys", aliases: ["/keys"], description: "Set API keys for Semantic Scholar, OpenAlex", category: "system" },
|
|
6312
6354
|
{ name: "doctor", aliases: [], description: "Diagnose auth, connectivity, and tool availability", category: "system" },
|
|
6313
6355
|
{ name: "preview", aliases: [], description: "Live preview a LaTeX file in browser (e.g. /preview papers/draft.tex)", category: "workspace" },
|
|
6314
6356
|
{ name: "memory", aliases: ["/memories"], description: "View or clear stored memories about you", category: "system" },
|
|
@@ -6327,8 +6369,50 @@ function matchSlashCommand(input2) {
|
|
|
6327
6369
|
}
|
|
6328
6370
|
return null;
|
|
6329
6371
|
}
|
|
6372
|
+
var SUBCOMMAND_HINTS = {
|
|
6373
|
+
"api-keys": [
|
|
6374
|
+
{ name: "api-keys semantic-scholar <key>", description: "Set your Semantic Scholar API key" },
|
|
6375
|
+
{ name: "api-keys openalex <key>", description: "Set your OpenAlex API key" }
|
|
6376
|
+
],
|
|
6377
|
+
"config": [
|
|
6378
|
+
{ name: "config theme dark|light", description: "Set color theme" },
|
|
6379
|
+
{ name: "config auto-approve on|off", description: "Toggle auto-approve mode" }
|
|
6380
|
+
],
|
|
6381
|
+
"memory": [
|
|
6382
|
+
{ name: "memory clear", description: "Clear all global memories" },
|
|
6383
|
+
{ name: "memory clear project", description: "Clear project memories" },
|
|
6384
|
+
{ name: "memory clear all", description: "Clear everything" },
|
|
6385
|
+
{ name: "memory delete <id>", description: "Delete a specific memory" }
|
|
6386
|
+
],
|
|
6387
|
+
"compact": [
|
|
6388
|
+
{ name: "compact", description: "Compress conversation (auto-selects what to keep)" },
|
|
6389
|
+
{ name: "compact keep the statistical findings", description: "Compress but prioritize specific content" }
|
|
6390
|
+
],
|
|
6391
|
+
"export": [
|
|
6392
|
+
{ name: "export", description: "Export to conversation-export.md" },
|
|
6393
|
+
{ name: "export <filename>", description: "Export to a specific file" }
|
|
6394
|
+
],
|
|
6395
|
+
"preview": [
|
|
6396
|
+
{ name: "preview <path-to-tex>", description: "Live preview a LaTeX file in browser" }
|
|
6397
|
+
]
|
|
6398
|
+
};
|
|
6330
6399
|
function getUnifiedSuggestions(partial, allSkills) {
|
|
6331
6400
|
if (!partial.startsWith("/")) return [];
|
|
6401
|
+
if (partial.includes(" ")) {
|
|
6402
|
+
const spaceIdx = partial.indexOf(" ");
|
|
6403
|
+
const cmdPart = partial.slice(1, spaceIdx).toLowerCase();
|
|
6404
|
+
const hints = SUBCOMMAND_HINTS[cmdPart];
|
|
6405
|
+
if (hints) {
|
|
6406
|
+
const argPart = partial.slice(spaceIdx + 1).toLowerCase();
|
|
6407
|
+
const filtered = argPart ? hints.filter((h) => h.name.toLowerCase().includes(argPart)) : hints;
|
|
6408
|
+
return filtered.map((h) => ({
|
|
6409
|
+
kind: "command",
|
|
6410
|
+
name: h.name,
|
|
6411
|
+
description: h.description
|
|
6412
|
+
}));
|
|
6413
|
+
}
|
|
6414
|
+
return [];
|
|
6415
|
+
}
|
|
6332
6416
|
const search = partial.slice(1).toLowerCase();
|
|
6333
6417
|
if (!search) {
|
|
6334
6418
|
const cmds = SLASH_COMMANDS.map((c) => ({
|
|
@@ -6374,7 +6458,7 @@ function getFileSuggestions(partial, files) {
|
|
|
6374
6458
|
label: f.label
|
|
6375
6459
|
}));
|
|
6376
6460
|
}
|
|
6377
|
-
function
|
|
6461
|
+
function truncate3(value, max = 96) {
|
|
6378
6462
|
return value.length <= max ? value : `${value.slice(0, max - 1).trimEnd()}\u2026`;
|
|
6379
6463
|
}
|
|
6380
6464
|
|
|
@@ -6908,7 +6992,7 @@ function App({
|
|
|
6908
6992
|
if (atMention) {
|
|
6909
6993
|
return getFileSuggestions(atMention.partial, workspaceFiles);
|
|
6910
6994
|
}
|
|
6911
|
-
if (!input2.startsWith("/")
|
|
6995
|
+
if (!input2.startsWith("/")) return [];
|
|
6912
6996
|
return getUnifiedSuggestions(input2, skills2);
|
|
6913
6997
|
}, [input2, skills2, atMention, workspaceFiles]);
|
|
6914
6998
|
useEffect2(() => {
|
|
@@ -7276,6 +7360,46 @@ ${msg.text}
|
|
|
7276
7360
|
}
|
|
7277
7361
|
break;
|
|
7278
7362
|
}
|
|
7363
|
+
case "api-keys": {
|
|
7364
|
+
if (!args) {
|
|
7365
|
+
const ssKey = getSemanticScholarApiKey(config);
|
|
7366
|
+
const oaKey = getOpenAlexApiKey(config);
|
|
7367
|
+
addSystemMessage("API Keys:");
|
|
7368
|
+
addSystemMessage(` Semantic Scholar: ${ssKey ? ssKey.slice(0, 8) + "..." : "not set"}`);
|
|
7369
|
+
addSystemMessage(` OpenAlex: ${oaKey ? oaKey.slice(0, 8) + "..." : "not set"}`);
|
|
7370
|
+
addSystemMessage("");
|
|
7371
|
+
addSystemMessage("Set via CLI:");
|
|
7372
|
+
addSystemMessage(" /api-keys semantic-scholar YOUR_KEY");
|
|
7373
|
+
addSystemMessage(" /api-keys openalex YOUR_KEY");
|
|
7374
|
+
addSystemMessage("");
|
|
7375
|
+
addSystemMessage("Or set environment variables:");
|
|
7376
|
+
addSystemMessage(" export SEMANTIC_SCHOLAR_API_KEY=your_key");
|
|
7377
|
+
addSystemMessage(" export OPENALEX_API_KEY=your_key");
|
|
7378
|
+
break;
|
|
7379
|
+
}
|
|
7380
|
+
const [keyName, ...keyParts] = args.split(/\s+/);
|
|
7381
|
+
const keyValue = keyParts.join("").trim();
|
|
7382
|
+
if (!keyValue) {
|
|
7383
|
+
addSystemMessage("Usage: /api-keys <semantic-scholar|openalex> <key>");
|
|
7384
|
+
break;
|
|
7385
|
+
}
|
|
7386
|
+
if (config) {
|
|
7387
|
+
const apiKeys = config.apiKeys ?? {};
|
|
7388
|
+
if (keyName === "semantic-scholar" || keyName === "ss") {
|
|
7389
|
+
apiKeys.semanticScholar = keyValue;
|
|
7390
|
+
} else if (keyName === "openalex" || keyName === "oa") {
|
|
7391
|
+
apiKeys.openAlex = keyValue;
|
|
7392
|
+
} else {
|
|
7393
|
+
addSystemMessage(`Unknown key: ${keyName}. Use semantic-scholar or openalex.`);
|
|
7394
|
+
break;
|
|
7395
|
+
}
|
|
7396
|
+
const updated = { ...config, apiKeys };
|
|
7397
|
+
setConfig(updated);
|
|
7398
|
+
await saveOpenResearchConfig(updated, { homeDir });
|
|
7399
|
+
addSystemMessage(`${keyName} API key saved.`);
|
|
7400
|
+
}
|
|
7401
|
+
break;
|
|
7402
|
+
}
|
|
7279
7403
|
case "doctor": {
|
|
7280
7404
|
addSystemMessage("Running diagnostics...");
|
|
7281
7405
|
const authResult = await getAuthStatus({ homeDir });
|
|
@@ -7283,6 +7407,10 @@ ${msg.text}
|
|
|
7283
7407
|
addSystemMessage(` Workspace: ${workspacePath ? workspacePath : "none"}`);
|
|
7284
7408
|
addSystemMessage(` Files: ${workspaceFiles.length}`);
|
|
7285
7409
|
addSystemMessage(` Skills: ${skills2.length} loaded`);
|
|
7410
|
+
const ssKey = getSemanticScholarApiKey(config);
|
|
7411
|
+
const oaKey = getOpenAlexApiKey(config);
|
|
7412
|
+
addSystemMessage(` Semantic Scholar API: ${ssKey ? "configured" : "not set (rate-limited)"}`);
|
|
7413
|
+
addSystemMessage(` OpenAlex API: ${oaKey ? "configured" : "not set (limited)"}`);
|
|
7286
7414
|
const mems = await loadAllMemories({ homeDir });
|
|
7287
7415
|
addSystemMessage(` Memories: ${mems.length} stored`);
|
|
7288
7416
|
addSystemMessage(` Node: ${process.version}`);
|
|
@@ -7912,7 +8040,7 @@ ${error.stack}` : String(error)}` }
|
|
|
7912
8040
|
PendingUpdateCard,
|
|
7913
8041
|
{
|
|
7914
8042
|
count: deferredPendingUpdates.length,
|
|
7915
|
-
summary:
|
|
8043
|
+
summary: truncate3(deferredPendingUpdates[0].summary, 80)
|
|
7916
8044
|
}
|
|
7917
8045
|
),
|
|
7918
8046
|
planningState.status === "charter-review" && planningState.charter && /* @__PURE__ */ jsxs4(
|
package/package.json
CHANGED