@skillport/cli 0.1.3 → 0.1.6
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/dist/index.js +142 -64
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2502,12 +2502,45 @@ async function uninstallCommand(skillId) {
|
|
|
2502
2502
|
init_config();
|
|
2503
2503
|
import { createServer } from "node:http";
|
|
2504
2504
|
import { randomBytes } from "node:crypto";
|
|
2505
|
-
import
|
|
2505
|
+
import chalk10 from "chalk";
|
|
2506
2506
|
import inquirer4 from "inquirer";
|
|
2507
|
-
|
|
2507
|
+
|
|
2508
|
+
// src/utils/register-key.ts
|
|
2509
|
+
init_config();
|
|
2510
|
+
import chalk9 from "chalk";
|
|
2511
|
+
async function registerPublicKey(config) {
|
|
2512
|
+
if (!hasKeys()) {
|
|
2513
|
+
return false;
|
|
2514
|
+
}
|
|
2515
|
+
try {
|
|
2516
|
+
const publicKey = loadPublicKey2();
|
|
2517
|
+
const res = await fetch(`${config.marketplace_url}/v1/keys`, {
|
|
2518
|
+
method: "POST",
|
|
2519
|
+
headers: {
|
|
2520
|
+
"Content-Type": "application/json",
|
|
2521
|
+
Authorization: `Bearer ${config.auth_token}`
|
|
2522
|
+
},
|
|
2523
|
+
body: JSON.stringify({ public_key_pem: publicKey, label: "default" })
|
|
2524
|
+
});
|
|
2525
|
+
if (res.ok || res.status === 409) {
|
|
2526
|
+
console.log(chalk9.dim(" Public key registered with marketplace."));
|
|
2527
|
+
return true;
|
|
2528
|
+
}
|
|
2529
|
+
const body = await res.json().catch(() => ({}));
|
|
2530
|
+
console.log(chalk9.yellow(` Warning: Could not register public key: ${body.error || res.statusText}`));
|
|
2531
|
+
return false;
|
|
2532
|
+
} catch {
|
|
2533
|
+
console.log(chalk9.yellow(" Warning: Could not reach marketplace to register public key."));
|
|
2534
|
+
return false;
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
// src/commands/login.ts
|
|
2539
|
+
var DEFAULT_HOST = "127.0.0.1";
|
|
2540
|
+
function listenOnPort(server, port, host) {
|
|
2508
2541
|
return new Promise((resolve, reject) => {
|
|
2509
2542
|
server.once("error", reject);
|
|
2510
|
-
server.listen(port, () => {
|
|
2543
|
+
server.listen(port, host, () => {
|
|
2511
2544
|
server.removeListener("error", reject);
|
|
2512
2545
|
const addr = server.address();
|
|
2513
2546
|
resolve(addr.port);
|
|
@@ -2516,8 +2549,8 @@ function listenOnPort(server, port) {
|
|
|
2516
2549
|
}
|
|
2517
2550
|
async function loginCommand(options) {
|
|
2518
2551
|
const config = loadConfig();
|
|
2519
|
-
console.log(
|
|
2520
|
-
console.log(
|
|
2552
|
+
console.log(chalk10.bold("SkillPort Market Login"));
|
|
2553
|
+
console.log(chalk10.dim(`Marketplace: ${config.marketplace_web_url}`));
|
|
2521
2554
|
console.log();
|
|
2522
2555
|
let method = options.method;
|
|
2523
2556
|
if (options.token) {
|
|
@@ -2530,8 +2563,8 @@ async function loginCommand(options) {
|
|
|
2530
2563
|
name: "method",
|
|
2531
2564
|
message: "Login method:",
|
|
2532
2565
|
choices: [
|
|
2533
|
-
{ name: "
|
|
2534
|
-
{ name: "
|
|
2566
|
+
{ name: "Paste CLI token (recommended)", value: "token" },
|
|
2567
|
+
{ name: "Browser (GitHub OAuth)", value: "browser" }
|
|
2535
2568
|
]
|
|
2536
2569
|
}
|
|
2537
2570
|
]);
|
|
@@ -2539,43 +2572,56 @@ async function loginCommand(options) {
|
|
|
2539
2572
|
}
|
|
2540
2573
|
if (method === "token") {
|
|
2541
2574
|
let token2 = options.token;
|
|
2575
|
+
if (!token2 && options.yes) {
|
|
2576
|
+
console.error(chalk10.red("Error: --token is required in non-interactive mode."));
|
|
2577
|
+
console.log(chalk10.dim(" Get your token at: https://skillport.market/auth/cli-token"));
|
|
2578
|
+
process.exitCode = 1;
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2542
2581
|
if (!token2) {
|
|
2582
|
+
console.log(chalk10.dim("Get your token at: https://skillport.market/auth/cli-token"));
|
|
2583
|
+
console.log();
|
|
2543
2584
|
const answer = await inquirer4.prompt([
|
|
2544
|
-
{ type: "password", name: "token", message: "Enter your
|
|
2585
|
+
{ type: "password", name: "token", message: "Enter your CLI token:" }
|
|
2545
2586
|
]);
|
|
2546
2587
|
token2 = answer.token;
|
|
2547
2588
|
}
|
|
2548
2589
|
config.auth_token = token2;
|
|
2549
2590
|
saveConfig(config);
|
|
2550
|
-
|
|
2591
|
+
await registerPublicKey(config);
|
|
2592
|
+
console.log(chalk10.green("Login successful! Token saved."));
|
|
2551
2593
|
return;
|
|
2552
2594
|
}
|
|
2553
2595
|
const state = randomBytes(16).toString("hex");
|
|
2554
2596
|
const requestedPort = options.port !== void 0 ? parseInt(options.port, 10) : 9876;
|
|
2555
2597
|
const userExplicitPort = options.port !== void 0;
|
|
2598
|
+
const bindHost = options.host || DEFAULT_HOST;
|
|
2556
2599
|
const server = createServer();
|
|
2557
2600
|
let actualPort;
|
|
2558
2601
|
try {
|
|
2559
|
-
actualPort = await listenOnPort(server, requestedPort);
|
|
2602
|
+
actualPort = await listenOnPort(server, requestedPort, bindHost);
|
|
2560
2603
|
} catch (err) {
|
|
2561
2604
|
const code = err.code;
|
|
2562
2605
|
if (code === "EADDRINUSE" && !userExplicitPort) {
|
|
2563
|
-
console.log(
|
|
2564
|
-
actualPort = await listenOnPort(server, 0);
|
|
2606
|
+
console.log(chalk10.yellow(`Port ${requestedPort} in use, selecting a free port...`));
|
|
2607
|
+
actualPort = await listenOnPort(server, 0, bindHost);
|
|
2565
2608
|
} else {
|
|
2566
2609
|
throw err;
|
|
2567
2610
|
}
|
|
2568
2611
|
}
|
|
2569
|
-
const
|
|
2612
|
+
const callbackHost = bindHost === "::1" ? "[::1]" : bindHost;
|
|
2613
|
+
const authUrl = `${config.marketplace_web_url}/auth/cli?state=${state}&port=${actualPort}&host=${encodeURIComponent(callbackHost)}`;
|
|
2570
2614
|
if (options.browser === false) {
|
|
2571
|
-
console.log(
|
|
2615
|
+
console.log(chalk10.bold("Open this URL in your browser to authenticate:"));
|
|
2572
2616
|
console.log();
|
|
2573
2617
|
console.log(` ${authUrl}`);
|
|
2574
2618
|
console.log();
|
|
2575
|
-
console.log(
|
|
2619
|
+
console.log(chalk10.dim(`Listening on ${callbackHost}:${actualPort}`));
|
|
2620
|
+
console.log(chalk10.dim("Waiting for authentication callback..."));
|
|
2576
2621
|
} else {
|
|
2577
|
-
console.log(
|
|
2578
|
-
console.log(
|
|
2622
|
+
console.log(chalk10.dim(`Opening browser to: ${authUrl}`));
|
|
2623
|
+
console.log(chalk10.dim(`Listening on ${callbackHost}:${actualPort}`));
|
|
2624
|
+
console.log(chalk10.dim("Waiting for authentication..."));
|
|
2579
2625
|
const { exec } = await import("node:child_process");
|
|
2580
2626
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2581
2627
|
exec(`${openCmd} "${authUrl}"`);
|
|
@@ -2583,10 +2629,16 @@ async function loginCommand(options) {
|
|
|
2583
2629
|
const token = await new Promise((resolve, reject) => {
|
|
2584
2630
|
const timeout = setTimeout(() => {
|
|
2585
2631
|
server.close();
|
|
2586
|
-
reject(new Error(
|
|
2632
|
+
reject(new Error(
|
|
2633
|
+
`Authentication timed out (60s).
|
|
2634
|
+
Listened on: ${callbackHost}:${actualPort}
|
|
2635
|
+
Self-test: curl http://${callbackHost}:${actualPort}/callback?state=test\\&token=test
|
|
2636
|
+
Retry: skillport login --yes --no-browser --port 0 --host 127.0.0.1
|
|
2637
|
+
Or use: skillport login --method token --token <your-token>`
|
|
2638
|
+
));
|
|
2587
2639
|
}, 6e4);
|
|
2588
2640
|
server.on("request", (req, res) => {
|
|
2589
|
-
const url = new URL(req.url || "", `http
|
|
2641
|
+
const url = new URL(req.url || "", `http://${callbackHost}:${actualPort}`);
|
|
2590
2642
|
if (url.pathname === "/callback") {
|
|
2591
2643
|
const callbackState = url.searchParams.get("state");
|
|
2592
2644
|
const accessToken = url.searchParams.get("token");
|
|
@@ -2627,34 +2679,20 @@ async function loginCommand(options) {
|
|
|
2627
2679
|
config.auth_token = token;
|
|
2628
2680
|
}
|
|
2629
2681
|
saveConfig(config);
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
const publicKey = loadPublicKey2();
|
|
2633
|
-
await fetch(`${config.marketplace_url}/v1/keys`, {
|
|
2634
|
-
method: "POST",
|
|
2635
|
-
headers: {
|
|
2636
|
-
"Content-Type": "application/json",
|
|
2637
|
-
Authorization: `Bearer ${config.auth_token}`
|
|
2638
|
-
},
|
|
2639
|
-
body: JSON.stringify({ public_key_pem: publicKey, label: "default" })
|
|
2640
|
-
});
|
|
2641
|
-
console.log(chalk9.dim(" Public key registered with marketplace."));
|
|
2642
|
-
} catch {
|
|
2643
|
-
}
|
|
2644
|
-
}
|
|
2645
|
-
console.log(chalk9.green("Login successful! Token saved."));
|
|
2682
|
+
await registerPublicKey(config);
|
|
2683
|
+
console.log(chalk10.green("Login successful! Token saved."));
|
|
2646
2684
|
}
|
|
2647
2685
|
|
|
2648
2686
|
// src/commands/publish.ts
|
|
2649
2687
|
import { readFileSync as readFileSync8, existsSync as existsSync5 } from "node:fs";
|
|
2650
2688
|
import { join as join6 } from "node:path";
|
|
2651
2689
|
import { homedir as homedir3 } from "node:os";
|
|
2652
|
-
import
|
|
2690
|
+
import chalk11 from "chalk";
|
|
2653
2691
|
init_config();
|
|
2654
2692
|
async function publishCommand(sspPath) {
|
|
2655
2693
|
const config = loadConfig();
|
|
2656
2694
|
if (!config.auth_token) {
|
|
2657
|
-
console.log(
|
|
2695
|
+
console.log(chalk11.red("Not logged in. Run 'skillport login' first."));
|
|
2658
2696
|
process.exitCode = 1;
|
|
2659
2697
|
return;
|
|
2660
2698
|
}
|
|
@@ -2663,12 +2701,12 @@ async function publishCommand(sspPath) {
|
|
|
2663
2701
|
const extracted = await extractSSP(data);
|
|
2664
2702
|
const { valid } = verifyChecksums(extracted.files, extracted.checksums);
|
|
2665
2703
|
if (!valid) {
|
|
2666
|
-
console.log(
|
|
2704
|
+
console.log(chalk11.red("Checksum verification failed. Cannot publish."));
|
|
2667
2705
|
process.exitCode = 1;
|
|
2668
2706
|
return;
|
|
2669
2707
|
}
|
|
2670
2708
|
if (!extracted.authorSignature) {
|
|
2671
|
-
console.log(
|
|
2709
|
+
console.log(chalk11.red("No author signature. Sign the package first."));
|
|
2672
2710
|
process.exitCode = 1;
|
|
2673
2711
|
return;
|
|
2674
2712
|
}
|
|
@@ -2682,53 +2720,91 @@ async function publishCommand(sspPath) {
|
|
|
2682
2720
|
pubKeyPem
|
|
2683
2721
|
);
|
|
2684
2722
|
if (!sigValid) {
|
|
2685
|
-
console.log(
|
|
2686
|
-
console.log(
|
|
2723
|
+
console.log(chalk11.red("Signature verification failed. Package may have been tampered with after signing."));
|
|
2724
|
+
console.log(chalk11.dim(` Key ID: ${keyId}`));
|
|
2687
2725
|
process.exitCode = 1;
|
|
2688
2726
|
return;
|
|
2689
2727
|
}
|
|
2690
|
-
console.log(
|
|
2728
|
+
console.log(chalk11.green("\u2713 Signature verified"));
|
|
2691
2729
|
} else {
|
|
2692
|
-
console.log(
|
|
2693
|
-
console.log(
|
|
2730
|
+
console.log(chalk11.yellow("\u26A0 Local public key not found \u2014 skipping local signature check"));
|
|
2731
|
+
console.log(chalk11.dim(" Server will verify signature against registered key."));
|
|
2694
2732
|
}
|
|
2695
2733
|
console.log("Uploading to marketplace...");
|
|
2696
|
-
|
|
2734
|
+
async function upload() {
|
|
2697
2735
|
const formData = new FormData();
|
|
2698
2736
|
formData.append("file", new Blob([data]), sspPath.split("/").pop());
|
|
2699
|
-
|
|
2737
|
+
return fetch(`${config.marketplace_url}/v1/skills`, {
|
|
2700
2738
|
method: "POST",
|
|
2701
|
-
headers: {
|
|
2702
|
-
Authorization: `Bearer ${config.auth_token}`
|
|
2703
|
-
},
|
|
2739
|
+
headers: { Authorization: `Bearer ${config.auth_token}` },
|
|
2704
2740
|
body: formData
|
|
2705
2741
|
});
|
|
2742
|
+
}
|
|
2743
|
+
try {
|
|
2744
|
+
let response = await upload();
|
|
2706
2745
|
if (!response.ok) {
|
|
2707
|
-
const errorBody = await response.json();
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2746
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
2747
|
+
const errorMsg = String(errorBody.error || "");
|
|
2748
|
+
if (errorMsg.includes("Signing key is not registered") && hasKeys()) {
|
|
2749
|
+
console.log(chalk11.yellow("Signing key not registered. Registering automatically..."));
|
|
2750
|
+
const registered = await registerPublicKey(config);
|
|
2751
|
+
if (registered) {
|
|
2752
|
+
console.log("Retrying upload...");
|
|
2753
|
+
response = await upload();
|
|
2754
|
+
} else {
|
|
2755
|
+
console.log(chalk11.red("Could not register key. Run 'skillport keys register' manually."));
|
|
2756
|
+
process.exitCode = 1;
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
if (!response.ok) {
|
|
2761
|
+
const retryBody = await response.json().catch(() => ({}));
|
|
2762
|
+
console.log(chalk11.red(`Upload failed: ${retryBody.error || response.statusText}`));
|
|
2763
|
+
process.exitCode = 1;
|
|
2764
|
+
return;
|
|
2765
|
+
}
|
|
2711
2766
|
}
|
|
2712
2767
|
const result = await response.json();
|
|
2713
|
-
console.log(
|
|
2768
|
+
console.log(chalk11.green("Published successfully!"));
|
|
2714
2769
|
console.log();
|
|
2715
|
-
console.log(` ${
|
|
2716
|
-
console.log(` ${
|
|
2717
|
-
console.log(` ${
|
|
2718
|
-
console.log(` ${
|
|
2719
|
-
console.log(` ${
|
|
2770
|
+
console.log(` ${chalk11.bold("Skill ID:")} ${result.id}`);
|
|
2771
|
+
console.log(` ${chalk11.bold("SSP ID:")} ${result.ssp_id}`);
|
|
2772
|
+
console.log(` ${chalk11.bold("Version:")} ${result.version}`);
|
|
2773
|
+
console.log(` ${chalk11.bold("Scan:")} ${result.scan_passed ? chalk11.green("PASSED") : chalk11.red("FAILED")}`);
|
|
2774
|
+
console.log(` ${chalk11.bold("Risk Score:")} ${result.risk_score}/100`);
|
|
2720
2775
|
console.log();
|
|
2721
|
-
console.log(
|
|
2722
|
-
console.log(
|
|
2776
|
+
console.log(chalk11.dim(` URL: ${config.marketplace_web_url}/skills/${result.id}`));
|
|
2777
|
+
console.log(chalk11.dim(` Install: skillport install ${result.ssp_id}@${result.version}`));
|
|
2723
2778
|
} catch (error) {
|
|
2724
|
-
console.log(
|
|
2779
|
+
console.log(chalk11.red(`Upload failed: ${error.message}`));
|
|
2780
|
+
process.exitCode = 1;
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
// src/commands/keys-register.ts
|
|
2785
|
+
init_config();
|
|
2786
|
+
import chalk12 from "chalk";
|
|
2787
|
+
async function keysRegisterCommand() {
|
|
2788
|
+
const config = loadConfig();
|
|
2789
|
+
if (!config.auth_token) {
|
|
2790
|
+
console.log(chalk12.red("Not logged in. Run 'skillport login' first."));
|
|
2791
|
+
process.exitCode = 1;
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
if (!hasKeys()) {
|
|
2795
|
+
console.log(chalk12.red("No signing keys found. Run 'skillport init' first."));
|
|
2796
|
+
process.exitCode = 1;
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2799
|
+
const ok = await registerPublicKey(config);
|
|
2800
|
+
if (!ok) {
|
|
2725
2801
|
process.exitCode = 1;
|
|
2726
2802
|
}
|
|
2727
2803
|
}
|
|
2728
2804
|
|
|
2729
2805
|
// src/index.ts
|
|
2730
2806
|
var program = new Command();
|
|
2731
|
-
program.name("skillport").description("SkillPort \u2014 secure skill distribution for OpenClaw").version("0.1.
|
|
2807
|
+
program.name("skillport").description("SkillPort \u2014 secure skill distribution for OpenClaw").version("0.1.6");
|
|
2732
2808
|
program.command("init").description("Generate Ed25519 key pair for signing").action(initCommand);
|
|
2733
2809
|
program.command("scan <path>").description("Run security scan on a skill directory or .ssp file").action(scanCommand);
|
|
2734
2810
|
program.command("export <path>").description("Export a skill directory as a SkillPort package (.ssp)").option("-o, --output <file>", "Output file path").option("-y, --yes", "Non-interactive mode (include all, skip prompts)").option("--id <id>", "Skill ID (author-slug/skill-slug)").option("--name <name>", "Skill name").option("--description <desc>", "Skill description").option("--skill-version <ver>", "Skill version (semver)").option("--author <name>", "Author name").option("--openclaw-compat <range>", "OpenClaw compatibility range").option("--os <os...>", "Compatible OS (macos, linux, windows)").action(exportCommand);
|
|
@@ -2737,6 +2813,8 @@ program.command("verify <ssp>").description("Verify SkillPort package signatures
|
|
|
2737
2813
|
program.command("install <target>").description("Install a SkillPort package").option("--accept-risk", "Accept high-risk permissions (shell, critical flags)").option("-y, --yes", "Non-interactive mode (auto-approve, use defaults)").action(installCommand);
|
|
2738
2814
|
program.command("dry-run <ssp>").description("Run installation diagnostics without installing").action(dryRunCommand);
|
|
2739
2815
|
program.command("uninstall <id>").description("Uninstall an installed skill").action(uninstallCommand);
|
|
2740
|
-
program.command("login").description("Authenticate with SkillPort Market").option("--method <method>", "Login method: browser or token", "browser").option("--token <token>", "API token (for --method token)").option("-y, --yes", "Non-interactive mode (skip prompts)").option("--no-browser", "Print auth URL instead of opening browser").option("--port <port>", "Callback port (default: 9876, use 0 for auto)").action(loginCommand);
|
|
2816
|
+
program.command("login").description("Authenticate with SkillPort Market").option("--method <method>", "Login method: browser or token", "browser").option("--token <token>", "API token (for --method token)").option("-y, --yes", "Non-interactive mode (skip prompts)").option("--no-browser", "Print auth URL instead of opening browser").option("--port <port>", "Callback port (default: 9876, use 0 for auto)").option("--host <host>", "Callback host (default: 127.0.0.1)").action(loginCommand);
|
|
2741
2817
|
program.command("publish <ssp>").description("Publish a SkillPort package to the marketplace").action(publishCommand);
|
|
2818
|
+
var keys = program.command("keys").description("Manage signing keys");
|
|
2819
|
+
keys.command("register").description("Register your public signing key with the marketplace").action(keysRegisterCommand);
|
|
2742
2820
|
program.parse();
|
package/package.json
CHANGED