reasonix 0.35.0 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/README.zh-CN.md +17 -0
- package/dashboard/dist/app.js +42 -3
- package/dashboard/dist/app.js.map +1 -1
- package/dist/cli/{chat-AB5D7I3V.js → chat-7AF5SPAJ.js} +16 -15
- package/dist/cli/{chunk-RJ5GUVS2.js → chunk-2MCYGFLK.js} +10 -10
- package/dist/cli/chunk-2MCYGFLK.js.map +1 -0
- package/dist/cli/{chunk-GPHBJWCV.js → chunk-3OBWN2NH.js} +650 -493
- package/dist/cli/chunk-3OBWN2NH.js.map +1 -0
- package/dist/cli/{chunk-SW3CCXEV.js → chunk-4Q3GRJIU.js} +2 -2
- package/dist/cli/{chunk-5JXXEPDM.js → chunk-BHLHOS5Y.js} +8 -2
- package/dist/cli/chunk-BHLHOS5Y.js.map +1 -0
- package/dist/cli/{chunk-IDP65VCC.js → chunk-BJ376EN3.js} +9 -8
- package/dist/cli/chunk-BJ376EN3.js.map +1 -0
- package/dist/cli/{chunk-2AWTGJ2C.js → chunk-CRPQUBP6.js} +26 -9
- package/dist/cli/{chunk-2AWTGJ2C.js.map → chunk-CRPQUBP6.js.map} +1 -1
- package/dist/cli/{chunk-JJTOZPM3.js → chunk-IPCPEZWQ.js} +2 -2
- package/dist/cli/{chunk-SN7YH6FC.js → chunk-MLXUGPJE.js} +168 -35
- package/dist/cli/chunk-MLXUGPJE.js.map +1 -0
- package/dist/cli/{chunk-SX6L4HZZ.js → chunk-QPNZWUZF.js} +53 -6
- package/dist/cli/chunk-QPNZWUZF.js.map +1 -0
- package/dist/cli/{chunk-N2IC4XDL.js → chunk-QRUQ2BFT.js} +13 -8
- package/dist/cli/{chunk-N2IC4XDL.js.map → chunk-QRUQ2BFT.js.map} +1 -1
- package/dist/cli/{chunk-KZHMKOJH.js → chunk-T52GAWPP.js} +25 -3
- package/dist/cli/chunk-T52GAWPP.js.map +1 -0
- package/dist/cli/{chunk-I6YIAK6C.js → chunk-UNMYFZPZ.js} +2 -2
- package/dist/cli/{update-4TJWRUIN.js → chunk-WJ3YX4PZ.js} +51 -12
- package/dist/cli/chunk-WJ3YX4PZ.js.map +1 -0
- package/dist/cli/{chunk-RXGEGA7K.js → chunk-XQIFIB3U.js} +18 -7
- package/dist/cli/{chunk-RXGEGA7K.js.map → chunk-XQIFIB3U.js.map} +1 -1
- package/dist/cli/{chunk-2EBODRRO.js → chunk-ZJR4QLXB.js} +5 -1
- package/dist/cli/{chunk-2EBODRRO.js.map → chunk-ZJR4QLXB.js.map} +1 -1
- package/dist/cli/{code-XBEFHXVM.js → code-SWI4EBME.js} +19 -17
- package/dist/cli/code-SWI4EBME.js.map +1 -0
- package/dist/cli/{commands-MEZPSEHV.js → commands-FE2UDFBC.js} +3 -3
- package/dist/cli/{commit-CE4EFTUQ.js → commit-3IAGB22T.js} +5 -4
- package/dist/cli/commit-3IAGB22T.js.map +1 -0
- package/dist/cli/{doctor-A565GMWD.js → doctor-DKD34EFD.js} +7 -7
- package/dist/cli/index.js +33 -31
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/{mcp-LDFK5QJI.js → mcp-2RDEQST6.js} +2 -2
- package/dist/cli/{mcp-browse-FYHEITCM.js → mcp-browse-VM5GLRBQ.js} +2 -2
- package/dist/cli/{mcp-inspect-T2HBR22P.js → mcp-inspect-CWSVCZUQ.js} +3 -3
- package/dist/cli/{replay-P2WC5N5X.js → replay-D7RT2DR7.js} +2 -2
- package/dist/cli/{run-QBWJETS3.js → run-FK5UBIIM.js} +11 -10
- package/dist/cli/run-FK5UBIIM.js.map +1 -0
- package/dist/cli/{server-SMLVXIW4.js → server-W4XJK4GX.js} +16 -16
- package/dist/cli/{server-SMLVXIW4.js.map → server-W4XJK4GX.js.map} +1 -1
- package/dist/cli/{sessions-55RIZVWG.js → sessions-YZXWMIWW.js} +8 -8
- package/dist/cli/{setup-QXMONZ4P.js → setup-IIAJXHP4.js} +196 -130
- package/dist/cli/setup-IIAJXHP4.js.map +1 -0
- package/dist/cli/update-GUCWB4UN.js +13 -0
- package/dist/cli/update-GUCWB4UN.js.map +1 -0
- package/dist/cli/{version-Q2HA3AAC.js → version-DWD6RLIU.js} +10 -10
- package/dist/index.d.ts +14 -2
- package/dist/index.js +270 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/chunk-5JXXEPDM.js.map +0 -1
- package/dist/cli/chunk-GPHBJWCV.js.map +0 -1
- package/dist/cli/chunk-IDP65VCC.js.map +0 -1
- package/dist/cli/chunk-KZHMKOJH.js.map +0 -1
- package/dist/cli/chunk-RJ5GUVS2.js.map +0 -1
- package/dist/cli/chunk-SN7YH6FC.js.map +0 -1
- package/dist/cli/chunk-SX6L4HZZ.js.map +0 -1
- package/dist/cli/code-XBEFHXVM.js.map +0 -1
- package/dist/cli/commit-CE4EFTUQ.js.map +0 -1
- package/dist/cli/run-QBWJETS3.js.map +0 -1
- package/dist/cli/setup-QXMONZ4P.js.map +0 -1
- package/dist/cli/update-4TJWRUIN.js.map +0 -1
- /package/dist/cli/{chat-AB5D7I3V.js.map → chat-7AF5SPAJ.js.map} +0 -0
- /package/dist/cli/{chunk-SW3CCXEV.js.map → chunk-4Q3GRJIU.js.map} +0 -0
- /package/dist/cli/{chunk-JJTOZPM3.js.map → chunk-IPCPEZWQ.js.map} +0 -0
- /package/dist/cli/{chunk-I6YIAK6C.js.map → chunk-UNMYFZPZ.js.map} +0 -0
- /package/dist/cli/{commands-MEZPSEHV.js.map → commands-FE2UDFBC.js.map} +0 -0
- /package/dist/cli/{doctor-A565GMWD.js.map → doctor-DKD34EFD.js.map} +0 -0
- /package/dist/cli/{mcp-LDFK5QJI.js.map → mcp-2RDEQST6.js.map} +0 -0
- /package/dist/cli/{mcp-browse-FYHEITCM.js.map → mcp-browse-VM5GLRBQ.js.map} +0 -0
- /package/dist/cli/{mcp-inspect-T2HBR22P.js.map → mcp-inspect-CWSVCZUQ.js.map} +0 -0
- /package/dist/cli/{replay-P2WC5N5X.js.map → replay-D7RT2DR7.js.map} +0 -0
- /package/dist/cli/{sessions-55RIZVWG.js.map → sessions-YZXWMIWW.js.map} +0 -0
- /package/dist/cli/{version-Q2HA3AAC.js.map → version-DWD6RLIU.js.map} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
indexCompatible,
|
|
4
4
|
querySemantic
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XQIFIB3U.js";
|
|
6
6
|
|
|
7
7
|
// src/index/semantic/tool.ts
|
|
8
8
|
async function registerSemanticSearchTool(registry, opts) {
|
|
@@ -91,4 +91,4 @@ export {
|
|
|
91
91
|
registerSemanticSearchTool,
|
|
92
92
|
bootstrapSemanticSearchInCodeMode
|
|
93
93
|
};
|
|
94
|
-
//# sourceMappingURL=chunk-
|
|
94
|
+
//# sourceMappingURL=chunk-4Q3GRJIU.js.map
|
|
@@ -164,6 +164,10 @@ function loadApiKey(path = defaultConfigPath()) {
|
|
|
164
164
|
if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
|
|
165
165
|
return readConfig(path).apiKey;
|
|
166
166
|
}
|
|
167
|
+
function loadBaseUrl(path = defaultConfigPath()) {
|
|
168
|
+
if (process.env.DEEPSEEK_BASE_URL) return process.env.DEEPSEEK_BASE_URL;
|
|
169
|
+
return readConfig(path).baseUrl;
|
|
170
|
+
}
|
|
167
171
|
function searchEnabled(path = defaultConfigPath()) {
|
|
168
172
|
const env = process.env.REASONIX_SEARCH;
|
|
169
173
|
if (env === "off" || env === "false" || env === "0") return false;
|
|
@@ -353,7 +357,8 @@ function markMouseClipboardHintShown(path = defaultConfigPath()) {
|
|
|
353
357
|
}
|
|
354
358
|
function isPlausibleKey(key) {
|
|
355
359
|
const trimmed = key.trim();
|
|
356
|
-
|
|
360
|
+
if (trimmed.length < 16) return false;
|
|
361
|
+
return !/\s/.test(trimmed);
|
|
357
362
|
}
|
|
358
363
|
function redactKey(key) {
|
|
359
364
|
if (!key) return "";
|
|
@@ -412,6 +417,7 @@ export {
|
|
|
412
417
|
loadLanguage,
|
|
413
418
|
saveLanguage,
|
|
414
419
|
loadApiKey,
|
|
420
|
+
loadBaseUrl,
|
|
415
421
|
searchEnabled,
|
|
416
422
|
webSearchEngine,
|
|
417
423
|
webSearchEndpoint,
|
|
@@ -440,4 +446,4 @@ export {
|
|
|
440
446
|
isPlausibleKey,
|
|
441
447
|
redactKey
|
|
442
448
|
};
|
|
443
|
-
//# sourceMappingURL=chunk-
|
|
449
|
+
//# sourceMappingURL=chunk-BHLHOS5Y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/config.ts","../../src/index/config.ts"],"sourcesContent":["/** Library reads only DEEPSEEK_API_KEY from env; the CLI bridges config.json → env var. */\n\nimport { chmodSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { type ThemeName, isThemeName, resolveThemeName } from \"./cli/ui/theme/tokens.js\";\nimport type { LanguageCode } from \"./i18n/types.js\";\nimport {\n type IndexUserConfig,\n type ResolvedIndexConfig,\n resolveIndexConfig,\n} from \"./index/config.js\";\n\n/** Legacy `fast|smart|max` kept for back-compat with existing config.json files. */\nexport type PresetName = \"auto\" | \"flash\" | \"pro\" | \"fast\" | \"smart\" | \"max\";\n\n/** Single trust dial: review queues edits + gates shell; auto applies + gates shell; yolo skips both gates. */\nexport type EditMode = \"review\" | \"auto\" | \"yolo\";\n\nexport type ReasoningEffort = \"high\" | \"max\";\n\nexport type EmbeddingProvider = \"ollama\" | \"openai-compat\";\n\nexport interface OllamaEmbeddingUserConfig {\n baseUrl?: string;\n model?: string;\n}\n\nexport interface OpenAICompatEmbeddingUserConfig {\n baseUrl?: string;\n apiKey?: string;\n model?: string;\n extraBody?: Record<string, unknown>;\n}\n\nexport interface SemanticEmbeddingUserConfig {\n provider?: EmbeddingProvider;\n ollama?: OllamaEmbeddingUserConfig;\n openaiCompat?: OpenAICompatEmbeddingUserConfig;\n}\n\nexport interface ResolvedOllamaEmbeddingConfig {\n provider: \"ollama\";\n baseUrl: string;\n model: string;\n timeoutMs: number;\n}\n\nexport interface ResolvedOpenAICompatEmbeddingConfig {\n provider: \"openai-compat\";\n baseUrl: string;\n apiKey: string;\n model: string;\n extraBody: Record<string, unknown>;\n timeoutMs: number;\n}\n\nexport type ResolvedEmbeddingConfig =\n | ResolvedOllamaEmbeddingConfig\n | ResolvedOpenAICompatEmbeddingConfig;\n\nexport interface SemanticEmbeddingConfigView {\n provider: EmbeddingProvider;\n ollama: {\n baseUrl: string;\n model: string;\n };\n openaiCompat: {\n baseUrl: string;\n apiKey: string;\n apiKeySet: boolean;\n model: string;\n extraBody: Record<string, unknown>;\n };\n}\n\nexport interface ReasonixConfig {\n apiKey?: string;\n baseUrl?: string;\n lang?: LanguageCode;\n preset?: PresetName;\n editMode?: EditMode;\n editModeHintShown?: boolean;\n mouseClipboardHintShown?: boolean;\n reasoningEffort?: ReasoningEffort;\n theme?: ThemeName | \"auto\";\n /** Stored as `--mcp`-format strings so one parser handles both flag and config. */\n mcp?: string[];\n /** Names of servers in `mcp` to skip on bridge — see `/mcp disable <name>`. */\n mcpDisabled?: string[];\n session?: string | null;\n setupCompleted?: boolean;\n search?: boolean;\n /** Web search engine backend: \"mojeek\" (default, scrapes Mojeek) or \"searxng\" (self-hosted SearXNG). */\n webSearchEngine?: \"mojeek\" | \"searxng\";\n /** Base URL for SearXNG instance (default http://localhost:8080). */\n webSearchEndpoint?: string;\n projects?: {\n [absoluteRootDir: string]: {\n shellAllowed?: string[];\n };\n };\n index?: IndexUserConfig;\n semantic?: SemanticEmbeddingUserConfig;\n}\n\nconst DEFAULT_OLLAMA_URL = \"http://localhost:11434\";\nconst DEFAULT_EMBED_MODEL = \"nomic-embed-text\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport function defaultConfigPath(): string {\n return join(homedir(), \".reasonix\", \"config.json\");\n}\n\nexport function readConfig(path: string = defaultConfigPath()): ReasonixConfig {\n try {\n const raw = readFileSync(path, \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\") return parsed as ReasonixConfig;\n } catch {\n /* missing or malformed → empty config */\n }\n return {};\n}\n\nexport function writeConfig(cfg: ReasonixConfig, path: string = defaultConfigPath()): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, JSON.stringify(cfg, null, 2), \"utf8\");\n try {\n chmodSync(path, 0o600);\n } catch {\n /* ignore on platforms without chmod */\n }\n}\n\n/** Resolve the language from config file. */\nexport function loadLanguage(path: string = defaultConfigPath()): LanguageCode | undefined {\n return readConfig(path).lang;\n}\n\n/** Persist the language so it survives a relaunch. */\nexport function saveLanguage(lang: LanguageCode, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.lang = lang;\n writeConfig(cfg, path);\n}\n\n/** Resolve the API key from env var first, then the config file. */\nexport function loadApiKey(path: string = defaultConfigPath()): string | undefined {\n if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;\n return readConfig(path).apiKey;\n}\n\n/** env > config > undefined. Client falls back to api.deepseek.com when undefined. */\nexport function loadBaseUrl(path: string = defaultConfigPath()): string | undefined {\n if (process.env.DEEPSEEK_BASE_URL) return process.env.DEEPSEEK_BASE_URL;\n return readConfig(path).baseUrl;\n}\n\nexport function saveBaseUrl(url: string, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n const trimmed = url.trim();\n if (trimmed) {\n cfg.baseUrl = trimmed;\n } else {\n cfg.baseUrl = undefined;\n }\n writeConfig(cfg, path);\n}\n\nexport function searchEnabled(path: string = defaultConfigPath()): boolean {\n const env = process.env.REASONIX_SEARCH;\n if (env === \"off\" || env === \"false\" || env === \"0\") return false;\n const cfg = readConfig(path).search;\n if (cfg === false) return false;\n return true;\n}\n\nexport function webSearchEngine(path: string = defaultConfigPath()): \"mojeek\" | \"searxng\" {\n const cfg = readConfig(path).webSearchEngine;\n if (cfg === \"searxng\") return \"searxng\";\n return \"mojeek\";\n}\n\nexport function webSearchEndpoint(path: string = defaultConfigPath()): string {\n const cfg = readConfig(path).webSearchEndpoint;\n if (cfg && typeof cfg === \"string\") return cfg;\n return \"http://localhost:8080\";\n}\n\nexport function saveApiKey(key: string, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.apiKey = key.trim();\n writeConfig(cfg, path);\n}\n\n/** Windows: case-insensitive — NTFS treats `F:\\Foo` and `f:\\foo` as one directory (#402). */\nfunction findProjectKey(cfg: ReasonixConfig, rootDir: string): string | undefined {\n const projects = cfg.projects;\n if (!projects) return undefined;\n if (Object.hasOwn(projects, rootDir)) return rootDir;\n if (process.platform !== \"win32\") return undefined;\n const lower = rootDir.toLowerCase();\n for (const k of Object.keys(projects)) {\n if (k.toLowerCase() === lower) return k;\n }\n return undefined;\n}\n\nexport function loadProjectShellAllowed(\n rootDir: string,\n path: string = defaultConfigPath(),\n): string[] {\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return [];\n return cfg.projects?.[key]?.shellAllowed ?? [];\n}\n\nexport function addProjectShellAllowed(\n rootDir: string,\n prefix: string,\n path: string = defaultConfigPath(),\n): void {\n const trimmed = prefix.trim();\n if (!trimmed) return;\n const cfg = readConfig(path);\n if (!cfg.projects) cfg.projects = {};\n const key = findProjectKey(cfg, rootDir) ?? rootDir;\n if (!cfg.projects[key]) cfg.projects[key] = {};\n const existing = cfg.projects[key].shellAllowed ?? [];\n if (existing.includes(trimmed)) return;\n cfg.projects[key].shellAllowed = [...existing, trimmed];\n writeConfig(cfg, path);\n}\n\n/** Match is exact after trim — NOT prefix-match: removing `git` MUST NOT drop `git push origin main`. */\nexport function removeProjectShellAllowed(\n rootDir: string,\n prefix: string,\n path: string = defaultConfigPath(),\n): boolean {\n const trimmed = prefix.trim();\n if (!trimmed) return false;\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return false;\n const existing = cfg.projects?.[key]?.shellAllowed ?? [];\n if (!existing.includes(trimmed)) return false;\n const next = existing.filter((p) => p !== trimmed);\n if (!cfg.projects) cfg.projects = {};\n if (!cfg.projects[key]) cfg.projects[key] = {};\n cfg.projects[key].shellAllowed = next;\n writeConfig(cfg, path);\n return true;\n}\n\nexport function clearProjectShellAllowed(\n rootDir: string,\n path: string = defaultConfigPath(),\n): number {\n const cfg = readConfig(path);\n const key = findProjectKey(cfg, rootDir);\n if (key === undefined) return 0;\n const existing = cfg.projects?.[key]?.shellAllowed ?? [];\n if (existing.length === 0) return 0;\n if (!cfg.projects) cfg.projects = {};\n if (!cfg.projects[key]) cfg.projects[key] = {};\n cfg.projects[key].shellAllowed = [];\n writeConfig(cfg, path);\n return existing.length;\n}\n\n/** Unknown values fall back to \"review\" so hand-edited bad config gets the safe default. */\nexport function loadEditMode(path: string = defaultConfigPath()): EditMode {\n const v = readConfig(path).editMode;\n return v === \"auto\" ? \"auto\" : \"review\";\n}\n\n/** Persist the edit mode so `/mode auto` survives a relaunch. */\nexport function saveEditMode(mode: EditMode, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.editMode = mode;\n writeConfig(cfg, path);\n}\n\n/** True when the onboarding tip for the review/AUTO gate has been shown. */\nexport function editModeHintShown(path: string = defaultConfigPath()): boolean {\n return readConfig(path).editModeHintShown === true;\n}\n\n/** True when the mouse-tracking + clipboard tip has been shown. */\nexport function mouseClipboardHintShown(path: string = defaultConfigPath()): boolean {\n return readConfig(path).mouseClipboardHintShown === true;\n}\n\n/** Unknown / missing fall back to \"max\" so hand-edited bad config can't silently override the default. */\nexport function loadReasoningEffort(path: string = defaultConfigPath()): ReasoningEffort {\n const v = readConfig(path).reasoningEffort;\n return v === \"high\" ? \"high\" : \"max\";\n}\n\nexport function loadTheme(path: string = defaultConfigPath()): ThemeName | \"auto\" | undefined {\n const value = readConfig(path).theme;\n if (value === \"auto\") return \"auto\";\n if (typeof value === \"string\" && isThemeName(value)) return value;\n return undefined;\n}\n\nexport function resolveThemePreference(\n configTheme: ThemeName | \"auto\" | undefined,\n envTheme?: string | null,\n): ThemeName {\n if (configTheme && configTheme !== \"auto\") return configTheme;\n return resolveThemeName(envTheme);\n}\n\nexport function saveTheme(theme: ThemeName | \"auto\", path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.theme = theme;\n writeConfig(cfg, path);\n}\n\n/** Persist the reasoning_effort cap so `/effort high` survives a relaunch. */\nexport function saveReasoningEffort(\n effort: ReasoningEffort,\n path: string = defaultConfigPath(),\n): void {\n const cfg = readConfig(path);\n cfg.reasoningEffort = effort;\n writeConfig(cfg, path);\n}\n\nexport function loadIndexUserConfig(path: string = defaultConfigPath()): IndexUserConfig {\n return readConfig(path).index ?? {};\n}\n\nexport function loadIndexConfig(path: string = defaultConfigPath()): ResolvedIndexConfig {\n return resolveIndexConfig(readConfig(path).index);\n}\n\nexport function saveIndexConfig(user: IndexUserConfig, path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n cfg.index = user;\n writeConfig(cfg, path);\n}\n\nexport function loadSemanticEmbeddingUserConfig(\n path: string = defaultConfigPath(),\n): SemanticEmbeddingUserConfig {\n return normalizeSemanticEmbeddingUserConfig(readConfig(path).semantic);\n}\n\nexport function saveSemanticEmbeddingConfig(\n user: SemanticEmbeddingUserConfig,\n path: string = defaultConfigPath(),\n): void {\n const cfg = readConfig(path);\n cfg.semantic = normalizeSemanticEmbeddingUserConfig(user);\n writeConfig(cfg, path);\n}\n\nexport function resolveSemanticEmbeddingConfig(\n path: string = defaultConfigPath(),\n): ResolvedEmbeddingConfig {\n const user = loadSemanticEmbeddingUserConfig(path);\n const provider = user.provider ?? \"ollama\";\n if (provider === \"openai-compat\") {\n const baseUrl = user.openaiCompat?.baseUrl?.trim() ?? \"\";\n const apiKey = user.openaiCompat?.apiKey?.trim() ?? \"\";\n const model = user.openaiCompat?.model?.trim() ?? \"\";\n if (!baseUrl) throw new Error(\"OpenAI-compatible embeddings require an API URL.\");\n requireValidUrl(baseUrl, \"OpenAI-compatible API URL\");\n if (!apiKey) throw new Error(\"OpenAI-compatible embeddings require an API key.\");\n if (!model) throw new Error(\"OpenAI-compatible embeddings require a model.\");\n return {\n provider,\n baseUrl,\n apiKey,\n model,\n extraBody: normalizeExtraBody(user.openaiCompat?.extraBody),\n timeoutMs: DEFAULT_TIMEOUT_MS,\n };\n }\n return {\n provider: \"ollama\",\n baseUrl: user.ollama?.baseUrl?.trim() || process.env.OLLAMA_URL || DEFAULT_OLLAMA_URL,\n model: user.ollama?.model?.trim() || process.env.REASONIX_EMBED_MODEL || DEFAULT_EMBED_MODEL,\n timeoutMs: DEFAULT_TIMEOUT_MS,\n };\n}\n\nexport function redactSemanticEmbeddingConfig(\n user: SemanticEmbeddingUserConfig,\n): SemanticEmbeddingConfigView {\n const normalized = normalizeSemanticEmbeddingUserConfig(user);\n return {\n provider: normalized.provider ?? \"ollama\",\n ollama: {\n baseUrl: normalized.ollama?.baseUrl?.trim() || process.env.OLLAMA_URL || DEFAULT_OLLAMA_URL,\n model:\n normalized.ollama?.model?.trim() || process.env.REASONIX_EMBED_MODEL || DEFAULT_EMBED_MODEL,\n },\n openaiCompat: {\n baseUrl: normalized.openaiCompat?.baseUrl?.trim() ?? \"\",\n apiKey: normalized.openaiCompat?.apiKey ? redactKey(normalized.openaiCompat.apiKey) : \"\",\n apiKeySet: Boolean(normalized.openaiCompat?.apiKey?.trim()),\n model: normalized.openaiCompat?.model?.trim() ?? \"\",\n extraBody: normalizeExtraBody(normalized.openaiCompat?.extraBody),\n },\n };\n}\n\n/** Mark the onboarding tip as shown so subsequent launches skip it. */\nexport function markEditModeHintShown(path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n if (cfg.editModeHintShown === true) return;\n cfg.editModeHintShown = true;\n writeConfig(cfg, path);\n}\n\n/** Mark the mouse + clipboard tip as shown. */\nexport function markMouseClipboardHintShown(path: string = defaultConfigPath()): void {\n const cfg = readConfig(path);\n if (cfg.mouseClipboardHintShown === true) return;\n cfg.mouseClipboardHintShown = true;\n writeConfig(cfg, path);\n}\n\n/** Self-hosted DeepSeek-compatible endpoints may issue any token shape, so we only typo-guard here — the real auth check is the first API call against `baseUrl`. */\nexport function isPlausibleKey(key: string): boolean {\n const trimmed = key.trim();\n if (trimmed.length < 16) return false;\n return !/\\s/.test(trimmed);\n}\n\n/** Mask a key for display: `sk-abcd...wxyz`. */\nexport function redactKey(key: string): string {\n if (!key) return \"\";\n if (key.length <= 12) return \"****\";\n return `${key.slice(0, 6)}…${key.slice(-4)}`;\n}\n\nfunction normalizeSemanticEmbeddingUserConfig(\n cfg: SemanticEmbeddingUserConfig | undefined,\n): SemanticEmbeddingUserConfig {\n return {\n provider: cfg?.provider === \"openai-compat\" ? \"openai-compat\" : \"ollama\",\n ollama: {\n baseUrl: normalizeOptionalString(cfg?.ollama?.baseUrl),\n model: normalizeOptionalString(cfg?.ollama?.model),\n },\n openaiCompat: {\n baseUrl: normalizeOptionalString(cfg?.openaiCompat?.baseUrl),\n apiKey: normalizeOptionalString(cfg?.openaiCompat?.apiKey),\n model: normalizeOptionalString(cfg?.openaiCompat?.model),\n extraBody: normalizeExtraBody(cfg?.openaiCompat?.extraBody),\n },\n };\n}\n\nfunction normalizeOptionalString(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction normalizeExtraBody(value: Record<string, unknown> | undefined): Record<string, unknown> {\n if (value === undefined) return {};\n if (!isPlainObject(value)) {\n throw new Error(\"Semantic embedding extraBody must be a JSON object.\");\n }\n return { ...value };\n}\n\nfunction requireValidUrl(value: string, label: string): void {\n try {\n new URL(value);\n } catch {\n throw new Error(`${label} must be a valid URL.`);\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return false;\n const proto = Object.getPrototypeOf(value);\n return proto === Object.prototype || proto === null;\n}\n","/** Shared exclude defaults + resolver — chunker, directory_tree, and dashboard read from here. */\n\nimport picomatch from \"picomatch\";\n\nexport interface IndexUserConfig {\n excludeDirs?: string[];\n excludeFiles?: string[];\n excludeExts?: string[];\n excludePatterns?: string[];\n respectGitignore?: boolean;\n maxFileBytes?: number;\n}\n\n/** Plain-data shape — JSON-safe so the dashboard endpoint can serialize. */\nexport interface ResolvedIndexConfig {\n excludeDirs: readonly string[];\n excludeFiles: readonly string[];\n excludeExts: readonly string[];\n excludePatterns: readonly string[];\n respectGitignore: boolean;\n maxFileBytes: number;\n}\n\n/** Hot-path lookup wrapper — built once per indexer run, never serialized. */\nexport interface IndexFilters {\n dirSet: ReadonlySet<string>;\n fileSet: ReadonlySet<string>;\n extSet: ReadonlySet<string>;\n patternMatch: (relPath: string) => boolean;\n respectGitignore: boolean;\n maxFileBytes: number;\n}\n\nexport const DEFAULT_INDEX_EXCLUDES = {\n dirs: [\n \"node_modules\",\n \".git\",\n \".hg\",\n \".svn\",\n \"dist\",\n \"build\",\n \"out\",\n \".next\",\n \".nuxt\",\n \"target\",\n \".venv\",\n \"venv\",\n \"__pycache__\",\n \".pytest_cache\",\n \".mypy_cache\",\n \".cache\",\n \"coverage\",\n \".turbo\",\n \".vercel\",\n \".reasonix\",\n ] as const,\n files: [\n \"package-lock.json\",\n \"yarn.lock\",\n \"pnpm-lock.yaml\",\n \"Cargo.lock\",\n \"poetry.lock\",\n \"Pipfile.lock\",\n \"go.sum\",\n \".DS_Store\",\n ] as const,\n exts: [\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".webp\",\n \".bmp\",\n \".ico\",\n \".tiff\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".otf\",\n \".eot\",\n \".zip\",\n \".tar\",\n \".gz\",\n \".bz2\",\n \".xz\",\n \".rar\",\n \".7z\",\n \".exe\",\n \".dll\",\n \".so\",\n \".dylib\",\n \".bin\",\n \".class\",\n \".jar\",\n \".war\",\n \".wasm\",\n \".o\",\n \".obj\",\n \".lib\",\n \".a\",\n \".pyc\",\n \".pyo\",\n \".mp3\",\n \".mp4\",\n \".wav\",\n \".ogg\",\n \".webm\",\n \".mov\",\n \".avi\",\n \".pdf\",\n \".sqlite\",\n \".db\",\n ] as const,\n} as const;\n\nexport const DEFAULT_MAX_FILE_BYTES = 256 * 1024;\nexport const DEFAULT_RESPECT_GITIGNORE = true;\n\nexport function defaultIndexConfig(): ResolvedIndexConfig {\n return {\n excludeDirs: [...DEFAULT_INDEX_EXCLUDES.dirs],\n excludeFiles: [...DEFAULT_INDEX_EXCLUDES.files],\n excludeExts: [...DEFAULT_INDEX_EXCLUDES.exts],\n excludePatterns: [],\n respectGitignore: DEFAULT_RESPECT_GITIGNORE,\n maxFileBytes: DEFAULT_MAX_FILE_BYTES,\n };\n}\n\n/** A field present in user config fully replaces the default for that field. Absent → default. */\nexport function resolveIndexConfig(user?: IndexUserConfig | null): ResolvedIndexConfig {\n const d = defaultIndexConfig();\n if (!user) return d;\n return {\n excludeDirs: Array.isArray(user.excludeDirs) ? [...user.excludeDirs] : d.excludeDirs,\n excludeFiles: Array.isArray(user.excludeFiles) ? [...user.excludeFiles] : d.excludeFiles,\n excludeExts: Array.isArray(user.excludeExts)\n ? user.excludeExts.map((e) => e.toLowerCase())\n : d.excludeExts,\n excludePatterns: Array.isArray(user.excludePatterns) ? [...user.excludePatterns] : [],\n respectGitignore:\n typeof user.respectGitignore === \"boolean\" ? user.respectGitignore : d.respectGitignore,\n maxFileBytes:\n typeof user.maxFileBytes === \"number\" && user.maxFileBytes > 0\n ? user.maxFileBytes\n : d.maxFileBytes,\n };\n}\n\nexport function compileFilters(cfg: ResolvedIndexConfig): IndexFilters {\n const matcher =\n cfg.excludePatterns.length === 0\n ? () => false\n : picomatch(cfg.excludePatterns as string[], { dot: true });\n return {\n dirSet: new Set(cfg.excludeDirs),\n fileSet: new Set(cfg.excludeFiles),\n extSet: new Set(cfg.excludeExts.map((e) => e.toLowerCase())),\n patternMatch: matcher as (p: string) => boolean,\n respectGitignore: cfg.respectGitignore,\n maxFileBytes: cfg.maxFileBytes,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -11,21 +11,22 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
checkOllamaStatus,
|
|
13
13
|
indexExists
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-XQIFIB3U.js";
|
|
15
15
|
import {
|
|
16
16
|
loadHooks
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import {
|
|
19
|
-
VERSION
|
|
20
|
-
} from "./chunk-2AWTGJ2C.js";
|
|
17
|
+
} from "./chunk-IPCPEZWQ.js";
|
|
21
18
|
import {
|
|
22
19
|
listSessions
|
|
23
20
|
} from "./chunk-DFP4YSVM.js";
|
|
24
21
|
import {
|
|
25
22
|
defaultConfigPath,
|
|
23
|
+
loadBaseUrl,
|
|
26
24
|
readConfig,
|
|
27
25
|
resolveSemanticEmbeddingConfig
|
|
28
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-BHLHOS5Y.js";
|
|
27
|
+
import {
|
|
28
|
+
VERSION
|
|
29
|
+
} from "./chunk-CRPQUBP6.js";
|
|
29
30
|
|
|
30
31
|
// src/cli/commands/doctor.ts
|
|
31
32
|
import { existsSync, readFileSync, statSync } from "fs";
|
|
@@ -125,7 +126,7 @@ async function checkApiReach() {
|
|
|
125
126
|
};
|
|
126
127
|
}
|
|
127
128
|
try {
|
|
128
|
-
const client = new DeepSeekClient({ apiKey: key });
|
|
129
|
+
const client = new DeepSeekClient({ apiKey: key, baseUrl: loadBaseUrl() });
|
|
129
130
|
const ctl = new AbortController();
|
|
130
131
|
const timer = setTimeout(() => ctl.abort(), 8e3);
|
|
131
132
|
let balance;
|
|
@@ -356,4 +357,4 @@ export {
|
|
|
356
357
|
runDoctorChecks,
|
|
357
358
|
doctorCommand
|
|
358
359
|
};
|
|
359
|
-
//# sourceMappingURL=chunk-
|
|
360
|
+
//# sourceMappingURL=chunk-BJ376EN3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/commands/doctor.ts"],"sourcesContent":["/** Plain-text (not Ink) — must work when everything else is broken. fail → exit 1; warn → exit 0. */\n\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { DeepSeekClient } from \"../../client.js\";\nimport {\n defaultConfigPath,\n loadBaseUrl,\n readConfig,\n resolveSemanticEmbeddingConfig,\n} from \"../../config.js\";\nimport { loadDotenv } from \"../../env.js\";\nimport { loadHooks } from \"../../hooks.js\";\nimport { indexExists } from \"../../index/semantic/builder.js\";\nimport { checkOllamaStatus } from \"../../index/semantic/ollama-launcher.js\";\nimport { listSessions } from \"../../memory/session.js\";\nimport { resolveDataPath } from \"../../tokenizer.js\";\nimport { VERSION } from \"../../version.js\";\n\nexport type DoctorLevel = \"ok\" | \"warn\" | \"fail\";\n\nexport interface DoctorCheck {\n label: string;\n level: DoctorLevel;\n detail: string;\n}\n\ntype Level = DoctorLevel;\ntype Check = DoctorCheck;\n\nexport async function runDoctorChecks(projectRoot: string): Promise<DoctorCheck[]> {\n return Promise.all([\n checkApiKey(),\n checkConfig(),\n checkApiReach(),\n checkTokenizer(),\n checkSessions(),\n checkHooks(projectRoot),\n checkOllama(projectRoot),\n checkProject(projectRoot),\n ]);\n}\n\nconst TTY = process.stdout.isTTY && process.env.TERM !== \"dumb\";\n\nfunction color(text: string, code: string): string {\n if (!TTY) return text;\n return `\\x1b[${code}m${text}\\x1b[0m`;\n}\n\nfunction badge(level: Level): string {\n if (level === \"ok\") return color(\"✓\", \"32\");\n if (level === \"warn\") return color(\"⚠\", \"33\");\n return color(\"✗\", \"31\");\n}\n\nfunction tail4(s: string): string {\n return s.length <= 4 ? s : `…${s.slice(-4)}`;\n}\n\nfunction fmtBytes(n: number): string {\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n return `${(n / 1024 / 1024).toFixed(1)} MB`;\n}\n\nasync function checkApiKey(): Promise<Check> {\n const fromEnv = process.env.DEEPSEEK_API_KEY;\n if (fromEnv) {\n return {\n label: \"api key \",\n level: \"ok\",\n detail: `set via env DEEPSEEK_API_KEY (${tail4(fromEnv)})`,\n };\n }\n try {\n const cfg = readConfig();\n if (cfg.apiKey) {\n return {\n label: \"api key \",\n level: \"ok\",\n detail: `from ${defaultConfigPath()} (${tail4(cfg.apiKey)})`,\n };\n }\n } catch {\n /* fall through */\n }\n return {\n label: \"api key \",\n level: \"fail\",\n detail:\n \"not set — `reasonix setup` to save one, or export DEEPSEEK_API_KEY. Get a key at https://platform.deepseek.com/api_keys\",\n };\n}\n\nasync function checkConfig(): Promise<Check> {\n const path = defaultConfigPath();\n if (!existsSync(path)) {\n return {\n label: \"config \",\n level: \"warn\",\n detail: \"missing — running with library defaults. `reasonix setup` writes one.\",\n };\n }\n try {\n const cfg = readConfig(path);\n const parts: string[] = [];\n if (cfg.preset) parts.push(`preset=${cfg.preset}`);\n if (cfg.editMode) parts.push(`editMode=${cfg.editMode}`);\n if (cfg.mcp && cfg.mcp.length > 0) parts.push(`mcp=${cfg.mcp.length}`);\n return {\n label: \"config \",\n level: \"ok\",\n detail: `${path}${parts.length ? ` (${parts.join(\", \")})` : \"\"}`,\n };\n } catch (err) {\n return {\n label: \"config \",\n level: \"fail\",\n detail: `${path} unreadable — ${(err as Error).message}`,\n };\n }\n}\n\nasync function checkApiReach(): Promise<Check> {\n const key = process.env.DEEPSEEK_API_KEY ?? readConfig().apiKey;\n if (!key) {\n return {\n label: \"api reach \",\n level: \"warn\",\n detail: \"skipped — no api key to test with\",\n };\n }\n try {\n const client = new DeepSeekClient({ apiKey: key, baseUrl: loadBaseUrl() });\n const ctl = new AbortController();\n const timer = setTimeout(() => ctl.abort(), 8_000);\n let balance: Awaited<ReturnType<DeepSeekClient[\"getBalance\"]>>;\n try {\n balance = await client.getBalance({ signal: ctl.signal });\n } finally {\n clearTimeout(timer);\n }\n if (!balance) {\n return {\n label: \"api reach \",\n level: \"fail\",\n detail: \"/user/balance returned null — auth failed or network blocked\",\n };\n }\n if (!balance.is_available) {\n const info = balance.balance_infos[0];\n return {\n label: \"api reach \",\n level: \"warn\",\n detail: `account flagged not-available${info ? ` (${info.total_balance} ${info.currency})` : \"\"} — top up or check your dashboard`,\n };\n }\n const info = balance.balance_infos[0];\n return {\n label: \"api reach \",\n level: \"ok\",\n detail: info\n ? `/user/balance ok — ${info.total_balance} ${info.currency}`\n : \"/user/balance ok\",\n };\n } catch (err) {\n return {\n label: \"api reach \",\n level: \"fail\",\n detail: `${(err as Error).message}`,\n };\n }\n}\n\nasync function checkTokenizer(): Promise<Check> {\n // Reuse the runtime's resolver so the doctor never disagrees with what\n // the tokenizer actually loads — three candidates including a global\n // npm install probe via createRequire.\n const path = resolveDataPath();\n if (existsSync(path)) {\n try {\n const stat = statSync(path);\n return {\n label: \"tokenizer \",\n level: \"ok\",\n detail: `${path} (${fmtBytes(stat.size)})`,\n };\n } catch {\n /* fall through to warn */\n }\n }\n return {\n label: \"tokenizer \",\n level: \"warn\",\n detail:\n \"data/deepseek-tokenizer.json.gz not found — token counts will fall back to char heuristics\",\n };\n}\n\nasync function checkSessions(): Promise<Check> {\n try {\n const list = listSessions();\n if (list.length === 0) {\n return {\n label: \"sessions \",\n level: \"ok\",\n detail: \"0 saved\",\n };\n }\n const totalBytes = list.reduce((s, e) => s + e.size, 0);\n const oldest = list[list.length - 1]!;\n const ageDays = Math.floor((Date.now() - oldest.mtime.getTime()) / (24 * 60 * 60 * 1000));\n const stale = list.filter(\n (e) => Date.now() - e.mtime.getTime() >= 90 * 24 * 60 * 60 * 1000,\n ).length;\n const detail = `${list.length} saved · ${fmtBytes(totalBytes)} · oldest ${ageDays}d`;\n if (stale > 0) {\n return {\n label: \"sessions \",\n level: \"warn\",\n detail: `${detail} · ${stale} idle ≥90d (run \\`reasonix prune-sessions\\`)`,\n };\n }\n return { label: \"sessions \", level: \"ok\", detail };\n } catch (err) {\n return {\n label: \"sessions \",\n level: \"warn\",\n detail: `cannot list — ${(err as Error).message}`,\n };\n }\n}\n\nasync function checkHooks(projectRoot: string): Promise<Check> {\n try {\n const all = loadHooks({ projectRoot });\n const global = all.filter((h) => h.scope === \"global\").length;\n const project = all.filter((h) => h.scope === \"project\").length;\n return {\n label: \"hooks \",\n level: \"ok\",\n detail: `${global} global, ${project} project`,\n };\n } catch (err) {\n return {\n label: \"hooks \",\n level: \"warn\",\n detail: `couldn't parse settings.json — ${(err as Error).message}`,\n };\n }\n}\n\nasync function checkOllama(projectRoot: string): Promise<Check> {\n let exists = false;\n try {\n exists = await indexExists(projectRoot);\n } catch {\n /* treat as no index */\n }\n if (!exists) {\n return {\n label: \"semantic \",\n level: \"ok\",\n detail: \"not in use (no semantic index built; `reasonix index` to enable)\",\n };\n }\n const meta = readSemanticMeta(projectRoot);\n if (meta?.provider === \"openai-compat\") {\n const resolved = resolveSemanticEmbeddingConfig();\n if (resolved.provider !== \"openai-compat\") {\n return {\n label: \"semantic \",\n level: \"warn\",\n detail: `index uses openai-compat/${meta.model} but current config resolves to ${resolved.provider}/${resolved.model} — rebuild before searching`,\n };\n }\n return {\n label: \"semantic \",\n level: \"ok\",\n detail: `openai-compat · ${resolved.baseUrl} · model ${resolved.model} · api key configured`,\n };\n }\n try {\n const model = meta?.model || process.env.REASONIX_EMBED_MODEL || \"nomic-embed-text\";\n const status = await checkOllamaStatus(model);\n if (!status.binaryFound) {\n return {\n label: \"semantic \",\n level: \"warn\",\n detail:\n \"ollama binary not on PATH — semantic_search will fail; install from https://ollama.com\",\n };\n }\n if (!status.daemonRunning) {\n return {\n label: \"semantic \",\n level: \"warn\",\n detail:\n \"ollama daemon not running — `ollama serve` (or call /semantic in TUI to auto-start)\",\n };\n }\n if (!status.modelPulled) {\n return {\n label: \"semantic \",\n level: \"warn\",\n detail: `model ${status.modelName} not pulled — \\`ollama pull ${status.modelName}\\``,\n };\n }\n return {\n label: \"semantic \",\n level: \"ok\",\n detail: `ollama daemon up · model ${status.modelName} ready`,\n };\n } catch (err) {\n return {\n label: \"semantic \",\n level: \"warn\",\n detail: `probe failed — ${(err as Error).message}`,\n };\n }\n}\n\nfunction readSemanticMeta(\n projectRoot: string,\n): { provider: \"ollama\" | \"openai-compat\"; model: string } | null {\n try {\n const raw = readFileSync(join(projectRoot, \".reasonix\", \"semantic\", \"index.meta.json\"), \"utf8\");\n const parsed = JSON.parse(raw) as { provider?: string; model?: string };\n return {\n provider: parsed.provider === \"openai-compat\" ? \"openai-compat\" : \"ollama\",\n model: typeof parsed.model === \"string\" ? parsed.model : \"\",\n };\n } catch {\n return null;\n }\n}\n\nasync function checkProject(projectRoot: string): Promise<Check> {\n // Heuristic: a \"real\" project has either .git, REASONIX.md, or\n // package.json. Lacking all three, `reasonix code` still works but\n // @-mentions and the project-memory pin won't surface much.\n const markers = [\".git\", \"REASONIX.md\", \"package.json\", \"pyproject.toml\", \"Cargo.toml\", \"go.mod\"];\n const found = markers.filter((m) => existsSync(join(projectRoot, m)));\n if (found.length === 0) {\n return {\n label: \"project \",\n level: \"warn\",\n detail: `${projectRoot} has none of: ${markers.slice(0, 3).join(\", \")} … — \\`reasonix code\\` will still run, but @-mentions and project memory have nothing to anchor`,\n };\n }\n return {\n label: \"project \",\n level: \"ok\",\n detail: `${projectRoot} (${found.join(\", \")})`,\n };\n}\n\nexport async function doctorCommand(): Promise<void> {\n loadDotenv();\n\n const projectRoot = resolve(process.cwd());\n console.log(`${color(`reasonix ${VERSION} · doctor`, \"1\")} (cwd: ${projectRoot})`);\n console.log(` home: ${homedir()}`);\n console.log(\"\");\n\n // Run independent checks in parallel — saves ~5s when api-reach has\n // to time out. Each handler swallows its own throws into a `fail`\n // result so a thrown promise can't kill the whole report.\n const checks = await runDoctorChecks(projectRoot);\n\n for (const c of checks) {\n console.log(` ${badge(c.level)} ${c.label} ${c.detail}`);\n }\n\n const ok = checks.filter((c) => c.level === \"ok\").length;\n const warn = checks.filter((c) => c.level === \"warn\").length;\n const fail = checks.filter((c) => c.level === \"fail\").length;\n console.log(\"\");\n const summary = `${ok} ok · ${warn} warn · ${fail} fail`;\n if (fail > 0) {\n console.log(color(summary, \"31\"));\n process.exit(1);\n } else if (warn > 0) {\n console.log(color(summary, \"33\"));\n } else {\n console.log(color(summary, \"32\"));\n }\n}\n"],"mappings":"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","names":["info"]}
|
|
@@ -92,19 +92,36 @@ function compareVersions(a, b) {
|
|
|
92
92
|
if (!bPre) return -1;
|
|
93
93
|
return aPre < bPre ? -1 : aPre > bPre ? 1 : 0;
|
|
94
94
|
}
|
|
95
|
-
function
|
|
96
|
-
const
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (
|
|
101
|
-
|
|
95
|
+
function detectInstallSource(bin) {
|
|
96
|
+
const raw = bin ?? process.argv[1] ?? "";
|
|
97
|
+
if (!raw) return "unknown";
|
|
98
|
+
const norm = raw.replace(/\\/g, "/").toLowerCase();
|
|
99
|
+
if (/\/_npx\//.test(norm)) return "npx";
|
|
100
|
+
if (/\/\.pnpm\//.test(norm) && /dlx/i.test(norm)) return "npx";
|
|
101
|
+
const ua = (process.env.npm_config_user_agent ?? "").toLowerCase();
|
|
102
|
+
if (ua.includes("npx/")) return "npx";
|
|
103
|
+
if (/\/\.bun\//.test(norm) || /\/bun\/install\//.test(norm)) return "bun";
|
|
104
|
+
if (/\/pnpm\/global\//.test(norm) || /\/pnpm\/[^/]+\/node_modules\//.test(norm)) return "pnpm";
|
|
105
|
+
if (/\/yarn\/global\//.test(norm) || /\/\.yarn\/global\//.test(norm)) return "yarn";
|
|
106
|
+
if (/\/node_modules\/reasonix(\b|\/)/.test(norm)) return "npm";
|
|
107
|
+
return "unknown";
|
|
108
|
+
}
|
|
109
|
+
function detectNpmInstallPrefix(bin) {
|
|
110
|
+
const raw = bin ?? process.argv[1] ?? "";
|
|
111
|
+
if (!raw) return null;
|
|
112
|
+
const norm = raw.replace(/\\/g, "/");
|
|
113
|
+
const posix = norm.match(/^(.+?)\/lib\/node_modules\/reasonix(?:\/|$)/i);
|
|
114
|
+
if (posix) return posix[1] ?? null;
|
|
115
|
+
const win = norm.match(/^(.+?)\/node_modules\/reasonix(?:\/|$)/i);
|
|
116
|
+
if (win) return win[1] ?? null;
|
|
117
|
+
return null;
|
|
102
118
|
}
|
|
103
119
|
|
|
104
120
|
export {
|
|
105
121
|
VERSION,
|
|
106
122
|
getLatestVersion,
|
|
107
123
|
compareVersions,
|
|
108
|
-
|
|
124
|
+
detectInstallSource,
|
|
125
|
+
detectNpmInstallPrefix
|
|
109
126
|
};
|
|
110
|
-
//# sourceMappingURL=chunk-
|
|
127
|
+
//# sourceMappingURL=chunk-CRPQUBP6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/version.ts"],"sourcesContent":["/** VERSION sourced from package.json so it never drifts from npm; latest-check returns null on any failure. */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** npm registry endpoint for the `latest` dist-tag of this package. */\nconst REGISTRY_URL = \"https://registry.npmjs.org/reasonix/latest\";\n\n/** TTL for the on-disk cache entry. 24h keeps noise low; users who\n * want a fresh check can run `reasonix update` which passes\n * `force: true`. */\nexport const LATEST_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\n\n/** Network timeout. Short — we never block the UI waiting on this. */\nexport const LATEST_FETCH_TIMEOUT_MS = 2_000;\n\n/** `name === \"reasonix\"` guard avoids picking up an outer package.json when loaded as a dep. */\nfunction readPackageVersion(): string {\n try {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 6; i++) {\n const p = join(dir, \"package.json\");\n if (existsSync(p)) {\n const pkg = JSON.parse(readFileSync(p, \"utf8\"));\n if (pkg?.name === \"reasonix\" && typeof pkg.version === \"string\") {\n return pkg.version;\n }\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n /* fall through to fallback */\n }\n return \"0.0.0-dev\";\n}\n\nexport const VERSION: string = readPackageVersion();\n\ninterface VersionCacheEntry {\n version: string;\n /** Epoch millis the entry was written. Drives TTL comparisons. */\n checkedAt: number;\n}\n\nfunction cachePath(homeDirOverride?: string): string {\n return join(homeDirOverride ?? homedir(), \".reasonix\", \"version-cache.json\");\n}\n\nfunction readCache(homeDirOverride?: string): VersionCacheEntry | null {\n try {\n const raw = readFileSync(cachePath(homeDirOverride), \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed.version === \"string\" && typeof parsed.checkedAt === \"number\") {\n return parsed;\n }\n } catch {\n /* missing or malformed → no cached entry */\n }\n return null;\n}\n\nfunction writeCache(entry: VersionCacheEntry, homeDirOverride?: string): void {\n try {\n const p = cachePath(homeDirOverride);\n mkdirSync(dirname(p), { recursive: true });\n writeFileSync(p, JSON.stringify(entry), \"utf8\");\n } catch {\n /* cache is best-effort — a failed write just means we'll re-fetch\n * next launch. No reason to surface this to the user. */\n }\n}\n\nexport interface GetLatestVersionOptions {\n /** Ignore the cached entry and always fetch fresh. Used by `reasonix update`. */\n force?: boolean;\n /** Registry URL override (tests). */\n registryUrl?: string;\n /** Home-directory override (tests). */\n homeDir?: string;\n /** Fetch implementation override (tests). Defaults to `globalThis.fetch`. */\n fetchImpl?: typeof fetch;\n /** TTL override (tests). */\n ttlMs?: number;\n /** Network timeout override (tests). */\n timeoutMs?: number;\n}\n\n/** Returns null on failure; cache only writes on success so bad responses can't poison it. */\nexport async function getLatestVersion(opts: GetLatestVersionOptions = {}): Promise<string | null> {\n const ttl = opts.ttlMs ?? LATEST_CACHE_TTL_MS;\n if (!opts.force) {\n const cached = readCache(opts.homeDir);\n if (cached && Date.now() - cached.checkedAt < ttl) return cached.version;\n }\n\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n if (!fetchImpl) return null;\n const url = opts.registryUrl ?? REGISTRY_URL;\n const timeout = opts.timeoutMs ?? LATEST_FETCH_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const res = await fetchImpl(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n });\n if (!res.ok) return null;\n const body = (await res.json()) as { version?: unknown };\n if (typeof body.version !== \"string\") return null;\n writeCache({ version: body.version, checkedAt: Date.now() }, opts.homeDir);\n return body.version;\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Pre-release with same core sorts BELOW the bare version — matches npm `latest` dist-tag semantics. */\nexport function compareVersions(a: string, b: string): number {\n const [aCore = \"0\", aPre = \"\"] = a.split(\"-\", 2);\n const [bCore = \"0\", bPre = \"\"] = b.split(\"-\", 2);\n const aParts = aCore.split(\".\").map((p) => Number.parseInt(p, 10) || 0);\n const bParts = bCore.split(\".\").map((p) => Number.parseInt(p, 10) || 0);\n for (let i = 0; i < 3; i++) {\n const diff = (aParts[i] ?? 0) - (bParts[i] ?? 0);\n if (diff !== 0) return diff;\n }\n if (!aPre && !bPre) return 0;\n if (!aPre) return 1;\n if (!bPre) return -1;\n return aPre < bPre ? -1 : aPre > bPre ? 1 : 0;\n}\n\n/** False negatives are safe — `npm i -g` works for npx users too. */\nexport function isNpxInstall(): boolean {\n const bin = process.argv[1] ?? \"\";\n if (/[/\\\\]_npx[/\\\\]/.test(bin)) return true;\n if (/[/\\\\]\\.pnpm[/\\\\]/.test(bin) && /dlx/i.test(bin)) return true;\n const ua = process.env.npm_config_user_agent ?? \"\";\n if (ua.includes(\"npx/\")) return true;\n return false;\n}\n"],"mappings":";;;AAEA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,eAAe;AACxB,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAG9B,IAAM,eAAe;AAKd,IAAM,sBAAsB,KAAK,KAAK,KAAK;AAG3C,IAAM,0BAA0B;AAGvC,SAAS,qBAA6B;AACpC,MAAI;AACF,QAAI,MAAM,QAAQ,cAAc,YAAY,GAAG,CAAC;AAChD,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,IAAI,KAAK,KAAK,cAAc;AAClC,UAAI,WAAW,CAAC,GAAG;AACjB,cAAM,MAAM,KAAK,MAAM,aAAa,GAAG,MAAM,CAAC;AAC9C,YAAI,KAAK,SAAS,cAAc,OAAO,IAAI,YAAY,UAAU;AAC/D,iBAAO,IAAI;AAAA,QACb;AAAA,MACF;AACA,YAAM,SAAS,QAAQ,GAAG;AAC1B,UAAI,WAAW,IAAK;AACpB,YAAM;AAAA,IACR;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,IAAM,UAAkB,mBAAmB;AAQlD,SAAS,UAAU,iBAAkC;AACnD,SAAO,KAAK,mBAAmB,QAAQ,GAAG,aAAa,oBAAoB;AAC7E;AAEA,SAAS,UAAU,iBAAoD;AACrE,MAAI;AACF,UAAM,MAAM,aAAa,UAAU,eAAe,GAAG,MAAM;AAC3D,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,UAAU,OAAO,OAAO,YAAY,YAAY,OAAO,OAAO,cAAc,UAAU;AACxF,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAA0B,iBAAgC;AAC5E,MAAI;AACF,UAAM,IAAI,UAAU,eAAe;AACnC,cAAU,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;AACzC,kBAAc,GAAG,KAAK,UAAU,KAAK,GAAG,MAAM;AAAA,EAChD,QAAQ;AAAA,EAGR;AACF;AAkBA,eAAsB,iBAAiB,OAAgC,CAAC,GAA2B;AACjG,QAAM,MAAM,KAAK,SAAS;AAC1B,MAAI,CAAC,KAAK,OAAO;AACf,UAAM,SAAS,UAAU,KAAK,OAAO;AACrC,QAAI,UAAU,KAAK,IAAI,IAAI,OAAO,YAAY,IAAK,QAAO,OAAO;AAAA,EACnE;AAEA,QAAM,YAAY,KAAK,aAAa,WAAW;AAC/C,MAAI,CAAC,UAAW,QAAO;AACvB,QAAM,MAAM,KAAK,eAAe;AAChC,QAAM,UAAU,KAAK,aAAa;AAClC,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAC1D,MAAI;AACF,UAAM,MAAM,MAAM,UAAU,KAAK;AAAA,MAC/B,QAAQ,WAAW;AAAA,MACnB,SAAS,EAAE,QAAQ,mBAAmB;AAAA,IACxC,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,QAAI,OAAO,KAAK,YAAY,SAAU,QAAO;AAC7C,eAAW,EAAE,SAAS,KAAK,SAAS,WAAW,KAAK,IAAI,EAAE,GAAG,KAAK,OAAO;AACzE,WAAO,KAAK;AAAA,EACd,QAAQ;AACN,WAAO;AAAA,EACT,UAAE;AACA,iBAAa,KAAK;AAAA,EACpB;AACF;AAGO,SAAS,gBAAgB,GAAW,GAAmB;AAC5D,QAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC/C,QAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC/C,QAAM,SAAS,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,OAAO,SAAS,GAAG,EAAE,KAAK,CAAC;AACtE,QAAM,SAAS,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,OAAO,SAAS,GAAG,EAAE,KAAK,CAAC;AACtE,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,QAAQ,OAAO,CAAC,KAAK,MAAM,OAAO,CAAC,KAAK;AAC9C,QAAI,SAAS,EAAG,QAAO;AAAA,EACzB;AACA,MAAI,CAAC,QAAQ,CAAC,KAAM,QAAO;AAC3B,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,OAAO,OAAO,KAAK,OAAO,OAAO,IAAI;AAC9C;AAGO,SAAS,eAAwB;AACtC,QAAM,MAAM,QAAQ,KAAK,CAAC,KAAK;AAC/B,MAAI,iBAAiB,KAAK,GAAG,EAAG,QAAO;AACvC,MAAI,mBAAmB,KAAK,GAAG,KAAK,OAAO,KAAK,GAAG,EAAG,QAAO;AAC7D,QAAM,KAAK,QAAQ,IAAI,yBAAyB;AAChD,MAAI,GAAG,SAAS,MAAM,EAAG,QAAO;AAChC,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/version.ts"],"sourcesContent":["/** VERSION sourced from package.json so it never drifts from npm; latest-check returns null on any failure. */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** npm registry endpoint for the `latest` dist-tag of this package. */\nconst REGISTRY_URL = \"https://registry.npmjs.org/reasonix/latest\";\n\n/** TTL for the on-disk cache entry. 24h keeps noise low; users who\n * want a fresh check can run `reasonix update` which passes\n * `force: true`. */\nexport const LATEST_CACHE_TTL_MS = 24 * 60 * 60 * 1000;\n\n/** Network timeout. Short — we never block the UI waiting on this. */\nexport const LATEST_FETCH_TIMEOUT_MS = 2_000;\n\n/** `name === \"reasonix\"` guard avoids picking up an outer package.json when loaded as a dep. */\nfunction readPackageVersion(): string {\n try {\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 6; i++) {\n const p = join(dir, \"package.json\");\n if (existsSync(p)) {\n const pkg = JSON.parse(readFileSync(p, \"utf8\"));\n if (pkg?.name === \"reasonix\" && typeof pkg.version === \"string\") {\n return pkg.version;\n }\n }\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n } catch {\n /* fall through to fallback */\n }\n return \"0.0.0-dev\";\n}\n\nexport const VERSION: string = readPackageVersion();\n\ninterface VersionCacheEntry {\n version: string;\n /** Epoch millis the entry was written. Drives TTL comparisons. */\n checkedAt: number;\n}\n\nfunction cachePath(homeDirOverride?: string): string {\n return join(homeDirOverride ?? homedir(), \".reasonix\", \"version-cache.json\");\n}\n\nfunction readCache(homeDirOverride?: string): VersionCacheEntry | null {\n try {\n const raw = readFileSync(cachePath(homeDirOverride), \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed.version === \"string\" && typeof parsed.checkedAt === \"number\") {\n return parsed;\n }\n } catch {\n /* missing or malformed → no cached entry */\n }\n return null;\n}\n\nfunction writeCache(entry: VersionCacheEntry, homeDirOverride?: string): void {\n try {\n const p = cachePath(homeDirOverride);\n mkdirSync(dirname(p), { recursive: true });\n writeFileSync(p, JSON.stringify(entry), \"utf8\");\n } catch {\n /* cache is best-effort — a failed write just means we'll re-fetch\n * next launch. No reason to surface this to the user. */\n }\n}\n\nexport interface GetLatestVersionOptions {\n /** Ignore the cached entry and always fetch fresh. Used by `reasonix update`. */\n force?: boolean;\n /** Registry URL override (tests). */\n registryUrl?: string;\n /** Home-directory override (tests). */\n homeDir?: string;\n /** Fetch implementation override (tests). Defaults to `globalThis.fetch`. */\n fetchImpl?: typeof fetch;\n /** TTL override (tests). */\n ttlMs?: number;\n /** Network timeout override (tests). */\n timeoutMs?: number;\n}\n\n/** Returns null on failure; cache only writes on success so bad responses can't poison it. */\nexport async function getLatestVersion(opts: GetLatestVersionOptions = {}): Promise<string | null> {\n const ttl = opts.ttlMs ?? LATEST_CACHE_TTL_MS;\n if (!opts.force) {\n const cached = readCache(opts.homeDir);\n if (cached && Date.now() - cached.checkedAt < ttl) return cached.version;\n }\n\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n if (!fetchImpl) return null;\n const url = opts.registryUrl ?? REGISTRY_URL;\n const timeout = opts.timeoutMs ?? LATEST_FETCH_TIMEOUT_MS;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const res = await fetchImpl(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\" },\n });\n if (!res.ok) return null;\n const body = (await res.json()) as { version?: unknown };\n if (typeof body.version !== \"string\") return null;\n writeCache({ version: body.version, checkedAt: Date.now() }, opts.homeDir);\n return body.version;\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** Pre-release with same core sorts BELOW the bare version — matches npm `latest` dist-tag semantics. */\nexport function compareVersions(a: string, b: string): number {\n const [aCore = \"0\", aPre = \"\"] = a.split(\"-\", 2);\n const [bCore = \"0\", bPre = \"\"] = b.split(\"-\", 2);\n const aParts = aCore.split(\".\").map((p) => Number.parseInt(p, 10) || 0);\n const bParts = bCore.split(\".\").map((p) => Number.parseInt(p, 10) || 0);\n for (let i = 0; i < 3; i++) {\n const diff = (aParts[i] ?? 0) - (bParts[i] ?? 0);\n if (diff !== 0) return diff;\n }\n if (!aPre && !bPre) return 0;\n if (!aPre) return 1;\n if (!bPre) return -1;\n return aPre < bPre ? -1 : aPre > bPre ? 1 : 0;\n}\n\nexport type InstallSource = \"npm\" | \"bun\" | \"pnpm\" | \"yarn\" | \"npx\" | \"unknown\";\n\n/** Each manager owns a unique global path segment, so argv[1] tells us who installed us. */\nexport function detectInstallSource(bin?: string): InstallSource {\n const raw = bin ?? process.argv[1] ?? \"\";\n if (!raw) return \"unknown\";\n const norm = raw.replace(/\\\\/g, \"/\").toLowerCase();\n if (/\\/_npx\\//.test(norm)) return \"npx\";\n if (/\\/\\.pnpm\\//.test(norm) && /dlx/i.test(norm)) return \"npx\";\n const ua = (process.env.npm_config_user_agent ?? \"\").toLowerCase();\n if (ua.includes(\"npx/\")) return \"npx\";\n if (/\\/\\.bun\\//.test(norm) || /\\/bun\\/install\\//.test(norm)) return \"bun\";\n if (/\\/pnpm\\/global\\//.test(norm) || /\\/pnpm\\/[^/]+\\/node_modules\\//.test(norm)) return \"pnpm\";\n if (/\\/yarn\\/global\\//.test(norm) || /\\/\\.yarn\\/global\\//.test(norm)) return \"yarn\";\n if (/\\/node_modules\\/reasonix(\\b|\\/)/.test(norm)) return \"npm\";\n return \"unknown\";\n}\n\n/** Returns null when no path is given. Callers must check installSource first. */\nexport function isNpxInstall(): boolean {\n return detectInstallSource() === \"npx\";\n}\n\n/** Pin npm to the install location via --prefix so `nvm use` doesn't redirect the install elsewhere. */\nexport function detectNpmInstallPrefix(bin?: string): string | null {\n const raw = bin ?? process.argv[1] ?? \"\";\n if (!raw) return null;\n const norm = raw.replace(/\\\\/g, \"/\");\n const posix = norm.match(/^(.+?)\\/lib\\/node_modules\\/reasonix(?:\\/|$)/i);\n if (posix) return posix[1] ?? null;\n const win = norm.match(/^(.+?)\\/node_modules\\/reasonix(?:\\/|$)/i);\n if (win) return win[1] ?? null;\n return null;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
t
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MLXUGPJE.js";
|
|
5
5
|
|
|
6
6
|
// src/hooks.ts
|
|
7
7
|
import { spawn } from "child_process";
|
|
@@ -205,4 +205,4 @@ export {
|
|
|
205
205
|
formatHookOutcomeMessage,
|
|
206
206
|
runHooks
|
|
207
207
|
};
|
|
208
|
-
//# sourceMappingURL=chunk-
|
|
208
|
+
//# sourceMappingURL=chunk-IPCPEZWQ.js.map
|