@juspay/neurolink 11.22.1 → 11.22.3

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.
@@ -179,10 +179,22 @@ export async function createClaudeCodeReader() {
179
179
  const unpriced = new Set();
180
180
  const files = [];
181
181
  await collectTranscripts(projectsRoot(), files);
182
- const sinceDays = options?.sinceDays ?? DEFAULT_SINCE_DAYS;
183
- const cutoff = Number.isFinite(sinceDays) && sinceDays > 0
184
- ? Date.now() - sinceDays * 86_400_000
185
- : undefined;
182
+ // Only Infinity means "no time filter". A non-positive sinceDays used to
183
+ // leave the cutoff undefined and read EVERYTHING measured at 17,534
184
+ // files and 35.9s for `sinceDays: 0`, which is the widest possible scan
185
+ // in answer to the narrowest possible request. Zero now means a
186
+ // zero-length window, which is what it reads as.
187
+ // NaN is not a window, and it is the case the previous fix missed:
188
+ // Math.max(0, NaN) is NaN, every comparison against NaN is false, so the
189
+ // filter passes EVERY file. Measured: sinceDays NaN read 17,537 files in
190
+ // 32.8s — the same unbounded sweep this guard exists to prevent, reached
191
+ // by a different door. The old guard caught it with Number.isFinite and
192
+ // the replacement dropped that check.
193
+ const requestedDays = options?.sinceDays ?? DEFAULT_SINCE_DAYS;
194
+ const sinceDays = Number.isNaN(requestedDays) ? 0 : requestedDays;
195
+ const cutoff = sinceDays === Infinity
196
+ ? undefined
197
+ : Date.now() - Math.max(0, sinceDays) * 86_400_000;
186
198
  let filesScanned = 0;
187
199
  for (const file of files) {
188
200
  try {
@@ -167,10 +167,22 @@ export async function createCodexReader() {
167
167
  const models = new Set();
168
168
  const files = [];
169
169
  await collectRollouts(sessionsRoot(), files);
170
- const sinceDays = options?.sinceDays ?? DEFAULT_SINCE_DAYS;
171
- const cutoff = Number.isFinite(sinceDays) && sinceDays > 0
172
- ? Date.now() - sinceDays * 86_400_000
173
- : undefined;
170
+ // Only Infinity means "no time filter". A non-positive sinceDays used to
171
+ // leave the cutoff undefined and read EVERYTHING measured at 17,534
172
+ // files and 35.9s for `sinceDays: 0`, which is the widest possible scan
173
+ // in answer to the narrowest possible request. Zero now means a
174
+ // zero-length window, which is what it reads as.
175
+ // NaN is not a window, and it is the case the previous fix missed:
176
+ // Math.max(0, NaN) is NaN, every comparison against NaN is false, so the
177
+ // filter passes EVERY file. Measured: sinceDays NaN read 17,537 files in
178
+ // 32.8s — the same unbounded sweep this guard exists to prevent, reached
179
+ // by a different door. The old guard caught it with Number.isFinite and
180
+ // the replacement dropped that check.
181
+ const requestedDays = options?.sinceDays ?? DEFAULT_SINCE_DAYS;
182
+ const sinceDays = Number.isNaN(requestedDays) ? 0 : requestedDays;
183
+ const cutoff = sinceDays === Infinity
184
+ ? undefined
185
+ : Date.now() - Math.max(0, sinceDays) * 86_400_000;
174
186
  let filesScanned = 0;
175
187
  for (const file of files) {
176
188
  try {
@@ -114,10 +114,28 @@ export async function createOpenCodeReader() {
114
114
  });
115
115
  return { cliId: CLI_ID, totals, filesScanned: 0, errors };
116
116
  }
117
- const sinceDays = options?.sinceDays ?? DEFAULT_SINCE_DAYS;
118
- const cutoffMs = Number.isFinite(sinceDays) && sinceDays > 0
119
- ? Date.now() - sinceDays * 86_400_000
120
- : 0;
117
+ // Only Infinity means "no time filter". A non-positive sinceDays used to
118
+ // leave the cutoff undefined and read EVERYTHING — measured at 17,534
119
+ // files and 35.9s for `sinceDays: 0`, which is the widest possible scan
120
+ // in answer to the narrowest possible request. Zero now means a
121
+ // zero-length window, which is what it reads as.
122
+ // NaN is not a window, and it is the case the previous fix missed:
123
+ // Math.max(0, NaN) is NaN, every comparison against NaN is false, so the
124
+ // filter passes EVERY file. Measured: sinceDays NaN read 17,537 files in
125
+ // 32.8s — the same unbounded sweep this guard exists to prevent, reached
126
+ // by a different door. The old guard caught it with Number.isFinite and
127
+ // the replacement dropped that check.
128
+ //
129
+ // On this reader NaN was worse than an unbounded scan: the value went
130
+ // into the SQL text, and SQLite parses a bare NaN as an identifier —
131
+ // "no such column: NaN" — so the whole scan failed rather than
132
+ // over-reading. The cutoff is a bound parameter now, so a value can
133
+ // never be SQL syntax whatever it is.
134
+ const requestedDays = options?.sinceDays ?? DEFAULT_SINCE_DAYS;
135
+ const sinceDays = Number.isNaN(requestedDays) ? 0 : requestedDays;
136
+ const cutoffMs = sinceDays === Infinity
137
+ ? 0
138
+ : Date.now() - Math.max(0, sinceDays) * 86_400_000;
121
139
  let db;
122
140
  try {
123
141
  // Read-only: this is the user's live store and OpenCode may be running.
@@ -125,9 +143,13 @@ export async function createOpenCodeReader() {
125
143
  // Filtered in SQL rather than in JS. `time_created` is epoch ms, and
126
144
  // the table holds thousands of rows whose `data` blobs are large — the
127
145
  // point of the time window is not reading them at all.
146
+ // Bound parameter, not interpolation. A number spliced into SQL text
147
+ // is still SQL text: NaN parsed as an identifier and failed the whole
148
+ // scan with "no such column: NaN". A bound value cannot become syntax
149
+ // whatever it holds.
128
150
  const rows = db
129
- .prepare(`SELECT data FROM message WHERE time_created >= ${cutoffMs}`)
130
- .all();
151
+ .prepare("SELECT data FROM message WHERE time_created >= ?")
152
+ .all(cutoffMs);
131
153
  for (const row of rows) {
132
154
  if (typeof row.data !== "string") {
133
155
  continue;
@@ -121,7 +121,7 @@ export type LocalUsageAggregateReport = {
121
121
  generatedAt: string;
122
122
  /** Only CLIs whose store was detected AND scanned appear here. */
123
123
  totals: Partial<Record<LocalUsageCliId, LocalUsageTotals>>;
124
- /** CLIs that were registered but produced nothing, and why. */
124
+ /** CLIs whose reader could not be created, detected, or scanned, and why. */
125
125
  failures: LocalUsageReaderFailure[];
126
126
  /** CLIs with no local store on this machine — absent, not failed. */
127
127
  notInstalled: LocalUsageCliId[];
@@ -161,7 +161,7 @@ export type LocalUsageCodexSessionRollup = {
161
161
  */
162
162
  export type LocalUsageSqliteDatabase = {
163
163
  prepare: (sql: string) => {
164
- all: () => unknown[];
164
+ all: (...params: unknown[]) => unknown[];
165
165
  };
166
166
  close: () => void;
167
167
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "11.22.1",
3
+ "version": "11.22.3",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {
@@ -1,3 +0,0 @@
1
- import type { Argv } from "yargs";
2
- export declare function addOllamaCommands(cli: Argv): void;
3
- export default addOllamaCommands;
@@ -1,334 +0,0 @@
1
- import { spawnSync } from "child_process";
2
- import chalk from "chalk";
3
- import ora from "ora";
4
- import inquirer from "inquirer";
5
- import { logger } from "../../utils/logger.js";
6
- import { OllamaUtils } from "../utils/ollamaUtils.js";
7
- export function addOllamaCommands(cli) {
8
- cli.command("ollama <command>", "Manage Ollama local AI models", (yargs) => {
9
- return yargs
10
- .command("list-models", "List installed Ollama models", {}, listModelsHandler)
11
- .command("pull <model>", "Download an Ollama model", {
12
- model: {
13
- describe: "Model name to download",
14
- type: "string",
15
- demandOption: true,
16
- },
17
- }, pullModelHandler)
18
- .command("remove <model>", "Remove an Ollama model", {
19
- model: {
20
- describe: "Model name to remove",
21
- type: "string",
22
- demandOption: true,
23
- },
24
- }, removeModelHandler)
25
- .command("status", "Check Ollama service status", {}, statusHandler)
26
- .command("start", "Start Ollama service", {}, startHandler)
27
- .command("stop", "Stop Ollama service", {}, stopHandler)
28
- .command("setup", "Interactive Ollama setup", {}, setupHandler)
29
- .demandCommand(1, "Please specify a command");
30
- }, () => { });
31
- }
32
- /** See the note on the identically-named constant in ../utils/ollamaUtils.ts. */
33
- const OLLAMA_QUERY_TIMEOUT_MS = 15_000;
34
- async function listModelsHandler() {
35
- const spinner = ora("Fetching installed models...").start();
36
- try {
37
- const res = spawnSync("ollama", ["list"], {
38
- encoding: "utf8",
39
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
40
- killSignal: "SIGKILL",
41
- });
42
- if (res.error) {
43
- throw res.error;
44
- }
45
- spinner.succeed("Installed models:");
46
- const output = res.stdout?.toString().trim();
47
- if (output) {
48
- logger.always(output);
49
- }
50
- else {
51
- logger.always(chalk.yellow('No models installed. Use "neurolink ollama pull <model>" to download a model.'));
52
- }
53
- }
54
- catch (error) {
55
- spinner.fail("Failed to list models. Is Ollama installed?");
56
- const errorMessage = error instanceof Error ? error.message : String(error);
57
- logger.error(chalk.red("Error:", errorMessage));
58
- logger.always(chalk.blue("\nTip: Install Ollama from https://ollama.ai"));
59
- process.exit(1);
60
- }
61
- }
62
- async function pullModelHandler(argv) {
63
- const { model } = argv;
64
- logger.always(chalk.blue(`Downloading model: ${model}`));
65
- logger.always(chalk.gray("This may take several minutes..."));
66
- try {
67
- // Deliberately unbounded: a model pull is hundreds of megabytes and
68
- // legitimately runs for many minutes. It also inherits stdio, so the user
69
- // sees progress and can Ctrl-C — the two things a wedged query lacks.
70
- const res = spawnSync("ollama", ["pull", model], { stdio: "inherit" });
71
- if (res.error) {
72
- throw res.error;
73
- }
74
- if (res.status !== 0) {
75
- throw new Error(`ollama pull exited with code ${res.status}`);
76
- }
77
- logger.always(chalk.green(`\n✅ Successfully downloaded ${model}`));
78
- logger.always(chalk.blue(`\nTest it with: npx @juspay/neurolink generate "Hello!" --provider ollama --model ${model}`));
79
- }
80
- catch (error) {
81
- logger.error(chalk.red(`\n❌ Failed to download ${model}`));
82
- const errorMessage = error instanceof Error ? error.message : String(error);
83
- logger.error(chalk.red("Error:", errorMessage));
84
- process.exit(1);
85
- }
86
- }
87
- async function removeModelHandler(argv) {
88
- const { model } = argv;
89
- const { confirm } = await inquirer.prompt([
90
- {
91
- type: "confirm",
92
- name: "confirm",
93
- message: `Are you sure you want to remove model "${model}"?`,
94
- default: false,
95
- },
96
- ]);
97
- if (!confirm) {
98
- logger.always(chalk.yellow("Removal cancelled."));
99
- return;
100
- }
101
- const spinner = ora(`Removing model ${model}...`).start();
102
- try {
103
- const res = spawnSync("ollama", ["rm", model], {
104
- encoding: "utf8",
105
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
106
- killSignal: "SIGKILL",
107
- });
108
- if (res.error) {
109
- throw res.error;
110
- }
111
- if (res.status !== 0) {
112
- throw new Error(`ollama rm exited with ${res.status}`);
113
- }
114
- spinner.succeed(`Successfully removed ${model}`);
115
- }
116
- catch (_error) {
117
- spinner.fail(`Failed to remove ${model}`);
118
- const errorMessage = _error instanceof Error ? _error.message : String(_error);
119
- logger.error(chalk.red("Error:", errorMessage));
120
- process.exit(1);
121
- }
122
- }
123
- async function statusHandler() {
124
- const spinner = ora("Checking Ollama service status...").start();
125
- try {
126
- const res = spawnSync("ollama", ["list"], {
127
- encoding: "utf8",
128
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
129
- killSignal: "SIGKILL",
130
- });
131
- if (res.error) {
132
- throw res.error;
133
- }
134
- if (res.status !== 0) {
135
- throw new Error("Ollama not running");
136
- }
137
- spinner.succeed("Ollama service is running");
138
- }
139
- catch (error) {
140
- spinner.fail("Ollama service is not running");
141
- logger.debug("Ollama status check failed:", error);
142
- logger.always(chalk.yellow("\nStart Ollama with: ollama serve"));
143
- process.exit(1);
144
- }
145
- }
146
- async function startHandler() {
147
- await OllamaUtils.startOllamaService();
148
- }
149
- async function stopHandler() {
150
- const spinner = ora("Stopping Ollama service...").start();
151
- try {
152
- if (process.platform === "darwin") {
153
- try {
154
- spawnSync("pkill", ["ollama"], {
155
- encoding: "utf8",
156
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
157
- killSignal: "SIGKILL",
158
- });
159
- }
160
- catch {
161
- spawnSync("killall", ["Ollama"], {
162
- encoding: "utf8",
163
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
164
- killSignal: "SIGKILL",
165
- });
166
- }
167
- }
168
- else if (process.platform === "linux") {
169
- try {
170
- spawnSync("systemctl", ["stop", "ollama"], {
171
- encoding: "utf8",
172
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
173
- killSignal: "SIGKILL",
174
- });
175
- }
176
- catch {
177
- spawnSync("pkill", ["ollama"], {
178
- encoding: "utf8",
179
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
180
- killSignal: "SIGKILL",
181
- });
182
- }
183
- }
184
- else {
185
- spawnSync("taskkill", ["/F", "/IM", "ollama.exe"], {
186
- encoding: "utf8",
187
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
188
- killSignal: "SIGKILL",
189
- });
190
- }
191
- spinner.succeed("Ollama service stopped");
192
- }
193
- catch (err) {
194
- spinner.fail("Failed to stop Ollama service");
195
- logger.error(chalk.red("It may not be running or requires manual stop"));
196
- logger.error(chalk.red(`Error details: ${err}`));
197
- }
198
- }
199
- async function setupHandler() {
200
- logger.always(chalk.blue("🦙 Welcome to Ollama Setup!\n"));
201
- // Check installation
202
- const checkSpinner = ora("Checking Ollama installation...").start();
203
- let isInstalled = false;
204
- try {
205
- spawnSync("ollama", ["--version"], {
206
- encoding: "utf8",
207
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
208
- killSignal: "SIGKILL",
209
- });
210
- isInstalled = true;
211
- checkSpinner.succeed("Ollama is installed");
212
- }
213
- catch {
214
- checkSpinner.fail("Ollama is not installed");
215
- }
216
- if (!isInstalled) {
217
- logger.always(chalk.yellow("\nOllama needs to be installed first."));
218
- logger.always(chalk.blue("\nInstallation instructions:"));
219
- if (process.platform === "darwin") {
220
- logger.always("\nFor macOS:");
221
- logger.always(chalk.gray(" brew install ollama"));
222
- logger.always(chalk.gray(" # or download from https://ollama.ai"));
223
- }
224
- else if (process.platform === "linux") {
225
- logger.always("\nFor Linux:");
226
- logger.always(chalk.gray(" curl -fsSL https://ollama.ai/install.sh | sh"));
227
- }
228
- else {
229
- logger.always("\nFor Windows:");
230
- logger.always(chalk.gray(" Download from https://ollama.ai"));
231
- }
232
- const { proceedAnyway } = await inquirer.prompt([
233
- {
234
- type: "confirm",
235
- name: "proceedAnyway",
236
- message: "Would you like to continue with setup anyway?",
237
- default: false,
238
- },
239
- ]);
240
- if (!proceedAnyway) {
241
- logger.always(chalk.blue("\nInstall Ollama and run setup again!"));
242
- return;
243
- }
244
- }
245
- // Check if service is running
246
- let serviceRunning = false;
247
- try {
248
- spawnSync("ollama", ["list"], {
249
- encoding: "utf8",
250
- timeout: OLLAMA_QUERY_TIMEOUT_MS,
251
- killSignal: "SIGKILL",
252
- });
253
- serviceRunning = true;
254
- logger.always(chalk.green("\n✅ Ollama service is running"));
255
- }
256
- catch {
257
- logger.always(chalk.yellow("\n⚠️ Ollama service is not running"));
258
- const { startService } = await inquirer.prompt([
259
- {
260
- type: "confirm",
261
- name: "startService",
262
- message: "Would you like to start the Ollama service?",
263
- default: true,
264
- },
265
- ]);
266
- if (startService) {
267
- await startHandler();
268
- serviceRunning = true;
269
- }
270
- }
271
- if (serviceRunning) {
272
- // List available models
273
- logger.always(chalk.blue("\n📦 Popular Ollama models:"));
274
- logger.always(" • llama2 (7B) - General purpose");
275
- logger.always(" • codellama (7B) - Code generation");
276
- logger.always(" • mistral (7B) - Fast and efficient");
277
- logger.always(" • tinyllama (1B) - Lightweight");
278
- logger.always(" • phi (2.7B) - Microsoft's compact model");
279
- const { downloadModel } = await inquirer.prompt([
280
- {
281
- type: "confirm",
282
- name: "downloadModel",
283
- message: "Would you like to download a model?",
284
- default: true,
285
- },
286
- ]);
287
- if (downloadModel) {
288
- const { selectedModel } = await inquirer.prompt([
289
- {
290
- type: "select",
291
- name: "selectedModel",
292
- message: "Select a model to download:",
293
- choices: [
294
- {
295
- name: "llama2 (7B) - Recommended for general use",
296
- value: "llama2",
297
- },
298
- {
299
- name: "codellama (7B) - Best for code generation",
300
- value: "codellama",
301
- },
302
- { name: "mistral (7B) - Fast and efficient", value: "mistral" },
303
- { name: "tinyllama (1B) - Lightweight, fast", value: "tinyllama" },
304
- { name: "phi (2.7B) - Microsoft's compact model", value: "phi" },
305
- { name: "Other (enter manually)", value: "other" },
306
- ],
307
- },
308
- ]);
309
- let modelToDownload = selectedModel;
310
- if (selectedModel === "other") {
311
- const { customModel } = await inquirer.prompt([
312
- {
313
- type: "input",
314
- name: "customModel",
315
- message: "Enter the model name:",
316
- validate: (input) => input.trim().length > 0 || "Model name is required",
317
- },
318
- ]);
319
- modelToDownload = customModel;
320
- }
321
- await pullModelHandler({ model: modelToDownload });
322
- }
323
- }
324
- logger.always(chalk.green("\n✅ Setup complete!\n"));
325
- logger.always(chalk.blue("Next steps:"));
326
- logger.always("1. List models: " + chalk.gray("neurolink ollama list-models"));
327
- logger.always("2. Generate text: " +
328
- chalk.gray('neurolink generate "Hello!" --provider ollama'));
329
- logger.always("3. Use specific model: " +
330
- chalk.gray('neurolink generate "Hello!" --provider ollama --model codellama'));
331
- logger.always(chalk.gray("\nFor more information, see: https://docs.neurolink.ai/providers/ollama"));
332
- }
333
- export default addOllamaCommands;
334
- //# sourceMappingURL=ollama.js.map
@@ -1,27 +0,0 @@
1
- /**
2
- * Workflow CLI Commands for NeuroLink
3
- *
4
- * Implements commands for workflow management and execution:
5
- * - neurolink workflow list - List available predefined workflows
6
- * - neurolink workflow info <name> - Show details of a workflow
7
- * - neurolink workflow execute <name> <prompt> - Execute a workflow
8
- */
9
- import type { CommandModule } from "yargs";
10
- /**
11
- * Workflow CLI command factory
12
- */
13
- export declare class WorkflowCommandFactory {
14
- static createWorkflowCommands(): CommandModule;
15
- /**
16
- * List all predefined workflows
17
- */
18
- private static executeList;
19
- /**
20
- * Show details of a specific workflow
21
- */
22
- private static executeInfo;
23
- /**
24
- * Execute a workflow
25
- */
26
- private static executeWorkflow;
27
- }