@ottocode/server 0.1.176 → 0.1.177

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottocode/server",
3
- "version": "0.1.176",
3
+ "version": "0.1.177",
4
4
  "description": "HTTP API server for ottocode",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -29,8 +29,8 @@
29
29
  "typecheck": "tsc --noEmit"
30
30
  },
31
31
  "dependencies": {
32
- "@ottocode/sdk": "0.1.176",
33
- "@ottocode/database": "0.1.176",
32
+ "@ottocode/sdk": "0.1.177",
33
+ "@ottocode/database": "0.1.177",
34
34
  "drizzle-orm": "^0.44.5",
35
35
  "hono": "^4.9.9",
36
36
  "zod": "^4.1.8"
@@ -1,14 +1,25 @@
1
1
  import type { Hono } from 'hono';
2
2
  import { readdir, readFile } from 'node:fs/promises';
3
3
  import { join, relative } from 'node:path';
4
+ import { spawn } from 'node:child_process';
4
5
  import { exec } from 'node:child_process';
5
6
  import { promisify } from 'node:util';
6
7
  import { serializeError } from '../runtime/errors/api-error.ts';
7
8
  import { logger } from '@ottocode/sdk';
9
+ import { resolveBinary } from '@ottocode/sdk/tools/bin-manager';
8
10
 
9
11
  const execAsync = promisify(exec);
10
12
 
11
- const EXCLUDED_PATTERNS = [
13
+ const EXCLUDED_FILES = new Set([
14
+ '.DS_Store',
15
+ 'bun.lockb',
16
+ '.env',
17
+ '.env.local',
18
+ '.env.production',
19
+ '.env.development',
20
+ ]);
21
+
22
+ const EXCLUDED_DIRS = new Set([
12
23
  'node_modules',
13
24
  '.git',
14
25
  'dist',
@@ -16,26 +27,72 @@ const EXCLUDED_PATTERNS = [
16
27
  '.next',
17
28
  '.nuxt',
18
29
  '.turbo',
30
+ '.astro',
31
+ '.svelte-kit',
32
+ '.vercel',
33
+ '.output',
19
34
  'coverage',
20
35
  '.cache',
21
- '.DS_Store',
22
- 'bun.lockb',
23
- '.env',
24
- '.env.local',
25
- '.env.production',
26
- '.env.development',
27
- ];
36
+ '__pycache__',
37
+ '.tsbuildinfo',
38
+ ]);
28
39
 
29
- function shouldExclude(name: string): boolean {
30
- for (const pattern of EXCLUDED_PATTERNS) {
31
- if (pattern.includes('*')) {
32
- const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`);
33
- if (regex.test(name)) return true;
34
- } else if (name === pattern || name.endsWith(pattern)) {
35
- return true;
36
- }
37
- }
38
- return false;
40
+ function shouldExcludeFile(name: string): boolean {
41
+ return EXCLUDED_FILES.has(name);
42
+ }
43
+
44
+ function shouldExcludeDir(name: string): boolean {
45
+ return EXCLUDED_DIRS.has(name);
46
+ }
47
+
48
+ async function listFilesWithRg(
49
+ projectRoot: string,
50
+ limit: number,
51
+ ): Promise<{ files: string[]; truncated: boolean }> {
52
+ const rgBin = await resolveBinary('rg');
53
+
54
+ return new Promise((resolve) => {
55
+ const args = [
56
+ '--files',
57
+ '--hidden',
58
+ '--glob', '!.git/',
59
+ '--sort', 'path',
60
+ ];
61
+
62
+ const proc = spawn(rgBin, args, { cwd: projectRoot });
63
+ let stdout = '';
64
+ let stderr = '';
65
+
66
+ proc.stdout.on('data', (data) => {
67
+ stdout += data.toString();
68
+ });
69
+
70
+ proc.stderr.on('data', (data) => {
71
+ stderr += data.toString();
72
+ });
73
+
74
+ proc.on('close', (code) => {
75
+ if (code !== 0 && code !== 1) {
76
+ logger.warn('rg --files failed, falling back', { stderr } as Record<string, unknown>);
77
+ resolve({ files: [], truncated: false });
78
+ return;
79
+ }
80
+
81
+ const allFiles = stdout.split('\n').filter(Boolean);
82
+
83
+ const filtered = allFiles.filter((f) => {
84
+ const filename = f.split('/').pop() || f;
85
+ return !shouldExcludeFile(filename);
86
+ });
87
+
88
+ const truncated = filtered.length > limit;
89
+ resolve({ files: filtered.slice(0, limit), truncated });
90
+ });
91
+
92
+ proc.on('error', () => {
93
+ resolve({ files: [], truncated: false });
94
+ });
95
+ });
39
96
  }
40
97
 
41
98
  async function parseGitignore(projectRoot: string): Promise<Set<string>> {
@@ -104,21 +161,17 @@ async function traverseDirectory(
104
161
  return { files: collected, truncated: true };
105
162
  }
106
163
 
107
- if (shouldExclude(entry.name)) {
108
- continue;
109
- }
110
-
111
164
  const fullPath = join(dir, entry.name);
112
165
  const relativePath = relative(projectRoot, fullPath);
113
166
 
114
- if (
115
- gitignorePatterns &&
116
- matchesGitignorePattern(relativePath, gitignorePatterns)
117
- ) {
118
- continue;
119
- }
120
-
121
167
  if (entry.isDirectory()) {
168
+ if (shouldExcludeDir(entry.name)) continue;
169
+ if (
170
+ gitignorePatterns &&
171
+ matchesGitignorePattern(relativePath, gitignorePatterns)
172
+ ) {
173
+ continue;
174
+ }
122
175
  const result = await traverseDirectory(
123
176
  fullPath,
124
177
  projectRoot,
@@ -132,11 +185,18 @@ async function traverseDirectory(
132
185
  return result;
133
186
  }
134
187
  } else if (entry.isFile()) {
188
+ if (shouldExcludeFile(entry.name)) continue;
189
+ if (
190
+ gitignorePatterns &&
191
+ matchesGitignorePattern(relativePath, gitignorePatterns)
192
+ ) {
193
+ continue;
194
+ }
135
195
  collected.push(relativePath);
136
196
  }
137
197
  }
138
198
  } catch (err) {
139
- logger.warn(`Failed to read directory ${dir}:`, err);
199
+ logger.warn(`Failed to read directory ${dir}:`, err as Record<string, unknown>);
140
200
  }
141
201
 
142
202
  return { files: collected, truncated: false };
@@ -167,7 +227,7 @@ async function getChangedFiles(
167
227
  }
168
228
  return changedFiles;
169
229
  } catch (_err) {
170
- return new Set();
230
+ return new Map();
171
231
  }
172
232
  }
173
233
 
@@ -178,17 +238,20 @@ export function registerFilesRoutes(app: Hono) {
178
238
  const maxDepth = Number.parseInt(c.req.query('maxDepth') || '10', 10);
179
239
  const limit = Number.parseInt(c.req.query('limit') || '1000', 10);
180
240
 
181
- const gitignorePatterns = await parseGitignore(projectRoot);
241
+ let result = await listFilesWithRg(projectRoot, limit);
182
242
 
183
- const result = await traverseDirectory(
184
- projectRoot,
185
- projectRoot,
186
- maxDepth,
187
- 0,
188
- limit,
189
- [],
190
- gitignorePatterns,
191
- );
243
+ if (result.files.length === 0) {
244
+ const gitignorePatterns = await parseGitignore(projectRoot);
245
+ result = await traverseDirectory(
246
+ projectRoot,
247
+ projectRoot,
248
+ maxDepth,
249
+ 0,
250
+ limit,
251
+ [],
252
+ gitignorePatterns,
253
+ );
254
+ }
192
255
 
193
256
  const changedFiles = await getChangedFiles(projectRoot);
194
257
 
@@ -19,7 +19,7 @@ import {
19
19
  type TopupMethod,
20
20
  } from '../runtime/topup/manager.ts';
21
21
 
22
- const SETU_BASE_URL = process.env.SETU_BASE_URL || 'https://api.setu.nitish.sh';
22
+ const SETU_BASE_URL = process.env.SETU_BASE_URL || 'https://api.setu.ottocode.io';
23
23
 
24
24
  function getSetuBaseUrl(): string {
25
25
  return SETU_BASE_URL.endsWith('/')