@jarel/myskills 0.1.0-alpha.3 → 0.1.0-beta.2

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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Command-line client for MySkills.
4
4
 
5
+ Supported runtime: Node.js `>=22.13 <23 || >=24 <25`.
6
+
5
7
  Package:
6
8
 
7
9
  ```text
@@ -49,7 +51,8 @@ myskills config reset api-url
49
51
  myskills config list
50
52
  myskills submit --path <file-directory-or-zip> [--api-url <url>] [--token <token>]
51
53
  myskills review submissions [--api-url <url>] [--token <token>]
52
- myskills review action <submission-id> --action <approve|request-changes|reject|publish> [--reason <text>] [--api-url <url>] [--token <token>]
54
+ myskills review bundle <submission-id> [--platform <name>] [--output <file>] [--api-url <url>] [--token <token>]
55
+ myskills review action <submission-id> --action <approve|request-changes|reject|publish> [--artifact-sha256 <hash>] [--reason <text>] [--api-url <url>] [--token <token>]
53
56
  myskills submissions list [--api-url <url>] [--token <token>]
54
57
  myskills submissions withdraw <submission-id> [--reason <text>] [--api-url <url>] [--token <token>]
55
58
  myskills skills edit <skill-slug> [--title <text>] [--summary <text>] [--visibility <scope>] [--tag <tag>] [--reason <text>] [--api-url <url>] [--token <token>]
@@ -74,9 +77,9 @@ myskills token list
74
77
  myskills token revoke <token-id>
75
78
  ```
76
79
 
77
- ## Public Alpha Install
80
+ ## Published And Candidate Channels
78
81
 
79
- The first public alpha package is distributed through npm under the `alpha` tag:
82
+ The currently published package remains available under npm's `alpha` tag:
80
83
 
81
84
  ```bash
82
85
  npm install -g @jarel/myskills@alpha
@@ -90,13 +93,15 @@ Update the CLI with:
90
93
  npm install -g @jarel/myskills@alpha
91
94
  ```
92
95
 
96
+ The `0.1.0-beta.2` source manifest is configured for the `beta` dist-tag, but package publication is a separate maintainer approval step. The release-verification workflow packs and installs the candidate without publishing it.
97
+
93
98
  `validate`, `scan`, and `submit` accept a manifest file, package directory, or local `.zip` package. `login` prompts for the API URL when one is not supplied; the default is the local API at `http://localhost:3001`, and custom hosted URLs can be entered manually. Successful login stores the selected API URL in local CLI config so later commands can omit `--api-url`. API URL resolution is `--api-url`, then `MYSKILLS_API_URL`, then saved config, then `http://localhost:3001`.
94
99
 
95
100
  `login` supports an email/password session flow and an API-key flow. The email/password flow handles MFA challenges with a TOTP or recovery code prompt and stores only the verified session token. The API-key flow validates the key with `/v1/me` before storing it. Token resolution is `--token`, then `MYSKILLS_TOKEN`, then the stored login token. The default token store uses the platform credential store through `@napi-rs/keyring` and falls back to `tokens.json` with user-only file permissions when keyring storage is unavailable or `MYSKILLS_TOKEN_STORE=file`/`MYSKILLS_TOKEN_FILE` is set. `auth status` validates the current token without printing it. `logout` revokes stored session tokens and clears the local entry; stored API tokens are removed locally and must be revoked with `token revoke`.
96
101
 
97
102
  `config get api-url`, `config set api-url <url>`, `config reset api-url`, and `config list` manage the saved API URL. `doctor` checks the CLI version, Node version, resolved API URL, `/health`, auth status, token-store backend, install-directory writability, and `/v1/capabilities`. If the CLI is pointed at the web app instead of the API, or a newer command is sent to an older server, command errors include concrete next steps and `--json` returns structured error codes.
98
103
 
99
- `submit` validates and scans locally before sending package directories as normalized text entries or `.zip` packages as base64 archive uploads for server-side extraction. Authors can inspect their submitted versions with `submissions list` and withdraw unreviewed or changes-requested submissions with `submissions withdraw`. Maintainers can approve, request changes, reject, and publish submitted versions through `review action`.
104
+ `submit` validates and scans locally before sending package directories as normalized text entries or `.zip` packages as base64 archive uploads for server-side extraction. Authors can inspect their submitted versions with `submissions list` and withdraw unreviewed or changes-requested submissions with `submissions withdraw`. Maintainers can fetch the reviewed artifact and approval hash with `review bundle`, then approve, request changes, reject, and publish submitted versions through `review action`.
100
105
 
101
106
  Published artifacts remain immutable. `skills edit` changes mutable skill metadata only, while `releases deprecate`, `releases unpublish`, `releases revoke`, `releases restore`, and `releases delete` update server-owned lifecycle state for a specific version. Deprecated releases remain visible and installable; unpublished, revoked, archived, and deleted releases are hidden from install/export queries. `export` downloads server-authorized bundle content, verifies byte size and SHA-256 against release metadata, and writes normalized package paths under the requested output directory. `install` uses the same verified bundle path, writes into `--dir`, `MYSKILLS_INSTALL_DIR`, or the user data directory, and records local state in `.myskills-app/installed.json`; `update` preserves a rollback snapshot before replacing files, and `rollback` restores the most recent snapshot. `token create` prints the plaintext API token only once and does not overwrite the stored login session. Browser/device login, platform-specific install adapters, and archive creation are still planned.
102
107
 
package/dist/index.js CHANGED
@@ -16321,7 +16321,7 @@ function decodePackageText(buffer, relativePath) {
16321
16321
 
16322
16322
  // src/cli.ts
16323
16323
  var DEFAULT_API_URL = "http://localhost:3001";
16324
- var CLI_VERSION = "0.1.0-alpha.3";
16324
+ var CLI_VERSION = "0.1.0-beta.2";
16325
16325
  var CLI_VISIBILITY_SCOPES = ["public", "authenticated", "organization", "team", "private", "explicit-users"];
16326
16326
  var LOGIN_AUTH_METHODS = ["password", "api-key"];
16327
16327
  async function runCli(argv, runtime) {
@@ -16780,16 +16780,21 @@ async function reviewCommand(parsed, runtime) {
16780
16780
  if (subcommand === "action") {
16781
16781
  const submissionId = parsed.args[1];
16782
16782
  if (!submissionId) {
16783
- throw new CliError("Usage: myskills review action <submission-id> --action <approve|request-changes|reject|publish> [--reason <text>] [--api-url <url>] [--token <token>]", 2);
16783
+ throw new CliError("Usage: myskills review action <submission-id> --action <approve|request-changes|reject|publish> [--artifact-sha256 <hash>] [--reason <text>] [--api-url <url>] [--token <token>]", 2);
16784
16784
  }
16785
16785
  const action = stringOption(parsed, "action");
16786
16786
  if (action !== "approve" && action !== "request-changes" && action !== "reject" && action !== "publish") {
16787
16787
  throw new CliError("--action must be approve, request-changes, reject, or publish.", 2);
16788
16788
  }
16789
16789
  const reason = optionalStringOption(parsed, "reason");
16790
+ const artifactSha256 = optionalStringOption(parsed, "artifact-sha256")?.toLowerCase();
16791
+ if (action === "approve" && !isArtifactSha256(artifactSha256)) {
16792
+ throw new CliError("--artifact-sha256 is required when approving a submission and must be a 64-character SHA-256 hex digest.", 2);
16793
+ }
16790
16794
  const response = await apiPost(`/v1/review/submissions/${encodeURIComponent(submissionId)}/actions`, {
16791
16795
  action,
16792
- ...reason ? { reason } : {}
16796
+ ...reason ? { reason } : {},
16797
+ ...action === "approve" ? { artifactSha256 } : {}
16793
16798
  }, parsed, runtime, token);
16794
16799
  if (parsed.options.json) {
16795
16800
  runtime.io.stdout(JSON.stringify(response, null, 2));
@@ -16799,7 +16804,35 @@ async function reviewCommand(parsed, runtime) {
16799
16804
  }
16800
16805
  return 0;
16801
16806
  }
16802
- throw new CliError("Usage: myskills review submissions | review action <submission-id> --action <approve|request-changes|reject|publish> [--reason <text>]", 2);
16807
+ if (subcommand === "bundle") {
16808
+ const submissionId = parsed.args[1];
16809
+ if (!submissionId) {
16810
+ throw new CliError("Usage: myskills review bundle <submission-id> [--platform <name>] [--output <file>] [--api-url <url>] [--token <token>]", 2);
16811
+ }
16812
+ const platform = optionalStringOption(parsed, "platform");
16813
+ const query = platform ? `?platform=${encodeURIComponent(platform)}` : "";
16814
+ const response = await apiGetWithHeaders(`/v1/review/submissions/${encodeURIComponent(submissionId)}/bundle${query}`, parsed, runtime, token);
16815
+ const artifactSha256 = response.headers["x-myskills-artifact-sha256"] ?? "";
16816
+ if (!isArtifactSha256(artifactSha256)) {
16817
+ throw new CliError("Review bundle response is missing artifact hash.", 1);
16818
+ }
16819
+ const outputPath = optionalStringOption(parsed, "output");
16820
+ if (outputPath) {
16821
+ await mkdir(path2.dirname(path2.resolve(outputPath)), { recursive: true });
16822
+ await writeFile(path2.resolve(outputPath), response.text, { encoding: "utf8", mode: 384 });
16823
+ }
16824
+ if (parsed.options.json) {
16825
+ runtime.io.stdout(JSON.stringify({
16826
+ artifactSha256,
16827
+ ...outputPath ? { output: path2.resolve(outputPath) } : {},
16828
+ payload: parseJsonResponse(`/v1/review/submissions/${submissionId}/bundle`, apiBaseUrl(parsed, runtime), response.text)
16829
+ }, null, 2));
16830
+ } else {
16831
+ runtime.io.stdout(`artifactSha256=${artifactSha256}${outputPath ? ` output=${path2.resolve(outputPath)}` : ""}`);
16832
+ }
16833
+ return 0;
16834
+ }
16835
+ throw new CliError("Usage: myskills review submissions | review bundle <submission-id> | review action <submission-id> --action <approve|request-changes|reject|publish> [--artifact-sha256 <hash>] [--reason <text>]", 2);
16803
16836
  }
16804
16837
  async function submissionsCommand(parsed, runtime) {
16805
16838
  const token = await requireToken(parsed, runtime);
@@ -17733,6 +17766,21 @@ async function apiGet(pathname, parsed, runtime, token) {
17733
17766
  }
17734
17767
  return await apiJsonRequest(pathname, parsed, runtime, { headers });
17735
17768
  }
17769
+ async function apiGetWithHeaders(pathname, parsed, runtime, token) {
17770
+ const headers = {};
17771
+ if (token) {
17772
+ headers.authorization = `Bearer ${token}`;
17773
+ }
17774
+ const response = await apiFetch(pathname, parsed, runtime, { headers });
17775
+ if (!response.ok) {
17776
+ const body = parseJsonResponse(pathname, apiBaseUrl(parsed, runtime), response.text);
17777
+ throw apiErrorFromBody(pathname, apiBaseUrl(parsed, runtime), response.status, body, response.text);
17778
+ }
17779
+ return {
17780
+ headers: response.headers,
17781
+ text: response.text
17782
+ };
17783
+ }
17736
17784
  async function apiGetText(pathname, parsed, runtime, token) {
17737
17785
  const headers = {};
17738
17786
  if (token) {
@@ -17802,11 +17850,21 @@ async function apiFetch(pathname, parsed, runtime, init) {
17802
17850
  ].join("\n"), 1, "API_UNREACHABLE");
17803
17851
  }
17804
17852
  return {
17853
+ headers: responseHeaders(response.headers),
17805
17854
  ok: response.ok,
17806
17855
  status: response.status,
17807
17856
  text: await response.text()
17808
17857
  };
17809
17858
  }
17859
+ function responseHeaders(input) {
17860
+ if (!input) {
17861
+ return {};
17862
+ }
17863
+ if (input instanceof Headers) {
17864
+ return Object.fromEntries([...input.entries()].map(([key, value]) => [key.toLowerCase(), value]));
17865
+ }
17866
+ return Object.fromEntries(Object.entries(input).map(([key, value]) => [key.toLowerCase(), value]));
17867
+ }
17810
17868
  function parseJsonResponse(pathname, baseUrl, text) {
17811
17869
  if (!text) {
17812
17870
  return {};
@@ -18191,6 +18249,9 @@ function optionalStringOption(parsed, key) {
18191
18249
  const value = parsed.options[key];
18192
18250
  return typeof value === "string" && value ? value : void 0;
18193
18251
  }
18252
+ function isArtifactSha256(value) {
18253
+ return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
18254
+ }
18194
18255
  function stringListOption(parsed, key) {
18195
18256
  const value = parsed.options[key];
18196
18257
  if (typeof value === "string") {
@@ -18310,7 +18371,8 @@ function helpText() {
18310
18371
  " config list",
18311
18372
  " submit --path <file-directory-or-zip> [--api-url <url>] [--token <token>]",
18312
18373
  " review submissions [--api-url <url>] [--token <token>]",
18313
- " review action <submission-id> --action <approve|request-changes|reject|publish> [--reason <text>] [--api-url <url>] [--token <token>]",
18374
+ " review bundle <submission-id> [--platform <name>] [--output <file>] [--api-url <url>] [--token <token>]",
18375
+ " review action <submission-id> --action <approve|request-changes|reject|publish> [--artifact-sha256 <hash>] [--reason <text>] [--api-url <url>] [--token <token>]",
18314
18376
  " submissions list [--api-url <url>] [--token <token>]",
18315
18377
  " submissions withdraw <submission-id> [--reason <text>] [--api-url <url>] [--token <token>]",
18316
18378
  " skills edit <skill-slug> [--title <text>] [--summary <text>] [--visibility <scope>] [--tag <tag>] [--reason <text>] [--api-url <url>] [--token <token>]",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jarel/myskills",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Command-line client for publishing, discovering, installing, updating, and rolling back MySkills packages.",
5
5
  "license": "Apache-2.0",
6
6
  "private": false,
@@ -13,7 +13,7 @@
13
13
  "README.md"
14
14
  ],
15
15
  "engines": {
16
- "node": ">=20"
16
+ "node": ">=22.13 <23 || >=24 <25"
17
17
  },
18
18
  "repository": {
19
19
  "type": "git",
@@ -33,7 +33,7 @@
33
33
  ],
34
34
  "publishConfig": {
35
35
  "access": "public",
36
- "tag": "alpha",
36
+ "tag": "beta",
37
37
  "provenance": true
38
38
  },
39
39
  "scripts": {
@@ -41,7 +41,7 @@
41
41
  "test": "node ../../scripts/run-node-tests.mjs --import tsx --extensions .test.ts"
42
42
  },
43
43
  "devDependencies": {
44
- "@myskills-app/skill-package": "0.1.0-alpha.0",
44
+ "@myskills-app/skill-package": "0.1.0-beta.2",
45
45
  "esbuild": "^0.28.1",
46
46
  "tsx": "^4.20.6"
47
47
  },