open-research 1.0.0 → 1.0.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.
@@ -0,0 +1,185 @@
1
+ import {
2
+ readJsonFile,
3
+ writeJsonFile
4
+ } from "./chunk-77Q5B5H7.js";
5
+ import {
6
+ getOpenResearchAuthFile,
7
+ getOpenResearchGeminiAuthFile,
8
+ getOpenResearchRoot
9
+ } from "./chunk-4HCPHCC2.js";
10
+
11
+ // src/lib/auth/store.ts
12
+ import fs from "fs/promises";
13
+ var AUTH_FILE_MODE = 384;
14
+ async function ensureCliHome(options) {
15
+ const root = getOpenResearchRoot(options);
16
+ await fs.mkdir(root, { recursive: true, mode: 448 });
17
+ return root;
18
+ }
19
+ async function saveStoredAuth(auth, options) {
20
+ await ensureCliHome(options);
21
+ const authFile = getOpenResearchAuthFile(options);
22
+ await writeJsonFile(authFile, auth, AUTH_FILE_MODE);
23
+ await fs.chmod(authFile, AUTH_FILE_MODE);
24
+ return authFile;
25
+ }
26
+ async function loadStoredAuth(options) {
27
+ const authFile = getOpenResearchAuthFile(options);
28
+ return readJsonFile(authFile, null);
29
+ }
30
+ async function clearStoredAuth(options) {
31
+ const authFile = getOpenResearchAuthFile(options);
32
+ await fs.rm(authFile, { force: true });
33
+ }
34
+ async function saveGeminiAuth(auth, options) {
35
+ await ensureCliHome(options);
36
+ const authFile = getOpenResearchGeminiAuthFile(options);
37
+ await writeJsonFile(authFile, auth, AUTH_FILE_MODE);
38
+ await fs.chmod(authFile, AUTH_FILE_MODE);
39
+ return authFile;
40
+ }
41
+ async function loadGeminiAuth(options) {
42
+ const authFile = getOpenResearchGeminiAuthFile(options);
43
+ return readJsonFile(authFile, null);
44
+ }
45
+
46
+ // src/lib/auth/gemini-oauth.ts
47
+ var GEMINI_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
48
+ var GEMINI_TOKEN_URL = "https://oauth2.googleapis.com/token";
49
+ var GEMINI_USERINFO_URL = "https://www.googleapis.com/oauth2/v1/userinfo";
50
+ var GEMINI_CODE_ASSIST_URL = "https://cloudcode-pa.googleapis.com";
51
+ var GEMINI_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com";
52
+ var GEMINI_CLIENT_SECRET = "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";
53
+ var GEMINI_SCOPES = "https://www.googleapis.com/auth/cloud-platform openid email profile";
54
+ function getGeminiRedirectUri(port) {
55
+ return `http://127.0.0.1:${port}/oauth2callback`;
56
+ }
57
+ function buildGeminiAuthorizationUrl(input) {
58
+ const params = new URLSearchParams({
59
+ client_id: GEMINI_CLIENT_ID,
60
+ response_type: "code",
61
+ redirect_uri: getGeminiRedirectUri(input.port),
62
+ scope: GEMINI_SCOPES,
63
+ state: input.state,
64
+ access_type: "offline",
65
+ prompt: "consent"
66
+ });
67
+ return `${GEMINI_AUTH_URL}?${params}`;
68
+ }
69
+ async function exchangeGeminiCodeForTokens(code, redirectUri) {
70
+ const response = await fetch(GEMINI_TOKEN_URL, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
73
+ body: new URLSearchParams({
74
+ client_id: GEMINI_CLIENT_ID,
75
+ client_secret: GEMINI_CLIENT_SECRET,
76
+ code,
77
+ grant_type: "authorization_code",
78
+ redirect_uri: redirectUri
79
+ })
80
+ });
81
+ if (!response.ok) {
82
+ const text = await response.text();
83
+ throw new Error(`Gemini token exchange failed: ${response.status} ${text}`);
84
+ }
85
+ return response.json();
86
+ }
87
+ async function refreshGeminiAccessToken(refreshToken) {
88
+ const response = await fetch(GEMINI_TOKEN_URL, {
89
+ method: "POST",
90
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
91
+ body: new URLSearchParams({
92
+ client_id: GEMINI_CLIENT_ID,
93
+ client_secret: GEMINI_CLIENT_SECRET,
94
+ refresh_token: refreshToken,
95
+ grant_type: "refresh_token"
96
+ })
97
+ });
98
+ if (!response.ok) {
99
+ const text = await response.text();
100
+ throw new Error(`Gemini token refresh failed: ${response.status} ${text}`);
101
+ }
102
+ return response.json();
103
+ }
104
+ async function getGeminiUserEmail(accessToken) {
105
+ const response = await fetch(`${GEMINI_USERINFO_URL}?alt=json`, {
106
+ headers: { Authorization: `Bearer ${accessToken}` }
107
+ });
108
+ if (!response.ok) return "unknown";
109
+ const data = await response.json();
110
+ return data.email ?? "unknown";
111
+ }
112
+ async function loadCodeAssistProject(accessToken) {
113
+ const response = await fetch(`${GEMINI_CODE_ASSIST_URL}/v1internal:loadCodeAssist`, {
114
+ method: "POST",
115
+ headers: {
116
+ Authorization: `Bearer ${accessToken}`,
117
+ "Content-Type": "application/json"
118
+ },
119
+ body: JSON.stringify({
120
+ metadata: {
121
+ ideType: "IDE_UNSPECIFIED",
122
+ platform: "PLATFORM_UNSPECIFIED",
123
+ pluginType: "GEMINI"
124
+ }
125
+ })
126
+ });
127
+ if (!response.ok) {
128
+ const text = await response.text();
129
+ throw new Error(`loadCodeAssist failed: ${response.status} ${text}`);
130
+ }
131
+ const data = await response.json();
132
+ const project = data.cloudaicompanionProject;
133
+ if (typeof project === "string") return project;
134
+ if (project && typeof project === "object" && "id" in project) return project.id;
135
+ if (data.allowedTiers?.length) {
136
+ return onboardUser(accessToken);
137
+ }
138
+ throw new Error("Could not determine Gemini project ID. You may need a Google AI subscription.");
139
+ }
140
+ async function onboardUser(accessToken) {
141
+ const response = await fetch(`${GEMINI_CODE_ASSIST_URL}/v1internal:onboardUser`, {
142
+ method: "POST",
143
+ headers: {
144
+ Authorization: `Bearer ${accessToken}`,
145
+ "Content-Type": "application/json"
146
+ },
147
+ body: JSON.stringify({})
148
+ });
149
+ if (!response.ok) {
150
+ throw new Error(`Gemini onboarding failed: ${response.status}`);
151
+ }
152
+ const data = await response.json();
153
+ if (data.done && data.response?.cloudaicompanionProject) {
154
+ return data.response.cloudaicompanionProject;
155
+ }
156
+ if (data.name) {
157
+ for (let i = 0; i < 10; i++) {
158
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
159
+ const poll = await fetch(`${GEMINI_CODE_ASSIST_URL}/v1internal/${data.name}`, {
160
+ headers: { Authorization: `Bearer ${accessToken}` }
161
+ });
162
+ if (!poll.ok) continue;
163
+ const result = await poll.json();
164
+ if (result.done && result.response?.cloudaicompanionProject) {
165
+ return result.response.cloudaicompanionProject;
166
+ }
167
+ }
168
+ }
169
+ throw new Error("Gemini onboarding timed out. Try again.");
170
+ }
171
+
172
+ export {
173
+ saveStoredAuth,
174
+ loadStoredAuth,
175
+ clearStoredAuth,
176
+ saveGeminiAuth,
177
+ loadGeminiAuth,
178
+ GEMINI_CODE_ASSIST_URL,
179
+ getGeminiRedirectUri,
180
+ buildGeminiAuthorizationUrl,
181
+ exchangeGeminiCodeForTokens,
182
+ refreshGeminiAccessToken,
183
+ getGeminiUserEmail,
184
+ loadCodeAssistProject
185
+ };
@@ -1,38 +1,24 @@
1
+ import {
2
+ readJsonFile,
3
+ writeJsonFile
4
+ } from "./chunk-77Q5B5H7.js";
1
5
  import {
2
6
  getOpenResearchConfigFile
3
- } from "./chunk-I5NVYKG7.js";
7
+ } from "./chunk-4HCPHCC2.js";
8
+ import {
9
+ getProviderCatalog
10
+ } from "./chunk-GVEVKDGV.js";
4
11
 
5
12
  // src/lib/config/store.ts
6
13
  import { z } from "zod";
7
-
8
- // src/lib/fs/json.ts
9
- import fs from "fs/promises";
10
- import path from "path";
11
- async function readJsonFile(filePath, fallback) {
12
- try {
13
- const raw = await fs.readFile(filePath, "utf8");
14
- return JSON.parse(raw);
15
- } catch (error) {
16
- const code = typeof error === "object" && error && "code" in error ? String(error.code) : "";
17
- if (code === "ENOENT") {
18
- return fallback;
19
- }
20
- throw error;
21
- }
22
- }
23
- async function writeJsonFile(filePath, value, mode) {
24
- await fs.mkdir(path.dirname(filePath), { recursive: true });
25
- await fs.writeFile(filePath, JSON.stringify(value, null, 2), {
26
- encoding: "utf8",
27
- mode
28
- });
29
- }
30
-
31
- // src/lib/config/store.ts
32
14
  var themeValues = ["dark", "light"];
15
+ var providerValues = ["openai", "gemini"];
33
16
  var openAIProviderConfigSchema = z.object({
34
17
  apiKey: z.string().optional()
35
18
  }).optional();
19
+ var geminiProviderConfigSchema = z.object({
20
+ apiKey: z.string().optional()
21
+ }).optional();
36
22
  var openResearchConfigSchema = z.object({
37
23
  version: z.literal(1),
38
24
  defaults: z.object({
@@ -41,15 +27,18 @@ var openResearchConfigSchema = z.object({
41
27
  editPolicy: z.literal("mixed")
42
28
  }),
43
29
  theme: z.enum(themeValues).default("dark"),
30
+ activeProvider: z.enum(providerValues).default("openai"),
44
31
  lastWorkspace: z.string().nullable(),
45
32
  providers: z.object({
46
- openai: openAIProviderConfigSchema
33
+ openai: openAIProviderConfigSchema,
34
+ gemini: geminiProviderConfigSchema
47
35
  }).optional(),
48
36
  apiKeys: z.object({
49
37
  openai: z.string().optional(),
50
38
  semanticScholar: z.string().optional(),
51
39
  openAlex: z.string().optional(),
52
- brave: z.string().optional()
40
+ brave: z.string().optional(),
41
+ gemini: z.string().optional()
53
42
  }).optional()
54
43
  });
55
44
  var DEFAULT_OPEN_RESEARCH_CONFIG = {
@@ -60,9 +49,11 @@ var DEFAULT_OPEN_RESEARCH_CONFIG = {
60
49
  editPolicy: "mixed"
61
50
  },
62
51
  theme: "dark",
52
+ activeProvider: "openai",
63
53
  lastWorkspace: null,
64
54
  providers: {
65
- openai: {}
55
+ openai: {},
56
+ gemini: {}
66
57
  },
67
58
  apiKeys: {}
68
59
  };
@@ -235,10 +226,10 @@ async function executeFetchUrl(args, signal) {
235
226
  }
236
227
 
237
228
  // src/lib/fs/pdf.ts
238
- import fs2 from "fs/promises";
229
+ import fs from "fs/promises";
239
230
  async function extractPdfText(filePath, options) {
240
231
  const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
241
- const buffer = await fs2.readFile(filePath);
232
+ const buffer = await fs.readFile(filePath);
242
233
  const document = await pdfjs.getDocument({ data: new Uint8Array(buffer) }).promise;
243
234
  const totalPages = document.numPages;
244
235
  const start = Math.max(1, options?.startPage ?? 1);
@@ -433,7 +424,7 @@ async function extractWithTarget(input, provider) {
433
424
  { role: "system", content: systemPrompt },
434
425
  { role: "user", content: truncatedContent }
435
426
  ],
436
- model: "gpt-5.4-mini",
427
+ model: getProviderCatalog(provider.kind).backgroundModel,
437
428
  temperature: 0,
438
429
  maxTokens: 1500,
439
430
  jsonSchema: EXTRACTION_SCHEMA
@@ -496,8 +487,6 @@ function formatExtractionResults(sources) {
496
487
  }
497
488
 
498
489
  export {
499
- readJsonFile,
500
- writeJsonFile,
501
490
  extractPdfText,
502
491
  themeValues,
503
492
  getConfiguredOpenAIApiKey,
@@ -10,6 +10,9 @@ function getOpenResearchRoot(options) {
10
10
  function getOpenResearchAuthFile(options) {
11
11
  return path.join(getOpenResearchRoot(options), "auth.json");
12
12
  }
13
+ function getOpenResearchGeminiAuthFile(options) {
14
+ return path.join(getOpenResearchRoot(options), "gemini-auth.json");
15
+ }
13
16
  function getOpenResearchConfigFile(options) {
14
17
  return path.join(getOpenResearchRoot(options), "config.json");
15
18
  }
@@ -29,6 +32,7 @@ function getWorkspaceSessionsDir(workspaceDir) {
29
32
  export {
30
33
  getOpenResearchRoot,
31
34
  getOpenResearchAuthFile,
35
+ getOpenResearchGeminiAuthFile,
32
36
  getOpenResearchConfigFile,
33
37
  getOpenResearchSkillsDir,
34
38
  getWorkspaceMetaDir,
@@ -0,0 +1,27 @@
1
+ // src/lib/fs/json.ts
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ async function readJsonFile(filePath, fallback) {
5
+ try {
6
+ const raw = await fs.readFile(filePath, "utf8");
7
+ return JSON.parse(raw);
8
+ } catch (error) {
9
+ const code = typeof error === "object" && error && "code" in error ? String(error.code) : "";
10
+ if (code === "ENOENT") {
11
+ return fallback;
12
+ }
13
+ throw error;
14
+ }
15
+ }
16
+ async function writeJsonFile(filePath, value, mode) {
17
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
18
+ await fs.writeFile(filePath, JSON.stringify(value, null, 2), {
19
+ encoding: "utf8",
20
+ mode
21
+ });
22
+ }
23
+
24
+ export {
25
+ readJsonFile,
26
+ writeJsonFile
27
+ };
@@ -0,0 +1,68 @@
1
+ // src/lib/llm/provider-catalog.ts
2
+ var OPENAI_PROVIDER_MODELS = [
3
+ "gpt-5.4",
4
+ "gpt-5.4-mini",
5
+ "o3",
6
+ "o4-mini"
7
+ ];
8
+ var OPENAI_CATALOG = {
9
+ family: "openai",
10
+ displayName: "OpenAI",
11
+ models: OPENAI_PROVIDER_MODELS,
12
+ defaultModel: "gpt-5.4",
13
+ backgroundModel: "gpt-5.4-mini"
14
+ };
15
+ var GEMINI_PROVIDER_MODELS = [
16
+ "gemini-3.1-pro-preview",
17
+ "gemini-3-flash-preview"
18
+ ];
19
+ var GEMINI_CATALOG = {
20
+ family: "gemini",
21
+ displayName: "Gemini",
22
+ models: GEMINI_PROVIDER_MODELS,
23
+ defaultModel: "gemini-3.1-pro-preview",
24
+ backgroundModel: "gemini-3-flash-preview"
25
+ };
26
+ function getProviderCatalog(providerKind) {
27
+ switch (providerKind) {
28
+ case "gemini_auth":
29
+ case "gemini_api_key":
30
+ return GEMINI_CATALOG;
31
+ case "openai_auth":
32
+ case "openai_api_key":
33
+ default:
34
+ return OPENAI_CATALOG;
35
+ }
36
+ }
37
+ function getAvailableModels(providerKind) {
38
+ return getProviderCatalog(providerKind).models;
39
+ }
40
+ function isSupportedModel(model, providerKind) {
41
+ if (!model) return false;
42
+ return getAvailableModels(providerKind).includes(model);
43
+ }
44
+ function getDefaultModel(providerKind) {
45
+ return getProviderCatalog(providerKind).defaultModel;
46
+ }
47
+ function selectModelForTask(providerKind, requestedModel, task) {
48
+ const catalog = getProviderCatalog(providerKind);
49
+ const selected = isSupportedModel(requestedModel, providerKind) ? requestedModel : catalog.defaultModel;
50
+ switch (task) {
51
+ case "conversation":
52
+ return selected;
53
+ case "compaction":
54
+ return selected.includes("5.4") || selected.includes("pro") ? catalog.backgroundModel : selected;
55
+ case "memory":
56
+ case "workspace":
57
+ return catalog.backgroundModel;
58
+ default:
59
+ return selected;
60
+ }
61
+ }
62
+
63
+ export {
64
+ getProviderCatalog,
65
+ getAvailableModels,
66
+ getDefaultModel,
67
+ selectModelForTask
68
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getWorkspaceSessionsDir
3
- } from "./chunk-I5NVYKG7.js";
3
+ } from "./chunk-4HCPHCC2.js";
4
4
 
5
5
  // src/lib/workspace/sessions.ts
6
6
  import fs from "fs/promises";