@ottocode/server 0.1.198 → 0.1.200

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.198",
3
+ "version": "0.1.200",
4
4
  "description": "HTTP API server for ottocode",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -49,8 +49,8 @@
49
49
  "typecheck": "tsc --noEmit"
50
50
  },
51
51
  "dependencies": {
52
- "@ottocode/sdk": "0.1.198",
53
- "@ottocode/database": "0.1.198",
52
+ "@ottocode/sdk": "0.1.200",
53
+ "@ottocode/database": "0.1.200",
54
54
  "drizzle-orm": "^0.44.5",
55
55
  "hono": "^4.9.9",
56
56
  "zod": "^4.1.8"
@@ -9,6 +9,7 @@ export type OttoEventType =
9
9
  | 'setu.topup.method_selected'
10
10
  | 'setu.topup.cancelled'
11
11
  | 'setu.fiat.checkout_created'
12
+ | 'setu.balance.updated'
12
13
  | 'session.created'
13
14
  | 'session.updated'
14
15
  | 'message.created'
@@ -61,6 +61,7 @@ export function registerModelsRoutes(app: Hono) {
61
61
  toolCall: m.toolCall,
62
62
  reasoningText: m.reasoningText,
63
63
  vision: m.modalities?.input?.includes('image') ?? false,
64
+ attachment: m.attachment ?? false,
64
65
  })),
65
66
  default: getDefault(
66
67
  embeddedConfig?.model,
@@ -124,6 +125,7 @@ export function registerModelsRoutes(app: Hono) {
124
125
  toolCall: m.toolCall,
125
126
  reasoningText: m.reasoningText,
126
127
  vision: m.modalities?.input?.includes('image') ?? false,
128
+ attachment: m.attachment ?? false,
127
129
  })),
128
130
  };
129
131
  }
@@ -279,4 +279,71 @@ export function registerFilesRoutes(app: Hono) {
279
279
  return c.json({ error: serializeError(err) }, 500);
280
280
  }
281
281
  });
282
+
283
+ app.get('/v1/files/tree', async (c) => {
284
+ try {
285
+ const projectRoot = c.req.query('project') || process.cwd();
286
+ const dirPath = c.req.query('path') || '.';
287
+ const targetDir = join(projectRoot, dirPath);
288
+
289
+ const gitignorePatterns = await parseGitignore(projectRoot);
290
+ const entries = await readdir(targetDir, { withFileTypes: true });
291
+
292
+ const items: Array<{
293
+ name: string;
294
+ path: string;
295
+ type: 'file' | 'directory';
296
+ }> = [];
297
+
298
+ for (const entry of entries) {
299
+ if (entry.name.startsWith('.') && entry.name !== '.otto') continue;
300
+ const relPath = relative(projectRoot, join(targetDir, entry.name));
301
+
302
+ if (entry.isDirectory()) {
303
+ if (shouldExcludeDir(entry.name)) continue;
304
+ if (matchesGitignorePattern(relPath, gitignorePatterns)) continue;
305
+ items.push({ name: entry.name, path: relPath, type: 'directory' });
306
+ } else if (entry.isFile()) {
307
+ if (shouldExcludeFile(entry.name)) continue;
308
+ if (matchesGitignorePattern(relPath, gitignorePatterns)) continue;
309
+ items.push({ name: entry.name, path: relPath, type: 'file' });
310
+ }
311
+ }
312
+
313
+ items.sort((a, b) => {
314
+ if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
315
+ return a.name.localeCompare(b.name);
316
+ });
317
+
318
+ return c.json({ items, path: dirPath });
319
+ } catch (err) {
320
+ logger.error('Files tree route error:', err);
321
+ return c.json({ error: serializeError(err) }, 500);
322
+ }
323
+ });
324
+
325
+ app.get('/v1/files/read', async (c) => {
326
+ try {
327
+ const projectRoot = c.req.query('project') || process.cwd();
328
+ const filePath = c.req.query('path');
329
+
330
+ if (!filePath) {
331
+ return c.json({ error: 'Missing required query parameter: path' }, 400);
332
+ }
333
+
334
+ const absPath = join(projectRoot, filePath);
335
+ if (!absPath.startsWith(projectRoot)) {
336
+ return c.json({ error: 'Path traversal not allowed' }, 403);
337
+ }
338
+
339
+ const content = await readFile(absPath, 'utf-8');
340
+ const extension = filePath.split('.').pop()?.toLowerCase() ?? '';
341
+ const lineCount = content.split('\n').length;
342
+
343
+ return c.json({ content, path: filePath, extension, lineCount });
344
+ } catch (err) {
345
+ logger.error('Files read route error:', err);
346
+ return c.json({ error: serializeError(err) }, 500);
347
+ }
348
+ });
282
349
  }
@@ -73,6 +73,12 @@ export function registerDiffRoute(app: Hono) {
73
73
  const diffArgs = query.staged
74
74
  ? ['diff', '--cached', '--', query.file]
75
75
  : ['diff', '--', query.file];
76
+
77
+ const fullFile = c.req.query('fullFile') === 'true';
78
+ if (fullFile) {
79
+ diffArgs.splice(1, 0, '-U99999');
80
+ }
81
+
76
82
  const numstatArgs = query.staged
77
83
  ? ['diff', '--cached', '--numstat', '--', query.file]
78
84
  : ['diff', '--numstat', '--', query.file];
@@ -116,6 +122,7 @@ export function registerDiffRoute(app: Hono) {
116
122
  file: query.file,
117
123
  absPath,
118
124
  diff: diffText,
125
+ fullFile,
119
126
  isNewFile: false,
120
127
  isBinary: binary,
121
128
  insertions,
@@ -82,6 +82,13 @@ export async function resolveSetuModel(
82
82
  payload: { error },
83
83
  });
84
84
  },
85
+ onBalanceUpdate: (update) => {
86
+ publish({
87
+ type: 'setu.balance.updated',
88
+ sessionId,
89
+ payload: update,
90
+ });
91
+ },
85
92
  onPaymentApproval: async (info): Promise<TopupMethod | 'cancel'> => {
86
93
  const suggestedTopupUsd = Math.max(
87
94
  MIN_TOPUP_USD,