@lovelybunch/api 1.0.68 → 1.0.69-alpha.0
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/lib/jobs/job-scheduler.js +86 -0
- package/dist/lib/terminal/terminal-manager.js +45 -1
- package/dist/routes/api/v1/context/architecture/route.js +37 -0
- package/dist/routes/api/v1/context/knowledge/[filename]/route.js +69 -0
- package/dist/routes/api/v1/context/knowledge/route.js +39 -0
- package/dist/routes/api/v1/context/project/route.js +37 -0
- package/dist/routes/api/v1/git/index.js +143 -2
- package/dist/routes/api/v1/identity/agent-card-service.d.ts +11 -0
- package/dist/routes/api/v1/identity/agent-card-service.js +64 -0
- package/dist/routes/api/v1/identity/index.d.ts +2 -0
- package/dist/routes/api/v1/identity/index.js +2 -0
- package/dist/routes/api/v1/identity/route.d.ts +3 -0
- package/dist/routes/api/v1/identity/route.js +57 -0
- package/dist/routes/api/v1/resources/[id]/route.js +23 -0
- package/dist/routes/api/v1/resources/route.js +24 -0
- package/dist/server-with-static.js +5 -0
- package/dist/server.js +18 -0
- package/dist/test-setup.d.ts +1 -0
- package/dist/test-setup.js +38 -0
- package/package.json +12 -6
- package/static/assets/index-DIVD0EVP.css +33 -0
- package/static/assets/index-VVQqAzvr.js +894 -0
- package/static/index.html +2 -2
- package/dist/lib/gait-path.d.ts.map +0 -1
- package/dist/lib/gait-path.js.map +0 -1
- package/dist/lib/git.d.ts.map +0 -1
- package/dist/lib/git.js.map +0 -1
- package/dist/lib/project-paths.d.ts.map +0 -1
- package/dist/lib/project-paths.js.map +0 -1
- package/dist/lib/storage/file-storage.d.ts.map +0 -1
- package/dist/lib/storage/file-storage.js.map +0 -1
- package/dist/lib/symlinks/symlink-manager.d.ts.map +0 -1
- package/dist/lib/symlinks/symlink-manager.js.map +0 -1
- package/dist/lib/symlinks/types.d.ts.map +0 -1
- package/dist/lib/symlinks/types.js.map +0 -1
- package/dist/lib/terminal/context-helper.d.ts.map +0 -1
- package/dist/lib/terminal/context-helper.js.map +0 -1
- package/dist/lib/terminal/global-manager.d.ts.map +0 -1
- package/dist/lib/terminal/global-manager.js.map +0 -1
- package/dist/lib/terminal/shell-utils.d.ts.map +0 -1
- package/dist/lib/terminal/shell-utils.js.map +0 -1
- package/dist/lib/terminal/terminal-manager.d.ts.map +0 -1
- package/dist/lib/terminal/terminal-manager.js.map +0 -1
- package/dist/lib/user-preferences.d.ts.map +0 -1
- package/dist/lib/user-preferences.js.map +0 -1
- package/dist/routes/api/v1/agents/[id]/index.d.ts.map +0 -1
- package/dist/routes/api/v1/agents/[id]/index.js.map +0 -1
- package/dist/routes/api/v1/agents/[id]/route.d.ts.map +0 -1
- package/dist/routes/api/v1/agents/[id]/route.js.map +0 -1
- package/dist/routes/api/v1/agents/index.d.ts.map +0 -1
- package/dist/routes/api/v1/agents/index.js.map +0 -1
- package/dist/routes/api/v1/agents/route.d.ts.map +0 -1
- package/dist/routes/api/v1/agents/route.js.map +0 -1
- package/dist/routes/api/v1/ai/index.d.ts.map +0 -1
- package/dist/routes/api/v1/ai/index.js.map +0 -1
- package/dist/routes/api/v1/ai/route.d.ts.map +0 -1
- package/dist/routes/api/v1/ai/route.js.map +0 -1
- package/dist/routes/api/v1/chats/[id]/index.d.ts.map +0 -1
- package/dist/routes/api/v1/chats/[id]/index.js.map +0 -1
- package/dist/routes/api/v1/chats/[id]/route.d.ts.map +0 -1
- package/dist/routes/api/v1/chats/[id]/route.js.map +0 -1
- package/dist/routes/api/v1/chats/index.d.ts.map +0 -1
- package/dist/routes/api/v1/chats/index.js.map +0 -1
- package/dist/routes/api/v1/chats/route.d.ts.map +0 -1
- package/dist/routes/api/v1/chats/route.js.map +0 -1
- package/dist/routes/api/v1/config/index.d.ts.map +0 -1
- package/dist/routes/api/v1/config/index.js.map +0 -1
- package/dist/routes/api/v1/config/route.d.ts.map +0 -1
- package/dist/routes/api/v1/config/route.js.map +0 -1
- package/dist/routes/api/v1/context/architecture/route.d.ts.map +0 -1
- package/dist/routes/api/v1/context/architecture/route.js.map +0 -1
- package/dist/routes/api/v1/context/index.d.ts.map +0 -1
- package/dist/routes/api/v1/context/index.js.map +0 -1
- package/dist/routes/api/v1/context/knowledge/[filename]/index.d.ts.map +0 -1
- package/dist/routes/api/v1/context/knowledge/[filename]/index.js.map +0 -1
- package/dist/routes/api/v1/context/knowledge/[filename]/route.d.ts.map +0 -1
- package/dist/routes/api/v1/context/knowledge/[filename]/route.js.map +0 -1
- package/dist/routes/api/v1/context/knowledge/index.d.ts.map +0 -1
- package/dist/routes/api/v1/context/knowledge/index.js.map +0 -1
- package/dist/routes/api/v1/context/knowledge/route.d.ts.map +0 -1
- package/dist/routes/api/v1/context/knowledge/route.js.map +0 -1
- package/dist/routes/api/v1/context/project/route.d.ts.map +0 -1
- package/dist/routes/api/v1/context/project/route.js.map +0 -1
- package/dist/routes/api/v1/events/events.test.d.ts +0 -4
- package/dist/routes/api/v1/events/events.test.js +0 -289
- package/dist/routes/api/v1/git/index.d.ts.map +0 -1
- package/dist/routes/api/v1/git/index.js.map +0 -1
- package/dist/routes/api/v1/init/index.d.ts +0 -1
- package/dist/routes/api/v1/init/index.js +0 -1
- package/dist/routes/api/v1/init/route.d.ts +0 -3
- package/dist/routes/api/v1/init/route.js +0 -129
- package/dist/routes/api/v1/mcp/index.d.ts.map +0 -1
- package/dist/routes/api/v1/mcp/index.js.map +0 -1
- package/dist/routes/api/v1/mcp/route.d.ts.map +0 -1
- package/dist/routes/api/v1/mcp/route.js.map +0 -1
- package/dist/routes/api/v1/onboard/index.d.ts +0 -3
- package/dist/routes/api/v1/onboard/index.js +0 -8
- package/dist/routes/api/v1/onboard/route.d.ts +0 -13
- package/dist/routes/api/v1/onboard/route.js +0 -311
- package/dist/routes/api/v1/onboarding/check/index.d.ts +0 -3
- package/dist/routes/api/v1/onboarding/check/index.js +0 -5
- package/dist/routes/api/v1/onboarding/check/route.d.ts +0 -12
- package/dist/routes/api/v1/onboarding/check/route.js +0 -24
- package/dist/routes/api/v1/onboarding/index.d.ts +0 -1
- package/dist/routes/api/v1/onboarding/index.js +0 -1
- package/dist/routes/api/v1/onboarding/route.d.ts +0 -3
- package/dist/routes/api/v1/onboarding/route.js +0 -158
- package/dist/routes/api/v1/proposals/[id]/route.d.ts.map +0 -1
- package/dist/routes/api/v1/proposals/[id]/route.js.map +0 -1
- package/dist/routes/api/v1/proposals/index.d.ts.map +0 -1
- package/dist/routes/api/v1/proposals/index.js.map +0 -1
- package/dist/routes/api/v1/proposals/route.d.ts.map +0 -1
- package/dist/routes/api/v1/proposals/route.js.map +0 -1
- package/dist/routes/api/v1/resources/[id]/index.d.ts.map +0 -1
- package/dist/routes/api/v1/resources/[id]/index.js.map +0 -1
- package/dist/routes/api/v1/resources/[id]/route.js.map +0 -1
- package/dist/routes/api/v1/resources/[id]/thumbnail/index.d.ts.map +0 -1
- package/dist/routes/api/v1/resources/[id]/thumbnail/index.js.map +0 -1
- package/dist/routes/api/v1/resources/[id]/thumbnail/route.js.map +0 -1
- package/dist/routes/api/v1/resources/index.d.ts.map +0 -1
- package/dist/routes/api/v1/resources/index.js.map +0 -1
- package/dist/routes/api/v1/resources/route.d.ts.map +0 -1
- package/dist/routes/api/v1/resources/route.js.map +0 -1
- package/dist/routes/api/v1/symlinks/index.d.ts.map +0 -1
- package/dist/routes/api/v1/symlinks/index.js.map +0 -1
- package/dist/routes/api/v1/symlinks/route.d.ts.map +0 -1
- package/dist/routes/api/v1/symlinks/route.js.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/create/index.d.ts.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/create/index.js.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/create/route.d.ts.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/create/route.js.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/destroy/index.d.ts.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/destroy/index.js.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/destroy/route.d.ts.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/destroy/route.js.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/resize/index.d.ts.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/resize/index.js.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/resize/route.d.ts.map +0 -1
- package/dist/routes/api/v1/terminal/[proposalId]/resize/route.js.map +0 -1
- package/dist/routes/api/v1/terminal/sessions/index.d.ts.map +0 -1
- package/dist/routes/api/v1/terminal/sessions/index.js.map +0 -1
- package/dist/routes/api/v1/terminal/sessions/route.d.ts.map +0 -1
- package/dist/routes/api/v1/terminal/sessions/route.js.map +0 -1
- package/dist/routes/api/v1/user/index.d.ts.map +0 -1
- package/dist/routes/api/v1/user/index.js.map +0 -1
- package/dist/routes/api/v1/user/settings/index.d.ts.map +0 -1
- package/dist/routes/api/v1/user/settings/index.js.map +0 -1
- package/dist/routes/api/v1/user/settings/route.d.ts.map +0 -1
- package/dist/routes/api/v1/user/settings/route.js.map +0 -1
- package/dist/server-with-static.d.ts.map +0 -1
- package/dist/server-with-static.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/static/assets/index-COf7Bc1u.js +0 -869
- package/static/assets/index-ChgXTZpc.css +0 -33
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
import { JobStore } from './job-store.js';
|
|
3
3
|
import { JobRunner } from './job-runner.js';
|
|
4
|
+
import { getLogger, JobKinds } from '@lovelybunch/core/logging';
|
|
4
5
|
const DAY_TO_INDEX = {
|
|
5
6
|
sunday: 0,
|
|
6
7
|
monday: 1,
|
|
@@ -38,6 +39,28 @@ export class JobScheduler {
|
|
|
38
39
|
}
|
|
39
40
|
const nextRun = this.calculateNextRun(job);
|
|
40
41
|
await this.updateJobMetadata(job, nextRun);
|
|
42
|
+
// Log job scheduling
|
|
43
|
+
try {
|
|
44
|
+
const logger = getLogger();
|
|
45
|
+
const interval = job.schedule.type === 'cron'
|
|
46
|
+
? `cron: ${job.schedule.expression}`
|
|
47
|
+
: `interval: ${job.schedule.hours}h on ${job.schedule.daysOfWeek.join(', ')}`;
|
|
48
|
+
logger.log({
|
|
49
|
+
kind: JobKinds.SCHEDULE,
|
|
50
|
+
actor: `system`,
|
|
51
|
+
subject: `job:${job.id}`,
|
|
52
|
+
tags: ["job", "schedule"],
|
|
53
|
+
payload: {
|
|
54
|
+
jobId: job.id,
|
|
55
|
+
agentName: job.model || 'claude',
|
|
56
|
+
interval,
|
|
57
|
+
nextRun: nextRun?.toISOString(),
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
catch (logError) {
|
|
62
|
+
console.error('Error logging job schedule:', logError);
|
|
63
|
+
}
|
|
41
64
|
if (!nextRun) {
|
|
42
65
|
console.warn(`No upcoming execution found for job ${job.id}`);
|
|
43
66
|
return;
|
|
@@ -109,6 +132,26 @@ export class JobScheduler {
|
|
|
109
132
|
job.metadata.lastRunAt = start;
|
|
110
133
|
job.metadata.updatedAt = new Date();
|
|
111
134
|
await this.store.saveJob(job);
|
|
135
|
+
// Log job run start
|
|
136
|
+
try {
|
|
137
|
+
const logger = getLogger();
|
|
138
|
+
logger.log({
|
|
139
|
+
kind: JobKinds.RUN_START,
|
|
140
|
+
actor: `agent:${job.model || 'claude'}`,
|
|
141
|
+
subject: `job:${job.id}`,
|
|
142
|
+
trace: runId,
|
|
143
|
+
tags: ["job", "run"],
|
|
144
|
+
payload: {
|
|
145
|
+
jobId: job.id,
|
|
146
|
+
runId,
|
|
147
|
+
agentName: job.model || 'claude',
|
|
148
|
+
trigger,
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (logError) {
|
|
153
|
+
console.error('Error logging job run start:', logError);
|
|
154
|
+
}
|
|
112
155
|
let outcome = null;
|
|
113
156
|
try {
|
|
114
157
|
const result = await this.runner.run(job, runId);
|
|
@@ -119,6 +162,28 @@ export class JobScheduler {
|
|
|
119
162
|
runRecord.error = result.error;
|
|
120
163
|
runRecord.cliCommand = result.cliCommand;
|
|
121
164
|
outcome = { ...runRecord };
|
|
165
|
+
// Log job run end
|
|
166
|
+
try {
|
|
167
|
+
const logger = getLogger();
|
|
168
|
+
const duration = runRecord.finishedAt.getTime() - start.getTime();
|
|
169
|
+
logger.log({
|
|
170
|
+
kind: JobKinds.RUN_END,
|
|
171
|
+
actor: `agent:${job.model || 'claude'}`,
|
|
172
|
+
subject: `job:${job.id}`,
|
|
173
|
+
trace: runId,
|
|
174
|
+
tags: ["job", "run"],
|
|
175
|
+
payload: {
|
|
176
|
+
jobId: job.id,
|
|
177
|
+
runId,
|
|
178
|
+
status: result.status,
|
|
179
|
+
duration,
|
|
180
|
+
logPath: result.outputPath,
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (logError) {
|
|
185
|
+
console.error('Error logging job run end:', logError);
|
|
186
|
+
}
|
|
122
187
|
}
|
|
123
188
|
catch (error) {
|
|
124
189
|
runRecord.status = 'failed';
|
|
@@ -126,6 +191,27 @@ export class JobScheduler {
|
|
|
126
191
|
runRecord.error = error?.message || 'Unknown error executing scheduled job';
|
|
127
192
|
runRecord.summary = runRecord.error;
|
|
128
193
|
outcome = { ...runRecord };
|
|
194
|
+
// Log job run error
|
|
195
|
+
try {
|
|
196
|
+
const logger = getLogger();
|
|
197
|
+
logger.log({
|
|
198
|
+
kind: JobKinds.RUN_ERROR,
|
|
199
|
+
actor: `agent:${job.model || 'claude'}`,
|
|
200
|
+
subject: `job:${job.id}`,
|
|
201
|
+
trace: runId,
|
|
202
|
+
level: "error",
|
|
203
|
+
tags: ["job", "run", "error"],
|
|
204
|
+
payload: {
|
|
205
|
+
jobId: job.id,
|
|
206
|
+
runId,
|
|
207
|
+
error: error?.message || 'Unknown error',
|
|
208
|
+
stack: error?.stack,
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
catch (logError) {
|
|
213
|
+
console.error('Error logging job run error:', logError);
|
|
214
|
+
}
|
|
129
215
|
}
|
|
130
216
|
try {
|
|
131
217
|
await this.store.saveJob(job);
|
|
@@ -4,6 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import { createInitScript } from './context-helper.js';
|
|
6
6
|
import { getShellPath, getShellArgs, prepareShellInit } from './shell-utils.js';
|
|
7
|
+
import { getLogger, AgentKinds } from '@lovelybunch/core/logging';
|
|
7
8
|
export class TerminalManager {
|
|
8
9
|
sessions = new Map();
|
|
9
10
|
cleanupInterval;
|
|
@@ -120,6 +121,27 @@ export class TerminalManager {
|
|
|
120
121
|
previewPingIntervals: new Map(),
|
|
121
122
|
};
|
|
122
123
|
this.sessions.set(sessionId, session);
|
|
124
|
+
// Log session start
|
|
125
|
+
try {
|
|
126
|
+
const logger = getLogger();
|
|
127
|
+
logger.log({
|
|
128
|
+
kind: AgentKinds.SESSION_START,
|
|
129
|
+
actor: `agent:terminal`,
|
|
130
|
+
subject: `session:${sessionId}`,
|
|
131
|
+
trace: sessionId,
|
|
132
|
+
tags: ["agent", "terminal", "session"],
|
|
133
|
+
payload: {
|
|
134
|
+
sessionId,
|
|
135
|
+
proposalId,
|
|
136
|
+
cwd: projectRoot,
|
|
137
|
+
shell: shellPath,
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// Logging errors should not break session creation
|
|
143
|
+
console.error('Error logging session start:', error);
|
|
144
|
+
}
|
|
123
145
|
// Set up PTY event handlers
|
|
124
146
|
ptyProcess.onData((data) => {
|
|
125
147
|
session.lastActivity = new Date();
|
|
@@ -155,10 +177,12 @@ export class TerminalManager {
|
|
|
155
177
|
this.enqueuePreviewBroadcast(session, data);
|
|
156
178
|
});
|
|
157
179
|
ptyProcess.onExit((e) => {
|
|
180
|
+
const endTime = new Date();
|
|
181
|
+
const duration = endTime.getTime() - session.createdAt.getTime();
|
|
158
182
|
// Log session end if logging is enabled
|
|
159
183
|
if (session.enableLogging && session.logFilePath) {
|
|
160
184
|
try {
|
|
161
|
-
const timestamp =
|
|
185
|
+
const timestamp = endTime.toISOString();
|
|
162
186
|
const logEntry = `\n[${timestamp}] SESSION ENDED: Exit code ${e.exitCode}\n`;
|
|
163
187
|
fs.appendFileSync(session.logFilePath, logEntry);
|
|
164
188
|
}
|
|
@@ -166,6 +190,26 @@ export class TerminalManager {
|
|
|
166
190
|
console.error('Error writing session end to log:', error);
|
|
167
191
|
}
|
|
168
192
|
}
|
|
193
|
+
// Log session end to activity log
|
|
194
|
+
try {
|
|
195
|
+
const logger = getLogger();
|
|
196
|
+
logger.log({
|
|
197
|
+
kind: AgentKinds.SESSION_END,
|
|
198
|
+
actor: `agent:terminal`,
|
|
199
|
+
subject: `session:${sessionId}`,
|
|
200
|
+
trace: sessionId,
|
|
201
|
+
tags: ["agent", "terminal", "session"],
|
|
202
|
+
payload: {
|
|
203
|
+
sessionId,
|
|
204
|
+
exitCode: e.exitCode,
|
|
205
|
+
duration,
|
|
206
|
+
signal: e.signal,
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
console.error('Error logging session end:', error);
|
|
212
|
+
}
|
|
169
213
|
// Clean up init script, temp directories, and ephemeral files
|
|
170
214
|
try {
|
|
171
215
|
if (fs.existsSync(initScriptPath)) {
|
|
@@ -3,6 +3,24 @@ import { promises as fs } from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import matter from 'gray-matter';
|
|
5
5
|
import { findGaitDirectory } from '../../../../../lib/gait-path.js';
|
|
6
|
+
import { getLogger, ContextKinds } from '@lovelybunch/core/logging';
|
|
7
|
+
import { requireAuth } from '../../../../../middleware/auth.js';
|
|
8
|
+
// Helper function to generate a simple summary from content
|
|
9
|
+
function generateSummary(content, maxLines = 3, maxChars = 200) {
|
|
10
|
+
// Remove markdown formatting for cleaner summary
|
|
11
|
+
const cleanContent = content
|
|
12
|
+
.replace(/^#+\s+/gm, '') // Remove headings
|
|
13
|
+
.replace(/[*_`]/g, '') // Remove bold, italic, code
|
|
14
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Convert links to text
|
|
15
|
+
.trim();
|
|
16
|
+
// Get first few lines
|
|
17
|
+
const lines = cleanContent.split('\n').filter(line => line.trim().length > 0);
|
|
18
|
+
const summary = lines.slice(0, maxLines).join(' ');
|
|
19
|
+
// Truncate if too long
|
|
20
|
+
return summary.length > maxChars
|
|
21
|
+
? summary.substring(0, maxChars) + '...'
|
|
22
|
+
: summary;
|
|
23
|
+
}
|
|
6
24
|
const app = new Hono();
|
|
7
25
|
async function getArchitecturePath() {
|
|
8
26
|
const gaitDir = await findGaitDirectory();
|
|
@@ -190,6 +208,25 @@ app.put('/', async (c) => {
|
|
|
190
208
|
const title = body.title ||
|
|
191
209
|
body.content.match(/^#\s+(.+)$/m)?.[1] ||
|
|
192
210
|
'Architecture Overview';
|
|
211
|
+
// Log architecture update event
|
|
212
|
+
try {
|
|
213
|
+
const session = await requireAuth(c);
|
|
214
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
215
|
+
const logger = getLogger();
|
|
216
|
+
logger.log({
|
|
217
|
+
kind: ContextKinds.ARCHITECTURE_UPDATE,
|
|
218
|
+
actor,
|
|
219
|
+
subject: `context:architecture.md`,
|
|
220
|
+
tags: ["context", "architecture"],
|
|
221
|
+
payload: {
|
|
222
|
+
title,
|
|
223
|
+
summary: generateSummary(body.content),
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
catch (logError) {
|
|
228
|
+
console.error('Error logging architecture update:', logError);
|
|
229
|
+
}
|
|
193
230
|
return c.json({
|
|
194
231
|
success: true,
|
|
195
232
|
document: {
|
|
@@ -2,6 +2,24 @@ import { Hono } from 'hono';
|
|
|
2
2
|
import { promises as fs } from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import matter from 'gray-matter';
|
|
5
|
+
import { getLogger, KnowledgeKinds } from '@lovelybunch/core/logging';
|
|
6
|
+
import { requireAuth } from '../../../../../../middleware/auth.js';
|
|
7
|
+
// Helper function to generate a simple summary from content
|
|
8
|
+
function generateSummary(content, maxLines = 3, maxChars = 200) {
|
|
9
|
+
// Remove markdown formatting for cleaner summary
|
|
10
|
+
const cleanContent = content
|
|
11
|
+
.replace(/^#+\s+/gm, '') // Remove headings
|
|
12
|
+
.replace(/[*_`]/g, '') // Remove bold, italic, code
|
|
13
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Convert links to text
|
|
14
|
+
.trim();
|
|
15
|
+
// Get first few lines
|
|
16
|
+
const lines = cleanContent.split('\n').filter(line => line.trim().length > 0);
|
|
17
|
+
const summary = lines.slice(0, maxLines).join(' ');
|
|
18
|
+
// Truncate if too long
|
|
19
|
+
return summary.length > maxChars
|
|
20
|
+
? summary.substring(0, maxChars) + '...'
|
|
21
|
+
: summary;
|
|
22
|
+
}
|
|
5
23
|
const app = new Hono();
|
|
6
24
|
function getKnowledgePath() {
|
|
7
25
|
let basePath;
|
|
@@ -128,6 +146,28 @@ app.put('/:filename', async (c) => {
|
|
|
128
146
|
const title = body.title ||
|
|
129
147
|
updatedContent.match(/^#\s+(.+)$/m)?.[1] ||
|
|
130
148
|
newFilename.replace('.md', '').replace(/[_-]/g, ' ').replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
|
|
149
|
+
// Log knowledge update event
|
|
150
|
+
try {
|
|
151
|
+
const session = await requireAuth(c);
|
|
152
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
153
|
+
const logger = getLogger();
|
|
154
|
+
logger.log({
|
|
155
|
+
kind: KnowledgeKinds.UPDATE,
|
|
156
|
+
actor,
|
|
157
|
+
subject: `knowledge:${newFilename}`,
|
|
158
|
+
tags: ["knowledge", ...(updatedMetadata.tags || [])],
|
|
159
|
+
payload: {
|
|
160
|
+
filename: newFilename,
|
|
161
|
+
oldFilename: actualFilename !== newFilename ? actualFilename : undefined,
|
|
162
|
+
title,
|
|
163
|
+
category: updatedMetadata.category,
|
|
164
|
+
summary: generateSummary(updatedContent),
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
catch (logError) {
|
|
169
|
+
console.error('Error logging knowledge update:', logError);
|
|
170
|
+
}
|
|
131
171
|
return c.json({
|
|
132
172
|
success: true,
|
|
133
173
|
document: {
|
|
@@ -160,8 +200,37 @@ app.delete('/:filename', async (c) => {
|
|
|
160
200
|
catch {
|
|
161
201
|
return c.json({ success: false, error: 'Knowledge document not found' }, 404);
|
|
162
202
|
}
|
|
203
|
+
// Read file before deletion for logging
|
|
204
|
+
let title;
|
|
205
|
+
try {
|
|
206
|
+
const fileContent = await fs.readFile(filePath, 'utf-8');
|
|
207
|
+
const { content } = matter(fileContent);
|
|
208
|
+
title = content.match(/^#\s+(.+)$/m)?.[1];
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// Ignore if we can't read the file
|
|
212
|
+
}
|
|
163
213
|
// Delete the file
|
|
164
214
|
await fs.unlink(filePath);
|
|
215
|
+
// Log knowledge deletion event
|
|
216
|
+
try {
|
|
217
|
+
const session = await requireAuth(c);
|
|
218
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
219
|
+
const logger = getLogger();
|
|
220
|
+
logger.log({
|
|
221
|
+
kind: KnowledgeKinds.DELETE,
|
|
222
|
+
actor,
|
|
223
|
+
subject: `knowledge:${actualFilename}`,
|
|
224
|
+
tags: ["knowledge"],
|
|
225
|
+
payload: {
|
|
226
|
+
filename: actualFilename,
|
|
227
|
+
title,
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
catch (logError) {
|
|
232
|
+
console.error('Error logging knowledge deletion:', logError);
|
|
233
|
+
}
|
|
165
234
|
return c.json({
|
|
166
235
|
success: true,
|
|
167
236
|
message: 'Knowledge document deleted successfully'
|
|
@@ -3,6 +3,24 @@ import { promises as fs } from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import matter from 'gray-matter';
|
|
5
5
|
import filenameRoute from './[filename]/index.js';
|
|
6
|
+
import { getLogger, KnowledgeKinds } from '@lovelybunch/core/logging';
|
|
7
|
+
import { requireAuth } from '../../../../../middleware/auth.js';
|
|
8
|
+
// Helper function to generate a simple summary from content
|
|
9
|
+
function generateSummary(content, maxLines = 3, maxChars = 200) {
|
|
10
|
+
// Remove markdown formatting for cleaner summary
|
|
11
|
+
const cleanContent = content
|
|
12
|
+
.replace(/^#+\s+/gm, '') // Remove headings
|
|
13
|
+
.replace(/[*_`]/g, '') // Remove bold, italic, code
|
|
14
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Convert links to text
|
|
15
|
+
.trim();
|
|
16
|
+
// Get first few lines
|
|
17
|
+
const lines = cleanContent.split('\n').filter(line => line.trim().length > 0);
|
|
18
|
+
const summary = lines.slice(0, maxLines).join(' ');
|
|
19
|
+
// Truncate if too long
|
|
20
|
+
return summary.length > maxChars
|
|
21
|
+
? summary.substring(0, maxChars) + '...'
|
|
22
|
+
: summary;
|
|
23
|
+
}
|
|
6
24
|
const app = new Hono();
|
|
7
25
|
function getKnowledgePath() {
|
|
8
26
|
let basePath;
|
|
@@ -111,6 +129,27 @@ app.post('/', async (c) => {
|
|
|
111
129
|
// Create the markdown content with frontmatter
|
|
112
130
|
const fileContent = matter.stringify(body.content, frontmatter);
|
|
113
131
|
await fs.writeFile(filePath, fileContent, 'utf-8');
|
|
132
|
+
// Log knowledge creation event
|
|
133
|
+
try {
|
|
134
|
+
const session = await requireAuth(c);
|
|
135
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
136
|
+
const logger = getLogger();
|
|
137
|
+
logger.log({
|
|
138
|
+
kind: KnowledgeKinds.CREATE,
|
|
139
|
+
actor,
|
|
140
|
+
subject: `knowledge:${filename}`,
|
|
141
|
+
tags: ["knowledge", ...(frontmatter.tags || [])],
|
|
142
|
+
payload: {
|
|
143
|
+
filename,
|
|
144
|
+
title: body.title,
|
|
145
|
+
category: frontmatter.category,
|
|
146
|
+
summary: generateSummary(body.content),
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
catch (logError) {
|
|
151
|
+
console.error('Error logging knowledge creation:', logError);
|
|
152
|
+
}
|
|
114
153
|
return c.json({
|
|
115
154
|
success: true,
|
|
116
155
|
document: {
|
|
@@ -3,6 +3,24 @@ import { promises as fs } from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import matter from 'gray-matter';
|
|
5
5
|
import { findGaitDirectory } from '../../../../../lib/gait-path.js';
|
|
6
|
+
import { getLogger, ContextKinds } from '@lovelybunch/core/logging';
|
|
7
|
+
import { requireAuth } from '../../../../../middleware/auth.js';
|
|
8
|
+
// Helper function to generate a simple summary from content
|
|
9
|
+
function generateSummary(content, maxLines = 3, maxChars = 200) {
|
|
10
|
+
// Remove markdown formatting for cleaner summary
|
|
11
|
+
const cleanContent = content
|
|
12
|
+
.replace(/^#+\s+/gm, '') // Remove headings
|
|
13
|
+
.replace(/[*_`]/g, '') // Remove bold, italic, code
|
|
14
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Convert links to text
|
|
15
|
+
.trim();
|
|
16
|
+
// Get first few lines
|
|
17
|
+
const lines = cleanContent.split('\n').filter(line => line.trim().length > 0);
|
|
18
|
+
const summary = lines.slice(0, maxLines).join(' ');
|
|
19
|
+
// Truncate if too long
|
|
20
|
+
return summary.length > maxChars
|
|
21
|
+
? summary.substring(0, maxChars) + '...'
|
|
22
|
+
: summary;
|
|
23
|
+
}
|
|
6
24
|
const app = new Hono();
|
|
7
25
|
async function getProjectPath() {
|
|
8
26
|
const gaitDir = await findGaitDirectory();
|
|
@@ -145,6 +163,25 @@ app.put('/', async (c) => {
|
|
|
145
163
|
const title = body.title ||
|
|
146
164
|
body.content.match(/^#\s+(.+)$/m)?.[1] ||
|
|
147
165
|
'Project Overview';
|
|
166
|
+
// Log project update event
|
|
167
|
+
try {
|
|
168
|
+
const session = await requireAuth(c);
|
|
169
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
170
|
+
const logger = getLogger();
|
|
171
|
+
logger.log({
|
|
172
|
+
kind: ContextKinds.PROJECT_UPDATE,
|
|
173
|
+
actor,
|
|
174
|
+
subject: `context:project.md`,
|
|
175
|
+
tags: ["context", "project"],
|
|
176
|
+
payload: {
|
|
177
|
+
title,
|
|
178
|
+
summary: generateSummary(body.content),
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch (logError) {
|
|
183
|
+
console.error('Error logging project update:', logError);
|
|
184
|
+
}
|
|
148
185
|
return c.json({
|
|
149
186
|
success: true,
|
|
150
187
|
document: {
|
|
@@ -5,6 +5,8 @@ import { saveGithubToken, clearGithubToken } from '../../../../lib/github-token.
|
|
|
5
5
|
import { createGithubAuthState, consumeGithubAuthState } from '../../../../lib/github-auth-state.js';
|
|
6
6
|
import { resolveCoconutId, resolveControlPlaneUrl } from '../../../../lib/coconut-context.js';
|
|
7
7
|
import { loadGitSettings, saveGitSettings, isGitAuthMode } from '../../../../lib/git-settings.js';
|
|
8
|
+
import { getLogger, CodeKinds } from '@lovelybunch/core/logging';
|
|
9
|
+
import { requireAuth } from '../../../../middleware/auth.js';
|
|
8
10
|
const app = new Hono();
|
|
9
11
|
// Settings
|
|
10
12
|
app.get('/settings', async (c) => {
|
|
@@ -201,6 +203,25 @@ app.post('/branches', async (c) => {
|
|
|
201
203
|
if (!name)
|
|
202
204
|
return c.json({ success: false, error: { message: 'name required' } }, 400);
|
|
203
205
|
await createBranch(name, from);
|
|
206
|
+
// Log branch creation
|
|
207
|
+
try {
|
|
208
|
+
const session = await requireAuth(c);
|
|
209
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
210
|
+
const logger = getLogger();
|
|
211
|
+
logger.log({
|
|
212
|
+
kind: CodeKinds.BRANCH_CREATE,
|
|
213
|
+
actor,
|
|
214
|
+
subject: `branch:${name}`,
|
|
215
|
+
tags: ["git", "branch"],
|
|
216
|
+
payload: {
|
|
217
|
+
branch: name,
|
|
218
|
+
baseBranch: from,
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch (logError) {
|
|
223
|
+
console.error('Error logging branch creation:', logError);
|
|
224
|
+
}
|
|
204
225
|
return c.json({ success: true, data: { created: name, from } });
|
|
205
226
|
}
|
|
206
227
|
catch (e) {
|
|
@@ -211,6 +232,24 @@ app.delete('/branches/:branch', async (c) => {
|
|
|
211
232
|
try {
|
|
212
233
|
const name = c.req.param('branch');
|
|
213
234
|
await deleteBranch(name);
|
|
235
|
+
// Log branch deletion
|
|
236
|
+
try {
|
|
237
|
+
const session = await requireAuth(c);
|
|
238
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
239
|
+
const logger = getLogger();
|
|
240
|
+
logger.log({
|
|
241
|
+
kind: CodeKinds.BRANCH_DELETE,
|
|
242
|
+
actor,
|
|
243
|
+
subject: `branch:${name}`,
|
|
244
|
+
tags: ["git", "branch"],
|
|
245
|
+
payload: {
|
|
246
|
+
branch: name,
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
catch (logError) {
|
|
251
|
+
console.error('Error logging branch deletion:', logError);
|
|
252
|
+
}
|
|
214
253
|
return c.json({ success: true, data: { deleted: name } });
|
|
215
254
|
}
|
|
216
255
|
catch (e) {
|
|
@@ -262,8 +301,40 @@ app.post('/commits', async (c) => {
|
|
|
262
301
|
await runGit(['add', '-A']);
|
|
263
302
|
}
|
|
264
303
|
await runGit(['commit', '-m', message]);
|
|
265
|
-
const { stdout } = await runGit(['rev-parse', 'HEAD']);
|
|
266
|
-
|
|
304
|
+
const { stdout: commitSha } = await runGit(['rev-parse', 'HEAD']);
|
|
305
|
+
const sha = commitSha.trim();
|
|
306
|
+
// Get current branch
|
|
307
|
+
const { stdout: branchOutput } = await runGit(['branch', '--show-current']);
|
|
308
|
+
const currentBranch = branchOutput.trim();
|
|
309
|
+
// Get commit stats
|
|
310
|
+
const { stdout: statsOutput } = await runGit(['show', '--stat', '--format=', sha]);
|
|
311
|
+
const filesChanged = (statsOutput.match(/\d+ files? changed/)?.[0] || '0 files changed').split(' ')[0];
|
|
312
|
+
const insertions = (statsOutput.match(/(\d+) insertions?\(\+\)/)?.[1] || '0');
|
|
313
|
+
const deletions = (statsOutput.match(/(\d+) deletions?\(-\)/)?.[1] || '0');
|
|
314
|
+
// Log commit event
|
|
315
|
+
try {
|
|
316
|
+
const session = await requireAuth(c);
|
|
317
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
318
|
+
const logger = getLogger();
|
|
319
|
+
logger.log({
|
|
320
|
+
kind: CodeKinds.COMMIT,
|
|
321
|
+
actor,
|
|
322
|
+
subject: `commit:${sha}`,
|
|
323
|
+
tags: ["git", "commit"],
|
|
324
|
+
payload: {
|
|
325
|
+
sha,
|
|
326
|
+
branch: currentBranch,
|
|
327
|
+
message,
|
|
328
|
+
filesChanged: parseInt(filesChanged),
|
|
329
|
+
insertions: parseInt(insertions),
|
|
330
|
+
deletions: parseInt(deletions),
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
catch (logError) {
|
|
335
|
+
console.error('Error logging commit:', logError);
|
|
336
|
+
}
|
|
337
|
+
return c.json({ success: true, data: { message, files: files ?? null, commitHash: sha } });
|
|
267
338
|
}
|
|
268
339
|
catch (e) {
|
|
269
340
|
return c.json({ success: false, error: { message: e.message } }, 500);
|
|
@@ -273,6 +344,31 @@ app.post('/commits', async (c) => {
|
|
|
273
344
|
app.post('/push', async (c) => {
|
|
274
345
|
try {
|
|
275
346
|
const result = await pushCurrent();
|
|
347
|
+
// Get current branch and remote info
|
|
348
|
+
try {
|
|
349
|
+
const { runGit } = await import('../../../../lib/git.js');
|
|
350
|
+
const { stdout: branchOutput } = await runGit(['branch', '--show-current']);
|
|
351
|
+
const currentBranch = branchOutput.trim();
|
|
352
|
+
const { stdout: remoteOutput } = await runGit(['config', '--get', `branch.${currentBranch}.remote`]);
|
|
353
|
+
const remoteName = remoteOutput.trim() || 'origin';
|
|
354
|
+
// Log push event
|
|
355
|
+
const session = await requireAuth(c);
|
|
356
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
357
|
+
const logger = getLogger();
|
|
358
|
+
logger.log({
|
|
359
|
+
kind: CodeKinds.PUSH,
|
|
360
|
+
actor,
|
|
361
|
+
subject: `branch:${currentBranch}`,
|
|
362
|
+
tags: ["git", "push"],
|
|
363
|
+
payload: {
|
|
364
|
+
branch: currentBranch,
|
|
365
|
+
remote: remoteName,
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
catch (logError) {
|
|
370
|
+
console.error('Error logging push:', logError);
|
|
371
|
+
}
|
|
276
372
|
return c.json({ success: true, data: { result } });
|
|
277
373
|
}
|
|
278
374
|
catch (e) {
|
|
@@ -288,6 +384,32 @@ app.post('/pull', async (c) => {
|
|
|
288
384
|
catch { }
|
|
289
385
|
const strategy = mode?.strategy === 'ff-only' || mode?.strategy === 'merge' ? mode.strategy : 'rebase';
|
|
290
386
|
const result = await pullCurrent(strategy);
|
|
387
|
+
// Get current branch and remote info
|
|
388
|
+
try {
|
|
389
|
+
const { runGit } = await import('../../../../lib/git.js');
|
|
390
|
+
const { stdout: branchOutput } = await runGit(['branch', '--show-current']);
|
|
391
|
+
const currentBranch = branchOutput.trim();
|
|
392
|
+
const { stdout: remoteOutput } = await runGit(['config', '--get', `branch.${currentBranch}.remote`]);
|
|
393
|
+
const remoteName = remoteOutput.trim() || 'origin';
|
|
394
|
+
// Log pull event
|
|
395
|
+
const session = await requireAuth(c);
|
|
396
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
397
|
+
const logger = getLogger();
|
|
398
|
+
logger.log({
|
|
399
|
+
kind: CodeKinds.PULL,
|
|
400
|
+
actor,
|
|
401
|
+
subject: `branch:${currentBranch}`,
|
|
402
|
+
tags: ["git", "pull"],
|
|
403
|
+
payload: {
|
|
404
|
+
branch: currentBranch,
|
|
405
|
+
remote: remoteName,
|
|
406
|
+
strategy,
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
catch (logError) {
|
|
411
|
+
console.error('Error logging pull:', logError);
|
|
412
|
+
}
|
|
291
413
|
return c.json({ success: true, data: { strategy, result } });
|
|
292
414
|
}
|
|
293
415
|
catch (e) {
|
|
@@ -312,6 +434,25 @@ app.post('/worktrees', async (c) => {
|
|
|
312
434
|
if (!branch)
|
|
313
435
|
return c.json({ success: false, error: { message: 'branch required' } }, 400);
|
|
314
436
|
const added = await addWorktree(branch, from);
|
|
437
|
+
// Log worktree creation
|
|
438
|
+
try {
|
|
439
|
+
const session = await requireAuth(c);
|
|
440
|
+
const actor = session ? `human:${session.email}` : "human:unknown";
|
|
441
|
+
const logger = getLogger();
|
|
442
|
+
logger.log({
|
|
443
|
+
kind: CodeKinds.WORKTREE_ADD,
|
|
444
|
+
actor,
|
|
445
|
+
subject: `worktree:${added.branch}`,
|
|
446
|
+
tags: ["git", "worktree"],
|
|
447
|
+
payload: {
|
|
448
|
+
path: added.path,
|
|
449
|
+
branch: added.branch,
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
catch (logError) {
|
|
454
|
+
console.error('Error logging worktree creation:', logError);
|
|
455
|
+
}
|
|
315
456
|
return c.json({ success: true, data: { added } });
|
|
316
457
|
}
|
|
317
458
|
catch (e) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const AGENT_CARD_FILENAME = "agent-card.json";
|
|
2
|
+
export declare const PUBLIC_AGENT_CARD_PATH = "/.well-known/agent-card.json";
|
|
3
|
+
export interface AgentCardDocument {
|
|
4
|
+
exists: boolean;
|
|
5
|
+
card: unknown | null;
|
|
6
|
+
publicPath: string;
|
|
7
|
+
storagePath: string;
|
|
8
|
+
updatedAt?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function readAgentCard(): Promise<AgentCardDocument>;
|
|
11
|
+
export declare function writeAgentCard(card: unknown): Promise<AgentCardDocument>;
|