@lovelybunch/api 1.0.69-alpha.16 → 1.0.69-alpha.18
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 +14 -1
- package/dist/lib/jobs/job-runner.js +22 -3
- package/dist/lib/terminal/terminal-manager.js +3 -2
- package/dist/routes/api/v1/config/index.js +1 -2
- package/dist/routes/api/v1/config/route.d.ts +0 -11
- package/dist/routes/api/v1/config/route.js +20 -104
- 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/git/index.js +20 -3
- 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/generate/route.js +58 -6
- package/dist/server-with-static.js +38 -23
- package/dist/server.js +38 -23
- package/package.json +4 -4
- package/static/assets/{index-8zDqsxO-.js → index-B8R7MEZ5.js} +231 -228
- package/static/assets/index-CfRmV6nM.css +33 -0
- package/static/index.html +2 -2
- package/static/assets/index-uyccX96d.css +0 -33
|
@@ -2,7 +2,11 @@ import { AuthConfig, LocalAuthUser, AuthSession, ApiKey, UserRole } from '@lovel
|
|
|
2
2
|
export declare class AuthManager {
|
|
3
3
|
private authConfigPath;
|
|
4
4
|
private authConfig;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Create an AuthManager instance.
|
|
7
|
+
* @param configPath - Optional custom path to auth.json. If not provided, uses OS app data directory.
|
|
8
|
+
*/
|
|
9
|
+
constructor(configPath?: string);
|
|
6
10
|
/**
|
|
7
11
|
* Check if auth is enabled
|
|
8
12
|
*/
|
|
@@ -119,4 +123,8 @@ export declare class AuthManager {
|
|
|
119
123
|
*/
|
|
120
124
|
clearCache(): void;
|
|
121
125
|
}
|
|
122
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Get the singleton AuthManager instance.
|
|
128
|
+
* @param configPath - Optional custom path to auth.json. If not provided, uses OS app data directory.
|
|
129
|
+
*/
|
|
130
|
+
export declare function getAuthManager(configPath?: string): AuthManager;
|
|
@@ -3,14 +3,19 @@ import path from 'path';
|
|
|
3
3
|
import bcrypt from 'bcrypt';
|
|
4
4
|
import jwt from 'jsonwebtoken';
|
|
5
5
|
import crypto from 'crypto';
|
|
6
|
+
import { getAuthConfigPath } from '@lovelybunch/core';
|
|
6
7
|
const SALT_ROUNDS = 10;
|
|
7
8
|
const DEFAULT_SESSION_EXPIRY = '7d';
|
|
8
9
|
const DEFAULT_COOKIE_NAME = 'nut-session';
|
|
9
10
|
export class AuthManager {
|
|
10
11
|
authConfigPath;
|
|
11
12
|
authConfig = null;
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Create an AuthManager instance.
|
|
15
|
+
* @param configPath - Optional custom path to auth.json. If not provided, uses OS app data directory.
|
|
16
|
+
*/
|
|
17
|
+
constructor(configPath) {
|
|
18
|
+
this.authConfigPath = configPath ?? getAuthConfigPath();
|
|
14
19
|
}
|
|
15
20
|
/**
|
|
16
21
|
* Check if auth is enabled
|
|
@@ -45,6 +50,9 @@ export class AuthManager {
|
|
|
45
50
|
* Save auth config to file
|
|
46
51
|
*/
|
|
47
52
|
async saveAuthConfig(config) {
|
|
53
|
+
// Ensure the directory exists
|
|
54
|
+
const dir = path.dirname(this.authConfigPath);
|
|
55
|
+
await fs.mkdir(dir, { recursive: true });
|
|
48
56
|
await fs.writeFile(this.authConfigPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
49
57
|
this.authConfig = config;
|
|
50
58
|
}
|
|
@@ -402,10 +410,13 @@ export class AuthManager {
|
|
|
402
410
|
}
|
|
403
411
|
// Singleton instance
|
|
404
412
|
let authManagerInstance = null;
|
|
405
|
-
|
|
413
|
+
/**
|
|
414
|
+
* Get the singleton AuthManager instance.
|
|
415
|
+
* @param configPath - Optional custom path to auth.json. If not provided, uses OS app data directory.
|
|
416
|
+
*/
|
|
417
|
+
export function getAuthManager(configPath) {
|
|
406
418
|
if (!authManagerInstance) {
|
|
407
|
-
|
|
408
|
-
authManagerInstance = new AuthManager(path);
|
|
419
|
+
authManagerInstance = new AuthManager(configPath);
|
|
409
420
|
}
|
|
410
421
|
return authManagerInstance;
|
|
411
422
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get environment variables to inject into spawned child processes
|
|
3
|
+
* These are the API keys that coding agents (Claude Code, Codex, Gemini CLI) will use
|
|
4
|
+
* Returns a full environment object with process.env merged with API keys
|
|
5
|
+
*/
|
|
6
|
+
export declare function getInjectedEnv(): Record<string, string>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
/**
|
|
5
|
+
* Get the path to the global config file
|
|
6
|
+
*/
|
|
7
|
+
function getGlobalConfigPath() {
|
|
8
|
+
const platform = process.platform;
|
|
9
|
+
let configDir;
|
|
10
|
+
if (platform === 'win32') {
|
|
11
|
+
configDir = join(process.env.APPDATA || homedir(), 'coconuts');
|
|
12
|
+
}
|
|
13
|
+
else if (platform === 'darwin') {
|
|
14
|
+
configDir = join(homedir(), 'Library', 'Application Support', 'coconuts');
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
// Linux/Unix
|
|
18
|
+
configDir = join(process.env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'coconuts');
|
|
19
|
+
}
|
|
20
|
+
return join(configDir, 'config.json');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load global config from OS-specific location
|
|
24
|
+
*/
|
|
25
|
+
function loadGlobalConfig() {
|
|
26
|
+
const configPath = getGlobalConfigPath();
|
|
27
|
+
if (!existsSync(configPath)) {
|
|
28
|
+
return { apiKeys: {}, defaults: {} };
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
32
|
+
return JSON.parse(content);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.warn('Warning: Could not parse global config file, using defaults');
|
|
36
|
+
return { apiKeys: {}, defaults: {} };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get environment variables to inject into spawned child processes
|
|
41
|
+
* These are the API keys that coding agents (Claude Code, Codex, Gemini CLI) will use
|
|
42
|
+
* Returns a full environment object with process.env merged with API keys
|
|
43
|
+
*/
|
|
44
|
+
export function getInjectedEnv() {
|
|
45
|
+
const config = loadGlobalConfig();
|
|
46
|
+
const env = { ...process.env };
|
|
47
|
+
// Only inject keys that are actually configured
|
|
48
|
+
if (config.apiKeys?.anthropic) {
|
|
49
|
+
env.ANTHROPIC_API_KEY = config.apiKeys.anthropic;
|
|
50
|
+
}
|
|
51
|
+
if (config.apiKeys?.openai) {
|
|
52
|
+
env.OPENAI_API_KEY = config.apiKeys.openai;
|
|
53
|
+
}
|
|
54
|
+
if (config.apiKeys?.gemini) {
|
|
55
|
+
env.GEMINI_API_KEY = config.apiKeys.gemini;
|
|
56
|
+
}
|
|
57
|
+
if (config.apiKeys?.replicate) {
|
|
58
|
+
env.REPLICATE_API_TOKEN = config.apiKeys.replicate;
|
|
59
|
+
}
|
|
60
|
+
if (config.apiKeys?.factorydroid) {
|
|
61
|
+
env.FACTORY_API_KEY = config.apiKeys.factorydroid;
|
|
62
|
+
}
|
|
63
|
+
return env;
|
|
64
|
+
}
|
package/dist/lib/git.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare function getCredentialConfig(): Promise<{
|
|
|
44
44
|
origin?: string;
|
|
45
45
|
}>;
|
|
46
46
|
export declare function setRemoteUrl(remoteUrl: string): Promise<void>;
|
|
47
|
+
export declare function removeRemote(): Promise<void>;
|
|
47
48
|
export declare function storeCredentials(username: string, password: string, remoteUrl?: string): Promise<void>;
|
|
48
49
|
export interface WorktreeInfo {
|
|
49
50
|
name: string;
|
package/dist/lib/git.js
CHANGED
|
@@ -158,7 +158,8 @@ export async function pushCurrent() {
|
|
|
158
158
|
console.error('[git] Error reading GitHub token:', tokenError);
|
|
159
159
|
}
|
|
160
160
|
console.log('[git] Executing git push...');
|
|
161
|
-
|
|
161
|
+
// Use -u to set upstream tracking if not already set (harmless if already configured)
|
|
162
|
+
const { stdout } = await runGit(['push', '-u', 'origin', 'HEAD'], { timeout: 30000 }); // 30 second timeout for push
|
|
162
163
|
console.log('[git] Push completed successfully');
|
|
163
164
|
return stdout;
|
|
164
165
|
}
|
|
@@ -257,6 +258,18 @@ export async function setRemoteUrl(remoteUrl) {
|
|
|
257
258
|
await runGit(['remote', 'add', 'origin', trimmed]);
|
|
258
259
|
}
|
|
259
260
|
}
|
|
261
|
+
export async function removeRemote() {
|
|
262
|
+
// Check if origin exists
|
|
263
|
+
try {
|
|
264
|
+
await runGit(['config', '--get', 'remote.origin.url']);
|
|
265
|
+
// If we get here, origin exists, so remove it
|
|
266
|
+
await runGit(['remote', 'remove', 'origin']);
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
// Origin doesn't exist, nothing to do
|
|
270
|
+
throw new Error('No remote configured');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
260
273
|
export async function storeCredentials(username, password, remoteUrl) {
|
|
261
274
|
// If a remote URL is provided, set it first
|
|
262
275
|
if (remoteUrl && remoteUrl.trim()) {
|
|
@@ -3,6 +3,7 @@ import { createWriteStream } from 'fs';
|
|
|
3
3
|
import { promises as fs } from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { getProjectRoot } from '../project-paths.js';
|
|
6
|
+
import { getInjectedEnv } from '../env-injection.js';
|
|
6
7
|
function shellQuote(value) {
|
|
7
8
|
if (value === '')
|
|
8
9
|
return "''";
|
|
@@ -16,6 +17,8 @@ function resolveAgent(model) {
|
|
|
16
17
|
return 'gemini';
|
|
17
18
|
if (lower.includes('codex') || lower.includes('gpt') || lower.includes('openai'))
|
|
18
19
|
return 'codex';
|
|
20
|
+
if (lower.includes('droid') || lower.includes('factory'))
|
|
21
|
+
return 'droid';
|
|
19
22
|
return 'claude';
|
|
20
23
|
}
|
|
21
24
|
function buildCommand(agent, instruction, config) {
|
|
@@ -42,6 +45,15 @@ function buildCommand(agent, instruction, config) {
|
|
|
42
45
|
: baseCmd;
|
|
43
46
|
break;
|
|
44
47
|
}
|
|
48
|
+
case 'droid': {
|
|
49
|
+
// For Factory Droid, use exec mode with --auto high for non-interactive scheduled jobs
|
|
50
|
+
// See: https://docs.factory.ai/reference/cli-reference#autonomy-levels
|
|
51
|
+
const mcpFlags = config.mcpServers && config.mcpServers.length > 0
|
|
52
|
+
? config.mcpServers.map(server => `--mcp ${shellQuote(server)}`).join(' ')
|
|
53
|
+
: '';
|
|
54
|
+
mainCommand = `droid exec --auto high ${mcpFlags} ${quotedInstruction}`.trim();
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
45
57
|
case 'claude':
|
|
46
58
|
default: {
|
|
47
59
|
// Claude uses .mcp.json for MCP server configuration (no --mcp flag)
|
|
@@ -55,12 +67,14 @@ function buildCommand(agent, instruction, config) {
|
|
|
55
67
|
const CLI_AGENT_LABEL = {
|
|
56
68
|
claude: 'Claude',
|
|
57
69
|
gemini: 'Gemini',
|
|
58
|
-
codex: '
|
|
70
|
+
codex: 'Codex',
|
|
71
|
+
droid: 'Factory Droid'
|
|
59
72
|
};
|
|
60
73
|
const CLI_AGENT_BINARY = {
|
|
61
74
|
claude: 'claude',
|
|
62
75
|
gemini: 'gemini',
|
|
63
|
-
codex: 'codex'
|
|
76
|
+
codex: 'codex',
|
|
77
|
+
droid: 'droid'
|
|
64
78
|
};
|
|
65
79
|
const DEFAULT_MAX_RUNTIME_MS = 30 * 60 * 1000; // 30 minutes
|
|
66
80
|
function getMaxRuntime() {
|
|
@@ -223,9 +237,14 @@ export class JobRunner {
|
|
|
223
237
|
});
|
|
224
238
|
return;
|
|
225
239
|
}
|
|
240
|
+
// Inject API keys from global config into child process environment
|
|
241
|
+
const injectedEnv = getInjectedEnv();
|
|
226
242
|
const child = spawn('bash', ['-lc', shellCommand], {
|
|
227
243
|
cwd: projectRoot,
|
|
228
|
-
env:
|
|
244
|
+
env: {
|
|
245
|
+
...process.env,
|
|
246
|
+
...injectedEnv
|
|
247
|
+
},
|
|
229
248
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
230
249
|
});
|
|
231
250
|
const maxRuntime = getMaxRuntime();
|
|
@@ -5,6 +5,7 @@ import fs from 'fs';
|
|
|
5
5
|
import { createInitScript } from './context-helper.js';
|
|
6
6
|
import { getShellPath, getShellArgs, prepareShellInit } from './shell-utils.js';
|
|
7
7
|
import { getLogger, AgentKinds } from '@lovelybunch/core/logging';
|
|
8
|
+
import { getInjectedEnv } from '../env-injection.js';
|
|
8
9
|
export class TerminalManager {
|
|
9
10
|
sessions = new Map();
|
|
10
11
|
cleanupInterval;
|
|
@@ -82,9 +83,9 @@ export class TerminalManager {
|
|
|
82
83
|
}
|
|
83
84
|
// Get shell arguments
|
|
84
85
|
const shellArgs = getShellArgs(shellPath, initScriptPath);
|
|
85
|
-
// Prepare environment variables
|
|
86
|
+
// Prepare environment variables with API keys injected
|
|
86
87
|
const env = {
|
|
87
|
-
...
|
|
88
|
+
...getInjectedEnv(),
|
|
88
89
|
COCONUT_PROPOSAL_ID: proposalId,
|
|
89
90
|
COCONUT_CONTEXT_PATH: path.join(projectRoot, '.nut', 'context'),
|
|
90
91
|
COCONUT_PROPOSAL_PATH: path.join(projectRoot, '.nut', 'proposals', `${proposalId}.md`),
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
|
-
import { GET, PUT, TEST
|
|
2
|
+
import { GET, PUT, TEST } from './route.js';
|
|
3
3
|
const config = new Hono();
|
|
4
4
|
config.get('/', GET);
|
|
5
5
|
config.put('/', PUT);
|
|
6
6
|
config.post('/test', TEST);
|
|
7
|
-
config.post('/export-to-env', EXPORT);
|
|
8
7
|
export default config;
|
|
@@ -30,14 +30,3 @@ export declare function TEST(c: Context): Promise<(Response & import("hono").Typ
|
|
|
30
30
|
success: false;
|
|
31
31
|
message: string;
|
|
32
32
|
}, 501, "json">)>;
|
|
33
|
-
export declare function EXPORT(c: Context): Promise<(Response & import("hono").TypedResponse<{
|
|
34
|
-
success: false;
|
|
35
|
-
message: string;
|
|
36
|
-
}, 400, "json">) | (Response & import("hono").TypedResponse<{
|
|
37
|
-
success: false;
|
|
38
|
-
message: string;
|
|
39
|
-
}, 500, "json">) | (Response & import("hono").TypedResponse<{
|
|
40
|
-
success: true;
|
|
41
|
-
message: string;
|
|
42
|
-
envPath: string;
|
|
43
|
-
}, import("hono/utils/http-status").ContentfulStatusCode, "json">)>;
|
|
@@ -233,114 +233,30 @@ export async function TEST(c) {
|
|
|
233
233
|
return c.json({ success: false, message: err instanceof Error ? err.message : 'Network error' }, 200);
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
baseten: 'BASETEN_API_KEY',
|
|
252
|
-
fireworks: 'FIREWORKS_API_KEY',
|
|
253
|
-
deepinfra: 'DEEPINFRA_API_KEY'
|
|
254
|
-
};
|
|
255
|
-
// Helper to find workspace root (where .env should be written)
|
|
256
|
-
async function findWorkspaceRoot() {
|
|
257
|
-
// Try to find .nut directory first
|
|
258
|
-
let currentDir = process.cwd();
|
|
259
|
-
while (currentDir !== path.parse(currentDir).root) {
|
|
260
|
-
const nutPath = path.join(currentDir, '.nut');
|
|
261
|
-
try {
|
|
262
|
-
await fs.access(nutPath);
|
|
263
|
-
// Found .nut directory, return parent as workspace root
|
|
264
|
-
return currentDir;
|
|
265
|
-
}
|
|
266
|
-
catch {
|
|
267
|
-
currentDir = path.dirname(currentDir);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
// Fallback to cwd if no .nut found
|
|
271
|
-
return process.cwd();
|
|
272
|
-
}
|
|
273
|
-
// POST /api/v1/config/export-to-env
|
|
274
|
-
// Body: { provider: string }
|
|
275
|
-
// Exports the saved API key for a provider to a .env file in workspace root
|
|
276
|
-
export async function EXPORT(c) {
|
|
277
|
-
try {
|
|
278
|
-
const body = await c.req.json();
|
|
279
|
-
const provider = (body?.provider || '').toString();
|
|
280
|
-
if (!provider) {
|
|
281
|
-
return c.json({ success: false, message: 'Missing provider' }, 400);
|
|
282
|
-
}
|
|
283
|
-
const envVarName = PROVIDER_ENV_VARS[provider];
|
|
284
|
-
if (!envVarName) {
|
|
285
|
-
return c.json({ success: false, message: `Unknown provider: ${provider}` }, 400);
|
|
286
|
-
}
|
|
287
|
-
// Load the API key from global config
|
|
288
|
-
const configPath = await getGlobalConfigPath();
|
|
289
|
-
let config = { apiKeys: {}, defaults: {} };
|
|
290
|
-
try {
|
|
291
|
-
const content = await fs.readFile(configPath, 'utf-8');
|
|
292
|
-
config = JSON.parse(content);
|
|
293
|
-
}
|
|
294
|
-
catch {
|
|
295
|
-
// Config doesn't exist yet
|
|
296
|
-
}
|
|
297
|
-
const apiKey = config.apiKeys?.[provider];
|
|
298
|
-
if (!apiKey) {
|
|
299
|
-
return c.json({ success: false, message: `No API key configured for ${provider}` }, 400);
|
|
300
|
-
}
|
|
301
|
-
// Find workspace root
|
|
302
|
-
const workspaceRoot = await findWorkspaceRoot();
|
|
303
|
-
if (!workspaceRoot) {
|
|
304
|
-
return c.json({ success: false, message: 'Could not find workspace root' }, 500);
|
|
305
|
-
}
|
|
306
|
-
const envPath = path.join(workspaceRoot, '.env');
|
|
307
|
-
// Read existing .env file if it exists
|
|
308
|
-
let envContent = '';
|
|
309
|
-
try {
|
|
310
|
-
envContent = await fs.readFile(envPath, 'utf-8');
|
|
311
|
-
}
|
|
312
|
-
catch {
|
|
313
|
-
// File doesn't exist, will create new
|
|
314
|
-
}
|
|
315
|
-
// Parse existing env vars
|
|
316
|
-
const envLines = envContent.split('\n');
|
|
317
|
-
let found = false;
|
|
318
|
-
const updatedLines = envLines.map(line => {
|
|
319
|
-
const trimmed = line.trim();
|
|
320
|
-
if (trimmed.startsWith(envVarName + '=') || trimmed.startsWith(envVarName + ' =')) {
|
|
321
|
-
found = true;
|
|
322
|
-
return `${envVarName}=${apiKey}`;
|
|
236
|
+
if (provider === 'replicate') {
|
|
237
|
+
try {
|
|
238
|
+
// Test Replicate API by listing models (lightweight endpoint)
|
|
239
|
+
const resp = await fetch('https://api.replicate.com/v1/models', {
|
|
240
|
+
method: 'GET',
|
|
241
|
+
headers: {
|
|
242
|
+
'Authorization': `Bearer ${effectiveKey}`,
|
|
243
|
+
'Content-Type': 'application/json',
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
if (!resp.ok) {
|
|
247
|
+
const text = await resp.text();
|
|
248
|
+
return c.json({ success: false, message: `Replicate rejected token: ${text.slice(0, 200)}` }, 200);
|
|
249
|
+
}
|
|
250
|
+
return c.json({ success: true, message: 'Replicate token is valid' });
|
|
323
251
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
// If not found, append to the end
|
|
327
|
-
if (!found) {
|
|
328
|
-
// Add a newline if the file doesn't end with one
|
|
329
|
-
if (updatedLines.length > 0 && updatedLines[updatedLines.length - 1] !== '') {
|
|
330
|
-
updatedLines.push('');
|
|
252
|
+
catch (err) {
|
|
253
|
+
return c.json({ success: false, message: err instanceof Error ? err.message : 'Network error' }, 200);
|
|
331
254
|
}
|
|
332
|
-
updatedLines.push(`${envVarName}=${apiKey}`);
|
|
333
255
|
}
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
return c.json({
|
|
337
|
-
success: true,
|
|
338
|
-
message: `${envVarName} saved to .env file`,
|
|
339
|
-
envPath
|
|
340
|
-
});
|
|
256
|
+
// Other providers not wired up yet
|
|
257
|
+
return c.json({ success: false, message: `Provider '${provider}' test not implemented yet` }, 501);
|
|
341
258
|
}
|
|
342
259
|
catch (error) {
|
|
343
|
-
|
|
344
|
-
return c.json({ success: false, message: error instanceof Error ? error.message : 'Failed to export to env' }, 500);
|
|
260
|
+
return c.json({ success: false, message: 'Invalid request body' }, 400);
|
|
345
261
|
}
|
|
346
262
|
}
|
|
@@ -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;
|
|
@@ -111,6 +111,17 @@ app.put('/remote', async (c) => {
|
|
|
111
111
|
return c.json({ success: false, error: { message: e.message } }, 500);
|
|
112
112
|
}
|
|
113
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
|
+
});
|
|
114
125
|
// Store Credentials
|
|
115
126
|
app.post('/credentials', async (c) => {
|
|
116
127
|
try {
|
|
@@ -402,13 +413,19 @@ app.post('/discard', async (c) => {
|
|
|
402
413
|
const statusCode = fileChange.status.trim();
|
|
403
414
|
// Handle different file statuses
|
|
404
415
|
if (statusCode === '??' || statusCode.includes('U')) {
|
|
405
|
-
// Untracked file - remove it
|
|
406
|
-
const {
|
|
416
|
+
// Untracked file or directory - remove it
|
|
417
|
+
const { rm, stat } = await import('fs/promises');
|
|
407
418
|
const { getRepoRoot } = await import('../../../../lib/git.js');
|
|
408
419
|
const repoRoot = await getRepoRoot();
|
|
409
420
|
const { join } = await import('path');
|
|
410
421
|
const fullPath = join(repoRoot, filePath);
|
|
411
|
-
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
|
+
}
|
|
412
429
|
}
|
|
413
430
|
else if (statusCode.includes('D')) {
|
|
414
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;
|