@lovelybunch/api 1.0.69-alpha.9 → 1.0.70

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.
Files changed (44) hide show
  1. package/dist/lib/auth/auth-manager.d.ts +10 -2
  2. package/dist/lib/auth/auth-manager.js +16 -5
  3. package/dist/lib/env-injection.d.ts +6 -0
  4. package/dist/lib/env-injection.js +64 -0
  5. package/dist/lib/git.d.ts +1 -0
  6. package/dist/lib/git.js +39 -1
  7. package/dist/lib/jobs/job-runner.js +22 -3
  8. package/dist/lib/jobs/job-scheduler.js +12 -1
  9. package/dist/lib/jobs/job-store.d.ts +1 -0
  10. package/dist/lib/jobs/job-store.js +150 -28
  11. package/dist/lib/storage/file-storage.js +16 -7
  12. package/dist/lib/terminal/terminal-manager.js +3 -2
  13. package/dist/routes/api/v1/config/route.js +20 -0
  14. package/dist/routes/api/v1/context/knowledge/[filename]/route.js +18 -11
  15. package/dist/routes/api/v1/context/knowledge/route.js +5 -2
  16. package/dist/routes/api/v1/events/purge/route.d.ts +0 -2
  17. package/dist/routes/api/v1/events/purge/route.js +2 -14
  18. package/dist/routes/api/v1/events/route.d.ts +0 -2
  19. package/dist/routes/api/v1/events/route.js +2 -14
  20. package/dist/routes/api/v1/events/status/route.d.ts +0 -2
  21. package/dist/routes/api/v1/events/status/route.js +2 -14
  22. package/dist/routes/api/v1/events/stream/route.js +2 -14
  23. package/dist/routes/api/v1/git/index.js +66 -6
  24. package/dist/routes/api/v1/jobs/[id]/run/route.d.ts +2 -2
  25. package/dist/routes/api/v1/jobs/status/route.d.ts +1 -1
  26. package/dist/routes/api/v1/proposals/[id]/route.d.ts +8 -8
  27. package/dist/routes/api/v1/resources/[id]/route.js +11 -7
  28. package/dist/routes/api/v1/resources/generate/index.d.ts +3 -0
  29. package/dist/routes/api/v1/resources/generate/index.js +5 -0
  30. package/dist/routes/api/v1/resources/generate/route.d.ts +19 -0
  31. package/dist/routes/api/v1/resources/generate/route.js +257 -0
  32. package/dist/routes/api/v1/resources/index.js +2 -0
  33. package/dist/routes/api/v1/version/index.d.ts +3 -0
  34. package/dist/routes/api/v1/version/index.js +5 -0
  35. package/dist/routes/api/v1/version/route.d.ts +24 -0
  36. package/dist/routes/api/v1/version/route.js +51 -0
  37. package/dist/server-with-static.js +40 -23
  38. package/dist/server.js +40 -23
  39. package/package.json +5 -4
  40. package/static/assets/index-BmLW21zG.js +969 -0
  41. package/static/assets/index-CfRmV6nM.css +33 -0
  42. package/static/index.html +2 -2
  43. package/static/assets/index-BFXazLjO.js +0 -911
  44. package/static/assets/index-CHBcfq10.css +0 -33
@@ -0,0 +1,257 @@
1
+ import Replicate from 'replicate';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import { homedir } from 'os';
5
+ import { existsSync, readFileSync } from 'fs';
6
+ /**
7
+ * Get Replicate API token from global config or environment variable
8
+ */
9
+ function getReplicateApiToken() {
10
+ // First try global config
11
+ try {
12
+ const platform = process.platform;
13
+ let configDir;
14
+ if (platform === 'win32') {
15
+ configDir = path.join(process.env.APPDATA || homedir(), 'coconuts');
16
+ }
17
+ else if (platform === 'darwin') {
18
+ configDir = path.join(homedir(), 'Library', 'Application Support', 'coconuts');
19
+ }
20
+ else {
21
+ configDir = path.join(process.env.XDG_CONFIG_HOME || path.join(homedir(), '.config'), 'coconuts');
22
+ }
23
+ const configPath = path.join(configDir, 'config.json');
24
+ if (existsSync(configPath)) {
25
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
26
+ if (config.apiKeys?.replicate) {
27
+ return config.apiKeys.replicate;
28
+ }
29
+ }
30
+ }
31
+ catch (error) {
32
+ console.warn('Failed to load Replicate token from config:', error);
33
+ }
34
+ // Fallback to environment variable
35
+ return process.env.REPLICATE_API_TOKEN || null;
36
+ }
37
+ // Initialize Replicate client lazily to ensure token is loaded at request time
38
+ function getReplicateClient() {
39
+ const token = getReplicateApiToken();
40
+ if (!token) {
41
+ throw new Error('Replicate API token not configured');
42
+ }
43
+ return new Replicate({
44
+ auth: token,
45
+ });
46
+ }
47
+ function getResourcesPath() {
48
+ let basePath;
49
+ if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
50
+ basePath = process.env.GAIT_DEV_ROOT;
51
+ }
52
+ else if (process.env.GAIT_DATA_PATH) {
53
+ basePath = path.resolve(process.env.GAIT_DATA_PATH, '.nut');
54
+ }
55
+ else {
56
+ basePath = path.resolve(process.cwd(), '.nut');
57
+ }
58
+ return path.join(basePath, 'resources');
59
+ }
60
+ const RESOURCES_DIR = getResourcesPath();
61
+ const FILES_DIR = path.join(RESOURCES_DIR, 'files');
62
+ const METADATA_DIR = path.join(RESOURCES_DIR, 'metadata');
63
+ const THUMBNAILS_DIR = path.join(RESOURCES_DIR, 'thumbnails');
64
+ async function getResourceMetadata(id) {
65
+ try {
66
+ const metadataPath = path.join(METADATA_DIR, `${id}.json`);
67
+ const content = await fs.readFile(metadataPath, 'utf-8');
68
+ return JSON.parse(content);
69
+ }
70
+ catch (error) {
71
+ if (error.code === 'ENOENT')
72
+ return null;
73
+ throw error;
74
+ }
75
+ }
76
+ function extractResourceId(imageRef) {
77
+ if (typeof imageRef !== 'string')
78
+ return null;
79
+ // Allow direct resource IDs or URLs containing the ID
80
+ const directMatch = imageRef.match(/^(res-[A-Za-z0-9-]+)/);
81
+ if (directMatch && directMatch[1]) {
82
+ return directMatch[1];
83
+ }
84
+ const urlMatch = imageRef.match(/\/resources\/([^/?]+)/);
85
+ if (urlMatch && urlMatch[1]) {
86
+ return urlMatch[1];
87
+ }
88
+ return null;
89
+ }
90
+ // Map dimensions to aspect ratios
91
+ function getAspectRatio(dimensions) {
92
+ if (!dimensions)
93
+ return '1:1';
94
+ // Legacy support for previous values
95
+ const legacyMapping = {
96
+ '1x1': '1:1',
97
+ '3x2': '3:2',
98
+ '2x3': '2:3',
99
+ '6x9': '2:3',
100
+ };
101
+ if (legacyMapping[dimensions]) {
102
+ return legacyMapping[dimensions];
103
+ }
104
+ if (dimensions === 'match_input_image') {
105
+ return null;
106
+ }
107
+ return dimensions;
108
+ }
109
+ export async function POST(c) {
110
+ try {
111
+ // Check if Replicate API token is configured
112
+ const replicateToken = getReplicateApiToken();
113
+ if (!replicateToken) {
114
+ return c.json({
115
+ success: false,
116
+ error: {
117
+ code: 'MISSING_API_TOKEN',
118
+ message: 'Replicate API token not configured. Please add it in Settings → Integrations.'
119
+ }
120
+ }, 400);
121
+ }
122
+ const body = await c.req.json();
123
+ const { prompt, inspiration, dimensions, resolution, model, image_input } = body;
124
+ if (!prompt) {
125
+ return c.json({
126
+ success: false,
127
+ error: {
128
+ code: 'MISSING_PROMPT',
129
+ message: 'Prompt is required'
130
+ }
131
+ }, 400);
132
+ }
133
+ // Build the full prompt with inspiration if provided
134
+ let fullPrompt = prompt;
135
+ if (inspiration) {
136
+ fullPrompt = `${inspiration}: ${prompt}`;
137
+ }
138
+ // Get aspect ratio from dimensions
139
+ const mappedAspectRatio = getAspectRatio(dimensions || '4:3');
140
+ // Process image_input: upload files to Replicate and get URLs
141
+ // image_input can contain resource IDs or URLs (or be empty if none selected)
142
+ const imageInputArray = [];
143
+ const imageInputs = Array.isArray(image_input)
144
+ ? image_input
145
+ : typeof image_input === 'string' && image_input
146
+ ? [image_input]
147
+ : [];
148
+ for (const imageRef of imageInputs) {
149
+ try {
150
+ const resourceId = extractResourceId(imageRef);
151
+ if (!resourceId) {
152
+ console.warn(`Unable to resolve resource id from image reference: ${imageRef}`);
153
+ continue;
154
+ }
155
+ const resource = await getResourceMetadata(resourceId);
156
+ if (!resource) {
157
+ console.warn(`Resource metadata not found for id ${resourceId}`);
158
+ continue;
159
+ }
160
+ const candidatePaths = [];
161
+ if (resource.path) {
162
+ candidatePaths.push(path.join(FILES_DIR, resource.path));
163
+ }
164
+ if (resource.thumbnailPath) {
165
+ candidatePaths.push(path.join(THUMBNAILS_DIR, resource.thumbnailPath));
166
+ }
167
+ let filePath = null;
168
+ for (const candidate of candidatePaths) {
169
+ try {
170
+ await fs.access(candidate);
171
+ filePath = candidate;
172
+ break;
173
+ }
174
+ catch {
175
+ // Try next candidate
176
+ }
177
+ }
178
+ if (!filePath) {
179
+ console.warn(`No accessible file found for resource ${resourceId}`);
180
+ continue;
181
+ }
182
+ const fileBuffer = await fs.readFile(filePath);
183
+ const replicateClient = getReplicateClient();
184
+ const uploadedFile = await replicateClient.files.create(fileBuffer, {
185
+ resourceId,
186
+ originalName: resource.name,
187
+ });
188
+ const fileUrl = uploadedFile?.urls?.get;
189
+ if (fileUrl) {
190
+ imageInputArray.push(fileUrl);
191
+ }
192
+ else {
193
+ console.warn(`Replicate file upload did not return a URL for resource ${resourceId}`);
194
+ }
195
+ }
196
+ catch (error) {
197
+ console.error(`Error processing image input ${imageRef}:`, error);
198
+ }
199
+ }
200
+ const input = {
201
+ prompt: fullPrompt,
202
+ resolution: resolution || '1K',
203
+ output_format: 'png',
204
+ safety_filter_level: 'block_only_high'
205
+ };
206
+ if (mappedAspectRatio) {
207
+ input.aspect_ratio = mappedAspectRatio;
208
+ }
209
+ if (imageInputArray.length > 0) {
210
+ input.image_input = imageInputArray;
211
+ }
212
+ // Run the model (defaulting to nano-banana-pro)
213
+ const modelId = model === 'Nano Banana Pro' ? 'google/nano-banana-pro' : 'google/nano-banana-pro';
214
+ const replicateClient = getReplicateClient();
215
+ const output = await replicateClient.run(modelId, { input });
216
+ // Extract URL from output
217
+ // Replicate output can be: string URL, array of URLs, or FileOutput object with url() method
218
+ let imageUrl;
219
+ if (typeof output === 'string') {
220
+ imageUrl = output;
221
+ }
222
+ else if (Array.isArray(output) && output.length > 0) {
223
+ imageUrl = output[0];
224
+ }
225
+ else if (output && typeof output === 'object') {
226
+ // Check for url() method (FileOutput object)
227
+ if (typeof output.url === 'function') {
228
+ imageUrl = output.url();
229
+ }
230
+ else if ('url' in output) {
231
+ imageUrl = output.url;
232
+ }
233
+ else {
234
+ throw new Error('Unexpected output format from Replicate');
235
+ }
236
+ }
237
+ else {
238
+ throw new Error('Unexpected output format from Replicate');
239
+ }
240
+ return c.json({
241
+ success: true,
242
+ data: {
243
+ imageUrl
244
+ }
245
+ });
246
+ }
247
+ catch (error) {
248
+ console.error('Error generating image:', error);
249
+ return c.json({
250
+ success: false,
251
+ error: {
252
+ code: 'GENERATION_ERROR',
253
+ message: error.message || 'Failed to generate image'
254
+ }
255
+ }, 500);
256
+ }
257
+ }
@@ -1,6 +1,8 @@
1
1
  import { Hono } from 'hono';
2
2
  import { GET, POST } from './route.js';
3
+ import generate from './generate/index.js';
3
4
  const app = new Hono();
4
5
  app.get('/', GET);
5
6
  app.post('/', POST);
7
+ app.route('/generate', generate);
6
8
  export default app;
@@ -0,0 +1,3 @@
1
+ import { Hono } from 'hono';
2
+ declare const version: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default version;
@@ -0,0 +1,5 @@
1
+ import { Hono } from 'hono';
2
+ import { GET } from './route.js';
3
+ const version = new Hono();
4
+ version.get('/', GET);
5
+ export default version;
@@ -0,0 +1,24 @@
1
+ import { Context } from 'hono';
2
+ /**
3
+ * GET /api/v1/version
4
+ * Returns the current version of Coconut
5
+ * Requires authentication
6
+ */
7
+ export declare function GET(c: Context): Promise<(Response & import("hono").TypedResponse<{
8
+ success: true;
9
+ data: {
10
+ version: any;
11
+ };
12
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
13
+ success: false;
14
+ error: {
15
+ code: string;
16
+ message: any;
17
+ };
18
+ }, 401, "json">) | (Response & import("hono").TypedResponse<{
19
+ success: false;
20
+ error: {
21
+ code: string;
22
+ message: any;
23
+ };
24
+ }, 500, "json">)>;
@@ -0,0 +1,51 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { requireAuth } from '../../../../middleware/auth.js';
5
+ /**
6
+ * GET /api/v1/version
7
+ * Returns the current version of Coconut
8
+ * Requires authentication
9
+ */
10
+ export async function GET(c) {
11
+ try {
12
+ // Require authentication
13
+ requireAuth(c);
14
+ // Read the API package's own package.json
15
+ // This works both in dev (from source) and production (from dist)
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+ // Navigate up from dist/routes/api/v1/version to the package root
19
+ // In dev: src/routes/api/v1/version -> ../../../../..
20
+ // In prod: dist/routes/api/v1/version -> ../../../../..
21
+ let packageJsonPath = path.resolve(__dirname, '..', '..', '..', '..', '..', 'package.json');
22
+ // Read package.json
23
+ const content = await fs.readFile(packageJsonPath, 'utf-8');
24
+ const packageJson = JSON.parse(content);
25
+ return c.json({
26
+ success: true,
27
+ data: {
28
+ version: packageJson.version
29
+ }
30
+ });
31
+ }
32
+ catch (error) {
33
+ console.error('Error reading version:', error);
34
+ if (error.message === 'Authentication required' || error.message === 'Admin access required') {
35
+ return c.json({
36
+ success: false,
37
+ error: {
38
+ code: 'UNAUTHORIZED',
39
+ message: error.message
40
+ }
41
+ }, 401);
42
+ }
43
+ return c.json({
44
+ success: false,
45
+ error: {
46
+ code: 'VERSION_ERROR',
47
+ message: error.message || 'Failed to read version'
48
+ }
49
+ }, 500);
50
+ }
51
+ }
@@ -11,6 +11,7 @@ import { getGlobalTerminalManager } from './lib/terminal/global-manager.js';
11
11
  import { getGlobalJobScheduler } from './lib/jobs/global-job-scheduler.js';
12
12
  import { fileURLToPath } from 'url';
13
13
  import { getLogger } from '@lovelybunch/core/logging';
14
+ import { getLogsDir } from '@lovelybunch/core';
14
15
  const __filename = fileURLToPath(import.meta.url);
15
16
  const __dirname = path.dirname(__filename);
16
17
  // Load environment variables from .env file in project root
@@ -30,11 +31,15 @@ function findNutDirectorySync() {
30
31
  }
31
32
  return null;
32
33
  }
33
- // Initialize logger with config from .nut/config.json
34
+ // Initialize logger with config from .nut/config.json or use OS app data directory
34
35
  // This must happen BEFORE importing route handlers (they call getLogger at module level)
35
36
  console.log('🔍 Initializing activity logging...');
36
37
  try {
37
38
  const nutDir = findNutDirectorySync();
39
+ let logsDir = getLogsDir(); // Default to OS app data directory
40
+ let coconutId = 'unknown.coconut';
41
+ let rotateBytes = 128 * 1024 * 1024;
42
+ let loggingEnabled = true; // Default to enabled
38
43
  if (nutDir) {
39
44
  const projectRoot = path.dirname(nutDir);
40
45
  const configPath = path.join(nutDir, 'config.json');
@@ -42,32 +47,42 @@ try {
42
47
  console.log(' Config path:', configPath);
43
48
  const configData = fs.readFileSync(configPath, 'utf-8');
44
49
  const config = JSON.parse(configData);
45
- if (config.logging?.enabled) {
46
- const logsDir = path.resolve(projectRoot, config.logging?.location || '.nut/logs');
47
- const logger = getLogger({
48
- coconutId: config.coconut?.id || 'unknown.coconut',
49
- logsDir: logsDir,
50
- rotateBytes: config.logging?.rotateBytes || 128 * 1024 * 1024
51
- });
52
- console.log('📝 Activity logging ENABLED');
53
- console.log(' Logs directory:', logsDir);
54
- console.log(' Coconut ID:', config.coconut?.id || 'unknown.coconut');
55
- // Test log immediately
56
- logger.log({
57
- kind: 'system.startup',
58
- actor: 'system',
59
- subject: 'server',
60
- tags: ['system', 'startup'],
61
- payload: { message: 'Server starting with logging enabled' }
62
- });
63
- console.log(' ✓ Test event logged');
50
+ // Check if logging is explicitly disabled in config
51
+ if (config.logging?.enabled === false) {
52
+ loggingEnabled = false;
64
53
  }
65
- else {
66
- console.log('📝 Activity logging disabled in config');
54
+ // Allow config to override logs location (relative paths resolve from project root)
55
+ if (config.logging?.location) {
56
+ logsDir = path.resolve(projectRoot, config.logging.location);
67
57
  }
58
+ if (config.coconut?.id) {
59
+ coconutId = config.coconut.id;
60
+ }
61
+ if (config.logging?.rotateBytes) {
62
+ rotateBytes = config.logging.rotateBytes;
63
+ }
64
+ }
65
+ if (loggingEnabled) {
66
+ const logger = getLogger({
67
+ coconutId,
68
+ logsDir,
69
+ rotateBytes
70
+ });
71
+ console.log('📝 Activity logging ENABLED');
72
+ console.log(' Logs directory:', logsDir);
73
+ console.log(' Coconut ID:', coconutId);
74
+ // Test log immediately
75
+ logger.log({
76
+ kind: 'system.startup',
77
+ actor: 'system',
78
+ subject: 'server',
79
+ tags: ['system', 'startup'],
80
+ payload: { message: 'Server starting with logging enabled' }
81
+ });
82
+ console.log(' ✓ Test event logged');
68
83
  }
69
84
  else {
70
- console.log(' .nut directory not found');
85
+ console.log('📝 Activity logging disabled in config');
71
86
  }
72
87
  }
73
88
  catch (error) {
@@ -163,6 +178,7 @@ import git from './routes/api/v1/git/index.js';
163
178
  import mcp from './routes/api/v1/mcp/index.js';
164
179
  import jobs from './routes/api/v1/jobs/index.js';
165
180
  import events from './routes/api/v1/events/index.js';
181
+ import version from './routes/api/v1/version/index.js';
166
182
  // Register API routes FIRST
167
183
  console.log('🔗 Registering API routes...');
168
184
  app.route('/api/v1/auth', auth);
@@ -189,6 +205,7 @@ app.route('/api/v1/git', git);
189
205
  app.route('/api/v1/mcp', mcp);
190
206
  app.route('/api/v1/jobs', jobs);
191
207
  app.route('/api/v1/events', events);
208
+ app.route('/api/v1/version', version);
192
209
  console.log('✅ API routes registered');
193
210
  app.get(PUBLIC_AGENT_CARD_PATH, authMiddleware, async (c) => {
194
211
  try {
package/dist/server.js CHANGED
@@ -10,6 +10,7 @@ import path from 'path';
10
10
  import { fileURLToPath } from 'url';
11
11
  import fs from 'fs';
12
12
  import { getLogger } from '@lovelybunch/core/logging';
13
+ import { getLogsDir } from '@lovelybunch/core';
13
14
  const __filename = fileURLToPath(import.meta.url);
14
15
  const __dirname = path.dirname(__filename);
15
16
  // Load environment variables from .env file in project root
@@ -29,11 +30,15 @@ function findNutDirectorySync() {
29
30
  }
30
31
  return null;
31
32
  }
32
- // Initialize logger with config from .nut/config.json
33
+ // Initialize logger with config from .nut/config.json or use OS app data directory
33
34
  // This must happen BEFORE importing route handlers (they call getLogger at module level)
34
35
  console.log('🔍 Initializing activity logging...');
35
36
  try {
36
37
  const nutDir = findNutDirectorySync();
38
+ let logsDir = getLogsDir(); // Default to OS app data directory
39
+ let coconutId = 'unknown.coconut';
40
+ let rotateBytes = 128 * 1024 * 1024;
41
+ let loggingEnabled = true; // Default to enabled
37
42
  if (nutDir) {
38
43
  const projectRoot = path.dirname(nutDir);
39
44
  const configPath = path.join(nutDir, 'config.json');
@@ -41,32 +46,42 @@ try {
41
46
  console.log(' Config path:', configPath);
42
47
  const configData = fs.readFileSync(configPath, 'utf-8');
43
48
  const config = JSON.parse(configData);
44
- if (config.logging?.enabled) {
45
- const logsDir = path.resolve(projectRoot, config.logging?.location || '.nut/logs');
46
- const logger = getLogger({
47
- coconutId: config.coconut?.id || 'unknown.coconut',
48
- logsDir: logsDir,
49
- rotateBytes: config.logging?.rotateBytes || 128 * 1024 * 1024
50
- });
51
- console.log('📝 Activity logging ENABLED');
52
- console.log(' Logs directory:', logsDir);
53
- console.log(' Coconut ID:', config.coconut?.id || 'unknown.coconut');
54
- // Test log immediately
55
- logger.log({
56
- kind: 'system.startup',
57
- actor: 'system',
58
- subject: 'server',
59
- tags: ['system', 'startup'],
60
- payload: { message: 'Server starting with logging enabled' }
61
- });
62
- console.log(' ✓ Test event logged');
49
+ // Check if logging is explicitly disabled in config
50
+ if (config.logging?.enabled === false) {
51
+ loggingEnabled = false;
63
52
  }
64
- else {
65
- console.log('📝 Activity logging disabled in config');
53
+ // Allow config to override logs location (relative paths resolve from project root)
54
+ if (config.logging?.location) {
55
+ logsDir = path.resolve(projectRoot, config.logging.location);
66
56
  }
57
+ if (config.coconut?.id) {
58
+ coconutId = config.coconut.id;
59
+ }
60
+ if (config.logging?.rotateBytes) {
61
+ rotateBytes = config.logging.rotateBytes;
62
+ }
63
+ }
64
+ if (loggingEnabled) {
65
+ const logger = getLogger({
66
+ coconutId,
67
+ logsDir,
68
+ rotateBytes
69
+ });
70
+ console.log('📝 Activity logging ENABLED');
71
+ console.log(' Logs directory:', logsDir);
72
+ console.log(' Coconut ID:', coconutId);
73
+ // Test log immediately
74
+ logger.log({
75
+ kind: 'system.startup',
76
+ actor: 'system',
77
+ subject: 'server',
78
+ tags: ['system', 'startup'],
79
+ payload: { message: 'Server starting with logging enabled' }
80
+ });
81
+ console.log(' ✓ Test event logged');
67
82
  }
68
83
  else {
69
- console.log(' .nut directory not found');
84
+ console.log('📝 Activity logging disabled in config');
70
85
  }
71
86
  }
72
87
  catch (error) {
@@ -163,6 +178,7 @@ import mcp from './routes/api/v1/mcp/index.js';
163
178
  import symlinks from './routes/api/v1/symlinks/index.js';
164
179
  import jobs from './routes/api/v1/jobs/index.js';
165
180
  import events from './routes/api/v1/events/index.js';
181
+ import version from './routes/api/v1/version/index.js';
166
182
  // Register API routes
167
183
  app.route('/api/v1/auth', auth);
168
184
  app.route('/api/v1/auth-settings', authSettings);
@@ -189,6 +205,7 @@ app.route('/api/v1/mcp', mcp);
189
205
  app.route('/api/v1/symlinks', symlinks);
190
206
  app.route('/api/v1/jobs', jobs);
191
207
  app.route('/api/v1/events', events);
208
+ app.route('/api/v1/version', version);
192
209
  app.get(PUBLIC_AGENT_CARD_PATH, authMiddleware, async (c) => {
193
210
  try {
194
211
  const document = await readAgentCard();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lovelybunch/api",
3
- "version": "1.0.69-alpha.9",
3
+ "version": "1.0.70",
4
4
  "type": "module",
5
5
  "main": "dist/server-with-static.js",
6
6
  "exports": {
@@ -36,9 +36,9 @@
36
36
  "dependencies": {
37
37
  "@hono/node-server": "^1.13.7",
38
38
  "@hono/node-ws": "^1.0.6",
39
- "@lovelybunch/core": "^1.0.69-alpha.9",
40
- "@lovelybunch/mcp": "^1.0.69-alpha.9",
41
- "@lovelybunch/types": "^1.0.69-alpha.9",
39
+ "@lovelybunch/core": "^1.0.70",
40
+ "@lovelybunch/mcp": "^1.0.70",
41
+ "@lovelybunch/types": "^1.0.70",
42
42
  "arctic": "^1.9.2",
43
43
  "bcrypt": "^5.1.1",
44
44
  "cookie": "^0.6.0",
@@ -48,6 +48,7 @@
48
48
  "hono": "^4.9.5",
49
49
  "jsonwebtoken": "^9.0.2",
50
50
  "node-pty": "^1.0.0",
51
+ "replicate": "^0.34.1",
51
52
  "ws": "^8.18.0"
52
53
  },
53
54
  "devDependencies": {