@openexpert/opencore 0.2.1 → 0.3.0

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 CHANGED
@@ -10,7 +10,7 @@ edición cloud.
10
10
  - `src/config.ts` — lee `openexpert.json` + `OPENEXPERT_*`.
11
11
  - `src/storage.ts` — interfaz de guardado local.
12
12
  - `src/secrets.ts` — interfaz de tokens de Drive (fichero local, `0600`).
13
- - `src/tools.ts` — catálogo de las 14 herramientas y sus dominios.
13
+ - `src/tools.ts` — catálogo de las 12 herramientas y sus dominios.
14
14
  - `src/cli/` — el CLI `opencore` (asistente, doctor, fix, models, serve, update).
15
15
 
16
16
  ## Uso
package/bin/opencore.js CHANGED
@@ -13,7 +13,7 @@ const dist = join(here, "..", "dist", "cli.js");
13
13
 
14
14
  if (!existsSync(dist)) {
15
15
  console.error(
16
- "OpenCore CLI is not built yet.\n" +
16
+ "OpenExpert CLI is not built yet.\n" +
17
17
  "Run: npm run build --workspace @openexpert/opencore\n" +
18
18
  "(or use `npm run opencore:doctor` from the repository root).",
19
19
  );
package/dist/cli.js CHANGED
@@ -9,8 +9,14 @@ import * as p from '@clack/prompts';
9
9
  // src/cli/commands.ts
10
10
 
11
11
  // src/model-provider.ts
12
+ function stripTrailingSlashes(value) {
13
+ let end = value.length;
14
+ while (end > 0 && value[end - 1] === "/") end -= 1;
15
+ return value.slice(0, end);
16
+ }
12
17
  function ollamaApiBaseUrl(baseUrl) {
13
- return baseUrl.trim().replace(/\/+$/, "").replace(/\/v1$/, "");
18
+ const base = stripTrailingSlashes(baseUrl.trim());
19
+ return base.endsWith("/v1") ? base.slice(0, -3) : base;
14
20
  }
15
21
  var DEFAULT_OLLAMA_URL = "http://localhost:11434";
16
22
  var DEFAULT_DATA_DIR = "~/.openexpert";
@@ -164,7 +170,7 @@ function run(cmd, args, opts = {}) {
164
170
 
165
171
  // src/cli/commands.ts
166
172
  function cmdVersion() {
167
- console.log(`@openexpert/opencore ${packageVersion()}`);
173
+ console.log(`OpenExpert ${packageVersion()}`);
168
174
  return 0;
169
175
  }
170
176
  function configuredProvider() {
@@ -176,12 +182,12 @@ function cmdDoctor(cwd = process.cwd()) {
176
182
  const provider = configuredProvider();
177
183
  const dataDir = resolvedDataDir(cwd);
178
184
  const secrets = readSecrets(cwd);
179
- console.log("OpenCore doctor");
185
+ console.log("OpenExpert doctor");
180
186
  console.log(` provider: ${provider}`);
181
187
  console.log(` data dir: ${dataDir}`);
182
188
  const problems = [];
183
189
  if (provider === "google" && !process.env["GOOGLE_API_KEY"] && !secrets.GOOGLE_API_KEY) {
184
- problems.push("Falta GOOGLE_API_KEY. Ejecuta `opencore init` o usa Ollama.");
190
+ problems.push("Falta GOOGLE_API_KEY. Ejecuta `openexpert init` o usa Ollama.");
185
191
  }
186
192
  if (provider === "openai-compatible") {
187
193
  if (!process.env["OPENEXPERT_MODEL_KEY"] && !secrets.OPENEXPERT_MODEL_KEY) {
@@ -236,7 +242,7 @@ async function cmdFix(cwd = process.cwd()) {
236
242
  }
237
243
  console.log("\u2713 directorio de datos listo:", dataDir);
238
244
  cmdDoctor(cwd);
239
- console.log("\nSiguiente paso: `opencore serve` (o `opencore init` para elegir modelo).");
245
+ console.log("\nSiguiente paso: `openexpert serve` (o `openexpert init` para elegir modelo).");
240
246
  return 0;
241
247
  }
242
248
  async function cmdModels(cwd = process.cwd()) {
@@ -262,7 +268,7 @@ async function cmdModels(cwd = process.cwd()) {
262
268
  const base = process.env["OPENEXPERT_BASE_URL"] || secrets.OPENEXPERT_BASE_URL;
263
269
  const key = process.env["OPENEXPERT_MODEL_KEY"] || secrets.OPENEXPERT_MODEL_KEY;
264
270
  if (!base || !key) {
265
- console.log("Configura OPENEXPERT_BASE_URL y OPENEXPERT_MODEL_KEY (opencore init).");
271
+ console.log("Configura OPENEXPERT_BASE_URL y OPENEXPERT_MODEL_KEY (openexpert init).");
266
272
  return 1;
267
273
  }
268
274
  const data = await httpGetJson(
@@ -295,7 +301,7 @@ async function cmdServe(cwd = process.cwd()) {
295
301
  );
296
302
  return 1;
297
303
  }
298
- console.log("Arrancando OpenCore en http://localhost:3000 \u2026");
304
+ console.log("Arrancando OpenExpert en http://localhost:3000 \u2026");
299
305
  return run(process.execPath, [join(app, ".output", "server", "index.mjs")], {
300
306
  cwd: app,
301
307
  env: { ...env, PORT: process.env["PORT"] || "3000" }
@@ -469,7 +475,7 @@ async function runWizard(opts = {}) {
469
475
  writeConfig(config2, cwd);
470
476
  return { config: config2, secrets: {}, provider: "ollama", modelId: config2.modelId, started: false };
471
477
  }
472
- p.intro("OpenCore \u2014 configuraci\xF3n");
478
+ p.intro("OpenExpert \u2014 configuraci\xF3n");
473
479
  const spin = p.spinner();
474
480
  spin.start("Buscando Ollama local\u2026");
475
481
  const ollama = await detectOllama();
@@ -494,7 +500,7 @@ async function runWizard(opts = {}) {
494
500
  if (p.isCancel(choice)) bail();
495
501
  if (choice === "install-ollama") {
496
502
  p.note(
497
- "Instala Ollama y descarga un modelo:\n\n curl -fsSL https://ollama.com/install.sh | sh\n ollama pull llama3.1\n\nVuelve a ejecutar `opencore init` cuando est\xE9 listo.",
503
+ "Instala Ollama y descarga un modelo:\n\n curl -fsSL https://ollama.com/install.sh | sh\n ollama pull llama3.1\n\nVuelve a ejecutar `openexpert init` cuando est\xE9 listo.",
498
504
  "Instalar Ollama"
499
505
  );
500
506
  p.outro("Sin cambios.");
@@ -554,18 +560,18 @@ async function runWizard(opts = {}) {
554
560
 
555
561
  // src/cli.ts
556
562
  function help() {
557
- console.log(`opencore \u2014 motor abierto de OpenExpert
563
+ console.log(`OpenExpert \u2014 motor local
558
564
 
559
565
  Uso:
560
- opencore asistente de configuraci\xF3n y arranque
561
- opencore init igual que sin argumentos
562
- opencore serve arranca la edici\xF3n local en http://localhost:3000
563
- opencore desktop arranca el servidor y abre la ventana de escritorio
564
- opencore doctor revisa tu configuraci\xF3n
565
- opencore fix auto-configura lo que falte
566
- opencore models lista los modelos del proveedor actual
567
- opencore update limpia la cach\xE9 del servidor descargado
568
- opencore version muestra la versi\xF3n
566
+ openexpert asistente de configuraci\xF3n y arranque
567
+ openexpert init igual que sin argumentos
568
+ openexpert serve arranca la edici\xF3n local en http://localhost:3000
569
+ openexpert desktop arranca el servidor y abre la ventana de escritorio
570
+ openexpert doctor revisa tu configuraci\xF3n
571
+ openexpert fix auto-configura lo que falte
572
+ openexpert models lista los modelos del proveedor actual
573
+ openexpert update limpia la cach\xE9 del servidor descargado
574
+ openexpert version muestra la versi\xF3n
569
575
 
570
576
  Ejemplos:
571
577
  npx @openexpert/opencore
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/model-provider.ts","../src/cli/lib.ts","../src/cli/commands.ts","../src/cli/desktop.ts","../src/cli/wizard.ts","../src/cli.ts"],"names":["p","mkdirSync","existsSync","join","writeFileSync","spawn","homedir","resolve","ollama","config","run"],"mappings":"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","file":"cli.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — which model to use. The harness is stable; the model is configuration.\n//\n// Providers:\n// - google: Gemini via GOOGLE_API_KEY (free API key).\n// - ollama: Local model via Ollama's OpenAI-compatible endpoint.\n// - openai-compatible: Any OpenAI-compatible endpoint with your own key.\n\nexport type ModelProviderId = \"google\" | \"ollama\" | \"openai-compatible\";\n\nexport type ModelSelection = {\n provider: ModelProviderId;\n modelId: string;\n /** Where the key comes from, without exposing the value. */\n keySource: \"GOOGLE_API_KEY\" | \"OPENEXPERT_MODEL_KEY\" | \"none (local)\";\n};\n\nexport function selectModel(env: NodeJS.ProcessEnv = process.env): ModelSelection {\n const raw = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const provider: ModelProviderId =\n raw === \"ollama\" || raw === \"openai-compatible\" ? raw : \"google\";\n\n if (provider === \"ollama\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"llama3.1\",\n keySource: \"none (local)\",\n };\n }\n if (provider === \"openai-compatible\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gpt-4o-mini\",\n keySource: \"OPENEXPERT_MODEL_KEY\",\n };\n }\n return {\n provider: \"google\",\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gemini-2.5-flash\",\n keySource: \"GOOGLE_API_KEY\",\n };\n}\n\n/**\n * Ollama exposes two surfaces: the native API at the server root (`/api/*`)\n * and an OpenAI-compatible API under `/v1`. Callers may configure either\n * shape, so normalise before using it.\n */\nexport function ollamaApiBaseUrl(baseUrl: string): string {\n return baseUrl.trim().replace(/\\/+$/, \"\").replace(/\\/v1$/, \"\");\n}\n\nexport function ollamaChatBaseUrl(baseUrl: string): string {\n return `${ollamaApiBaseUrl(baseUrl)}/v1`;\n}\n\nexport function missingKeyHint(\n sel: ModelSelection,\n env: NodeJS.ProcessEnv = process.env,\n): string | null {\n if (sel.provider === \"ollama\") return null;\n if (sel.provider === \"google\" && !env[\"GOOGLE_API_KEY\"]) {\n return \"Falta GOOGLE_API_KEY (o usa OPENEXPERT_MODEL_PROVIDER=ollama para modelo local).\";\n }\n if (sel.provider === \"openai-compatible\" && !env[\"OPENEXPERT_MODEL_KEY\"]) {\n return \"Falta OPENEXPERT_MODEL_KEY para tu endpoint OpenAI-compatible.\";\n }\n return null;\n}\n","// SPDX-License-Identifier: MIT\n// Shared helpers for the OpenCore CLI: paths, config/secrets I/O, HTTP\n// detection, and release download with checksum verification.\n\nimport { spawn, spawnSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { ollamaApiBaseUrl } from \"../model-provider.js\";\n\nexport type Provider = \"google\" | \"ollama\" | \"openai-compatible\";\n\nexport type FileConfig = {\n modelProvider?: Provider;\n modelId?: string;\n ollamaBaseUrl?: string;\n dataDir?: string;\n};\n\nexport type Secrets = {\n GOOGLE_API_KEY?: string;\n GOOGLE_CLIENT_ID?: string;\n GOOGLE_CLIENT_SECRET?: string;\n OPENEXPERT_MODEL_KEY?: string;\n OPENEXPERT_BASE_URL?: string;\n};\n\nexport const DEFAULT_OLLAMA_URL = \"http://localhost:11434\";\nexport const DEFAULT_DATA_DIR = \"~/.openexpert\";\n\nexport function expandHome(p: string): string {\n if (p === \"~\") return homedir();\n if (p.startsWith(\"~/\")) return join(homedir(), p.slice(2));\n return p;\n}\n\nexport function packageRoot(): string {\n // dist/cli.js -> package root is one level up.\n return resolve(dirname(fileURLToPath(import.meta.url)), \"..\");\n}\n\nexport function packageVersion(): string {\n try {\n const pkg = JSON.parse(readFileSync(join(packageRoot(), \"package.json\"), \"utf8\")) as {\n version?: string;\n };\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n/** Walk up from `start` to find a checkout (package.json + packages/opencore). */\nexport function findRepoRoot(start = process.cwd()): string | null {\n let dir = resolve(start);\n for (let i = 0; i < 8; i++) {\n if (\n existsSync(join(dir, \"package.json\")) &&\n existsSync(join(dir, \"packages\", \"opencore\")) &&\n existsSync(join(dir, \"vite.config.ts\"))\n ) {\n return dir;\n }\n const parent = resolve(dir, \"..\");\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nexport function configPath(cwd = process.cwd()): string {\n return join(cwd, \"openexpert.json\");\n}\n\nexport function readConfig(cwd = process.cwd()): FileConfig {\n try {\n const p = configPath(cwd);\n if (!existsSync(p)) return {};\n return JSON.parse(readFileSync(p, \"utf8\")) as FileConfig;\n } catch {\n return {};\n }\n}\n\nexport function writeConfig(cfg: FileConfig, cwd = process.cwd()): void {\n writeFileSync(configPath(cwd), JSON.stringify(cfg, null, 2) + \"\\n\");\n}\n\nexport function resolvedDataDir(cwd = process.cwd()): string {\n const cfg = readConfig(cwd);\n const raw = process.env[\"OPENEXPERT_DATA_DIR\"] || cfg.dataDir || DEFAULT_DATA_DIR;\n return expandHome(raw);\n}\n\nexport function secretsPath(cwd = process.cwd()): string {\n return join(resolvedDataDir(cwd), \"secrets.json\");\n}\n\nexport function readSecrets(cwd = process.cwd()): Secrets {\n try {\n const p = secretsPath(cwd);\n if (!existsSync(p)) return {};\n return JSON.parse(readFileSync(p, \"utf8\")) as Secrets;\n } catch {\n return {};\n }\n}\n\nexport function writeSecrets(secrets: Secrets, cwd = process.cwd()): string {\n const dir = resolvedDataDir(cwd);\n mkdirSync(dir, { recursive: true });\n const p = join(dir, \"secrets.json\");\n writeFileSync(p, JSON.stringify(secrets, null, 2), { mode: 0o600 });\n return p;\n}\n\n/** Merge stored secrets into the current environment for the child server. */\nexport function secretsAsEnv(cwd = process.cwd()): Record<string, string> {\n const s = readSecrets(cwd);\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(s)) if (v) out[k] = v;\n return out;\n}\n\nexport async function httpGetJson<T>(\n url: string,\n opts: { headers?: Record<string, string>; timeoutMs?: number } = {},\n): Promise<T | null> {\n try {\n const res = await fetch(url, {\n ...(opts.headers ? { headers: opts.headers } : {}),\n signal: AbortSignal.timeout(opts.timeoutMs ?? 2500),\n });\n if (!res.ok) return null;\n return (await res.json()) as T;\n } catch {\n return null;\n }\n}\n\nexport async function detectOllama(\n baseUrl = DEFAULT_OLLAMA_URL,\n timeoutMs = 1500,\n): Promise<{ up: boolean; models: string[] }> {\n const root = ollamaApiBaseUrl(baseUrl);\n const data = await httpGetJson<{ models?: { name: string }[] }>(`${root}/api/tags`, {\n timeoutMs,\n });\n if (!data) return { up: false, models: [] };\n return { up: true, models: (data.models ?? []).map((m) => m.name) };\n}\n\nexport function appCacheDir(version: string): string {\n return join(expandHome(DEFAULT_DATA_DIR), \"app\", version);\n}\n\n/**\n * Ensure a standalone server build exists for `version`. Downloads the\n * release tarball published by the CI workflow and verifies its checksum.\n * Returns the directory containing `.output/server/index.mjs`, or null.\n */\nexport async function ensureStandaloneApp(\n version: string,\n opts: { repo?: string; log?: (m: string) => void } = {},\n): Promise<string | null> {\n const dir = appCacheDir(version);\n const entry = join(dir, \".output\", \"server\", \"index.mjs\");\n if (existsSync(entry)) return dir;\n\n const repo = opts.repo ?? \"OpenExpert-ai/OpenExpert\";\n const log = opts.log ?? (() => {});\n const tarball = `openexpert-server-${version}.tar.gz`;\n // Different tools tag releases differently; try the common shapes.\n const tags = [`v${version}`, `@openexpert/opencore@${version}`, version];\n\n for (const tag of tags) {\n const base = `https://github.com/${repo}/releases/download/${tag}`;\n const url = `${base}/${tarball}`;\n log(`downloading ${url}`);\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(60_000) });\n if (!res.ok) continue;\n const buf = Buffer.from(await res.arrayBuffer());\n\n // Verify checksum when the .sha256 asset is available.\n try {\n const shaRes = await fetch(`${base}/${tarball}.sha256`, {\n signal: AbortSignal.timeout(15_000),\n });\n if (shaRes.ok) {\n const expected = (await shaRes.text()).trim().split(/\\s+/)[0];\n const actual = createHash(\"sha256\").update(buf).digest(\"hex\");\n if (expected && expected !== actual) {\n log(\"checksum mismatch, aborting\");\n return null;\n }\n }\n } catch {\n // No checksum asset: continue (release is fetched over HTTPS).\n }\n\n mkdirSync(dir, { recursive: true });\n const tmp = join(dir, tarball);\n writeFileSync(tmp, buf);\n const r = spawnSync(\"tar\", [\"-xzf\", tmp, \"-C\", dir], { stdio: \"ignore\" });\n if (r.status !== 0) return null;\n return existsSync(entry) ? dir : null;\n } catch {\n // try the next tag\n }\n }\n return null;\n}\n\nexport function run(\n cmd: string,\n args: string[],\n opts: { cwd?: string; env?: NodeJS.ProcessEnv } = {},\n): Promise<number> {\n return new Promise((res) => {\n const child = spawn(cmd, args, { stdio: \"inherit\", cwd: opts.cwd, env: opts.env });\n const forward = (sig: NodeJS.Signals) => () => child.kill(sig);\n process.on(\"SIGINT\", forward(\"SIGINT\"));\n process.on(\"SIGTERM\", forward(\"SIGTERM\"));\n child.on(\"exit\", (code) => res(code ?? 0));\n });\n}\n","// SPDX-License-Identifier: MIT\n// Non-interactive CLI commands: doctor, fix, models, serve, version, update.\n\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { ollamaApiBaseUrl } from \"../model-provider.js\";\nimport {\n DEFAULT_OLLAMA_URL,\n appCacheDir,\n detectOllama,\n ensureStandaloneApp,\n findRepoRoot,\n httpGetJson,\n packageVersion,\n readConfig,\n readSecrets,\n resolvedDataDir,\n run,\n secretsAsEnv,\n writeConfig,\n type Provider,\n} from \"./lib.js\";\n\nexport function cmdVersion(): number {\n console.log(`@openexpert/opencore ${packageVersion()}`);\n return 0;\n}\n\nfunction configuredProvider(): Provider {\n const raw = process.env[\"OPENEXPERT_MODEL_PROVIDER\"] || readConfig().modelProvider;\n if (raw === \"ollama\" || raw === \"openai-compatible\") return raw;\n return \"google\";\n}\n\nexport function cmdDoctor(cwd = process.cwd()): number {\n const provider = configuredProvider();\n const dataDir = resolvedDataDir(cwd);\n const secrets = readSecrets(cwd);\n\n console.log(\"OpenCore doctor\");\n console.log(` provider: ${provider}`);\n console.log(` data dir: ${dataDir}`);\n\n const problems: string[] = [];\n\n if (provider === \"google\" && !process.env[\"GOOGLE_API_KEY\"] && !secrets.GOOGLE_API_KEY) {\n problems.push(\"Falta GOOGLE_API_KEY. Ejecuta `opencore init` o usa Ollama.\");\n }\n if (provider === \"openai-compatible\") {\n if (!process.env[\"OPENEXPERT_MODEL_KEY\"] && !secrets.OPENEXPERT_MODEL_KEY) {\n problems.push(\"Falta OPENEXPERT_MODEL_KEY.\");\n }\n if (!process.env[\"OPENEXPERT_BASE_URL\"] && !secrets.OPENEXPERT_BASE_URL) {\n problems.push(\"Falta OPENEXPERT_BASE_URL.\");\n }\n }\n if (provider === \"ollama\") {\n console.log(` ollama: ${process.env[\"OLLAMA_BASE_URL\"] || DEFAULT_OLLAMA_URL}`);\n }\n if (!process.env[\"GOOGLE_CLIENT_ID\"] && !secrets.GOOGLE_CLIENT_ID) {\n console.log(\" note: sin Google OAuth el chat funciona; Drive queda desconectado.\");\n }\n try {\n mkdirSync(dataDir, { recursive: true });\n } catch {\n problems.push(`No se puede escribir en ${dataDir}.`);\n }\n\n if (!problems.length) {\n console.log(\" OK: listo para arrancar.\");\n return 0;\n }\n console.log(\" pendiente:\");\n for (const x of problems) console.log(` - ${x}`);\n return 1;\n}\n\nexport async function cmdFix(cwd = process.cwd()): Promise<number> {\n const dataDir = resolvedDataDir(cwd);\n mkdirSync(dataDir, { recursive: true });\n\n if (!existsSync(join(cwd, \"openexpert.json\"))) {\n const ollama = await detectOllama();\n writeConfig(\n {\n modelProvider: \"ollama\",\n modelId: ollama.models[0] ?? \"llama3.1\",\n ollamaBaseUrl: DEFAULT_OLLAMA_URL,\n dataDir: \"~/.openexpert\",\n },\n cwd,\n );\n console.log(\"✓ openexpert.json creado (Ollama).\");\n } else {\n console.log(\"• openexpert.json ya existe.\");\n }\n\n const secrets = readSecrets(cwd);\n if (Object.keys(secrets).length) {\n const p = join(dataDir, \"secrets.json\");\n writeFileSync(p, JSON.stringify(secrets, null, 2), { mode: 0o600 });\n console.log(`✓ permisos de ${p} normalizados (0600).`);\n }\n\n console.log(\"✓ directorio de datos listo:\", dataDir);\n cmdDoctor(cwd);\n console.log(\"\\nSiguiente paso: `opencore serve` (o `opencore init` para elegir modelo).\");\n return 0;\n}\n\nexport async function cmdModels(cwd = process.cwd()): Promise<number> {\n const provider = configuredProvider();\n const secrets = readSecrets(cwd);\n const cfg = readConfig(cwd);\n\n if (provider === \"ollama\") {\n const base = ollamaApiBaseUrl(\n process.env[\"OLLAMA_BASE_URL\"] || cfg.ollamaBaseUrl || DEFAULT_OLLAMA_URL,\n );\n const { up, models } = await detectOllama(base);\n if (!up) {\n console.log(`No se puede contactar con Ollama en ${base}.`);\n console.log(\"Instálalo con: curl -fsSL https://ollama.com/install.sh | sh\");\n return 1;\n }\n console.log(`Modelos en Ollama (${base}):`);\n for (const m of models) console.log(` ${m === cfg.modelId ? \"*\" : \" \"} ${m}`);\n if (!models.length) console.log(\" (ninguno; prueba `ollama pull llama3.1`)\");\n return 0;\n }\n\n if (provider === \"openai-compatible\") {\n const base = process.env[\"OPENEXPERT_BASE_URL\"] || secrets.OPENEXPERT_BASE_URL;\n const key = process.env[\"OPENEXPERT_MODEL_KEY\"] || secrets.OPENEXPERT_MODEL_KEY;\n if (!base || !key) {\n console.log(\"Configura OPENEXPERT_BASE_URL y OPENEXPERT_MODEL_KEY (opencore init).\");\n return 1;\n }\n const data = await httpGetJson<{ data?: { id: string }[] }>(\n `${base.replace(/\\/+$/, \"\")}/models`,\n { headers: { Authorization: `Bearer ${key}` } },\n );\n if (!data) {\n console.log(\"No se pudo listar modelos del endpoint.\");\n return 1;\n }\n console.log(\"Modelos disponibles:\");\n for (const m of data.data ?? []) console.log(` ${m.id === cfg.modelId ? \"*\" : \" \"} ${m.id}`);\n return 0;\n }\n\n console.log(\"Proveedor google: usa `gemini-2.5-flash` o consulta Google AI Studio.\");\n return 0;\n}\n\nexport async function cmdServe(cwd = process.cwd()): Promise<number> {\n const env = { ...process.env, ...secretsAsEnv(cwd) } as NodeJS.ProcessEnv;\n\n const repo = findRepoRoot(cwd);\n if (repo) {\n console.log(\"Arrancando OpenExpert (desde el código fuente)…\");\n return run(\"npm\", [\"run\", \"dev\"], { cwd: repo, env });\n }\n\n const version = packageVersion();\n const app = await ensureStandaloneApp(version, { log: (m) => console.log(` ${m}`) });\n if (!app) {\n console.error(\n \"No hay un servidor preconstruido disponible y no se pudo descargar.\\n\" +\n \"Opciones:\\n\" +\n \" • Usa Docker: docker run -p 3000:3000 -v openexpert-data:/data ghcr.io/openexpert-ai/openexpert:local\\n\" +\n \" • O clona el repositorio y ejecuta `npm ci && npm run dev`.\",\n );\n return 1;\n }\n console.log(\"Arrancando OpenCore en http://localhost:3000 …\");\n return run(process.execPath, [join(app, \".output\", \"server\", \"index.mjs\")], {\n cwd: app,\n env: { ...env, PORT: process.env[\"PORT\"] || \"3000\" },\n });\n}\n\nexport function cmdUpdate(): number {\n const dir = appCacheDir(packageVersion());\n if (existsSync(dir)) {\n rmSync(dir, { recursive: true, force: true });\n console.log(`✓ caché del servidor eliminada (${dir}).`);\n } else {\n console.log(\"No hay servidor descargado que actualizar.\");\n }\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n// `opencore desktop` — start the local server (unless it is already running)\n// and open the native OpenExpert window (the Tauri shell). This command owns\n// the server lifecycle so it can stop the server when the window closes.\n//\n// The repository is located from the CLI installation itself (not the current\n// directory), so `OpenExpert` works from anywhere.\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n ensureStandaloneApp,\n findRepoRoot,\n packageRoot,\n packageVersion,\n secretsAsEnv,\n} from \"./lib.js\";\n\nfunction port(): string {\n return process.env[\"PORT\"] || \"3000\";\n}\n\nfunction appUrl(): string {\n return `http://localhost:${port()}`;\n}\n\nasync function isServerUp(timeoutMs = 1500): Promise<boolean> {\n try {\n const res = await fetch(appUrl(), { signal: AbortSignal.timeout(timeoutMs) });\n return res.status < 500;\n } catch {\n return false;\n }\n}\n\nasync function waitForServer(timeoutMs = 120_000): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await isServerUp()) return true;\n await new Promise((r) => setTimeout(r, 400));\n }\n return false;\n}\n\n/**\n * Find the source checkout, in order of confidence:\n * 1. OPENEXPERT_DESKTOP_REPO (baked by the installer),\n * 2. relative to the CLI installation (packages/opencore → repo root),\n * 3. walking up from the current directory.\n */\nfunction resolveRepo(cwd: string): string | null {\n const fromEnv = process.env[\"OPENEXPERT_DESKTOP_REPO\"];\n if (fromEnv) {\n const r = findRepoRoot(fromEnv);\n if (r) return r;\n }\n return findRepoRoot(packageRoot()) ?? findRepoRoot(cwd);\n}\n\n/** Newest modification time (ms) under a directory, or 0 if unreadable. */\nfunction newestMtime(dir: string): number {\n let newest = 0;\n try {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) newest = Math.max(newest, newestMtime(full));\n else if (entry.isFile()) newest = Math.max(newest, statSync(full).mtimeMs);\n }\n } catch {\n // ignore unreadable paths\n }\n return newest;\n}\n\n/** True when the production build exists and is not older than src/. */\nfunction buildIsFresh(repo: string): boolean {\n const built = join(repo, \".output\", \"server\", \"index.mjs\");\n if (!existsSync(built)) return false;\n try {\n return statSync(built).mtimeMs >= newestMtime(join(repo, \"src\"));\n } catch {\n return true;\n }\n}\n\n/** Start the local server, reusing the source checkout when available. */\nasync function spawnServer(cwd: string, env: NodeJS.ProcessEnv): Promise<ChildProcess | null> {\n const repo = resolveRepo(cwd);\n if (repo) {\n const built = join(repo, \".output\", \"server\", \"index.mjs\");\n if (existsSync(built) && buildIsFresh(repo)) {\n return spawn(process.execPath, [built], {\n cwd: repo,\n env: { ...env, NODE_ENV: \"production\", PORT: port() },\n stdio: \"inherit\",\n });\n }\n console.log(\n existsSync(built)\n ? \"La build está desactualizada; arrancando en modo desarrollo…\"\n : \"Arrancando el servidor en modo desarrollo (no hay build)…\",\n );\n return spawn(\"npm\", [\"run\", \"dev\"], { cwd: repo, env, stdio: \"inherit\" });\n }\n\n const app = await ensureStandaloneApp(packageVersion(), { log: (m) => console.log(` ${m}`) });\n if (!app) return null;\n return spawn(process.execPath, [join(app, \".output\", \"server\", \"index.mjs\")], {\n cwd: app,\n env: { ...env, NODE_ENV: \"production\", PORT: port() },\n stdio: \"inherit\",\n });\n}\n\nfunction findDesktopBinary(cwd: string): string | null {\n const fromEnv = process.env[\"OPENEXPERT_DESKTOP_BIN\"];\n if (fromEnv && existsSync(fromEnv)) return fromEnv;\n\n const candidates = [join(homedir(), \".local\", \"bin\", \"openexpert-desktop\")];\n const repo = resolveRepo(cwd);\n if (repo) {\n candidates.push(\n join(repo, \"src-tauri\", \"target\", \"release\", \"openexpert-desktop\"),\n join(repo, \"src-tauri\", \"target\", \"debug\", \"openexpert-desktop\"),\n );\n }\n return candidates.find((c) => existsSync(c)) ?? null;\n}\n\nexport async function cmdDesktop(cwd = process.cwd()): Promise<number> {\n const env = { ...process.env, ...secretsAsEnv(cwd) } as NodeJS.ProcessEnv;\n\n const bin = findDesktopBinary(cwd);\n if (!bin) {\n console.error(\n \"No se encontró la ventana de escritorio (openexpert-desktop).\\n\" +\n \"Compílala con `npx tauri build` o usa `npm run desktop:install`.\",\n );\n return 1;\n }\n\n let server: ChildProcess | null = null;\n let startedServer = false;\n\n if (await isServerUp()) {\n console.log(`• Usando el servidor ya activo en ${appUrl()}`);\n } else {\n console.log(\"Arrancando el servidor local…\");\n server = await spawnServer(cwd, env);\n if (!server) {\n console.error(\n \"No se pudo arrancar el servidor.\\n\" +\n \"Si usas el código fuente, entra en el repositorio y ejecuta:\\n\" +\n \" npm ci && npm run build && npm run desktop:install\",\n );\n return 1;\n }\n startedServer = true;\n if (!(await waitForServer())) {\n console.error(\"El servidor no respondió a tiempo.\");\n server.kill(\"SIGTERM\");\n return 1;\n }\n }\n\n const stopServer = (): void => {\n if (startedServer && server && !server.killed) {\n try {\n server.kill(\"SIGTERM\");\n } catch {\n // already gone\n }\n }\n };\n\n const onSignal = (): void => {\n stopServer();\n process.exit(0);\n };\n process.once(\"SIGINT\", onSignal);\n process.once(\"SIGTERM\", onSignal);\n\n console.log(\"Abriendo la ventana de OpenExpert…\");\n const code = await new Promise<number>((resolve) => {\n const win = spawn(bin, [], { stdio: \"inherit\", env });\n win.on(\"exit\", (c) => resolve(c ?? 0));\n });\n\n stopServer();\n return code;\n}\n","// SPDX-License-Identifier: MIT\n// Interactive setup wizard: pick a model provider, write config + secrets.\n\nimport * as p from \"@clack/prompts\";\nimport {\n DEFAULT_OLLAMA_URL,\n detectOllama,\n readConfig,\n writeConfig,\n writeSecrets,\n type FileConfig,\n type Provider,\n type Secrets,\n} from \"./lib.js\";\n\nexport type WizardResult = {\n config: FileConfig;\n secrets: Secrets;\n provider: Provider;\n modelId: string;\n started: boolean;\n};\n\nfunction bail(): never {\n p.cancel(\"Configuración cancelada.\");\n process.exit(1);\n}\n\nexport async function runWizard(\n opts: { cwd?: string; assumeYes?: boolean } = {},\n): Promise<WizardResult> {\n const cwd = opts.cwd ?? process.cwd();\n const existing = readConfig(cwd);\n const nonInteractive = opts.assumeYes || !process.stdin.isTTY;\n\n if (nonInteractive) {\n const ollama = await detectOllama();\n const config: FileConfig = {\n modelProvider: \"ollama\",\n modelId: ollama.models[0] ?? \"llama3.1\",\n ollamaBaseUrl: DEFAULT_OLLAMA_URL,\n dataDir: \"~/.openexpert\",\n };\n writeConfig(config, cwd);\n return { config, secrets: {}, provider: \"ollama\", modelId: config.modelId!, started: false };\n }\n\n p.intro(\"OpenCore — configuración\");\n\n const spin = p.spinner();\n spin.start(\"Buscando Ollama local…\");\n const ollama = await detectOllama();\n spin.stop(\n ollama.up\n ? `Ollama detectado (${ollama.models.length} modelo(s))`\n : \"No se ha detectado Ollama\",\n );\n\n const choice = await p.select({\n message: \"¿Qué modelo quieres usar?\",\n options: [\n ...(ollama.up\n ? [\n {\n value: \"ollama\" as const,\n label: \"Modelo local (Ollama)\",\n hint: \"sin claves, datos en tu equipo\",\n },\n ]\n : []),\n { value: \"google\" as const, label: \"Google Gemini (clave gratuita)\", hint: \"AI Studio\" },\n { value: \"openai-compatible\" as const, label: \"Otro endpoint compatible\", hint: \"BYOK\" },\n ...(!ollama.up\n ? [{ value: \"install-ollama\" as const, label: \"Instalar Ollama\", hint: \"te muestro cómo\" }]\n : []),\n ],\n });\n if (p.isCancel(choice)) bail();\n\n if (choice === \"install-ollama\") {\n p.note(\n \"Instala Ollama y descarga un modelo:\\n\\n\" +\n \" curl -fsSL https://ollama.com/install.sh | sh\\n\" +\n \" ollama pull llama3.1\\n\\n\" +\n \"Vuelve a ejecutar `opencore init` cuando esté listo.\",\n \"Instalar Ollama\",\n );\n p.outro(\"Sin cambios.\");\n process.exit(0);\n }\n\n const config: FileConfig = { dataDir: \"~/.openexpert\" };\n const secrets: Secrets = {};\n let modelId = \"llama3.1\";\n\n if (choice === \"ollama\") {\n const models = ollama.models.length ? ollama.models : [\"llama3.1\"];\n const picked = await p.select({\n message: \"Modelo de Ollama\",\n options: models.map((m) => ({ value: m, label: m })),\n });\n if (p.isCancel(picked)) bail();\n modelId = String(picked);\n config.modelProvider = \"ollama\";\n config.modelId = modelId;\n config.ollamaBaseUrl = DEFAULT_OLLAMA_URL;\n } else if (choice === \"google\") {\n const apiKey = await p.password({ message: \"GOOGLE_API_KEY (AI Studio)\" });\n if (p.isCancel(apiKey)) bail();\n const id = await p.text({ message: \"Modelo\", initialValue: \"gemini-2.5-flash\" });\n if (p.isCancel(id)) bail();\n modelId = String(id) || \"gemini-2.5-flash\";\n config.modelProvider = \"google\";\n config.modelId = modelId;\n secrets.GOOGLE_API_KEY = String(apiKey);\n } else if (choice === \"openai-compatible\") {\n const baseURL = await p.text({ message: \"Base URL (…/v1)\", initialValue: \"\" });\n if (p.isCancel(baseURL)) bail();\n const apiKey = await p.password({ message: \"API key\" });\n if (p.isCancel(apiKey)) bail();\n const id = await p.text({ message: \"Modelo\", initialValue: \"gpt-4o-mini\" });\n if (p.isCancel(id)) bail();\n modelId = String(id) || \"gpt-4o-mini\";\n config.modelProvider = \"openai-compatible\";\n config.modelId = modelId;\n secrets.OPENEXPERT_BASE_URL = String(baseURL);\n secrets.OPENEXPERT_MODEL_KEY = String(apiKey);\n }\n\n writeConfig({ ...existing, ...config }, cwd);\n if (Object.keys(secrets).length) writeSecrets({ ...secrets }, cwd);\n\n p.outro(\n `Configuración guardada en openexpert.json (modelo: ${config.modelProvider}:${modelId}).`,\n );\n\n const go = await p.confirm({ message: \"¿Arrancar ahora?\", initialValue: true });\n if (p.isCancel(go)) bail();\n\n return {\n config: { ...existing, ...config },\n secrets,\n provider: config.modelProvider as Provider,\n modelId,\n started: Boolean(go),\n };\n}\n","// SPDX-License-Identifier: MIT\n// OpenCore CLI entry point. Dispatches subcommands. See `run`.\n\nimport { cmdDoctor, cmdFix, cmdModels, cmdServe, cmdUpdate, cmdVersion } from \"./cli/commands.js\";\nimport { cmdDesktop } from \"./cli/desktop.js\";\nimport { runWizard } from \"./cli/wizard.js\";\n\nfunction help(): void {\n console.log(`opencore — motor abierto de OpenExpert\n\nUso:\n opencore asistente de configuración y arranque\n opencore init igual que sin argumentos\n opencore serve arranca la edición local en http://localhost:3000\n opencore desktop arranca el servidor y abre la ventana de escritorio\n opencore doctor revisa tu configuración\n opencore fix auto-configura lo que falte\n opencore models lista los modelos del proveedor actual\n opencore update limpia la caché del servidor descargado\n opencore version muestra la versión\n\nEjemplos:\n npx @openexpert/opencore\n npx @openexpert/opencore serve\n\nDocker (sin Node):\n docker run -p 3000:3000 -v openexpert-data:/data ghcr.io/openexpert-ai/openexpert`);\n}\n\nexport async function run(argv: string[]): Promise<void> {\n const cmd = argv[0] ?? \"\";\n switch (cmd) {\n case \"\":\n case \"init\":\n case \"setup\": {\n const res = await runWizard({});\n if (res.started) process.exit(await cmdServe());\n return;\n }\n case \"doctor\":\n return process.exit(cmdDoctor());\n case \"fix\":\n return process.exit(await cmdFix());\n case \"models\":\n return process.exit(await cmdModels());\n case \"serve\":\n case \"dev\":\n return process.exit(await cmdServe());\n case \"desktop\":\n case \"app\": {\n const code = await cmdDesktop();\n return process.exit(code);\n }\n case \"update\":\n return process.exit(cmdUpdate());\n case \"version\":\n case \"--version\":\n case \"-v\":\n return process.exit(cmdVersion());\n case \"help\":\n case \"--help\":\n case \"-h\":\n help();\n return;\n default:\n console.error(`Comando desconocido: ${cmd}\\n`);\n help();\n return process.exit(1);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/model-provider.ts","../src/cli/lib.ts","../src/cli/commands.ts","../src/cli/desktop.ts","../src/cli/wizard.ts","../src/cli.ts"],"names":["p","mkdirSync","existsSync","join","writeFileSync","spawn","homedir","resolve","ollama","config","run"],"mappings":"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","file":"cli.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — which model to use. The harness is stable; the model is configuration.\n//\n// Providers:\n// - google: Gemini via GOOGLE_API_KEY (free API key).\n// - ollama: Local model via Ollama's OpenAI-compatible endpoint.\n// - openai-compatible: Any OpenAI-compatible endpoint with your own key.\n\nexport type ModelProviderId = \"google\" | \"ollama\" | \"openai-compatible\";\n\nexport type ModelSelection = {\n provider: ModelProviderId;\n modelId: string;\n /** Where the key comes from, without exposing the value. */\n keySource: \"GOOGLE_API_KEY\" | \"OPENEXPERT_MODEL_KEY\" | \"none (local)\";\n};\n\nexport function selectModel(env: NodeJS.ProcessEnv = process.env): ModelSelection {\n const raw = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const provider: ModelProviderId =\n raw === \"ollama\" || raw === \"openai-compatible\" ? raw : \"google\";\n\n if (provider === \"ollama\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"llama3.1\",\n keySource: \"none (local)\",\n };\n }\n if (provider === \"openai-compatible\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gpt-4o-mini\",\n keySource: \"OPENEXPERT_MODEL_KEY\",\n };\n }\n return {\n provider: \"google\",\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gemini-2.5-flash\",\n keySource: \"GOOGLE_API_KEY\",\n };\n}\n\n/**\n * Ollama exposes two surfaces: the native API at the server root (`/api/*`)\n * and an OpenAI-compatible API under `/v1`. Callers may configure either\n * shape, so normalise before using it.\n *\n * Trailing slashes are removed with a loop, not with `/\\/+$/`: a regex has to\n * retry at every position of a long run of slashes, which is what CodeQL\n * reports as a polynomial ReDoS on `baseUrl`.\n */\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === \"/\") end -= 1;\n return value.slice(0, end);\n}\n\nexport function ollamaApiBaseUrl(baseUrl: string): string {\n const base = stripTrailingSlashes(baseUrl.trim());\n return base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\n\nexport function ollamaChatBaseUrl(baseUrl: string): string {\n return `${ollamaApiBaseUrl(baseUrl)}/v1`;\n}\n\nexport function missingKeyHint(\n sel: ModelSelection,\n env: NodeJS.ProcessEnv = process.env,\n): string | null {\n if (sel.provider === \"ollama\") return null;\n if (sel.provider === \"google\" && !env[\"GOOGLE_API_KEY\"]) {\n return \"Falta GOOGLE_API_KEY (o usa OPENEXPERT_MODEL_PROVIDER=ollama para modelo local).\";\n }\n if (sel.provider === \"openai-compatible\" && !env[\"OPENEXPERT_MODEL_KEY\"]) {\n return \"Falta OPENEXPERT_MODEL_KEY para tu endpoint OpenAI-compatible.\";\n }\n return null;\n}\n","// SPDX-License-Identifier: MIT\n// Shared helpers for the OpenCore CLI: paths, config/secrets I/O, HTTP\n// detection, and release download with checksum verification.\n\nimport { spawn, spawnSync } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { ollamaApiBaseUrl } from \"../model-provider.js\";\n\nexport type Provider = \"google\" | \"ollama\" | \"openai-compatible\";\n\nexport type FileConfig = {\n modelProvider?: Provider;\n modelId?: string;\n ollamaBaseUrl?: string;\n dataDir?: string;\n};\n\nexport type Secrets = {\n GOOGLE_API_KEY?: string;\n GOOGLE_CLIENT_ID?: string;\n GOOGLE_CLIENT_SECRET?: string;\n OPENEXPERT_MODEL_KEY?: string;\n OPENEXPERT_BASE_URL?: string;\n};\n\nexport const DEFAULT_OLLAMA_URL = \"http://localhost:11434\";\nexport const DEFAULT_DATA_DIR = \"~/.openexpert\";\n\nexport function expandHome(p: string): string {\n if (p === \"~\") return homedir();\n if (p.startsWith(\"~/\")) return join(homedir(), p.slice(2));\n return p;\n}\n\nexport function packageRoot(): string {\n // dist/cli.js -> package root is one level up.\n return resolve(dirname(fileURLToPath(import.meta.url)), \"..\");\n}\n\nexport function packageVersion(): string {\n try {\n const pkg = JSON.parse(readFileSync(join(packageRoot(), \"package.json\"), \"utf8\")) as {\n version?: string;\n };\n return pkg.version ?? \"0.0.0\";\n } catch {\n return \"0.0.0\";\n }\n}\n\n/** Walk up from `start` to find a checkout (package.json + packages/opencore). */\nexport function findRepoRoot(start = process.cwd()): string | null {\n let dir = resolve(start);\n for (let i = 0; i < 8; i++) {\n if (\n existsSync(join(dir, \"package.json\")) &&\n existsSync(join(dir, \"packages\", \"opencore\")) &&\n existsSync(join(dir, \"vite.config.ts\"))\n ) {\n return dir;\n }\n const parent = resolve(dir, \"..\");\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nexport function configPath(cwd = process.cwd()): string {\n return join(cwd, \"openexpert.json\");\n}\n\nexport function readConfig(cwd = process.cwd()): FileConfig {\n try {\n const p = configPath(cwd);\n if (!existsSync(p)) return {};\n return JSON.parse(readFileSync(p, \"utf8\")) as FileConfig;\n } catch {\n return {};\n }\n}\n\nexport function writeConfig(cfg: FileConfig, cwd = process.cwd()): void {\n writeFileSync(configPath(cwd), JSON.stringify(cfg, null, 2) + \"\\n\");\n}\n\nexport function resolvedDataDir(cwd = process.cwd()): string {\n const cfg = readConfig(cwd);\n const raw = process.env[\"OPENEXPERT_DATA_DIR\"] || cfg.dataDir || DEFAULT_DATA_DIR;\n return expandHome(raw);\n}\n\nexport function secretsPath(cwd = process.cwd()): string {\n return join(resolvedDataDir(cwd), \"secrets.json\");\n}\n\nexport function readSecrets(cwd = process.cwd()): Secrets {\n try {\n const p = secretsPath(cwd);\n if (!existsSync(p)) return {};\n return JSON.parse(readFileSync(p, \"utf8\")) as Secrets;\n } catch {\n return {};\n }\n}\n\nexport function writeSecrets(secrets: Secrets, cwd = process.cwd()): string {\n const dir = resolvedDataDir(cwd);\n mkdirSync(dir, { recursive: true });\n const p = join(dir, \"secrets.json\");\n writeFileSync(p, JSON.stringify(secrets, null, 2), { mode: 0o600 });\n return p;\n}\n\n/** Merge stored secrets into the current environment for the child server. */\nexport function secretsAsEnv(cwd = process.cwd()): Record<string, string> {\n const s = readSecrets(cwd);\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(s)) if (v) out[k] = v;\n return out;\n}\n\nexport async function httpGetJson<T>(\n url: string,\n opts: { headers?: Record<string, string>; timeoutMs?: number } = {},\n): Promise<T | null> {\n try {\n const res = await fetch(url, {\n ...(opts.headers ? { headers: opts.headers } : {}),\n signal: AbortSignal.timeout(opts.timeoutMs ?? 2500),\n });\n if (!res.ok) return null;\n return (await res.json()) as T;\n } catch {\n return null;\n }\n}\n\nexport async function detectOllama(\n baseUrl = DEFAULT_OLLAMA_URL,\n timeoutMs = 1500,\n): Promise<{ up: boolean; models: string[] }> {\n const root = ollamaApiBaseUrl(baseUrl);\n const data = await httpGetJson<{ models?: { name: string }[] }>(`${root}/api/tags`, {\n timeoutMs,\n });\n if (!data) return { up: false, models: [] };\n return { up: true, models: (data.models ?? []).map((m) => m.name) };\n}\n\nexport function appCacheDir(version: string): string {\n return join(expandHome(DEFAULT_DATA_DIR), \"app\", version);\n}\n\n/**\n * Ensure a standalone server build exists for `version`. Downloads the\n * release tarball published by the CI workflow and verifies its checksum.\n * Returns the directory containing `.output/server/index.mjs`, or null.\n */\nexport async function ensureStandaloneApp(\n version: string,\n opts: { repo?: string; log?: (m: string) => void } = {},\n): Promise<string | null> {\n const dir = appCacheDir(version);\n const entry = join(dir, \".output\", \"server\", \"index.mjs\");\n if (existsSync(entry)) return dir;\n\n const repo = opts.repo ?? \"OpenExpert-ai/OpenExpert\";\n const log = opts.log ?? (() => {});\n const tarball = `openexpert-server-${version}.tar.gz`;\n // Different tools tag releases differently; try the common shapes.\n const tags = [`v${version}`, `@openexpert/opencore@${version}`, version];\n\n for (const tag of tags) {\n const base = `https://github.com/${repo}/releases/download/${tag}`;\n const url = `${base}/${tarball}`;\n log(`downloading ${url}`);\n try {\n const res = await fetch(url, { signal: AbortSignal.timeout(60_000) });\n if (!res.ok) continue;\n const buf = Buffer.from(await res.arrayBuffer());\n\n // Verify checksum when the .sha256 asset is available.\n try {\n const shaRes = await fetch(`${base}/${tarball}.sha256`, {\n signal: AbortSignal.timeout(15_000),\n });\n if (shaRes.ok) {\n const expected = (await shaRes.text()).trim().split(/\\s+/)[0];\n const actual = createHash(\"sha256\").update(buf).digest(\"hex\");\n if (expected && expected !== actual) {\n log(\"checksum mismatch, aborting\");\n return null;\n }\n }\n } catch {\n // No checksum asset: continue (release is fetched over HTTPS).\n }\n\n mkdirSync(dir, { recursive: true });\n const tmp = join(dir, tarball);\n writeFileSync(tmp, buf);\n const r = spawnSync(\"tar\", [\"-xzf\", tmp, \"-C\", dir], { stdio: \"ignore\" });\n if (r.status !== 0) return null;\n return existsSync(entry) ? dir : null;\n } catch {\n // try the next tag\n }\n }\n return null;\n}\n\nexport function run(\n cmd: string,\n args: string[],\n opts: { cwd?: string; env?: NodeJS.ProcessEnv } = {},\n): Promise<number> {\n return new Promise((res) => {\n const child = spawn(cmd, args, { stdio: \"inherit\", cwd: opts.cwd, env: opts.env });\n const forward = (sig: NodeJS.Signals) => () => child.kill(sig);\n process.on(\"SIGINT\", forward(\"SIGINT\"));\n process.on(\"SIGTERM\", forward(\"SIGTERM\"));\n child.on(\"exit\", (code) => res(code ?? 0));\n });\n}\n","// SPDX-License-Identifier: MIT\n// Non-interactive CLI commands: doctor, fix, models, serve, version, update.\n\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { ollamaApiBaseUrl } from \"../model-provider.js\";\nimport {\n DEFAULT_OLLAMA_URL,\n appCacheDir,\n detectOllama,\n ensureStandaloneApp,\n findRepoRoot,\n httpGetJson,\n packageVersion,\n readConfig,\n readSecrets,\n resolvedDataDir,\n run,\n secretsAsEnv,\n writeConfig,\n type Provider,\n} from \"./lib.js\";\n\nexport function cmdVersion(): number {\n console.log(`OpenExpert ${packageVersion()}`);\n return 0;\n}\n\nfunction configuredProvider(): Provider {\n const raw = process.env[\"OPENEXPERT_MODEL_PROVIDER\"] || readConfig().modelProvider;\n if (raw === \"ollama\" || raw === \"openai-compatible\") return raw;\n return \"google\";\n}\n\nexport function cmdDoctor(cwd = process.cwd()): number {\n const provider = configuredProvider();\n const dataDir = resolvedDataDir(cwd);\n const secrets = readSecrets(cwd);\n\n console.log(\"OpenExpert doctor\");\n console.log(` provider: ${provider}`);\n console.log(` data dir: ${dataDir}`);\n\n const problems: string[] = [];\n\n if (provider === \"google\" && !process.env[\"GOOGLE_API_KEY\"] && !secrets.GOOGLE_API_KEY) {\n problems.push(\"Falta GOOGLE_API_KEY. Ejecuta `openexpert init` o usa Ollama.\");\n }\n if (provider === \"openai-compatible\") {\n if (!process.env[\"OPENEXPERT_MODEL_KEY\"] && !secrets.OPENEXPERT_MODEL_KEY) {\n problems.push(\"Falta OPENEXPERT_MODEL_KEY.\");\n }\n if (!process.env[\"OPENEXPERT_BASE_URL\"] && !secrets.OPENEXPERT_BASE_URL) {\n problems.push(\"Falta OPENEXPERT_BASE_URL.\");\n }\n }\n if (provider === \"ollama\") {\n console.log(` ollama: ${process.env[\"OLLAMA_BASE_URL\"] || DEFAULT_OLLAMA_URL}`);\n }\n if (!process.env[\"GOOGLE_CLIENT_ID\"] && !secrets.GOOGLE_CLIENT_ID) {\n console.log(\" note: sin Google OAuth el chat funciona; Drive queda desconectado.\");\n }\n try {\n mkdirSync(dataDir, { recursive: true });\n } catch {\n problems.push(`No se puede escribir en ${dataDir}.`);\n }\n\n if (!problems.length) {\n console.log(\" OK: listo para arrancar.\");\n return 0;\n }\n console.log(\" pendiente:\");\n for (const x of problems) console.log(` - ${x}`);\n return 1;\n}\n\nexport async function cmdFix(cwd = process.cwd()): Promise<number> {\n const dataDir = resolvedDataDir(cwd);\n mkdirSync(dataDir, { recursive: true });\n\n if (!existsSync(join(cwd, \"openexpert.json\"))) {\n const ollama = await detectOllama();\n writeConfig(\n {\n modelProvider: \"ollama\",\n modelId: ollama.models[0] ?? \"llama3.1\",\n ollamaBaseUrl: DEFAULT_OLLAMA_URL,\n dataDir: \"~/.openexpert\",\n },\n cwd,\n );\n console.log(\"✓ openexpert.json creado (Ollama).\");\n } else {\n console.log(\"• openexpert.json ya existe.\");\n }\n\n const secrets = readSecrets(cwd);\n if (Object.keys(secrets).length) {\n const p = join(dataDir, \"secrets.json\");\n writeFileSync(p, JSON.stringify(secrets, null, 2), { mode: 0o600 });\n console.log(`✓ permisos de ${p} normalizados (0600).`);\n }\n\n console.log(\"✓ directorio de datos listo:\", dataDir);\n cmdDoctor(cwd);\n console.log(\"\\nSiguiente paso: `openexpert serve` (o `openexpert init` para elegir modelo).\");\n return 0;\n}\n\nexport async function cmdModels(cwd = process.cwd()): Promise<number> {\n const provider = configuredProvider();\n const secrets = readSecrets(cwd);\n const cfg = readConfig(cwd);\n\n if (provider === \"ollama\") {\n const base = ollamaApiBaseUrl(\n process.env[\"OLLAMA_BASE_URL\"] || cfg.ollamaBaseUrl || DEFAULT_OLLAMA_URL,\n );\n const { up, models } = await detectOllama(base);\n if (!up) {\n console.log(`No se puede contactar con Ollama en ${base}.`);\n console.log(\"Instálalo con: curl -fsSL https://ollama.com/install.sh | sh\");\n return 1;\n }\n console.log(`Modelos en Ollama (${base}):`);\n for (const m of models) console.log(` ${m === cfg.modelId ? \"*\" : \" \"} ${m}`);\n if (!models.length) console.log(\" (ninguno; prueba `ollama pull llama3.1`)\");\n return 0;\n }\n\n if (provider === \"openai-compatible\") {\n const base = process.env[\"OPENEXPERT_BASE_URL\"] || secrets.OPENEXPERT_BASE_URL;\n const key = process.env[\"OPENEXPERT_MODEL_KEY\"] || secrets.OPENEXPERT_MODEL_KEY;\n if (!base || !key) {\n console.log(\"Configura OPENEXPERT_BASE_URL y OPENEXPERT_MODEL_KEY (openexpert init).\");\n return 1;\n }\n const data = await httpGetJson<{ data?: { id: string }[] }>(\n `${base.replace(/\\/+$/, \"\")}/models`,\n { headers: { Authorization: `Bearer ${key}` } },\n );\n if (!data) {\n console.log(\"No se pudo listar modelos del endpoint.\");\n return 1;\n }\n console.log(\"Modelos disponibles:\");\n for (const m of data.data ?? []) console.log(` ${m.id === cfg.modelId ? \"*\" : \" \"} ${m.id}`);\n return 0;\n }\n\n console.log(\"Proveedor google: usa `gemini-2.5-flash` o consulta Google AI Studio.\");\n return 0;\n}\n\nexport async function cmdServe(cwd = process.cwd()): Promise<number> {\n const env = { ...process.env, ...secretsAsEnv(cwd) } as NodeJS.ProcessEnv;\n\n const repo = findRepoRoot(cwd);\n if (repo) {\n console.log(\"Arrancando OpenExpert (desde el código fuente)…\");\n return run(\"npm\", [\"run\", \"dev\"], { cwd: repo, env });\n }\n\n const version = packageVersion();\n const app = await ensureStandaloneApp(version, { log: (m) => console.log(` ${m}`) });\n if (!app) {\n console.error(\n \"No hay un servidor preconstruido disponible y no se pudo descargar.\\n\" +\n \"Opciones:\\n\" +\n \" • Usa Docker: docker run -p 3000:3000 -v openexpert-data:/data ghcr.io/openexpert-ai/openexpert:local\\n\" +\n \" • O clona el repositorio y ejecuta `npm ci && npm run dev`.\",\n );\n return 1;\n }\n console.log(\"Arrancando OpenExpert en http://localhost:3000 …\");\n return run(process.execPath, [join(app, \".output\", \"server\", \"index.mjs\")], {\n cwd: app,\n env: { ...env, PORT: process.env[\"PORT\"] || \"3000\" },\n });\n}\n\nexport function cmdUpdate(): number {\n const dir = appCacheDir(packageVersion());\n if (existsSync(dir)) {\n rmSync(dir, { recursive: true, force: true });\n console.log(`✓ caché del servidor eliminada (${dir}).`);\n } else {\n console.log(\"No hay servidor descargado que actualizar.\");\n }\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n// `opencore desktop` — start the local server (unless it is already running)\n// and open the native OpenExpert window (the Tauri shell). This command owns\n// the server lifecycle so it can stop the server when the window closes.\n//\n// The repository is located from the CLI installation itself (not the current\n// directory), so `OpenExpert` works from anywhere.\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n ensureStandaloneApp,\n findRepoRoot,\n packageRoot,\n packageVersion,\n secretsAsEnv,\n} from \"./lib.js\";\n\nfunction port(): string {\n return process.env[\"PORT\"] || \"3000\";\n}\n\nfunction appUrl(): string {\n return `http://localhost:${port()}`;\n}\n\nasync function isServerUp(timeoutMs = 1500): Promise<boolean> {\n try {\n const res = await fetch(appUrl(), { signal: AbortSignal.timeout(timeoutMs) });\n return res.status < 500;\n } catch {\n return false;\n }\n}\n\nasync function waitForServer(timeoutMs = 120_000): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n if (await isServerUp()) return true;\n await new Promise((r) => setTimeout(r, 400));\n }\n return false;\n}\n\n/**\n * Find the source checkout, in order of confidence:\n * 1. OPENEXPERT_DESKTOP_REPO (baked by the installer),\n * 2. relative to the CLI installation (packages/opencore → repo root),\n * 3. walking up from the current directory.\n */\nfunction resolveRepo(cwd: string): string | null {\n const fromEnv = process.env[\"OPENEXPERT_DESKTOP_REPO\"];\n if (fromEnv) {\n const r = findRepoRoot(fromEnv);\n if (r) return r;\n }\n return findRepoRoot(packageRoot()) ?? findRepoRoot(cwd);\n}\n\n/** Newest modification time (ms) under a directory, or 0 if unreadable. */\nfunction newestMtime(dir: string): number {\n let newest = 0;\n try {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const full = join(dir, entry.name);\n if (entry.isDirectory()) newest = Math.max(newest, newestMtime(full));\n else if (entry.isFile()) newest = Math.max(newest, statSync(full).mtimeMs);\n }\n } catch {\n // ignore unreadable paths\n }\n return newest;\n}\n\n/** True when the production build exists and is not older than src/. */\nfunction buildIsFresh(repo: string): boolean {\n const built = join(repo, \".output\", \"server\", \"index.mjs\");\n if (!existsSync(built)) return false;\n try {\n return statSync(built).mtimeMs >= newestMtime(join(repo, \"src\"));\n } catch {\n return true;\n }\n}\n\n/** Start the local server, reusing the source checkout when available. */\nasync function spawnServer(cwd: string, env: NodeJS.ProcessEnv): Promise<ChildProcess | null> {\n const repo = resolveRepo(cwd);\n if (repo) {\n const built = join(repo, \".output\", \"server\", \"index.mjs\");\n if (existsSync(built) && buildIsFresh(repo)) {\n return spawn(process.execPath, [built], {\n cwd: repo,\n env: { ...env, NODE_ENV: \"production\", PORT: port() },\n stdio: \"inherit\",\n });\n }\n console.log(\n existsSync(built)\n ? \"La build está desactualizada; arrancando en modo desarrollo…\"\n : \"Arrancando el servidor en modo desarrollo (no hay build)…\",\n );\n return spawn(\"npm\", [\"run\", \"dev\"], { cwd: repo, env, stdio: \"inherit\" });\n }\n\n const app = await ensureStandaloneApp(packageVersion(), { log: (m) => console.log(` ${m}`) });\n if (!app) return null;\n return spawn(process.execPath, [join(app, \".output\", \"server\", \"index.mjs\")], {\n cwd: app,\n env: { ...env, NODE_ENV: \"production\", PORT: port() },\n stdio: \"inherit\",\n });\n}\n\nfunction findDesktopBinary(cwd: string): string | null {\n const fromEnv = process.env[\"OPENEXPERT_DESKTOP_BIN\"];\n if (fromEnv && existsSync(fromEnv)) return fromEnv;\n\n const candidates = [join(homedir(), \".local\", \"bin\", \"openexpert-desktop\")];\n const repo = resolveRepo(cwd);\n if (repo) {\n candidates.push(\n join(repo, \"src-tauri\", \"target\", \"release\", \"openexpert-desktop\"),\n join(repo, \"src-tauri\", \"target\", \"debug\", \"openexpert-desktop\"),\n );\n }\n return candidates.find((c) => existsSync(c)) ?? null;\n}\n\nexport async function cmdDesktop(cwd = process.cwd()): Promise<number> {\n const env = { ...process.env, ...secretsAsEnv(cwd) } as NodeJS.ProcessEnv;\n\n const bin = findDesktopBinary(cwd);\n if (!bin) {\n console.error(\n \"No se encontró la ventana de escritorio (openexpert-desktop).\\n\" +\n \"Compílala con `npx tauri build` o usa `npm run desktop:install`.\",\n );\n return 1;\n }\n\n let server: ChildProcess | null = null;\n let startedServer = false;\n\n if (await isServerUp()) {\n console.log(`• Usando el servidor ya activo en ${appUrl()}`);\n } else {\n console.log(\"Arrancando el servidor local…\");\n server = await spawnServer(cwd, env);\n if (!server) {\n console.error(\n \"No se pudo arrancar el servidor.\\n\" +\n \"Si usas el código fuente, entra en el repositorio y ejecuta:\\n\" +\n \" npm ci && npm run build && npm run desktop:install\",\n );\n return 1;\n }\n startedServer = true;\n if (!(await waitForServer())) {\n console.error(\"El servidor no respondió a tiempo.\");\n server.kill(\"SIGTERM\");\n return 1;\n }\n }\n\n const stopServer = (): void => {\n if (startedServer && server && !server.killed) {\n try {\n server.kill(\"SIGTERM\");\n } catch {\n // already gone\n }\n }\n };\n\n const onSignal = (): void => {\n stopServer();\n process.exit(0);\n };\n process.once(\"SIGINT\", onSignal);\n process.once(\"SIGTERM\", onSignal);\n\n console.log(\"Abriendo la ventana de OpenExpert…\");\n const code = await new Promise<number>((resolve) => {\n const win = spawn(bin, [], { stdio: \"inherit\", env });\n win.on(\"exit\", (c) => resolve(c ?? 0));\n });\n\n stopServer();\n return code;\n}\n","// SPDX-License-Identifier: MIT\n// Interactive setup wizard: pick a model provider, write config + secrets.\n\nimport * as p from \"@clack/prompts\";\nimport {\n DEFAULT_OLLAMA_URL,\n detectOllama,\n readConfig,\n writeConfig,\n writeSecrets,\n type FileConfig,\n type Provider,\n type Secrets,\n} from \"./lib.js\";\n\nexport type WizardResult = {\n config: FileConfig;\n secrets: Secrets;\n provider: Provider;\n modelId: string;\n started: boolean;\n};\n\nfunction bail(): never {\n p.cancel(\"Configuración cancelada.\");\n process.exit(1);\n}\n\nexport async function runWizard(\n opts: { cwd?: string; assumeYes?: boolean } = {},\n): Promise<WizardResult> {\n const cwd = opts.cwd ?? process.cwd();\n const existing = readConfig(cwd);\n const nonInteractive = opts.assumeYes || !process.stdin.isTTY;\n\n if (nonInteractive) {\n const ollama = await detectOllama();\n const config: FileConfig = {\n modelProvider: \"ollama\",\n modelId: ollama.models[0] ?? \"llama3.1\",\n ollamaBaseUrl: DEFAULT_OLLAMA_URL,\n dataDir: \"~/.openexpert\",\n };\n writeConfig(config, cwd);\n return { config, secrets: {}, provider: \"ollama\", modelId: config.modelId!, started: false };\n }\n\n p.intro(\"OpenExpert — configuración\");\n\n const spin = p.spinner();\n spin.start(\"Buscando Ollama local…\");\n const ollama = await detectOllama();\n spin.stop(\n ollama.up\n ? `Ollama detectado (${ollama.models.length} modelo(s))`\n : \"No se ha detectado Ollama\",\n );\n\n const choice = await p.select({\n message: \"¿Qué modelo quieres usar?\",\n options: [\n ...(ollama.up\n ? [\n {\n value: \"ollama\" as const,\n label: \"Modelo local (Ollama)\",\n hint: \"sin claves, datos en tu equipo\",\n },\n ]\n : []),\n { value: \"google\" as const, label: \"Google Gemini (clave gratuita)\", hint: \"AI Studio\" },\n { value: \"openai-compatible\" as const, label: \"Otro endpoint compatible\", hint: \"BYOK\" },\n ...(!ollama.up\n ? [{ value: \"install-ollama\" as const, label: \"Instalar Ollama\", hint: \"te muestro cómo\" }]\n : []),\n ],\n });\n if (p.isCancel(choice)) bail();\n\n if (choice === \"install-ollama\") {\n p.note(\n \"Instala Ollama y descarga un modelo:\\n\\n\" +\n \" curl -fsSL https://ollama.com/install.sh | sh\\n\" +\n \" ollama pull llama3.1\\n\\n\" +\n \"Vuelve a ejecutar `openexpert init` cuando esté listo.\",\n \"Instalar Ollama\",\n );\n p.outro(\"Sin cambios.\");\n process.exit(0);\n }\n\n const config: FileConfig = { dataDir: \"~/.openexpert\" };\n const secrets: Secrets = {};\n let modelId = \"llama3.1\";\n\n if (choice === \"ollama\") {\n const models = ollama.models.length ? ollama.models : [\"llama3.1\"];\n const picked = await p.select({\n message: \"Modelo de Ollama\",\n options: models.map((m) => ({ value: m, label: m })),\n });\n if (p.isCancel(picked)) bail();\n modelId = String(picked);\n config.modelProvider = \"ollama\";\n config.modelId = modelId;\n config.ollamaBaseUrl = DEFAULT_OLLAMA_URL;\n } else if (choice === \"google\") {\n const apiKey = await p.password({ message: \"GOOGLE_API_KEY (AI Studio)\" });\n if (p.isCancel(apiKey)) bail();\n const id = await p.text({ message: \"Modelo\", initialValue: \"gemini-2.5-flash\" });\n if (p.isCancel(id)) bail();\n modelId = String(id) || \"gemini-2.5-flash\";\n config.modelProvider = \"google\";\n config.modelId = modelId;\n secrets.GOOGLE_API_KEY = String(apiKey);\n } else if (choice === \"openai-compatible\") {\n const baseURL = await p.text({ message: \"Base URL (…/v1)\", initialValue: \"\" });\n if (p.isCancel(baseURL)) bail();\n const apiKey = await p.password({ message: \"API key\" });\n if (p.isCancel(apiKey)) bail();\n const id = await p.text({ message: \"Modelo\", initialValue: \"gpt-4o-mini\" });\n if (p.isCancel(id)) bail();\n modelId = String(id) || \"gpt-4o-mini\";\n config.modelProvider = \"openai-compatible\";\n config.modelId = modelId;\n secrets.OPENEXPERT_BASE_URL = String(baseURL);\n secrets.OPENEXPERT_MODEL_KEY = String(apiKey);\n }\n\n writeConfig({ ...existing, ...config }, cwd);\n if (Object.keys(secrets).length) writeSecrets({ ...secrets }, cwd);\n\n p.outro(\n `Configuración guardada en openexpert.json (modelo: ${config.modelProvider}:${modelId}).`,\n );\n\n const go = await p.confirm({ message: \"¿Arrancar ahora?\", initialValue: true });\n if (p.isCancel(go)) bail();\n\n return {\n config: { ...existing, ...config },\n secrets,\n provider: config.modelProvider as Provider,\n modelId,\n started: Boolean(go),\n };\n}\n","// SPDX-License-Identifier: MIT\n// OpenCore CLI entry point. Dispatches subcommands. See `run`.\n\nimport { cmdDoctor, cmdFix, cmdModels, cmdServe, cmdUpdate, cmdVersion } from \"./cli/commands.js\";\nimport { cmdDesktop } from \"./cli/desktop.js\";\nimport { runWizard } from \"./cli/wizard.js\";\n\nfunction help(): void {\n console.log(`OpenExpert — motor local\n\nUso:\n openexpert asistente de configuración y arranque\n openexpert init igual que sin argumentos\n openexpert serve arranca la edición local en http://localhost:3000\n openexpert desktop arranca el servidor y abre la ventana de escritorio\n openexpert doctor revisa tu configuración\n openexpert fix auto-configura lo que falte\n openexpert models lista los modelos del proveedor actual\n openexpert update limpia la caché del servidor descargado\n openexpert version muestra la versión\n\nEjemplos:\n npx @openexpert/opencore\n npx @openexpert/opencore serve\n\nDocker (sin Node):\n docker run -p 3000:3000 -v openexpert-data:/data ghcr.io/openexpert-ai/openexpert`);\n}\n\nexport async function run(argv: string[]): Promise<void> {\n const cmd = argv[0] ?? \"\";\n switch (cmd) {\n case \"\":\n case \"init\":\n case \"setup\": {\n const res = await runWizard({});\n if (res.started) process.exit(await cmdServe());\n return;\n }\n case \"doctor\":\n return process.exit(cmdDoctor());\n case \"fix\":\n return process.exit(await cmdFix());\n case \"models\":\n return process.exit(await cmdModels());\n case \"serve\":\n case \"dev\":\n return process.exit(await cmdServe());\n case \"desktop\":\n case \"app\": {\n const code = await cmdDesktop();\n return process.exit(code);\n }\n case \"update\":\n return process.exit(cmdUpdate());\n case \"version\":\n case \"--version\":\n case \"-v\":\n return process.exit(cmdVersion());\n case \"help\":\n case \"--help\":\n case \"-h\":\n help();\n return;\n default:\n console.error(`Comando desconocido: ${cmd}\\n`);\n help();\n return process.exit(1);\n }\n}\n"]}
package/dist/config.d.ts CHANGED
@@ -16,6 +16,16 @@ type OpenExpertConfig = {
16
16
  modelId: string;
17
17
  ollamaBaseUrl: string;
18
18
  dataDir: string;
19
+ /**
20
+ * Google OAuth client ID (non-secret public identifier) for the Drive
21
+ * integration. The secret lives in env or ~/.openexpert/secrets.json.
22
+ */
23
+ googleClientId: string;
24
+ /**
25
+ * Notion OAuth client ID (non-secret public identifier) for the Notion
26
+ * connection. The secret lives in env or ~/.openexpert/secrets.json.
27
+ */
28
+ notionClientId: string;
19
29
  ai: AiConfig;
20
30
  chat: ChatConfig;
21
31
  };
package/dist/config.js CHANGED
@@ -7,7 +7,9 @@ var DEFAULTS = {
7
7
  modelId: "gemini-2.5-flash",
8
8
  ollamaBaseUrl: "http://localhost:11434",
9
9
  dataDir: "~/.openexpert",
10
- ai: { temperature: 0.2, topP: 1, maxOutputTokens: 4096 },
10
+ googleClientId: "",
11
+ notionClientId: "",
12
+ ai: { temperature: 0.5, topP: 1, maxOutputTokens: 4096 },
11
13
  chat: {
12
14
  maxSteps: 50,
13
15
  injectionGuard: true,
@@ -46,6 +48,8 @@ function loadConfig(cwd = process.cwd()) {
46
48
  modelId: env["OPENEXPERT_MODEL_ID"] ?? file.modelId ?? DEFAULTS.modelId,
47
49
  ollamaBaseUrl: env["OLLAMA_BASE_URL"] ?? file.ollamaBaseUrl ?? DEFAULTS.ollamaBaseUrl,
48
50
  dataDir: expandHome(env["OPENEXPERT_DATA_DIR"] ?? file.dataDir ?? DEFAULTS.dataDir),
51
+ googleClientId: env["OPENEXPERT_GOOGLE_CLIENT_ID"] ?? file.googleClientId ?? DEFAULTS.googleClientId,
52
+ notionClientId: env["OPENEXPERT_NOTION_CLIENT_ID"] ?? file.notionClientId ?? DEFAULTS.notionClientId,
49
53
  ai: {
50
54
  temperature: num(fileAi.temperature, DEFAULTS.ai.temperature),
51
55
  topP: num(fileAi.topP, DEFAULTS.ai.topP),
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts"],"names":[],"mappings":";;;;AA8BO,IAAM,QAAA,GAA6B;AAAA,EACxC,aAAA,EAAe,QAAA;AAAA,EACf,OAAA,EAAS,kBAAA;AAAA,EACT,aAAA,EAAe,wBAAA;AAAA,EACf,OAAA,EAAS,eAAA;AAAA,EACT,IAAI,EAAE,WAAA,EAAa,KAAK,IAAA,EAAM,CAAA,EAAG,iBAAiB,IAAA,EAAK;AAAA,EACvD,IAAA,EAAM;AAAA,IACJ,QAAA,EAAU,EAAA;AAAA,IACV,cAAA,EAAgB,IAAA;AAAA,IAChB,wBAAwB,EAAC;AAAA,IACzB,aAAA,EAAe;AAAA;AAEnB;AAEA,SAAS,WAAW,CAAA,EAAmB;AACrC,EAAA,IAAI,CAAA,CAAE,UAAA,CAAW,IAAI,CAAA,EAAG;AACtB,IAAA,MAAM,IAAA,GAAO,QAAQ,GAAA,CAAI,MAAM,KAAK,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA,IAAK,GAAA;AAClE,IAAA,OAAO,IAAA,CAAK,IAAA,EAAM,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA;AAAA,EAC9B;AACA,EAAA,OAAO,CAAA;AACT;AAEA,SAAS,GAAA,CAAI,OAAgB,QAAA,EAA0B;AACrD,EAAA,MAAM,IAAI,OAAO,KAAA,KAAU,QAAA,GAAW,MAAA,CAAO,KAAK,CAAA,GAAI,KAAA;AACtD,EAAA,OAAO,OAAO,CAAA,KAAM,QAAA,IAAY,OAAO,QAAA,CAAS,CAAC,IAAI,CAAA,GAAI,QAAA;AAC3D;AAEO,SAAS,UAAA,CAAW,GAAA,GAAc,OAAA,CAAQ,GAAA,EAAI,EAAqB;AACxE,EAAA,IAAI,OAAkC,EAAC;AACvC,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,EAAK,iBAAiB,CAAA;AACxC,EAAA,IAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AACpB,IAAA,IAAI;AACF,MAAA,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,YAAA,CAAa,IAAA,EAAM,MAAM,CAAC,CAAA;AAAA,IAC9C,CAAA,CAAA,MAAQ;AAEN,MAAA,IAAA,GAAO,EAAC;AAAA,IACV;AAAA,EACF;AACA,EAAA,MAAM,MAAM,OAAA,CAAQ,GAAA;AACpB,EAAA,MAAM,WAAA,GAAc,IAAI,2BAA2B,CAAA;AACnD,EAAA,MAAM,kBACJ,WAAA,KAAgB,QAAA,IAAY,gBAAgB,QAAA,IAAY,WAAA,KAAgB,sBACpE,WAAA,GACA,MAAA;AAEN,EAAA,MAAM,MAAA,GAA4B,IAAA,CAAK,EAAA,IAAM,EAAC;AAC9C,EAAA,MAAM,QAAA,GAAgC,IAAA,CAAK,IAAA,IAAQ,EAAC;AAEpD,EAAA,OAAO;AAAA,IACL,aAAA,EAAe,eAAA,IAAmB,IAAA,CAAK,aAAA,IAAiB,QAAA,CAAS,aAAA;AAAA,IACjE,SAAS,GAAA,CAAI,qBAAqB,CAAA,IAAK,IAAA,CAAK,WAAW,QAAA,CAAS,OAAA;AAAA,IAChE,eAAe,GAAA,CAAI,iBAAiB,CAAA,IAAK,IAAA,CAAK,iBAAiB,QAAA,CAAS,aAAA;AAAA,IACxE,OAAA,EAAS,WAAW,GAAA,CAAI,qBAAqB,KAAK,IAAA,CAAK,OAAA,IAAW,SAAS,OAAO,CAAA;AAAA,IAClF,EAAA,EAAI;AAAA,MACF,aAAa,GAAA,CAAI,MAAA,CAAO,WAAA,EAAa,QAAA,CAAS,GAAG,WAAW,CAAA;AAAA,MAC5D,MAAM,GAAA,CAAI,MAAA,CAAO,IAAA,EAAM,QAAA,CAAS,GAAG,IAAI,CAAA;AAAA,MACvC,iBAAiB,GAAA,CAAI,MAAA,CAAO,eAAA,EAAiB,QAAA,CAAS,GAAG,eAAe;AAAA,KAC1E;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,UAAU,GAAA,CAAI,QAAA,CAAS,QAAA,EAAU,QAAA,CAAS,KAAK,QAAQ,CAAA;AAAA,MACvD,cAAA,EAAgB,QAAA,CAAS,cAAA,IAAkB,QAAA,CAAS,IAAA,CAAK,cAAA;AAAA,MACzD,wBAAwB,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,sBAAsB,IACjE,QAAA,CAAS,sBAAA,CAAuB,MAAA,CAAO,CAAC,MAAmB,OAAO,CAAA,KAAM,QAAQ,CAAA,GAChF,SAAS,IAAA,CAAK,sBAAA;AAAA,MAClB,eAAe,GAAA,CAAI,QAAA,CAAS,aAAA,EAAe,QAAA,CAAS,KAAK,aAAa;AAAA;AACxE,GACF;AACF","file":"config.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — local configuration.\n// Precedence: OPENEXPERT_* env vars > openexpert.json > defaults.\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModelProviderId } from \"./model-provider.js\";\n\nexport type AiConfig = {\n temperature: number;\n topP: number;\n maxOutputTokens: number;\n};\n\nexport type ChatConfig = {\n maxSteps: number;\n injectionGuard: boolean;\n injectionExtraPatterns: string[];\n retentionDays: number;\n};\n\nexport type OpenExpertConfig = {\n modelProvider: ModelProviderId;\n modelId: string;\n ollamaBaseUrl: string;\n dataDir: string;\n ai: AiConfig;\n chat: ChatConfig;\n};\n\nexport const DEFAULTS: OpenExpertConfig = {\n modelProvider: \"google\",\n modelId: \"gemini-2.5-flash\",\n ollamaBaseUrl: \"http://localhost:11434\",\n dataDir: \"~/.openexpert\",\n ai: { temperature: 0.2, topP: 1, maxOutputTokens: 4096 },\n chat: {\n maxSteps: 50,\n injectionGuard: true,\n injectionExtraPatterns: [],\n retentionDays: 30,\n },\n};\n\nfunction expandHome(p: string): string {\n if (p.startsWith(\"~/\")) {\n const home = process.env[\"HOME\"] || process.env[\"USERPROFILE\"] || \"~\";\n return join(home, p.slice(2));\n }\n return p;\n}\n\nfunction num(value: unknown, fallback: number): number {\n const n = typeof value === \"string\" ? Number(value) : value;\n return typeof n === \"number\" && Number.isFinite(n) ? n : fallback;\n}\n\nexport function loadConfig(cwd: string = process.cwd()): OpenExpertConfig {\n let file: Partial<OpenExpertConfig> = {};\n const path = join(cwd, \"openexpert.json\");\n if (existsSync(path)) {\n try {\n file = JSON.parse(readFileSync(path, \"utf8\")) as Partial<OpenExpertConfig>;\n } catch {\n // Broken file: ignore it, env/defaults win.\n file = {};\n }\n }\n const env = process.env;\n const providerEnv = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const providerFromEnv: ModelProviderId | undefined =\n providerEnv === \"google\" || providerEnv === \"ollama\" || providerEnv === \"openai-compatible\"\n ? providerEnv\n : undefined;\n\n const fileAi: Partial<AiConfig> = file.ai ?? {};\n const fileChat: Partial<ChatConfig> = file.chat ?? {};\n\n return {\n modelProvider: providerFromEnv ?? file.modelProvider ?? DEFAULTS.modelProvider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] ?? file.modelId ?? DEFAULTS.modelId,\n ollamaBaseUrl: env[\"OLLAMA_BASE_URL\"] ?? file.ollamaBaseUrl ?? DEFAULTS.ollamaBaseUrl,\n dataDir: expandHome(env[\"OPENEXPERT_DATA_DIR\"] ?? file.dataDir ?? DEFAULTS.dataDir),\n ai: {\n temperature: num(fileAi.temperature, DEFAULTS.ai.temperature),\n topP: num(fileAi.topP, DEFAULTS.ai.topP),\n maxOutputTokens: num(fileAi.maxOutputTokens, DEFAULTS.ai.maxOutputTokens),\n },\n chat: {\n maxSteps: num(fileChat.maxSteps, DEFAULTS.chat.maxSteps),\n injectionGuard: fileChat.injectionGuard ?? DEFAULTS.chat.injectionGuard,\n injectionExtraPatterns: Array.isArray(fileChat.injectionExtraPatterns)\n ? fileChat.injectionExtraPatterns.filter((p): p is string => typeof p === \"string\")\n : DEFAULTS.chat.injectionExtraPatterns,\n retentionDays: num(fileChat.retentionDays, DEFAULTS.chat.retentionDays),\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/config.ts"],"names":[],"mappings":"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","file":"config.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — local configuration.\n// Precedence: OPENEXPERT_* env vars > openexpert.json > defaults.\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModelProviderId } from \"./model-provider.js\";\n\nexport type AiConfig = {\n temperature: number;\n topP: number;\n maxOutputTokens: number;\n};\n\nexport type ChatConfig = {\n maxSteps: number;\n injectionGuard: boolean;\n injectionExtraPatterns: string[];\n retentionDays: number;\n};\n\nexport type OpenExpertConfig = {\n modelProvider: ModelProviderId;\n modelId: string;\n ollamaBaseUrl: string;\n dataDir: string;\n /**\n * Google OAuth client ID (non-secret public identifier) for the Drive\n * integration. The secret lives in env or ~/.openexpert/secrets.json.\n */\n googleClientId: string;\n /**\n * Notion OAuth client ID (non-secret public identifier) for the Notion\n * connection. The secret lives in env or ~/.openexpert/secrets.json.\n */\n notionClientId: string;\n ai: AiConfig;\n chat: ChatConfig;\n};\n\nexport const DEFAULTS: OpenExpertConfig = {\n modelProvider: \"google\",\n modelId: \"gemini-2.5-flash\",\n ollamaBaseUrl: \"http://localhost:11434\",\n dataDir: \"~/.openexpert\",\n googleClientId: \"\",\n notionClientId: \"\",\n ai: { temperature: 0.5, topP: 1, maxOutputTokens: 4096 },\n chat: {\n maxSteps: 50,\n injectionGuard: true,\n injectionExtraPatterns: [],\n retentionDays: 30,\n },\n};\n\nfunction expandHome(p: string): string {\n if (p.startsWith(\"~/\")) {\n const home = process.env[\"HOME\"] || process.env[\"USERPROFILE\"] || \"~\";\n return join(home, p.slice(2));\n }\n return p;\n}\n\nfunction num(value: unknown, fallback: number): number {\n const n = typeof value === \"string\" ? Number(value) : value;\n return typeof n === \"number\" && Number.isFinite(n) ? n : fallback;\n}\n\nexport function loadConfig(cwd: string = process.cwd()): OpenExpertConfig {\n let file: Partial<OpenExpertConfig> = {};\n const path = join(cwd, \"openexpert.json\");\n if (existsSync(path)) {\n try {\n file = JSON.parse(readFileSync(path, \"utf8\")) as Partial<OpenExpertConfig>;\n } catch {\n // Broken file: ignore it, env/defaults win.\n file = {};\n }\n }\n const env = process.env;\n const providerEnv = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const providerFromEnv: ModelProviderId | undefined =\n providerEnv === \"google\" || providerEnv === \"ollama\" || providerEnv === \"openai-compatible\"\n ? providerEnv\n : undefined;\n\n const fileAi: Partial<AiConfig> = file.ai ?? {};\n const fileChat: Partial<ChatConfig> = file.chat ?? {};\n\n return {\n modelProvider: providerFromEnv ?? file.modelProvider ?? DEFAULTS.modelProvider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] ?? file.modelId ?? DEFAULTS.modelId,\n ollamaBaseUrl: env[\"OLLAMA_BASE_URL\"] ?? file.ollamaBaseUrl ?? DEFAULTS.ollamaBaseUrl,\n dataDir: expandHome(env[\"OPENEXPERT_DATA_DIR\"] ?? file.dataDir ?? DEFAULTS.dataDir),\n googleClientId:\n env[\"OPENEXPERT_GOOGLE_CLIENT_ID\"] ?? file.googleClientId ?? DEFAULTS.googleClientId,\n notionClientId:\n env[\"OPENEXPERT_NOTION_CLIENT_ID\"] ?? file.notionClientId ?? DEFAULTS.notionClientId,\n ai: {\n temperature: num(fileAi.temperature, DEFAULTS.ai.temperature),\n topP: num(fileAi.topP, DEFAULTS.ai.topP),\n maxOutputTokens: num(fileAi.maxOutputTokens, DEFAULTS.ai.maxOutputTokens),\n },\n chat: {\n maxSteps: num(fileChat.maxSteps, DEFAULTS.chat.maxSteps),\n injectionGuard: fileChat.injectionGuard ?? DEFAULTS.chat.injectionGuard,\n injectionExtraPatterns: Array.isArray(fileChat.injectionExtraPatterns)\n ? fileChat.injectionExtraPatterns.filter((p): p is string => typeof p === \"string\")\n : DEFAULTS.chat.injectionExtraPatterns,\n retentionDays: num(fileChat.retentionDays, DEFAULTS.chat.retentionDays),\n },\n };\n}\n"]}
package/dist/index.js CHANGED
@@ -7,7 +7,9 @@ var DEFAULTS = {
7
7
  modelId: "gemini-2.5-flash",
8
8
  ollamaBaseUrl: "http://localhost:11434",
9
9
  dataDir: "~/.openexpert",
10
- ai: { temperature: 0.2, topP: 1, maxOutputTokens: 4096 },
10
+ googleClientId: "",
11
+ notionClientId: "",
12
+ ai: { temperature: 0.5, topP: 1, maxOutputTokens: 4096 },
11
13
  chat: {
12
14
  maxSteps: 50,
13
15
  injectionGuard: true,
@@ -46,6 +48,8 @@ function loadConfig(cwd = process.cwd()) {
46
48
  modelId: env["OPENEXPERT_MODEL_ID"] ?? file.modelId ?? DEFAULTS.modelId,
47
49
  ollamaBaseUrl: env["OLLAMA_BASE_URL"] ?? file.ollamaBaseUrl ?? DEFAULTS.ollamaBaseUrl,
48
50
  dataDir: expandHome(env["OPENEXPERT_DATA_DIR"] ?? file.dataDir ?? DEFAULTS.dataDir),
51
+ googleClientId: env["OPENEXPERT_GOOGLE_CLIENT_ID"] ?? file.googleClientId ?? DEFAULTS.googleClientId,
52
+ notionClientId: env["OPENEXPERT_NOTION_CLIENT_ID"] ?? file.notionClientId ?? DEFAULTS.notionClientId,
49
53
  ai: {
50
54
  temperature: num(fileAi.temperature, DEFAULTS.ai.temperature),
51
55
  topP: num(fileAi.topP, DEFAULTS.ai.topP),
@@ -84,8 +88,14 @@ function selectModel(env = process.env) {
84
88
  keySource: "GOOGLE_API_KEY"
85
89
  };
86
90
  }
91
+ function stripTrailingSlashes(value) {
92
+ let end = value.length;
93
+ while (end > 0 && value[end - 1] === "/") end -= 1;
94
+ return value.slice(0, end);
95
+ }
87
96
  function ollamaApiBaseUrl(baseUrl) {
88
- return baseUrl.trim().replace(/\/+$/, "").replace(/\/v1$/, "");
97
+ const base = stripTrailingSlashes(baseUrl.trim());
98
+ return base.endsWith("/v1") ? base.slice(0, -3) : base;
89
99
  }
90
100
  function ollamaChatBaseUrl(baseUrl) {
91
101
  return `${ollamaApiBaseUrl(baseUrl)}/v1`;
@@ -105,7 +115,6 @@ function missingKeyHint(sel, env = process.env) {
105
115
  var LOCAL_TABLES = [
106
116
  "experts",
107
117
  "integrations",
108
- "processes",
109
118
  "activity",
110
119
  "chat_messages",
111
120
  "deals",
@@ -128,14 +137,12 @@ var TOOL_CATALOG = [
128
137
  { name: "list_overdue_invoices", domain: "finanzas", needs: "read", proposesOnly: false },
129
138
  { name: "get_campaign_performance", domain: "marketing", needs: "read", proposesOnly: false },
130
139
  { name: "get_churn_risk", domain: "general", needs: "read", proposesOnly: false },
131
- { name: "list_processes", domain: "transversal", needs: "read", proposesOnly: false },
132
140
  { name: "search_drive", domain: "drive", needs: "read", proposesOnly: false },
133
141
  { name: "read_drive_file", domain: "drive", needs: "read", proposesOnly: false },
134
142
  { name: "create_drive_file", domain: "drive", needs: "exec", proposesOnly: false },
135
143
  { name: "update_drive_file", domain: "drive", needs: "exec", proposesOnly: false },
136
144
  { name: "propose_invoice_reminders", domain: "finanzas", needs: "exec", proposesOnly: true },
137
145
  { name: "propose_pause_campaigns", domain: "marketing", needs: "exec", proposesOnly: true },
138
- { name: "request_process_run", domain: "transversal", needs: "exec", proposesOnly: true },
139
146
  { name: "open_expert_form", domain: "transversal", needs: "exec", proposesOnly: false }
140
147
  ];
141
148
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config.ts","../src/model-provider.ts","../src/storage.ts","../src/secrets.ts","../src/tools.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — local configuration.\n// Precedence: OPENEXPERT_* env vars > openexpert.json > defaults.\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModelProviderId } from \"./model-provider.js\";\n\nexport type AiConfig = {\n temperature: number;\n topP: number;\n maxOutputTokens: number;\n};\n\nexport type ChatConfig = {\n maxSteps: number;\n injectionGuard: boolean;\n injectionExtraPatterns: string[];\n retentionDays: number;\n};\n\nexport type OpenExpertConfig = {\n modelProvider: ModelProviderId;\n modelId: string;\n ollamaBaseUrl: string;\n dataDir: string;\n ai: AiConfig;\n chat: ChatConfig;\n};\n\nexport const DEFAULTS: OpenExpertConfig = {\n modelProvider: \"google\",\n modelId: \"gemini-2.5-flash\",\n ollamaBaseUrl: \"http://localhost:11434\",\n dataDir: \"~/.openexpert\",\n ai: { temperature: 0.2, topP: 1, maxOutputTokens: 4096 },\n chat: {\n maxSteps: 50,\n injectionGuard: true,\n injectionExtraPatterns: [],\n retentionDays: 30,\n },\n};\n\nfunction expandHome(p: string): string {\n if (p.startsWith(\"~/\")) {\n const home = process.env[\"HOME\"] || process.env[\"USERPROFILE\"] || \"~\";\n return join(home, p.slice(2));\n }\n return p;\n}\n\nfunction num(value: unknown, fallback: number): number {\n const n = typeof value === \"string\" ? Number(value) : value;\n return typeof n === \"number\" && Number.isFinite(n) ? n : fallback;\n}\n\nexport function loadConfig(cwd: string = process.cwd()): OpenExpertConfig {\n let file: Partial<OpenExpertConfig> = {};\n const path = join(cwd, \"openexpert.json\");\n if (existsSync(path)) {\n try {\n file = JSON.parse(readFileSync(path, \"utf8\")) as Partial<OpenExpertConfig>;\n } catch {\n // Broken file: ignore it, env/defaults win.\n file = {};\n }\n }\n const env = process.env;\n const providerEnv = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const providerFromEnv: ModelProviderId | undefined =\n providerEnv === \"google\" || providerEnv === \"ollama\" || providerEnv === \"openai-compatible\"\n ? providerEnv\n : undefined;\n\n const fileAi: Partial<AiConfig> = file.ai ?? {};\n const fileChat: Partial<ChatConfig> = file.chat ?? {};\n\n return {\n modelProvider: providerFromEnv ?? file.modelProvider ?? DEFAULTS.modelProvider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] ?? file.modelId ?? DEFAULTS.modelId,\n ollamaBaseUrl: env[\"OLLAMA_BASE_URL\"] ?? file.ollamaBaseUrl ?? DEFAULTS.ollamaBaseUrl,\n dataDir: expandHome(env[\"OPENEXPERT_DATA_DIR\"] ?? file.dataDir ?? DEFAULTS.dataDir),\n ai: {\n temperature: num(fileAi.temperature, DEFAULTS.ai.temperature),\n topP: num(fileAi.topP, DEFAULTS.ai.topP),\n maxOutputTokens: num(fileAi.maxOutputTokens, DEFAULTS.ai.maxOutputTokens),\n },\n chat: {\n maxSteps: num(fileChat.maxSteps, DEFAULTS.chat.maxSteps),\n injectionGuard: fileChat.injectionGuard ?? DEFAULTS.chat.injectionGuard,\n injectionExtraPatterns: Array.isArray(fileChat.injectionExtraPatterns)\n ? fileChat.injectionExtraPatterns.filter((p): p is string => typeof p === \"string\")\n : DEFAULTS.chat.injectionExtraPatterns,\n retentionDays: num(fileChat.retentionDays, DEFAULTS.chat.retentionDays),\n },\n };\n}\n","// SPDX-License-Identifier: MIT\n// OpenCore — which model to use. The harness is stable; the model is configuration.\n//\n// Providers:\n// - google: Gemini via GOOGLE_API_KEY (free API key).\n// - ollama: Local model via Ollama's OpenAI-compatible endpoint.\n// - openai-compatible: Any OpenAI-compatible endpoint with your own key.\n\nexport type ModelProviderId = \"google\" | \"ollama\" | \"openai-compatible\";\n\nexport type ModelSelection = {\n provider: ModelProviderId;\n modelId: string;\n /** Where the key comes from, without exposing the value. */\n keySource: \"GOOGLE_API_KEY\" | \"OPENEXPERT_MODEL_KEY\" | \"none (local)\";\n};\n\nexport function selectModel(env: NodeJS.ProcessEnv = process.env): ModelSelection {\n const raw = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const provider: ModelProviderId =\n raw === \"ollama\" || raw === \"openai-compatible\" ? raw : \"google\";\n\n if (provider === \"ollama\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"llama3.1\",\n keySource: \"none (local)\",\n };\n }\n if (provider === \"openai-compatible\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gpt-4o-mini\",\n keySource: \"OPENEXPERT_MODEL_KEY\",\n };\n }\n return {\n provider: \"google\",\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gemini-2.5-flash\",\n keySource: \"GOOGLE_API_KEY\",\n };\n}\n\n/**\n * Ollama exposes two surfaces: the native API at the server root (`/api/*`)\n * and an OpenAI-compatible API under `/v1`. Callers may configure either\n * shape, so normalise before using it.\n */\nexport function ollamaApiBaseUrl(baseUrl: string): string {\n return baseUrl.trim().replace(/\\/+$/, \"\").replace(/\\/v1$/, \"\");\n}\n\nexport function ollamaChatBaseUrl(baseUrl: string): string {\n return `${ollamaApiBaseUrl(baseUrl)}/v1`;\n}\n\nexport function missingKeyHint(\n sel: ModelSelection,\n env: NodeJS.ProcessEnv = process.env,\n): string | null {\n if (sel.provider === \"ollama\") return null;\n if (sel.provider === \"google\" && !env[\"GOOGLE_API_KEY\"]) {\n return \"Falta GOOGLE_API_KEY (o usa OPENEXPERT_MODEL_PROVIDER=ollama para modelo local).\";\n }\n if (sel.provider === \"openai-compatible\" && !env[\"OPENEXPERT_MODEL_KEY\"]) {\n return \"Falta OPENEXPERT_MODEL_KEY para tu endpoint OpenAI-compatible.\";\n }\n return null;\n}\n","// SPDX-License-Identifier: MIT\n// OpenCore — storage interface. OpenExpert is local-first: implementations\n// are backed by the local data directory (SQLite / files).\n\nexport type TableName =\n | \"experts\"\n | \"integrations\"\n | \"processes\"\n | \"activity\"\n | \"chat_messages\"\n | \"deals\"\n | \"invoices\"\n | \"campaigns\"\n | \"accounts\"\n | \"settings\";\n\nexport type Row = Record<string, unknown>;\n\nexport interface Storage {\n readonly kind: \"file\";\n list(table: TableName): Promise<Row[]>;\n insert(table: TableName, rows: Row | Row[]): Promise<void>;\n}\n\nexport const LOCAL_TABLES: TableName[] = [\n \"experts\",\n \"integrations\",\n \"processes\",\n \"activity\",\n \"chat_messages\",\n \"deals\",\n \"invoices\",\n \"campaigns\",\n \"accounts\",\n \"settings\",\n];\n","// SPDX-License-Identifier: MIT\n// OpenCore — where Google Drive tokens live.\n// Cloud: google_tokens table (only the server touches it).\n// Local: a file on your machine (~/.openexpert/credentials.json by\n// default), never uploaded to Git.\n\nexport type DriveTokenRow = {\n access_token: string;\n refresh_token: string | null;\n expires_at: string;\n scopes: string;\n updated_at: string;\n};\n\nexport interface TokenStore {\n readonly kind: \"file\";\n load(userId: string): Promise<DriveTokenRow | null>;\n save(userId: string, row: DriveTokenRow): Promise<void>;\n remove(userId: string): Promise<void>;\n}\n\nexport function credentialsPath(dataDir: string): string {\n const home = dataDir.startsWith(\"~/\")\n ? `${process.env[\"HOME\"] || \"~\"}/${dataDir.slice(2)}`\n : dataDir;\n return `${home.replace(/\\/$/, \"\")}/credentials.json`;\n}\n","// SPDX-License-Identifier: MIT\n// OpenCore — tool catalog (the current 14).\n// The model never touches the database directly: it only calls these\n// functions, and each one checks your permission before answering.\n\nexport type ToolDomain = \"ventas\" | \"finanzas\" | \"marketing\" | \"general\" | \"transversal\" | \"drive\";\n\nexport type ToolDef = {\n name: string;\n domain: ToolDomain;\n needs: \"read\" | \"exec\";\n proposesOnly: boolean;\n};\n\nexport const TOOL_CATALOG: ToolDef[] = [\n { name: \"get_pipeline_summary\", domain: \"ventas\", needs: \"read\", proposesOnly: false },\n { name: \"list_deals\", domain: \"ventas\", needs: \"read\", proposesOnly: false },\n { name: \"list_overdue_invoices\", domain: \"finanzas\", needs: \"read\", proposesOnly: false },\n { name: \"get_campaign_performance\", domain: \"marketing\", needs: \"read\", proposesOnly: false },\n { name: \"get_churn_risk\", domain: \"general\", needs: \"read\", proposesOnly: false },\n { name: \"list_processes\", domain: \"transversal\", needs: \"read\", proposesOnly: false },\n { name: \"search_drive\", domain: \"drive\", needs: \"read\", proposesOnly: false },\n { name: \"read_drive_file\", domain: \"drive\", needs: \"read\", proposesOnly: false },\n { name: \"create_drive_file\", domain: \"drive\", needs: \"exec\", proposesOnly: false },\n { name: \"update_drive_file\", domain: \"drive\", needs: \"exec\", proposesOnly: false },\n { name: \"propose_invoice_reminders\", domain: \"finanzas\", needs: \"exec\", proposesOnly: true },\n { name: \"propose_pause_campaigns\", domain: \"marketing\", needs: \"exec\", proposesOnly: true },\n { name: \"request_process_run\", domain: \"transversal\", needs: \"exec\", proposesOnly: true },\n { name: \"open_expert_form\", domain: \"transversal\", needs: \"exec\", proposesOnly: false },\n];\n"]}
1
+ {"version":3,"sources":["../src/config.ts","../src/model-provider.ts","../src/storage.ts","../src/secrets.ts","../src/tools.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — local configuration.\n// Precedence: OPENEXPERT_* env vars > openexpert.json > defaults.\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { ModelProviderId } from \"./model-provider.js\";\n\nexport type AiConfig = {\n temperature: number;\n topP: number;\n maxOutputTokens: number;\n};\n\nexport type ChatConfig = {\n maxSteps: number;\n injectionGuard: boolean;\n injectionExtraPatterns: string[];\n retentionDays: number;\n};\n\nexport type OpenExpertConfig = {\n modelProvider: ModelProviderId;\n modelId: string;\n ollamaBaseUrl: string;\n dataDir: string;\n /**\n * Google OAuth client ID (non-secret public identifier) for the Drive\n * integration. The secret lives in env or ~/.openexpert/secrets.json.\n */\n googleClientId: string;\n /**\n * Notion OAuth client ID (non-secret public identifier) for the Notion\n * connection. The secret lives in env or ~/.openexpert/secrets.json.\n */\n notionClientId: string;\n ai: AiConfig;\n chat: ChatConfig;\n};\n\nexport const DEFAULTS: OpenExpertConfig = {\n modelProvider: \"google\",\n modelId: \"gemini-2.5-flash\",\n ollamaBaseUrl: \"http://localhost:11434\",\n dataDir: \"~/.openexpert\",\n googleClientId: \"\",\n notionClientId: \"\",\n ai: { temperature: 0.5, topP: 1, maxOutputTokens: 4096 },\n chat: {\n maxSteps: 50,\n injectionGuard: true,\n injectionExtraPatterns: [],\n retentionDays: 30,\n },\n};\n\nfunction expandHome(p: string): string {\n if (p.startsWith(\"~/\")) {\n const home = process.env[\"HOME\"] || process.env[\"USERPROFILE\"] || \"~\";\n return join(home, p.slice(2));\n }\n return p;\n}\n\nfunction num(value: unknown, fallback: number): number {\n const n = typeof value === \"string\" ? Number(value) : value;\n return typeof n === \"number\" && Number.isFinite(n) ? n : fallback;\n}\n\nexport function loadConfig(cwd: string = process.cwd()): OpenExpertConfig {\n let file: Partial<OpenExpertConfig> = {};\n const path = join(cwd, \"openexpert.json\");\n if (existsSync(path)) {\n try {\n file = JSON.parse(readFileSync(path, \"utf8\")) as Partial<OpenExpertConfig>;\n } catch {\n // Broken file: ignore it, env/defaults win.\n file = {};\n }\n }\n const env = process.env;\n const providerEnv = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const providerFromEnv: ModelProviderId | undefined =\n providerEnv === \"google\" || providerEnv === \"ollama\" || providerEnv === \"openai-compatible\"\n ? providerEnv\n : undefined;\n\n const fileAi: Partial<AiConfig> = file.ai ?? {};\n const fileChat: Partial<ChatConfig> = file.chat ?? {};\n\n return {\n modelProvider: providerFromEnv ?? file.modelProvider ?? DEFAULTS.modelProvider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] ?? file.modelId ?? DEFAULTS.modelId,\n ollamaBaseUrl: env[\"OLLAMA_BASE_URL\"] ?? file.ollamaBaseUrl ?? DEFAULTS.ollamaBaseUrl,\n dataDir: expandHome(env[\"OPENEXPERT_DATA_DIR\"] ?? file.dataDir ?? DEFAULTS.dataDir),\n googleClientId:\n env[\"OPENEXPERT_GOOGLE_CLIENT_ID\"] ?? file.googleClientId ?? DEFAULTS.googleClientId,\n notionClientId:\n env[\"OPENEXPERT_NOTION_CLIENT_ID\"] ?? file.notionClientId ?? DEFAULTS.notionClientId,\n ai: {\n temperature: num(fileAi.temperature, DEFAULTS.ai.temperature),\n topP: num(fileAi.topP, DEFAULTS.ai.topP),\n maxOutputTokens: num(fileAi.maxOutputTokens, DEFAULTS.ai.maxOutputTokens),\n },\n chat: {\n maxSteps: num(fileChat.maxSteps, DEFAULTS.chat.maxSteps),\n injectionGuard: fileChat.injectionGuard ?? DEFAULTS.chat.injectionGuard,\n injectionExtraPatterns: Array.isArray(fileChat.injectionExtraPatterns)\n ? fileChat.injectionExtraPatterns.filter((p): p is string => typeof p === \"string\")\n : DEFAULTS.chat.injectionExtraPatterns,\n retentionDays: num(fileChat.retentionDays, DEFAULTS.chat.retentionDays),\n },\n };\n}\n","// SPDX-License-Identifier: MIT\n// OpenCore — which model to use. The harness is stable; the model is configuration.\n//\n// Providers:\n// - google: Gemini via GOOGLE_API_KEY (free API key).\n// - ollama: Local model via Ollama's OpenAI-compatible endpoint.\n// - openai-compatible: Any OpenAI-compatible endpoint with your own key.\n\nexport type ModelProviderId = \"google\" | \"ollama\" | \"openai-compatible\";\n\nexport type ModelSelection = {\n provider: ModelProviderId;\n modelId: string;\n /** Where the key comes from, without exposing the value. */\n keySource: \"GOOGLE_API_KEY\" | \"OPENEXPERT_MODEL_KEY\" | \"none (local)\";\n};\n\nexport function selectModel(env: NodeJS.ProcessEnv = process.env): ModelSelection {\n const raw = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const provider: ModelProviderId =\n raw === \"ollama\" || raw === \"openai-compatible\" ? raw : \"google\";\n\n if (provider === \"ollama\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"llama3.1\",\n keySource: \"none (local)\",\n };\n }\n if (provider === \"openai-compatible\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gpt-4o-mini\",\n keySource: \"OPENEXPERT_MODEL_KEY\",\n };\n }\n return {\n provider: \"google\",\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gemini-2.5-flash\",\n keySource: \"GOOGLE_API_KEY\",\n };\n}\n\n/**\n * Ollama exposes two surfaces: the native API at the server root (`/api/*`)\n * and an OpenAI-compatible API under `/v1`. Callers may configure either\n * shape, so normalise before using it.\n *\n * Trailing slashes are removed with a loop, not with `/\\/+$/`: a regex has to\n * retry at every position of a long run of slashes, which is what CodeQL\n * reports as a polynomial ReDoS on `baseUrl`.\n */\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === \"/\") end -= 1;\n return value.slice(0, end);\n}\n\nexport function ollamaApiBaseUrl(baseUrl: string): string {\n const base = stripTrailingSlashes(baseUrl.trim());\n return base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\n\nexport function ollamaChatBaseUrl(baseUrl: string): string {\n return `${ollamaApiBaseUrl(baseUrl)}/v1`;\n}\n\nexport function missingKeyHint(\n sel: ModelSelection,\n env: NodeJS.ProcessEnv = process.env,\n): string | null {\n if (sel.provider === \"ollama\") return null;\n if (sel.provider === \"google\" && !env[\"GOOGLE_API_KEY\"]) {\n return \"Falta GOOGLE_API_KEY (o usa OPENEXPERT_MODEL_PROVIDER=ollama para modelo local).\";\n }\n if (sel.provider === \"openai-compatible\" && !env[\"OPENEXPERT_MODEL_KEY\"]) {\n return \"Falta OPENEXPERT_MODEL_KEY para tu endpoint OpenAI-compatible.\";\n }\n return null;\n}\n","// SPDX-License-Identifier: MIT\n// OpenCore — storage interface. OpenExpert is local-first: implementations\n// are backed by the local data directory (SQLite / files).\n\nexport type TableName =\n | \"experts\"\n | \"integrations\"\n | \"activity\"\n | \"chat_messages\"\n | \"deals\"\n | \"invoices\"\n | \"campaigns\"\n | \"accounts\"\n | \"settings\";\n\nexport type Row = Record<string, unknown>;\n\nexport interface Storage {\n readonly kind: \"file\";\n list(table: TableName): Promise<Row[]>;\n insert(table: TableName, rows: Row | Row[]): Promise<void>;\n}\n\nexport const LOCAL_TABLES: TableName[] = [\n \"experts\",\n \"integrations\",\n \"activity\",\n \"chat_messages\",\n \"deals\",\n \"invoices\",\n \"campaigns\",\n \"accounts\",\n \"settings\",\n];\n","// SPDX-License-Identifier: MIT\n// OpenCore — where Google Drive tokens live.\n// Cloud: google_tokens table (only the server touches it).\n// Local: a file on your machine (~/.openexpert/credentials.json by\n// default), never uploaded to Git.\n\nexport type DriveTokenRow = {\n access_token: string;\n refresh_token: string | null;\n expires_at: string;\n scopes: string;\n updated_at: string;\n};\n\nexport interface TokenStore {\n readonly kind: \"file\";\n load(userId: string): Promise<DriveTokenRow | null>;\n save(userId: string, row: DriveTokenRow): Promise<void>;\n remove(userId: string): Promise<void>;\n}\n\nexport function credentialsPath(dataDir: string): string {\n const home = dataDir.startsWith(\"~/\")\n ? `${process.env[\"HOME\"] || \"~\"}/${dataDir.slice(2)}`\n : dataDir;\n return `${home.replace(/\\/$/, \"\")}/credentials.json`;\n}\n","// SPDX-License-Identifier: MIT\n// OpenCore — tool catalog (the current 12).\n// The model never touches the database directly: it only calls these\n// functions, and each one checks your permission before answering.\n\nexport type ToolDomain = \"ventas\" | \"finanzas\" | \"marketing\" | \"general\" | \"transversal\" | \"drive\";\n\nexport type ToolDef = {\n name: string;\n domain: ToolDomain;\n needs: \"read\" | \"exec\";\n proposesOnly: boolean;\n};\n\nexport const TOOL_CATALOG: ToolDef[] = [\n { name: \"get_pipeline_summary\", domain: \"ventas\", needs: \"read\", proposesOnly: false },\n { name: \"list_deals\", domain: \"ventas\", needs: \"read\", proposesOnly: false },\n { name: \"list_overdue_invoices\", domain: \"finanzas\", needs: \"read\", proposesOnly: false },\n { name: \"get_campaign_performance\", domain: \"marketing\", needs: \"read\", proposesOnly: false },\n { name: \"get_churn_risk\", domain: \"general\", needs: \"read\", proposesOnly: false },\n { name: \"search_drive\", domain: \"drive\", needs: \"read\", proposesOnly: false },\n { name: \"read_drive_file\", domain: \"drive\", needs: \"read\", proposesOnly: false },\n { name: \"create_drive_file\", domain: \"drive\", needs: \"exec\", proposesOnly: false },\n { name: \"update_drive_file\", domain: \"drive\", needs: \"exec\", proposesOnly: false },\n { name: \"propose_invoice_reminders\", domain: \"finanzas\", needs: \"exec\", proposesOnly: true },\n { name: \"propose_pause_campaigns\", domain: \"marketing\", needs: \"exec\", proposesOnly: true },\n { name: \"open_expert_form\", domain: \"transversal\", needs: \"exec\", proposesOnly: false },\n];\n"]}
@@ -6,11 +6,6 @@ type ModelSelection = {
6
6
  keySource: "GOOGLE_API_KEY" | "OPENEXPERT_MODEL_KEY" | "none (local)";
7
7
  };
8
8
  declare function selectModel(env?: NodeJS.ProcessEnv): ModelSelection;
9
- /**
10
- * Ollama exposes two surfaces: the native API at the server root (`/api/*`)
11
- * and an OpenAI-compatible API under `/v1`. Callers may configure either
12
- * shape, so normalise before using it.
13
- */
14
9
  declare function ollamaApiBaseUrl(baseUrl: string): string;
15
10
  declare function ollamaChatBaseUrl(baseUrl: string): string;
16
11
  declare function missingKeyHint(sel: ModelSelection, env?: NodeJS.ProcessEnv): string | null;
@@ -22,8 +22,14 @@ function selectModel(env = process.env) {
22
22
  keySource: "GOOGLE_API_KEY"
23
23
  };
24
24
  }
25
+ function stripTrailingSlashes(value) {
26
+ let end = value.length;
27
+ while (end > 0 && value[end - 1] === "/") end -= 1;
28
+ return value.slice(0, end);
29
+ }
25
30
  function ollamaApiBaseUrl(baseUrl) {
26
- return baseUrl.trim().replace(/\/+$/, "").replace(/\/v1$/, "");
31
+ const base = stripTrailingSlashes(baseUrl.trim());
32
+ return base.endsWith("/v1") ? base.slice(0, -3) : base;
27
33
  }
28
34
  function ollamaChatBaseUrl(baseUrl) {
29
35
  return `${ollamaApiBaseUrl(baseUrl)}/v1`;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/model-provider.ts"],"names":[],"mappings":";AAiBO,SAAS,WAAA,CAAY,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAqB;AAChF,EAAA,MAAM,GAAA,GAAM,IAAI,2BAA2B,CAAA;AAC3C,EAAA,MAAM,QAAA,GACJ,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,sBAAsB,GAAA,GAAM,QAAA;AAE1D,EAAA,IAAI,aAAa,QAAA,EAAU;AACzB,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,OAAA,EAAS,GAAA,CAAI,qBAAqB,CAAA,IAAK,UAAA;AAAA,MACvC,SAAA,EAAW;AAAA,KACb;AAAA,EACF;AACA,EAAA,IAAI,aAAa,mBAAA,EAAqB;AACpC,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,OAAA,EAAS,GAAA,CAAI,qBAAqB,CAAA,IAAK,aAAA;AAAA,MACvC,SAAA,EAAW;AAAA,KACb;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,QAAA;AAAA,IACV,OAAA,EAAS,GAAA,CAAI,qBAAqB,CAAA,IAAK,kBAAA;AAAA,IACvC,SAAA,EAAW;AAAA,GACb;AACF;AAOO,SAAS,iBAAiB,OAAA,EAAyB;AACxD,EAAA,OAAO,OAAA,CAAQ,MAAK,CAAE,OAAA,CAAQ,QAAQ,EAAE,CAAA,CAAE,OAAA,CAAQ,OAAA,EAAS,EAAE,CAAA;AAC/D;AAEO,SAAS,kBAAkB,OAAA,EAAyB;AACzD,EAAA,OAAO,CAAA,EAAG,gBAAA,CAAiB,OAAO,CAAC,CAAA,GAAA,CAAA;AACrC;AAEO,SAAS,cAAA,CACd,GAAA,EACA,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAClB;AACf,EAAA,IAAI,GAAA,CAAI,QAAA,KAAa,QAAA,EAAU,OAAO,IAAA;AACtC,EAAA,IAAI,IAAI,QAAA,KAAa,QAAA,IAAY,CAAC,GAAA,CAAI,gBAAgB,CAAA,EAAG;AACvD,IAAA,OAAO,kFAAA;AAAA,EACT;AACA,EAAA,IAAI,IAAI,QAAA,KAAa,mBAAA,IAAuB,CAAC,GAAA,CAAI,sBAAsB,CAAA,EAAG;AACxE,IAAA,OAAO,gEAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA;AACT","file":"model-provider.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — which model to use. The harness is stable; the model is configuration.\n//\n// Providers:\n// - google: Gemini via GOOGLE_API_KEY (free API key).\n// - ollama: Local model via Ollama's OpenAI-compatible endpoint.\n// - openai-compatible: Any OpenAI-compatible endpoint with your own key.\n\nexport type ModelProviderId = \"google\" | \"ollama\" | \"openai-compatible\";\n\nexport type ModelSelection = {\n provider: ModelProviderId;\n modelId: string;\n /** Where the key comes from, without exposing the value. */\n keySource: \"GOOGLE_API_KEY\" | \"OPENEXPERT_MODEL_KEY\" | \"none (local)\";\n};\n\nexport function selectModel(env: NodeJS.ProcessEnv = process.env): ModelSelection {\n const raw = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const provider: ModelProviderId =\n raw === \"ollama\" || raw === \"openai-compatible\" ? raw : \"google\";\n\n if (provider === \"ollama\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"llama3.1\",\n keySource: \"none (local)\",\n };\n }\n if (provider === \"openai-compatible\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gpt-4o-mini\",\n keySource: \"OPENEXPERT_MODEL_KEY\",\n };\n }\n return {\n provider: \"google\",\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gemini-2.5-flash\",\n keySource: \"GOOGLE_API_KEY\",\n };\n}\n\n/**\n * Ollama exposes two surfaces: the native API at the server root (`/api/*`)\n * and an OpenAI-compatible API under `/v1`. Callers may configure either\n * shape, so normalise before using it.\n */\nexport function ollamaApiBaseUrl(baseUrl: string): string {\n return baseUrl.trim().replace(/\\/+$/, \"\").replace(/\\/v1$/, \"\");\n}\n\nexport function ollamaChatBaseUrl(baseUrl: string): string {\n return `${ollamaApiBaseUrl(baseUrl)}/v1`;\n}\n\nexport function missingKeyHint(\n sel: ModelSelection,\n env: NodeJS.ProcessEnv = process.env,\n): string | null {\n if (sel.provider === \"ollama\") return null;\n if (sel.provider === \"google\" && !env[\"GOOGLE_API_KEY\"]) {\n return \"Falta GOOGLE_API_KEY (o usa OPENEXPERT_MODEL_PROVIDER=ollama para modelo local).\";\n }\n if (sel.provider === \"openai-compatible\" && !env[\"OPENEXPERT_MODEL_KEY\"]) {\n return \"Falta OPENEXPERT_MODEL_KEY para tu endpoint OpenAI-compatible.\";\n }\n return null;\n}\n"]}
1
+ {"version":3,"sources":["../src/model-provider.ts"],"names":[],"mappings":";AAiBO,SAAS,WAAA,CAAY,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAqB;AAChF,EAAA,MAAM,GAAA,GAAM,IAAI,2BAA2B,CAAA;AAC3C,EAAA,MAAM,QAAA,GACJ,GAAA,KAAQ,QAAA,IAAY,GAAA,KAAQ,sBAAsB,GAAA,GAAM,QAAA;AAE1D,EAAA,IAAI,aAAa,QAAA,EAAU;AACzB,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,OAAA,EAAS,GAAA,CAAI,qBAAqB,CAAA,IAAK,UAAA;AAAA,MACvC,SAAA,EAAW;AAAA,KACb;AAAA,EACF;AACA,EAAA,IAAI,aAAa,mBAAA,EAAqB;AACpC,IAAA,OAAO;AAAA,MACL,QAAA;AAAA,MACA,OAAA,EAAS,GAAA,CAAI,qBAAqB,CAAA,IAAK,aAAA;AAAA,MACvC,SAAA,EAAW;AAAA,KACb;AAAA,EACF;AACA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,QAAA;AAAA,IACV,OAAA,EAAS,GAAA,CAAI,qBAAqB,CAAA,IAAK,kBAAA;AAAA,IACvC,SAAA,EAAW;AAAA,GACb;AACF;AAWA,SAAS,qBAAqB,KAAA,EAAuB;AACnD,EAAA,IAAI,MAAM,KAAA,CAAM,MAAA;AAChB,EAAA,OAAO,MAAM,CAAA,IAAK,KAAA,CAAM,MAAM,CAAC,CAAA,KAAM,KAAK,GAAA,IAAO,CAAA;AACjD,EAAA,OAAO,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA;AAC3B;AAEO,SAAS,iBAAiB,OAAA,EAAyB;AACxD,EAAA,MAAM,IAAA,GAAO,oBAAA,CAAqB,OAAA,CAAQ,IAAA,EAAM,CAAA;AAChD,EAAA,OAAO,IAAA,CAAK,SAAS,KAAK,CAAA,GAAI,KAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GAAI,IAAA;AACpD;AAEO,SAAS,kBAAkB,OAAA,EAAyB;AACzD,EAAA,OAAO,CAAA,EAAG,gBAAA,CAAiB,OAAO,CAAC,CAAA,GAAA,CAAA;AACrC;AAEO,SAAS,cAAA,CACd,GAAA,EACA,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAClB;AACf,EAAA,IAAI,GAAA,CAAI,QAAA,KAAa,QAAA,EAAU,OAAO,IAAA;AACtC,EAAA,IAAI,IAAI,QAAA,KAAa,QAAA,IAAY,CAAC,GAAA,CAAI,gBAAgB,CAAA,EAAG;AACvD,IAAA,OAAO,kFAAA;AAAA,EACT;AACA,EAAA,IAAI,IAAI,QAAA,KAAa,mBAAA,IAAuB,CAAC,GAAA,CAAI,sBAAsB,CAAA,EAAG;AACxE,IAAA,OAAO,gEAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA;AACT","file":"model-provider.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — which model to use. The harness is stable; the model is configuration.\n//\n// Providers:\n// - google: Gemini via GOOGLE_API_KEY (free API key).\n// - ollama: Local model via Ollama's OpenAI-compatible endpoint.\n// - openai-compatible: Any OpenAI-compatible endpoint with your own key.\n\nexport type ModelProviderId = \"google\" | \"ollama\" | \"openai-compatible\";\n\nexport type ModelSelection = {\n provider: ModelProviderId;\n modelId: string;\n /** Where the key comes from, without exposing the value. */\n keySource: \"GOOGLE_API_KEY\" | \"OPENEXPERT_MODEL_KEY\" | \"none (local)\";\n};\n\nexport function selectModel(env: NodeJS.ProcessEnv = process.env): ModelSelection {\n const raw = env[\"OPENEXPERT_MODEL_PROVIDER\"];\n const provider: ModelProviderId =\n raw === \"ollama\" || raw === \"openai-compatible\" ? raw : \"google\";\n\n if (provider === \"ollama\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"llama3.1\",\n keySource: \"none (local)\",\n };\n }\n if (provider === \"openai-compatible\") {\n return {\n provider,\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gpt-4o-mini\",\n keySource: \"OPENEXPERT_MODEL_KEY\",\n };\n }\n return {\n provider: \"google\",\n modelId: env[\"OPENEXPERT_MODEL_ID\"] || \"gemini-2.5-flash\",\n keySource: \"GOOGLE_API_KEY\",\n };\n}\n\n/**\n * Ollama exposes two surfaces: the native API at the server root (`/api/*`)\n * and an OpenAI-compatible API under `/v1`. Callers may configure either\n * shape, so normalise before using it.\n *\n * Trailing slashes are removed with a loop, not with `/\\/+$/`: a regex has to\n * retry at every position of a long run of slashes, which is what CodeQL\n * reports as a polynomial ReDoS on `baseUrl`.\n */\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === \"/\") end -= 1;\n return value.slice(0, end);\n}\n\nexport function ollamaApiBaseUrl(baseUrl: string): string {\n const base = stripTrailingSlashes(baseUrl.trim());\n return base.endsWith(\"/v1\") ? base.slice(0, -3) : base;\n}\n\nexport function ollamaChatBaseUrl(baseUrl: string): string {\n return `${ollamaApiBaseUrl(baseUrl)}/v1`;\n}\n\nexport function missingKeyHint(\n sel: ModelSelection,\n env: NodeJS.ProcessEnv = process.env,\n): string | null {\n if (sel.provider === \"ollama\") return null;\n if (sel.provider === \"google\" && !env[\"GOOGLE_API_KEY\"]) {\n return \"Falta GOOGLE_API_KEY (o usa OPENEXPERT_MODEL_PROVIDER=ollama para modelo local).\";\n }\n if (sel.provider === \"openai-compatible\" && !env[\"OPENEXPERT_MODEL_KEY\"]) {\n return \"Falta OPENEXPERT_MODEL_KEY para tu endpoint OpenAI-compatible.\";\n }\n return null;\n}\n"]}
package/dist/storage.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- type TableName = "experts" | "integrations" | "processes" | "activity" | "chat_messages" | "deals" | "invoices" | "campaigns" | "accounts" | "settings";
1
+ type TableName = "experts" | "integrations" | "activity" | "chat_messages" | "deals" | "invoices" | "campaigns" | "accounts" | "settings";
2
2
  type Row = Record<string, unknown>;
3
3
  interface Storage {
4
4
  readonly kind: "file";
package/dist/storage.js CHANGED
@@ -2,7 +2,6 @@
2
2
  var LOCAL_TABLES = [
3
3
  "experts",
4
4
  "integrations",
5
- "processes",
6
5
  "activity",
7
6
  "chat_messages",
8
7
  "deals",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage.ts"],"names":[],"mappings":";AAwBO,IAAM,YAAA,GAA4B;AAAA,EACvC,SAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,eAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF","file":"storage.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — storage interface. OpenExpert is local-first: implementations\n// are backed by the local data directory (SQLite / files).\n\nexport type TableName =\n | \"experts\"\n | \"integrations\"\n | \"processes\"\n | \"activity\"\n | \"chat_messages\"\n | \"deals\"\n | \"invoices\"\n | \"campaigns\"\n | \"accounts\"\n | \"settings\";\n\nexport type Row = Record<string, unknown>;\n\nexport interface Storage {\n readonly kind: \"file\";\n list(table: TableName): Promise<Row[]>;\n insert(table: TableName, rows: Row | Row[]): Promise<void>;\n}\n\nexport const LOCAL_TABLES: TableName[] = [\n \"experts\",\n \"integrations\",\n \"processes\",\n \"activity\",\n \"chat_messages\",\n \"deals\",\n \"invoices\",\n \"campaigns\",\n \"accounts\",\n \"settings\",\n];\n"]}
1
+ {"version":3,"sources":["../src/storage.ts"],"names":[],"mappings":";AAuBO,IAAM,YAAA,GAA4B;AAAA,EACvC,SAAA;AAAA,EACA,cAAA;AAAA,EACA,UAAA;AAAA,EACA,eAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA;AACF","file":"storage.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — storage interface. OpenExpert is local-first: implementations\n// are backed by the local data directory (SQLite / files).\n\nexport type TableName =\n | \"experts\"\n | \"integrations\"\n | \"activity\"\n | \"chat_messages\"\n | \"deals\"\n | \"invoices\"\n | \"campaigns\"\n | \"accounts\"\n | \"settings\";\n\nexport type Row = Record<string, unknown>;\n\nexport interface Storage {\n readonly kind: \"file\";\n list(table: TableName): Promise<Row[]>;\n insert(table: TableName, rows: Row | Row[]): Promise<void>;\n}\n\nexport const LOCAL_TABLES: TableName[] = [\n \"experts\",\n \"integrations\",\n \"activity\",\n \"chat_messages\",\n \"deals\",\n \"invoices\",\n \"campaigns\",\n \"accounts\",\n \"settings\",\n];\n"]}
package/dist/tools.js CHANGED
@@ -5,14 +5,12 @@ var TOOL_CATALOG = [
5
5
  { name: "list_overdue_invoices", domain: "finanzas", needs: "read", proposesOnly: false },
6
6
  { name: "get_campaign_performance", domain: "marketing", needs: "read", proposesOnly: false },
7
7
  { name: "get_churn_risk", domain: "general", needs: "read", proposesOnly: false },
8
- { name: "list_processes", domain: "transversal", needs: "read", proposesOnly: false },
9
8
  { name: "search_drive", domain: "drive", needs: "read", proposesOnly: false },
10
9
  { name: "read_drive_file", domain: "drive", needs: "read", proposesOnly: false },
11
10
  { name: "create_drive_file", domain: "drive", needs: "exec", proposesOnly: false },
12
11
  { name: "update_drive_file", domain: "drive", needs: "exec", proposesOnly: false },
13
12
  { name: "propose_invoice_reminders", domain: "finanzas", needs: "exec", proposesOnly: true },
14
13
  { name: "propose_pause_campaigns", domain: "marketing", needs: "exec", proposesOnly: true },
15
- { name: "request_process_run", domain: "transversal", needs: "exec", proposesOnly: true },
16
14
  { name: "open_expert_form", domain: "transversal", needs: "exec", proposesOnly: false }
17
15
  ];
18
16
 
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tools.ts"],"names":[],"mappings":";AAcO,IAAM,YAAA,GAA0B;AAAA,EACrC,EAAE,MAAM,sBAAA,EAAwB,MAAA,EAAQ,UAAU,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACrF,EAAE,MAAM,YAAA,EAAc,MAAA,EAAQ,UAAU,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAC3E,EAAE,MAAM,uBAAA,EAAyB,MAAA,EAAQ,YAAY,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACxF,EAAE,MAAM,0BAAA,EAA4B,MAAA,EAAQ,aAAa,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAC5F,EAAE,MAAM,gBAAA,EAAkB,MAAA,EAAQ,WAAW,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAChF,EAAE,MAAM,gBAAA,EAAkB,MAAA,EAAQ,eAAe,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACpF,EAAE,MAAM,cAAA,EAAgB,MAAA,EAAQ,SAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAC5E,EAAE,MAAM,iBAAA,EAAmB,MAAA,EAAQ,SAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAC/E,EAAE,MAAM,mBAAA,EAAqB,MAAA,EAAQ,SAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACjF,EAAE,MAAM,mBAAA,EAAqB,MAAA,EAAQ,SAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACjF,EAAE,MAAM,2BAAA,EAA6B,MAAA,EAAQ,YAAY,KAAA,EAAO,MAAA,EAAQ,cAAc,IAAA,EAAK;AAAA,EAC3F,EAAE,MAAM,yBAAA,EAA2B,MAAA,EAAQ,aAAa,KAAA,EAAO,MAAA,EAAQ,cAAc,IAAA,EAAK;AAAA,EAC1F,EAAE,MAAM,qBAAA,EAAuB,MAAA,EAAQ,eAAe,KAAA,EAAO,MAAA,EAAQ,cAAc,IAAA,EAAK;AAAA,EACxF,EAAE,MAAM,kBAAA,EAAoB,MAAA,EAAQ,eAAe,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA;AAClF","file":"tools.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — tool catalog (the current 14).\n// The model never touches the database directly: it only calls these\n// functions, and each one checks your permission before answering.\n\nexport type ToolDomain = \"ventas\" | \"finanzas\" | \"marketing\" | \"general\" | \"transversal\" | \"drive\";\n\nexport type ToolDef = {\n name: string;\n domain: ToolDomain;\n needs: \"read\" | \"exec\";\n proposesOnly: boolean;\n};\n\nexport const TOOL_CATALOG: ToolDef[] = [\n { name: \"get_pipeline_summary\", domain: \"ventas\", needs: \"read\", proposesOnly: false },\n { name: \"list_deals\", domain: \"ventas\", needs: \"read\", proposesOnly: false },\n { name: \"list_overdue_invoices\", domain: \"finanzas\", needs: \"read\", proposesOnly: false },\n { name: \"get_campaign_performance\", domain: \"marketing\", needs: \"read\", proposesOnly: false },\n { name: \"get_churn_risk\", domain: \"general\", needs: \"read\", proposesOnly: false },\n { name: \"list_processes\", domain: \"transversal\", needs: \"read\", proposesOnly: false },\n { name: \"search_drive\", domain: \"drive\", needs: \"read\", proposesOnly: false },\n { name: \"read_drive_file\", domain: \"drive\", needs: \"read\", proposesOnly: false },\n { name: \"create_drive_file\", domain: \"drive\", needs: \"exec\", proposesOnly: false },\n { name: \"update_drive_file\", domain: \"drive\", needs: \"exec\", proposesOnly: false },\n { name: \"propose_invoice_reminders\", domain: \"finanzas\", needs: \"exec\", proposesOnly: true },\n { name: \"propose_pause_campaigns\", domain: \"marketing\", needs: \"exec\", proposesOnly: true },\n { name: \"request_process_run\", domain: \"transversal\", needs: \"exec\", proposesOnly: true },\n { name: \"open_expert_form\", domain: \"transversal\", needs: \"exec\", proposesOnly: false },\n];\n"]}
1
+ {"version":3,"sources":["../src/tools.ts"],"names":[],"mappings":";AAcO,IAAM,YAAA,GAA0B;AAAA,EACrC,EAAE,MAAM,sBAAA,EAAwB,MAAA,EAAQ,UAAU,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACrF,EAAE,MAAM,YAAA,EAAc,MAAA,EAAQ,UAAU,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAC3E,EAAE,MAAM,uBAAA,EAAyB,MAAA,EAAQ,YAAY,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACxF,EAAE,MAAM,0BAAA,EAA4B,MAAA,EAAQ,aAAa,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAC5F,EAAE,MAAM,gBAAA,EAAkB,MAAA,EAAQ,WAAW,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAChF,EAAE,MAAM,cAAA,EAAgB,MAAA,EAAQ,SAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAC5E,EAAE,MAAM,iBAAA,EAAmB,MAAA,EAAQ,SAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EAC/E,EAAE,MAAM,mBAAA,EAAqB,MAAA,EAAQ,SAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACjF,EAAE,MAAM,mBAAA,EAAqB,MAAA,EAAQ,SAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA,EAAM;AAAA,EACjF,EAAE,MAAM,2BAAA,EAA6B,MAAA,EAAQ,YAAY,KAAA,EAAO,MAAA,EAAQ,cAAc,IAAA,EAAK;AAAA,EAC3F,EAAE,MAAM,yBAAA,EAA2B,MAAA,EAAQ,aAAa,KAAA,EAAO,MAAA,EAAQ,cAAc,IAAA,EAAK;AAAA,EAC1F,EAAE,MAAM,kBAAA,EAAoB,MAAA,EAAQ,eAAe,KAAA,EAAO,MAAA,EAAQ,cAAc,KAAA;AAClF","file":"tools.js","sourcesContent":["// SPDX-License-Identifier: MIT\n// OpenCore — tool catalog (the current 12).\n// The model never touches the database directly: it only calls these\n// functions, and each one checks your permission before answering.\n\nexport type ToolDomain = \"ventas\" | \"finanzas\" | \"marketing\" | \"general\" | \"transversal\" | \"drive\";\n\nexport type ToolDef = {\n name: string;\n domain: ToolDomain;\n needs: \"read\" | \"exec\";\n proposesOnly: boolean;\n};\n\nexport const TOOL_CATALOG: ToolDef[] = [\n { name: \"get_pipeline_summary\", domain: \"ventas\", needs: \"read\", proposesOnly: false },\n { name: \"list_deals\", domain: \"ventas\", needs: \"read\", proposesOnly: false },\n { name: \"list_overdue_invoices\", domain: \"finanzas\", needs: \"read\", proposesOnly: false },\n { name: \"get_campaign_performance\", domain: \"marketing\", needs: \"read\", proposesOnly: false },\n { name: \"get_churn_risk\", domain: \"general\", needs: \"read\", proposesOnly: false },\n { name: \"search_drive\", domain: \"drive\", needs: \"read\", proposesOnly: false },\n { name: \"read_drive_file\", domain: \"drive\", needs: \"read\", proposesOnly: false },\n { name: \"create_drive_file\", domain: \"drive\", needs: \"exec\", proposesOnly: false },\n { name: \"update_drive_file\", domain: \"drive\", needs: \"exec\", proposesOnly: false },\n { name: \"propose_invoice_reminders\", domain: \"finanzas\", needs: \"exec\", proposesOnly: true },\n { name: \"propose_pause_campaigns\", domain: \"marketing\", needs: \"exec\", proposesOnly: true },\n { name: \"open_expert_form\", domain: \"transversal\", needs: \"exec\", proposesOnly: false },\n];\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openexpert/opencore",
3
- "version": "0.2.1",
4
- "description": "OpenCore — open-source engine of OpenExpert (MIT). Mode, config, model provider selector (Google / Ollama / OpenAI-compatible / OpenExpert gateway), storage and secrets interfaces, tool catalog, and CLI.",
3
+ "version": "0.3.0",
4
+ "description": "OpenCore — open-source engine of OpenExpert (MIT). Config, model provider selector (Google / Ollama / OpenAI-compatible), storage and secrets interfaces, tool catalog, and CLI.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -56,7 +56,7 @@
56
56
  "agents",
57
57
  "mit",
58
58
  "oss",
59
- "model-gateway"
59
+ "local-first"
60
60
  ],
61
61
  "repository": {
62
62
  "type": "git",
@@ -85,7 +85,7 @@
85
85
  },
86
86
  "dependencies": {
87
87
  "@ai-sdk/openai": "^4.0.5",
88
- "@clack/prompts": "^0.9.1",
88
+ "@clack/prompts": "^1.8.1",
89
89
  "ai": "^7.0.0"
90
90
  },
91
91
  "devDependencies": {
@@ -26,6 +26,16 @@
26
26
  "minLength": 1,
27
27
  "description": "Directory where the SQLite database and credentials live. `~/` expands to the user's home directory."
28
28
  },
29
+ "googleClientId": {
30
+ "type": "string",
31
+ "minLength": 1,
32
+ "description": "Google OAuth client ID (public identifier) used for the Drive integration. The matching client secret lives in secrets.json."
33
+ },
34
+ "notionClientId": {
35
+ "type": "string",
36
+ "minLength": 1,
37
+ "description": "Notion OAuth client ID (public identifier) used to connect Notion. The matching client secret lives in secrets.json."
38
+ },
29
39
  "ai": {
30
40
  "type": "object",
31
41
  "additionalProperties": false,