@skillport/cli 0.1.6 → 1.0.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.
Files changed (2) hide show
  1. package/dist/index.js +345 -7
  2. package/package.json +5 -5
package/dist/index.js CHANGED
@@ -2765,34 +2765,227 @@ async function publishCommand(sspPath) {
2765
2765
  }
2766
2766
  }
2767
2767
  const result = await response.json();
2768
- console.log(chalk11.green("Published successfully!"));
2768
+ if (result.status === "published") {
2769
+ console.log(chalk11.green("Version updated successfully!"));
2770
+ } else {
2771
+ console.log(chalk11.green("Uploaded as draft."));
2772
+ }
2769
2773
  console.log();
2770
2774
  console.log(` ${chalk11.bold("Skill ID:")} ${result.id}`);
2771
2775
  console.log(` ${chalk11.bold("SSP ID:")} ${result.ssp_id}`);
2772
2776
  console.log(` ${chalk11.bold("Version:")} ${result.version}`);
2777
+ console.log(` ${chalk11.bold("Status:")} ${result.status === "published" ? chalk11.green("published") : chalk11.yellow(result.status || "draft")}`);
2773
2778
  console.log(` ${chalk11.bold("Scan:")} ${result.scan_passed ? chalk11.green("PASSED") : chalk11.red("FAILED")}`);
2774
2779
  console.log(` ${chalk11.bold("Risk Score:")} ${result.risk_score}/100`);
2775
2780
  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}`));
2781
+ if (result.status === "published") {
2782
+ console.log(chalk11.dim(` URL: ${config.marketplace_web_url}/skills/${result.id}`));
2783
+ console.log(chalk11.dim(` Install: skillport install ${result.ssp_id}@${result.version}`));
2784
+ } else {
2785
+ console.log(chalk11.dim(` Go to Dashboard to publish: ${config.marketplace_web_url}/dashboard`));
2786
+ }
2778
2787
  } catch (error) {
2779
2788
  console.log(chalk11.red(`Upload failed: ${error.message}`));
2780
2789
  process.exitCode = 1;
2781
2790
  }
2782
2791
  }
2783
2792
 
2793
+ // src/commands/whoami.ts
2794
+ import { existsSync as existsSync6 } from "node:fs";
2795
+ import chalk12 from "chalk";
2796
+ init_config();
2797
+ function gather() {
2798
+ const cfgPath = configPath();
2799
+ const cfgExists = existsSync6(cfgPath);
2800
+ const config = loadConfig();
2801
+ const keysExist = hasKeys();
2802
+ let localKeyId = null;
2803
+ if (keysExist) {
2804
+ try {
2805
+ const pem = loadPublicKey2();
2806
+ localKeyId = computeKeyId(pem);
2807
+ } catch {
2808
+ }
2809
+ }
2810
+ return {
2811
+ config_path: cfgPath,
2812
+ config_exists: cfgExists,
2813
+ marketplace_url: config.marketplace_url,
2814
+ marketplace_web_url: config.marketplace_web_url,
2815
+ authenticated: !!config.auth_token,
2816
+ default_key_id: config.default_key_id ?? null,
2817
+ keys_exist: keysExist,
2818
+ local_key_id: localKeyId
2819
+ };
2820
+ }
2821
+ function whoamiCommand(opts) {
2822
+ const info = gather();
2823
+ if (opts.json) {
2824
+ console.log(JSON.stringify(info, null, 2));
2825
+ return;
2826
+ }
2827
+ console.log(chalk12.bold("SkillPort CLI"));
2828
+ console.log();
2829
+ console.log(` ${chalk12.bold("Config:")} ${info.config_exists ? chalk12.green(info.config_path) : chalk12.yellow("not created yet")}`);
2830
+ console.log(` ${chalk12.bold("API:")} ${info.marketplace_url}`);
2831
+ console.log(` ${chalk12.bold("Web:")} ${info.marketplace_web_url}`);
2832
+ console.log(` ${chalk12.bold("Authenticated:")} ${info.authenticated ? chalk12.green("yes") : chalk12.red("no")}`);
2833
+ console.log(` ${chalk12.bold("Signing keys:")} ${info.keys_exist ? chalk12.green("present") : chalk12.red("not found")}`);
2834
+ if (info.local_key_id) {
2835
+ console.log(` ${chalk12.bold("Key ID:")} ${info.local_key_id}`);
2836
+ }
2837
+ if (info.default_key_id) {
2838
+ console.log(` ${chalk12.bold("Default key:")} ${info.default_key_id}`);
2839
+ }
2840
+ }
2841
+
2842
+ // src/commands/doctor.ts
2843
+ import { existsSync as existsSync7 } from "node:fs";
2844
+ import chalk13 from "chalk";
2845
+ init_config();
2846
+ async function fetchHealth(url, timeoutMs = 5e3) {
2847
+ try {
2848
+ const controller = new AbortController();
2849
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2850
+ const res = await fetch(`${url}/health`, { signal: controller.signal });
2851
+ clearTimeout(timer);
2852
+ return res.ok;
2853
+ } catch {
2854
+ return false;
2855
+ }
2856
+ }
2857
+ async function fetchWebReachable(url, timeoutMs = 5e3) {
2858
+ try {
2859
+ const controller = new AbortController();
2860
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
2861
+ const res = await fetch(url, {
2862
+ method: "HEAD",
2863
+ signal: controller.signal
2864
+ });
2865
+ clearTimeout(timer);
2866
+ return res.ok || res.status === 308 || res.status === 307;
2867
+ } catch {
2868
+ return false;
2869
+ }
2870
+ }
2871
+ async function runChecks() {
2872
+ const checks = [];
2873
+ const config = loadConfig();
2874
+ const cfgExists = existsSync7(configPath());
2875
+ checks.push({
2876
+ name: "config",
2877
+ status: cfgExists ? "ok" : "warn",
2878
+ message: cfgExists ? `Found: ${configPath()}` : "No config file (using defaults)"
2879
+ });
2880
+ checks.push({
2881
+ name: "auth",
2882
+ status: config.auth_token ? "ok" : "warn",
2883
+ message: config.auth_token ? "Authenticated" : "Not logged in \u2014 run 'skillport login'"
2884
+ });
2885
+ const keysExist = hasKeys();
2886
+ let keyMsg = "Not found \u2014 run 'skillport init'";
2887
+ if (keysExist) {
2888
+ try {
2889
+ const pem = loadPublicKey2();
2890
+ const kid = computeKeyId(pem);
2891
+ keyMsg = `Present (key ID: ${kid})`;
2892
+ } catch {
2893
+ keyMsg = "Files exist but unreadable";
2894
+ }
2895
+ }
2896
+ checks.push({
2897
+ name: "keys",
2898
+ status: keysExist ? "ok" : "warn",
2899
+ message: keyMsg
2900
+ });
2901
+ const apiOk = await fetchHealth(config.marketplace_url);
2902
+ checks.push({
2903
+ name: "api",
2904
+ status: apiOk ? "ok" : "fail",
2905
+ message: apiOk ? `Reachable: ${config.marketplace_url}` : `Unreachable: ${config.marketplace_url}`
2906
+ });
2907
+ const webOk = await fetchWebReachable(config.marketplace_web_url);
2908
+ checks.push({
2909
+ name: "web",
2910
+ status: webOk ? "ok" : "fail",
2911
+ message: webOk ? `Reachable: ${config.marketplace_web_url}` : `Unreachable: ${config.marketplace_web_url}`
2912
+ });
2913
+ if (config.auth_token && keysExist) {
2914
+ try {
2915
+ const pem = loadPublicKey2();
2916
+ const kid = computeKeyId(pem);
2917
+ const controller = new AbortController();
2918
+ const timer = setTimeout(() => controller.abort(), 5e3);
2919
+ const res = await fetch(`${config.marketplace_url}/v1/keys`, {
2920
+ method: "POST",
2921
+ headers: {
2922
+ "Content-Type": "application/json",
2923
+ Authorization: `Bearer ${config.auth_token}`
2924
+ },
2925
+ body: JSON.stringify({ public_key_pem: pem, label: "default" }),
2926
+ signal: controller.signal
2927
+ });
2928
+ clearTimeout(timer);
2929
+ if (res.ok || res.status === 409) {
2930
+ checks.push({
2931
+ name: "key_registered",
2932
+ status: "ok",
2933
+ message: `Key ${kid} is registered on marketplace`
2934
+ });
2935
+ } else {
2936
+ const body = await res.json().catch(() => ({}));
2937
+ checks.push({
2938
+ name: "key_registered",
2939
+ status: "warn",
2940
+ message: `Key registration check failed: ${body.error || res.statusText}`
2941
+ });
2942
+ }
2943
+ } catch {
2944
+ checks.push({
2945
+ name: "key_registered",
2946
+ status: "warn",
2947
+ message: "Could not verify key registration (API unreachable)"
2948
+ });
2949
+ }
2950
+ }
2951
+ const ok = checks.every((c) => c.status !== "fail");
2952
+ return { checks, ok };
2953
+ }
2954
+ async function doctorCommand(opts) {
2955
+ if (!opts.json) {
2956
+ console.log(chalk13.bold("SkillPort Doctor"));
2957
+ console.log();
2958
+ }
2959
+ const result = await runChecks();
2960
+ if (opts.json) {
2961
+ console.log(JSON.stringify(result, null, 2));
2962
+ } else {
2963
+ for (const check of result.checks) {
2964
+ const icon = check.status === "ok" ? chalk13.green("OK") : check.status === "warn" ? chalk13.yellow("WARN") : chalk13.red("FAIL");
2965
+ console.log(` [${icon}] ${chalk13.bold(check.name)}: ${check.message}`);
2966
+ }
2967
+ console.log();
2968
+ if (result.ok) {
2969
+ console.log(chalk13.green("All critical checks passed."));
2970
+ } else {
2971
+ console.log(chalk13.red("Some checks failed. See above for details."));
2972
+ process.exitCode = 1;
2973
+ }
2974
+ }
2975
+ }
2976
+
2784
2977
  // src/commands/keys-register.ts
2785
2978
  init_config();
2786
- import chalk12 from "chalk";
2979
+ import chalk14 from "chalk";
2787
2980
  async function keysRegisterCommand() {
2788
2981
  const config = loadConfig();
2789
2982
  if (!config.auth_token) {
2790
- console.log(chalk12.red("Not logged in. Run 'skillport login' first."));
2983
+ console.log(chalk14.red("Not logged in. Run 'skillport login' first."));
2791
2984
  process.exitCode = 1;
2792
2985
  return;
2793
2986
  }
2794
2987
  if (!hasKeys()) {
2795
- console.log(chalk12.red("No signing keys found. Run 'skillport init' first."));
2988
+ console.log(chalk14.red("No signing keys found. Run 'skillport init' first."));
2796
2989
  process.exitCode = 1;
2797
2990
  return;
2798
2991
  }
@@ -2802,9 +2995,150 @@ async function keysRegisterCommand() {
2802
2995
  }
2803
2996
  }
2804
2997
 
2998
+ // src/commands/list.ts
2999
+ init_config();
3000
+ import chalk15 from "chalk";
3001
+ var STATUS_COLORS = {
3002
+ published: chalk15.green,
3003
+ draft: chalk15.yellow,
3004
+ archived: chalk15.gray,
3005
+ suspended: chalk15.red,
3006
+ pending_review: chalk15.cyan
3007
+ };
3008
+ var STATUS_LABELS = {
3009
+ published: "published",
3010
+ draft: "draft",
3011
+ archived: "deleted",
3012
+ suspended: "suspended",
3013
+ pending_review: "pending"
3014
+ };
3015
+ async function listCommand(opts) {
3016
+ const config = loadConfig();
3017
+ if (!config.auth_token) {
3018
+ console.log(chalk15.red("Not logged in. Run 'skillport login' first."));
3019
+ process.exitCode = 1;
3020
+ return;
3021
+ }
3022
+ try {
3023
+ const res = await fetch(`${config.marketplace_url}/v1/me/skills`, {
3024
+ headers: { Authorization: `Bearer ${config.auth_token}` }
3025
+ });
3026
+ if (!res.ok) {
3027
+ const body = await res.json().catch(() => ({}));
3028
+ console.log(chalk15.red(`Failed to fetch skills: ${body.error || res.statusText}`));
3029
+ process.exitCode = 1;
3030
+ return;
3031
+ }
3032
+ const skills = await res.json();
3033
+ if (opts.json) {
3034
+ console.log(JSON.stringify(skills, null, 2));
3035
+ return;
3036
+ }
3037
+ if (skills.length === 0) {
3038
+ console.log(chalk15.dim("No skills found. Publish your first skill with:"));
3039
+ console.log(chalk15.dim(" skillport export ./my-skill -o my-skill.ssp && skillport publish my-skill.ssp"));
3040
+ return;
3041
+ }
3042
+ console.log(chalk15.bold(`Your Skills (${skills.length})`));
3043
+ console.log();
3044
+ const idWidth = Math.max(4, ...skills.map((s) => s.ssp_id.length));
3045
+ const titleWidth = Math.max(5, ...skills.map((s) => s.title.length));
3046
+ const statusWidth = 10;
3047
+ const versionWidth = 8;
3048
+ const dlWidth = 5;
3049
+ console.log(
3050
+ chalk15.dim(
3051
+ " " + "SSP ID".padEnd(idWidth + 2) + "TITLE".padEnd(titleWidth + 2) + "STATUS".padEnd(statusWidth + 2) + "VERSION".padEnd(versionWidth + 2) + "DL".padEnd(dlWidth + 2) + "ID"
3052
+ )
3053
+ );
3054
+ for (const skill of skills) {
3055
+ const colorFn = STATUS_COLORS[skill.status] || chalk15.white;
3056
+ const label = STATUS_LABELS[skill.status] || skill.status;
3057
+ console.log(
3058
+ " " + skill.ssp_id.padEnd(idWidth + 2) + skill.title.padEnd(titleWidth + 2) + colorFn(label.padEnd(statusWidth + 2)) + (skill.latest_version || "\u2014").padEnd(versionWidth + 2) + String(skill.downloads || 0).padEnd(dlWidth + 2) + chalk15.dim(skill.id)
3059
+ );
3060
+ }
3061
+ console.log();
3062
+ console.log(chalk15.dim(" Manage: skillport manage <skill-id> publish|unpublish|delete"));
3063
+ } catch (error) {
3064
+ console.log(chalk15.red(`Failed to fetch skills: ${error.message}`));
3065
+ process.exitCode = 1;
3066
+ }
3067
+ }
3068
+
3069
+ // src/commands/manage.ts
3070
+ init_config();
3071
+ import chalk16 from "chalk";
3072
+ var ACTION_DESC = {
3073
+ publish: "Publishing skill (draft \u2192 published)...",
3074
+ unpublish: "Unpublishing skill (published \u2192 draft)...",
3075
+ delete: "Deleting skill..."
3076
+ };
3077
+ var ACTION_SUCCESS = {
3078
+ publish: "Skill published! It is now live on the marketplace.",
3079
+ unpublish: "Skill unpublished. It is now in draft.",
3080
+ delete: "Skill deleted. It is no longer visible on the marketplace."
3081
+ };
3082
+ async function manageCommand(skillId, action) {
3083
+ const validActions = ["publish", "unpublish", "delete"];
3084
+ if (!validActions.includes(action)) {
3085
+ console.log(chalk16.red(`Invalid action: ${action}`));
3086
+ console.log(`Valid actions: ${validActions.join(", ")}`);
3087
+ process.exitCode = 1;
3088
+ return;
3089
+ }
3090
+ const config = loadConfig();
3091
+ if (!config.auth_token) {
3092
+ console.log(chalk16.red("Not logged in. Run 'skillport login' first."));
3093
+ process.exitCode = 1;
3094
+ return;
3095
+ }
3096
+ const act = action;
3097
+ console.log(ACTION_DESC[act]);
3098
+ try {
3099
+ let res;
3100
+ if (act === "delete") {
3101
+ res = await fetch(`${config.marketplace_url}/v1/skills/${skillId}`, {
3102
+ method: "DELETE",
3103
+ headers: { Authorization: `Bearer ${config.auth_token}` }
3104
+ });
3105
+ } else {
3106
+ res = await fetch(`${config.marketplace_url}/v1/skills/${skillId}/${act}`, {
3107
+ method: "POST",
3108
+ headers: { Authorization: `Bearer ${config.auth_token}` }
3109
+ });
3110
+ }
3111
+ if (!res.ok) {
3112
+ const body = await res.json().catch(() => ({}));
3113
+ const msg = String(body.error || res.statusText);
3114
+ if (res.status === 403) {
3115
+ console.log(chalk16.red("Permission denied. You are not the author of this skill."));
3116
+ } else if (res.status === 404) {
3117
+ console.log(chalk16.red(`Skill not found: ${skillId}`));
3118
+ } else {
3119
+ console.log(chalk16.red(`Failed: ${msg}`));
3120
+ }
3121
+ process.exitCode = 1;
3122
+ return;
3123
+ }
3124
+ const result = await res.json();
3125
+ console.log(chalk16.green(ACTION_SUCCESS[act]));
3126
+ console.log();
3127
+ console.log(` ${chalk16.bold("Skill ID:")} ${result.id}`);
3128
+ console.log(` ${chalk16.bold("Status:")} ${result.status}`);
3129
+ if (act === "publish") {
3130
+ console.log();
3131
+ console.log(chalk16.dim(` URL: ${config.marketplace_web_url}/skills/${result.id}`));
3132
+ }
3133
+ } catch (error) {
3134
+ console.log(chalk16.red(`Failed: ${error.message}`));
3135
+ process.exitCode = 1;
3136
+ }
3137
+ }
3138
+
2805
3139
  // src/index.ts
2806
3140
  var program = new Command();
2807
- program.name("skillport").description("SkillPort \u2014 secure skill distribution for OpenClaw").version("0.1.6");
3141
+ program.name("skillport").description("SkillPort \u2014 open-source secure skill distribution for OpenClaw").version("1.0.0");
2808
3142
  program.command("init").description("Generate Ed25519 key pair for signing").action(initCommand);
2809
3143
  program.command("scan <path>").description("Run security scan on a skill directory or .ssp file").action(scanCommand);
2810
3144
  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);
@@ -2815,6 +3149,10 @@ program.command("dry-run <ssp>").description("Run installation diagnostics witho
2815
3149
  program.command("uninstall <id>").description("Uninstall an installed skill").action(uninstallCommand);
2816
3150
  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);
2817
3151
  program.command("publish <ssp>").description("Publish a SkillPort package to the marketplace").action(publishCommand);
3152
+ program.command("list").description("List your marketplace skills and their status").option("--json", "Output as JSON").action(listCommand);
3153
+ program.command("manage <skill-id> <action>").description("Manage a skill: publish, unpublish, or delete").action(manageCommand);
3154
+ program.command("whoami").description("Show current configuration and identity").option("--json", "Output as JSON").action(whoamiCommand);
3155
+ program.command("doctor").description("Check connectivity and setup health").option("--json", "Output as JSON").action(doctorCommand);
2818
3156
  var keys = program.command("keys").description("Manage signing keys");
2819
3157
  keys.command("register").description("Register your public signing key with the marketplace").action(keysRegisterCommand);
2820
3158
  program.parse();
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@skillport/cli",
3
- "version": "0.1.6",
4
- "description": "SkillPort CLI — secure skill distribution for OpenClaw. Export, scan, sign, and install AI skill packages.",
3
+ "version": "1.0.0",
4
+ "description": "SkillPort CLI — open-source secure skill distribution for OpenClaw. Export, scan, sign, and install AI skill packages.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/my-name-is-yu/skillport.git",
9
+ "url": "https://github.com/skillport-dev/skillport.git",
10
10
  "directory": "apps/cli"
11
11
  },
12
12
  "homepage": "https://skillport.market",
13
- "bugs": "https://github.com/my-name-is-yu/skillport/issues",
13
+ "bugs": "https://github.com/skillport-dev/skillport/issues",
14
14
  "keywords": [
15
15
  "skillport",
16
16
  "openclaw",
@@ -27,7 +27,7 @@
27
27
  ],
28
28
  "publishConfig": {
29
29
  "access": "public",
30
- "tag": "beta"
30
+ "tag": "latest"
31
31
  },
32
32
  "scripts": {
33
33
  "build": "rm -rf dist && node scripts/bundle.js",