Add local proxy server for AI draft button; point button at /api/claude
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
## Copy this file to .env and fill in your key. Do not commit .env.
|
||||
ANTHROPIC_API_KEY=your-anthropic-api-key-here
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@ node_modules/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.log
|
||||
.env
|
||||
|
||||
112
QUICKSTART.md
Normal file
112
QUICKSTART.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Quick Start: SDE Meeting Toolkit
|
||||
|
||||
## Open a tool
|
||||
|
||||
1. Go to the `tools` folder.
|
||||
2. Double-click `field-problem-workshop.html` or `scope-lock-meeting-suite.html`.
|
||||
3. The file opens in your default browser.
|
||||
|
||||
No install step is needed for this method.
|
||||
|
||||
## Field Problem Workshop
|
||||
|
||||
The tool has three panels: Problem Map, Breadcrumbs, Actions.
|
||||
|
||||
### Problem Map panel
|
||||
|
||||
1. Type a problem into the entry box and click **Add**.
|
||||
2. Click **Add cluster** to create a new group.
|
||||
3. Drag a problem into a cluster, or use the cluster picker on the item.
|
||||
4. Use dot voting to mark priority problems.
|
||||
5. Unsorted problems stay under **Unclustered** at the top.
|
||||
|
||||
### Breadcrumbs panel
|
||||
|
||||
1. Click **Add a blank breadcrumb** to start a new one.
|
||||
2. Fill in **Baseline**, **Method**, **Metric**, and, once you have a baseline, **Target**.
|
||||
3. Click **Scan for incomplete items** to find breadcrumbs missing a field.
|
||||
4. Click **Save** on a breadcrumb to lock in your edits.
|
||||
|
||||
### Actions panel
|
||||
|
||||
1. Click **Add** to create an action item.
|
||||
2. Assign an owner and a due date.
|
||||
3. Click **Save** to store the action.
|
||||
|
||||
### Save your session
|
||||
|
||||
The tool does not save to disk on its own.
|
||||
|
||||
1. Click **Save JSON** before you close the tab.
|
||||
2. Store the file somewhere you can find it.
|
||||
3. Next time, click **Load JSON** and pick that file to pick up where you left off.
|
||||
4. Click **Export summary** to produce the meeting write-up (markdown).
|
||||
5. Click **Reset** only when you want to return to the July 21 baseline (43 problems, 7 clusters, 6 breadcrumbs, 5 actions). Reset erases current data, so export first.
|
||||
|
||||
## Scope Lock Meeting Suite
|
||||
|
||||
The tool has four panels: Scope Boundary Board, MVP Priority Ranker, Decision Registry, Micron Pilot Readiness.
|
||||
|
||||
### Scope Boundary Board
|
||||
|
||||
1. Click **Add item** to enter a new scope item.
|
||||
2. Move it into one of four columns: **In MVP**, **Later Phase**, **Out**, **Parking Lot**.
|
||||
3. Items start in **Unsorted** until you sort them.
|
||||
|
||||
### MVP Priority Ranker
|
||||
|
||||
1. Use **Move line up** / **Move line down** to reorder items by priority.
|
||||
|
||||
### Decision Registry
|
||||
|
||||
1. Click **Log decision** to record a decision made in the meeting.
|
||||
|
||||
### Micron Pilot Readiness
|
||||
|
||||
1. Review the readiness checklist shown on this panel.
|
||||
|
||||
### Save your session
|
||||
|
||||
1. This tool autosaves to the browser's local storage on the computer you use. It does not need a manual save step.
|
||||
2. Local storage does not follow you to another computer or browser.
|
||||
3. Click **Export meeting summary** to create the durable record to share or file.
|
||||
4. **Reset all** returns the tool to its starting state. Export first if you want to keep current data.
|
||||
|
||||
## Run it from a local server instead
|
||||
|
||||
Use this if you want the tools served over HTTP, or if you want the AI draft button to work outside of Claude.
|
||||
|
||||
1. Open a terminal in the `sde-meeting-toolkit` folder.
|
||||
2. Run `npm run serve`.
|
||||
3. Open `http://localhost:5173` in a browser.
|
||||
4. Click a tool link on that page, or go directly to a file under `tools`.
|
||||
|
||||
## Use the AI draft button
|
||||
|
||||
The Field Problem Workshop has an AI draft feature on the Breadcrumbs panel. It calls Claude to draft breadcrumb fields for you. Use one of these two methods.
|
||||
|
||||
### Method A: Local server (needs an API key)
|
||||
|
||||
1. Copy `.env.example` to a new file named `.env`, in the `sde-meeting-toolkit` folder.
|
||||
2. Open `.env` and set `ANTHROPIC_API_KEY` to your key.
|
||||
3. Run `npm run serve`.
|
||||
4. Open the tool at `http://localhost:5173/tools/field-problem-workshop.html`.
|
||||
5. Click the AI draft button.
|
||||
|
||||
If the button reports a missing key, check that `.env` exists, holds the correct key, and that you restarted the server after editing it.
|
||||
|
||||
### Method B: Claude artifact (no key needed)
|
||||
|
||||
1. Upload `field-problem-workshop.html` to a Claude chat, or add it to a Claude project.
|
||||
2. Ask Claude to render it as an artifact.
|
||||
3. Use the AI draft button from inside that artifact.
|
||||
|
||||
If you open the file directly, with no server running, the AI draft button fails. This is expected. Use Method A or Method B instead.
|
||||
|
||||
## Install the breadcrumb skill
|
||||
|
||||
1. In Claude, open `skills/construction-breadcrumbs.skill`.
|
||||
2. Use **Save skill** to install it.
|
||||
3. Start a new chat to use it.
|
||||
|
||||
Next: open a tool and click **Add** to create your first item.
|
||||
33
README.md
33
README.md
@@ -14,7 +14,7 @@ This folder holds two workshop tools for Project SDE. Each tool is one HTML file
|
||||
|
||||
## Requirements
|
||||
|
||||
Install Node.js version 18 or later to run the local server. Node.js includes `npx`.
|
||||
Install Node.js version 18 or later to run the local server.
|
||||
|
||||
You do not need Node.js to open a tool file directly in a browser.
|
||||
|
||||
@@ -28,8 +28,11 @@ Follow these steps to set up the project on your computer.
|
||||
```
|
||||
git status
|
||||
```
|
||||
4. To use the AI draft button through the local server, copy `.env.example` to `.env`.
|
||||
5. Open `.env` and set `ANTHROPIC_API_KEY` to your key.
|
||||
6. Do not commit `.env`. Git already ignores it.
|
||||
|
||||
The setup step ends here. The project has no other dependencies to install.
|
||||
Skip steps 4 through 6 if you plan to run the AI draft button only from inside a Claude artifact (see Method 3 below). The project has no other dependencies to install.
|
||||
|
||||
## Run a tool
|
||||
|
||||
@@ -45,7 +48,7 @@ This method works offline. This method does not run the AI draft button in the F
|
||||
|
||||
### Method 2: Run a local server
|
||||
|
||||
Use this method to test the tools the way a browser would load them from a web server.
|
||||
Use this method to test the tools the way a browser would load them from a web server. This method also makes the AI draft button work, if you completed steps 4 through 6 in Setup.
|
||||
|
||||
1. Open a terminal.
|
||||
2. Go to the `sde-meeting-toolkit` folder.
|
||||
@@ -53,20 +56,32 @@ Use this method to test the tools the way a browser would load them from a web s
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
4. Open a browser.
|
||||
5. Go to this address: `http://localhost:5173`
|
||||
6. Click a tool file under the `tools` folder.
|
||||
7. Press `Ctrl+C` in the terminal to stop the server.
|
||||
4. Check the terminal output. It reports whether it found an API key.
|
||||
5. Open a browser.
|
||||
6. Go to this address: `http://localhost:5173`
|
||||
7. Click a tool link, or go directly to a file under the `tools` folder.
|
||||
8. Press `Ctrl+C` in the terminal to stop the server.
|
||||
|
||||
### Method 3: Use the tool as a Claude artifact
|
||||
|
||||
Use this method to run the AI draft button in the Field Problem Workshop.
|
||||
Use this method to run the AI draft button without setting up a local API key.
|
||||
|
||||
1. Open a chat in Claude.
|
||||
2. Upload the tool file, or add it to a Claude project.
|
||||
3. Ask Claude to render the file as an artifact.
|
||||
|
||||
The AI draft button needs a network connection and the Claude environment. Outside of Claude, the button fails without an error.
|
||||
This method needs a network connection. It does not need the local server or a `.env` file.
|
||||
|
||||
## AI draft button
|
||||
|
||||
The Field Problem Workshop calls Claude to draft breadcrumb fields. It reaches Claude through one of two paths, never directly from the browser to the Anthropic API.
|
||||
|
||||
- **Local server path**: The browser calls `/api/claude` on your own machine. `server.js` reads `ANTHROPIC_API_KEY` from `.env` and forwards the request. Your key stays on your machine and is never sent to the browser.
|
||||
- **Claude artifact path**: The Claude environment handles the request. No key setup is needed on your side.
|
||||
|
||||
If you run a tool by double-clicking the file, with no server, the AI draft button fails. This is expected: a plain file has no server to call.
|
||||
|
||||
If you run `npm run serve` without a `.env` file, or with `ANTHROPIC_API_KEY` unset, the button reports that the key is missing. Add the key to `.env` and restart the server to fix it.
|
||||
|
||||
## Save and load your work
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"description": "Local environment for the Project SDE meeting and workshop tools",
|
||||
"scripts": {
|
||||
"serve": "npx --yes serve . -l 5173",
|
||||
"serve": "node server.js",
|
||||
"start": "npm run serve"
|
||||
}
|
||||
}
|
||||
|
||||
128
server.js
Normal file
128
server.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Local server for the SDE meeting toolkit.
|
||||
* Serves the static tool files.
|
||||
* Proxies AI draft requests to the Anthropic API, so the API key
|
||||
* stays on the server and never appears in the browser.
|
||||
*/
|
||||
const http = require('http');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = __dirname;
|
||||
const PORT = process.env.PORT || 5173;
|
||||
const ANTHROPIC_VERSION = '2023-06-01';
|
||||
|
||||
const MIME = {
|
||||
'.html': 'text/html; charset=utf-8',
|
||||
'.js': 'text/javascript; charset=utf-8',
|
||||
'.css': 'text/css; charset=utf-8',
|
||||
'.json': 'application/json; charset=utf-8',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.png': 'image/png',
|
||||
'.skill': 'application/zip'
|
||||
};
|
||||
|
||||
function loadEnvFile() {
|
||||
const envPath = path.join(ROOT, '.env');
|
||||
let content;
|
||||
try {
|
||||
content = fs.readFileSync(envPath, 'utf8');
|
||||
} catch (err) {
|
||||
return; // no .env file; that is allowed, the AI draft route will report it
|
||||
}
|
||||
content.split('\n').forEach((line) => {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith('#')) return;
|
||||
const idx = trimmed.indexOf('=');
|
||||
if (idx === -1) return;
|
||||
const key = trimmed.slice(0, idx).trim();
|
||||
const value = trimmed.slice(idx + 1).trim().replace(/^["']|["']$/g, '');
|
||||
if (key && !(key in process.env)) process.env[key] = value;
|
||||
});
|
||||
}
|
||||
loadEnvFile();
|
||||
|
||||
function sendJson(res, status, obj) {
|
||||
const body = JSON.stringify(obj);
|
||||
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(body);
|
||||
}
|
||||
|
||||
function proxyToClaude(req, res) {
|
||||
if (!process.env.ANTHROPIC_API_KEY) {
|
||||
sendJson(res, 500, {
|
||||
error: 'ANTHROPIC_API_KEY is not set. Add it to the .env file in this folder, then restart the server.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
let body = '';
|
||||
req.on('data', (chunk) => { body += chunk; });
|
||||
req.on('end', async () => {
|
||||
try {
|
||||
const upstream = await fetch('https://api.anthropic.com/v1/messages', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': process.env.ANTHROPIC_API_KEY,
|
||||
'anthropic-version': ANTHROPIC_VERSION
|
||||
},
|
||||
body
|
||||
});
|
||||
const text = await upstream.text();
|
||||
res.writeHead(upstream.status, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(text);
|
||||
} catch (err) {
|
||||
sendJson(res, 502, { error: 'Could not reach the Anthropic API: ' + err.message });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function serveStatic(req, res) {
|
||||
const urlPath = decodeURIComponent(req.url.split('?')[0]);
|
||||
if (urlPath === '/') {
|
||||
const index = `<!doctype html><html><body style="font:14px system-ui;padding:2rem">
|
||||
<h1>SDE Meeting Toolkit</h1>
|
||||
<ul>
|
||||
<li><a href="/tools/field-problem-workshop.html">Field Problem Workshop</a></li>
|
||||
<li><a href="/tools/scope-lock-meeting-suite.html">Scope Lock Meeting Suite</a></li>
|
||||
</ul>
|
||||
</body></html>`;
|
||||
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.end(index);
|
||||
return;
|
||||
}
|
||||
const filePath = path.normalize(path.join(ROOT, urlPath));
|
||||
if (!filePath.startsWith(ROOT)) {
|
||||
res.writeHead(403);
|
||||
res.end('Forbidden');
|
||||
return;
|
||||
}
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.end('Not found: ' + urlPath);
|
||||
return;
|
||||
}
|
||||
const ext = path.extname(filePath);
|
||||
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
|
||||
res.end(data);
|
||||
});
|
||||
}
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
if (req.method === 'POST' && req.url === '/api/claude') {
|
||||
proxyToClaude(req, res);
|
||||
return;
|
||||
}
|
||||
if (req.method === 'GET') {
|
||||
serveStatic(req, res);
|
||||
return;
|
||||
}
|
||||
res.writeHead(405, { 'Content-Type': 'text/plain' });
|
||||
res.end('Method not allowed');
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`SDE meeting toolkit running at http://localhost:${PORT}`);
|
||||
console.log(process.env.ANTHROPIC_API_KEY ? 'ANTHROPIC_API_KEY loaded: AI draft button is live.' : 'No ANTHROPIC_API_KEY found: AI draft button will return an error until you add one to .env.');
|
||||
});
|
||||
@@ -653,7 +653,7 @@ document.getElementById('btnAddBc').addEventListener('click', ()=>{
|
||||
|
||||
/* ===== AI: draft breadcrumb fields from methodology ===== */
|
||||
async function callClaude(prompt){
|
||||
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
||||
const response = await fetch("/api/claude", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ model: "claude-sonnet-4-6", max_tokens: 1000, messages: [{ role: "user", content: prompt }] })
|
||||
|
||||
Reference in New Issue
Block a user