@super-hands/connect 0.1.17 → 0.1.18
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 +15 -6
- package/client.mjs +64 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,12 +3,20 @@
|
|
|
3
3
|
Connect the coding agents on this machine to your team's
|
|
4
4
|
[Superhands](https://app.superhands.ai) MCP server, in one command.
|
|
5
5
|
|
|
6
|
-
Superhands
|
|
6
|
+
Superhands generates the command for you, with a short-lived setup code on
|
|
7
|
+
the end of it:
|
|
7
8
|
|
|
8
9
|
```
|
|
9
|
-
|
|
10
|
+
npx -y @super-hands/connect@latest --code shsetup_…
|
|
10
11
|
```
|
|
11
12
|
|
|
13
|
+
The client exchanges that code for the credential over one request to your
|
|
14
|
+
deployment and writes the credential into the clients below. The code is
|
|
15
|
+
worth nothing twenty minutes later; the token it becomes never appears in the
|
|
16
|
+
command, the prompt you pasted it from, or your shell history. A command
|
|
17
|
+
written by hand around a token from the Agents page still works:
|
|
18
|
+
`SUPERHANDS_MCP_TOKEN="…" npx -y @super-hands/connect@latest`.
|
|
19
|
+
|
|
12
20
|
It connects to `https://app.superhands.ai/api/mcp` unless `SUPERHANDS_MCP_URL`
|
|
13
21
|
says otherwise — a command from a preview or local deployment carries that
|
|
14
22
|
variable too.
|
|
@@ -71,10 +79,11 @@ remove one client and leave the rest.
|
|
|
71
79
|
|
|
72
80
|
## What it does not do
|
|
73
81
|
|
|
74
|
-
It reads no repository, runs none of your code, and uploads nothing
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
It reads no repository, runs none of your code, and uploads nothing but the
|
|
83
|
+
setup code it was given, once, to the deployment that issued it. The one
|
|
84
|
+
secret it then holds is the token that came back, and it is written only into
|
|
85
|
+
the client configs above. The token can be revoked at any time from the
|
|
86
|
+
Superhands Agents page.
|
|
78
87
|
|
|
79
88
|
This file is generated from
|
|
80
89
|
[`lib/connect-client-entry.ts`](https://github.com/superhandsai/superhands/blob/main/lib/connect-client-entry.ts)
|
package/client.mjs
CHANGED
|
@@ -21,6 +21,7 @@ var MCP_ACCESS_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
|
21
21
|
// lib/mcp-clients.ts
|
|
22
22
|
var CONNECT_TOKEN_ENV = "SUPERHANDS_MCP_TOKEN";
|
|
23
23
|
var CONNECT_URL_ENV = "SUPERHANDS_MCP_URL";
|
|
24
|
+
var CONNECT_CODE_FLAG = "--code";
|
|
24
25
|
var CONNECT_DEFAULT_MCP_URL = "https://app.superhands.ai/api/mcp";
|
|
25
26
|
var CONNECT_CLIENT_PACKAGE = "@super-hands/connect";
|
|
26
27
|
var CONNECT_CLIENT_SPEC = `${CONNECT_CLIENT_PACKAGE}@latest`;
|
|
@@ -328,6 +329,59 @@ function writeSkill(clientDir) {
|
|
|
328
329
|
function connectReportUrl(endpoint) {
|
|
329
330
|
return `${endpoint.replace(/\/+$/, "")}/connect`;
|
|
330
331
|
}
|
|
332
|
+
function connectExchangeUrl(endpoint) {
|
|
333
|
+
return `${connectReportUrl(endpoint)}/exchange`;
|
|
334
|
+
}
|
|
335
|
+
function takeCodeArgument(argv) {
|
|
336
|
+
const rest = [];
|
|
337
|
+
let code = null;
|
|
338
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
339
|
+
const arg = argv[i];
|
|
340
|
+
if (arg === CONNECT_CODE_FLAG) {
|
|
341
|
+
const next = argv[i + 1];
|
|
342
|
+
if (next !== void 0 && !next.startsWith("--")) {
|
|
343
|
+
code = next;
|
|
344
|
+
i += 1;
|
|
345
|
+
} else {
|
|
346
|
+
code = "";
|
|
347
|
+
}
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (arg.startsWith(`${CONNECT_CODE_FLAG}=`)) {
|
|
351
|
+
code = arg.slice(CONNECT_CODE_FLAG.length + 1);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
rest.push(arg);
|
|
355
|
+
}
|
|
356
|
+
return { code, rest };
|
|
357
|
+
}
|
|
358
|
+
async function exchangeSetupCode(args) {
|
|
359
|
+
let response;
|
|
360
|
+
try {
|
|
361
|
+
response = await fetch(connectExchangeUrl(args.endpoint), {
|
|
362
|
+
method: "POST",
|
|
363
|
+
headers: { "content-type": "application/json" },
|
|
364
|
+
body: JSON.stringify({ code: args.code }),
|
|
365
|
+
signal: AbortSignal.timeout(1e4)
|
|
366
|
+
});
|
|
367
|
+
} catch {
|
|
368
|
+
return stop(
|
|
369
|
+
`Superhands at ${args.endpoint} could not be reached to turn the setup code into a credential. Check the connection and run this command again.`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const body = await response.json().catch(() => null);
|
|
373
|
+
if (response.ok && body?.ok && typeof body.token === "string" && body.token) {
|
|
374
|
+
return body.token;
|
|
375
|
+
}
|
|
376
|
+
if (response.status === 401 || response.status === 400) {
|
|
377
|
+
return stop(
|
|
378
|
+
"this setup code has expired or has been used up. Get a fresh command from Superhands and run it unchanged."
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return stop(
|
|
382
|
+
`Superhands answered ${response.status} to the setup code. Try again in a moment, or get a fresh command from Superhands.`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
331
385
|
async function reportSkillVersion(args) {
|
|
332
386
|
try {
|
|
333
387
|
await fetch(connectReportUrl(args.endpoint), {
|
|
@@ -427,7 +481,7 @@ function chosenClients(flags) {
|
|
|
427
481
|
return explicit ? { cursor, claude, codex, explicit } : { cursor: true, claude: true, codex: true, explicit };
|
|
428
482
|
}
|
|
429
483
|
async function main() {
|
|
430
|
-
const argv = process.argv.slice(2);
|
|
484
|
+
const { code, rest: argv } = takeCodeArgument(process.argv.slice(2));
|
|
431
485
|
const flags = new Set(argv.filter((arg) => arg.startsWith("--")));
|
|
432
486
|
const command = argv.find((arg) => !arg.startsWith("--")) ?? "install";
|
|
433
487
|
if (command === "uninstall") return uninstall(flags);
|
|
@@ -437,14 +491,19 @@ async function main() {
|
|
|
437
491
|
`unknown command "${command}". This tool takes "install" (the default), "update" or "uninstall".`
|
|
438
492
|
);
|
|
439
493
|
}
|
|
440
|
-
return install(flags);
|
|
494
|
+
return install(flags, code);
|
|
441
495
|
}
|
|
442
|
-
async function install(flags) {
|
|
443
|
-
const token = process.env[CONNECT_TOKEN_ENV]?.trim();
|
|
496
|
+
async function install(flags, code) {
|
|
444
497
|
const endpoint = process.env[CONNECT_URL_ENV]?.trim() || CONNECT_DEFAULT_MCP_URL;
|
|
498
|
+
if (code === "") {
|
|
499
|
+
stop(
|
|
500
|
+
`${CONNECT_CODE_FLAG} needs the setup code after it. Copy the whole command from Superhands and run it unchanged.`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
const token = code ? await exchangeSetupCode({ endpoint, code }) : process.env[CONNECT_TOKEN_ENV]?.trim();
|
|
445
504
|
if (!token) {
|
|
446
505
|
stop(
|
|
447
|
-
`this command needs ${CONNECT_TOKEN_ENV} set on the same line. Copy the whole command from Superhands
|
|
506
|
+
`this command needs a setup code after ${CONNECT_CODE_FLAG}, or ${CONNECT_TOKEN_ENV} set on the same line. Copy the whole command from Superhands and run it unchanged.`
|
|
448
507
|
);
|
|
449
508
|
}
|
|
450
509
|
const wanted = chosenClients(flags);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@super-hands/connect",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Connect the coding agents on this machine to your team's Superhands MCP server, refresh it later with `update` (no token needed), and take it back off again with `uninstall`. Writes each client's own config; reads no repository, uploads nothing.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"superhands-connect": "client.mjs"
|