@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
@@ -59,14 +59,16 @@ app.get('/:filename', async (c) => {
59
59
  const filePath = path.join(knowledgePath, actualFilename);
60
60
  const fileContent = await fs.readFile(filePath, 'utf-8');
61
61
  const { data, content } = matter(fileContent);
62
- // Extract title from first heading or use filename
63
- const title = content.match(/^#\s+(.+)$/m)?.[1] ||
62
+ // Extract title from metadata, first heading, or use filename
63
+ const title = data.title ||
64
+ content.match(/^#\s+(.+)$/m)?.[1] ||
64
65
  actualFilename.replace('.md', '').replace(/[_-]/g, ' ').replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
65
66
  const document = {
66
67
  filename: actualFilename,
67
68
  metadata: {
68
69
  ...data,
69
- updated: data.updated || new Date().toISOString().split('T')[0],
70
+ title, // Include title in metadata
71
+ updated: data.updated || new Date().toISOString(),
70
72
  tags: data.tags || [],
71
73
  sources: data.sources || []
72
74
  },
@@ -107,11 +109,19 @@ app.put('/:filename', async (c) => {
107
109
  // Read current content
108
110
  const currentContent = await fs.readFile(filePath, 'utf-8');
109
111
  const { data: currentData, content: currentMarkdown } = matter(currentContent);
112
+ // Extract current title from markdown content or metadata
113
+ const currentTitle = currentMarkdown.match(/^#\s+(.+)$/m)?.[1] ||
114
+ currentData.title ||
115
+ actualFilename.replace('.md', '').replace(/[_-]/g, ' ').replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
110
116
  // Prepare updated content
111
117
  const updatedContent = body.content !== undefined ? body.content : currentMarkdown;
118
+ // Determine if title has changed
119
+ const newTitle = body.title !== undefined ? body.title : currentTitle;
120
+ const titleChanged = body.title !== undefined && body.title !== currentTitle;
112
121
  const updatedMetadata = {
113
122
  ...currentData,
114
123
  ...body.metadata,
124
+ title: newTitle, // Store title in metadata
115
125
  updated: new Date().toISOString(),
116
126
  // Ensure these are arrays
117
127
  tags: body.metadata?.tags !== undefined ? body.metadata.tags : (currentData.tags || []),
@@ -120,8 +130,9 @@ app.put('/:filename', async (c) => {
120
130
  // Handle title change - might need to rename file
121
131
  let newFilename = actualFilename;
122
132
  let newFilePath = filePath;
123
- if (body.title) {
124
- newFilename = generateFilename(body.title);
133
+ // Only rename file if title explicitly changed
134
+ if (titleChanged) {
135
+ newFilename = generateFilename(newTitle);
125
136
  newFilePath = path.join(knowledgePath, newFilename);
126
137
  // Check if new filename conflicts with existing file (unless it's the same file)
127
138
  if (newFilename !== actualFilename) {
@@ -142,10 +153,6 @@ app.put('/:filename', async (c) => {
142
153
  if (newFilename !== actualFilename) {
143
154
  await fs.unlink(filePath);
144
155
  }
145
- // Extract updated title
146
- const title = body.title ||
147
- updatedContent.match(/^#\s+(.+)$/m)?.[1] ||
148
- newFilename.replace('.md', '').replace(/[_-]/g, ' ').replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
149
156
  // Log knowledge update event
150
157
  try {
151
158
  const session = await requireAuth(c);
@@ -159,7 +166,7 @@ app.put('/:filename', async (c) => {
159
166
  payload: {
160
167
  filename: newFilename,
161
168
  oldFilename: actualFilename !== newFilename ? actualFilename : undefined,
162
- title,
169
+ title: newTitle,
163
170
  category: updatedMetadata.category,
164
171
  summary: generateSummary(updatedContent),
165
172
  }
@@ -172,7 +179,7 @@ app.put('/:filename', async (c) => {
172
179
  success: true,
173
180
  document: {
174
181
  filename: newFilename,
175
- title,
182
+ title: newTitle,
176
183
  metadata: updatedMetadata,
177
184
  content: updatedContent
178
185
  }
@@ -64,13 +64,15 @@ app.get('/', async (c) => {
64
64
  const filePath = path.join(knowledgePath, file);
65
65
  const fileContent = await fs.readFile(filePath, 'utf-8');
66
66
  const { data, content } = matter(fileContent);
67
- // Extract title from first heading or use filename
68
- const title = content.match(/^#\s+(.+)$/m)?.[1] ||
67
+ // Extract title from metadata, first heading, or use filename
68
+ const title = data.title ||
69
+ content.match(/^#\s+(.+)$/m)?.[1] ||
69
70
  file.replace('.md', '').replace(/[_-]/g, ' ').replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
70
71
  return {
71
72
  filename: file,
72
73
  metadata: {
73
74
  ...data,
75
+ title, // Include title in metadata
74
76
  updated: data.updated || new Date().toISOString(),
75
77
  tags: data.tags || [],
76
78
  sources: data.sources || []
@@ -119,6 +121,7 @@ app.post('/', async (c) => {
119
121
  const now = new Date();
120
122
  const frontmatter = {
121
123
  version: '1.0',
124
+ title: body.title, // Store title in metadata
122
125
  updated: now.toISOString(),
123
126
  type: 'knowledge',
124
127
  category: body.metadata?.category || 'general',
@@ -9,8 +9,6 @@ import { Context } from "hono";
9
9
  */
10
10
  export declare function POST(c: Context): Promise<(Response & import("hono").TypedResponse<{
11
11
  error: string;
12
- }, 404, "json">) | (Response & import("hono").TypedResponse<{
13
- error: string;
14
12
  }, 400, "json">) | (Response & import("hono").TypedResponse<{
15
13
  deleted: number;
16
14
  }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
@@ -1,18 +1,9 @@
1
1
  /**
2
2
  * Events purge endpoint
3
3
  */
4
+ import { getLogsDir } from "@lovelybunch/core";
4
5
  import { promises as fs } from "fs";
5
6
  import path from "path";
6
- import { findGaitDirectory } from "../../../../../lib/gait-path.js";
7
- /**
8
- * Get the events directory path
9
- */
10
- async function getEventsDir() {
11
- const gaitDir = await findGaitDirectory();
12
- if (!gaitDir)
13
- return null;
14
- return path.join(gaitDir, "logs");
15
- }
16
7
  /**
17
8
  * POST /api/v1/events/purge
18
9
  * Delete rotated files older than a timestamp
@@ -20,10 +11,7 @@ async function getEventsDir() {
20
11
  */
21
12
  export async function POST(c) {
22
13
  try {
23
- const eventsDir = await getEventsDir();
24
- if (!eventsDir) {
25
- return c.json({ error: "Events directory not found" }, 404);
26
- }
14
+ const eventsDir = getLogsDir();
27
15
  const body = await c.req.json();
28
16
  const beforeIso = body.beforeIso;
29
17
  if (!beforeIso) {
@@ -19,8 +19,6 @@ export declare function POST(c: Context): Promise<(Response & import("hono").Typ
19
19
  * Fetch a page of events from the current file
20
20
  */
21
21
  export declare function GET(c: Context): Promise<(Response & import("hono").TypedResponse<{
22
- error: string;
23
- }, 404, "json">) | (Response & import("hono").TypedResponse<{
24
22
  items: any[];
25
23
  next_since_seq: number;
26
24
  eof: boolean;
@@ -3,20 +3,11 @@
3
3
  * Provides endpoints for logging, querying, and streaming events
4
4
  */
5
5
  import { getLogger } from "@lovelybunch/core/logging";
6
+ import { getLogsDir } from "@lovelybunch/core";
6
7
  import { promises as fs } from "fs";
7
8
  import * as fsSync from "fs";
8
9
  import path from "path";
9
- import { findGaitDirectory } from "../../../../lib/gait-path.js";
10
10
  import readline from "readline";
11
- /**
12
- * Get the events directory path
13
- */
14
- async function getEventsDir() {
15
- const gaitDir = await findGaitDirectory();
16
- if (!gaitDir)
17
- return null;
18
- return path.join(gaitDir, "logs");
19
- }
20
11
  /**
21
12
  * POST /api/v1/events
22
13
  * Enqueue one or many events
@@ -50,10 +41,7 @@ export async function POST(c) {
50
41
  */
51
42
  export async function GET(c) {
52
43
  try {
53
- const eventsDir = await getEventsDir();
54
- if (!eventsDir) {
55
- return c.json({ error: "Events directory not found" }, 404);
56
- }
44
+ const eventsDir = getLogsDir();
57
45
  const url = new URL(c.req.url);
58
46
  const sinceSeq = parseInt(url.searchParams.get("since_seq") || "0", 10);
59
47
  const limit = Math.min(parseInt(url.searchParams.get("limit") || "1000", 10), 5000);
@@ -7,8 +7,6 @@ import { Context } from "hono";
7
7
  * Get logging system status and configuration
8
8
  */
9
9
  export declare function GET(c: Context): Promise<(Response & import("hono").TypedResponse<{
10
- error: string;
11
- }, 404, "json">) | (Response & import("hono").TypedResponse<{
12
10
  currentFile: string;
13
11
  sizeBytes: number;
14
12
  lastSeq: number;
@@ -2,28 +2,16 @@
2
2
  * Events status endpoint
3
3
  */
4
4
  import { getLogger } from "@lovelybunch/core/logging";
5
+ import { getLogsDir } from "@lovelybunch/core";
5
6
  import { promises as fs } from "fs";
6
7
  import path from "path";
7
- import { findGaitDirectory } from "../../../../../lib/gait-path.js";
8
- /**
9
- * Get the events directory path
10
- */
11
- async function getEventsDir() {
12
- const gaitDir = await findGaitDirectory();
13
- if (!gaitDir)
14
- return null;
15
- return path.join(gaitDir, "logs");
16
- }
17
8
  /**
18
9
  * GET /api/v1/events/status
19
10
  * Get logging system status and configuration
20
11
  */
21
12
  export async function GET(c) {
22
13
  try {
23
- const eventsDir = await getEventsDir();
24
- if (!eventsDir) {
25
- return c.json({ error: "Events directory not found" }, 404);
26
- }
14
+ const eventsDir = getLogsDir();
27
15
  const logger = getLogger();
28
16
  const currentFile = path.join(eventsDir, "events-current.jsonl");
29
17
  let sizeBytes = 0;
@@ -2,29 +2,17 @@
2
2
  * Events streaming endpoint for real-time tailing
3
3
  */
4
4
  import { streamSSE } from "hono/streaming";
5
+ import { getLogsDir } from "@lovelybunch/core";
5
6
  import { promises as fs } from "fs";
6
7
  import * as fsSync from "fs";
7
8
  import path from "path";
8
- import { findGaitDirectory } from "../../../../../lib/gait-path.js";
9
9
  import readline from "readline";
10
- /**
11
- * Get the events directory path
12
- */
13
- async function getEventsDir() {
14
- const gaitDir = await findGaitDirectory();
15
- if (!gaitDir)
16
- return null;
17
- return path.join(gaitDir, "logs");
18
- }
19
10
  /**
20
11
  * GET /api/v1/events/stream?since_seq=
21
12
  * Server-Sent Events stream for near real-time tails
22
13
  */
23
14
  export async function GET(c) {
24
- const eventsDir = await getEventsDir();
25
- if (!eventsDir) {
26
- return c.json({ error: "Events directory not found" }, 404);
27
- }
15
+ const eventsDir = getLogsDir();
28
16
  const url = new URL(c.req.url);
29
17
  const sinceSeq = parseInt(url.searchParams.get("since_seq") || "0", 10);
30
18
  const currentFile = path.join(eventsDir, "events-current.jsonl");
@@ -1,7 +1,7 @@
1
1
  import { Hono } from 'hono';
2
2
  import crypto from 'crypto';
3
- import { getRepoStatus, listBranches, createBranch, deleteBranch, switchBranch, mergeBranch, pushCurrent, pullCurrent, listWorktrees, addWorktree, removeWorktree, commitInWorktree, pushWorktree, pullWorktree, checkRemoteAuth, getCredentialConfig, storeCredentials, getCommitDetails, getFileDiff, } from '../../../../lib/git.js';
4
- import { saveGithubToken, clearGithubToken } from '../../../../lib/github-token.js';
3
+ import { getRepoStatus, listBranches, createBranch, deleteBranch, switchBranch, mergeBranch, pushCurrent, pullCurrent, listWorktrees, addWorktree, removeWorktree, commitInWorktree, pushWorktree, pullWorktree, checkRemoteAuth, getCredentialConfig, storeCredentials, getCommitDetails, getFileDiff, setRemoteUrl, } from '../../../../lib/git.js';
4
+ import { saveGithubToken, clearGithubToken, readGithubToken, isGithubTokenValid } from '../../../../lib/github-token.js';
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';
@@ -78,6 +78,50 @@ app.get('/credential-config', async (c) => {
78
78
  return c.json({ success: false, error: { message: e.message } }, 500);
79
79
  }
80
80
  });
81
+ // Set Remote URL
82
+ app.put('/remote', async (c) => {
83
+ try {
84
+ const body = await c.req.json();
85
+ const remoteUrl = String(body?.remoteUrl || '');
86
+ if (!remoteUrl) {
87
+ return c.json({ success: false, error: { message: 'Remote URL is required' } }, 400);
88
+ }
89
+ await setRemoteUrl(remoteUrl);
90
+ // If a GitHub token exists and is valid, store it in the credential helper now that we have a remote
91
+ try {
92
+ const tokenRecord = await readGithubToken();
93
+ if (tokenRecord && isGithubTokenValid(tokenRecord)) {
94
+ try {
95
+ await storeCredentials('x-access-token', tokenRecord.token);
96
+ console.log('[git] Successfully stored GitHub token in credential helper after remote creation');
97
+ }
98
+ catch (credError) {
99
+ console.warn('[git] Failed to store GitHub token in credential helper after remote creation:', credError);
100
+ // Don't fail the remote creation if credential storage fails
101
+ }
102
+ }
103
+ }
104
+ catch (tokenError) {
105
+ // Ignore token read errors - remote creation should still succeed
106
+ console.warn('[git] Could not check for GitHub token after remote creation:', tokenError);
107
+ }
108
+ return c.json({ success: true, data: { message: 'Remote URL set successfully' } });
109
+ }
110
+ catch (e) {
111
+ return c.json({ success: false, error: { message: e.message } }, 500);
112
+ }
113
+ });
114
+ // Delete Remote URL
115
+ app.delete('/remote', async (c) => {
116
+ try {
117
+ const { removeRemote } = await import('../../../../lib/git.js');
118
+ await removeRemote();
119
+ return c.json({ success: true, data: { message: 'Remote URL removed successfully' } });
120
+ }
121
+ catch (e) {
122
+ return c.json({ success: false, error: { message: e.message } }, 500);
123
+ }
124
+ });
81
125
  // Store Credentials
82
126
  app.post('/credentials', async (c) => {
83
127
  try {
@@ -163,11 +207,21 @@ app.post('/providers/github/token', async (c) => {
163
207
  }
164
208
  }
165
209
  const record = await saveGithubToken(token, expiresAt);
210
+ // Try to store credentials in the credential helper if a remote exists
166
211
  try {
167
212
  await storeCredentials('x-access-token', token);
213
+ console.log('[git] Successfully stored GitHub token in credential helper');
168
214
  }
169
215
  catch (credError) {
170
- console.warn('[git] Failed to install GitHub token into credential store:', credError);
216
+ // Check if the error is because no remote exists (expected case)
217
+ const errorMessage = credError?.message || '';
218
+ if (errorMessage.includes('No git remote configured')) {
219
+ console.log('[git] GitHub token saved, but no remote configured yet. Token will be stored in credential helper when remote is created.');
220
+ }
221
+ else {
222
+ console.warn('[git] Failed to install GitHub token into credential store:', credError);
223
+ }
224
+ // Don't fail token storage if credential helper setup fails - the token is still saved
171
225
  }
172
226
  return c.json({ success: true, data: { expiresAt: record.expiresAt } });
173
227
  }
@@ -359,13 +413,19 @@ app.post('/discard', async (c) => {
359
413
  const statusCode = fileChange.status.trim();
360
414
  // Handle different file statuses
361
415
  if (statusCode === '??' || statusCode.includes('U')) {
362
- // Untracked file - remove it
363
- const { unlink } = await import('fs/promises');
416
+ // Untracked file or directory - remove it
417
+ const { rm, stat } = await import('fs/promises');
364
418
  const { getRepoRoot } = await import('../../../../lib/git.js');
365
419
  const repoRoot = await getRepoRoot();
366
420
  const { join } = await import('path');
367
421
  const fullPath = join(repoRoot, filePath);
368
- await unlink(fullPath);
422
+ const stats = await stat(fullPath);
423
+ if (stats.isDirectory()) {
424
+ await rm(fullPath, { recursive: true });
425
+ }
426
+ else {
427
+ await rm(fullPath);
428
+ }
369
429
  }
370
430
  else if (statusCode.includes('D')) {
371
431
  // Deleted file - restore it
@@ -12,8 +12,8 @@ export declare function POST(c: Context): Promise<(Response & import("hono").Typ
12
12
  run: {
13
13
  id: string;
14
14
  jobId: string;
15
- trigger: import("@lovelybunch/types").ScheduledJobTrigger;
16
- status: import("@lovelybunch/types").ScheduledJobRunStatus;
15
+ trigger: import("@lovelybunch/core").ScheduledJobTrigger;
16
+ status: import("@lovelybunch/core").ScheduledJobRunStatus;
17
17
  startedAt: string;
18
18
  finishedAt?: string;
19
19
  outputPath?: string;
@@ -7,7 +7,7 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
7
7
  runningCount: number;
8
8
  jobs: {
9
9
  id: string;
10
- status: import("@lovelybunch/types").ScheduledJobStatus;
10
+ status: import("@lovelybunch/core").ScheduledJobStatus;
11
11
  nextRunAt?: string;
12
12
  lastRunAt?: string;
13
13
  timerActive: boolean;
@@ -12,7 +12,7 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
12
12
  intent: string;
13
13
  content?: string;
14
14
  author: {
15
- type: import("@lovelybunch/types").AuthorType;
15
+ type: import("@lovelybunch/core").AuthorType;
16
16
  id: string;
17
17
  name: string;
18
18
  email?: string;
@@ -47,7 +47,7 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
47
47
  version: string;
48
48
  name: string;
49
49
  description: string;
50
- type: import("@lovelybunch/types").FeatureFlagType;
50
+ type: import("@lovelybunch/core").FeatureFlagType;
51
51
  defaultValue: any;
52
52
  scopes: string[];
53
53
  targets: {
@@ -95,7 +95,7 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
95
95
  minimumSampleSize: number;
96
96
  testType: "two-tailed" | "one-tailed";
97
97
  };
98
- status: import("@lovelybunch/types").ExperimentStatus;
98
+ status: import("@lovelybunch/core").ExperimentStatus;
99
99
  startedAt?: string;
100
100
  endedAt?: string;
101
101
  }[];
@@ -133,7 +133,7 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
133
133
  schedule?: string;
134
134
  rollbackPlan?: string;
135
135
  };
136
- status: import("@lovelybunch/types").CPStatus;
136
+ status: import("@lovelybunch/core").CPStatus;
137
137
  metadata: {
138
138
  createdAt: string;
139
139
  updatedAt: string;
@@ -169,7 +169,7 @@ export declare function PATCH(c: Context): Promise<(Response & import("hono").Ty
169
169
  intent: string;
170
170
  content?: string;
171
171
  author: {
172
- type: import("@lovelybunch/types").AuthorType;
172
+ type: import("@lovelybunch/core").AuthorType;
173
173
  id: string;
174
174
  name: string;
175
175
  email?: string;
@@ -204,7 +204,7 @@ export declare function PATCH(c: Context): Promise<(Response & import("hono").Ty
204
204
  version: string;
205
205
  name: string;
206
206
  description: string;
207
- type: import("@lovelybunch/types").FeatureFlagType;
207
+ type: import("@lovelybunch/core").FeatureFlagType;
208
208
  defaultValue: any;
209
209
  scopes: string[];
210
210
  targets: {
@@ -252,7 +252,7 @@ export declare function PATCH(c: Context): Promise<(Response & import("hono").Ty
252
252
  minimumSampleSize: number;
253
253
  testType: "two-tailed" | "one-tailed";
254
254
  };
255
- status: import("@lovelybunch/types").ExperimentStatus;
255
+ status: import("@lovelybunch/core").ExperimentStatus;
256
256
  startedAt?: string;
257
257
  endedAt?: string;
258
258
  }[];
@@ -290,7 +290,7 @@ export declare function PATCH(c: Context): Promise<(Response & import("hono").Ty
290
290
  schedule?: string;
291
291
  rollbackPlan?: string;
292
292
  };
293
- status: import("@lovelybunch/types").CPStatus;
293
+ status: import("@lovelybunch/core").CPStatus;
294
294
  metadata: {
295
295
  createdAt: string;
296
296
  updatedAt: string;
@@ -52,10 +52,11 @@ export async function GET(c) {
52
52
  }
53
53
  const url = new URL(c.req.url);
54
54
  const download = url.searchParams.get('download');
55
+ // Return the actual file (either for download or inline display)
56
+ const filePath = path.join(FILES_DIR, resource.path);
57
+ const fileBuffer = await fs.readFile(filePath);
55
58
  if (download === 'true') {
56
- // Return the actual file
57
- const filePath = path.join(FILES_DIR, resource.path);
58
- const fileBuffer = await fs.readFile(filePath);
59
+ // Force download with Content-Disposition: attachment
59
60
  return new Response(fileBuffer, {
60
61
  headers: {
61
62
  'Content-Type': resource.type,
@@ -64,10 +65,13 @@ export async function GET(c) {
64
65
  }
65
66
  });
66
67
  }
67
- // Return resource metadata
68
- return c.json({
69
- success: true,
70
- data: resource
68
+ // Serve file inline (for preview in iframe, img, video, etc.)
69
+ return new Response(fileBuffer, {
70
+ headers: {
71
+ 'Content-Type': resource.type,
72
+ 'Content-Length': resource.size.toString(),
73
+ 'Cache-Control': 'public, max-age=31536000'
74
+ }
71
75
  });
72
76
  }
73
77
  catch (error) {
@@ -0,0 +1,3 @@
1
+ import { Hono } from 'hono';
2
+ declare const app: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default app;
@@ -0,0 +1,5 @@
1
+ import { Hono } from 'hono';
2
+ import { POST } from './route.js';
3
+ const app = new Hono();
4
+ app.post('/', POST);
5
+ export default app;
@@ -0,0 +1,19 @@
1
+ import { Context } from 'hono';
2
+ export declare function POST(c: Context): Promise<(Response & import("hono").TypedResponse<{
3
+ success: false;
4
+ error: {
5
+ code: string;
6
+ message: string;
7
+ };
8
+ }, 400, "json">) | (Response & import("hono").TypedResponse<{
9
+ success: true;
10
+ data: {
11
+ imageUrl: string;
12
+ };
13
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
14
+ success: false;
15
+ error: {
16
+ code: string;
17
+ message: any;
18
+ };
19
+ }, 500, "json">)>;