@ahood/cli 0.3.1 → 0.3.3
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/commands/unpublish.js +36 -2
- package/dist/commands/versions.js +6 -0
- package/dist/help.js +6 -3
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
2
|
import { apiJson } from "../http.js";
|
|
3
|
-
import { parseOwnerSkill } from "../spec.js";
|
|
4
|
-
const USAGE = "Usage: ahood unpublish <owner>/<skill> [--yes]";
|
|
3
|
+
import { parseOwnerSkill, parseOwnerSkillVersion } from "../spec.js";
|
|
4
|
+
const USAGE = "Usage: ahood unpublish <owner>/<skill>[@version] [--yes]";
|
|
5
5
|
// On closed/non-interactive stdin, readline/promises' rl.question() promise
|
|
6
6
|
// never settles -- but since nothing else keeps the event loop alive at that
|
|
7
7
|
// point, the process used to exit 0 anyway once the loop drained, having
|
|
@@ -27,11 +27,45 @@ function confirm(promptText) {
|
|
|
27
27
|
});
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
+
// A bare "owner/skill" has no "@" at all, so lastIndexOf("@") is -1 and this
|
|
31
|
+
// is a whole-skill unpublish (preserves the exact pre-existing behavior).
|
|
32
|
+
// "owner/skill@1.2.3" has an "@" at index > 0 and is a single-version yank
|
|
33
|
+
// instead -- parseOwnerSkillVersion is reused rather than re-parsing "@" by
|
|
34
|
+
// hand, so the same version-format validation (spec.ts's SEMVER_RE / the
|
|
35
|
+
// "latest" literal) applies here as it already does for `add`/`view`.
|
|
36
|
+
function isVersioned(spec) {
|
|
37
|
+
return spec.lastIndexOf("@") > 0;
|
|
38
|
+
}
|
|
30
39
|
export async function unpublish(args) {
|
|
31
40
|
const yes = args.includes("--yes");
|
|
32
41
|
const spec = args.find((a) => !a.startsWith("--"));
|
|
33
42
|
if (!spec)
|
|
34
43
|
throw new Error(USAGE);
|
|
44
|
+
if (isVersioned(spec)) {
|
|
45
|
+
const { owner, skill, version } = parseOwnerSkillVersion(spec, USAGE);
|
|
46
|
+
const key = `${owner}/${skill}@${version}`;
|
|
47
|
+
// Yanking is less destructive than a whole-skill delete (existing
|
|
48
|
+
// lockfile-pinned installs keep resolving/verifying against it -- see
|
|
49
|
+
// add.ts's yanked_at warning), so the prompt wording says so instead of
|
|
50
|
+
// reusing the whole-skill "permanently delete ... for everyone" text,
|
|
51
|
+
// which would overstate what actually happens here.
|
|
52
|
+
const confirmed = yes
|
|
53
|
+
? true
|
|
54
|
+
: await confirm(`Yank ${key}? Existing installs keep working; new installs will be warned. Type "yes" to confirm: `);
|
|
55
|
+
if (!confirmed) {
|
|
56
|
+
console.log("Aborted.");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
// DELETE on the version resource itself (no separate /yank sub-route):
|
|
60
|
+
// the server treats this the same way whole-skill DELETE already does
|
|
61
|
+
// (a soft delete that sets a timestamp column, never removes the row),
|
|
62
|
+
// just scoped one level down to skill_versions.yanked_at/yanked_reason
|
|
63
|
+
// instead of skills.deleted_at. Mirroring that existing verb/path
|
|
64
|
+
// convention rather than inventing a new one.
|
|
65
|
+
await apiJson(`/api/v1/skills/${encodeURIComponent(owner)}/${encodeURIComponent(skill)}/versions/${encodeURIComponent(version)}`, { method: "DELETE" });
|
|
66
|
+
console.log(`Yanked ${key}. Existing lockfile pins still resolve; new installs will be warned off it.`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
35
69
|
const { owner, skill } = parseOwnerSkill(spec, USAGE);
|
|
36
70
|
const key = `${owner}/${skill}`;
|
|
37
71
|
// --yes bypasses the prompt for scripts/CI; otherwise a closed stdin (no
|
|
@@ -36,5 +36,11 @@ export async function versions(args) {
|
|
|
36
36
|
field("published:", v.created_at);
|
|
37
37
|
field("size:", formatSize(v.package_size_bytes));
|
|
38
38
|
field("changelog:", v.changelog_md ? v.changelog_md : "-");
|
|
39
|
+
// Marked right after the version's other details, only when the API
|
|
40
|
+
// actually flags it (issue #58) -- non-yanked versions print exactly as
|
|
41
|
+
// before, no "status:" line at all.
|
|
42
|
+
if (v.yanked_at) {
|
|
43
|
+
field("status:", v.yanked_reason ? `YANKED -- ${v.yanked_reason}` : "YANKED");
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
}
|
package/dist/help.js
CHANGED
|
@@ -79,9 +79,12 @@ export const COMMANDS_HELP = [
|
|
|
79
79
|
examples: ['ahood edit alice/pdf-tools --tagline "Merge and split PDFs"'],
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
|
-
usage: "ahood unpublish <owner>/<skill> [--yes]",
|
|
83
|
-
summary: "Delete a skill from the registry for every consumer, not just your local install (prompts for confirmation unless --yes is passed).",
|
|
84
|
-
desc: "
|
|
82
|
+
usage: "ahood unpublish <owner>/<skill>[@version] [--yes]",
|
|
83
|
+
summary: "Delete a skill from the registry for every consumer, or yank a single version, not just your local install (prompts for confirmation unless --yes is passed).",
|
|
84
|
+
desc: "Without @version: delete a skill from the registry for every consumer (not just your local install). " +
|
|
85
|
+
"With @version (e.g. alice/foo@1.2.3): yank that single version instead of deleting the whole skill -- " +
|
|
86
|
+
"it is marked yanked, not removed. Existing lockfile pins still resolve and verify against it, but new " +
|
|
87
|
+
"installs are warned off it. Prompts for a typed \"yes\" unless --yes is passed.",
|
|
85
88
|
flags: ["--yes Skip the interactive confirmation, for scripts/CI."],
|
|
86
89
|
},
|
|
87
90
|
{ usage: "ahood star <owner>/<skill>", summary: "Star a skill.", desc: "Star a skill." },
|
package/package.json
CHANGED