@skillport/cli 0.1.4 → 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 +125 -58
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2502,8 +2502,40 @@ 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
|
+
|
|
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
|
|
2507
2539
|
var DEFAULT_HOST = "127.0.0.1";
|
|
2508
2540
|
function listenOnPort(server, port, host) {
|
|
2509
2541
|
return new Promise((resolve, reject) => {
|
|
@@ -2517,8 +2549,8 @@ function listenOnPort(server, port, host) {
|
|
|
2517
2549
|
}
|
|
2518
2550
|
async function loginCommand(options) {
|
|
2519
2551
|
const config = loadConfig();
|
|
2520
|
-
console.log(
|
|
2521
|
-
console.log(
|
|
2552
|
+
console.log(chalk10.bold("SkillPort Market Login"));
|
|
2553
|
+
console.log(chalk10.dim(`Marketplace: ${config.marketplace_web_url}`));
|
|
2522
2554
|
console.log();
|
|
2523
2555
|
let method = options.method;
|
|
2524
2556
|
if (options.token) {
|
|
@@ -2531,8 +2563,8 @@ async function loginCommand(options) {
|
|
|
2531
2563
|
name: "method",
|
|
2532
2564
|
message: "Login method:",
|
|
2533
2565
|
choices: [
|
|
2534
|
-
{ name: "
|
|
2535
|
-
{ name: "
|
|
2566
|
+
{ name: "Paste CLI token (recommended)", value: "token" },
|
|
2567
|
+
{ name: "Browser (GitHub OAuth)", value: "browser" }
|
|
2536
2568
|
]
|
|
2537
2569
|
}
|
|
2538
2570
|
]);
|
|
@@ -2540,15 +2572,24 @@ async function loginCommand(options) {
|
|
|
2540
2572
|
}
|
|
2541
2573
|
if (method === "token") {
|
|
2542
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
|
+
}
|
|
2543
2581
|
if (!token2) {
|
|
2582
|
+
console.log(chalk10.dim("Get your token at: https://skillport.market/auth/cli-token"));
|
|
2583
|
+
console.log();
|
|
2544
2584
|
const answer = await inquirer4.prompt([
|
|
2545
|
-
{ type: "password", name: "token", message: "Enter your
|
|
2585
|
+
{ type: "password", name: "token", message: "Enter your CLI token:" }
|
|
2546
2586
|
]);
|
|
2547
2587
|
token2 = answer.token;
|
|
2548
2588
|
}
|
|
2549
2589
|
config.auth_token = token2;
|
|
2550
2590
|
saveConfig(config);
|
|
2551
|
-
|
|
2591
|
+
await registerPublicKey(config);
|
|
2592
|
+
console.log(chalk10.green("Login successful! Token saved."));
|
|
2552
2593
|
return;
|
|
2553
2594
|
}
|
|
2554
2595
|
const state = randomBytes(16).toString("hex");
|
|
@@ -2562,7 +2603,7 @@ async function loginCommand(options) {
|
|
|
2562
2603
|
} catch (err) {
|
|
2563
2604
|
const code = err.code;
|
|
2564
2605
|
if (code === "EADDRINUSE" && !userExplicitPort) {
|
|
2565
|
-
console.log(
|
|
2606
|
+
console.log(chalk10.yellow(`Port ${requestedPort} in use, selecting a free port...`));
|
|
2566
2607
|
actualPort = await listenOnPort(server, 0, bindHost);
|
|
2567
2608
|
} else {
|
|
2568
2609
|
throw err;
|
|
@@ -2571,16 +2612,16 @@ async function loginCommand(options) {
|
|
|
2571
2612
|
const callbackHost = bindHost === "::1" ? "[::1]" : bindHost;
|
|
2572
2613
|
const authUrl = `${config.marketplace_web_url}/auth/cli?state=${state}&port=${actualPort}&host=${encodeURIComponent(callbackHost)}`;
|
|
2573
2614
|
if (options.browser === false) {
|
|
2574
|
-
console.log(
|
|
2615
|
+
console.log(chalk10.bold("Open this URL in your browser to authenticate:"));
|
|
2575
2616
|
console.log();
|
|
2576
2617
|
console.log(` ${authUrl}`);
|
|
2577
2618
|
console.log();
|
|
2578
|
-
console.log(
|
|
2579
|
-
console.log(
|
|
2619
|
+
console.log(chalk10.dim(`Listening on ${callbackHost}:${actualPort}`));
|
|
2620
|
+
console.log(chalk10.dim("Waiting for authentication callback..."));
|
|
2580
2621
|
} else {
|
|
2581
|
-
console.log(
|
|
2582
|
-
console.log(
|
|
2583
|
-
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..."));
|
|
2584
2625
|
const { exec } = await import("node:child_process");
|
|
2585
2626
|
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2586
2627
|
exec(`${openCmd} "${authUrl}"`);
|
|
@@ -2638,34 +2679,20 @@ async function loginCommand(options) {
|
|
|
2638
2679
|
config.auth_token = token;
|
|
2639
2680
|
}
|
|
2640
2681
|
saveConfig(config);
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
const publicKey = loadPublicKey2();
|
|
2644
|
-
await fetch(`${config.marketplace_url}/v1/keys`, {
|
|
2645
|
-
method: "POST",
|
|
2646
|
-
headers: {
|
|
2647
|
-
"Content-Type": "application/json",
|
|
2648
|
-
Authorization: `Bearer ${config.auth_token}`
|
|
2649
|
-
},
|
|
2650
|
-
body: JSON.stringify({ public_key_pem: publicKey, label: "default" })
|
|
2651
|
-
});
|
|
2652
|
-
console.log(chalk9.dim(" Public key registered with marketplace."));
|
|
2653
|
-
} catch {
|
|
2654
|
-
}
|
|
2655
|
-
}
|
|
2656
|
-
console.log(chalk9.green("Login successful! Token saved."));
|
|
2682
|
+
await registerPublicKey(config);
|
|
2683
|
+
console.log(chalk10.green("Login successful! Token saved."));
|
|
2657
2684
|
}
|
|
2658
2685
|
|
|
2659
2686
|
// src/commands/publish.ts
|
|
2660
2687
|
import { readFileSync as readFileSync8, existsSync as existsSync5 } from "node:fs";
|
|
2661
2688
|
import { join as join6 } from "node:path";
|
|
2662
2689
|
import { homedir as homedir3 } from "node:os";
|
|
2663
|
-
import
|
|
2690
|
+
import chalk11 from "chalk";
|
|
2664
2691
|
init_config();
|
|
2665
2692
|
async function publishCommand(sspPath) {
|
|
2666
2693
|
const config = loadConfig();
|
|
2667
2694
|
if (!config.auth_token) {
|
|
2668
|
-
console.log(
|
|
2695
|
+
console.log(chalk11.red("Not logged in. Run 'skillport login' first."));
|
|
2669
2696
|
process.exitCode = 1;
|
|
2670
2697
|
return;
|
|
2671
2698
|
}
|
|
@@ -2674,12 +2701,12 @@ async function publishCommand(sspPath) {
|
|
|
2674
2701
|
const extracted = await extractSSP(data);
|
|
2675
2702
|
const { valid } = verifyChecksums(extracted.files, extracted.checksums);
|
|
2676
2703
|
if (!valid) {
|
|
2677
|
-
console.log(
|
|
2704
|
+
console.log(chalk11.red("Checksum verification failed. Cannot publish."));
|
|
2678
2705
|
process.exitCode = 1;
|
|
2679
2706
|
return;
|
|
2680
2707
|
}
|
|
2681
2708
|
if (!extracted.authorSignature) {
|
|
2682
|
-
console.log(
|
|
2709
|
+
console.log(chalk11.red("No author signature. Sign the package first."));
|
|
2683
2710
|
process.exitCode = 1;
|
|
2684
2711
|
return;
|
|
2685
2712
|
}
|
|
@@ -2693,53 +2720,91 @@ async function publishCommand(sspPath) {
|
|
|
2693
2720
|
pubKeyPem
|
|
2694
2721
|
);
|
|
2695
2722
|
if (!sigValid) {
|
|
2696
|
-
console.log(
|
|
2697
|
-
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}`));
|
|
2698
2725
|
process.exitCode = 1;
|
|
2699
2726
|
return;
|
|
2700
2727
|
}
|
|
2701
|
-
console.log(
|
|
2728
|
+
console.log(chalk11.green("\u2713 Signature verified"));
|
|
2702
2729
|
} else {
|
|
2703
|
-
console.log(
|
|
2704
|
-
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."));
|
|
2705
2732
|
}
|
|
2706
2733
|
console.log("Uploading to marketplace...");
|
|
2707
|
-
|
|
2734
|
+
async function upload() {
|
|
2708
2735
|
const formData = new FormData();
|
|
2709
2736
|
formData.append("file", new Blob([data]), sspPath.split("/").pop());
|
|
2710
|
-
|
|
2737
|
+
return fetch(`${config.marketplace_url}/v1/skills`, {
|
|
2711
2738
|
method: "POST",
|
|
2712
|
-
headers: {
|
|
2713
|
-
Authorization: `Bearer ${config.auth_token}`
|
|
2714
|
-
},
|
|
2739
|
+
headers: { Authorization: `Bearer ${config.auth_token}` },
|
|
2715
2740
|
body: formData
|
|
2716
2741
|
});
|
|
2742
|
+
}
|
|
2743
|
+
try {
|
|
2744
|
+
let response = await upload();
|
|
2717
2745
|
if (!response.ok) {
|
|
2718
|
-
const errorBody = await response.json();
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
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
|
+
}
|
|
2722
2766
|
}
|
|
2723
2767
|
const result = await response.json();
|
|
2724
|
-
console.log(
|
|
2768
|
+
console.log(chalk11.green("Published successfully!"));
|
|
2725
2769
|
console.log();
|
|
2726
|
-
console.log(` ${
|
|
2727
|
-
console.log(` ${
|
|
2728
|
-
console.log(` ${
|
|
2729
|
-
console.log(` ${
|
|
2730
|
-
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`);
|
|
2731
2775
|
console.log();
|
|
2732
|
-
console.log(
|
|
2733
|
-
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}`));
|
|
2734
2778
|
} catch (error) {
|
|
2735
|
-
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) {
|
|
2736
2801
|
process.exitCode = 1;
|
|
2737
2802
|
}
|
|
2738
2803
|
}
|
|
2739
2804
|
|
|
2740
2805
|
// src/index.ts
|
|
2741
2806
|
var program = new Command();
|
|
2742
|
-
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");
|
|
2743
2808
|
program.command("init").description("Generate Ed25519 key pair for signing").action(initCommand);
|
|
2744
2809
|
program.command("scan <path>").description("Run security scan on a skill directory or .ssp file").action(scanCommand);
|
|
2745
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);
|
|
@@ -2750,4 +2815,6 @@ program.command("dry-run <ssp>").description("Run installation diagnostics witho
|
|
|
2750
2815
|
program.command("uninstall <id>").description("Uninstall an installed skill").action(uninstallCommand);
|
|
2751
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);
|
|
2752
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);
|
|
2753
2820
|
program.parse();
|
package/package.json
CHANGED