@sx4im/skillcheck 0.1.1 → 0.2.1

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.
@@ -0,0 +1,111 @@
1
+ # Skillcheck Cloud setup
2
+
3
+ Use this when you want users to install the CLI and run checks without configuring model-provider secrets locally.
4
+
5
+ ## Architecture
6
+
7
+ ```text
8
+ skillcheck CLI
9
+ -> Skillcheck Cloud API
10
+ -> model provider
11
+
12
+ Dashboard
13
+ -> user signs up
14
+ -> user creates a Skillcheck token
15
+ -> token is stored hashed in your database
16
+ ```
17
+
18
+ The CLI only needs:
19
+
20
+ ```bash
21
+ skillcheck setup
22
+ ```
23
+
24
+ The setup wizard asks for:
25
+
26
+ ```bash
27
+ https://api.yourdomain.com/v1
28
+ ```
29
+
30
+ For token-gated private beta, users can additionally set:
31
+
32
+ ```bash
33
+ export SKILLCHECK_TOKEN=sk_live_...
34
+ ```
35
+
36
+ If you want public free trials, the proxy can allow anonymous requests with strict rate limits and no token.
37
+
38
+ ## API contract
39
+
40
+ The CLI expects an OpenAI-compatible endpoint:
41
+
42
+ ```http
43
+ POST /v1/chat/completions
44
+ Authorization: Bearer <skillcheck-token>
45
+ Content-Type: application/json
46
+ ```
47
+
48
+ Response should match OpenAI chat completions enough for the `openai` Node SDK:
49
+
50
+ ```json
51
+ {
52
+ "id": "chatcmpl_...",
53
+ "object": "chat.completion",
54
+ "created": 1780000000,
55
+ "model": "your-model",
56
+ "choices": [
57
+ {
58
+ "index": 0,
59
+ "message": {
60
+ "role": "assistant",
61
+ "content": "..."
62
+ },
63
+ "finish_reason": "stop"
64
+ }
65
+ ],
66
+ "usage": {
67
+ "prompt_tokens": 1,
68
+ "completion_tokens": 1,
69
+ "total_tokens": 2
70
+ }
71
+ }
72
+ ```
73
+
74
+ ## Minimal proxy
75
+
76
+ The repo includes a tiny Node proxy in `examples/nvidia-proxy/`. It is useful for testing the `SKILLCHECK_API_URL` flow before building the dashboard.
77
+
78
+ Run it on a server:
79
+
80
+ ```bash
81
+ export NVIDIA_API_KEY=...
82
+ export SKILLCHECK_PROXY_TOKEN=dev-token
83
+ node examples/nvidia-proxy/server.mjs
84
+ ```
85
+
86
+ Point the CLI at it:
87
+
88
+ ```bash
89
+ export SKILLCHECK_API_URL=https://your-proxy.example.com/v1
90
+ export SKILLCHECK_TOKEN=dev-token
91
+ skillcheck check path/to/SKILL.md
92
+ ```
93
+
94
+ ## Dashboard requirements
95
+
96
+ - `users`: id, email, password/session provider, created_at.
97
+ - `tokens`: id, user_id, token_hash, prefix, created_at, last_used_at, revoked_at.
98
+ - `usage_events`: user_id, token_id, request_id, model, prompt_tokens, completion_tokens, created_at.
99
+ - Rate limit by token and IP.
100
+ - Store model-provider secrets only on the server.
101
+ - Never expose upstream provider secrets to the browser or CLI.
102
+
103
+ ## First production path
104
+
105
+ 1. Deploy the proxy API at `https://api.yourdomain.com/v1`.
106
+ 2. Add dashboard auth with GitHub or email login.
107
+ 3. Add “Create token” in the dashboard and show the token once.
108
+ 4. Hash tokens before storing them.
109
+ 5. Verify `Authorization: Bearer <token>` in the proxy.
110
+ 6. Add rate limits and usage logging.
111
+ 7. Ship the CLI with docs telling users to set `SKILLCHECK_API_URL` and `SKILLCHECK_TOKEN`.
@@ -0,0 +1,19 @@
1
+ # skillcheck NVIDIA proxy
2
+
3
+ This is the safe way to let CLI users run `skillcheck` without seeing your NVIDIA key.
4
+
5
+ Run the proxy on a server:
6
+
7
+ ```bash
8
+ export NVIDIA_API_KEY=...
9
+ node examples/nvidia-proxy/server.mjs
10
+ ```
11
+
12
+ Point the CLI at the proxy:
13
+
14
+ ```bash
15
+ export SKILLCHECK_API_URL=https://your-proxy.example.com/v1
16
+ skillcheck check path/to/SKILL.md
17
+ ```
18
+
19
+ Do not publish `NVIDIA_API_KEY` inside the npm package. If the proxy is public, put it behind rate limiting, quotas, or authentication before sharing it broadly.
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import { createServer } from 'node:http';
3
+
4
+ const port = Number(process.env.PORT || 8787);
5
+ const nvidiaApiKey = process.env.NVIDIA_API_KEY?.trim();
6
+ const nvidiaBaseUrl = process.env.NVIDIA_BASE_URL?.trim() || 'https://integrate.api.nvidia.com/v1';
7
+ const proxyToken = process.env.SKILLCHECK_PROXY_TOKEN?.trim();
8
+ const maxBodyBytes = Number(process.env.MAX_BODY_BYTES || 5_000_000);
9
+
10
+ if (!nvidiaApiKey) {
11
+ throw new Error('Missing NVIDIA_API_KEY. Set it on the server, never inside the npm CLI.');
12
+ }
13
+
14
+ function writeJson(res, status, body) {
15
+ res.writeHead(status, {
16
+ 'access-control-allow-headers': 'authorization, content-type',
17
+ 'access-control-allow-methods': 'POST, OPTIONS',
18
+ 'access-control-allow-origin': '*',
19
+ 'content-type': 'application/json'
20
+ });
21
+ res.end(`${JSON.stringify(body)}\n`);
22
+ }
23
+
24
+ function readBody(req) {
25
+ return new Promise((resolve, reject) => {
26
+ const chunks = [];
27
+ let size = 0;
28
+ req.on('data', (chunk) => {
29
+ size += chunk.length;
30
+ if (size > maxBodyBytes) {
31
+ reject(new Error('Request body is too large'));
32
+ req.destroy();
33
+ return;
34
+ }
35
+ chunks.push(chunk);
36
+ });
37
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
38
+ req.on('error', reject);
39
+ });
40
+ }
41
+
42
+ function isAuthorized(req) {
43
+ if (!proxyToken) {
44
+ return true;
45
+ }
46
+ return req.headers.authorization === `Bearer ${proxyToken}`;
47
+ }
48
+
49
+ const server = createServer(async (req, res) => {
50
+ const url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`);
51
+
52
+ if (req.method === 'OPTIONS') {
53
+ writeJson(res, 204, {});
54
+ return;
55
+ }
56
+
57
+ if (req.method === 'GET' && url.pathname === '/health') {
58
+ writeJson(res, 200, { ok: true });
59
+ return;
60
+ }
61
+
62
+ if (req.method !== 'POST' || url.pathname !== '/v1/chat/completions') {
63
+ writeJson(res, 404, { error: 'Not found' });
64
+ return;
65
+ }
66
+
67
+ if (!isAuthorized(req)) {
68
+ writeJson(res, 401, { error: 'Unauthorized' });
69
+ return;
70
+ }
71
+
72
+ try {
73
+ const body = await readBody(req);
74
+ const upstream = await fetch(`${nvidiaBaseUrl}/chat/completions`, {
75
+ method: 'POST',
76
+ headers: {
77
+ authorization: `Bearer ${nvidiaApiKey}`,
78
+ 'content-type': 'application/json'
79
+ },
80
+ body
81
+ });
82
+
83
+ res.writeHead(upstream.status, {
84
+ 'access-control-allow-origin': '*',
85
+ 'content-type': upstream.headers.get('content-type') || 'application/json'
86
+ });
87
+ res.end(await upstream.text());
88
+ } catch (error) {
89
+ const message = error instanceof Error ? error.message : String(error);
90
+ writeJson(res, 500, { error: message });
91
+ }
92
+ });
93
+
94
+ server.listen(port, () => {
95
+ console.log(`skillcheck NVIDIA proxy listening on http://localhost:${port}`);
96
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sx4im/skillcheck",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Measure whether agent skills improve task performance.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,11 +28,13 @@
28
28
  },
29
29
  "files": [
30
30
  "dist",
31
+ "docs",
32
+ "examples",
31
33
  "README.md",
32
34
  "METHODOLOGY.md"
33
35
  ],
34
36
  "scripts": {
35
- "build": "rm -rf dist && tsc -p tsconfig.json",
37
+ "build": "rm -rf dist && tsc -p tsconfig.json && chmod +x dist/bin/skillcheck.js",
36
38
  "m0": "tsx packages/cli/bin/skillcheck.ts m0",
37
39
  "site:build": "next build packages/site",
38
40
  "test": "vitest run",