ht-skills 0.2.8 → 0.2.10
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/lib/cli.js +150 -95
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -841,8 +841,15 @@ function getPublishStepStatusLabel(status) {
|
|
|
841
841
|
}
|
|
842
842
|
}
|
|
843
843
|
|
|
844
|
-
function renderPublishFlowCard(flowState, {
|
|
845
|
-
|
|
844
|
+
function renderPublishFlowCard(flowState, {
|
|
845
|
+
colorize = (text) => text,
|
|
846
|
+
width = 80,
|
|
847
|
+
currentMessage = "",
|
|
848
|
+
} = {}) {
|
|
849
|
+
const contentWidth = Math.max(
|
|
850
|
+
28,
|
|
851
|
+
...PUBLISH_FLOW_STEPS.map((step, index) => getDisplayWidth(`${index + 1}. ${step.label} Pending`)),
|
|
852
|
+
);
|
|
846
853
|
const lines = PUBLISH_FLOW_STEPS.map((step, index) => {
|
|
847
854
|
const status = flowState?.[step.id] || "pending";
|
|
848
855
|
const symbol = status === "done"
|
|
@@ -859,24 +866,57 @@ function renderPublishFlowCard(flowState, { colorize = (text) => text, width = 8
|
|
|
859
866
|
: status === "error"
|
|
860
867
|
? colorize(step.label, "warn")
|
|
861
868
|
: step.label;
|
|
862
|
-
const statusLabel = colorize(
|
|
869
|
+
const statusLabel = colorize(
|
|
870
|
+
getPublishStepStatusLabel(status),
|
|
871
|
+
status === "pending" ? "muted" : status === "error" ? "warn" : status === "active" ? "accent" : "success",
|
|
872
|
+
);
|
|
863
873
|
const plainLine = `${index + 1}. ${step.label} ${getPublishStepStatusLabel(status)}`;
|
|
864
|
-
const paddedWidth = Math.max(0,
|
|
874
|
+
const paddedWidth = Math.max(0, contentWidth - getDisplayWidth(plainLine));
|
|
865
875
|
return `│ ${symbol} ${label}${" ".repeat(paddedWidth)} ${statusLabel} │`;
|
|
866
876
|
});
|
|
867
877
|
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
const normalizedLines = lines.map((line) => {
|
|
874
|
-
const plainWidth = getDisplayWidth(line);
|
|
875
|
-
const targetWidth = contentWidth + 4;
|
|
876
|
-
return plainWidth < targetWidth ? `${line.slice(0, -2)}${" ".repeat(targetWidth - plainWidth)} │` : line;
|
|
877
|
-
});
|
|
878
|
+
const titleSuffix = "─".repeat(Math.max(0, contentWidth - getDisplayWidth("Publish Flow") - 1));
|
|
879
|
+
const title = `◇ ${colorize("Publish Flow", "accent")} ${colorize(titleSuffix, "muted")}╮`;
|
|
880
|
+
const messageLines = currentMessage
|
|
881
|
+
? wrapText(currentMessage, contentWidth).map((line) => `│ ${colorize(line, "muted")}${" ".repeat(Math.max(0, contentWidth - getDisplayWidth(line)))} │`)
|
|
882
|
+
: [];
|
|
878
883
|
const bottom = `╰${"─".repeat(contentWidth + 2)}╯`;
|
|
879
|
-
return [title, ...
|
|
884
|
+
return [title, ...lines, ...messageLines, bottom].join("\n");
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
function createLiveBlockRenderer(output = process.stdout) {
|
|
888
|
+
let renderedLines = 0;
|
|
889
|
+
|
|
890
|
+
function clearPrevious() {
|
|
891
|
+
if (!renderedLines || !output.isTTY) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
output.write(`\x1B[${renderedLines}F`);
|
|
895
|
+
for (let index = 0; index < renderedLines; index += 1) {
|
|
896
|
+
output.write("\x1B[2K");
|
|
897
|
+
if (index < renderedLines - 1) {
|
|
898
|
+
output.write("\x1B[1E");
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
if (renderedLines > 1) {
|
|
902
|
+
output.write(`\x1B[${renderedLines - 1}F`);
|
|
903
|
+
} else {
|
|
904
|
+
output.write("\r");
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
return {
|
|
909
|
+
render(content) {
|
|
910
|
+
const text = String(content || "");
|
|
911
|
+
clearPrevious();
|
|
912
|
+
output.write(`${text}\n`);
|
|
913
|
+
renderedLines = text.split("\n").length;
|
|
914
|
+
},
|
|
915
|
+
clear() {
|
|
916
|
+
clearPrevious();
|
|
917
|
+
renderedLines = 0;
|
|
918
|
+
},
|
|
919
|
+
};
|
|
880
920
|
}
|
|
881
921
|
|
|
882
922
|
function printFallbackIntro({ registry, slug, version, skillName, skillDescription, installTargets }, log = console.log) {
|
|
@@ -1529,15 +1569,25 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1529
1569
|
inspect: "pending",
|
|
1530
1570
|
submit: "pending",
|
|
1531
1571
|
};
|
|
1572
|
+
let currentMessage = "Ready to publish";
|
|
1573
|
+
const flowRenderer = ui ? createLiveBlockRenderer(process.stdout) : null;
|
|
1532
1574
|
|
|
1533
1575
|
const renderPublishFlow = () => {
|
|
1534
|
-
if (!
|
|
1535
|
-
|
|
1536
|
-
console.log(renderPublishFlowCard(flowState, {
|
|
1576
|
+
if (!flowRenderer) return;
|
|
1577
|
+
flowRenderer.render(renderPublishFlowCard(flowState, {
|
|
1537
1578
|
colorize,
|
|
1538
1579
|
width: Math.max(44, Math.min(outputWidth - 4, 72)),
|
|
1580
|
+
currentMessage,
|
|
1539
1581
|
}));
|
|
1540
1582
|
};
|
|
1583
|
+
const setFlowStatus = (stepId, status) => {
|
|
1584
|
+
flowState[stepId] = status;
|
|
1585
|
+
renderPublishFlow();
|
|
1586
|
+
};
|
|
1587
|
+
const setFlowMessage = (message) => {
|
|
1588
|
+
currentMessage = message;
|
|
1589
|
+
renderPublishFlow();
|
|
1590
|
+
};
|
|
1541
1591
|
|
|
1542
1592
|
if (ui) {
|
|
1543
1593
|
// eslint-disable-next-line no-console
|
|
@@ -1557,53 +1607,65 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1557
1607
|
printFallbackPublishIntro({ registry, skillDir, archiveName, visibility }, log);
|
|
1558
1608
|
}
|
|
1559
1609
|
|
|
1560
|
-
|
|
1610
|
+
setFlowMessage(`Checking login for ${registry}`);
|
|
1611
|
+
setFlowStatus("auth", "active");
|
|
1561
1612
|
if (!ui) {
|
|
1562
1613
|
log(`Checking login for ${registry}...`);
|
|
1563
1614
|
}
|
|
1564
1615
|
const { token } = await ensureValidAuthToken(registry, flags, deps);
|
|
1565
|
-
|
|
1616
|
+
setFlowMessage("Login confirmed");
|
|
1617
|
+
setFlowStatus("auth", "done");
|
|
1566
1618
|
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
if (
|
|
1570
|
-
packSpinner.start(`Packing ${path.basename(skillDir) || skillDir}`);
|
|
1571
|
-
} else {
|
|
1619
|
+
setFlowMessage(`Packing ${path.basename(skillDir) || skillDir}`);
|
|
1620
|
+
setFlowStatus("pack", "active");
|
|
1621
|
+
if (!ui) {
|
|
1572
1622
|
log(`Packing skill directory: ${skillDir}`);
|
|
1573
1623
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1624
|
+
let archiveBuffer;
|
|
1625
|
+
try {
|
|
1626
|
+
archiveBuffer = await createZipFromDirectory(skillDir);
|
|
1627
|
+
setFlowMessage(`Created ${archiveName} (${formatBytes(archiveBuffer.length)})`);
|
|
1628
|
+
setFlowStatus("pack", "done");
|
|
1629
|
+
if (!ui) {
|
|
1630
|
+
log(`Created archive ${archiveName} (${formatBytes(archiveBuffer.length)})`);
|
|
1631
|
+
}
|
|
1632
|
+
} catch (error) {
|
|
1633
|
+
setFlowMessage(`Packing failed: ${error.message}`);
|
|
1634
|
+
setFlowStatus("pack", "error");
|
|
1635
|
+
throw error;
|
|
1580
1636
|
}
|
|
1581
1637
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
if (
|
|
1585
|
-
uploadSpinner.start("Uploading archive for package inspection");
|
|
1586
|
-
} else {
|
|
1638
|
+
setFlowMessage("Uploading archive for package inspection");
|
|
1639
|
+
setFlowStatus("inspect", "active");
|
|
1640
|
+
if (!ui) {
|
|
1587
1641
|
log("Uploading archive for package inspection...");
|
|
1588
1642
|
}
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1643
|
+
let job;
|
|
1644
|
+
try {
|
|
1645
|
+
job = await requestJsonImpl(
|
|
1646
|
+
`${registry}/api/skills/inspect-package-jobs/upload?archive_name=${encodeURIComponent(archiveName)}`,
|
|
1647
|
+
{
|
|
1648
|
+
method: "POST",
|
|
1649
|
+
headers: withBearerToken({
|
|
1650
|
+
"content-type": "application/zip",
|
|
1651
|
+
}, token),
|
|
1652
|
+
body: archiveBuffer,
|
|
1653
|
+
},
|
|
1654
|
+
);
|
|
1655
|
+
} catch (error) {
|
|
1656
|
+
setFlowMessage(`Inspection upload failed: ${error.message}`);
|
|
1657
|
+
setFlowStatus("inspect", "error");
|
|
1658
|
+
throw error;
|
|
1659
|
+
}
|
|
1599
1660
|
|
|
1600
1661
|
const jobId = String(job.job_id || "").trim();
|
|
1601
1662
|
if (!jobId) {
|
|
1663
|
+
setFlowMessage("Inspection upload failed: missing inspection job id");
|
|
1664
|
+
setFlowStatus("inspect", "error");
|
|
1602
1665
|
throw new Error("registry did not return an inspection job id");
|
|
1603
1666
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
} else {
|
|
1667
|
+
setFlowMessage(`Inspection job created (${jobId})`);
|
|
1668
|
+
if (!ui) {
|
|
1607
1669
|
log(`Inspection job created: ${jobId}`);
|
|
1608
1670
|
}
|
|
1609
1671
|
|
|
@@ -1611,16 +1673,10 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1611
1673
|
let lastProgressKey = "";
|
|
1612
1674
|
let consecutivePollErrors = 0;
|
|
1613
1675
|
const publishDeadline = Date.now() + publishTimeoutMs;
|
|
1614
|
-
const inspectSpinner = ui ? ui.spinner() : null;
|
|
1615
|
-
if (inspectSpinner) {
|
|
1616
|
-
inspectSpinner.start("Inspecting archive");
|
|
1617
|
-
}
|
|
1618
1676
|
while (inspection.status !== "succeeded" && inspection.status !== "failed") {
|
|
1619
1677
|
if (Date.now() > publishDeadline) {
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
inspectSpinner.stop("Inspection timed out");
|
|
1623
|
-
}
|
|
1678
|
+
setFlowMessage(`Inspection timed out after ${Math.round(publishTimeoutMs / 1000)}s`);
|
|
1679
|
+
setFlowStatus("inspect", "error");
|
|
1624
1680
|
throw new Error(`Inspection timed out after ${Math.round(publishTimeoutMs / 1000)}s`);
|
|
1625
1681
|
}
|
|
1626
1682
|
|
|
@@ -1636,13 +1692,12 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1636
1692
|
} catch (error) {
|
|
1637
1693
|
consecutivePollErrors += 1;
|
|
1638
1694
|
if (consecutivePollErrors >= pollErrorLimit) {
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
inspectSpinner.stop("Inspection polling failed");
|
|
1642
|
-
}
|
|
1695
|
+
setFlowMessage(`Inspection polling failed: ${error.message}`);
|
|
1696
|
+
setFlowStatus("inspect", "error");
|
|
1643
1697
|
throw new Error(`Inspection polling failed ${consecutivePollErrors} times: ${error.message}`);
|
|
1644
1698
|
}
|
|
1645
|
-
|
|
1699
|
+
setFlowMessage(`Inspection polling retry ${consecutivePollErrors}/${pollErrorLimit}: ${error.message}`);
|
|
1700
|
+
if (!ui) {
|
|
1646
1701
|
log(`Inspection polling retry ${consecutivePollErrors}/${pollErrorLimit}: ${error.message}`);
|
|
1647
1702
|
}
|
|
1648
1703
|
continue;
|
|
@@ -1650,10 +1705,8 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1650
1705
|
|
|
1651
1706
|
const runtimeError = String(inspection.error?.message || inspection.error || "").trim();
|
|
1652
1707
|
if (runtimeError && inspection.status !== "succeeded") {
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
inspectSpinner.stop("Inspection failed");
|
|
1656
|
-
}
|
|
1708
|
+
setFlowMessage(`Inspection failed: ${runtimeError}`);
|
|
1709
|
+
setFlowStatus("inspect", "error");
|
|
1657
1710
|
throw new Error(runtimeError);
|
|
1658
1711
|
}
|
|
1659
1712
|
|
|
@@ -1665,30 +1718,28 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1665
1718
|
const progressLabel = step
|
|
1666
1719
|
? `${step}${typeof percent === "number" ? ` ${percent}%` : ""}`
|
|
1667
1720
|
: inspection.status;
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
} else {
|
|
1721
|
+
setFlowMessage(`Inspecting archive (${progressLabel})`);
|
|
1722
|
+
if (!ui) {
|
|
1671
1723
|
log(`Inspection in progress: ${progressLabel}`);
|
|
1672
1724
|
}
|
|
1673
1725
|
}
|
|
1674
1726
|
}
|
|
1675
1727
|
|
|
1676
1728
|
if (inspection.status !== "succeeded") {
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
inspectSpinner.stop("Inspection failed");
|
|
1680
|
-
}
|
|
1729
|
+
setFlowMessage(`Inspection failed: ${inspection.error || "unknown error"}`);
|
|
1730
|
+
setFlowStatus("inspect", "error");
|
|
1681
1731
|
throw new Error(inspection.error || "skill archive inspection failed");
|
|
1682
1732
|
}
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
} else {
|
|
1733
|
+
setFlowMessage("Inspection passed");
|
|
1734
|
+
setFlowStatus("inspect", "done");
|
|
1735
|
+
if (!ui) {
|
|
1687
1736
|
log("Inspection passed.");
|
|
1688
1737
|
}
|
|
1689
1738
|
|
|
1690
1739
|
const preview = inspection.result || {};
|
|
1691
1740
|
if (!preview.valid || !preview.preview_token) {
|
|
1741
|
+
setFlowMessage("Inspection failed: preview token missing");
|
|
1742
|
+
setFlowStatus("inspect", "error");
|
|
1692
1743
|
throw new Error(summarizePreviewErrors(preview));
|
|
1693
1744
|
}
|
|
1694
1745
|
if (!ui) {
|
|
@@ -1709,25 +1760,29 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1709
1760
|
body.publish_now = true;
|
|
1710
1761
|
}
|
|
1711
1762
|
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
if (
|
|
1715
|
-
submitSpinner.start(`Submitting review request (${visibility})`);
|
|
1716
|
-
} else {
|
|
1763
|
+
setFlowMessage(`Submitting review request (${visibility})`);
|
|
1764
|
+
setFlowStatus("submit", "active");
|
|
1765
|
+
if (!ui) {
|
|
1717
1766
|
log(`Submitting review request with access=${visibility}...`);
|
|
1718
1767
|
}
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1768
|
+
let result;
|
|
1769
|
+
try {
|
|
1770
|
+
result = await requestJsonImpl(`${registry}/api/skills/submit`, {
|
|
1771
|
+
method: "POST",
|
|
1772
|
+
headers: withBearerToken({
|
|
1773
|
+
"content-type": "application/json",
|
|
1774
|
+
}, token),
|
|
1775
|
+
body: JSON.stringify(body),
|
|
1776
|
+
});
|
|
1777
|
+
setFlowMessage(`Review submission created (${result.submission_id || "pending"})`);
|
|
1778
|
+
setFlowStatus("submit", "done");
|
|
1779
|
+
if (!ui) {
|
|
1780
|
+
log(`Review submission created: ${result.submission_id || "(no submission id returned)"}`);
|
|
1781
|
+
}
|
|
1782
|
+
} catch (error) {
|
|
1783
|
+
setFlowMessage(`Submit failed: ${error.message}`);
|
|
1784
|
+
setFlowStatus("submit", "error");
|
|
1785
|
+
throw error;
|
|
1731
1786
|
}
|
|
1732
1787
|
|
|
1733
1788
|
const summaryPayload = {
|
|
@@ -1750,7 +1805,7 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1750
1805
|
].join("\n"),
|
|
1751
1806
|
"Published",
|
|
1752
1807
|
);
|
|
1753
|
-
|
|
1808
|
+
setFlowMessage(`Submitted ${archiveName} for review`);
|
|
1754
1809
|
ui.outro(`Submitted ${ui.pc.cyan(archiveName)} for review.`);
|
|
1755
1810
|
} else {
|
|
1756
1811
|
log(JSON.stringify(summaryPayload, null, 2));
|