create-interview-cockpit 0.1.0
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 +62 -0
- package/index.js +302 -0
- package/package.json +44 -0
- package/template/.env.example +14 -0
- package/template/client/index.html +12 -0
- package/template/client/package-lock.json +6012 -0
- package/template/client/package.json +34 -0
- package/template/client/postcss.config.cjs +6 -0
- package/template/client/src/App.tsx +120 -0
- package/template/client/src/api.ts +132 -0
- package/template/client/src/components/AnnotationDialog.tsx +307 -0
- package/template/client/src/components/ChatMessage.tsx +89 -0
- package/template/client/src/components/ChatView.tsx +763 -0
- package/template/client/src/components/CodeContextPanel.tsx +470 -0
- package/template/client/src/components/FileAttachments.tsx +107 -0
- package/template/client/src/components/FileViewerModal.tsx +470 -0
- package/template/client/src/components/MarkdownRenderer.tsx +333 -0
- package/template/client/src/components/MermaidDiagram.tsx +157 -0
- package/template/client/src/components/Sidebar.tsx +419 -0
- package/template/client/src/components/TextAnnotator.tsx +476 -0
- package/template/client/src/index.css +61 -0
- package/template/client/src/main.tsx +10 -0
- package/template/client/src/store.ts +321 -0
- package/template/client/src/types.ts +65 -0
- package/template/client/src/vite-env.d.ts +1 -0
- package/template/client/tailwind.config.cjs +8 -0
- package/template/client/tsconfig.json +16 -0
- package/template/client/tsconfig.tsbuildinfo +1 -0
- package/template/client/vite.config.ts +12 -0
- package/template/cockpit.json +3 -0
- package/template/data/context-files/.gitkeep +0 -0
- package/template/data/questions/.gitkeep +0 -0
- package/template/data/topics.json +1 -0
- package/template/package.json +14 -0
- package/template/server/package-lock.json +2266 -0
- package/template/server/package.json +31 -0
- package/template/server/src/index.ts +758 -0
- package/template/server/src/storage.ts +303 -0
- package/template/server/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# create-interview-cockpit
|
|
2
|
+
|
|
3
|
+
Scaffold your own AI-powered interview prep cockpit in seconds.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx create-interview-cockpit
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## What you get
|
|
10
|
+
|
|
11
|
+
A self-hosted, full-stack interview prep tool with:
|
|
12
|
+
|
|
13
|
+
- **Topics** — organise prep by theme (C#, System Design, Azure, etc.)
|
|
14
|
+
- **Questions** — nested questions under each topic with persistent chat history
|
|
15
|
+
- **AI chat** — powered by any of OpenAI / Google Gemini / Anthropic Claude via the Vercel AI SDK
|
|
16
|
+
- **Context files** — attach PDFs, DOCX, or plain text (job specs, CVs, notes) as context per topic or question
|
|
17
|
+
- **Code context panel** — point the AI at your local codebase files
|
|
18
|
+
- **Text annotations** — highlight any AI response and ask follow-up questions inline
|
|
19
|
+
- **Response tuning** — control length (concise / moderate / full) and style (prose / bullets / structured)
|
|
20
|
+
- **Mermaid diagrams** — the AI renders architecture diagrams inline
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx create-interview-cockpit my-cockpit
|
|
26
|
+
cd my-cockpit
|
|
27
|
+
npm install
|
|
28
|
+
npm run dev
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The CLI will prompt you to choose an AI provider (OpenAI, Google, or Anthropic) and optionally paste your API key. Everything is written to a local `.env` file — nothing leaves your machine.
|
|
32
|
+
|
|
33
|
+
Open **http://localhost:5173**.
|
|
34
|
+
|
|
35
|
+
## Tech stack
|
|
36
|
+
|
|
37
|
+
| Layer | Tech |
|
|
38
|
+
|----------|--------------------------------------|
|
|
39
|
+
| Frontend | React 19, TypeScript, Vite, Tailwind |
|
|
40
|
+
| Backend | Express, TypeScript, tsx |
|
|
41
|
+
| AI | Vercel AI SDK (multi-provider) |
|
|
42
|
+
| Storage | Local JSON files |
|
|
43
|
+
|
|
44
|
+
## Supported AI providers
|
|
45
|
+
|
|
46
|
+
| Provider | Env var | Default model |
|
|
47
|
+
|-----------|----------------------|----------------------------|
|
|
48
|
+
| OpenAI | `OPENAI_API_KEY` | `gpt-4o` |
|
|
49
|
+
| Google | `GOOGLE_API_KEY` | `gemini-2.5-flash` |
|
|
50
|
+
| Anthropic | `ANTHROPIC_API_KEY` | `claude-sonnet-4-20250514` |
|
|
51
|
+
|
|
52
|
+
Switch provider at any time by editing `.env`:
|
|
53
|
+
|
|
54
|
+
```ini
|
|
55
|
+
AI_PROVIDER=google
|
|
56
|
+
AI_MODEL=gemini-2.5-flash
|
|
57
|
+
GOOGLE_API_KEY=your-key-here
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import readline from "readline";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const templateDir = path.join(__dirname, "template");
|
|
11
|
+
|
|
12
|
+
// This CLI's own version — used for upgrade version stamping
|
|
13
|
+
const CLI_VERSION = JSON.parse(
|
|
14
|
+
fs.readFileSync(path.join(__dirname, "package.json"), "utf-8")
|
|
15
|
+
).version;
|
|
16
|
+
|
|
17
|
+
// ── Readline helpers ───────────────────────────────────────
|
|
18
|
+
const rl = readline.createInterface({
|
|
19
|
+
input: process.stdin,
|
|
20
|
+
output: process.stdout,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const ask = (question, fallback) =>
|
|
24
|
+
new Promise((resolve) => {
|
|
25
|
+
const suffix = fallback ? ` (${fallback}): ` : ": ";
|
|
26
|
+
rl.question(question + suffix, (answer) => {
|
|
27
|
+
resolve(answer.trim() || fallback || "");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const askChoice = (question, choices, fallback) =>
|
|
32
|
+
new Promise((resolve) => {
|
|
33
|
+
rl.question(`${question} [${choices.join("/")}] (${fallback}): `, (answer) => {
|
|
34
|
+
const v = answer.trim().toLowerCase();
|
|
35
|
+
resolve(choices.includes(v) ? v : fallback);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const confirm = (message) =>
|
|
40
|
+
new Promise((resolve) => {
|
|
41
|
+
rl.question(`${message} [y/N]: `, (answer) => {
|
|
42
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ── Helpers ────────────────────────────────────────────────
|
|
47
|
+
// Files/dirs never overwritten during upgrade
|
|
48
|
+
const UPGRADE_KEEP = new Set([".env", "data", "node_modules", "dist"]);
|
|
49
|
+
// Files/dirs excluded when copying from the template
|
|
50
|
+
const COPY_EXCLUDE = new Set(["node_modules", "dist", ".env"]);
|
|
51
|
+
|
|
52
|
+
function copyDirSync(src, dest, exclude = COPY_EXCLUDE) {
|
|
53
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
54
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
55
|
+
if (exclude.has(entry.name)) continue;
|
|
56
|
+
const srcPath = path.join(src, entry.name);
|
|
57
|
+
const destPath = path.join(dest, entry.name);
|
|
58
|
+
if (entry.isDirectory()) {
|
|
59
|
+
copyDirSync(srcPath, destPath, exclude);
|
|
60
|
+
} else {
|
|
61
|
+
fs.copyFileSync(srcPath, destPath);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Merge incoming deps into existing — never downgrade a pinned version. */
|
|
67
|
+
function mergeDeps(existing = {}, incoming = {}) {
|
|
68
|
+
const merged = { ...existing };
|
|
69
|
+
for (const [pkg, ver] of Object.entries(incoming)) {
|
|
70
|
+
if (!(pkg in merged)) merged[pkg] = ver;
|
|
71
|
+
}
|
|
72
|
+
return merged;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── Create command ────────────────────────────────────────
|
|
76
|
+
async function runCreate() {
|
|
77
|
+
const projectArg = process.argv[2];
|
|
78
|
+
|
|
79
|
+
console.log("");
|
|
80
|
+
console.log("╔══════════════════════════════════════════════════╗");
|
|
81
|
+
console.log("║ create-interview-cockpit ║");
|
|
82
|
+
console.log("║ Your personal AI-powered interview prep tool ║");
|
|
83
|
+
console.log("╚══════════════════════════════════════════════════╝");
|
|
84
|
+
console.log("");
|
|
85
|
+
|
|
86
|
+
// ── Project directory ──────────────────────────────────
|
|
87
|
+
const projectName =
|
|
88
|
+
projectArg || (await ask(" Project directory name", "my-cockpit"));
|
|
89
|
+
const targetDir = path.resolve(process.cwd(), projectName);
|
|
90
|
+
|
|
91
|
+
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
|
|
92
|
+
console.error(`\n ✖ Directory "${projectName}" already exists and is not empty.\n`);
|
|
93
|
+
rl.close();
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── AI Provider ────────────────────────────────────────
|
|
98
|
+
console.log("");
|
|
99
|
+
console.log(" Interview Cockpit supports OpenAI, Google Gemini, and Anthropic Claude.");
|
|
100
|
+
console.log("");
|
|
101
|
+
const provider = await askChoice(
|
|
102
|
+
" AI provider",
|
|
103
|
+
["openai", "google", "anthropic"],
|
|
104
|
+
"openai"
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const keyEnvNames = {
|
|
108
|
+
openai: "OPENAI_API_KEY",
|
|
109
|
+
google: "GOOGLE_API_KEY",
|
|
110
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
111
|
+
};
|
|
112
|
+
const defaultModels = {
|
|
113
|
+
openai: "gpt-4o",
|
|
114
|
+
google: "gemini-2.5-flash",
|
|
115
|
+
anthropic: "claude-sonnet-4-20250514",
|
|
116
|
+
};
|
|
117
|
+
const keyName = keyEnvNames[provider];
|
|
118
|
+
const defaultModel = defaultModels[provider];
|
|
119
|
+
|
|
120
|
+
const apiKey = await ask(
|
|
121
|
+
` ${keyName} (paste your key, or leave blank to add to .env later)`,
|
|
122
|
+
""
|
|
123
|
+
);
|
|
124
|
+
const model = await ask(" Model name", defaultModel);
|
|
125
|
+
|
|
126
|
+
console.log("");
|
|
127
|
+
console.log(" ─── Summary ─────────────────────────────────────");
|
|
128
|
+
console.log(" Directory : " + projectName);
|
|
129
|
+
console.log(" Provider : " + provider);
|
|
130
|
+
console.log(" Model : " + model);
|
|
131
|
+
console.log(" ─────────────────────────────────────────────────");
|
|
132
|
+
console.log("");
|
|
133
|
+
|
|
134
|
+
// ── 1. Copy template ──────────────────────────────────
|
|
135
|
+
console.log(" Scaffolding project…");
|
|
136
|
+
copyDirSync(templateDir, targetDir);
|
|
137
|
+
// cockpit.json is already in the template with the correct version
|
|
138
|
+
console.log(" ✔ Copied template");
|
|
139
|
+
|
|
140
|
+
// ── 2. Write .env ─────────────────────────────────────
|
|
141
|
+
const envLines = [
|
|
142
|
+
`# AI provider: openai | google | anthropic`,
|
|
143
|
+
`AI_PROVIDER=${provider}`,
|
|
144
|
+
`AI_MODEL=${model}`,
|
|
145
|
+
``,
|
|
146
|
+
`# Paste your API key for the provider you selected`,
|
|
147
|
+
`OPENAI_API_KEY=${provider === "openai" ? apiKey : ""}`,
|
|
148
|
+
`GOOGLE_API_KEY=${provider === "google" ? apiKey : ""}`,
|
|
149
|
+
`ANTHROPIC_API_KEY=${provider === "anthropic" ? apiKey : ""}`,
|
|
150
|
+
``,
|
|
151
|
+
`# Port the backend runs on`,
|
|
152
|
+
`PORT=3001`,
|
|
153
|
+
``,
|
|
154
|
+
`# Optional: absolute path to a local code directory you want the AI to reference`,
|
|
155
|
+
`# CODE_CONTEXT_DIR=`,
|
|
156
|
+
];
|
|
157
|
+
fs.writeFileSync(path.join(targetDir, ".env"), envLines.join("\n") + "\n");
|
|
158
|
+
console.log(" ✔ Created .env");
|
|
159
|
+
|
|
160
|
+
rl.close();
|
|
161
|
+
|
|
162
|
+
console.log("");
|
|
163
|
+
console.log(" ✔ Done! Get started:");
|
|
164
|
+
console.log("");
|
|
165
|
+
console.log(` cd ${projectName}`);
|
|
166
|
+
console.log(" npm install");
|
|
167
|
+
console.log(" npm run dev");
|
|
168
|
+
console.log("");
|
|
169
|
+
console.log(" The app opens at http://localhost:5173");
|
|
170
|
+
console.log(" Add topics, create questions, and start prepping.");
|
|
171
|
+
console.log("");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Upgrade command ────────────────────────────────────────
|
|
175
|
+
async function runUpgrade() {
|
|
176
|
+
const cwd = process.cwd();
|
|
177
|
+
|
|
178
|
+
console.log("");
|
|
179
|
+
console.log("╔══════════════════════════════════════════════════╗");
|
|
180
|
+
console.log("║ create-interview-cockpit — upgrade ║");
|
|
181
|
+
console.log("╚══════════════════════════════════════════════════╝");
|
|
182
|
+
console.log("");
|
|
183
|
+
|
|
184
|
+
// ── Detect cockpit project ─────────────────────────────
|
|
185
|
+
const hasClient = fs.existsSync(path.join(cwd, "client"));
|
|
186
|
+
const hasServer = fs.existsSync(path.join(cwd, "server"));
|
|
187
|
+
|
|
188
|
+
if (!hasClient || !hasServer) {
|
|
189
|
+
console.error(
|
|
190
|
+
" ✖ This doesn't look like an Interview Cockpit project.\n" +
|
|
191
|
+
" Run this command from inside a cockpit project directory.\n"
|
|
192
|
+
);
|
|
193
|
+
rl.close();
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Version comparison ─────────────────────────────────
|
|
198
|
+
const cockpitJsonPath = path.join(cwd, "cockpit.json");
|
|
199
|
+
let currentVersion = "unknown";
|
|
200
|
+
if (fs.existsSync(cockpitJsonPath)) {
|
|
201
|
+
try {
|
|
202
|
+
currentVersion = JSON.parse(
|
|
203
|
+
fs.readFileSync(cockpitJsonPath, "utf-8")
|
|
204
|
+
).version;
|
|
205
|
+
} catch {
|
|
206
|
+
// malformed cockpit.json
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const currentLabel =
|
|
211
|
+
currentVersion === "unknown" ? "(pre-versioning / unknown)" : `v${currentVersion}`;
|
|
212
|
+
|
|
213
|
+
console.log(` Installed version : ${currentLabel}`);
|
|
214
|
+
console.log(` Available version : v${CLI_VERSION}`);
|
|
215
|
+
console.log("");
|
|
216
|
+
|
|
217
|
+
if (currentVersion === CLI_VERSION) {
|
|
218
|
+
console.log(" Already up to date. Nothing to do.");
|
|
219
|
+
console.log("");
|
|
220
|
+
rl.close();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ── What will change ───────────────────────────────────
|
|
225
|
+
console.log(" The following will be updated:");
|
|
226
|
+
console.log(" ✦ client/ (all source files)");
|
|
227
|
+
console.log(" ✦ server/ (all source files)");
|
|
228
|
+
console.log(" ✦ package.json (new deps merged in, your pinned versions preserved)");
|
|
229
|
+
console.log("");
|
|
230
|
+
console.log(" These will NOT be touched:");
|
|
231
|
+
console.log(" ✧ .env");
|
|
232
|
+
console.log(" ✧ data/ (your topics, questions, and context files)");
|
|
233
|
+
console.log(" ✧ node_modules/");
|
|
234
|
+
console.log("");
|
|
235
|
+
|
|
236
|
+
const ok = await confirm(
|
|
237
|
+
` Upgrade ${currentLabel} → v${CLI_VERSION}?`
|
|
238
|
+
);
|
|
239
|
+
if (!ok) {
|
|
240
|
+
console.log("\n Upgrade cancelled.\n");
|
|
241
|
+
rl.close();
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
console.log("");
|
|
246
|
+
|
|
247
|
+
// ── Apply upgrade ──────────────────────────────────────
|
|
248
|
+
copyDirSync(path.join(templateDir, "client"), path.join(cwd, "client"));
|
|
249
|
+
console.log(" ✔ client/");
|
|
250
|
+
|
|
251
|
+
copyDirSync(path.join(templateDir, "server"), path.join(cwd, "server"));
|
|
252
|
+
console.log(" ✔ server/");
|
|
253
|
+
|
|
254
|
+
// package.json — merge scripts + deps, never replace outright
|
|
255
|
+
const templatePkgPath = path.join(templateDir, "package.json");
|
|
256
|
+
const targetPkgPath = path.join(cwd, "package.json");
|
|
257
|
+
if (fs.existsSync(templatePkgPath) && fs.existsSync(targetPkgPath)) {
|
|
258
|
+
const tmpl = JSON.parse(fs.readFileSync(templatePkgPath, "utf-8"));
|
|
259
|
+
const target = JSON.parse(fs.readFileSync(targetPkgPath, "utf-8"));
|
|
260
|
+
// Template scripts win (may contain new like sync:template)
|
|
261
|
+
target.scripts = { ...target.scripts, ...tmpl.scripts };
|
|
262
|
+
// Deps — only add new packages
|
|
263
|
+
target.dependencies = mergeDeps(target.dependencies, tmpl.dependencies);
|
|
264
|
+
target.devDependencies = mergeDeps(
|
|
265
|
+
target.devDependencies,
|
|
266
|
+
tmpl.devDependencies
|
|
267
|
+
);
|
|
268
|
+
fs.writeFileSync(targetPkgPath, JSON.stringify(target, null, 2) + "\n");
|
|
269
|
+
console.log(" ✔ package.json (deps merged)");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Stamp the new version
|
|
273
|
+
fs.writeFileSync(
|
|
274
|
+
cockpitJsonPath,
|
|
275
|
+
JSON.stringify({ version: CLI_VERSION }, null, 2) + "\n"
|
|
276
|
+
);
|
|
277
|
+
console.log(` ✔ cockpit.json → v${CLI_VERSION}`);
|
|
278
|
+
|
|
279
|
+
rl.close();
|
|
280
|
+
|
|
281
|
+
console.log("");
|
|
282
|
+
console.log(` ✔ Upgraded to v${CLI_VERSION}!`);
|
|
283
|
+
console.log("");
|
|
284
|
+
console.log(" Run npm install to pick up any dependency changes,");
|
|
285
|
+
console.log(" then npm run dev to start.");
|
|
286
|
+
console.log("");
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ── Entry point ────────────────────────────────────────────
|
|
290
|
+
const subcommand = process.argv[2];
|
|
291
|
+
|
|
292
|
+
if (subcommand === "upgrade") {
|
|
293
|
+
runUpgrade().catch((e) => {
|
|
294
|
+
console.error(e);
|
|
295
|
+
process.exit(1);
|
|
296
|
+
});
|
|
297
|
+
} else {
|
|
298
|
+
runCreate().catch((e) => {
|
|
299
|
+
console.error(e);
|
|
300
|
+
process.exit(1);
|
|
301
|
+
});
|
|
302
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-interview-cockpit",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold a personal AI-powered interview prep cockpit",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-interview-cockpit": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"changeset": "changeset"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"interview",
|
|
18
|
+
"ai",
|
|
19
|
+
"cockpit",
|
|
20
|
+
"scaffold",
|
|
21
|
+
"create",
|
|
22
|
+
"prep"
|
|
23
|
+
],
|
|
24
|
+
"author": {
|
|
25
|
+
"name": "Chipili Kafwilo",
|
|
26
|
+
"email": "ckafwilo@gmail.com",
|
|
27
|
+
"url": "https://chipilidev.com"
|
|
28
|
+
},
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@changesets/changelog-github": "^0.5.2",
|
|
32
|
+
"@changesets/cli": "^2.29.6"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/ChipiKaf/create-interview-cockpit.git"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# AI provider: openai | google | anthropic
|
|
2
|
+
AI_PROVIDER=openai
|
|
3
|
+
AI_MODEL=gpt-4o
|
|
4
|
+
|
|
5
|
+
# Paste the key for whichever provider you use
|
|
6
|
+
OPENAI_API_KEY=
|
|
7
|
+
GOOGLE_API_KEY=
|
|
8
|
+
ANTHROPIC_API_KEY=
|
|
9
|
+
|
|
10
|
+
# Port the backend listens on
|
|
11
|
+
PORT=3001
|
|
12
|
+
|
|
13
|
+
# Optional: absolute path to a local code directory you want the AI to reference
|
|
14
|
+
# CODE_CONTEXT_DIR=
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Interview Cockpit</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body class="bg-slate-950 text-slate-100">
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|