open-research 0.1.17 → 0.1.18

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.
Files changed (2) hide show
  1. package/dist/cli.js +121 -35
  2. 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.17";
814
+ var PACKAGE_VERSION = "0.1.18";
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);
@@ -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
- return /* @__PURE__ */ jsxs2(Box3, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [
5715
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: "cyan", children: "Resume Session" }),
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: "single",
5743
+ borderStyle: "round",
5720
5744
  borderColor: filter ? "cyan" : "gray",
5721
5745
  paddingX: 1,
5722
5746
  marginTop: 1,
5723
5747
  marginBottom: 1,
5724
- children: /* @__PURE__ */ jsx3(Text3, { color: "gray", children: filter ? filter : "Type to search..." })
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, { color: isSelected ? "white" : "gray", bold: isSelected, children: preview.length > 70 ? preview.slice(0, 70) + "\u2026" : preview })
5740
- ] }),
5741
- isSelected && /* @__PURE__ */ jsx3(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsxs2(Text3, { color: "gray", dimColor: true, children: [
5742
- age,
5743
- " \xB7 ",
5744
- turns,
5745
- " \xB7 ",
5746
- session.id.slice(0, 8)
5747
- ] }) })
5748
- ] }, session.id);
5749
- }) }),
5750
- /* @__PURE__ */ jsx3(Box3, { marginTop: 1, children: /* @__PURE__ */ jsx3(Text3, { color: "gray", dimColor: true, children: "\u2191\u2193 navigate \xB7 Enter select \xB7 Type to search \xB7 Esc cancel" }) })
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 (e.g. /api-keys semantic-scholar YOUR_KEY)", 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" },
@@ -6374,7 +6416,7 @@ function getFileSuggestions(partial, files) {
6374
6416
  label: f.label
6375
6417
  }));
6376
6418
  }
6377
- function truncate2(value, max = 96) {
6419
+ function truncate3(value, max = 96) {
6378
6420
  return value.length <= max ? value : `${value.slice(0, max - 1).trimEnd()}\u2026`;
6379
6421
  }
6380
6422
 
@@ -7276,6 +7318,46 @@ ${msg.text}
7276
7318
  }
7277
7319
  break;
7278
7320
  }
7321
+ case "api-keys": {
7322
+ if (!args) {
7323
+ const ssKey = getSemanticScholarApiKey(config);
7324
+ const oaKey = getOpenAlexApiKey(config);
7325
+ addSystemMessage("API Keys:");
7326
+ addSystemMessage(` Semantic Scholar: ${ssKey ? ssKey.slice(0, 8) + "..." : "not set"}`);
7327
+ addSystemMessage(` OpenAlex: ${oaKey ? oaKey.slice(0, 8) + "..." : "not set"}`);
7328
+ addSystemMessage("");
7329
+ addSystemMessage("Set via CLI:");
7330
+ addSystemMessage(" /api-keys semantic-scholar YOUR_KEY");
7331
+ addSystemMessage(" /api-keys openalex YOUR_KEY");
7332
+ addSystemMessage("");
7333
+ addSystemMessage("Or set environment variables:");
7334
+ addSystemMessage(" export SEMANTIC_SCHOLAR_API_KEY=your_key");
7335
+ addSystemMessage(" export OPENALEX_API_KEY=your_key");
7336
+ break;
7337
+ }
7338
+ const [keyName, ...keyParts] = args.split(/\s+/);
7339
+ const keyValue = keyParts.join("").trim();
7340
+ if (!keyValue) {
7341
+ addSystemMessage("Usage: /api-keys <semantic-scholar|openalex> <key>");
7342
+ break;
7343
+ }
7344
+ if (config) {
7345
+ const apiKeys = config.apiKeys ?? {};
7346
+ if (keyName === "semantic-scholar" || keyName === "ss") {
7347
+ apiKeys.semanticScholar = keyValue;
7348
+ } else if (keyName === "openalex" || keyName === "oa") {
7349
+ apiKeys.openAlex = keyValue;
7350
+ } else {
7351
+ addSystemMessage(`Unknown key: ${keyName}. Use semantic-scholar or openalex.`);
7352
+ break;
7353
+ }
7354
+ const updated = { ...config, apiKeys };
7355
+ setConfig(updated);
7356
+ await saveOpenResearchConfig(updated, { homeDir });
7357
+ addSystemMessage(`${keyName} API key saved.`);
7358
+ }
7359
+ break;
7360
+ }
7279
7361
  case "doctor": {
7280
7362
  addSystemMessage("Running diagnostics...");
7281
7363
  const authResult = await getAuthStatus({ homeDir });
@@ -7283,6 +7365,10 @@ ${msg.text}
7283
7365
  addSystemMessage(` Workspace: ${workspacePath ? workspacePath : "none"}`);
7284
7366
  addSystemMessage(` Files: ${workspaceFiles.length}`);
7285
7367
  addSystemMessage(` Skills: ${skills2.length} loaded`);
7368
+ const ssKey = getSemanticScholarApiKey(config);
7369
+ const oaKey = getOpenAlexApiKey(config);
7370
+ addSystemMessage(` Semantic Scholar API: ${ssKey ? "configured" : "not set (rate-limited)"}`);
7371
+ addSystemMessage(` OpenAlex API: ${oaKey ? "configured" : "not set (limited)"}`);
7286
7372
  const mems = await loadAllMemories({ homeDir });
7287
7373
  addSystemMessage(` Memories: ${mems.length} stored`);
7288
7374
  addSystemMessage(` Node: ${process.version}`);
@@ -7912,7 +7998,7 @@ ${error.stack}` : String(error)}` }
7912
7998
  PendingUpdateCard,
7913
7999
  {
7914
8000
  count: deferredPendingUpdates.length,
7915
- summary: truncate2(deferredPendingUpdates[0].summary, 80)
8001
+ summary: truncate3(deferredPendingUpdates[0].summary, 80)
7916
8002
  }
7917
8003
  ),
7918
8004
  planningState.status === "charter-review" && planningState.charter && /* @__PURE__ */ jsxs4(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-research",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Local-first research CLI agent — discover papers, synthesize notes, run analysis, and draft artifacts from your terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",