@lovelybunch/api 1.0.66 → 1.0.68

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 (35) hide show
  1. package/dist/routes/api/v1/events/events.test.d.ts +4 -0
  2. package/dist/routes/api/v1/events/events.test.js +289 -0
  3. package/dist/routes/api/v1/events/index.d.ts +7 -0
  4. package/dist/routes/api/v1/events/index.js +19 -0
  5. package/dist/routes/api/v1/events/purge/route.d.ts +19 -0
  6. package/dist/routes/api/v1/events/purge/route.js +62 -0
  7. package/dist/routes/api/v1/events/route.d.ts +30 -0
  8. package/dist/routes/api/v1/events/route.js +109 -0
  9. package/dist/routes/api/v1/events/status/route.d.ts +20 -0
  10. package/dist/routes/api/v1/events/status/route.js +53 -0
  11. package/dist/routes/api/v1/events/stream/route.d.ts +9 -0
  12. package/dist/routes/api/v1/events/stream/route.js +132 -0
  13. package/dist/routes/api/v1/init/index.d.ts +1 -0
  14. package/dist/routes/api/v1/init/index.js +1 -0
  15. package/dist/routes/api/v1/init/route.d.ts +3 -0
  16. package/dist/routes/api/v1/init/route.js +129 -0
  17. package/dist/routes/api/v1/onboard/index.d.ts +3 -0
  18. package/dist/routes/api/v1/onboard/index.js +8 -0
  19. package/dist/routes/api/v1/onboard/route.d.ts +13 -0
  20. package/dist/routes/api/v1/onboard/route.js +311 -0
  21. package/dist/routes/api/v1/onboarding/check/index.d.ts +3 -0
  22. package/dist/routes/api/v1/onboarding/check/index.js +5 -0
  23. package/dist/routes/api/v1/onboarding/check/route.d.ts +12 -0
  24. package/dist/routes/api/v1/onboarding/check/route.js +24 -0
  25. package/dist/routes/api/v1/onboarding/index.d.ts +1 -0
  26. package/dist/routes/api/v1/onboarding/index.js +1 -0
  27. package/dist/routes/api/v1/onboarding/route.d.ts +3 -0
  28. package/dist/routes/api/v1/onboarding/route.js +158 -0
  29. package/dist/routes/api/v1/proposals/[id]/route.js +57 -0
  30. package/dist/routes/api/v1/proposals/route.js +18 -0
  31. package/dist/server-with-static.js +62 -0
  32. package/dist/server.js +63 -0
  33. package/package.json +4 -4
  34. package/static/assets/{index-DuLX7Zvh.js → index-COf7Bc1u.js} +33 -33
  35. package/static/index.html +1 -1
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Events streaming endpoint for real-time tailing
3
+ */
4
+ import { streamSSE } from "hono/streaming";
5
+ import { promises as fs } from "fs";
6
+ import * as fsSync from "fs";
7
+ import path from "path";
8
+ import { findGaitDirectory } from "../../../../../lib/gait-path.js";
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
+ /**
20
+ * GET /api/v1/events/stream?since_seq=
21
+ * Server-Sent Events stream for near real-time tails
22
+ */
23
+ export async function GET(c) {
24
+ const eventsDir = await getEventsDir();
25
+ if (!eventsDir) {
26
+ return c.json({ error: "Events directory not found" }, 404);
27
+ }
28
+ const url = new URL(c.req.url);
29
+ const sinceSeq = parseInt(url.searchParams.get("since_seq") || "0", 10);
30
+ const currentFile = path.join(eventsDir, "events-current.jsonl");
31
+ return streamSSE(c, async (stream) => {
32
+ let lastSeq = sinceSeq;
33
+ let running = true;
34
+ // Send initial events from file
35
+ try {
36
+ const fileExists = await fs
37
+ .access(currentFile)
38
+ .then(() => true)
39
+ .catch(() => false);
40
+ if (fileExists) {
41
+ const fileStream = fsSync.createReadStream(currentFile);
42
+ const rl = readline.createInterface({
43
+ input: fileStream,
44
+ crlfDelay: Infinity,
45
+ });
46
+ for await (const line of rl) {
47
+ if (!line.trim())
48
+ continue;
49
+ try {
50
+ const event = JSON.parse(line);
51
+ if (event.seq > sinceSeq) {
52
+ await stream.writeSSE({
53
+ data: JSON.stringify(event),
54
+ });
55
+ lastSeq = Math.max(lastSeq, event.seq);
56
+ }
57
+ }
58
+ catch (err) {
59
+ // Skip malformed lines
60
+ continue;
61
+ }
62
+ }
63
+ }
64
+ }
65
+ catch (error) {
66
+ // Ignore errors during initial load
67
+ }
68
+ // Poll for new events
69
+ // In a production implementation, this would use file watching (e.g., chokidar)
70
+ // For now, we'll poll every 1 second
71
+ const pollInterval = setInterval(async () => {
72
+ try {
73
+ const fileExists = await fs
74
+ .access(currentFile)
75
+ .then(() => true)
76
+ .catch(() => false);
77
+ if (!fileExists)
78
+ return;
79
+ const fileStream = fsSync.createReadStream(currentFile);
80
+ const rl = readline.createInterface({
81
+ input: fileStream,
82
+ crlfDelay: Infinity,
83
+ });
84
+ for await (const line of rl) {
85
+ if (!line.trim())
86
+ continue;
87
+ try {
88
+ const event = JSON.parse(line);
89
+ if (event.seq > lastSeq) {
90
+ await stream.writeSSE({
91
+ data: JSON.stringify(event),
92
+ });
93
+ lastSeq = event.seq;
94
+ }
95
+ }
96
+ catch (err) {
97
+ // Skip malformed lines
98
+ continue;
99
+ }
100
+ }
101
+ }
102
+ catch (error) {
103
+ // Continue polling even on errors
104
+ }
105
+ }, 1000);
106
+ // Send heartbeat keepalive every 15 seconds
107
+ const heartbeatInterval = setInterval(async () => {
108
+ try {
109
+ // Send empty event to keep connection alive
110
+ await stream.writeSSE({
111
+ data: "",
112
+ event: "heartbeat",
113
+ });
114
+ }
115
+ catch (error) {
116
+ clearInterval(heartbeatInterval);
117
+ clearInterval(pollInterval);
118
+ running = false;
119
+ }
120
+ }, 15000);
121
+ // Clean up on disconnect
122
+ c.req.raw.signal.addEventListener("abort", () => {
123
+ clearInterval(pollInterval);
124
+ clearInterval(heartbeatInterval);
125
+ running = false;
126
+ });
127
+ // Keep stream alive
128
+ while (running) {
129
+ await new Promise((resolve) => setTimeout(resolve, 1000));
130
+ }
131
+ });
132
+ }
@@ -0,0 +1 @@
1
+ export { default } from './route.js';
@@ -0,0 +1 @@
1
+ export { default } from './route.js';
@@ -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,129 @@
1
+ import { Hono } from 'hono';
2
+ import { fileStorage } from '@lovelybunch/core';
3
+ import { promisify } from 'util';
4
+ import { exec } from 'child_process';
5
+ const execAsync = promisify(exec);
6
+ const app = new Hono();
7
+ // Check if Coconut is initialized
8
+ app.get('/', async (c) => {
9
+ try {
10
+ const isInitialized = await fileStorage.isInitialized();
11
+ return c.json({ initialized: isInitialized });
12
+ }
13
+ catch (error) {
14
+ console.error('Error checking initialization status:', error);
15
+ return c.json({ error: 'Failed to check initialization status' }, 500);
16
+ }
17
+ });
18
+ // Initialize Coconut
19
+ app.post('/', async (c) => {
20
+ try {
21
+ const body = await c.req.json();
22
+ const { name, description, initGit = false } = body;
23
+ if (!name || !description) {
24
+ return c.json({ error: 'Name and description are required' }, 400);
25
+ }
26
+ // Check if already initialized
27
+ const isInitialized = await fileStorage.isInitialized();
28
+ if (isInitialized) {
29
+ return c.json({ error: 'Coconut is already initialized' }, 400);
30
+ }
31
+ // Create configuration
32
+ const config = {
33
+ version: '1.0.0',
34
+ repository: {
35
+ name,
36
+ description,
37
+ },
38
+ policies: {
39
+ requireApproval: true,
40
+ minApprovers: 1,
41
+ allowSelfApproval: false,
42
+ autoMerge: false,
43
+ },
44
+ storage: {
45
+ type: 'file',
46
+ path: '.nut',
47
+ },
48
+ };
49
+ // Initialize storage
50
+ await fileStorage.init();
51
+ await fileStorage.saveConfig(config);
52
+ // Initialize git if requested
53
+ if (initGit) {
54
+ try {
55
+ const cwd = process.cwd();
56
+ // Check if git is already initialized
57
+ try {
58
+ await execAsync('git rev-parse --git-dir', { cwd });
59
+ // Git is already initialized, skip
60
+ }
61
+ catch {
62
+ // Git is not initialized, initialize it
63
+ await execAsync('git init', { cwd });
64
+ await execAsync('git add .', { cwd });
65
+ }
66
+ }
67
+ catch (gitError) {
68
+ console.error('Git initialization failed:', gitError);
69
+ // Don't fail the whole operation if git fails
70
+ return c.json({
71
+ success: true,
72
+ message: 'Coconut initialized successfully, but git initialization failed',
73
+ gitError: gitError instanceof Error ? gitError.message : 'Unknown error',
74
+ });
75
+ }
76
+ }
77
+ return c.json({
78
+ success: true,
79
+ message: 'Coconut initialized successfully',
80
+ config,
81
+ });
82
+ }
83
+ catch (error) {
84
+ console.error('Error initializing Coconut:', error);
85
+ return c.json({
86
+ error: 'Failed to initialize Coconut',
87
+ details: error instanceof Error ? error.message : 'Unknown error',
88
+ }, 500);
89
+ }
90
+ });
91
+ // Clone a repository
92
+ app.post('/clone', async (c) => {
93
+ try {
94
+ const body = await c.req.json();
95
+ const { url } = body;
96
+ if (!url) {
97
+ return c.json({ error: 'Repository URL is required' }, 400);
98
+ }
99
+ // Validate URL format
100
+ const urlPattern = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
101
+ if (!urlPattern.test(url)) {
102
+ return c.json({ error: 'Invalid repository URL' }, 400);
103
+ }
104
+ const cwd = process.cwd();
105
+ try {
106
+ // Clone into current directory
107
+ await execAsync(`git clone ${url} .`, { cwd });
108
+ return c.json({
109
+ success: true,
110
+ message: 'Repository cloned successfully',
111
+ });
112
+ }
113
+ catch (cloneError) {
114
+ console.error('Git clone failed:', cloneError);
115
+ return c.json({
116
+ error: 'Failed to clone repository',
117
+ details: cloneError instanceof Error ? cloneError.message : 'Unknown error',
118
+ }, 500);
119
+ }
120
+ }
121
+ catch (error) {
122
+ console.error('Error cloning repository:', error);
123
+ return c.json({
124
+ error: 'Failed to clone repository',
125
+ details: error instanceof Error ? error.message : 'Unknown error',
126
+ }, 500);
127
+ }
128
+ });
129
+ export default app;
@@ -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,8 @@
1
+ import { Hono } from 'hono';
2
+ import * as route from './route.js';
3
+ const app = new Hono();
4
+ // GET /api/v1/onboard/check - Check if .nut exists and onboarding is needed
5
+ app.get('/check', route.GET_CHECK);
6
+ // POST /api/v1/onboard/initialize - Initialize Coconut (empty or clone)
7
+ app.post('/initialize', route.POST_INITIALIZE);
8
+ export default app;
@@ -0,0 +1,13 @@
1
+ import { Context } from 'hono';
2
+ export declare function GET_CHECK(c: Context): Promise<Response & import("hono").TypedResponse<{
3
+ onboardCheckEnabled: boolean;
4
+ initialized: boolean;
5
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">>;
6
+ export declare function POST_INITIALIZE(c: Context): Promise<(Response & import("hono").TypedResponse<{
7
+ error: string;
8
+ }, 400, "json">) | (Response & import("hono").TypedResponse<{
9
+ success: true;
10
+ message: string;
11
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
12
+ error: any;
13
+ }, 500, "json">)>;
@@ -0,0 +1,311 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { exec } from 'child_process';
4
+ import { promisify } from 'util';
5
+ import { findGaitDirectory } from '../../../../lib/gait-path.js';
6
+ const execAsync = promisify(exec);
7
+ export async function GET_CHECK(c) {
8
+ try {
9
+ // Check if COCONUT_ONBOARD_CHECK environment variable is set
10
+ const onboardCheckEnabled = process.env.COCONUT_ONBOARD_CHECK === 'true';
11
+ // Check if .nut directory exists
12
+ const gaitDir = await findGaitDirectory();
13
+ const initialized = gaitDir !== null;
14
+ return c.json({
15
+ onboardCheckEnabled,
16
+ initialized,
17
+ });
18
+ }
19
+ catch (error) {
20
+ console.error('Error checking onboarding status:', error);
21
+ return c.json({
22
+ onboardCheckEnabled: false,
23
+ initialized: true, // Default to initialized on error to avoid redirecting
24
+ });
25
+ }
26
+ }
27
+ export async function POST_INITIALIZE(c) {
28
+ try {
29
+ const data = await c.req.json();
30
+ // Get the current working directory (where the server was started)
31
+ const cwd = process.env.GAIT_DATA_PATH || process.cwd();
32
+ if (data.type === 'empty') {
33
+ // Create an empty Coconut project
34
+ if (!data.name || data.name.trim().length === 0) {
35
+ return c.json({ error: 'Project name is required' }, 400);
36
+ }
37
+ // Initialize .nut directory
38
+ const nutDir = path.join(cwd, '.nut');
39
+ // Check if .nut already exists
40
+ try {
41
+ await fs.access(nutDir);
42
+ return c.json({ error: '.nut directory already exists' }, 400);
43
+ }
44
+ catch {
45
+ // Directory doesn't exist, which is what we want
46
+ }
47
+ // Create directory structure
48
+ await fs.mkdir(nutDir, { recursive: true });
49
+ await fs.mkdir(path.join(nutDir, 'proposals'), { recursive: true });
50
+ await fs.mkdir(path.join(nutDir, 'agents'), { recursive: true });
51
+ await fs.mkdir(path.join(nutDir, 'context'), { recursive: true });
52
+ await fs.mkdir(path.join(nutDir, 'context', 'knowledge'), { recursive: true });
53
+ await fs.mkdir(path.join(nutDir, 'resources'), { recursive: true });
54
+ await fs.mkdir(path.join(nutDir, 'resources', 'files'), { recursive: true });
55
+ await fs.mkdir(path.join(nutDir, 'resources', 'thumbnails'), { recursive: true });
56
+ await fs.mkdir(path.join(nutDir, 'jobs'), { recursive: true });
57
+ await fs.mkdir(path.join(nutDir, '.schema'), { recursive: true });
58
+ // Create config.json
59
+ const config = {
60
+ version: '1.0',
61
+ project: {
62
+ name: data.name.trim(),
63
+ description: data.description?.trim() || '',
64
+ },
65
+ created: new Date().toISOString(),
66
+ };
67
+ await fs.writeFile(path.join(nutDir, 'config.json'), JSON.stringify(config, null, 2), 'utf-8');
68
+ // Create basic project.md
69
+ const projectMd = `---
70
+ version: '1.0'
71
+ updated: '${new Date().toISOString().split('T')[0]}'
72
+ type: project
73
+ status: active
74
+ category: overview
75
+ project:
76
+ name: ${data.name.trim()}
77
+ description: ${data.description?.trim() || 'A new Coconut project'}
78
+ version: '0.1.0'
79
+ stage: development
80
+ goals:
81
+ primary: Build an amazing product
82
+ metrics: []
83
+ non_goals: []
84
+ ---
85
+ # ${data.name.trim()}
86
+
87
+ ${data.description?.trim() || 'Welcome to your new Coconut project!'}
88
+
89
+ ## Getting Started
90
+
91
+ This project was initialized with Coconut. Start by creating change proposals and working with AI agents to build your product.
92
+ `;
93
+ await fs.writeFile(path.join(nutDir, 'context', 'project.md'), projectMd, 'utf-8');
94
+ // Create basic architecture.md
95
+ const architectureMd = `---
96
+ version: '1.0'
97
+ updated: '${new Date().toISOString().split('T')[0]}'
98
+ type: architecture
99
+ category: design
100
+ stack:
101
+ runtime: node
102
+ framework: ''
103
+ language: typescript
104
+ database: ''
105
+ deployment: ''
106
+ ---
107
+ # Architecture
108
+
109
+ Document your system architecture here.
110
+
111
+ ## Stack
112
+
113
+ Describe your technology stack and key architectural decisions.
114
+
115
+ ## Components
116
+
117
+ Outline the major components of your system.
118
+ `;
119
+ await fs.writeFile(path.join(nutDir, 'context', 'architecture.md'), architectureMd, 'utf-8');
120
+ // Initialize git repository
121
+ try {
122
+ await execAsync('git init', { cwd });
123
+ // Create .gitignore if it doesn't exist
124
+ const gitignorePath = path.join(cwd, '.gitignore');
125
+ let gitignoreContent = '';
126
+ try {
127
+ gitignoreContent = await fs.readFile(gitignorePath, 'utf-8');
128
+ }
129
+ catch {
130
+ // File doesn't exist, create it
131
+ }
132
+ // Add .nut/auth.json to .gitignore if not already there
133
+ if (!gitignoreContent.includes('.nut/auth.json')) {
134
+ gitignoreContent += '\n# Coconut authentication (contains secrets)\n.nut/auth.json\n';
135
+ await fs.writeFile(gitignorePath, gitignoreContent, 'utf-8');
136
+ }
137
+ }
138
+ catch (error) {
139
+ console.error('Failed to initialize git repository:', error);
140
+ // Continue even if git init fails
141
+ }
142
+ return c.json({
143
+ success: true,
144
+ message: `Coconut project "${data.name}" initialized successfully!`,
145
+ });
146
+ }
147
+ else if (data.type === 'clone') {
148
+ // Clone a repository
149
+ if (!data.repoUrl || data.repoUrl.trim().length === 0) {
150
+ return c.json({ error: 'Repository URL is required' }, 400);
151
+ }
152
+ // Validate that the current directory is empty or only has .git
153
+ const files = await fs.readdir(cwd);
154
+ const nonGitFiles = files.filter(f => f !== '.git' && f !== '.gitignore');
155
+ if (nonGitFiles.length > 0) {
156
+ return c.json({
157
+ error: 'Current directory is not empty. Please run this in an empty directory or remove existing files.'
158
+ }, 400);
159
+ }
160
+ // Clone the repository into current directory
161
+ try {
162
+ await execAsync(`git clone "${data.repoUrl.trim()}" .`, { cwd });
163
+ }
164
+ catch (error) {
165
+ console.error('Failed to clone repository:', error);
166
+ return c.json({
167
+ error: `Failed to clone repository: ${error.message}`
168
+ }, 500);
169
+ }
170
+ // Initialize .nut directory (similar to empty project but without git init)
171
+ const nutDir = path.join(cwd, '.nut');
172
+ // Check if .nut already exists in the cloned repo
173
+ try {
174
+ await fs.access(nutDir);
175
+ return c.json({
176
+ success: true,
177
+ message: 'Repository cloned successfully. Coconut is already initialized in this project.',
178
+ });
179
+ }
180
+ catch {
181
+ // Directory doesn't exist, initialize it
182
+ }
183
+ // Create directory structure
184
+ await fs.mkdir(nutDir, { recursive: true });
185
+ await fs.mkdir(path.join(nutDir, 'proposals'), { recursive: true });
186
+ await fs.mkdir(path.join(nutDir, 'agents'), { recursive: true });
187
+ await fs.mkdir(path.join(nutDir, 'context'), { recursive: true });
188
+ await fs.mkdir(path.join(nutDir, 'context', 'knowledge'), { recursive: true });
189
+ await fs.mkdir(path.join(nutDir, 'resources'), { recursive: true });
190
+ await fs.mkdir(path.join(nutDir, 'resources', 'files'), { recursive: true });
191
+ await fs.mkdir(path.join(nutDir, 'resources', 'thumbnails'), { recursive: true });
192
+ await fs.mkdir(path.join(nutDir, 'jobs'), { recursive: true });
193
+ await fs.mkdir(path.join(nutDir, '.schema'), { recursive: true });
194
+ // Try to extract project name from package.json if it exists
195
+ let projectName = 'Cloned Project';
196
+ let projectDescription = 'A project cloned from a Git repository';
197
+ try {
198
+ const packageJsonPath = path.join(cwd, 'package.json');
199
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
200
+ if (packageJson.name) {
201
+ projectName = packageJson.name;
202
+ }
203
+ if (packageJson.description) {
204
+ projectDescription = packageJson.description;
205
+ }
206
+ }
207
+ catch {
208
+ // package.json doesn't exist or couldn't be read
209
+ }
210
+ // Create config.json
211
+ const config = {
212
+ version: '1.0',
213
+ project: {
214
+ name: projectName,
215
+ description: projectDescription,
216
+ clonedFrom: data.repoUrl.trim(),
217
+ },
218
+ created: new Date().toISOString(),
219
+ };
220
+ await fs.writeFile(path.join(nutDir, 'config.json'), JSON.stringify(config, null, 2), 'utf-8');
221
+ // Create basic project.md
222
+ const projectMd = `---
223
+ version: '1.0'
224
+ updated: '${new Date().toISOString().split('T')[0]}'
225
+ type: project
226
+ status: active
227
+ category: overview
228
+ project:
229
+ name: ${projectName}
230
+ description: ${projectDescription}
231
+ version: '0.1.0'
232
+ stage: development
233
+ repository: ${data.repoUrl.trim()}
234
+ goals:
235
+ primary: Continue development with Coconut
236
+ metrics: []
237
+ non_goals: []
238
+ ---
239
+ # ${projectName}
240
+
241
+ ${projectDescription}
242
+
243
+ ## About
244
+
245
+ This project was cloned from: ${data.repoUrl.trim()}
246
+
247
+ ## Getting Started
248
+
249
+ Start by creating change proposals and working with AI agents to enhance this codebase.
250
+ `;
251
+ await fs.writeFile(path.join(nutDir, 'context', 'project.md'), projectMd, 'utf-8');
252
+ // Create basic architecture.md
253
+ const architectureMd = `---
254
+ version: '1.0'
255
+ updated: '${new Date().toISOString().split('T')[0]}'
256
+ type: architecture
257
+ category: design
258
+ stack:
259
+ runtime: node
260
+ framework: ''
261
+ language: typescript
262
+ database: ''
263
+ deployment: ''
264
+ ---
265
+ # Architecture
266
+
267
+ Document your system architecture here.
268
+
269
+ ## Stack
270
+
271
+ Describe your technology stack and key architectural decisions.
272
+
273
+ ## Components
274
+
275
+ Outline the major components of your system.
276
+ `;
277
+ await fs.writeFile(path.join(nutDir, 'context', 'architecture.md'), architectureMd, 'utf-8');
278
+ // Add .nut/auth.json to .gitignore
279
+ try {
280
+ const gitignorePath = path.join(cwd, '.gitignore');
281
+ let gitignoreContent = '';
282
+ try {
283
+ gitignoreContent = await fs.readFile(gitignorePath, 'utf-8');
284
+ }
285
+ catch {
286
+ // File doesn't exist, create it
287
+ }
288
+ if (!gitignoreContent.includes('.nut/auth.json')) {
289
+ gitignoreContent += '\n# Coconut authentication (contains secrets)\n.nut/auth.json\n';
290
+ await fs.writeFile(gitignorePath, gitignoreContent, 'utf-8');
291
+ }
292
+ }
293
+ catch (error) {
294
+ console.error('Failed to update .gitignore:', error);
295
+ }
296
+ return c.json({
297
+ success: true,
298
+ message: `Repository cloned and Coconut initialized successfully!`,
299
+ });
300
+ }
301
+ else {
302
+ return c.json({ error: 'Invalid onboarding type' }, 400);
303
+ }
304
+ }
305
+ catch (error) {
306
+ console.error('Onboarding initialization error:', error);
307
+ return c.json({
308
+ error: error.message || 'Failed to initialize Coconut'
309
+ }, 500);
310
+ }
311
+ }
@@ -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 { GET } from './route.js';
3
+ const app = new Hono();
4
+ app.get('/', GET);
5
+ export default app;
@@ -0,0 +1,12 @@
1
+ import { Context } from 'hono';
2
+ /**
3
+ * GET /api/v1/onboarding/check
4
+ * Check if .nut directory exists WITHOUT creating it
5
+ */
6
+ export declare function GET(c: Context): Promise<(Response & import("hono").TypedResponse<{
7
+ success: true;
8
+ initialized: boolean;
9
+ }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
10
+ success: false;
11
+ message: any;
12
+ }, 500, "json">)>;
@@ -0,0 +1,24 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ /**
4
+ * GET /api/v1/onboarding/check
5
+ * Check if .nut directory exists WITHOUT creating it
6
+ */
7
+ export async function GET(c) {
8
+ try {
9
+ const cwd = process.env.GAIT_DATA_PATH || process.cwd();
10
+ const nutPath = join(cwd, '.nut');
11
+ const nutExists = existsSync(nutPath);
12
+ return c.json({
13
+ success: true,
14
+ initialized: nutExists,
15
+ });
16
+ }
17
+ catch (error) {
18
+ console.error('Error checking .nut directory:', error);
19
+ return c.json({
20
+ success: false,
21
+ message: error.message || 'Failed to check initialization status',
22
+ }, 500);
23
+ }
24
+ }
@@ -0,0 +1 @@
1
+ export { default } from './route.js';
@@ -0,0 +1 @@
1
+ export { default } from './route.js';
@@ -0,0 +1,3 @@
1
+ import { Hono } from 'hono';
2
+ declare const onboarding: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export default onboarding;