@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.
Files changed (2) hide show
  1. package/dist/index.js +125 -58
  2. 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 chalk9 from "chalk";
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(chalk9.bold("SkillPort Market Login"));
2521
- console.log(chalk9.dim(`Marketplace: ${config.marketplace_web_url}`));
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: "Browser (GitHub OAuth)", value: "browser" },
2535
- { name: "Paste API token", value: "token" }
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 API token:" }
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
- console.log(chalk9.green("Login successful! Token saved."));
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(chalk9.yellow(`Port ${requestedPort} in use, selecting a free port...`));
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(chalk9.bold("Open this URL in your browser to authenticate:"));
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(chalk9.dim(`Listening on ${callbackHost}:${actualPort}`));
2579
- console.log(chalk9.dim("Waiting for authentication callback..."));
2619
+ console.log(chalk10.dim(`Listening on ${callbackHost}:${actualPort}`));
2620
+ console.log(chalk10.dim("Waiting for authentication callback..."));
2580
2621
  } else {
2581
- console.log(chalk9.dim(`Opening browser to: ${authUrl}`));
2582
- console.log(chalk9.dim(`Listening on ${callbackHost}:${actualPort}`));
2583
- console.log(chalk9.dim("Waiting for authentication..."));
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
- if (hasKeys()) {
2642
- try {
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 chalk10 from "chalk";
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(chalk10.red("Not logged in. Run 'skillport login' first."));
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(chalk10.red("Checksum verification failed. Cannot publish."));
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(chalk10.red("No author signature. Sign the package first."));
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(chalk10.red("Signature verification failed. Package may have been tampered with after signing."));
2697
- console.log(chalk10.dim(` Key ID: ${keyId}`));
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(chalk10.green("\u2713 Signature verified"));
2728
+ console.log(chalk11.green("\u2713 Signature verified"));
2702
2729
  } else {
2703
- console.log(chalk10.yellow("\u26A0 Local public key not found \u2014 skipping local signature check"));
2704
- console.log(chalk10.dim(" Server will verify signature against registered key."));
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
- try {
2734
+ async function upload() {
2708
2735
  const formData = new FormData();
2709
2736
  formData.append("file", new Blob([data]), sspPath.split("/").pop());
2710
- const response = await fetch(`${config.marketplace_url}/v1/skills`, {
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
- console.log(chalk10.red(`Upload failed: ${errorBody.error || response.statusText}`));
2720
- process.exitCode = 1;
2721
- return;
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(chalk10.green("Published successfully!"));
2768
+ console.log(chalk11.green("Published successfully!"));
2725
2769
  console.log();
2726
- console.log(` ${chalk10.bold("Skill ID:")} ${result.id}`);
2727
- console.log(` ${chalk10.bold("SSP ID:")} ${result.ssp_id}`);
2728
- console.log(` ${chalk10.bold("Version:")} ${result.version}`);
2729
- console.log(` ${chalk10.bold("Scan:")} ${result.scan_passed ? chalk10.green("PASSED") : chalk10.red("FAILED")}`);
2730
- console.log(` ${chalk10.bold("Risk Score:")} ${result.risk_score}/100`);
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(chalk10.dim(` URL: ${config.marketplace_web_url}/skills/${result.id}`));
2733
- console.log(chalk10.dim(` Install: skillport install ${result.ssp_id}@${result.version}`));
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(chalk10.red(`Upload failed: ${error.message}`));
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.4");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillport/cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "SkillPort CLI — secure skill distribution for OpenClaw. Export, scan, sign, and install AI skill packages.",
5
5
  "type": "module",
6
6
  "license": "MIT",