@lovelybunch/api 1.0.47 → 1.0.48
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/gait-path.d.ts +7 -5
- package/dist/lib/gait-path.js +23 -17
- package/dist/lib/project-paths.d.ts +2 -2
- package/dist/lib/project-paths.js +8 -5
- package/dist/lib/storage/file-storage.js +4 -4
- package/dist/lib/symlinks/symlink-manager.js +40 -24
- package/dist/lib/terminal/context-helper.js +27 -27
- package/dist/lib/terminal/shell-utils.js +6 -6
- package/dist/lib/terminal/terminal-manager.d.ts +2 -0
- package/dist/lib/terminal/terminal-manager.js +31 -9
- package/dist/routes/api/v1/agents/[id]/route.js +4 -4
- package/dist/routes/api/v1/agents/route.js +4 -4
- package/dist/routes/api/v1/ai/route.js +3 -6
- package/dist/routes/api/v1/chats/[id]/route.js +4 -4
- package/dist/routes/api/v1/chats/route.js +4 -4
- package/dist/routes/api/v1/config/index.js +2 -1
- package/dist/routes/api/v1/config/route.d.ts +13 -0
- package/dist/routes/api/v1/config/route.js +69 -0
- package/dist/routes/api/v1/context/knowledge/[filename]/route.js +4 -4
- package/dist/routes/api/v1/context/knowledge/route.js +4 -4
- package/dist/routes/api/v1/mcp/config/index.d.ts +1 -0
- package/dist/routes/api/v1/mcp/config/index.js +1 -0
- package/dist/routes/api/v1/mcp/config/route.d.ts +3 -0
- package/dist/routes/api/v1/mcp/config/route.js +59 -0
- package/dist/routes/api/v1/mcp/index.js +2 -2
- package/dist/routes/api/v1/proposals/[id]/route.d.ts +0 -2
- package/dist/routes/api/v1/proposals/route.d.ts +0 -2
- package/dist/routes/api/v1/resources/[id]/route.js +4 -4
- package/dist/routes/api/v1/resources/[id]/thumbnail/route.js +4 -4
- package/dist/routes/api/v1/resources/route.js +4 -4
- package/dist/routes/api/v1/symlinks/index.d.ts +1 -0
- package/dist/routes/api/v1/symlinks/index.js +1 -0
- package/dist/routes/api/v1/symlinks/route.d.ts +3 -0
- package/dist/routes/api/v1/symlinks/route.js +135 -0
- package/dist/server.js +2 -0
- package/package.json +5 -5
- package/static/assets/index-DnaNaR1E.js +714 -0
- package/static/index.html +1 -1
|
@@ -3,16 +3,16 @@ import { join, resolve } from "path";
|
|
|
3
3
|
function getChatsPath() {
|
|
4
4
|
let basePath;
|
|
5
5
|
if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
|
|
6
|
-
// Dev mode: use project root .
|
|
6
|
+
// Dev mode: use project root .nut directory
|
|
7
7
|
basePath = process.env.GAIT_DEV_ROOT;
|
|
8
8
|
}
|
|
9
9
|
else if (process.env.GAIT_DATA_PATH) {
|
|
10
10
|
// Production mode: use GAIT_DATA_PATH (set by CLI)
|
|
11
|
-
basePath = resolve(process.env.GAIT_DATA_PATH, '.
|
|
11
|
+
basePath = resolve(process.env.GAIT_DATA_PATH, '.nut');
|
|
12
12
|
}
|
|
13
13
|
else {
|
|
14
|
-
// Fallback: use current directory .
|
|
15
|
-
basePath = resolve(process.cwd(), '.
|
|
14
|
+
// Fallback: use current directory .nut
|
|
15
|
+
basePath = resolve(process.cwd(), '.nut');
|
|
16
16
|
}
|
|
17
17
|
return join(basePath, 'chats');
|
|
18
18
|
}
|
|
@@ -17,3 +17,16 @@ export declare function PUT(c: Context): Promise<(Response & import("hono").Type
|
|
|
17
17
|
}, 500, "json">) | (Response & import("hono").TypedResponse<{
|
|
18
18
|
error: string;
|
|
19
19
|
}, 405, "json">)>;
|
|
20
|
+
export declare function TEST(c: Context): Promise<(Response & import("hono").TypedResponse<{
|
|
21
|
+
success: false;
|
|
22
|
+
message: string;
|
|
23
|
+
}, 400, "json">) | (Response & import("hono").TypedResponse<{
|
|
24
|
+
success: false;
|
|
25
|
+
message: string;
|
|
26
|
+
}, 200, "json">) | (Response & import("hono").TypedResponse<{
|
|
27
|
+
success: true;
|
|
28
|
+
message: string;
|
|
29
|
+
}, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
|
|
30
|
+
success: false;
|
|
31
|
+
message: string;
|
|
32
|
+
}, 501, "json">)>;
|
|
@@ -127,3 +127,72 @@ export async function PUT(c) {
|
|
|
127
127
|
return c.json({ error: 'PUT not supported for project config' }, 405);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
+
// POST /api/v1/config/test
|
|
131
|
+
// Body: { provider: string, key?: string }
|
|
132
|
+
// Tests the provided API key (or the saved key if not provided) for a supported provider.
|
|
133
|
+
export async function TEST(c) {
|
|
134
|
+
try {
|
|
135
|
+
const body = await c.req.json();
|
|
136
|
+
const provider = (body?.provider || '').toString();
|
|
137
|
+
const providedKey = typeof body?.key === 'string' ? body.key : undefined;
|
|
138
|
+
if (!provider) {
|
|
139
|
+
return c.json({ success: false, message: 'Missing provider' }, 400);
|
|
140
|
+
}
|
|
141
|
+
// Load saved key if none provided (or if a masked placeholder was provided)
|
|
142
|
+
let effectiveKey = providedKey && providedKey !== '***' ? providedKey : undefined;
|
|
143
|
+
if (!effectiveKey) {
|
|
144
|
+
// Read from global config
|
|
145
|
+
const configPath = await getGlobalConfigPath();
|
|
146
|
+
let config = { apiKeys: {}, defaults: {} };
|
|
147
|
+
try {
|
|
148
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
149
|
+
config = JSON.parse(content);
|
|
150
|
+
}
|
|
151
|
+
catch { }
|
|
152
|
+
effectiveKey = config.apiKeys?.[provider];
|
|
153
|
+
// Fallback: env var for OpenRouter only (optional)
|
|
154
|
+
if (!effectiveKey && provider === 'openrouter') {
|
|
155
|
+
effectiveKey = process.env.OPENROUTER_API_KEY;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (!effectiveKey) {
|
|
159
|
+
return c.json({ success: false, message: 'No API key configured or provided' }, 400);
|
|
160
|
+
}
|
|
161
|
+
if (provider === 'openrouter') {
|
|
162
|
+
try {
|
|
163
|
+
// Minimal auth-required request to validate key
|
|
164
|
+
const resp = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
|
165
|
+
method: 'POST',
|
|
166
|
+
headers: {
|
|
167
|
+
'Authorization': `Bearer ${effectiveKey}`,
|
|
168
|
+
'Content-Type': 'application/json',
|
|
169
|
+
'HTTP-Referer': process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3001',
|
|
170
|
+
'X-Title': 'Coconut AI Assistant',
|
|
171
|
+
},
|
|
172
|
+
body: JSON.stringify({
|
|
173
|
+
model: 'openai/gpt-4o-mini',
|
|
174
|
+
messages: [
|
|
175
|
+
{ role: 'system', content: 'Health check' },
|
|
176
|
+
{ role: 'user', content: 'ping' },
|
|
177
|
+
],
|
|
178
|
+
temperature: 0,
|
|
179
|
+
max_tokens: 1,
|
|
180
|
+
}),
|
|
181
|
+
});
|
|
182
|
+
if (!resp.ok) {
|
|
183
|
+
const text = await resp.text();
|
|
184
|
+
return c.json({ success: false, message: `OpenRouter rejected key: ${text.slice(0, 200)}` }, 200);
|
|
185
|
+
}
|
|
186
|
+
return c.json({ success: true, message: 'OpenRouter key is valid' });
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
return c.json({ success: false, message: err instanceof Error ? err.message : 'Network error' }, 200);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Other providers not wired up yet
|
|
193
|
+
return c.json({ success: false, message: `Provider '${provider}' test not implemented yet` }, 501);
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
return c.json({ success: false, message: 'Invalid request body' }, 400);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -6,16 +6,16 @@ const app = new Hono();
|
|
|
6
6
|
function getKnowledgePath() {
|
|
7
7
|
let basePath;
|
|
8
8
|
if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
|
|
9
|
-
// Dev mode: use project root .
|
|
9
|
+
// Dev mode: use project root .nut directory
|
|
10
10
|
basePath = process.env.GAIT_DEV_ROOT;
|
|
11
11
|
}
|
|
12
12
|
else if (process.env.GAIT_DATA_PATH) {
|
|
13
13
|
// Production mode: use GAIT_DATA_PATH (set by CLI)
|
|
14
|
-
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.
|
|
14
|
+
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.nut');
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
|
-
// Fallback: use current directory .
|
|
18
|
-
basePath = path.resolve(process.cwd(), '.
|
|
17
|
+
// Fallback: use current directory .nut
|
|
18
|
+
basePath = path.resolve(process.cwd(), '.nut');
|
|
19
19
|
}
|
|
20
20
|
return path.join(basePath, 'context', 'knowledge');
|
|
21
21
|
}
|
|
@@ -7,16 +7,16 @@ const app = new Hono();
|
|
|
7
7
|
function getKnowledgePath() {
|
|
8
8
|
let basePath;
|
|
9
9
|
if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
|
|
10
|
-
// Dev mode: use project root .
|
|
10
|
+
// Dev mode: use project root .nut directory
|
|
11
11
|
basePath = process.env.GAIT_DEV_ROOT;
|
|
12
12
|
}
|
|
13
13
|
else if (process.env.GAIT_DATA_PATH) {
|
|
14
14
|
// Production mode: use GAIT_DATA_PATH (set by CLI)
|
|
15
|
-
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.
|
|
15
|
+
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.nut');
|
|
16
16
|
}
|
|
17
17
|
else {
|
|
18
|
-
// Fallback: use current directory .
|
|
19
|
-
basePath = path.resolve(process.cwd(), '.
|
|
18
|
+
// Fallback: use current directory .nut
|
|
19
|
+
basePath = path.resolve(process.cwd(), '.nut');
|
|
20
20
|
}
|
|
21
21
|
return path.join(basePath, 'context', 'knowledge');
|
|
22
22
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { promises as fs } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
const app = new Hono();
|
|
5
|
+
const getMcpConfigPath = () => {
|
|
6
|
+
const gaitPath = path.join(process.cwd(), '.gait');
|
|
7
|
+
return path.join(gaitPath, 'mcp', 'config.json');
|
|
8
|
+
};
|
|
9
|
+
const ensureMcpDirectory = async () => {
|
|
10
|
+
const gaitPath = path.join(process.cwd(), '.gait');
|
|
11
|
+
const mcpPath = path.join(gaitPath, 'mcp');
|
|
12
|
+
try {
|
|
13
|
+
await fs.access(mcpPath);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
await fs.mkdir(mcpPath, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
// GET /api/v1/mcp/config - Get MCP configuration
|
|
20
|
+
app.get('/', async (c) => {
|
|
21
|
+
try {
|
|
22
|
+
await ensureMcpDirectory();
|
|
23
|
+
const configPath = getMcpConfigPath();
|
|
24
|
+
try {
|
|
25
|
+
const configData = await fs.readFile(configPath, 'utf-8');
|
|
26
|
+
const config = JSON.parse(configData);
|
|
27
|
+
return c.json(config);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
// If file doesn't exist or is invalid, return empty config
|
|
31
|
+
return c.json({});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error('Error reading MCP config:', error);
|
|
36
|
+
return c.json({ error: 'Failed to read MCP configuration' }, 500);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
// PUT /api/v1/mcp/config - Update MCP configuration
|
|
40
|
+
app.put('/', async (c) => {
|
|
41
|
+
try {
|
|
42
|
+
const body = await c.req.json();
|
|
43
|
+
await ensureMcpDirectory();
|
|
44
|
+
const configPath = getMcpConfigPath();
|
|
45
|
+
// Validate the configuration structure
|
|
46
|
+
if (typeof body !== 'object') {
|
|
47
|
+
return c.json({ error: 'Invalid configuration format' }, 400);
|
|
48
|
+
}
|
|
49
|
+
// Write the configuration to file
|
|
50
|
+
const configData = JSON.stringify(body, null, 2);
|
|
51
|
+
await fs.writeFile(configPath, configData, 'utf-8');
|
|
52
|
+
return c.json({ message: 'MCP configuration updated successfully' });
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error('Error updating MCP config:', error);
|
|
56
|
+
return c.json({ error: 'Failed to update MCP configuration' }, 500);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
export default app;
|
|
@@ -12,10 +12,10 @@ function resolveGaitPath() {
|
|
|
12
12
|
basePath = process.env.GAIT_DEV_ROOT;
|
|
13
13
|
}
|
|
14
14
|
else if (process.env.GAIT_DATA_PATH) {
|
|
15
|
-
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.
|
|
15
|
+
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.nut');
|
|
16
16
|
}
|
|
17
17
|
else {
|
|
18
|
-
basePath = path.resolve(process.cwd(), '.
|
|
18
|
+
basePath = path.resolve(process.cwd(), '.nut');
|
|
19
19
|
}
|
|
20
20
|
return basePath;
|
|
21
21
|
}
|
|
@@ -16,7 +16,6 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
|
|
|
16
16
|
id: string;
|
|
17
17
|
name: string;
|
|
18
18
|
email?: string;
|
|
19
|
-
role?: string;
|
|
20
19
|
};
|
|
21
20
|
productSpecRef?: string;
|
|
22
21
|
planSteps: {
|
|
@@ -174,7 +173,6 @@ export declare function PATCH(c: Context): Promise<(Response & import("hono").Ty
|
|
|
174
173
|
id: string;
|
|
175
174
|
name: string;
|
|
176
175
|
email?: string;
|
|
177
|
-
role?: string;
|
|
178
176
|
};
|
|
179
177
|
productSpecRef?: string;
|
|
180
178
|
planSteps: {
|
|
@@ -10,7 +10,6 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
|
|
|
10
10
|
id: string;
|
|
11
11
|
name: string;
|
|
12
12
|
email?: string;
|
|
13
|
-
role?: string;
|
|
14
13
|
};
|
|
15
14
|
productSpecRef?: string;
|
|
16
15
|
planSteps: {
|
|
@@ -168,7 +167,6 @@ export declare function POST(c: Context): Promise<(Response & import("hono").Typ
|
|
|
168
167
|
id: string;
|
|
169
168
|
name: string;
|
|
170
169
|
email?: string;
|
|
171
|
-
role?: string;
|
|
172
170
|
};
|
|
173
171
|
productSpecRef?: string;
|
|
174
172
|
planSteps: {
|
|
@@ -3,16 +3,16 @@ import path from 'path';
|
|
|
3
3
|
function getResourcesPath() {
|
|
4
4
|
let basePath;
|
|
5
5
|
if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
|
|
6
|
-
// Dev mode: use project root .
|
|
6
|
+
// Dev mode: use project root .nut directory
|
|
7
7
|
basePath = process.env.GAIT_DEV_ROOT;
|
|
8
8
|
}
|
|
9
9
|
else if (process.env.GAIT_DATA_PATH) {
|
|
10
10
|
// Production mode: use GAIT_DATA_PATH (set by CLI)
|
|
11
|
-
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.
|
|
11
|
+
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.nut');
|
|
12
12
|
}
|
|
13
13
|
else {
|
|
14
|
-
// Fallback: use current directory .
|
|
15
|
-
basePath = path.resolve(process.cwd(), '.
|
|
14
|
+
// Fallback: use current directory .nut
|
|
15
|
+
basePath = path.resolve(process.cwd(), '.nut');
|
|
16
16
|
}
|
|
17
17
|
return path.join(basePath, 'resources');
|
|
18
18
|
}
|
|
@@ -3,16 +3,16 @@ import path from 'path';
|
|
|
3
3
|
function getResourcesPath() {
|
|
4
4
|
let basePath;
|
|
5
5
|
if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
|
|
6
|
-
// Dev mode: use project root .
|
|
6
|
+
// Dev mode: use project root .nut directory
|
|
7
7
|
basePath = process.env.GAIT_DEV_ROOT;
|
|
8
8
|
}
|
|
9
9
|
else if (process.env.GAIT_DATA_PATH) {
|
|
10
10
|
// Production mode: use GAIT_DATA_PATH (set by CLI)
|
|
11
|
-
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.
|
|
11
|
+
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.nut');
|
|
12
12
|
}
|
|
13
13
|
else {
|
|
14
|
-
// Fallback: use current directory .
|
|
15
|
-
basePath = path.resolve(process.cwd(), '.
|
|
14
|
+
// Fallback: use current directory .nut
|
|
15
|
+
basePath = path.resolve(process.cwd(), '.nut');
|
|
16
16
|
}
|
|
17
17
|
return path.join(basePath, 'resources');
|
|
18
18
|
}
|
|
@@ -7,16 +7,16 @@ function generateId() {
|
|
|
7
7
|
function getResourcesPath() {
|
|
8
8
|
let basePath;
|
|
9
9
|
if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
|
|
10
|
-
// Dev mode: use project root .
|
|
10
|
+
// Dev mode: use project root .nut directory
|
|
11
11
|
basePath = process.env.GAIT_DEV_ROOT;
|
|
12
12
|
}
|
|
13
13
|
else if (process.env.GAIT_DATA_PATH) {
|
|
14
14
|
// Production mode: use GAIT_DATA_PATH (set by CLI)
|
|
15
|
-
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.
|
|
15
|
+
basePath = path.resolve(process.env.GAIT_DATA_PATH, '.nut');
|
|
16
16
|
}
|
|
17
17
|
else {
|
|
18
|
-
// Fallback: use current directory .
|
|
19
|
-
basePath = path.resolve(process.cwd(), '.
|
|
18
|
+
// Fallback: use current directory .nut
|
|
19
|
+
basePath = path.resolve(process.cwd(), '.nut');
|
|
20
20
|
}
|
|
21
21
|
return path.join(basePath, 'resources');
|
|
22
22
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './route.js';
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import { getSymlinkManager } from '../../../../lib/symlinks/symlink-manager.js';
|
|
3
|
+
const app = new Hono();
|
|
4
|
+
/**
|
|
5
|
+
* GET /api/v1/symlinks
|
|
6
|
+
* Get all symlink configurations
|
|
7
|
+
*/
|
|
8
|
+
app.get('/', async (c) => {
|
|
9
|
+
try {
|
|
10
|
+
const manager = await getSymlinkManager();
|
|
11
|
+
const symlinks = await manager.getSymlinks();
|
|
12
|
+
return c.json({
|
|
13
|
+
success: true,
|
|
14
|
+
symlinks
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
console.error('Failed to get symlinks:', error);
|
|
19
|
+
return c.json({ success: false, error: 'Failed to get symlinks' }, 500);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* POST /api/v1/symlinks
|
|
24
|
+
* Create a new symlink configuration
|
|
25
|
+
*/
|
|
26
|
+
app.post('/', async (c) => {
|
|
27
|
+
try {
|
|
28
|
+
const body = await c.req.json();
|
|
29
|
+
const { name, description, linkPath, targetPath, isActive = false } = body;
|
|
30
|
+
if (!name || !linkPath || !targetPath) {
|
|
31
|
+
return c.json({ success: false, error: 'Name, linkPath, and targetPath are required' }, 400);
|
|
32
|
+
}
|
|
33
|
+
const manager = await getSymlinkManager();
|
|
34
|
+
const result = await manager.addSymlink({
|
|
35
|
+
name,
|
|
36
|
+
description: description || '',
|
|
37
|
+
linkPath,
|
|
38
|
+
targetPath,
|
|
39
|
+
isActive
|
|
40
|
+
});
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
return c.json({ success: false, error: result.message }, 400);
|
|
43
|
+
}
|
|
44
|
+
return c.json({
|
|
45
|
+
success: true,
|
|
46
|
+
symlink: result.symlink,
|
|
47
|
+
message: result.message
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error('Failed to create symlink:', error);
|
|
52
|
+
return c.json({ success: false, error: 'Failed to create symlink' }, 500);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* PUT /api/v1/symlinks/:id
|
|
57
|
+
* Update a symlink configuration
|
|
58
|
+
*/
|
|
59
|
+
app.put('/:id', async (c) => {
|
|
60
|
+
try {
|
|
61
|
+
const id = c.req.param('id');
|
|
62
|
+
const body = await c.req.json();
|
|
63
|
+
const { name, description, linkPath, targetPath, isActive } = body;
|
|
64
|
+
const manager = await getSymlinkManager();
|
|
65
|
+
const result = await manager.updateSymlink(id, {
|
|
66
|
+
name,
|
|
67
|
+
description,
|
|
68
|
+
linkPath,
|
|
69
|
+
targetPath,
|
|
70
|
+
isActive
|
|
71
|
+
});
|
|
72
|
+
if (!result.success) {
|
|
73
|
+
return c.json({ success: false, error: result.message }, 400);
|
|
74
|
+
}
|
|
75
|
+
return c.json({
|
|
76
|
+
success: true,
|
|
77
|
+
symlink: result.symlink,
|
|
78
|
+
message: result.message
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error('Failed to update symlink:', error);
|
|
83
|
+
return c.json({ success: false, error: 'Failed to update symlink' }, 500);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* DELETE /api/v1/symlinks/:id
|
|
88
|
+
* Delete a symlink configuration
|
|
89
|
+
*/
|
|
90
|
+
app.delete('/:id', async (c) => {
|
|
91
|
+
try {
|
|
92
|
+
const id = c.req.param('id');
|
|
93
|
+
const manager = await getSymlinkManager();
|
|
94
|
+
const result = await manager.deleteSymlink(id);
|
|
95
|
+
if (!result.success) {
|
|
96
|
+
return c.json({ success: false, error: result.message }, 400);
|
|
97
|
+
}
|
|
98
|
+
return c.json({
|
|
99
|
+
success: true,
|
|
100
|
+
message: result.message
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error('Failed to delete symlink:', error);
|
|
105
|
+
return c.json({ success: false, error: 'Failed to delete symlink' }, 500);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
/**
|
|
109
|
+
* POST /api/v1/symlinks/:id/toggle
|
|
110
|
+
* Toggle symlink active state (create/remove actual symlink)
|
|
111
|
+
*/
|
|
112
|
+
app.post('/:id/toggle', async (c) => {
|
|
113
|
+
try {
|
|
114
|
+
const id = c.req.param('id');
|
|
115
|
+
const manager = await getSymlinkManager();
|
|
116
|
+
const symlink = await manager.getSymlink(id);
|
|
117
|
+
if (!symlink) {
|
|
118
|
+
return c.json({ success: false, error: 'Symlink not found' }, 404);
|
|
119
|
+
}
|
|
120
|
+
const result = await manager.toggleSymlink(id);
|
|
121
|
+
if (!result.success) {
|
|
122
|
+
return c.json({ success: false, error: result.message }, 400);
|
|
123
|
+
}
|
|
124
|
+
return c.json({
|
|
125
|
+
success: true,
|
|
126
|
+
symlink: result.symlink,
|
|
127
|
+
message: result.message
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error('Failed to toggle symlink:', error);
|
|
132
|
+
return c.json({ success: false, error: 'Failed to toggle symlink' }, 500);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
export default app;
|
package/dist/server.js
CHANGED
|
@@ -69,6 +69,7 @@ import agents from './routes/api/v1/agents/index.js';
|
|
|
69
69
|
import agentsById from './routes/api/v1/agents/[id]/index.js';
|
|
70
70
|
import git from './routes/api/v1/git/index.js';
|
|
71
71
|
import mcp from './routes/api/v1/mcp/index.js';
|
|
72
|
+
import symlinks from './routes/api/v1/symlinks/index.js';
|
|
72
73
|
// Register API routes
|
|
73
74
|
app.route('/api/v1/proposals', proposals);
|
|
74
75
|
app.route('/api/v1/terminal/sessions', terminalSessions);
|
|
@@ -88,6 +89,7 @@ app.route('/api/v1/agents', agents);
|
|
|
88
89
|
app.route('/api/v1/agents/:id', agentsById);
|
|
89
90
|
app.route('/api/v1/git', git);
|
|
90
91
|
app.route('/api/v1/mcp', mcp);
|
|
92
|
+
app.route('/api/v1/symlinks', symlinks);
|
|
91
93
|
// Health check endpoint
|
|
92
94
|
app.get('/health', (c) => {
|
|
93
95
|
return c.json({ status: 'ok', timestamp: new Date().toISOString() });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovelybunch/api",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.48",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/server-with-static.js",
|
|
6
6
|
"exports": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"static/**/*"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"dev": "NODE_ENV=development GAIT_DEV_ROOT=../../.
|
|
15
|
+
"dev": "NODE_ENV=development GAIT_DEV_ROOT=../../.nut tsx watch src/server.ts",
|
|
16
16
|
"build": "tsc",
|
|
17
17
|
"build:bundle": "tsc && node scripts/bundle-frontend.js",
|
|
18
18
|
"start": "node dist/server-with-static.js",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@hono/node-server": "^1.13.7",
|
|
34
34
|
"@hono/node-ws": "^1.0.6",
|
|
35
|
-
"@lovelybunch/core": "^1.0.
|
|
36
|
-
"@lovelybunch/mcp": "^1.0.
|
|
37
|
-
"@lovelybunch/types": "^1.0.
|
|
35
|
+
"@lovelybunch/core": "^1.0.48",
|
|
36
|
+
"@lovelybunch/mcp": "^1.0.48",
|
|
37
|
+
"@lovelybunch/types": "^1.0.48",
|
|
38
38
|
"dotenv": "^17.2.1",
|
|
39
39
|
"fuse.js": "^7.0.0",
|
|
40
40
|
"gray-matter": "^4.0.3",
|