@lovelybunch/api 1.0.68 → 1.0.69-alpha.1

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 (155) hide show
  1. package/dist/lib/jobs/job-scheduler.js +86 -0
  2. package/dist/lib/terminal/terminal-manager.js +45 -1
  3. package/dist/routes/api/v1/context/architecture/route.js +37 -0
  4. package/dist/routes/api/v1/context/knowledge/[filename]/route.js +69 -0
  5. package/dist/routes/api/v1/context/knowledge/route.js +39 -0
  6. package/dist/routes/api/v1/context/project/route.js +37 -0
  7. package/dist/routes/api/v1/git/index.js +143 -2
  8. package/dist/routes/api/v1/identity/agent-card-service.d.ts +11 -0
  9. package/dist/routes/api/v1/identity/agent-card-service.js +64 -0
  10. package/dist/routes/api/v1/identity/index.d.ts +2 -0
  11. package/dist/routes/api/v1/identity/index.js +2 -0
  12. package/dist/routes/api/v1/identity/route.d.ts +3 -0
  13. package/dist/routes/api/v1/identity/route.js +57 -0
  14. package/dist/routes/api/v1/resources/[id]/route.js +23 -0
  15. package/dist/routes/api/v1/resources/route.js +24 -0
  16. package/dist/server-with-static.js +23 -0
  17. package/dist/server.js +18 -0
  18. package/dist/test-setup.d.ts +1 -0
  19. package/dist/test-setup.js +38 -0
  20. package/package.json +12 -6
  21. package/static/assets/index-DIVD0EVP.css +33 -0
  22. package/static/assets/index-gdnIvn_s.js +894 -0
  23. package/static/index.html +2 -2
  24. package/dist/lib/gait-path.d.ts.map +0 -1
  25. package/dist/lib/gait-path.js.map +0 -1
  26. package/dist/lib/git.d.ts.map +0 -1
  27. package/dist/lib/git.js.map +0 -1
  28. package/dist/lib/project-paths.d.ts.map +0 -1
  29. package/dist/lib/project-paths.js.map +0 -1
  30. package/dist/lib/storage/file-storage.d.ts.map +0 -1
  31. package/dist/lib/storage/file-storage.js.map +0 -1
  32. package/dist/lib/symlinks/symlink-manager.d.ts.map +0 -1
  33. package/dist/lib/symlinks/symlink-manager.js.map +0 -1
  34. package/dist/lib/symlinks/types.d.ts.map +0 -1
  35. package/dist/lib/symlinks/types.js.map +0 -1
  36. package/dist/lib/terminal/context-helper.d.ts.map +0 -1
  37. package/dist/lib/terminal/context-helper.js.map +0 -1
  38. package/dist/lib/terminal/global-manager.d.ts.map +0 -1
  39. package/dist/lib/terminal/global-manager.js.map +0 -1
  40. package/dist/lib/terminal/shell-utils.d.ts.map +0 -1
  41. package/dist/lib/terminal/shell-utils.js.map +0 -1
  42. package/dist/lib/terminal/terminal-manager.d.ts.map +0 -1
  43. package/dist/lib/terminal/terminal-manager.js.map +0 -1
  44. package/dist/lib/user-preferences.d.ts.map +0 -1
  45. package/dist/lib/user-preferences.js.map +0 -1
  46. package/dist/routes/api/v1/agents/[id]/index.d.ts.map +0 -1
  47. package/dist/routes/api/v1/agents/[id]/index.js.map +0 -1
  48. package/dist/routes/api/v1/agents/[id]/route.d.ts.map +0 -1
  49. package/dist/routes/api/v1/agents/[id]/route.js.map +0 -1
  50. package/dist/routes/api/v1/agents/index.d.ts.map +0 -1
  51. package/dist/routes/api/v1/agents/index.js.map +0 -1
  52. package/dist/routes/api/v1/agents/route.d.ts.map +0 -1
  53. package/dist/routes/api/v1/agents/route.js.map +0 -1
  54. package/dist/routes/api/v1/ai/index.d.ts.map +0 -1
  55. package/dist/routes/api/v1/ai/index.js.map +0 -1
  56. package/dist/routes/api/v1/ai/route.d.ts.map +0 -1
  57. package/dist/routes/api/v1/ai/route.js.map +0 -1
  58. package/dist/routes/api/v1/chats/[id]/index.d.ts.map +0 -1
  59. package/dist/routes/api/v1/chats/[id]/index.js.map +0 -1
  60. package/dist/routes/api/v1/chats/[id]/route.d.ts.map +0 -1
  61. package/dist/routes/api/v1/chats/[id]/route.js.map +0 -1
  62. package/dist/routes/api/v1/chats/index.d.ts.map +0 -1
  63. package/dist/routes/api/v1/chats/index.js.map +0 -1
  64. package/dist/routes/api/v1/chats/route.d.ts.map +0 -1
  65. package/dist/routes/api/v1/chats/route.js.map +0 -1
  66. package/dist/routes/api/v1/config/index.d.ts.map +0 -1
  67. package/dist/routes/api/v1/config/index.js.map +0 -1
  68. package/dist/routes/api/v1/config/route.d.ts.map +0 -1
  69. package/dist/routes/api/v1/config/route.js.map +0 -1
  70. package/dist/routes/api/v1/context/architecture/route.d.ts.map +0 -1
  71. package/dist/routes/api/v1/context/architecture/route.js.map +0 -1
  72. package/dist/routes/api/v1/context/index.d.ts.map +0 -1
  73. package/dist/routes/api/v1/context/index.js.map +0 -1
  74. package/dist/routes/api/v1/context/knowledge/[filename]/index.d.ts.map +0 -1
  75. package/dist/routes/api/v1/context/knowledge/[filename]/index.js.map +0 -1
  76. package/dist/routes/api/v1/context/knowledge/[filename]/route.d.ts.map +0 -1
  77. package/dist/routes/api/v1/context/knowledge/[filename]/route.js.map +0 -1
  78. package/dist/routes/api/v1/context/knowledge/index.d.ts.map +0 -1
  79. package/dist/routes/api/v1/context/knowledge/index.js.map +0 -1
  80. package/dist/routes/api/v1/context/knowledge/route.d.ts.map +0 -1
  81. package/dist/routes/api/v1/context/knowledge/route.js.map +0 -1
  82. package/dist/routes/api/v1/context/project/route.d.ts.map +0 -1
  83. package/dist/routes/api/v1/context/project/route.js.map +0 -1
  84. package/dist/routes/api/v1/events/events.test.d.ts +0 -4
  85. package/dist/routes/api/v1/events/events.test.js +0 -289
  86. package/dist/routes/api/v1/git/index.d.ts.map +0 -1
  87. package/dist/routes/api/v1/git/index.js.map +0 -1
  88. package/dist/routes/api/v1/init/index.d.ts +0 -1
  89. package/dist/routes/api/v1/init/index.js +0 -1
  90. package/dist/routes/api/v1/init/route.d.ts +0 -3
  91. package/dist/routes/api/v1/init/route.js +0 -129
  92. package/dist/routes/api/v1/mcp/index.d.ts.map +0 -1
  93. package/dist/routes/api/v1/mcp/index.js.map +0 -1
  94. package/dist/routes/api/v1/mcp/route.d.ts.map +0 -1
  95. package/dist/routes/api/v1/mcp/route.js.map +0 -1
  96. package/dist/routes/api/v1/onboard/index.d.ts +0 -3
  97. package/dist/routes/api/v1/onboard/index.js +0 -8
  98. package/dist/routes/api/v1/onboard/route.d.ts +0 -13
  99. package/dist/routes/api/v1/onboard/route.js +0 -311
  100. package/dist/routes/api/v1/onboarding/check/index.d.ts +0 -3
  101. package/dist/routes/api/v1/onboarding/check/index.js +0 -5
  102. package/dist/routes/api/v1/onboarding/check/route.d.ts +0 -12
  103. package/dist/routes/api/v1/onboarding/check/route.js +0 -24
  104. package/dist/routes/api/v1/onboarding/index.d.ts +0 -1
  105. package/dist/routes/api/v1/onboarding/index.js +0 -1
  106. package/dist/routes/api/v1/onboarding/route.d.ts +0 -3
  107. package/dist/routes/api/v1/onboarding/route.js +0 -158
  108. package/dist/routes/api/v1/proposals/[id]/route.d.ts.map +0 -1
  109. package/dist/routes/api/v1/proposals/[id]/route.js.map +0 -1
  110. package/dist/routes/api/v1/proposals/index.d.ts.map +0 -1
  111. package/dist/routes/api/v1/proposals/index.js.map +0 -1
  112. package/dist/routes/api/v1/proposals/route.d.ts.map +0 -1
  113. package/dist/routes/api/v1/proposals/route.js.map +0 -1
  114. package/dist/routes/api/v1/resources/[id]/index.d.ts.map +0 -1
  115. package/dist/routes/api/v1/resources/[id]/index.js.map +0 -1
  116. package/dist/routes/api/v1/resources/[id]/route.js.map +0 -1
  117. package/dist/routes/api/v1/resources/[id]/thumbnail/index.d.ts.map +0 -1
  118. package/dist/routes/api/v1/resources/[id]/thumbnail/index.js.map +0 -1
  119. package/dist/routes/api/v1/resources/[id]/thumbnail/route.js.map +0 -1
  120. package/dist/routes/api/v1/resources/index.d.ts.map +0 -1
  121. package/dist/routes/api/v1/resources/index.js.map +0 -1
  122. package/dist/routes/api/v1/resources/route.d.ts.map +0 -1
  123. package/dist/routes/api/v1/resources/route.js.map +0 -1
  124. package/dist/routes/api/v1/symlinks/index.d.ts.map +0 -1
  125. package/dist/routes/api/v1/symlinks/index.js.map +0 -1
  126. package/dist/routes/api/v1/symlinks/route.d.ts.map +0 -1
  127. package/dist/routes/api/v1/symlinks/route.js.map +0 -1
  128. package/dist/routes/api/v1/terminal/[proposalId]/create/index.d.ts.map +0 -1
  129. package/dist/routes/api/v1/terminal/[proposalId]/create/index.js.map +0 -1
  130. package/dist/routes/api/v1/terminal/[proposalId]/create/route.d.ts.map +0 -1
  131. package/dist/routes/api/v1/terminal/[proposalId]/create/route.js.map +0 -1
  132. package/dist/routes/api/v1/terminal/[proposalId]/destroy/index.d.ts.map +0 -1
  133. package/dist/routes/api/v1/terminal/[proposalId]/destroy/index.js.map +0 -1
  134. package/dist/routes/api/v1/terminal/[proposalId]/destroy/route.d.ts.map +0 -1
  135. package/dist/routes/api/v1/terminal/[proposalId]/destroy/route.js.map +0 -1
  136. package/dist/routes/api/v1/terminal/[proposalId]/resize/index.d.ts.map +0 -1
  137. package/dist/routes/api/v1/terminal/[proposalId]/resize/index.js.map +0 -1
  138. package/dist/routes/api/v1/terminal/[proposalId]/resize/route.d.ts.map +0 -1
  139. package/dist/routes/api/v1/terminal/[proposalId]/resize/route.js.map +0 -1
  140. package/dist/routes/api/v1/terminal/sessions/index.d.ts.map +0 -1
  141. package/dist/routes/api/v1/terminal/sessions/index.js.map +0 -1
  142. package/dist/routes/api/v1/terminal/sessions/route.d.ts.map +0 -1
  143. package/dist/routes/api/v1/terminal/sessions/route.js.map +0 -1
  144. package/dist/routes/api/v1/user/index.d.ts.map +0 -1
  145. package/dist/routes/api/v1/user/index.js.map +0 -1
  146. package/dist/routes/api/v1/user/settings/index.d.ts.map +0 -1
  147. package/dist/routes/api/v1/user/settings/index.js.map +0 -1
  148. package/dist/routes/api/v1/user/settings/route.d.ts.map +0 -1
  149. package/dist/routes/api/v1/user/settings/route.js.map +0 -1
  150. package/dist/server-with-static.d.ts.map +0 -1
  151. package/dist/server-with-static.js.map +0 -1
  152. package/dist/server.d.ts.map +0 -1
  153. package/dist/server.js.map +0 -1
  154. package/static/assets/index-COf7Bc1u.js +0 -869
  155. 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 = new Date().toISOString();
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
- return c.json({ success: true, data: { message, files: files ?? null, commitHash: stdout.trim() } });
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>;