algocoach 0.1.0 → 0.1.2
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/README.md +3 -3
- package/cli/index.ts +42 -11
- package/package.json +1 -1
- package/server/index.ts +13 -2
package/README.md
CHANGED
|
@@ -21,8 +21,8 @@ algocoach start
|
|
|
21
21
|
|
|
22
22
|
## Setup
|
|
23
23
|
|
|
24
|
-
1. Run `algocoach start` — creates
|
|
25
|
-
2. Edit
|
|
24
|
+
1. Run `algocoach start` — creates `~/.algocoach/.env`
|
|
25
|
+
2. Edit `~/.algocoach/.env` — uncomment and set at least one API key:
|
|
26
26
|
|
|
27
27
|
```env
|
|
28
28
|
AI_PROVIDER=google
|
|
@@ -88,7 +88,7 @@ src/ — React frontend (Vite + Tailwind)
|
|
|
88
88
|
## Data
|
|
89
89
|
|
|
90
90
|
- **Database**: `~/.algocoach/data.db` — SQLite, created on first use
|
|
91
|
-
- **Config**:
|
|
91
|
+
- **Config**: `~/.algocoach/.env` — API keys and settings
|
|
92
92
|
|
|
93
93
|
## AI Providers
|
|
94
94
|
|
package/cli/index.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import path from "path"
|
|
3
3
|
import fs from "fs"
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const CONFIG_DIR = path.join(process.env.HOME || process.env.USERPROFILE || ".", ".algocoach")
|
|
6
|
+
const ENV_PATH = path.join(CONFIG_DIR, ".env")
|
|
6
7
|
|
|
7
8
|
function envTemplate() {
|
|
8
9
|
return [
|
|
@@ -23,11 +24,27 @@ function envTemplate() {
|
|
|
23
24
|
].join("\n") + "\n"
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
function loadEnv() {
|
|
28
|
+
if (!fs.existsSync(ENV_PATH)) return
|
|
29
|
+
const lines = fs.readFileSync(ENV_PATH, "utf-8").split("\n")
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const trimmed = line.trim()
|
|
32
|
+
if (!trimmed || trimmed.startsWith("#")) continue
|
|
33
|
+
const eqIdx = trimmed.indexOf("=")
|
|
34
|
+
if (eqIdx === -1) continue
|
|
35
|
+
const key = trimmed.slice(0, eqIdx).trim()
|
|
36
|
+
const val = trimmed.slice(eqIdx + 1).trim()
|
|
37
|
+
if (key && val) process.env[key] = val
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
26
41
|
function hasAnyKey(): boolean {
|
|
27
42
|
return !!(process.env.GEMINI_API_KEY || process.env.GROQ_API_KEY || process.env.NVIDIA_API_KEY)
|
|
28
43
|
}
|
|
29
44
|
|
|
30
45
|
async function cmdInit() {
|
|
46
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true })
|
|
47
|
+
|
|
31
48
|
if (!fs.existsSync(ENV_PATH)) {
|
|
32
49
|
fs.writeFileSync(ENV_PATH, envTemplate())
|
|
33
50
|
console.log(`Created ${ENV_PATH}`)
|
|
@@ -47,31 +64,45 @@ Then run: algocoach start
|
|
|
47
64
|
}
|
|
48
65
|
|
|
49
66
|
async function cmdStart() {
|
|
67
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true })
|
|
68
|
+
|
|
50
69
|
if (!fs.existsSync(ENV_PATH)) {
|
|
51
|
-
console.log("No
|
|
70
|
+
console.log("No config found. Creating one...")
|
|
52
71
|
fs.writeFileSync(ENV_PATH, envTemplate())
|
|
53
72
|
console.log(`Created ${ENV_PATH}`)
|
|
54
73
|
console.log("Edit it with your API keys, then run 'algocoach start' again.\n")
|
|
55
74
|
return
|
|
56
75
|
}
|
|
57
76
|
|
|
77
|
+
loadEnv()
|
|
78
|
+
|
|
58
79
|
if (!hasAnyKey()) {
|
|
59
|
-
console.error("No AI API key configured
|
|
80
|
+
console.error("No AI API key configured. Add at least one key to " + ENV_PATH)
|
|
60
81
|
process.exit(1)
|
|
61
82
|
}
|
|
62
83
|
|
|
63
|
-
const
|
|
84
|
+
const preferredPort = parseInt(process.env.PORT || "3000", 10)
|
|
64
85
|
|
|
65
86
|
process.env.LOCAL_DEV = "true"
|
|
66
87
|
process.env.LOCAL_USER_ID = "local-user"
|
|
67
|
-
process.env.BETTER_AUTH_URL = `http://localhost:${port}`
|
|
68
88
|
process.env.BETTER_AUTH_SECRET = "local-dev-secret-min-32-chars-long-for-better-auth"
|
|
69
|
-
process.env.CORS_ORIGIN = `http://localhost:${port}`
|
|
70
89
|
|
|
71
90
|
const { serve } = await import("../server/index")
|
|
72
|
-
serve(
|
|
91
|
+
const { port } = serve(preferredPort)
|
|
92
|
+
|
|
93
|
+
process.env.BETTER_AUTH_URL = `http://localhost:${port}`
|
|
94
|
+
process.env.CORS_ORIGIN = `http://localhost:${port}`
|
|
95
|
+
|
|
96
|
+
const url = `http://localhost:${port}`
|
|
97
|
+
console.log(`\n AlgoCoach running at ${url}\n`)
|
|
73
98
|
|
|
74
|
-
|
|
99
|
+
// Auto-open browser
|
|
100
|
+
const openCmd = process.platform === "darwin"
|
|
101
|
+
? ["open", url]
|
|
102
|
+
: process.platform === "win32"
|
|
103
|
+
? ["cmd", "/c", "start", url]
|
|
104
|
+
: ["xdg-open", url]
|
|
105
|
+
Bun.spawn(openCmd).unwrap().catch(() => {})
|
|
75
106
|
}
|
|
76
107
|
|
|
77
108
|
const cmd = process.argv[2] || "start"
|
|
@@ -89,9 +120,9 @@ switch (cmd) {
|
|
|
89
120
|
Usage: algocoach <command>
|
|
90
121
|
|
|
91
122
|
Commands:
|
|
92
|
-
start Create
|
|
93
|
-
init Create
|
|
123
|
+
start Create config if needed and start the server
|
|
124
|
+
init Create config file only
|
|
94
125
|
|
|
95
|
-
|
|
126
|
+
Config: ${ENV_PATH}
|
|
96
127
|
`)
|
|
97
128
|
}
|
package/package.json
CHANGED
package/server/index.ts
CHANGED
|
@@ -50,8 +50,19 @@ if (fs.existsSync(distPath)) {
|
|
|
50
50
|
})
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export function serve(
|
|
54
|
-
|
|
53
|
+
export function serve(preferredPort: number = 3000) {
|
|
54
|
+
const maxAttempts = 10
|
|
55
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
56
|
+
const port = preferredPort + i
|
|
57
|
+
try {
|
|
58
|
+
const server = Bun.serve({ fetch: app.fetch, port, idleTimeout: 255 })
|
|
59
|
+
return { server, port }
|
|
60
|
+
} catch (err: any) {
|
|
61
|
+
if (err.code !== "EADDRINUSE" || i === maxAttempts - 1) {
|
|
62
|
+
throw err
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
export default app
|