ht-skills 0.2.5 → 0.2.7
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 +316 -8
- package/package.json +1 -1
package/lib/cli.js
CHANGED
|
@@ -157,6 +157,15 @@ async function setStoredRegistryAuth(registry, value, { homeDir = os.homedir() }
|
|
|
157
157
|
await saveCliConfig(config, homeDir);
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
async function clearStoredRegistryAuth(registry, { homeDir = os.homedir() } = {}) {
|
|
161
|
+
const config = await loadCliConfig(homeDir);
|
|
162
|
+
if (!config.registries || typeof config.registries !== "object") {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
delete config.registries[getRegistryConfigKey(registry)];
|
|
166
|
+
await saveCliConfig(config, homeDir);
|
|
167
|
+
}
|
|
168
|
+
|
|
160
169
|
async function resolveAuthToken(registry, flags, { homeDir = os.homedir() } = {}) {
|
|
161
170
|
const inlineToken = String(flags.token || "").trim();
|
|
162
171
|
if (inlineToken) {
|
|
@@ -174,6 +183,79 @@ async function getRequiredAuthToken(registry, flags, { homeDir = os.homedir() }
|
|
|
174
183
|
return token;
|
|
175
184
|
}
|
|
176
185
|
|
|
186
|
+
function isInteractiveSession(deps = {}) {
|
|
187
|
+
return typeof deps.isInteractive === "boolean"
|
|
188
|
+
? deps.isInteractive
|
|
189
|
+
: Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function validateAuthToken(registry, token, deps = {}) {
|
|
193
|
+
const requestJsonImpl = deps.requestJson || requestJson;
|
|
194
|
+
try {
|
|
195
|
+
const payload = await requestJsonImpl(`${registry}/auth/me`, {
|
|
196
|
+
headers: withBearerToken({}, token),
|
|
197
|
+
});
|
|
198
|
+
if (!payload || payload.authenticated !== true) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
return payload;
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (Number(error?.status || 0) === 401) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function ensureValidAuthToken(registry, flags, deps = {}) {
|
|
211
|
+
const log = deps.log || ((message) => console.log(message));
|
|
212
|
+
const homeDir = deps.homeDir || os.homedir();
|
|
213
|
+
const loginImpl = deps.cmdLogin || cmdLogin;
|
|
214
|
+
const interactive = isInteractiveSession(deps);
|
|
215
|
+
const explicitToken = String(flags.token || "").trim();
|
|
216
|
+
|
|
217
|
+
let token = await resolveAuthToken(registry, flags, { homeDir });
|
|
218
|
+
if (!token) {
|
|
219
|
+
if (!interactive) {
|
|
220
|
+
throw new Error(`No saved login for ${registry}. Run "ht-skills login --registry ${registry}" first.`);
|
|
221
|
+
}
|
|
222
|
+
log(`Login required for ${registry}. Starting browser sign-in...`);
|
|
223
|
+
await loginImpl({ ...flags, registry }, deps);
|
|
224
|
+
token = await getRequiredAuthToken(registry, flags, { homeDir });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const authState = await validateAuthToken(registry, token, deps);
|
|
228
|
+
if (authState) {
|
|
229
|
+
return {
|
|
230
|
+
token,
|
|
231
|
+
authState,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (explicitToken) {
|
|
236
|
+
throw new Error(`The provided token for ${registry} is invalid or expired.`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
await clearStoredRegistryAuth(registry, { homeDir });
|
|
240
|
+
|
|
241
|
+
if (!interactive) {
|
|
242
|
+
throw new Error(`Saved login for ${registry} is invalid or expired. Run "ht-skills login --registry ${registry}" first.`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
log(`Saved login for ${registry} is invalid or expired. Starting browser sign-in...`);
|
|
246
|
+
await loginImpl({ ...flags, registry }, deps);
|
|
247
|
+
token = await getRequiredAuthToken(registry, flags, { homeDir });
|
|
248
|
+
const refreshedAuthState = await validateAuthToken(registry, token, deps);
|
|
249
|
+
if (!refreshedAuthState) {
|
|
250
|
+
throw new Error(`Login completed but ${registry} did not accept the refreshed token.`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
token,
|
|
255
|
+
authState: refreshedAuthState,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
177
259
|
function withBearerToken(headers = {}, token = null) {
|
|
178
260
|
if (!token) return { ...headers };
|
|
179
261
|
return {
|
|
@@ -186,6 +268,17 @@ function sleep(ms) {
|
|
|
186
268
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
187
269
|
}
|
|
188
270
|
|
|
271
|
+
function formatBytes(bytes) {
|
|
272
|
+
const value = Number(bytes || 0);
|
|
273
|
+
if (value >= 1024 * 1024) {
|
|
274
|
+
return `${(value / (1024 * 1024)).toFixed(2)} MB`;
|
|
275
|
+
}
|
|
276
|
+
if (value >= 1024) {
|
|
277
|
+
return `${(value / 1024).toFixed(2)} KB`;
|
|
278
|
+
}
|
|
279
|
+
return `${value} B`;
|
|
280
|
+
}
|
|
281
|
+
|
|
189
282
|
function openBrowserUrl(url) {
|
|
190
283
|
const safeUrl = String(url || "").trim();
|
|
191
284
|
if (!safeUrl) {
|
|
@@ -726,6 +819,64 @@ function formatSearchCard(item, { index = 0, colorize = (text) => text, width =
|
|
|
726
819
|
return [title, blank, firstLine, ...detailRendered, blank, bottom].join("\n");
|
|
727
820
|
}
|
|
728
821
|
|
|
822
|
+
const PUBLISH_FLOW_STEPS = [
|
|
823
|
+
{ id: "auth", label: "Login" },
|
|
824
|
+
{ id: "pack", label: "Pack" },
|
|
825
|
+
{ id: "inspect", label: "Inspect" },
|
|
826
|
+
{ id: "submit", label: "Submit" },
|
|
827
|
+
];
|
|
828
|
+
|
|
829
|
+
function getPublishStepStatusLabel(status) {
|
|
830
|
+
switch (status) {
|
|
831
|
+
case "done":
|
|
832
|
+
return "Done";
|
|
833
|
+
case "active":
|
|
834
|
+
return "Active";
|
|
835
|
+
case "error":
|
|
836
|
+
return "Error";
|
|
837
|
+
default:
|
|
838
|
+
return "Pending";
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function renderPublishFlowCard(flowState, { colorize = (text) => text, width = 80 } = {}) {
|
|
843
|
+
const maxInnerWidth = Math.max(28, width - 6);
|
|
844
|
+
const lines = PUBLISH_FLOW_STEPS.map((step, index) => {
|
|
845
|
+
const status = flowState?.[step.id] || "pending";
|
|
846
|
+
const symbol = status === "done"
|
|
847
|
+
? colorize("●", "success")
|
|
848
|
+
: status === "active"
|
|
849
|
+
? colorize("◉", "accent")
|
|
850
|
+
: status === "error"
|
|
851
|
+
? colorize("▲", "warn")
|
|
852
|
+
: colorize("○", "muted");
|
|
853
|
+
const label = status === "done"
|
|
854
|
+
? colorize(step.label, "success")
|
|
855
|
+
: status === "active"
|
|
856
|
+
? colorize(step.label, "accent")
|
|
857
|
+
: status === "error"
|
|
858
|
+
? colorize(step.label, "warn")
|
|
859
|
+
: step.label;
|
|
860
|
+
const statusLabel = colorize(getPublishStepStatusLabel(status), status === "pending" ? "muted" : status === "error" ? "warn" : status === "active" ? "accent" : "success");
|
|
861
|
+
const plainLine = `${index + 1}. ${step.label} ${getPublishStepStatusLabel(status)}`;
|
|
862
|
+
const paddedWidth = Math.max(0, maxInnerWidth - getDisplayWidth(plainLine));
|
|
863
|
+
return `│ ${symbol} ${label}${" ".repeat(paddedWidth)} ${statusLabel} │`;
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
const contentWidth = Math.max(
|
|
867
|
+
28,
|
|
868
|
+
...PUBLISH_FLOW_STEPS.map((step, index) => getDisplayWidth(`${index + 1}. ${step.label} Pending`)),
|
|
869
|
+
);
|
|
870
|
+
const title = `◇ ${colorize("Publish Flow", "accent")} ${colorize("─".repeat(Math.max(0, contentWidth - getDisplayWidth("Publish Flow") - 1)), "muted")}╮`;
|
|
871
|
+
const normalizedLines = lines.map((line) => {
|
|
872
|
+
const plainWidth = getDisplayWidth(line);
|
|
873
|
+
const targetWidth = contentWidth + 4;
|
|
874
|
+
return plainWidth < targetWidth ? `${line.slice(0, -2)}${" ".repeat(targetWidth - plainWidth)} │` : line;
|
|
875
|
+
});
|
|
876
|
+
const bottom = `╰${"─".repeat(contentWidth + 2)}╯`;
|
|
877
|
+
return [title, ...normalizedLines, bottom].join("\n");
|
|
878
|
+
}
|
|
879
|
+
|
|
729
880
|
function printFallbackIntro({ registry, slug, version, skillName, skillDescription, installTargets }, log = console.log) {
|
|
730
881
|
log("");
|
|
731
882
|
log("ht-skills");
|
|
@@ -755,6 +906,19 @@ function printFallbackSearchIntro({ registry, query, limit }, log = console.log)
|
|
|
755
906
|
log("");
|
|
756
907
|
}
|
|
757
908
|
|
|
909
|
+
function printFallbackPublishIntro({ registry, skillDir, archiveName, visibility }, log = console.log) {
|
|
910
|
+
log("");
|
|
911
|
+
log(renderGradientBanner());
|
|
912
|
+
log("");
|
|
913
|
+
log("Publish");
|
|
914
|
+
log("");
|
|
915
|
+
log(`Source: ${registry}`);
|
|
916
|
+
log(`Directory: ${skillDir}`);
|
|
917
|
+
log(`Archive: ${archiveName}`);
|
|
918
|
+
log(`Access: ${visibility}`);
|
|
919
|
+
log("");
|
|
920
|
+
}
|
|
921
|
+
|
|
758
922
|
async function loadTerminalUi() {
|
|
759
923
|
const [{ intro, outro, note, multiselect, select, spinner, isCancel, cancel }, pcModule] = await Promise.all([
|
|
760
924
|
import("@clack/prompts"),
|
|
@@ -1345,14 +1509,90 @@ async function cmdLogin(flags, deps = {}) {
|
|
|
1345
1509
|
async function cmdPublish(flags, deps = {}) {
|
|
1346
1510
|
const requestJsonImpl = deps.requestJson || requestJson;
|
|
1347
1511
|
const log = deps.log || ((message) => console.log(message));
|
|
1348
|
-
const homeDir = deps.homeDir || os.homedir();
|
|
1349
1512
|
const registry = getRegistryUrl(flags);
|
|
1350
1513
|
const skillDir = path.resolve(flags._[0] || ".");
|
|
1351
|
-
const
|
|
1514
|
+
const visibility = String(flags.access || flags.visibility || "public").trim().toLowerCase() || "public";
|
|
1352
1515
|
const archiveName = `${path.basename(skillDir) || "skill"}.zip`;
|
|
1353
|
-
const
|
|
1516
|
+
const isInteractive = isInteractiveSession(deps);
|
|
1517
|
+
const canUseFancyUi = Boolean(isInteractive && !deps.disableUi);
|
|
1518
|
+
const ui = deps.ui || (canUseFancyUi ? await loadTerminalUi().catch(() => null) : null);
|
|
1519
|
+
const colorize = ui ? createUiColorizer(ui.pc) : (text) => String(text);
|
|
1520
|
+
const outputWidth = getOutputWidth(deps.outputWidth);
|
|
1354
1521
|
const pollIntervalMs = Math.max(500, Number(flags["poll-interval"] || deps.pollIntervalMs || DEFAULT_PUBLISH_POLL_MS));
|
|
1522
|
+
const flowState = {
|
|
1523
|
+
auth: "pending",
|
|
1524
|
+
pack: "pending",
|
|
1525
|
+
inspect: "pending",
|
|
1526
|
+
submit: "pending",
|
|
1527
|
+
};
|
|
1528
|
+
|
|
1529
|
+
const renderPublishFlow = () => {
|
|
1530
|
+
if (!ui) return;
|
|
1531
|
+
// eslint-disable-next-line no-console
|
|
1532
|
+
console.log(renderPublishFlowCard(flowState, {
|
|
1533
|
+
colorize,
|
|
1534
|
+
width: Math.max(44, Math.min(outputWidth - 4, 72)),
|
|
1535
|
+
}));
|
|
1536
|
+
};
|
|
1537
|
+
|
|
1538
|
+
if (ui) {
|
|
1539
|
+
// eslint-disable-next-line no-console
|
|
1540
|
+
console.log(`\n${renderGradientBanner()}\n`);
|
|
1541
|
+
ui.intro(ui.pc.bgCyan(ui.pc.black(" ht-skills ")));
|
|
1542
|
+
ui.note(
|
|
1543
|
+
[
|
|
1544
|
+
`${colorize("Source", "muted")}: ${registry}`,
|
|
1545
|
+
`${colorize("Directory", "muted")}: ${skillDir}`,
|
|
1546
|
+
`${colorize("Archive", "muted")}: ${archiveName}`,
|
|
1547
|
+
`${colorize("Access", "muted")}: ${visibility}`,
|
|
1548
|
+
].join("\n"),
|
|
1549
|
+
"Publish",
|
|
1550
|
+
);
|
|
1551
|
+
renderPublishFlow();
|
|
1552
|
+
} else {
|
|
1553
|
+
printFallbackPublishIntro({ registry, skillDir, archiveName, visibility }, log);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
const authSpinner = ui ? ui.spinner() : null;
|
|
1557
|
+
flowState.auth = "active";
|
|
1558
|
+
renderPublishFlow();
|
|
1559
|
+
if (authSpinner) {
|
|
1560
|
+
authSpinner.start("Checking login");
|
|
1561
|
+
} else {
|
|
1562
|
+
log(`Checking login for ${registry}...`);
|
|
1563
|
+
}
|
|
1564
|
+
const { token } = await ensureValidAuthToken(registry, flags, deps);
|
|
1565
|
+
flowState.auth = "done";
|
|
1566
|
+
renderPublishFlow();
|
|
1567
|
+
if (authSpinner) {
|
|
1568
|
+
authSpinner.stop("Login confirmed");
|
|
1569
|
+
}
|
|
1355
1570
|
|
|
1571
|
+
const packSpinner = ui ? ui.spinner() : null;
|
|
1572
|
+
flowState.pack = "active";
|
|
1573
|
+
renderPublishFlow();
|
|
1574
|
+
if (packSpinner) {
|
|
1575
|
+
packSpinner.start(`Packing ${path.basename(skillDir) || skillDir}`);
|
|
1576
|
+
} else {
|
|
1577
|
+
log(`Packing skill directory: ${skillDir}`);
|
|
1578
|
+
}
|
|
1579
|
+
const archiveBuffer = await createZipFromDirectory(skillDir);
|
|
1580
|
+
flowState.pack = "done";
|
|
1581
|
+
renderPublishFlow();
|
|
1582
|
+
if (packSpinner) {
|
|
1583
|
+
packSpinner.stop(`Created ${archiveName} (${formatBytes(archiveBuffer.length)})`);
|
|
1584
|
+
} else {
|
|
1585
|
+
log(`Created archive ${archiveName} (${formatBytes(archiveBuffer.length)})`);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
const uploadSpinner = ui ? ui.spinner() : null;
|
|
1589
|
+
flowState.inspect = "active";
|
|
1590
|
+
renderPublishFlow();
|
|
1591
|
+
if (uploadSpinner) {
|
|
1592
|
+
uploadSpinner.start("Uploading archive for package inspection");
|
|
1593
|
+
} else {
|
|
1594
|
+
log("Uploading archive for package inspection...");
|
|
1595
|
+
}
|
|
1356
1596
|
const job = await requestJsonImpl(
|
|
1357
1597
|
`${registry}/api/skills/inspect-package-jobs/upload?archive_name=${encodeURIComponent(archiveName)}`,
|
|
1358
1598
|
{
|
|
@@ -1368,8 +1608,18 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1368
1608
|
if (!jobId) {
|
|
1369
1609
|
throw new Error("registry did not return an inspection job id");
|
|
1370
1610
|
}
|
|
1611
|
+
if (uploadSpinner) {
|
|
1612
|
+
uploadSpinner.stop(`Inspection job created (${jobId})`);
|
|
1613
|
+
} else {
|
|
1614
|
+
log(`Inspection job created: ${jobId}`);
|
|
1615
|
+
}
|
|
1371
1616
|
|
|
1372
1617
|
let inspection = job;
|
|
1618
|
+
let lastProgressKey = "";
|
|
1619
|
+
const inspectSpinner = ui ? ui.spinner() : null;
|
|
1620
|
+
if (inspectSpinner) {
|
|
1621
|
+
inspectSpinner.start("Inspecting archive");
|
|
1622
|
+
}
|
|
1373
1623
|
while (inspection.status !== "succeeded" && inspection.status !== "failed") {
|
|
1374
1624
|
await sleep(pollIntervalMs);
|
|
1375
1625
|
inspection = await requestJsonImpl(
|
|
@@ -1378,18 +1628,47 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1378
1628
|
headers: withBearerToken({}, token),
|
|
1379
1629
|
},
|
|
1380
1630
|
);
|
|
1631
|
+
|
|
1632
|
+
const step = String(inspection.progress?.step || inspection.result?.step || "").trim();
|
|
1633
|
+
const percent = inspection.progress?.percent;
|
|
1634
|
+
const progressKey = `${inspection.status}:${step}:${percent}`;
|
|
1635
|
+
if (progressKey !== lastProgressKey) {
|
|
1636
|
+
lastProgressKey = progressKey;
|
|
1637
|
+
const progressLabel = step
|
|
1638
|
+
? `${step}${typeof percent === "number" ? ` ${percent}%` : ""}`
|
|
1639
|
+
: inspection.status;
|
|
1640
|
+
if (inspectSpinner) {
|
|
1641
|
+
inspectSpinner.message(`Inspecting archive (${progressLabel})`);
|
|
1642
|
+
} else {
|
|
1643
|
+
log(`Inspection in progress: ${progressLabel}`);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1381
1646
|
}
|
|
1382
1647
|
|
|
1383
1648
|
if (inspection.status !== "succeeded") {
|
|
1649
|
+
flowState.inspect = "error";
|
|
1650
|
+
renderPublishFlow();
|
|
1651
|
+
if (inspectSpinner) {
|
|
1652
|
+
inspectSpinner.stop("Inspection failed");
|
|
1653
|
+
}
|
|
1384
1654
|
throw new Error(inspection.error || "skill archive inspection failed");
|
|
1385
1655
|
}
|
|
1656
|
+
flowState.inspect = "done";
|
|
1657
|
+
renderPublishFlow();
|
|
1658
|
+
if (inspectSpinner) {
|
|
1659
|
+
inspectSpinner.stop("Inspection passed");
|
|
1660
|
+
} else {
|
|
1661
|
+
log("Inspection passed.");
|
|
1662
|
+
}
|
|
1386
1663
|
|
|
1387
1664
|
const preview = inspection.result || {};
|
|
1388
1665
|
if (!preview.valid || !preview.preview_token) {
|
|
1389
1666
|
throw new Error(summarizePreviewErrors(preview));
|
|
1390
1667
|
}
|
|
1668
|
+
if (!ui) {
|
|
1669
|
+
log(`Preview token created: ${preview.preview_token}`);
|
|
1670
|
+
}
|
|
1391
1671
|
|
|
1392
|
-
const visibility = String(flags.access || flags.visibility || "public").trim().toLowerCase() || "public";
|
|
1393
1672
|
const body = {
|
|
1394
1673
|
preview_token: preview.preview_token,
|
|
1395
1674
|
visibility,
|
|
@@ -1404,6 +1683,14 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1404
1683
|
body.publish_now = true;
|
|
1405
1684
|
}
|
|
1406
1685
|
|
|
1686
|
+
const submitSpinner = ui ? ui.spinner() : null;
|
|
1687
|
+
flowState.submit = "active";
|
|
1688
|
+
renderPublishFlow();
|
|
1689
|
+
if (submitSpinner) {
|
|
1690
|
+
submitSpinner.start(`Submitting review request (${visibility})`);
|
|
1691
|
+
} else {
|
|
1692
|
+
log(`Submitting review request with access=${visibility}...`);
|
|
1693
|
+
}
|
|
1407
1694
|
const result = await requestJsonImpl(`${registry}/api/skills/submit`, {
|
|
1408
1695
|
method: "POST",
|
|
1409
1696
|
headers: withBearerToken({
|
|
@@ -1411,8 +1698,15 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1411
1698
|
}, token),
|
|
1412
1699
|
body: JSON.stringify(body),
|
|
1413
1700
|
});
|
|
1701
|
+
flowState.submit = "done";
|
|
1702
|
+
renderPublishFlow();
|
|
1703
|
+
if (submitSpinner) {
|
|
1704
|
+
submitSpinner.stop(`Review submission created (${result.submission_id || "pending"})`);
|
|
1705
|
+
} else {
|
|
1706
|
+
log(`Review submission created: ${result.submission_id || "(no submission id returned)"}`);
|
|
1707
|
+
}
|
|
1414
1708
|
|
|
1415
|
-
|
|
1709
|
+
const summaryPayload = {
|
|
1416
1710
|
status: result.status,
|
|
1417
1711
|
submission_id: result.submission_id || null,
|
|
1418
1712
|
created_at: result.created_at || null,
|
|
@@ -1420,7 +1714,22 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1420
1714
|
publication: result.publication || null,
|
|
1421
1715
|
preview_token: preview.preview_token,
|
|
1422
1716
|
archive_name: archiveName,
|
|
1423
|
-
}
|
|
1717
|
+
};
|
|
1718
|
+
|
|
1719
|
+
if (ui) {
|
|
1720
|
+
ui.note(
|
|
1721
|
+
[
|
|
1722
|
+
`${colorize("Status", "muted")}: ${result.status || "pending"}`,
|
|
1723
|
+
`${colorize("Submission", "muted")}: ${result.submission_id || "-"}`,
|
|
1724
|
+
`${colorize("Preview", "muted")}: ${preview.preview_token}`,
|
|
1725
|
+
`${colorize("Access", "muted")}: ${summaryPayload.visibility || visibility}`,
|
|
1726
|
+
].join("\n"),
|
|
1727
|
+
"Published",
|
|
1728
|
+
);
|
|
1729
|
+
ui.outro(`Submitted ${ui.pc.cyan(archiveName)} for review.`);
|
|
1730
|
+
} else {
|
|
1731
|
+
log(JSON.stringify(summaryPayload, null, 2));
|
|
1732
|
+
}
|
|
1424
1733
|
|
|
1425
1734
|
return result;
|
|
1426
1735
|
}
|
|
@@ -1428,11 +1737,10 @@ async function cmdPublish(flags, deps = {}) {
|
|
|
1428
1737
|
async function cmdSubmit(flags, deps = {}) {
|
|
1429
1738
|
const requestJsonImpl = deps.requestJson || requestJson;
|
|
1430
1739
|
const log = deps.log || ((message) => console.log(message));
|
|
1431
|
-
const homeDir = deps.homeDir || os.homedir();
|
|
1432
1740
|
const skillDirArg = flags._[0] || ".";
|
|
1433
1741
|
const skillDir = path.resolve(skillDirArg);
|
|
1434
1742
|
const registry = getRegistryUrl(flags);
|
|
1435
|
-
const token = await
|
|
1743
|
+
const { token } = await ensureValidAuthToken(registry, flags, deps);
|
|
1436
1744
|
const manifestPath = path.resolve(flags.manifest || path.join(skillDir, "skill.json"));
|
|
1437
1745
|
const manifestRaw = await fs.readFile(manifestPath, "utf8");
|
|
1438
1746
|
const manifest = JSON.parse(manifestRaw);
|