scai 0.1.43 → 0.1.44

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.
@@ -1,27 +1,45 @@
1
- import fsSync from 'fs';
2
- import { LOG_PATH, PID_PATH, SCAI_HOME } from '../constants.js';
1
+ import { db } from '../db/client.js';
2
+ import { runDaemonBatch } from './daemonBatch.js'; // assuming this function is already defined
3
3
  import { log } from '../utils/log.js';
4
- import { runDaemonBatch } from '../daemon/daemonBatch.js'; // āœ… now from utils
5
- const SLEEP_MS = 30 * 1000;
6
- const IDLE_SLEEP_MS = 4 * SLEEP_MS;
7
- function sleep(ms) {
8
- return new Promise(resolve => setTimeout(resolve, ms));
4
+ // Time between each batch in milliseconds
5
+ const SLEEP_MS = 2000; // Adjust as needed
6
+ const IDLE_SLEEP_MS = 5000; // Adjust as needed
7
+ // Check if there are any files left to process
8
+ async function isQueueEmpty() {
9
+ // Query the database for the count of files with certain processing statuses
10
+ const row = db.prepare(`
11
+ SELECT COUNT(*) AS count
12
+ FROM files
13
+ WHERE processing_status IN ('unprocessed')
14
+ `).get();
15
+ // Cast the row to an object that has a `count` property of type number
16
+ const castRow = row;
17
+ // Check if the casted `row` has a valid `count` property (number)
18
+ if (typeof castRow.count !== 'number') {
19
+ console.error('Error: Invalid count value in the database query result.');
20
+ return true; // Assume queue is empty if the count is invalid
21
+ }
22
+ // Return true if count is 0, otherwise false
23
+ return castRow.count === 0;
9
24
  }
10
- async function runDaemonScheduler() {
11
- fsSync.mkdirSync(SCAI_HOME, { recursive: true });
12
- fsSync.writeFileSync(PID_PATH, process.pid.toString(), 'utf-8');
13
- fsSync.appendFileSync(LOG_PATH, `\n\n🧠 Daemon started at ${new Date().toISOString()} — PID ${process.pid}\n`);
14
- let cycles = 0;
25
+ export async function daemonWorker() {
15
26
  while (true) {
27
+ // Execute a batch job
16
28
  const didWork = await runDaemonBatch();
17
- cycles++;
18
- if (cycles % 20 === 0) {
19
- log(`šŸŒ€ Still running. Cycles: ${cycles}`);
29
+ if (!didWork) {
30
+ // Check if the queue is empty after a batch job
31
+ const queueEmpty = await isQueueEmpty();
32
+ if (queueEmpty) {
33
+ // If no files are left to process, stop the daemon
34
+ log("āœ… No more work left. Stopping daemon.");
35
+ break;
36
+ }
20
37
  }
38
+ // Sleep for a set amount of time before checking again
21
39
  await sleep(didWork ? SLEEP_MS : IDLE_SLEEP_MS);
22
40
  }
23
41
  }
24
- runDaemonScheduler().catch(err => {
25
- log(`āŒ Daemon crashed: ${err instanceof Error ? err.message : String(err)}`);
26
- process.exit(1);
27
- });
42
+ // Sleep function to control how often the worker checks
43
+ function sleep(ms) {
44
+ return new Promise(resolve => setTimeout(resolve, ms));
45
+ }
@@ -1,14 +1,22 @@
1
1
  import { parse } from 'acorn';
2
- import { simple as walkSimple } from 'acorn-walk';
2
+ import { ancestor as walkAncestor } from 'acorn-walk';
3
3
  import { generateEmbedding } from '../../lib/generateEmbedding.js';
4
4
  import { db } from '../client.js';
5
5
  import path from 'path';
6
6
  import { log } from '../../utils/log.js';
7
- /**
8
- * Parses a JavaScript/TypeScript file, extracts all top-level functions,
9
- * generates embeddings, and indexes both the functions and any calls made
10
- * within each function into the database.
11
- */
7
+ function getFunctionName(node, parent, fileName) {
8
+ if (node.id?.name)
9
+ return node.id.name;
10
+ if (parent?.type === 'VariableDeclarator' && parent.id?.name)
11
+ return parent.id.name;
12
+ if (parent?.type === 'Property' && parent.key?.name)
13
+ return parent.key.name;
14
+ if (parent?.type === 'AssignmentExpression' && parent.left?.name)
15
+ return parent.left.name;
16
+ if (parent?.type === 'MethodDefinition' && parent.key?.name)
17
+ return parent.key.name;
18
+ return `${fileName}:<anon>`;
19
+ }
12
20
  export async function extractFromJS(filePath, content, fileId) {
13
21
  const ast = parse(content, {
14
22
  ecmaVersion: 'latest',
@@ -16,27 +24,36 @@ export async function extractFromJS(filePath, content, fileId) {
16
24
  locations: true,
17
25
  });
18
26
  const functions = [];
19
- walkSimple(ast, {
20
- FunctionDeclaration(node) {
21
- const name = node.id?.name || `${path.basename(filePath)}:<anon>`;
22
- const start_line = node?.loc?.start.line ?? -1;
23
- const end_line = node?.loc?.end.line ?? -1;
24
- const body = content.slice(node.start, node.end);
25
- functions.push({ name, start_line, end_line, body });
27
+ walkAncestor(ast, {
28
+ FunctionDeclaration(node, ancestors) {
29
+ const parent = ancestors[ancestors.length - 2];
30
+ const name = getFunctionName(node, parent, path.basename(filePath));
31
+ functions.push({
32
+ name,
33
+ start_line: node.loc?.start.line ?? -1,
34
+ end_line: node.loc?.end.line ?? -1,
35
+ body: content.slice(node.start, node.end),
36
+ });
26
37
  },
27
- FunctionExpression(node) {
28
- const name = `${path.basename(filePath)}:<anon>`;
29
- const start_line = node?.loc?.start.line ?? -1;
30
- const end_line = node?.loc?.end.line ?? -1;
31
- const body = content.slice(node.start, node.end);
32
- functions.push({ name, start_line, end_line, body });
38
+ FunctionExpression(node, ancestors) {
39
+ const parent = ancestors[ancestors.length - 2];
40
+ const name = getFunctionName(node, parent, path.basename(filePath));
41
+ functions.push({
42
+ name,
43
+ start_line: node.loc?.start.line ?? -1,
44
+ end_line: node.loc?.end.line ?? -1,
45
+ body: content.slice(node.start, node.end),
46
+ });
33
47
  },
34
- ArrowFunctionExpression(node) {
35
- const name = `${path.basename(filePath)}:<anon>`;
36
- const start_line = node?.loc?.start.line ?? -1;
37
- const end_line = node?.loc?.end.line ?? -1;
38
- const body = content.slice(node.start, node.end);
39
- functions.push({ name, start_line, end_line, body });
48
+ ArrowFunctionExpression(node, ancestors) {
49
+ const parent = ancestors[ancestors.length - 2];
50
+ const name = getFunctionName(node, parent, path.basename(filePath));
51
+ functions.push({
52
+ name,
53
+ start_line: node.loc?.start.line ?? -1,
54
+ end_line: node.loc?.end.line ?? -1,
55
+ body: content.slice(node.start, node.end),
56
+ });
40
57
  },
41
58
  });
42
59
  if (functions.length === 0) {
@@ -62,15 +79,15 @@ export async function extractFromJS(filePath, content, fileId) {
62
79
  lang: 'js'
63
80
  });
64
81
  const callerId = result.lastInsertRowid;
65
- const fnAst = parse(content, {
82
+ const fnAst = parse(fn.body, {
66
83
  ecmaVersion: 'latest',
67
84
  sourceType: 'module',
68
85
  locations: true,
69
86
  });
70
87
  const calls = [];
71
- walkSimple(fnAst, {
88
+ walkAncestor(fnAst, {
72
89
  CallExpression(node) {
73
- if (node.callee.type === 'Identifier' && node.callee.name) {
90
+ if (node.callee?.type === 'Identifier' && node.callee.name) {
74
91
  calls.push({ calleeName: node.callee.name });
75
92
  }
76
93
  }
@@ -86,7 +103,6 @@ export async function extractFromJS(filePath, content, fileId) {
86
103
  }
87
104
  log(`šŸ“Œ Indexed function: ${fn.name} with ${calls.length} calls`);
88
105
  }
89
- // āœ… Mark as extracted using new processing_status column
90
106
  db.prepare(`
91
107
  UPDATE files
92
108
  SET processing_status = 'extracted'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.43",
3
+ "version": "0.1.44",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"