ht-skills 0.2.7 → 0.2.8
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 +47 -22
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -36,6 +36,8 @@ const CONFIG_FILE_NAME = "config.json";
|
|
|
36
36
|
const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
37
37
|
const DEFAULT_LOGIN_POLL_MS = 1500;
|
|
38
38
|
const DEFAULT_PUBLISH_POLL_MS = 1500;
|
|
39
|
+
const DEFAULT_PUBLISH_TIMEOUT_MS = 5 * 60 * 1000;
|
|
40
|
+
const DEFAULT_PUBLISH_POLL_ERROR_LIMIT = 3;
|
|
39
41
|
|
|
40
42
|
const BANNER_TEXT = String.raw` _ _ _____ ___ _ _ _ _ __ __ _ _ _
|
|
41
43
|
| || |_ _| / __| |_(_) | |___ | \/ |__ _ _ _| |_____| |_ _ __| |__ _ __
|
|
@@ -1519,6 +1521,8 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1519
1521
|
const colorize = ui ? createUiColorizer(ui.pc) : (text) => String(text);
|
|
1520
1522
|
const outputWidth = getOutputWidth(deps.outputWidth);
|
|
1521
1523
|
const pollIntervalMs = Math.max(500, Number(flags["poll-interval"] || deps.pollIntervalMs || DEFAULT_PUBLISH_POLL_MS));
|
|
1524
|
+
const publishTimeoutMs = Math.max(10_000, Number(flags.timeout || deps.timeoutMs || DEFAULT_PUBLISH_TIMEOUT_MS));
|
|
1525
|
+
const pollErrorLimit = Math.max(1, Number(flags["poll-error-limit"] || deps.pollErrorLimit || DEFAULT_PUBLISH_POLL_ERROR_LIMIT));
|
|
1522
1526
|
const flowState = {
|
|
1523
1527
|
auth: "pending",
|
|
1524
1528
|
pack: "pending",
|
|
@@ -1553,24 +1557,15 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1553
1557
|
printFallbackPublishIntro({ registry, skillDir, archiveName, visibility }, log);
|
|
1554
1558
|
}
|
|
1555
1559
|
|
|
1556
|
-
const authSpinner = ui ? ui.spinner() : null;
|
|
1557
1560
|
flowState.auth = "active";
|
|
1558
|
-
|
|
1559
|
-
if (authSpinner) {
|
|
1560
|
-
authSpinner.start("Checking login");
|
|
1561
|
-
} else {
|
|
1561
|
+
if (!ui) {
|
|
1562
1562
|
log(`Checking login for ${registry}...`);
|
|
1563
1563
|
}
|
|
1564
1564
|
const { token } = await ensureValidAuthToken(registry, flags, deps);
|
|
1565
1565
|
flowState.auth = "done";
|
|
1566
|
-
renderPublishFlow();
|
|
1567
|
-
if (authSpinner) {
|
|
1568
|
-
authSpinner.stop("Login confirmed");
|
|
1569
|
-
}
|
|
1570
1566
|
|
|
1571
1567
|
const packSpinner = ui ? ui.spinner() : null;
|
|
1572
1568
|
flowState.pack = "active";
|
|
1573
|
-
renderPublishFlow();
|
|
1574
1569
|
if (packSpinner) {
|
|
1575
1570
|
packSpinner.start(`Packing ${path.basename(skillDir) || skillDir}`);
|
|
1576
1571
|
} else {
|
|
@@ -1578,7 +1573,6 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1578
1573
|
}
|
|
1579
1574
|
const archiveBuffer = await createZipFromDirectory(skillDir);
|
|
1580
1575
|
flowState.pack = "done";
|
|
1581
|
-
renderPublishFlow();
|
|
1582
1576
|
if (packSpinner) {
|
|
1583
1577
|
packSpinner.stop(`Created ${archiveName} (${formatBytes(archiveBuffer.length)})`);
|
|
1584
1578
|
} else {
|
|
@@ -1587,7 +1581,6 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1587
1581
|
|
|
1588
1582
|
const uploadSpinner = ui ? ui.spinner() : null;
|
|
1589
1583
|
flowState.inspect = "active";
|
|
1590
|
-
renderPublishFlow();
|
|
1591
1584
|
if (uploadSpinner) {
|
|
1592
1585
|
uploadSpinner.start("Uploading archive for package inspection");
|
|
1593
1586
|
} else {
|
|
@@ -1616,18 +1609,53 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1616
1609
|
|
|
1617
1610
|
let inspection = job;
|
|
1618
1611
|
let lastProgressKey = "";
|
|
1612
|
+
let consecutivePollErrors = 0;
|
|
1613
|
+
const publishDeadline = Date.now() + publishTimeoutMs;
|
|
1619
1614
|
const inspectSpinner = ui ? ui.spinner() : null;
|
|
1620
1615
|
if (inspectSpinner) {
|
|
1621
1616
|
inspectSpinner.start("Inspecting archive");
|
|
1622
1617
|
}
|
|
1623
1618
|
while (inspection.status !== "succeeded" && inspection.status !== "failed") {
|
|
1619
|
+
if (Date.now() > publishDeadline) {
|
|
1620
|
+
flowState.inspect = "error";
|
|
1621
|
+
if (inspectSpinner) {
|
|
1622
|
+
inspectSpinner.stop("Inspection timed out");
|
|
1623
|
+
}
|
|
1624
|
+
throw new Error(`Inspection timed out after ${Math.round(publishTimeoutMs / 1000)}s`);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1624
1627
|
await sleep(pollIntervalMs);
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1628
|
+
try {
|
|
1629
|
+
inspection = await requestJsonImpl(
|
|
1630
|
+
`${registry}/api/skills/inspect-package-jobs/${encodeURIComponent(jobId)}`,
|
|
1631
|
+
{
|
|
1632
|
+
headers: withBearerToken({}, token),
|
|
1633
|
+
},
|
|
1634
|
+
);
|
|
1635
|
+
consecutivePollErrors = 0;
|
|
1636
|
+
} catch (error) {
|
|
1637
|
+
consecutivePollErrors += 1;
|
|
1638
|
+
if (consecutivePollErrors >= pollErrorLimit) {
|
|
1639
|
+
flowState.inspect = "error";
|
|
1640
|
+
if (inspectSpinner) {
|
|
1641
|
+
inspectSpinner.stop("Inspection polling failed");
|
|
1642
|
+
}
|
|
1643
|
+
throw new Error(`Inspection polling failed ${consecutivePollErrors} times: ${error.message}`);
|
|
1644
|
+
}
|
|
1645
|
+
if (!inspectSpinner) {
|
|
1646
|
+
log(`Inspection polling retry ${consecutivePollErrors}/${pollErrorLimit}: ${error.message}`);
|
|
1647
|
+
}
|
|
1648
|
+
continue;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
const runtimeError = String(inspection.error?.message || inspection.error || "").trim();
|
|
1652
|
+
if (runtimeError && inspection.status !== "succeeded") {
|
|
1653
|
+
flowState.inspect = "error";
|
|
1654
|
+
if (inspectSpinner) {
|
|
1655
|
+
inspectSpinner.stop("Inspection failed");
|
|
1656
|
+
}
|
|
1657
|
+
throw new Error(runtimeError);
|
|
1658
|
+
}
|
|
1631
1659
|
|
|
1632
1660
|
const step = String(inspection.progress?.step || inspection.result?.step || "").trim();
|
|
1633
1661
|
const percent = inspection.progress?.percent;
|
|
@@ -1647,14 +1675,12 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1647
1675
|
|
|
1648
1676
|
if (inspection.status !== "succeeded") {
|
|
1649
1677
|
flowState.inspect = "error";
|
|
1650
|
-
renderPublishFlow();
|
|
1651
1678
|
if (inspectSpinner) {
|
|
1652
1679
|
inspectSpinner.stop("Inspection failed");
|
|
1653
1680
|
}
|
|
1654
1681
|
throw new Error(inspection.error || "skill archive inspection failed");
|
|
1655
1682
|
}
|
|
1656
1683
|
flowState.inspect = "done";
|
|
1657
|
-
renderPublishFlow();
|
|
1658
1684
|
if (inspectSpinner) {
|
|
1659
1685
|
inspectSpinner.stop("Inspection passed");
|
|
1660
1686
|
} else {
|
|
@@ -1685,7 +1711,6 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1685
1711
|
|
|
1686
1712
|
const submitSpinner = ui ? ui.spinner() : null;
|
|
1687
1713
|
flowState.submit = "active";
|
|
1688
|
-
renderPublishFlow();
|
|
1689
1714
|
if (submitSpinner) {
|
|
1690
1715
|
submitSpinner.start(`Submitting review request (${visibility})`);
|
|
1691
1716
|
} else {
|
|
@@ -1699,7 +1724,6 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1699
1724
|
body: JSON.stringify(body),
|
|
1700
1725
|
});
|
|
1701
1726
|
flowState.submit = "done";
|
|
1702
|
-
renderPublishFlow();
|
|
1703
1727
|
if (submitSpinner) {
|
|
1704
1728
|
submitSpinner.stop(`Review submission created (${result.submission_id || "pending"})`);
|
|
1705
1729
|
} else {
|
|
@@ -1726,6 +1750,7 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1726
1750
|
].join("\n"),
|
|
1727
1751
|
"Published",
|
|
1728
1752
|
);
|
|
1753
|
+
renderPublishFlow();
|
|
1729
1754
|
ui.outro(`Submitted ${ui.pc.cyan(archiveName)} for review.`);
|
|
1730
1755
|
} else {
|
|
1731
1756
|
log(JSON.stringify(summaryPayload, null, 2));
|