@lovelybunch/api 1.0.66 → 1.0.68
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/dist/routes/api/v1/events/events.test.d.ts +4 -0
- package/dist/routes/api/v1/events/events.test.js +289 -0
- package/dist/routes/api/v1/events/index.d.ts +7 -0
- package/dist/routes/api/v1/events/index.js +19 -0
- package/dist/routes/api/v1/events/purge/route.d.ts +19 -0
- package/dist/routes/api/v1/events/purge/route.js +62 -0
- package/dist/routes/api/v1/events/route.d.ts +30 -0
- package/dist/routes/api/v1/events/route.js +109 -0
- package/dist/routes/api/v1/events/status/route.d.ts +20 -0
- package/dist/routes/api/v1/events/status/route.js +53 -0
- package/dist/routes/api/v1/events/stream/route.d.ts +9 -0
- package/dist/routes/api/v1/events/stream/route.js +132 -0
- package/dist/routes/api/v1/init/index.d.ts +1 -0
- package/dist/routes/api/v1/init/index.js +1 -0
- package/dist/routes/api/v1/init/route.d.ts +3 -0
- package/dist/routes/api/v1/init/route.js +129 -0
- package/dist/routes/api/v1/onboard/index.d.ts +3 -0
- package/dist/routes/api/v1/onboard/index.js +8 -0
- package/dist/routes/api/v1/onboard/route.d.ts +13 -0
- package/dist/routes/api/v1/onboard/route.js +311 -0
- package/dist/routes/api/v1/onboarding/check/index.d.ts +3 -0
- package/dist/routes/api/v1/onboarding/check/index.js +5 -0
- package/dist/routes/api/v1/onboarding/check/route.d.ts +12 -0
- package/dist/routes/api/v1/onboarding/check/route.js +24 -0
- package/dist/routes/api/v1/onboarding/index.d.ts +1 -0
- package/dist/routes/api/v1/onboarding/index.js +1 -0
- package/dist/routes/api/v1/onboarding/route.d.ts +3 -0
- package/dist/routes/api/v1/onboarding/route.js +158 -0
- package/dist/routes/api/v1/proposals/[id]/route.js +57 -0
- package/dist/routes/api/v1/proposals/route.js +18 -0
- package/dist/server-with-static.js +62 -0
- package/dist/server.js +63 -0
- package/package.json +4 -4
- package/static/assets/{index-DuLX7Zvh.js → index-COf7Bc1u.js} +33 -33
- package/static/index.html +1 -1
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { promises as fs } from 'fs';
|
|
4
|
+
import { execFile } from 'child_process';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { MarkdownStorageAdapter } from '@lovelybunch/core';
|
|
7
|
+
import { getGlobalJobScheduler } from '../../../../lib/jobs/global-job-scheduler.js';
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const onboarding = new Hono();
|
|
10
|
+
function isOnboardingActive() {
|
|
11
|
+
return process.env.COCONUT_ONBOARDING_MODE === '1';
|
|
12
|
+
}
|
|
13
|
+
function getProjectRoot() {
|
|
14
|
+
if (process.env.GAIT_DATA_PATH) {
|
|
15
|
+
return path.resolve(process.env.GAIT_DATA_PATH);
|
|
16
|
+
}
|
|
17
|
+
return process.cwd();
|
|
18
|
+
}
|
|
19
|
+
async function nutDirectoryExists(root) {
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(path.join(root, '.nut'));
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function isGitRepository(root) {
|
|
29
|
+
try {
|
|
30
|
+
await execFileAsync('git', ['rev-parse', '--is-inside-work-tree'], { cwd: root });
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function sanitizeString(value) {
|
|
38
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
39
|
+
}
|
|
40
|
+
async function ensureSchedulerStarted() {
|
|
41
|
+
try {
|
|
42
|
+
const scheduler = getGlobalJobScheduler();
|
|
43
|
+
await scheduler.initialize();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('Failed to initialize job scheduler after onboarding:', error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
onboarding.get('/status', async (c) => {
|
|
50
|
+
const root = getProjectRoot();
|
|
51
|
+
const coconutInitialized = await nutDirectoryExists(root);
|
|
52
|
+
const gitInitialized = await isGitRepository(root);
|
|
53
|
+
return c.json({
|
|
54
|
+
success: true,
|
|
55
|
+
data: {
|
|
56
|
+
onboardingActive: isOnboardingActive() && !coconutInitialized,
|
|
57
|
+
coconutInitialized,
|
|
58
|
+
gitInitialized
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
onboarding.post('/start-empty', async (c) => {
|
|
63
|
+
const root = getProjectRoot();
|
|
64
|
+
const body = await c.req.json().catch(() => ({}));
|
|
65
|
+
const name = sanitizeString(body?.name);
|
|
66
|
+
const description = sanitizeString(body?.description);
|
|
67
|
+
if (!name) {
|
|
68
|
+
return c.json({ success: false, error: 'Repository name is required' }, 400);
|
|
69
|
+
}
|
|
70
|
+
if (!description) {
|
|
71
|
+
return c.json({ success: false, error: 'Repository description is required' }, 400);
|
|
72
|
+
}
|
|
73
|
+
if (await nutDirectoryExists(root)) {
|
|
74
|
+
return c.json({ success: false, error: 'Coconut is already initialized in this directory' }, 409);
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const storage = new MarkdownStorageAdapter(path.join(root, '.nut'));
|
|
78
|
+
await storage.init();
|
|
79
|
+
const config = (await storage.loadConfig()) ?? {
|
|
80
|
+
version: '1.0.0',
|
|
81
|
+
repository: {
|
|
82
|
+
name,
|
|
83
|
+
description
|
|
84
|
+
},
|
|
85
|
+
policies: {
|
|
86
|
+
requireApproval: true,
|
|
87
|
+
minApprovers: 1,
|
|
88
|
+
allowSelfApproval: false,
|
|
89
|
+
autoMerge: false
|
|
90
|
+
},
|
|
91
|
+
storage: {
|
|
92
|
+
type: 'file',
|
|
93
|
+
path: '.nut'
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
config.repository.name = name;
|
|
97
|
+
config.repository.description = description;
|
|
98
|
+
await storage.saveConfig(config);
|
|
99
|
+
const gitAlreadyInitialized = await isGitRepository(root);
|
|
100
|
+
if (!gitAlreadyInitialized) {
|
|
101
|
+
await execFileAsync('git', ['init'], { cwd: root });
|
|
102
|
+
}
|
|
103
|
+
await ensureSchedulerStarted();
|
|
104
|
+
return c.json({
|
|
105
|
+
success: true,
|
|
106
|
+
data: {
|
|
107
|
+
coconutInitialized: true,
|
|
108
|
+
gitInitialized: true
|
|
109
|
+
}
|
|
110
|
+
}, 201);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.error('Failed to initialize Coconut during onboarding:', error);
|
|
114
|
+
return c.json({
|
|
115
|
+
success: false,
|
|
116
|
+
error: error?.message || 'Failed to initialize Coconut'
|
|
117
|
+
}, 500);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
onboarding.post('/clone', async (c) => {
|
|
121
|
+
const root = getProjectRoot();
|
|
122
|
+
const body = await c.req.json().catch(() => ({}));
|
|
123
|
+
const repositoryUrl = sanitizeString(body?.url);
|
|
124
|
+
if (!repositoryUrl) {
|
|
125
|
+
return c.json({ success: false, error: 'Repository URL is required' }, 400);
|
|
126
|
+
}
|
|
127
|
+
if (await nutDirectoryExists(root)) {
|
|
128
|
+
return c.json({ success: false, error: 'Coconut is already initialized in this directory' }, 409);
|
|
129
|
+
}
|
|
130
|
+
if (await isGitRepository(root)) {
|
|
131
|
+
return c.json({ success: false, error: 'Git repository already exists in this directory' }, 409);
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
await execFileAsync('git', ['clone', repositoryUrl, '.'], { cwd: root });
|
|
135
|
+
const coconutInitialized = await nutDirectoryExists(root);
|
|
136
|
+
const gitInitialized = await isGitRepository(root);
|
|
137
|
+
if (coconutInitialized) {
|
|
138
|
+
await ensureSchedulerStarted();
|
|
139
|
+
}
|
|
140
|
+
return c.json({
|
|
141
|
+
success: true,
|
|
142
|
+
data: {
|
|
143
|
+
coconutInitialized,
|
|
144
|
+
gitInitialized
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('Failed to clone repository during onboarding:', error);
|
|
150
|
+
const stderr = error?.stderr?.toString?.() || '';
|
|
151
|
+
const message = stderr.trim() || error?.message || 'Failed to clone repository';
|
|
152
|
+
return c.json({
|
|
153
|
+
success: false,
|
|
154
|
+
error: message
|
|
155
|
+
}, 400);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
export default onboarding;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { FileStorageAdapter } from '../../../../../lib/storage/file-storage.js';
|
|
2
|
+
import { getLogger } from '@lovelybunch/core/logging';
|
|
2
3
|
const storage = new FileStorageAdapter();
|
|
4
|
+
// Logger is lazily initialized inside handlers to use server config
|
|
3
5
|
export async function GET(c) {
|
|
4
6
|
try {
|
|
5
7
|
const id = c.req.param('id');
|
|
@@ -52,6 +54,43 @@ export async function PATCH(c) {
|
|
|
52
54
|
await storage.updateCP(id, finalUpdates);
|
|
53
55
|
// Fetch the updated proposal
|
|
54
56
|
const updatedProposal = await storage.getCP(id);
|
|
57
|
+
// Determine what changed
|
|
58
|
+
const changedFields = Object.keys(updates);
|
|
59
|
+
const oldStatus = existing.status;
|
|
60
|
+
const newStatus = updatedProposal?.status || existing.status;
|
|
61
|
+
// Log proposal.update event
|
|
62
|
+
const logger = getLogger();
|
|
63
|
+
logger.log({
|
|
64
|
+
kind: 'proposal.update',
|
|
65
|
+
actor: updatedProposal?.author.type === 'agent'
|
|
66
|
+
? `agent:${updatedProposal.author.name}`
|
|
67
|
+
: `human:${updatedProposal?.author.email || 'unknown'}`,
|
|
68
|
+
subject: `proposal:${id}`,
|
|
69
|
+
tags: ['proposal'],
|
|
70
|
+
payload: {
|
|
71
|
+
id,
|
|
72
|
+
changes: changedFields,
|
|
73
|
+
oldStatus,
|
|
74
|
+
newStatus
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// If status changed, also log a status.change event
|
|
78
|
+
if (oldStatus !== newStatus) {
|
|
79
|
+
logger.log({
|
|
80
|
+
kind: 'proposal.status.change',
|
|
81
|
+
actor: updatedProposal?.author.type === 'agent'
|
|
82
|
+
? `agent:${updatedProposal.author.name}`
|
|
83
|
+
: `human:${updatedProposal?.author.email || 'unknown'}`,
|
|
84
|
+
subject: `proposal:${id}`,
|
|
85
|
+
tags: ['proposal', 'status'],
|
|
86
|
+
payload: {
|
|
87
|
+
id,
|
|
88
|
+
from: oldStatus,
|
|
89
|
+
to: newStatus,
|
|
90
|
+
reason: null
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
55
94
|
return c.json({
|
|
56
95
|
success: true,
|
|
57
96
|
data: updatedProposal
|
|
@@ -80,7 +119,25 @@ export async function PATCH(c) {
|
|
|
80
119
|
export async function DELETE(c) {
|
|
81
120
|
try {
|
|
82
121
|
const id = c.req.param('id');
|
|
122
|
+
// Get proposal info before deleting
|
|
123
|
+
const proposal = await storage.getCP(id);
|
|
83
124
|
await storage.deleteCP(id);
|
|
125
|
+
// Log the deletion event
|
|
126
|
+
if (proposal) {
|
|
127
|
+
const logger = getLogger();
|
|
128
|
+
logger.log({
|
|
129
|
+
kind: 'proposal.delete',
|
|
130
|
+
actor: proposal.author.type === 'agent'
|
|
131
|
+
? `agent:${proposal.author.name}`
|
|
132
|
+
: `human:${proposal.author.email || 'unknown'}`,
|
|
133
|
+
subject: `proposal:${id}`,
|
|
134
|
+
tags: ['proposal'],
|
|
135
|
+
payload: {
|
|
136
|
+
id,
|
|
137
|
+
intent: proposal.intent
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
84
141
|
return c.json({
|
|
85
142
|
success: true,
|
|
86
143
|
message: `Proposal ${id} deleted successfully`
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { FileStorageAdapter } from '../../../../lib/storage/file-storage.js';
|
|
2
2
|
import { getAuthorInfo } from '../../../../lib/user-preferences.js';
|
|
3
3
|
import Fuse from 'fuse.js';
|
|
4
|
+
import { getLogger } from '@lovelybunch/core/logging';
|
|
4
5
|
const storage = new FileStorageAdapter();
|
|
6
|
+
// Logger is lazily initialized inside handlers to use server config
|
|
5
7
|
export async function GET(c) {
|
|
6
8
|
try {
|
|
7
9
|
const url = new URL(c.req.url);
|
|
@@ -85,6 +87,22 @@ export async function POST(c) {
|
|
|
85
87
|
productSpecRef: body.productSpecRef
|
|
86
88
|
};
|
|
87
89
|
await storage.createCP(proposal);
|
|
90
|
+
// Log the proposal creation event
|
|
91
|
+
const logger = getLogger();
|
|
92
|
+
logger.log({
|
|
93
|
+
kind: 'proposal.create',
|
|
94
|
+
actor: proposal.author.type === 'agent'
|
|
95
|
+
? `agent:${proposal.author.name}`
|
|
96
|
+
: `human:${proposal.author.email}`,
|
|
97
|
+
subject: `proposal:${proposal.id}`,
|
|
98
|
+
tags: ['proposal'],
|
|
99
|
+
payload: {
|
|
100
|
+
id: proposal.id,
|
|
101
|
+
intent: proposal.intent,
|
|
102
|
+
priority: proposal.metadata.priority,
|
|
103
|
+
author: proposal.author
|
|
104
|
+
}
|
|
105
|
+
});
|
|
88
106
|
return c.json({
|
|
89
107
|
success: true,
|
|
90
108
|
data: proposal
|
|
@@ -10,10 +10,70 @@ import fs from 'fs';
|
|
|
10
10
|
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
|
+
import { getLogger } from '@lovelybunch/core/logging';
|
|
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
|
|
16
17
|
dotenvConfig({ path: path.resolve(__dirname, '../../../.env') });
|
|
18
|
+
// Helper: Find .nut directory by traversing up from cwd
|
|
19
|
+
// Only returns a .nut directory if it contains config.json
|
|
20
|
+
function findNutDirectorySync() {
|
|
21
|
+
let currentDir = process.cwd();
|
|
22
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
23
|
+
const nutPath = path.join(currentDir, '.nut');
|
|
24
|
+
const configPath = path.join(nutPath, 'config.json');
|
|
25
|
+
// Check if .nut exists AND has config.json
|
|
26
|
+
if (fs.existsSync(nutPath) && fs.existsSync(configPath)) {
|
|
27
|
+
return nutPath;
|
|
28
|
+
}
|
|
29
|
+
currentDir = path.dirname(currentDir);
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
// Initialize logger with config from .nut/config.json
|
|
34
|
+
// This must happen BEFORE importing route handlers (they call getLogger at module level)
|
|
35
|
+
console.log('🔍 Initializing activity logging...');
|
|
36
|
+
try {
|
|
37
|
+
const nutDir = findNutDirectorySync();
|
|
38
|
+
if (nutDir) {
|
|
39
|
+
const projectRoot = path.dirname(nutDir);
|
|
40
|
+
const configPath = path.join(nutDir, 'config.json');
|
|
41
|
+
console.log(' Project root:', projectRoot);
|
|
42
|
+
console.log(' Config path:', configPath);
|
|
43
|
+
const configData = fs.readFileSync(configPath, 'utf-8');
|
|
44
|
+
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');
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log('📝 Activity logging disabled in config');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log(' ✗ .nut directory not found');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
console.error('⚠️ Failed to initialize logger:');
|
|
75
|
+
console.error(error);
|
|
76
|
+
}
|
|
17
77
|
const app = new Hono();
|
|
18
78
|
// Enable CORS for development
|
|
19
79
|
app.use('/api/*', cors({
|
|
@@ -101,6 +161,7 @@ import agentsById from './routes/api/v1/agents/[id]/index.js';
|
|
|
101
161
|
import git from './routes/api/v1/git/index.js';
|
|
102
162
|
import mcp from './routes/api/v1/mcp/index.js';
|
|
103
163
|
import jobs from './routes/api/v1/jobs/index.js';
|
|
164
|
+
import events from './routes/api/v1/events/index.js';
|
|
104
165
|
// Register API routes FIRST
|
|
105
166
|
console.log('🔗 Registering API routes...');
|
|
106
167
|
app.route('/api/v1/auth', auth);
|
|
@@ -125,6 +186,7 @@ app.route('/api/v1/agents/:id', agentsById);
|
|
|
125
186
|
app.route('/api/v1/git', git);
|
|
126
187
|
app.route('/api/v1/mcp', mcp);
|
|
127
188
|
app.route('/api/v1/jobs', jobs);
|
|
189
|
+
app.route('/api/v1/events', events);
|
|
128
190
|
console.log('✅ API routes registered');
|
|
129
191
|
// Initialize background services
|
|
130
192
|
getGlobalJobScheduler();
|
package/dist/server.js
CHANGED
|
@@ -8,10 +8,71 @@ import { getGlobalTerminalManager } from './lib/terminal/global-manager.js';
|
|
|
8
8
|
import { getGlobalJobScheduler } from './lib/jobs/global-job-scheduler.js';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import { getLogger } from '@lovelybunch/core/logging';
|
|
11
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
14
|
const __dirname = path.dirname(__filename);
|
|
13
15
|
// Load environment variables from .env file in project root
|
|
14
16
|
dotenvConfig({ path: path.resolve(__dirname, '../../../.env') });
|
|
17
|
+
// Helper: Find .nut directory by traversing up from cwd
|
|
18
|
+
// Only returns a .nut directory if it contains config.json
|
|
19
|
+
function findNutDirectorySync() {
|
|
20
|
+
let currentDir = process.cwd();
|
|
21
|
+
while (currentDir !== path.parse(currentDir).root) {
|
|
22
|
+
const nutPath = path.join(currentDir, '.nut');
|
|
23
|
+
const configPath = path.join(nutPath, 'config.json');
|
|
24
|
+
// Check if .nut exists AND has config.json
|
|
25
|
+
if (fs.existsSync(nutPath) && fs.existsSync(configPath)) {
|
|
26
|
+
return nutPath;
|
|
27
|
+
}
|
|
28
|
+
currentDir = path.dirname(currentDir);
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
// Initialize logger with config from .nut/config.json
|
|
33
|
+
// This must happen BEFORE importing route handlers (they call getLogger at module level)
|
|
34
|
+
console.log('🔍 Initializing activity logging...');
|
|
35
|
+
try {
|
|
36
|
+
const nutDir = findNutDirectorySync();
|
|
37
|
+
if (nutDir) {
|
|
38
|
+
const projectRoot = path.dirname(nutDir);
|
|
39
|
+
const configPath = path.join(nutDir, 'config.json');
|
|
40
|
+
console.log(' Project root:', projectRoot);
|
|
41
|
+
console.log(' Config path:', configPath);
|
|
42
|
+
const configData = fs.readFileSync(configPath, 'utf-8');
|
|
43
|
+
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');
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log('📝 Activity logging disabled in config');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(' ✗ .nut directory not found');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error('⚠️ Failed to initialize logger:');
|
|
74
|
+
console.error(error);
|
|
75
|
+
}
|
|
15
76
|
const app = new Hono();
|
|
16
77
|
// Enable CORS for frontend
|
|
17
78
|
app.use('*', cors({
|
|
@@ -100,6 +161,7 @@ import git from './routes/api/v1/git/index.js';
|
|
|
100
161
|
import mcp from './routes/api/v1/mcp/index.js';
|
|
101
162
|
import symlinks from './routes/api/v1/symlinks/index.js';
|
|
102
163
|
import jobs from './routes/api/v1/jobs/index.js';
|
|
164
|
+
import events from './routes/api/v1/events/index.js';
|
|
103
165
|
// Register API routes
|
|
104
166
|
app.route('/api/v1/auth', auth);
|
|
105
167
|
app.route('/api/v1/auth-settings', authSettings);
|
|
@@ -124,6 +186,7 @@ app.route('/api/v1/git', git);
|
|
|
124
186
|
app.route('/api/v1/mcp', mcp);
|
|
125
187
|
app.route('/api/v1/symlinks', symlinks);
|
|
126
188
|
app.route('/api/v1/jobs', jobs);
|
|
189
|
+
app.route('/api/v1/events', events);
|
|
127
190
|
// Health check endpoint
|
|
128
191
|
app.get('/health', (c) => {
|
|
129
192
|
return c.json({ status: 'ok', timestamp: new Date().toISOString() });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovelybunch/api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.68",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/server-with-static.js",
|
|
6
6
|
"exports": {
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@hono/node-server": "^1.13.7",
|
|
34
34
|
"@hono/node-ws": "^1.0.6",
|
|
35
|
-
"@lovelybunch/core": "^1.0.
|
|
36
|
-
"@lovelybunch/mcp": "^1.0.
|
|
37
|
-
"@lovelybunch/types": "^1.0.
|
|
35
|
+
"@lovelybunch/core": "^1.0.68",
|
|
36
|
+
"@lovelybunch/mcp": "^1.0.68",
|
|
37
|
+
"@lovelybunch/types": "^1.0.68",
|
|
38
38
|
"arctic": "^1.9.2",
|
|
39
39
|
"bcrypt": "^5.1.1",
|
|
40
40
|
"cookie": "^0.6.0",
|