@lovelybunch/api 1.0.69-alpha.9 → 1.0.69
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/auth/auth-manager.d.ts +10 -2
- package/dist/lib/auth/auth-manager.js +16 -5
- package/dist/lib/env-injection.d.ts +6 -0
- package/dist/lib/env-injection.js +64 -0
- package/dist/lib/git.d.ts +1 -0
- package/dist/lib/git.js +39 -1
- package/dist/lib/jobs/job-runner.js +22 -3
- package/dist/lib/jobs/job-scheduler.js +12 -1
- package/dist/lib/jobs/job-store.d.ts +1 -0
- package/dist/lib/jobs/job-store.js +150 -28
- package/dist/lib/storage/file-storage.js +16 -7
- package/dist/lib/terminal/terminal-manager.js +3 -2
- package/dist/routes/api/v1/config/route.js +20 -0
- package/dist/routes/api/v1/context/knowledge/[filename]/route.js +18 -11
- package/dist/routes/api/v1/context/knowledge/route.js +5 -2
- package/dist/routes/api/v1/events/purge/route.d.ts +0 -2
- package/dist/routes/api/v1/events/purge/route.js +2 -14
- package/dist/routes/api/v1/events/route.d.ts +0 -2
- package/dist/routes/api/v1/events/route.js +2 -14
- package/dist/routes/api/v1/events/status/route.d.ts +0 -2
- package/dist/routes/api/v1/events/status/route.js +2 -14
- package/dist/routes/api/v1/events/stream/route.js +2 -14
- package/dist/routes/api/v1/git/index.js +66 -6
- package/dist/routes/api/v1/jobs/[id]/run/route.d.ts +2 -2
- package/dist/routes/api/v1/jobs/status/route.d.ts +1 -1
- package/dist/routes/api/v1/proposals/[id]/route.d.ts +8 -8
- package/dist/routes/api/v1/resources/[id]/route.js +11 -7
- package/dist/routes/api/v1/resources/generate/index.d.ts +3 -0
- package/dist/routes/api/v1/resources/generate/index.js +5 -0
- package/dist/routes/api/v1/resources/generate/route.d.ts +19 -0
- package/dist/routes/api/v1/resources/generate/route.js +257 -0
- package/dist/routes/api/v1/resources/index.js +2 -0
- package/dist/routes/api/v1/version/index.d.ts +3 -0
- package/dist/routes/api/v1/version/index.js +5 -0
- package/dist/routes/api/v1/version/route.d.ts +24 -0
- package/dist/routes/api/v1/version/route.js +51 -0
- package/dist/server-with-static.js +40 -23
- package/dist/server.js +40 -23
- package/package.json +5 -4
- package/static/assets/index-CfRmV6nM.css +33 -0
- package/static/assets/index-DzYTksNb.js +969 -0
- package/static/index.html +2 -2
- package/static/assets/index-BFXazLjO.js +0 -911
- 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 =
|
|
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
|
-
|
|
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
|
|
124
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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/
|
|
16
|
-
status: import("@lovelybunch/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|
-
//
|
|
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
|
-
//
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
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,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">)>;
|