haas-mcp 0.2.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 +98 -0
- package/dist/check.d.ts +23 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +34 -0
- package/dist/check.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +173 -0
- package/dist/cli.js.map +1 -0
- package/dist/client.d.ts +44 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +168 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +53 -0
- package/dist/config.js.map +1 -0
- package/dist/connect.d.ts +68 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +117 -0
- package/dist/connect.js.map +1 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +77 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +94 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +149 -0
- package/dist/setup.js.map +1 -0
- package/dist/tools/cancel_job.d.ts +3 -0
- package/dist/tools/cancel_job.d.ts.map +1 -0
- package/dist/tools/cancel_job.js +22 -0
- package/dist/tools/cancel_job.js.map +1 -0
- package/dist/tools/estimate_cost.d.ts +3 -0
- package/dist/tools/estimate_cost.d.ts.map +1 -0
- package/dist/tools/estimate_cost.js +28 -0
- package/dist/tools/estimate_cost.js.map +1 -0
- package/dist/tools/get_balance.d.ts +3 -0
- package/dist/tools/get_balance.d.ts.map +1 -0
- package/dist/tools/get_balance.js +13 -0
- package/dist/tools/get_balance.js.map +1 -0
- package/dist/tools/get_job_status.d.ts +3 -0
- package/dist/tools/get_job_status.d.ts.map +1 -0
- package/dist/tools/get_job_status.js +19 -0
- package/dist/tools/get_job_status.js.map +1 -0
- package/dist/tools/list_devices.d.ts +3 -0
- package/dist/tools/list_devices.d.ts.map +1 -0
- package/dist/tools/list_devices.js +13 -0
- package/dist/tools/list_devices.js.map +1 -0
- package/dist/tools/purchase_lease.d.ts +16 -0
- package/dist/tools/purchase_lease.d.ts.map +1 -0
- package/dist/tools/purchase_lease.js +50 -0
- package/dist/tools/purchase_lease.js.map +1 -0
- package/dist/tools/submit_batch.d.ts +3 -0
- package/dist/tools/submit_batch.d.ts.map +1 -0
- package/dist/tools/submit_batch.js +58 -0
- package/dist/tools/submit_batch.js.map +1 -0
- package/dist/tools/submit_job.d.ts +3 -0
- package/dist/tools/submit_job.d.ts.map +1 -0
- package/dist/tools/submit_job.js +49 -0
- package/dist/tools/submit_job.js.map +1 -0
- package/dist/tools/submit_source.d.ts +3 -0
- package/dist/tools/submit_source.d.ts.map +1 -0
- package/dist/tools/submit_source.js +55 -0
- package/dist/tools/submit_source.js.map +1 -0
- package/dist/tools/tail_serial.d.ts +3 -0
- package/dist/tools/tail_serial.d.ts.map +1 -0
- package/dist/tools/tail_serial.js +26 -0
- package/dist/tools/tail_serial.js.map +1 -0
- package/dist/tools/types.d.ts +9 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# haas-mcp
|
|
2
|
+
|
|
3
|
+
stdio MCP server for [haas-lab](https://haas-lab.com) — gives any AI client
|
|
4
|
+
(Claude Desktop, Cursor, OpenAI Codex CLI, etc.) native tools to compile and
|
|
5
|
+
flash firmware on real ESP32 boards in the cloud.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
One line, once the package is on npm. It opens your browser at the token page
|
|
10
|
+
(sign in if asked); you click **Connect** there, and the token it mints
|
|
11
|
+
arrives at the command by itself — nothing to paste. The command proves it
|
|
12
|
+
against the API, then connects every AI client it finds on the machine: Claude
|
|
13
|
+
Code and Codex through their own CLIs, Claude Desktop and Cursor by merging
|
|
14
|
+
their server lists.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx -y haas-mcp setup
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The terminal prints a four-character check and the page shows the same one;
|
|
21
|
+
if they differ, or you did not start the command yourself, close the tab. The
|
|
22
|
+
token it mints has the `read` and `submit` scopes only.
|
|
23
|
+
|
|
24
|
+
`setup --paste` asks for the token at the prompt instead, and is what the
|
|
25
|
+
command falls back to on a Node older than 22, which has no WebSocket.
|
|
26
|
+
|
|
27
|
+
By hand instead, one line per client:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Claude Code
|
|
31
|
+
claude mcp add haas-lab -e HAAS_PAT=pat_... -- npx -y haas-mcp
|
|
32
|
+
|
|
33
|
+
# OpenAI Codex CLI
|
|
34
|
+
codex mcp add haas-lab --env HAAS_PAT=pat_... -- npx -y haas-mcp
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Claude Desktop, Cursor and every other client that takes a JSON server list
|
|
38
|
+
get the same thing in that form — `claude_desktop_config.json` under
|
|
39
|
+
`~/Library/Application Support/Claude/` on a Mac or `%APPDATA%\Claude\` on
|
|
40
|
+
Windows, `.cursor/mcp.json` for Cursor:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"haas-lab": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "haas-mcp"],
|
|
48
|
+
"env": { "HAAS_PAT": "pat_..." }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Prove the token before restarting the client:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
HAAS_PAT=pat_... npx -y haas-mcp --check
|
|
58
|
+
# haas-mcp: connected to https://haas-lab.com — 1200 credits available
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Then ask the client: *"List the haas-lab devices."* — it will call
|
|
62
|
+
`list_devices`.
|
|
63
|
+
|
|
64
|
+
## Configure
|
|
65
|
+
|
|
66
|
+
Two environment variables (or `--token` / `--base-url` flags):
|
|
67
|
+
|
|
68
|
+
| Var | What | Default |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `HAAS_PAT` | Personal Access Token from your haas-lab dashboard | required |
|
|
71
|
+
| `HAAS_BASE_URL` | API root | `https://haas-lab.com` |
|
|
72
|
+
|
|
73
|
+
A developer running the web app locally sets `HAAS_BASE_URL=http://localhost:3000`.
|
|
74
|
+
`--help` prints the install lines above.
|
|
75
|
+
|
|
76
|
+
## Tools
|
|
77
|
+
|
|
78
|
+
| Tool | What |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `list_devices` | List rentable hardware |
|
|
81
|
+
| `submit_source` | Compile C source into a FreeRTOS app on the bench and flash it — the default way to run a program, no local toolchain |
|
|
82
|
+
| `submit_job` | Safety-check and flash one prebuilt application image (base64; simulation recorded as skipped) |
|
|
83
|
+
| `submit_batch` | Fan out N variants in parallel (the differentiator) |
|
|
84
|
+
| `get_job_status` | Fetch a job's state, cost, escrow |
|
|
85
|
+
| `tail_serial` | Long-poll the SSE feed for serial output |
|
|
86
|
+
| `get_balance` | Check wallet credits + reputation |
|
|
87
|
+
| `estimate_cost` | Price a hypothetical job |
|
|
88
|
+
| `cancel_job` | Cancel + refund |
|
|
89
|
+
| `purchase_lease` | Book a board for a number of hours; runs on it during the window pay no runtime cost |
|
|
90
|
+
|
|
91
|
+
## Develop
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
cd mcp
|
|
95
|
+
npm install
|
|
96
|
+
npm run dev # tsx, no build step
|
|
97
|
+
HAAS_PAT=pat_... npm run dev
|
|
98
|
+
```
|
package/dist/check.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prove a token against the API before an AI client is restarted around it
|
|
3
|
+
* (HAAS-142).
|
|
4
|
+
*
|
|
5
|
+
* One read-scoped call, the cheapest the API has — the wallet balance — so a
|
|
6
|
+
* `--check` costs the same as one tool call and says the three things a
|
|
7
|
+
* misconfigured install cannot otherwise tell apart: the base URL is right,
|
|
8
|
+
* the token is accepted, and the account behind it is the expected one.
|
|
9
|
+
*/
|
|
10
|
+
import type { HaasClient } from "./client.js";
|
|
11
|
+
export type CheckResult = {
|
|
12
|
+
ok: true;
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
credits: number | null;
|
|
15
|
+
} | {
|
|
16
|
+
ok: false;
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
};
|
|
20
|
+
/** Only `get` is needed, which is also what lets a test hand in a double that throws. */
|
|
21
|
+
export declare function checkConnection(client: Pick<HaasClient, "get">, baseUrl: string): Promise<CheckResult>;
|
|
22
|
+
export declare function describeCheck(result: CheckResult): string;
|
|
23
|
+
//# sourceMappingURL=check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,yFAAyF;AACzF,wBAAsB,eAAe,CACnC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,EAC/B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CAOtB;AASD,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAMzD"}
|
package/dist/check.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prove a token against the API before an AI client is restarted around it
|
|
3
|
+
* (HAAS-142).
|
|
4
|
+
*
|
|
5
|
+
* One read-scoped call, the cheapest the API has — the wallet balance — so a
|
|
6
|
+
* `--check` costs the same as one tool call and says the three things a
|
|
7
|
+
* misconfigured install cannot otherwise tell apart: the base URL is right,
|
|
8
|
+
* the token is accepted, and the account behind it is the expected one.
|
|
9
|
+
*/
|
|
10
|
+
/** Only `get` is needed, which is also what lets a test hand in a double that throws. */
|
|
11
|
+
export async function checkConnection(client, baseUrl) {
|
|
12
|
+
try {
|
|
13
|
+
const body = await client.get("/api/v1/balance");
|
|
14
|
+
return { ok: true, baseUrl, credits: creditsOf(body) };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return { ok: false, baseUrl, reason: error instanceof Error ? error.message : String(error) };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** The balance, when the API's answer carries one; the check does not depend on it. */
|
|
21
|
+
function creditsOf(body) {
|
|
22
|
+
if (typeof body !== "object" || body === null)
|
|
23
|
+
return null;
|
|
24
|
+
const { availableCredits } = body;
|
|
25
|
+
return typeof availableCredits === "number" ? availableCredits : null;
|
|
26
|
+
}
|
|
27
|
+
export function describeCheck(result) {
|
|
28
|
+
if (result.ok) {
|
|
29
|
+
const balance = result.credits === null ? "" : ` — ${result.credits} credits available`;
|
|
30
|
+
return `haas-mcp: connected to ${result.baseUrl}${balance}`;
|
|
31
|
+
}
|
|
32
|
+
return `haas-mcp: could not reach ${result.baseUrl} with this token: ${result.reason}`;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../src/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,yFAAyF;AACzF,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAA+B,EAC/B,OAAe;IAEf,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QACjD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;IACzD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAChG,CAAC;AACH,CAAC;AAED,uFAAuF;AACvF,SAAS,SAAS,CAAC,IAAa;IAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,EAAE,gBAAgB,EAAE,GAAG,IAAsC,CAAC;IACpE,OAAO,OAAO,gBAAgB,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAmB;IAC/C,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,OAAO,oBAAoB,CAAC;QACxF,OAAO,0BAA0B,MAAM,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC;IAC9D,CAAC;IACD,OAAO,6BAA6B,MAAM,CAAC,OAAO,qBAAqB,MAAM,CAAC,MAAM,EAAE,CAAC;AACzF,CAAC"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { homedir, hostname } from "node:os";
|
|
6
|
+
import { delimiter, dirname, join } from "node:path";
|
|
7
|
+
import { createInterface } from "node:readline/promises";
|
|
8
|
+
import { checkConnection, describeCheck } from "./check.js";
|
|
9
|
+
import { HaasClient } from "./client.js";
|
|
10
|
+
import { readConfig, USAGE } from "./config.js";
|
|
11
|
+
import { CONNECT_TIMEOUT_MS, connectPageUrl, newSetupCode, pairingCheck, receiveToken, registerUrl, setupStreamUrl, } from "./connect.js";
|
|
12
|
+
import { startMcpServer } from "./server.js";
|
|
13
|
+
import { browserCommand, describeOutcomes, planSetup, runSetup, tokenPageUrl, } from "./setup.js";
|
|
14
|
+
async function main() {
|
|
15
|
+
const config = readConfig(process.argv.slice(2), process.env);
|
|
16
|
+
if (config.help) {
|
|
17
|
+
process.stdout.write(USAGE);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (config.setup) {
|
|
21
|
+
process.exit(await setup(config.pat, config.baseUrl, config.paste));
|
|
22
|
+
}
|
|
23
|
+
if (!config.pat) {
|
|
24
|
+
process.stderr.write("haas-mcp: HAAS_PAT environment variable (or --token flag) is required.\n" +
|
|
25
|
+
`Run \`npx -y haas-mcp setup\` once, or get a token from ${config.baseUrl}/dashboard/tokens\n`);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const client = new HaasClient(config.baseUrl, config.pat);
|
|
29
|
+
if (config.check) {
|
|
30
|
+
const result = await checkConnection(client, config.baseUrl);
|
|
31
|
+
process.stderr.write(`${describeCheck(result)}\n`);
|
|
32
|
+
process.exit(result.ok ? 0 : 1);
|
|
33
|
+
}
|
|
34
|
+
await startMcpServer(client);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The wizard. No question when no token was given: the token arrives over a
|
|
38
|
+
* socket once the person clicks Connect on the page this opens (HAAS-144),
|
|
39
|
+
* and the paste prompt remains only where that path cannot be used. Then the
|
|
40
|
+
* plan is run against this machine. Deliberately no second question: which
|
|
41
|
+
* clients to connect is detected, not asked, because a person who has to
|
|
42
|
+
* answer that is a person who could have edited the config themselves.
|
|
43
|
+
*/
|
|
44
|
+
async function setup(given, baseUrl, paste) {
|
|
45
|
+
let token = given;
|
|
46
|
+
if (!token && !paste)
|
|
47
|
+
token = await receiveOverSocket(baseUrl);
|
|
48
|
+
if (!token) {
|
|
49
|
+
// The browser is opened at the token page; signed out, the site's
|
|
50
|
+
// middleware takes the person through sign-in and back to it. The URL is
|
|
51
|
+
// printed as well, for a machine with no browser or where the launch
|
|
52
|
+
// fails silently — the launch is best-effort and never blocks the prompt.
|
|
53
|
+
const url = tokenPageUrl(baseUrl);
|
|
54
|
+
openBrowser(url);
|
|
55
|
+
process.stdout.write(`haas-mcp setup — opening ${url}\n` +
|
|
56
|
+
"Sign in if asked, mint a token with the read and submit scopes, then\n");
|
|
57
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
58
|
+
token = (await prompt.question("paste it here: ")).trim();
|
|
59
|
+
prompt.close();
|
|
60
|
+
if (!token) {
|
|
61
|
+
process.stderr.write("haas-mcp: no token given\n");
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const proof = await checkConnection(new HaasClient(baseUrl, token), baseUrl);
|
|
66
|
+
process.stdout.write(`${describeCheck(proof)}\n`);
|
|
67
|
+
if (!proof.ok)
|
|
68
|
+
return 1;
|
|
69
|
+
const actions = planSetup(token, {
|
|
70
|
+
platform: process.platform,
|
|
71
|
+
env: process.env,
|
|
72
|
+
home: homedir(),
|
|
73
|
+
exists: existsSync,
|
|
74
|
+
onPath: findOnPath,
|
|
75
|
+
});
|
|
76
|
+
const outcomes = runSetup(actions, token, {
|
|
77
|
+
run: (program, args) => {
|
|
78
|
+
// A `.cmd` shim on Windows only runs through a shell; elsewhere the
|
|
79
|
+
// resolved path runs directly.
|
|
80
|
+
const result = spawnSync(program, args, {
|
|
81
|
+
encoding: "utf8",
|
|
82
|
+
shell: process.platform === "win32",
|
|
83
|
+
});
|
|
84
|
+
return { ok: result.status === 0, output: `${result.stdout ?? ""}${result.stderr ?? ""}` };
|
|
85
|
+
},
|
|
86
|
+
read: (path) => (existsSync(path) ? readFileSync(path, "utf8") : null),
|
|
87
|
+
write: (path, text) => {
|
|
88
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
89
|
+
writeFileSync(path, text, "utf8");
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
process.stdout.write(describeOutcomes(outcomes));
|
|
93
|
+
return outcomes.length > 0 && outcomes.every((o) => o.ok) ? 0 : 1;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The no-paste path (HAAS-144): a code, a registration, the browser at the
|
|
97
|
+
* token page with the code, and a socket that receives what the click mints.
|
|
98
|
+
* Null when it cannot be used here — no WebSocket in this Node, a server that
|
|
99
|
+
* would not register the code, nobody clicking in time — with the reason
|
|
100
|
+
* printed, so the paste prompt that follows is a fallback the person can see.
|
|
101
|
+
*/
|
|
102
|
+
async function receiveOverSocket(baseUrl) {
|
|
103
|
+
// Looked up by name because the global exists only on Node 22 and newer;
|
|
104
|
+
// the structural type is the part of it this uses.
|
|
105
|
+
const Socket = globalThis.WebSocket;
|
|
106
|
+
if (!Socket) {
|
|
107
|
+
process.stdout.write("haas-mcp setup — this Node has no WebSocket (Node 22 or newer does); falling back to pasting the token.\n");
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const code = newSetupCode((n) => randomBytes(n));
|
|
111
|
+
try {
|
|
112
|
+
const registered = await fetch(registerUrl(baseUrl, code), {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers: { "content-type": "application/json" },
|
|
115
|
+
body: JSON.stringify({ machine: hostname() }),
|
|
116
|
+
});
|
|
117
|
+
if (!registered.ok) {
|
|
118
|
+
process.stdout.write(`haas-mcp setup — ${baseUrl} would not register a setup code (HTTP ${registered.status}); falling back to pasting the token.\n`);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
process.stdout.write(`haas-mcp setup — could not reach ${baseUrl} (${error instanceof Error ? error.message : String(error)}); falling back to pasting the token.\n`);
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
const url = connectPageUrl(baseUrl, code);
|
|
127
|
+
const check = pairingCheck(code, (input) => createHash("sha256").update(input, "utf8").digest("hex"));
|
|
128
|
+
openBrowser(url);
|
|
129
|
+
process.stdout.write(`haas-mcp setup — opening ${url}\n` +
|
|
130
|
+
`Sign in if asked. The page shows the check ${check}; if it shows anything else,\n` +
|
|
131
|
+
"do not click Connect. Waiting for it here, up to 10 minutes\n" +
|
|
132
|
+
"(Ctrl-C to stop; `haas-mcp setup --paste` to paste a token instead).\n");
|
|
133
|
+
try {
|
|
134
|
+
return await receiveToken(new Socket(setupStreamUrl(baseUrl, code)), CONNECT_TIMEOUT_MS);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
process.stdout.write(`haas-mcp setup — ${error instanceof Error ? error.message : String(error)}; falling back to pasting the token.\n`);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Open a URL in the default browser, detached, and never fail the wizard over it. */
|
|
142
|
+
function openBrowser(url) {
|
|
143
|
+
try {
|
|
144
|
+
const { program, args } = browserCommand(process.platform, url);
|
|
145
|
+
const child = spawn(program, args, { detached: true, stdio: "ignore" });
|
|
146
|
+
child.on("error", () => {
|
|
147
|
+
// No browser here; the URL is printed beside the prompt.
|
|
148
|
+
});
|
|
149
|
+
child.unref();
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Same: the printed URL is the fallback.
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Where a program is on PATH, honouring Windows' executable suffixes. */
|
|
156
|
+
function findOnPath(name) {
|
|
157
|
+
const suffixes = process.platform === "win32" ? [".cmd", ".exe", ".bat", ""] : [""];
|
|
158
|
+
for (const dir of (process.env.PATH ?? "").split(delimiter)) {
|
|
159
|
+
if (!dir)
|
|
160
|
+
continue;
|
|
161
|
+
for (const suffix of suffixes) {
|
|
162
|
+
const candidate = join(dir, `${name}${suffix}`);
|
|
163
|
+
if (existsSync(candidate))
|
|
164
|
+
return candidate;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
main().catch((err) => {
|
|
170
|
+
process.stderr.write(`haas-mcp fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
|
173
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal REST client for the haas-lab API. Forwards `Authorization: Bearer <pat>`
|
|
3
|
+
* on every call, throws on non-2xx with the response body for visibility.
|
|
4
|
+
*/
|
|
5
|
+
export declare class HaasClient {
|
|
6
|
+
private readonly baseUrl;
|
|
7
|
+
private readonly pat;
|
|
8
|
+
constructor(baseUrl: string, pat: string);
|
|
9
|
+
private headers;
|
|
10
|
+
private parse;
|
|
11
|
+
private ensureOk;
|
|
12
|
+
get(path: string): Promise<unknown>;
|
|
13
|
+
postJson(path: string, body: unknown): Promise<unknown>;
|
|
14
|
+
postEmpty(path: string): Promise<unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* Multipart upload — used by submit_job. Pass the firmware as a Buffer.
|
|
17
|
+
*/
|
|
18
|
+
postMultipartFirmware(path: string, firmware: Buffer | Uint8Array, fields?: Record<string, string>): Promise<unknown>;
|
|
19
|
+
/**
|
|
20
|
+
* Submit C to be compiled on the bench (HAAS-230): the same multipart intake
|
|
21
|
+
* as a firmware, with a `source` text field in place of the `firmware` blob.
|
|
22
|
+
*/
|
|
23
|
+
postMultipartSource(path: string, source: string, fields?: Record<string, string>): Promise<unknown>;
|
|
24
|
+
/**
|
|
25
|
+
* SSE long-poll: open the feed, collect chunks/events for up to `timeoutMs`,
|
|
26
|
+
* then close and return them. Used by tail_serial.
|
|
27
|
+
*/
|
|
28
|
+
streamFeed(path: string, opts: {
|
|
29
|
+
timeoutMs: number;
|
|
30
|
+
maxChunks: number;
|
|
31
|
+
sinceSeq?: number;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
chunks: {
|
|
34
|
+
seq: number;
|
|
35
|
+
data: string;
|
|
36
|
+
}[];
|
|
37
|
+
events: {
|
|
38
|
+
event: string;
|
|
39
|
+
data: string;
|
|
40
|
+
}[];
|
|
41
|
+
timedOut: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBADH,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM;IAG9B,OAAO,CAAC,OAAO;YAOD,KAAK;YAUL,QAAQ;IAShB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQnC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IASvD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQ/C;;OAEG;IACG,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,UAAU,EAC7B,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,OAAO,CAAC;IAiBnB;;;OAGG;IACG,mBAAmB,CACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAClC,OAAO,CAAC,OAAO,CAAC;IAcnB;;;OAGG;IACG,UAAU,CACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAChE,OAAO,CAAC;QAAE,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,MAAM,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;CA6DtH"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal REST client for the haas-lab API. Forwards `Authorization: Bearer <pat>`
|
|
3
|
+
* on every call, throws on non-2xx with the response body for visibility.
|
|
4
|
+
*/
|
|
5
|
+
export class HaasClient {
|
|
6
|
+
baseUrl;
|
|
7
|
+
pat;
|
|
8
|
+
constructor(baseUrl, pat) {
|
|
9
|
+
this.baseUrl = baseUrl;
|
|
10
|
+
this.pat = pat;
|
|
11
|
+
}
|
|
12
|
+
headers(extra = {}) {
|
|
13
|
+
return {
|
|
14
|
+
Authorization: `Bearer ${this.pat}`,
|
|
15
|
+
...extra,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async parse(res) {
|
|
19
|
+
const text = await res.text();
|
|
20
|
+
if (!text)
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(text);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return text;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async ensureOk(res, label) {
|
|
30
|
+
const body = await this.parse(res);
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
const detail = typeof body === "string" ? body : JSON.stringify(body);
|
|
33
|
+
throw new Error(`${label} failed: HTTP ${res.status} — ${detail}`);
|
|
34
|
+
}
|
|
35
|
+
return body;
|
|
36
|
+
}
|
|
37
|
+
async get(path) {
|
|
38
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
39
|
+
method: "GET",
|
|
40
|
+
headers: this.headers(),
|
|
41
|
+
});
|
|
42
|
+
return this.ensureOk(res, `GET ${path}`);
|
|
43
|
+
}
|
|
44
|
+
async postJson(path, body) {
|
|
45
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: this.headers({ "Content-Type": "application/json" }),
|
|
48
|
+
body: JSON.stringify(body),
|
|
49
|
+
});
|
|
50
|
+
return this.ensureOk(res, `POST ${path}`);
|
|
51
|
+
}
|
|
52
|
+
async postEmpty(path) {
|
|
53
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: this.headers(),
|
|
56
|
+
});
|
|
57
|
+
return this.ensureOk(res, `POST ${path}`);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Multipart upload — used by submit_job. Pass the firmware as a Buffer.
|
|
61
|
+
*/
|
|
62
|
+
async postMultipartFirmware(path, firmware, fields = {}) {
|
|
63
|
+
const form = new FormData();
|
|
64
|
+
const blob = new Blob([new Uint8Array(firmware)], {
|
|
65
|
+
type: "application/octet-stream",
|
|
66
|
+
});
|
|
67
|
+
form.append("firmware", blob, "firmware.bin");
|
|
68
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
69
|
+
form.append(k, v);
|
|
70
|
+
}
|
|
71
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: this.headers(), // do not set Content-Type; fetch sets multipart boundary
|
|
74
|
+
body: form,
|
|
75
|
+
});
|
|
76
|
+
return this.ensureOk(res, `POST ${path} (multipart)`);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Submit C to be compiled on the bench (HAAS-230): the same multipart intake
|
|
80
|
+
* as a firmware, with a `source` text field in place of the `firmware` blob.
|
|
81
|
+
*/
|
|
82
|
+
async postMultipartSource(path, source, fields = {}) {
|
|
83
|
+
const form = new FormData();
|
|
84
|
+
form.append("source", source);
|
|
85
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
86
|
+
form.append(k, v);
|
|
87
|
+
}
|
|
88
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: this.headers(), // do not set Content-Type; fetch sets multipart boundary
|
|
91
|
+
body: form,
|
|
92
|
+
});
|
|
93
|
+
return this.ensureOk(res, `POST ${path} (source)`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* SSE long-poll: open the feed, collect chunks/events for up to `timeoutMs`,
|
|
97
|
+
* then close and return them. Used by tail_serial.
|
|
98
|
+
*/
|
|
99
|
+
async streamFeed(path, opts) {
|
|
100
|
+
const ac = new AbortController();
|
|
101
|
+
const timer = setTimeout(() => ac.abort(), opts.timeoutMs);
|
|
102
|
+
const chunks = [];
|
|
103
|
+
const events = [];
|
|
104
|
+
try {
|
|
105
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
106
|
+
method: "GET",
|
|
107
|
+
headers: this.headers({ Accept: "text/event-stream" }),
|
|
108
|
+
signal: ac.signal,
|
|
109
|
+
});
|
|
110
|
+
if (!res.ok || !res.body) {
|
|
111
|
+
await this.ensureOk(res, `GET ${path}`);
|
|
112
|
+
}
|
|
113
|
+
const reader = res.body.getReader();
|
|
114
|
+
const decoder = new TextDecoder();
|
|
115
|
+
let buf = "";
|
|
116
|
+
let currentEvent = "message";
|
|
117
|
+
let currentData = "";
|
|
118
|
+
let seq = opts.sinceSeq ?? 0;
|
|
119
|
+
while (chunks.length < opts.maxChunks) {
|
|
120
|
+
const { done, value } = await reader.read();
|
|
121
|
+
if (done)
|
|
122
|
+
break;
|
|
123
|
+
buf += decoder.decode(value, { stream: true });
|
|
124
|
+
const frames = buf.split(/\n\n/);
|
|
125
|
+
buf = frames.pop() ?? "";
|
|
126
|
+
for (const frame of frames) {
|
|
127
|
+
currentEvent = "message";
|
|
128
|
+
currentData = "";
|
|
129
|
+
let sawData = false;
|
|
130
|
+
for (const line of frame.split(/\n/)) {
|
|
131
|
+
if (line.startsWith("event:"))
|
|
132
|
+
currentEvent = line.slice(6).trim();
|
|
133
|
+
// Several data lines are one value joined with newlines — the
|
|
134
|
+
// specification's rule, and what a multi-line serial chunk needs
|
|
135
|
+
// (HAAS-139). One leading space after the colon is the separator,
|
|
136
|
+
// not content; anything else stays.
|
|
137
|
+
else if (line.startsWith("data:")) {
|
|
138
|
+
const piece = line.slice(5).replace(/^ /, "");
|
|
139
|
+
currentData = currentData === "" && !sawData ? piece : `${currentData}\n${piece}`;
|
|
140
|
+
sawData = true;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
events.push({ event: currentEvent, data: currentData });
|
|
144
|
+
if (currentEvent === "serial" || currentEvent === "message") {
|
|
145
|
+
chunks.push({ seq: ++seq, data: currentData });
|
|
146
|
+
if (chunks.length >= opts.maxChunks)
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
reader.releaseLock();
|
|
153
|
+
}
|
|
154
|
+
catch { /* ignore */ }
|
|
155
|
+
return { chunks, events, timedOut: false };
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
if (ac.signal.aborted) {
|
|
159
|
+
return { chunks, events, timedOut: true };
|
|
160
|
+
}
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
clearTimeout(timer);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `haas-mcp` is told, and what it assumes when told nothing (HAAS-142).
|
|
3
|
+
*
|
|
4
|
+
* Pure, so the one-line install can be tested: the whole point of a one-liner
|
|
5
|
+
* is that the only thing a user supplies is a token, which means every other
|
|
6
|
+
* value has to default to the right thing. The base URL defaulted to a
|
|
7
|
+
* localhost port for the first months of this package's life, so a user who
|
|
8
|
+
* ran the documented `npx haas-mcp` against production would have been
|
|
9
|
+
* talking to nothing.
|
|
10
|
+
*/
|
|
11
|
+
/** Where the hosted service is. A developer running the app locally overrides it. */
|
|
12
|
+
export declare const PRODUCTION_BASE_URL = "https://haas-lab.com";
|
|
13
|
+
export interface CliConfig {
|
|
14
|
+
/** The Personal Access Token, or null when none was given anywhere. */
|
|
15
|
+
pat: string | null;
|
|
16
|
+
/** No trailing slash, so paths concatenate cleanly. */
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
/** Prove the token against the API and exit, instead of serving. */
|
|
19
|
+
check: boolean;
|
|
20
|
+
/** `haas-mcp setup`: connect every AI client on this machine, then exit. */
|
|
21
|
+
setup: boolean;
|
|
22
|
+
/** `setup --paste`: ask for the token at the prompt instead of receiving it over a socket. */
|
|
23
|
+
paste: boolean;
|
|
24
|
+
help: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Flags win over the environment, which wins over the default. `--token` and
|
|
28
|
+
* `--base-url` take a value; `--check` and `--help` stand alone.
|
|
29
|
+
*/
|
|
30
|
+
export declare function readConfig(argv: readonly string[], env: Readonly<Record<string, string | undefined>>): CliConfig;
|
|
31
|
+
/** Printed by `--help`, and the same lines the README and the docs page carry. */
|
|
32
|
+
export declare const USAGE = "haas-mcp \u2014 flash firmware on real boards from any AI client, over MCP.\n\n npx -y haas-mcp setup opens the token page; click Connect there; every AI client here is connected\n npx -y haas-mcp setup --paste the same, pasting the token instead (a Node older than 22 has no WebSocket)\n HAAS_PAT=pat_... npx -y haas-mcp serve (stdio)\n HAAS_PAT=pat_... npx -y haas-mcp --check prove the token, then exit 0 or 1\n npx -y haas-mcp --help\n\nFlags --token <pat> --base-url <url> --check --paste --help\nEnvironment HAAS_PAT (required to serve) HAAS_BASE_URL (default https://haas-lab.com)\n\nMint a token at https://haas-lab.com/dashboard/tokens with the read and submit scopes.\n\nBy hand, one line per client:\n Claude Code claude mcp add haas-lab -e HAAS_PAT=pat_... -- npx -y haas-mcp\n Codex CLI codex mcp add haas-lab --env HAAS_PAT=pat_... -- npx -y haas-mcp\n Claude Desktop, Cursor and others take the JSON form:\n {\"mcpServers\":{\"haas-lab\":{\"command\":\"npx\",\"args\":[\"-y\",\"haas-mcp\"],\"env\":{\"HAAS_PAT\":\"pat_...\"}}}}\n";
|
|
33
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,qFAAqF;AACrF,eAAO,MAAM,mBAAmB,yBAAyB,CAAC;AAE1D,MAAM,WAAW,SAAS;IACxB,uEAAuE;IACvE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,KAAK,EAAE,OAAO,CAAC;IACf,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,8FAA8F;IAC9F,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,GAChD,SAAS,CAoBX;AAED,kFAAkF;AAClF,eAAO,MAAM,KAAK,4mCAkBjB,CAAC"}
|